单向链表的基本操作


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

链表:是一种常见且重要的动态存储分布的数据结构,它由若干个同一结构体类型的“节点”组成,每一个节点含有存储数据的信息以及存放指向下一个节点的指针,我们称之为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\n",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\n",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 学号 姓名 信息\n"); printf("%d\t%s\t%d\n",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\t%s\t%2d\n",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.建立链表\t2.插入\t3.删除\t4.遍历\t5.查找\t0.退出\n"); 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); }

下面是测试的结果:

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM