第四周 可变数组和链表——MOOC慕课笔记
1.可变数组
typedef struct{
int *array;
int size;
} Array;//不定义结构指针
- 可变数组的创建
- 不返回结构指针:调用函数内部的变量都是临时创建的,函数返回时将会被释放,如果返回指针,指向的本地变量将会失效
- 不建议用传结构的指针进去修改其值,有可能结构的指针指向的内存空间是动态分配的,这时候需要free;又有可能a–>NULL,这时候会出错
Array array_create(int init_size){
Array a;
a.array = (int*)malloc(sizeof(int)*init_size);//动态分配的内存空间函数调用返回时不会被释放
a.size = init_size;
return a;//不返回结构指针return &a;
}
- 可变数组大小的访问(封装——不把内部实现细节暴露)
int array_size(const Array *a){
return a->size;
}
- 可变数组元素的访问
int *array_at(Array *a,int index){
return &(a->array[index]);//即可访问该值又可修改该值
}
-
可变数组的长大(重要)
- 方法一(简单易懂但不方便)
int array_inflate(Array *a,int more_size){ int *p = (int*)malloc(sizeof(int)*(a->size + more_size)); for (int i = 0;i<a->size;++i){ p[i] = a->array[i];//复制原数组的元素 } free(a->array); a->array = p; a->size += more_size; }
- 方法二(block的概念——每次增加一个块)
const int Block_Size = 20;//每个块的大小存放20个元素 int *array_at(Array *a,int index){ if ( index >= a->size)//每次增加以一个块为基准 //(index/Block_Size+1)*Block_Size = 增加元素后数组的大小 array_inflate(a, (index/Block_Size+1)*Block_Size-(a->size)); return &(a->array[index]);//即可访问该值又可修改该值 } int array_inflate(Array *a,int more_size){ int *p = (int*)malloc(sizeof(int)*(a->size + more_size)); for (int i = 0;i<a->size;++i){ p[i] = a->array[i];//复制原数组的元素 } free(a->array); a->array = p; a->size += more_size; }
2.单向链表
-
可变数组的设计缺陷
- 每一次扩展数据都需要拷贝数组
- 每一次扩展数组都需要申请新的内存(有可能明明内存足够但是却申请不了)
- 需要allocated+more但是allocated需要在新内存分配后才能释放
-
链表——linked blocks(将分配的新块链接起来)
- 每个结点存放数据以及指向下一个结构的指针
- 每个结构称作结点node
- 末尾结构内的指针指向NULL
-
链表元素的新增
-
方法一
typedef struct _node{
int value;
struct _node *next;
}Node;
//读入数据实现的核心代码
Node * head =NULL;
//增加结点
Node *p =(Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
//寻找链表尾部
Node *last = head;
if (last){
//head 不为NULL
while (last->next)//寻找指向最后结点的指针
last = last->next;
last->next = p;//找到最后一个节点,将新结点拼接在其后面
}else head = p;
- 方法二(自定义List结构——只有head无tail)
- 好处:自定义数据结构来代表整个链表,给将来升级改造整个链表带来无限可能,否则head只是光秃秃的一个head,没有具体意义的head
- 注意:因为指针head的内容有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值(pp指向p,p指向具体的内容——需要传进去的是指针p的地址才能够修改其内容)
typedef struct _node{
int value;
struct _node *next;
}Node;
typedef struct _list{
Node * head;
// Node * tail;
}List;//自定义一个List结构来代表链表
//读入数据实现的核心代码
List list;//初始化list
list.head = list.tail = NULL;
//增加结点的函数
void add(List * plist,int number){
//因为head的数据有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值
//增加结点
Node *p =(Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
//find the last
Node *last = plist->head;
if (last){
//head 不为NULL
while (last->next)//寻找指向最后结点的指针
last = last->next;
last->next = p;//找到最后一个节点,将新结点拼接在其后面
}
else
plist->head = p;
}
- 方法三(自定义List结构——有head有tail)
typedef struct _node{
int value;
struct _node *next;
}Node;
typedef struct _list{
Node * head;
Node * tail;
}List;//自定义一个List结构来代表链表
//读入数据实现的核心代码
List list;//初始化list
list.head = list.tail = NULL;
//增加结点的函数
void add(List * plist,int number){
//因为head的数据有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值
//增加结点
Node *p =(Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
//folow the last
if ( plist->head == NULL)//链表没有元素
plist->head = plist->tail = p;
else {
//链表已有至少一个元素,head不需要动,只需将元素接着每个尾巴指向的next后面,并且使该尾巴指向后一个插入结点
plist->tail->next = p;
plist->tail = p;
}
}
- 链表的遍历
void printf(List *plist){
Node *p;
for (p=plist->head; p; p= p->next)
printf("%d\t",p->value);
printf("\n");
}
- 链表元素的查找
void find(List *plist,int number){
Node *p;
bool isFound = false;
for (p=plist->head; p; p= p->next)
if (p->value == number){
printf("找到了\n");
isFond = true;
break;
}
if (!isFound )
printf("没找到\n");
}
-
链表元素的删除
‘->’符号右边的指针必须check是否为NULL,否则会出错(边界条件需注意)
void delete(List *plist,int number){
Node *p,*q;
for (q=NULL,p=plist->head; p; q=p,p=p->next){
if (p->value = number){
if ( q )//第一个元素即为所删除元素,此时q=NULL
plist->head = p->next;
else q->next = p->next; }
free(p);
break;
}
}
- 链表的清除
void free_List(List *plist){
Node *p,*q;
for (p = plist->head; p ; q = p;){
q = p->next;
free(p);
}
}