一、链表
(一)概念
逻辑结构:线性
存储结构:链式存储,在内存中不连续
分为有头链表和无头链表
同时又细分为单向、循环、双向链表
(二)有头单向链表示意图
以下数据及地址只是为了方便理解设置
(三)操作
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;
}