当前位置:   article > 正文

单链表的基本操作和应用_单链表的基本操作与应用

单链表的基本操作与应用

一、单链表的特点

  1. 有一个head 指针变量,它存放头结点的地址,称之为头指针
  2. 头结点的指针域 head->next,存放首元结点(第一个实际有效结点)的地址。
  3. 每个结点都包含一个数据域和一个指针域,数据域存放用户需要的实际数据,指针域存放下一个结点的地址。从头指针 head开始,head 指向头结点,头结点指向首元结点,首元结点指向第二个结点,…,直到最后一个结点。所有结点都是单线联系环环相扣
  4. 最后一个结点不再指向其他结点,称为“表尾结点”,它的指针域为空指针“NULL”,表示链表到此结束。指向表尾结点的指针称为尾指针。
  5. 链表各结点之间的顺序关系由指针域next来确定,并不要求逻辑上相邻的结点物理位置上也相邻,也就是说,链表依靠指钍相连不需要占用一片连续的内存空间
  6. 随着处理数据量的增加,链表可以不受程序中变量定义的限制无限地延长(仅受内存总量的限制)。在插入和删除操作中,只需修改相关结点指针域的链接关系,不需要像数组那样大量地改变数据的实际存储位置。链表的使用,可以使程序的内存利用率和时间效率大大提高。

二、创建头结点

  1. typedef struct node{
  2. int data;//数据
  3. struct node *next;//next的类型是指向本结构体类型的指针类型
  4. }node,*linklist;
  5. linklist headlist(){
  6. node*head;//定义头指针变量
  7. head=(node*)malloc(sizeof(node));//头指针指向分配的头结点内存空间
  8. head->next=NULL;//头结点的指针域为空
  9. return head;//返回头结点的地址,即头指针
  10. }

其中,node是结构体类型,linklist是结构体指针类型。linklist head相当于是node *head,也相当于是struct node*head。 

三、链表的建立

1.头插法

  1. void creatlist(linklist head){
  2. node* s;
  3. int data;
  4. while(1){
  5. scanf("%d",&data);
  6. if(data==0)//结束循环的条件
  7. break;
  8. s=(node*)malloc(sizeof(node));
  9. s->data=data;
  10. s->next=head->next;//新结点指向原来的首元结点
  11. head->next=s;//链表的头结点指向新结点
  12. }
  13. }
  14. //就是将新结点插入到当前链表的表头结点之后

2.尾插法

  1. void creatlist (linklist head){
  2. node *r,*s;
  3. int data;
  4. r=head;//r指向头结点
  5. while(1){
  6. scanf("%d",&data);
  7. if(data==0){//结束输入退出循环的条件
  8. break;
  9. }
  10. s=(node*)malloc(sizeof(node));//分配结点的内存空间
  11. s->data=data;
  12. r->next=s;//原来的结点指向新结点
  13. r=s;//r指向新结点
  14. }
  15. s->next=NULL;//链表的尾结点指针为空
  16. }

四、链表的遍历

  1. void output(linklist head){
  2. node *p;//循环所用的临时指针
  3. p=head->next;//p指向链表的首元结点
  4. while(p){
  5. printf("%d ",p->data);//输出数据
  6. p=p->next;//移动临时指针到下一个结点
  7. }
  8. }
  9. //如果是最后一个结点,指针指向NULL,表示链表中的结点都已输出,循环结束

五、链表的插入

1.在单链表第i个位置上插入新结点

  1. void insert(linklist head,int i){
  2. node *=head,*s;
  3. int j=0;
  4. while(j<i-1&&p){//找到第i-1个结点的地址p
  5. p=p->next;
  6. j++;
  7. }
  8. if(p){
  9. s=(node*)malloc(sizeof(node));//定义s指向新分配的空间
  10. scanf("%s",s->data);
  11. s->next=p->next;//新结点指向原来的第i个结点
  12. p->next=s;//新结点成为新链表的第i个结点
  13. }
  14. }

2.表首位置后添加结点 (同理创建链表的头插法)

3.在链表的最后添加结点

  1. void insertrear(linklist head){
  2. node *p=head,*s;
  3. while(p && p->next){//找到最后一个结点的地址p
  4. p=p->next;
  5. }
  6. if(p){
  7. s=(node*)malloc(sizeof(node));//定义s指向新分配的空间
  8. scanf("%s",s->data);
  9. p->next=s;//尾结点的指针指向新结点
  10. s->next=NULL;//新结点指针指向NULL
  11. }
  12. }

