当前位置:   article > 正文

单链表简单操作_单链表有前驱节点吗

单链表有前驱节点吗

 

链表—链式存储

由于数组的这些缺点,自然而然的就产生链表的思想了。

链表通过不连续的储存方式,自适应内存大小,以及指针的灵活使用,巧妙的简化了上述的内容。

链表的基本思维是,利用结构体的设置,额外开辟出一份内存空间去作指针,它总是指向下一个结点,一个个结点通过NEXT指针相互串联,就形成了链表。

其中DATA为自定义的数据类型,NEXT为指向下一个链表结点的指针,通过访问NEXT,可以引导我们去访问链表的下一个结点。

对于一连串的结点而言,就形成了链表如下图:

 

上文所说的插入删除操作只需要修改指针所指向的区域就可以了,不需要进行大量的数据移动操作。如下图:

相比起数组,链表解决了数组不方便移动,插入,删除元素的弊端,但相应的,链表付出了更加大的内存牺牲换来的这些功能的实现。

链表概述

包含单链表,双链表,循环单链表,实际应用中的功能不同,但实现方式都差不多。

  • 单链表就像是美国男篮,一代一代往下传;

  • 双链表像是中国男足,你传球给我,我传球给你,最终传给了守门员;

  • 循环链表就像是中国男篮,火炬从姚明传给王治郅,王治郅传给易建联,现在易建联伤了,又传给了姚明

单链表

单链表概念和简单的设计

单链表是一种链式存取的数据结构,链表中的数据是以结点来表示的,每个结点由元素和指针构成。

元素表示数据元素的映象,就是存储数据的存储单元;指针指示出后继元素存储位置,就是连接每个结点的地址数据。

结点的序列表示的线性表称作线性链表,也就是单链表,单链表是链式存取的结构。

对于链表的每一个结点,我们使用结构体进行设计,其主要内容有:

其中,DATA数据元素,可以为你想要储存的任何数据格式,可以是数组,可以是int,甚至可以是结构体(这就是传说中的结构体套结构体)

NEXT为一个指针,其代表了一个可以指向的区域,通常是用来指向下一个结点,链表的尾部NEXT指向NULL(空),因为尾部没有任何可以指向的空间了

故,对于一个单链表的结点定义,可以代码描述成:

  1. //定义结点类型
  2. typedef struct Node {
  3. int data; //数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型
  4. struct Node *next; //单链表的指针域
  5. } Node,*LinkedList;
  6. //Node表示结点的类型,LinkedList表示指向Node结点类型的指针类型

链表的初始化

初始化主要完成以下工作:创建一个单链表的前置节点并向后逐步添加节点,一般指的是申请结点的空间,同时对一个结点赋空值(NULL),其代码可以表示为:

  1. LinkedList listinit(){
  2. Node *L;
  3. L=(Node*)malloc(sizeof(Node)); //开辟空间
  4. if(L==NULL){ //判断是否开辟空间失败,这一步很有必要
  5. printf("申请空间失败");
  6. //exit(0); //开辟空间失败可以考虑直接结束程序
  7. }
  8. L->next=NULL; //指针指向空
  9. }

注意:一定要判断是否开辟空间失败,否则生产中由于未知的情况造成空间开辟失败,仍然在继续执行代码,后果将不堪设想啦,因此养成这样的判断是很有必要的。

头插入法创建单链表

利用指针指向下一个结点元素的方式进行逐个创建,使用头插入法最终得到的结果是逆序的。如图所示:

从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。

  1. //头插法建立单链表
  2. LinkedList LinkedListCreatH() {
  3.     Node *L;
  4.     L = (Node *)malloc(sizeof(Node));   //申请头结点空间
  5.     L->next = NULL;                      //初始化一个空链表
  6.   
  7.     int x;                         //x为链表数据域中的数据
  8.     while(scanf("%d",&x) != EOF) {
  9.         Node *p;
  10.         p = (Node *)malloc(sizeof(Node));   //申请新的结点
  11.         p->data = x;                     //结点数据域赋值
  12.         p->next = L->next;     //将结点插入到表头L-->|2|-->|1|-->NULL
  13.         L->next = p;
  14.     }
  15.     return L;
  16. }

尾插入法创建单链表

如图所示为尾插入法的创建过程。

