单向链表的基本操作

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

链表:是一种常见且重要的动态存储分布的数据结构,它由若干个同一结构体类型的“节点”组成,每一个节点含有存储数据的信息以及存放指向下一个节点的指针,我们称之为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); 
 }

下面是测试的结果:

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

原文地址:https://www.cnblogs.com/xaimicom/p/8978348.html