六、链表的删除

  1. void DeleteL(linklist head,int pos){
  2. node *p=head,*q;
  3. int pos=0;
  4. while(j<pos-1&&p){//找到第pos-1个结点的地址p
  5. p=p->next;
  6. j++;
  7. }
  8. if(p==NULL||p->next==NULL){//第pos个结点不存在
  9. printf("the pos is ERROR!");
  10. }else{
  11. q=p->next;//q指向pos个结点
  12. p->next=q->next;//连接删除结点两边的结点
  13. free(q); //释放要删除结点的内存空间
  14. }
  15. }

七、链表的查询

  1. node *search(linklist head,int data){
  2. node *p=head->next;
  3. while(p){
  4. if(p->data!=data)
  5. p=p->next;
  6. else
  7. break;//查找成功
  8. }
  9. if(p==NULL){
  10. printf("没有找到值为%d的结点!",data);
  11. }
  12. return p;
  13. }

八、链表的长度

  1. int len(linklist head){
  2. node* p;
  3. int count=0;
  4. p=head->next;//指针变量p指向链表的首元结点
  5. while(p){//结点存在,表示链表没有遍历结束
  6. count++;//结点个数累计器加1
  7. p=p->next;//指向当前节点的下一个结点
  8. }
  9. return count;//返回结点的个数
  10. }

九、不带头节点的单链表

1.不带头结点单链表的插入

链表的插入操作如果在链表的首位置,则插入时,首先为插入的新结点分配内存,然后将新结点的指针s指向原来首结点(s->next=head),最后将头指针指向新结点(head=s),这样就完成了插入结点的操作,很显然,这种情况下,头指针发生了改变,所以需要返回新头指针
链表的插入操作如果插入位置不是首位置,则需要先通过循环找到链表的第i-1个结点的地址p。如果该结点存在,则可以在第i-1个结点后面插入第i个结点。为插入的新结点分配内存,然后向新结点输入数据。插入时,首先将新结点的指针s指向原来第i个结点(s->next=p->next),然后将第i-1个结点指向新结点(p->next=s),这样就完成了插入结点的操作,很显然,这种情况下的操作和带头结点的操作是一样的。

2.不带头结点单链表的删除

删除的结点如果是链表的首结点,则定义指针变量q指向待删结点(q=head),再让头指针指向第二个结点(head=head->next),成为新的首结点最后释放原来的首结点(free(q)),则这样就完成了删除首结点的操作。很显然,这种情况下头指针发生了改变,所以需要返回新头指针
删除的结点如果不是链表的首结点,定义整型变量i来控制循环的次数,然后定义指变量p表示该结点之前的结点。接着利用循环找到要删除的结点之前的结点p;如果该结点存在并且待删除结点存在,则将指针变量q指向待删除结点(q=p->next),再连接要删除结点两边的结点(p->next=q>next),并使用free函数将q指向的内存空间进行释放的第i-1个结(free(q)),这样就完成了删除结点的操作。很显然,这种情况下的操作和带头结点的删除操作是一样的。 

十、单链表的应用

1.合并两个升序链表

2.反转链表

迭代:

  1. void reverse(linklist head){
  2. node*p,*q;
  3. p=head->next;//p指向首元结点
  4. head->next=NULL;//将原链表置为空表
  5. while(q){
  6. q=p->next;
  7. p->next=head->next;//将当前结点插入到头结点的后面
  8. head->next=p;
  9. p=q;
  10. }
  11. }

递归: 

3.回文链表 

  1. int huiwen(linklist head){
  2. node *p=head->next,*h,*s,*r;
  3. h=(node*)malloc(sizeof(node));//创建一个新链表的头
  4. h->next=NULL;
  5. int n=len(head);//求出链表的长度
  6. int j=1;
  7. while(p&&j<=n/2){//将原链表的一半结点数据赋给新链表的结点,用头插法创建
  8. s=(node*)malloc(sizeof(node));
  9. s->data=p->data;
  10. s->next=h->next;
  11. h->next=s;
  12. p=p->next;
  13. j++;
  14. }
  15. if(n%2){//如果原链表的长度为奇数,p指向p的下一个结点
  16. j++;
  17. p=p->next;
  18. }
  19. r=h->next;
  20. while(p&&j<=n){//比较新链表和原链表的后一半结点是否相等
  21. if(r->data!=p->data){//若不相等返回0,说明不是回文链表
  22. return 0;
  23. }
  24. j++;
  25. r=r->next;
  26. p=p->next;
  27. }
  28. return 1;//返回1,说明是回文链表
  29. }

4.两两交换链表中的结点

5.删除链表中的重复元素

6.对链表进行插入排序 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/426601
推荐阅读
相关标签
  

闽ICP备14008679号