头插法生成的链表中,结点的次序和输入数据的顺序不一致。若希望两者次序一致,则需要尾插法。

该方法是将新结点逐个插入到当前链表的表尾上,为此必须增加一个尾指针r, 使其始终指向当前链表的尾结点,代码如下:

  1. //尾插法建立单链表
  2.   
  3. LinkedList LinkedListCreatT() {
  4.     Node *L;
  5.     L = (Node *)malloc(sizeof(Node));   //申请头结点空间
  6.     L->next = NULL;                  //初始化一个空链表
  7.     Node *r;
  8.     r = L;                          //r始终指向终端结点,开始时指向头结点
  9.     int x;                         //x为链表数据域中的数据
  10.     while(scanf("%d",&x) != EOF) {
  11.         Node *p;
  12.         p = (Node *)malloc(sizeof(Node));   //申请新的结点
  13.         p->data = x;                     //结点数据域赋值
  14.         r->next = p;            //将结点插入到表头L-->|1|-->|2|-->NULL
  15.         r = p;
  16.     }
  17.     r->next = NULL;
  18.     return L;
  19. }

遍历单链表如打印、修改

从链表的头开始,逐步向后进行每一个元素的访问,称为遍历。

对于遍历操作,我们可以衍生出很多常用的数据操作,比如查询元素,修改元素,获取元素个数,打印整个链表数据等等。

进行遍历的思路极其简单,只需要建立一个指向链表L的结点,然后沿着链表L逐个向后搜索即可,代码如下:

  1. //便利输出单链表
  2. void printList(LinkedList L){
  3.     Node *p=L->next;
  4.     int i=0;
  5.     while(p){
  6.         printf("第%d个元素的值为:%d\n",++i,p->data);
  7.         p=p->next;
  8.     }
  9. }

对于元素修改操作,以下是代码实现:

  1. //链表内容的修改,在链表中修改值为x的元素变为为k。
  2. LinkedList LinkedListReplace(LinkedList L,int x,int k) {
  3.     Node *p=L->next;
  4.     int i=0;
  5.     while(p){
  6.         if(p->data==x){
  7.             p->data=k;
  8.         }
  9.         p=p->next;
  10.     }
  11.     return L;
  12. }

简单的遍历设计的函数只需要void无参即可,而当涉及到元素操作时,可以设计一个LinkedList类型的函数,使其返回一个操作后的新链表。

插入操作

链表的插入操作主要分为查找到第i个位置,将该位置的next指针修改为指向我们新插入的结点,而新插入的结点next指针指向我们i+1个位置的结点。

其操作方式可以设置一个前驱结点,利用循环找到第i个位置,再进行插入。

如图,在DATA1和DATA2数据结点之中插入一个NEW_DATA数据结点:

从原来的链表状态

到新的链表状态:

代码实现如下:

  1. //单链表的插入,在链表的第i个位置插入x的元素
  2.   
  3. LinkedList LinkedListInsert(LinkedList L,int i,int x) {
  4.     Node *pre;                      //pre为前驱结点
  5.     pre = L;
  6.     int tempi = 0;
  7.     for (tempi = 1; tempi < i; tempi++) {
  8.         pre = pre->next;                 //查找第i个位置的前驱结点
  9.     }
  10.     Node *p;                                //插入的结点为p
  11.     p = (Node *)malloc(sizeof(Node));
  12.     p->data = x;
  13.     p->next = pre->next;
  14.     pre->next = p;
  15.   
  16.     return L;
  17. }

删除操作

删除元素要建立一个前驱结点和一个当前结点,当找到了我们需要删除的数据时,直接使用前驱结点跳过要删除的结点指向要删除结点的后一个结点,再将原有的结点通过free函数释放掉。如图所示:

