一、链表
(一)概念
逻辑结构:线性
存储结构:链式存储,在内存中不连续
分为有头链表和无头链表
同时又细分为单向、循环、双向链表
(二)有头单向链表示意图
以下数据及地址只是为了方便理解设置
(三)操作
1. 节点的结构体定义:
① 定义:
typedef struct node
{int data; //保存数据struct node* next; //记录下一节点的地址,注意此时指针类型不能定义为nd_t*
}nd_t;
② 注意点:
- 定义结构体成员时,指针的类型此时不能使用nd_t定义。
2. 创建链表
(1) 函数声明
create_list(nd_t **phead,int num);
该函数需要在内存中申请一块nd_t类型大小的空间,并将其地址返回给main函数中用户定义的指针中;
num是用来初始化这块空间中数据域的值。
(2)注意点:
- 必须传入二级指针
- phead不能为空,即不能传一个NULL作为形参,后面需要对phead取值
- 需要检查是否申请内存成功
(3)示意图
(4)代码实现
//创建节点
create_list(nd_t **phead,int num){if(NULL==phead){printf("传入指针为NULL");}//*phead可以为空,即main函数中的phead可以是一个空指针//但是phead不能为空,即不能传一个NULL作为形参*phead=(nd_t *)malloc(sizeof(nd_t));if(NULL==*phead){printf("申请空间失败!\n");return -1;}//初始化(*phead)->data=num;(*phead)->next=NULL;return 0;
}
3. 清空
(1) 函数声明
int clean_list(nd_t *phead);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
int clean_list(nd_t *phead){if(NULL==phead){printf("传入指针为NULL");return -1;}if(NULL==phead->next){printf("表为空\n");return -1;}nd_t *ptemp=NULL;//使用头删清空while(NULL!=phead->next){ptemp=phead->next;phead->next=ptemp->next;free(ptemp);}ptemp=NULL;return 0;
}
4. 销毁
(1) 函数声明
int destory_list(nd_t **phead);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
int destory_list(nd_t **phead){if(NULL==phead || NULL==*phead){printf("传入指针为NULL");return -1;}clean_list(*phead);free(*phead);//free参数不能是一个空指针*phead=NULL;return 0;
}
5. 插入(头插、尾插、任意位置插)
(1)头插
① 函数声明
int insert_list_by_head(nd_t *phead,int num);
② 注意点:
- 传入一级指针即可
- 需要判断传入的列表指针是否是空指针
③ 示意图
③ 代码实现
int insert_list_by_head(nd_t *phead,int num){if(NULL==phead){printf("传入指针为NULL");return -1;}//创建新节点nd_t *ptemp = NULL;if(create_node(&ptemp,num)<0){printf("内存分配失败\n");return -1;}ptemp->next=phead->next;phead->next=ptemp;return 0;
}
(2)尾插
① 函数声明
int insert_list_by_tail(nd_t *phead,int num);
② 注意点:
③ 代码实现
int insert_list_by_tail(nd_t *phead,int num){if(NULL==phead){printf("传入指针为NULL");return -1;}nd_t *ptemp=phead;//找到尾节点while(NULL!=ptemp->next){ptemp=ptemp->next;}//创建新节点nd_t *pnew = NULL;if(create_node(&pnew,num)<0){printf("内存分配失败\n");return -1;}ptemp->next=pnew;return 0;
}
(3)任意位置插入
① 函数声明
int insert_list_by_pos(nd_t *phead,int pos,int num);
② 注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
③ 代码实现
int insert_list_by_pos(nd_t *phead,int pos,int num){if(NULL==phead){printf("传入指针为NULL");return -1;}if(0>pos){printf("位置不合理\n");return -1;}nd_t *ptemp=phead;//找到要插入的位置的前一个节点for(int i=0;i<pos;i++){//当ptemp->next为0时,说明不能继续向下遍历了if(NULL==ptemp->next){printf("位置不合理\n");return -1;}ptemp=ptemp->next;}//创建新节点nd_t *pnew = NULL;if(create_node(&pnew,num)<0){printf("内存分配失败\n");return -1;}pnew->next=ptemp->next;ptemp->next=pnew;return 0;
}
6. 删除(头删、尾删、任意位置删)
(1)头删
① 函数声明
int insert_list_by_tail(nd_t *phead,int num);
② 注意点:
③ 代码实现
int delete_list_by_head(nd_t *phead){if(NULL==phead){printf("传入指针为NULL");return -1;}if(NULL==phead->next){printf("表为空\n");return -1;}nd_t *ptemp=phead->next;phead->next=ptemp->next;free(ptemp);ptemp=NULL;return 0;
}
(2)尾删
① 函数声明
int insert_list_by_tail(nd_t *phead,int num);
② 注意点:
③ 代码实现
int delete_list_by_tail(nd_t *phead){if(NULL==phead){printf("传入指针为NULL");return -1;}if(NULL==phead->next){printf("表为空\n");return -1;}//找到尾节点的前一个节点nd_t *ptemp=phead;while(NULL!=ptemp->next->next){ptemp=ptemp->next;}free(ptemp->next);ptemp->next=NULL;return 0;
}
(3)任意位置删除
① 函数声明
int delete_list_by_pos(nd_t *phead,int pos);
② 注意点:
③ 代码实现
int delete_list_by_pos(nd_t *phead,int pos){if(NULL==phead){printf("传入指针为NULL");return -1;}if(0>pos){printf("位置不合理\n");return -1;}nd_t *ptemp=phead;//找到要删除的位置的前一个节点for(int i=0;i<pos;i++){//当ptemp->next为0时,说明此时ptemp位于链表的最后一个节点,但是此时ptemp后的节点是空,不能进行删除操作//所以如果ptemp->next->next为空时,就不能继续往后移动了if(NULL==ptemp->next->next){printf("位置不合理\n");return -1;}ptemp=ptemp->next;}nd_t *pdel=ptemp->next;ptemp->next=pdel->next;free(pdel);pdel=NULL;return 0;
}
7. 修改
(1) 函数声明
int modify_list_by_pos(nd_t *phead,int pos,int num);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
int modify_list_by_pos(nd_t *phead,int pos,int num){if(NULL==phead){printf("传入指针为NULL");return -1;}if(0>pos){printf("位置不合理\n");return -1;}nd_t *ptemp=phead;//找到要修改的位置的节点for(int i=0;i<=pos;i++){//当ptemp->next为0时,说明此时ptemp位于链表的最后一个节点,不能继续后移了if(NULL==ptemp->next){printf("位置不合理\n");return -1;}ptemp=ptemp->next;}ptemp->data=num;return 0;
}
8. 查询
(1) 函数声明
int search_list_by_pos(nd_t *phead,int pos,int *num);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
int search_list_by_pos(nd_t *phead,int pos,int *num){if(NULL==phead){printf("传入指针为NULL");return -1;}if(0>pos){printf("位置不合理\n");return -1;}nd_t *ptemp=phead;//找到要查找的位置的节点for(int i=0;i<=pos;i++){//当ptemp->next为0时,说明此时ptemp位于链表的最后一个节点,不能继续后移了if(NULL==ptemp->next){printf("位置不合理\n");return -1;}ptemp=ptemp->next;}*num=ptemp->data;return 0;
}
9. 链表合并
(1) 函数声明
int merge_list(nd_t *phead1,nd_t **phead2);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
int merge_list(nd_t *phead1,nd_t **phead2){if(NULL==phead1||NULL==phead2||NULL==*phead2){printf("传参为空\n");return -1;}//先找到表1的尾部nd_t *ptemp=phead1;while(NULL!=ptemp->next){ptemp=ptemp->next;}ptemp->next=(*phead2)->next;free(*phead2);*phead2=NULL;return 0;
}
10. 链表排序
(1) 函数声明
int create_node(node_t **list,int *num);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
11. 链表翻转
(1) 函数声明
int create_node(node_t **list,int *num);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
12. 链表剔重
(1) 函数声明
int create_node(node_t **list,int *num);
(2)注意点:
- 必须传入二级指针
- 需要判断传入的列表指针是否是空指针
(3)代码实现
13. 打印链表
(1) 函数声明
int print_list(nd_t *phead);
(2)注意点:
- 注意对于边界点的判定
(3)代码实现
//打印
int print_list(nd_t *phead){if(NULL==phead){printf("传入指针为NULL");return -1;}nd_t *ptemp=phead->next;while(NULL!=ptemp){printf("%d ",ptemp->data);ptemp=ptemp->next;}putchar(10);return 0;
}