一、前言
单链表是线性表的链式存储结构,作为数据结构中最基础也是最重要的线性结构之一,在考研数据结构科目中占有重要地位。本文将总结带头结点单链表的各项基本操作,包括初始化、插入、删除、查找等,并附上完整C语言实现代码,帮助大家系统掌握这一重要知识点。本文基于王道考研做的知识点总结和代码复现。
二、基本操作
1. 初始化单链表(带头结点)
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;
//初始化,带头结点
bool InitLinkList(LinkList &L){L=(LNode*)malloc(sizeof(LNode));L->next=NULL;return true;
}
2. 创建单链表
//头插法
LinkList List_HeadInsert(LinkList &L){LNode *s;ElemType x;scanf("%d",&x);while(x!=9999){s=(LNode*)malloc(sizeof(LNode));s->data=x;s->next=L->next;L->next=s;scanf("%d",&x); }return L;
}//尾插法
LinkList List_TailInsert(LinkList &L){LNode *r=L,*s;ElemType x;scanf("%d",&x);while(x!=9999){s=(LNode*)malloc(sizeof(LNode));s->data=x;r->next=s;r=s;//r指向新的尾节点scanf("%d",&x);}r->next=NULL;return r;
}
3. 查找、插入与删除操作
//求长度--O(n)
int Length(LinkList L){int len=0;LNode *p=L->next;while(p!=NULL){len++;p=p->next;}return len;
}
//按位查找--O(n)
LNode *GetElem(LinkList L,int i){LNode *p=L;int j=0;while(p!=NULL&&j<i){p=p->next;j++;}return p;
}
//按值查找--O(n)
LNode *LocateElem(LinkList L,ElemType e){LNode *p=L->next;while(p!=NULL&&p->data!=e)p=p->next;return p;
}
//插入某元素到链表的指定位置--O(n)
bool ListInsert(LinkList &L,int i,ElemType &e){LNode *p=L;int j=0;while(p->next!=NULL && j<i-1){p=p->next;j++;}if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode));s->data = e;s->next = p->next;p->next = s;return true;
}//删除某元素到链表的指定位置--O(n)
bool ListDelete(LinkList &L,int i,ElemType &e){LNode *p=L;int j=0;while(p!=NULL&&j<i-1){p=p->next;j++;}if(p==NULL||p->next==NULL){return false;}LNode *q=p->next;e=q->data;p->next=q->next;free(q);return true;
}
4. 判断两链表是否有相同节点
//判断两个链表是否有相同节点
LNode *getSimilar(LNode *A,LNode *B){LNode *a=A->next,*b=B->next;int ab=Length(A)-Length(B);if(ab>0){while(ab){a=a->next;ab--;}}else if(ab<0){while(ab){b=b->next;ab++;}}while(a&&b){if(a->data==b->data) return a;a=a->next;b=b->next;}return NULL;
}
三、主函数测试代码
int main()
{//创建链表A,BLinkList A,B;InitLinkList(A); InitLinkList(B);printf("创建链表A(按9999退出):");List_TailInsert(A);printf("创建链表B(按9999退出):");List_TailInsert(B);//统计表长,查询指定的值int len_A,len_B;len_A=Length(A);len_B=Length(B);printf("A,B的长度分别是%d和%d\n",len_A,len_B);printf("按位查询A的第2个节点是,%d\n",GetElem(A,2)->data);if(LocateElem(B,2)->data != NULL)printf("按值查询B中是否有节点值为2,有\n");elseprintf("按值查询B中是否有节点值为2,没有\n");//打印链表A,Bprintf("打印链表A:");LNode *p=A->next,*q=B->next;while(p!=NULL){printf("%d\t ",p->data);p=p->next;} printf("\n打印链表B:");while(q!=NULL){printf("%d\t ",q->data);q=q->next;}//查找A,B是否有相同的节点if(getSimilar(A,B)!=NULL) printf("\nA,B中有相同值的节点,%d\n",getSimilar(A,B)->data);else printf("\nA,B中没有相同值的节点\n");//删除链表A,Bint x,y;for(int i=0;i<=len_A;i++)ListDelete(A,i,x);for(int j=0;j<=len_B;j++)ListDelete(B,j,y);printf("删除A,B的最后两个元素是%d,%d",x,y);return 0;
}
基于上述的代码进行测试,结果如下图所示:
四、练习模板
万丈高楼的搭建,也离不开简单的一砖一瓦,坚实的基础才是拿下大题的关键。
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;
//初始化,带头结点
bool InitLinkList(LinkList &L){}
//头插法
LinkList List_HeadInsert(LinkList &L){}
//尾插法
LinkList List_TailInsert(LinkList &L){}
//求长度--O(n)
int Length(LinkList L){}
//按位查找--O(n)
LNode *GetElem(LinkList L,int i){}
//按值查找--O(n)
LNode *LocateElem(LinkList L,ElemType e){}
//插入某元素到链表的指定位置--O(n)
bool ListInsert(LinkList &L,int i,ElemType &e){}
//删除某元素到链表的指定位置--O(n)
bool ListDelete(LinkList &L,int i,ElemType &e){}
//判断两个链表是否有相同节点
LNode *getSimilar(LNode *A,LNode *B){}
int main(){
//可用上面的main函数替换该部分的内容
}
五、总结
掌握带头结点单链表的各项操作是数据结构学习的基础,也是考研的重点内容。带头结点的链表相比普通链表有以下优势:
- 统一操作:无论链表是否为空,操作方式一致
- 简化代码:不需要特殊处理第一个结点的情况
- 提高效率:某些操作如头插法更加高效
六、附录
操作 | 代码模板 | 注意事项 |
头插法 | s->next=L->next; L->next=s | 两句顺序不能反 |
尾插法 | r->next=s; r=s; | 最后指针要置NULL |
结点删除 | p->next = q->next; free(q); | 需要临时保存q的data |
链表遍历 | while(p!=NULL){...p=p->next;} | 结束条件勿用p->next!=NULL |
💡 应试技巧
1. 画图!用不同颜色标注指针变化
2. 先写边界条件(空表、单结点表)
3. 检查循环结束后尾结点是否为NULL