代码如下:

  1. //单链表的删除,在链表中删除值为x的元素
  2.   
  3. LinkedList LinkedListDelete(LinkedList L,int x) {
  4.     Node *p,*pre;                   //pre为前驱结点,p为查找的结点。
  5.     p = L->next;
  6.      
  7.     while(p->data != x) {              //查找值为x的元素
  8.         pre = p;
  9.         p = p->next;
  10.     }
  11.     pre->next = p->next;          //删除操作,将其前驱next指向其后继。
  12.     free(p);
  13.      
  14.     return L;
  15. }

 

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. typedef char datatype;
  4. typedef struct node
  5. {
  6.   datatype data;   //节点数据
  7.   struct node *next;//节点指针
  8. }linklist;
  9. //链表建立
  10. //返回:头指针
  11. linklist * creat_list()
  12. {
  13.   char ch;
  14.   int i = 1;
  15.   linklist *head = NULL, *p = NULL, *tail = NULL,*q = NULL;
  16.   printf("\n输入单个字符作为第一个数据:");
  17.   scanf("\n%c", &ch);
  18.   head = (linklist *)malloc(sizeof(linklist));//建立头结点
  19.   head->next = NULL;
  20.   tail = head;   //尾指针,开始指向头,以后指向尾
  21.   
  22.   while(ch != '#')
  23.   {
  24.     p = (linklist *)malloc(sizeof(linklist));//p指向新建节点
  25.     p->next =NULL;                          
  26.     p->data = ch;                            //尾节点指向新节点
  27.     tail->next = p;
  28.     tail = p;                                
  29.     
  30.     printf("\n已经建立了%d个字符.\n", i);
  31.     q = head->next;
  32.     while(NULL != q)
  33.     {
  34.       printf("%c->", q->data);
  35.       q = q->next;    
  36.     }
  37.     printf("NULL");
  38.     i++;
  39.     printf("\n 输入单个字符链表的第%d个数据:", i);
  40.     scanf("\n%c", &ch);
  41.   }
  42. return (head);
  43. }
  44. //按序号查找
  45. //参数:序号,和对应的链表头
  46. //返回相应指针(地址)
  47. linklist * find2(linklist *head, int i)
  48. {
  49.     linklist * p = head;
  50.     int j =1;//当前序号
  51.     while(NULL != p && j<i)
  52.     {
  53.        p = p->next;
  54.        j++;    
  55.     }
  56.     if(j == i) 
  57.     {
  58.         printf("\n第%d个节点数据为:%c",j,p->data);
  59.         return (p);
  60.     }
  61.     else 
  62.     {
  63.         return (NULL);
  64.     }
  65. }
  66. //按值查找
  67. //参数:值,和对应的链表头
  68. //返回相应指针(地址)
  69. linklist * find1(linklist *head, datatype ch)
  70. {
  71.     linklist * p = head;
  72.     int i = 1;//节点计数
  73.     while(NULL != p)
  74.     {
  75.        if(p->data != ch) 
  76.        {
  77.           i++;
  78.           p = p->next;
  79.        }
  80.        else
  81.        {
  82.         break;
  83.        
  84.        }
  85.     }
  86.    return (p);
  87. }
  88. //单链表插入                  头,   值,    序号
  89. int linklist_insert(linklist *head,datatype ch ,int num)
  90. {
  91.   linklist *s = NULL,*p = head;//从头开始搜索   p用于指向搜索的节点 s用于指向插入的节点
  92.   int i = 0;
  93.   while(i < num-1 && p->next != NULL)  //搜索到插入序号的前一个
  94.   {
  95.      i++;
  96.      p = p->next;
  97.   }
  98.   if(num - 1 != i) return 0;
  99.   
  100.   s= (linklist *)malloc(sizeof(linklist));   //新建节点将数值保存
  101.   s->data = ch;                              //将数值保存
  102.   s->next = p->next;                         //将待插节点 连接后继节点
  103.   p->next = s;                                //将待插节点 连接前继节点
  104.   return 1;
  105. }
  106. // 按位置 删除运算
  107. int list_delete(linklist *head, int num)//删除第num个
  108. {
  109.   int i =1;//记录当前搜索的位置
  110.   linklist *p = head;//指向搜索的节点
  111.   linklist *s;//用于指向删除的节点
  112.   p = head;
  113.   while(i < num -1 && p->next != NULL)//序号的前一个
  114.   {
  115.     p = p->next;
  116.     i++;
  117.   }
  118.   if(num - 1 != i) return 0;
  119.   s = p->next;   // p->next 就是要删除节点的位置 用s保存
  120.   p->next = s->next;  //p->next 指向后一个节点
  121.   free(s);
  122.   return 1;
  123. }

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

闽ICP备14008679号