zoukankan      html  css  js  c++  java
  • 单向链表的基本操作

    我会尽量把每一步都说明清楚,每一行代码所表示的含义,以及会用直观的代码讲明白

    链表:是一种常见且重要的动态存储分布的数据结构,它由若干个同一结构体类型的“节点”组成,每一个节点含有存储数据的信息以及存放指向下一个节点的指针,我们称之为next指针,最后一个单元的Next指针指向NULL

    链表的常用操作包括建立链表,链表的遍历,插入节点,删除节点,和查找等等。

    下面是结点的结构图

    链表的节点是用结构体类型的递归来定义的,一个单向链表节点的类型定义如下:

    struct stu_node
    {
       int data;               /*数据域*/
       struct stu_node *next; //指向下一个结点的指针
    };

     为了方便理解我就不用下面的这种方式了

    struct stu_node
    {
     int data;
    struct stu_node *next; }stu_node,*Linklist;

     

    单向链表的示意图:

                                                                                                                                     单向链表的示意图

          链表中有一个head头指针变量,头结点是为了处理空表的方便所引用的,用来存放链表中的第一节点的位置,它的data域不存放任何信息

         链表的创建之前先假定定链表节点的类型定义为一个学生链表,定义如下

    #include <stdio.h>  
    #include <stdlib.h>
    struct stu_node
    {
       int num ;
       char naem[20];
       int score;
       struct stu_node *next;
    };

    1.链表的建立:建立链表就是一个一个的输入各节点的数据,然后建立各节点之间的勾链关系

    单向链表的建立有“插表头”,“插表尾”两种方法,插表头是将新节点作为新的表头插入链表,插表尾是将新节点作为表尾链接到链表的表尾,无论使用哪种方法,首先要建立一个空表,然后在此空表的表头或表尾插入新节点。

                                                                                                插表头建立 单向链表

    插表头的算法如下:

     ① head=NULL;  //头指针指向空,表示空链表
    
     ②指针p指向一个产生的新节点
    
     ③p->next=head;head=p; //插表头操作(新节点的next指针指向原来的头指针head。head再指向p作为头指针)
    
     ④循环执行②③,可继续建立新节点

     

    插表尾

                                                                                      插表尾建立单向链表,需要定义一个指针last一直指向表尾,指针p指向新的节点,将新节点直接链在表尾(last->next=p)

    插表尾算法的描述如下:

    ①head=last=NUll              //建立空表。last是表尾指针
    ②指针p指向一个产生的一个新节点p->next=NULL;      //新节点作表尾
    ③如果head为NULL则 head=p;            //在空表中插入第一个节点
    否则last->next=p; //插表尾操作 ④last=p //表尾指针last指向新的表尾节点 ⑤循环执行②③④,可继续建立新节点

     下面将采用插表尾的方法创建一个学生成绩信息的单向链表

    #include<stdio.h>
    #include<stdlib.h>
    struct stu_node
    {
        int num;
        char name[20];
        int score;
        struct stu_node *next;
     } ;
     int n; //n表示链表的长度
     struct stu_node * create_stu(){
         struct stu_node *head,*last,*p;
         head=last=NULL; //建立空表
         n=0;
         printf("输入学号姓名成绩(学号为0时停止输入)");
         do
         {
             p=(struct stu_node *)malloc(sizeof(struct stu_node));
             scanf("%d%s%d",&p->num,p->name,&p->score);
             p->next=NULL;                 //新节点作为表尾 
             if(p->num==0)break;          //学号为0时停止循环
             else if(head==NULL)head=p; //当表为空表时,直接加入新节点
             else last->next=p;        //新节点插入表尾
               last=p;                //表尾指针指向新的表尾节点p
               n++;                  //表长加一 
          } while(1);
          free(p);                  //释放指针p
          return head ;             //返回头指针 
     } 

    链表的遍历:链表的遍历就是逐个扫描链表的节点,然后显示节点的信息,从头指针开始依次通过及节点的next成员访问后继节点(p=p->next)直到表尾

                              先将指针p指向head, 再通过p=p->next访问后一个节点,直到表尾。

    以下是代码

     void print_stu(struct stu_node *head) {
         struct stu_node *p;
         p=head;
         if(head==NULL){
             printf("空表");
             return;
              
         }
         printf("学号 姓名 成绩");
         do{
             printf("%2d %s%d
    ",p->num,p->name,p->score); 
             p=p->next;     //指针指向下一个节点 
         } while(p!=NULL) 
     }

    链表的插入

    代码如下

    /*
    插入指定节点的后面(此例是指定学号的节点)
    */
    struct
    stu_node *insert(struct stu_node *head, int num) { struct stu_node *p; p=head; // 指针p指向第一个节点 struct stu_node *s=(struct stu_node *)malloc(struct stu_node); // 待插入的结点s if(head==NULL) { head=s;s->next=NULL; //当链表为空时,新节点作为头结点插入 n+=1; 链表节点长度加1 return head; } else { while(p->num!=num&&p->next!=NULL)//p指向的结点不是所查找的,并且不是最后一个结点,继续往下找 { p=p->next; if(p->num=num){ /*找到了想要插入的位置*/ s->next=p->next;
    p->next=s;
    n+=1; //节点总数加一 }
    return head; }
    }

     

    如果不好理解的话再换个方法吧 下面用两个指针来插入,操作如下图

    下面是代码

    /*  
       假设有一个学生成绩信息的单向链表,插入学号为num的节点 
     */ 
     struct stu_node *insert(struct stu_node * head,struct stu_node * s) 
     {                                        //s 是将要插入的节点 
            struct stu_node *p,*q;
            p=head;                           //指针p 指向第一个节点 
            if(head==NULL)
         {
             head=s;s->next=NULL;             //链表为空时,新节点作为头节点插入 
         }
             else{
             while((s->num!=p->num)&&(p->next!=NULL)) 
             {
                 q=p;p=p->next;             //移动pq指针 寻找插入点 
             }
                  if(s->num==p->num){      //找到插入的节点后在指针p与q之间插入新节点 
                 if(p==head)head=s;       //新节点作为头节点插入 
                 else q->next=s;         //新节点插入q指向的节点之后 
                 s->next=p;
             }
                 else{                  //新节点作为尾节点插入 
                 p->next=s;            
                 s->next=NULL;
             }
         }
                 n++;                //链表节点长度加一 
                return head;
     }

     链表的删除

    删除应该很好理解:就是利用指针s从第一个节点开始找,指针p总是指向指针s所指节点的前驱。当指针s指向的节点就是待删除的节点时直接修改其前驱节点的成员值(p->next=s->next;),直接改变勾链关系,就可以实现节点删除

    删除算法详细如下

    ①指针s指向表头s=head;

    ②当s指向的不是待删除的节点且还没有到表尾时,移动指针p=s;s=s->next;

    ③当找到删除节点时:

     如果s==head,则:p=head;head=head->next;  /* 当删除的节点是表头节点时*/

     否则p->next=s->next;                                      /*删除s所指向的节点*/

    ④free(s);                                                          /*释放删除节点的内存*/

    //假设有一个学生成绩的单向链表,编写一个函数实现 删除学号为num的节点

    struct stu_node *delete_stu(struct stu_node* head,int num)
    {
    struct stu_node *p,*s;
    if(head==NULL) /* 当链表为空时*/
    {
    printf("链表为空");
    }
    s=head; /*指针s指向第一个节点,从表头开始查找*/
    while(num!=s->num&&s->next!=NULL) /*查找的节点不是想要的且还没到达表尾*/
    {
    p=s;s=s->next; /*指针后移*/
    }
    if(num==s->num){ /*找到删除的节点*/
    if(s==head)head=s->next; /*当删除的是头结点时*/
    else p->next=s->next; /*当删除的节点不是头节点时*/
    printf("已经删除:%d ",num);
    free(s); /*释放删除节点的内存*/
    n--; /*节点长度减一*/
    }
    else printf("%d 表中无此点",num); /*当找不到符合的节点时*/
    return head;
    }
    /*delete_stu()函数的形参num接收的是想删除的学号,函数返回值是头指针*/

     

     emmmm接下来是关于链表的查找

    链表的查找也是分为两种①按照序号查找②按值查找

    ①链表不是随机存取结构在链表中,即使知道被访问结点的序号i,也不能像顺序表中那样直接按序号i访问结点,而只能从链表的头指针出发,顺链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构。

    头节点看做第0个节点,下面给的例子意思是 查找节点值为num的节点信息

     /*链表的查询,查找特定节点的信息*/
     struct stu_node *find_stu(struct stu_node* head,int num)
      {
         struct stu_node *p=head;
         int j=0;
         while(p->next&&j<num)
         {
             p=p->next;
             j++;
         }
         if(j==num)
         {
            printf("查找的节点为%d   学号  姓名  信息
    "); 
             printf("%d	%s	%d
    ",p->num,p->name,p->score);
             return p;
         }
         else
         return NULL;
     }

    ②按值查找:从头节点出发依次逐个将节点的数据域和定值num比较(这里的num代表的指定学生的学号)若节点的数据域与定值num相同,打印该节点的信息,且返回该节点,若找不到匹配节点的数据域则返回NULL

    下面是实现按值查找的算法

    /*查询特定学号的学生的信息*/
     struct stu_node *look_stu(struct stu_node *head,int num)
     {
         struct stu_node *p=head;
         while(p&&num!=p->num)
         {
             p=p->next;  
         }
         printf("%2d	%s	%2d
    ",p->num,p->name,p->score);
         return p;
    
     }

    emmm关于链表的基本操作的介绍与操作已经介绍完下面我们进行测试;

    测试代码如下

    main()
     {
         struct stu_node* head=NULL,*p;
         int choise,num;
         do
         {
             printf("请选择(0-4)");
             printf("1.建立链表	2.插入	3.删除	4.遍历	5.查找	0.退出
    ");
             scanf("%d",&choise);
             switch(choise)
             {
                 case 1: head=create_stu();
                        break;
                 case 2: printf("输入待插入的学生的信息:");
                         p=(struct stu_node *)malloc(sizeof(struct stu_node));
                         scanf("%d%s%d",&p->num,p->name,&p->score);
                         head=insert_stu(head,p);
                         break;
                 case 3:printf("删除的学生学号");
                       scanf("%d",&num);
                       head=delete_stu(head,num);
                       break;
                case 4:print_stu(head);
                case 5:printf("输入想要查找的节点序号");
                       scanf("%d",&num);
                       head=look_stu(head,num);
                       break; 
                case 0: break;      
              } 
         }while(choise!=0); 
     }

    下面是测试的结果:

    最后也是最重要的:谢谢你看我的博客 (如果有人看我的博客的话),如果有不足之处的话还请指出,后续有时间的话我会考虑增加新的功能的。 禁止转载

  • 相关阅读:
    ios数据处理 简单文件处理
    ios硬件开发 照相机图像选取器(UIImagePickerController)的用法
    ios UI设计与开发 卷动视图
    ios数据处理 使用SQLite保存学生信息
    ios数据处理 Application preferfences
    ios数据处理 SQLite基本知识
    GDI+ 为了阴影和透明,使用双层窗口遇到的一些问题
    MFC CToolTipCtrl 总是显示
    遍历删除文件夹及文件
    WebService远程调试
  • 原文地址:https://www.cnblogs.com/xaimicom/p/8978348.html
Copyright © 2011-2022 走看看