当前位置:   article > 正文

数据结构——单链表(小白入门第二天)_typedef struct lnode

typedef struct lnode

一、什么是单链表

定义:每个结点 除了存放数据元素外,还要存储指向下一个节点的指针;

优点:不要求大片连续空间,改变容量方便;

缺点:不可随机存取,要耗费一定空间存放指针

局限性:无法逆向检索

二、用代码定义一个单链表

方法一:

  1. struct LNode {
  2. ElemType data;//定义单链表节点类型 data变量用来存放数据元素(数据域)每个节点存放一个数据元素
  3. struct LNode* next;//定义指向下一个节点的指针(指针域) 指针指向下一个节点
  4. };
  5. struct LNode* p = (struct LNode*)malloc(sizeof(struct LNode));//增加一个新节点;再内存中申请一个节点所需空间,并用指针p指向这个结点

typedef<数据类型><别名>

  1. typedef struct LNode LNode;
  2. LNode* p = (LNode*)malloc(sizeof(LNode));

方法二:

  1. struct LNode {
  2. ElemType data;
  3. struct LNode* next;
  4. };//先定义数据类型
  5. typedef struct LNode LNode;//struct LNode 重命名为LNode
  6. typedef struct LNode* LinkList;//用LinkList表示这个一个指向Struct LNode的指针

方法三:(方法二的简写)

  1. typedef struct LNode{
  2. ELemType data;
  3. struct LNode* next;
  4. }LNode, * LinList;

三、表示一个单链表

只需要声明一个头指针L,指向单链表的第一个结点

两种表达方式实质含义相同,强调重点不同 

 四、初始化单链表

4.1不带头结点的单链表:

4.2带头结点的单链表:

 L->next = NULL;( L这个指针指向的next指针域设置为NULL)

五、单链表的插入

5.1按位序插入(带头结点)

ListInsert(&L,i,e):插入操作;再表L的第i个位置上插入指定元素e,先找到i-1个结点

然后用malloc申请一个新结点,存入元素e,然后对指针进行修改

代码框架

  1. typedef struct LNode
  2. {
  3. ElemType data; //数据域
  4. struct LNode *next; //指针域
  5. }LNode,*LinkList;//定义结点
  6. bool ListInsert(LinkList &L,int i,ElemType e) /* 改变L */
  7. { /* 在L的第i个位置之前插入元素e */
  8. if(i<1)
  9. return false;//i从1开始
  10. LNode *p;
  11. int j=0;//第0个结点,指向头结点,实际从第一个开始;表示当前p指向第几个跌点
  12. p=L; //L指向头结点,头结点不存数据
  13. while(p!=NULL && j<i-1) /* 寻找第i-1个结点 */
  14. {
  15. p=p->next;
  16. j++;
  17. }
  18. /*i-1插入新结点*/
  19. if(p==NULL)
  20. return false;
  21. LNode *s;//新的结点s;
  22. s=(LinkList)malloc(sizeof(LNode)); /* 生成新结点 */
  23. s->data=e; /* 插入L中 */
  24. s->next=p->next;
  25. p->next=s;
  26. return true;
  27. }

(55条消息) 王道2-06 单链表的插入 删除_黑糖儿*的博客-CSDN博客

代码实现

  1. #include <stdio.h>
  2. #include<stdlib.h>
  3. typedef struct LNode{ //定义单链表结点类型
  4. int data; //数据域
  5. struct LNode *next; //指针域 (为什么next指针域要定义为struct LNode呢,)
  6. }LNode, *LinkList;
  7. //(为什么next指针域要定义为struct LNode呢?)
  8. //next指针用来指向链表的下一个结点,该结点同样为一个LNode结构体,
  9. //因此next要声明为指向LNode结构体的指针struct LNode*
  10. bool InitList(LinkList &L)
  11. {
  12. L = (LNode *)malloc(sizeof(LNode));
  13. if(L == NULL)
  14. return false;
  15. L->next = NULL;
  16. return true;
  17. }
  18. //在第i个位置插入元素e(带头结点)
  19. bool ListInsert(LinkList &L, int i, int e){
  20. //判断i的合法性, i是位序号(从1开始)
  21. if(i<1)
  22. return false;
  23. LNode *p; //指针p指向当前扫描到的结点
  24. int j=0; //当前p指向的是第几个结点
  25. p = L; //L指向头结点,头结点是第0个结点(不存数据)
  26. //循环找到第i-1个结点
  27. while(p!=NULL && j<i-1){ //如果i>lengh, p最后会等于NULL 寻找第i-1个结点
  28. p = p->next; //p指向下一个结点
  29. j++;
  30. }
  31. if (p==NULL) //i值不合法
  32. return false;
  33. //在第i-1个结点后插入新结点
  34. LNode *s = (LNode *)malloc(sizeof(LNode)); //申请一个结点
  35. s->data = e;
  36. s->next = p->next;
  37. p->next = s; //将结点s连到p后,后两步千万不能颠倒qwq
  38. return true;
  39. }
  40. void test()
  41. {
  42. LinkList L;
  43. if(InitList(L))
  44. printf("true");
  45. printf("\n");
  46. ListInsert(L,1,3);
  47. ListInsert(L,2,5);
  48. LNode *s = L->next;
  49. while(s)
  50. {
  51. printf("%d ",s->data);
  52. s = s->next;
  53. }
  54. return;
  55. }
  56. int main(void)
  57. {
  58. test();
  59. return 0;
  60. }

(55条消息) 【数据结构】单链表按位序插入(带头节点)_向前的诚_的博客-CSDN博客_按位序插入

s->data = e;//将结点s的data值设置为e

s->next = p->next;//链表指针赋值,将p的下一个节点位置赋给s的下一个结点

p->next = s;//将结点s连接到p之后

平均复杂度O(n)

5.2按位序插入(不带头结点)

特殊处理:

  1. if(i==1)
  2. {
  3. LNode *s;//新的结点s;
  4. s=(LinkList)malloc(sizeof(LNode)); /* 生成新结点 */
  5. s->data=e; /* 插入L中 */
  6. s->next=L;//p->next就是头指针
  7. L=s;
  8. return true;
  9. }
  10. int j=1;//j从1开始

代码实现:
 

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define ElemType int
  4. typedef struct LNode{ //定义单链表结点类型
  5. ElemType data; //每个结点存放一个数据元素
  6. struct LNode* next; //指针指向下一个结点
  7. }LNode,*LinkList;
  8. //初始化一个空白的单链表
  9. bool InitList(LinkList &L)
  10. {
  11. L=NULL; //空表,暂时还没有存放任何结点(防止脏数据)
  12. return true;
  13. }
  14. //在第i个位置插入元素e:
  15. bool ListInsert(LinkList &L,int i,ElemType e)
  16. {
  17. if(i<1)return false;
  18. if(i==1) //插入第一个结点的操作与其他结点不同。
  19. {
  20. LNode* s=(LNode*)malloc(sizeof(LNode));
  21. s->data=e;
  22. s->next=L;
  23. L=s; //头指针指向新结点
  24. return true;
  25. }
  26. LNode *p; //指针p指向当前扫描到的结点
  27. int j=1; //当前p指向的是第几个结点
  28. p=L; //p指向第一个结点(不是头结点)
  29. while(p!=NULL&&j<i-1)
  30. {
  31. p=p->next;
  32. j++;
  33. }
  34. if(p==NULL)return false; //i值不合法
  35. LNode* s=(LNode*)malloc(sizeof(LNode));
  36. s->data=e;
  37. s->next=p->next;
  38. p->next=s;
  39. return true;
  40. }
  41. void OutputList(LinkList L)
  42. {
  43. LinkList p=L;
  44. while(p)
  45. {
  46. printf("%d ",p->data);
  47. p=p->next;
  48. }
  49. printf("\n");
  50. }
  51. int main()
  52. { //声明一个指向单链表的指针
  53. LinkList L;
  54. //初始化一个空表
  55. InitList(L);
  56. //在单链表的第i个位置插入元素i:
  57. for(int i=1;i<=5;i++)ListInsert(L,i,i);
  58. //输出单链表中的所有元素:
  59. OutputList(L);
  60. return 0;
  61. }

注意j最开始设置为1,表示p刚开始指向的结点是第一个结点

5.3指定结点的后插操作

InsertNextNode(LNode *p,ElemType e);

  1. typedef struct LNode{
  2. int data;
  3. struct LNode *next;
  4. }LNode, *LinkList;
  5. bool InsertNextNode(LNode *p, int e){
  6. if(p==NULL)
  7. return false;
  8. LNode *s = (LNode *)malloc(sizeof(LNode));
  9. if(s==NULL) //内存分配失败
  10. return false;
  11. s->data = e;
  12. s->next = p->next;
  13. p->next = s; //将结点s连到p之后
  14. return true;
  15. }

(55条消息) 数据结构单链表:指定结点的前插、后插操作_挑灯夜未央的博客-CSDN博客_指定结点的前插操作

时间复杂度O(1)

直接调用InsertNextNode(p,e);

5.4 指定节点的前插操作

InsertPriorNode(LNode *p,ElemType e);

单链表只能往后找,不能往前面。

malloc——先申请一个结点s;

 p->next = s——把新结点作为原来p结点的后置结点;

s->data = p->data——把p结点以前存放的数据元素复制到s结点;

p->data = e——把新插入的元素e放入原来p位置;

方法一:

  1. typedef struct LNode{
  2. int data;
  3. struct LNode *next;
  4. }LNode, *LinkList;
  5. //在p结点之前插入元素e
  6. bool InsertPriorNode(LNode *p, int e){
  7. if(p==NULL)
  8. return false;
  9. LNode *s = (LNode *)malloc(sizeof(LNode));
  10. if(s==NULL) //内存分配失败
  11. return false;
  12. s->next = p->next;
  13. p->next = s; //新结点s连到p之后
  14. s->data = p->data; //将p中元素复制到s中
  15. p->data = e; //p中元素覆盖为e
  16. return true;
  17. }
  18. //核心思想:由于没法直接找到p结点的前结点,可以先把s结点插入p结点之后,
  19. //再把p结点的数据复制到s结点,最后把p中数据赋值为新插入值e。

方法二:

时间复杂度O(1)

六、单链表的删除

6.1 按位序删除(带头结点)

ListDelete(&L,i,&e)——删除操作,删除表中L中第i个位置的元素,并用e返回删除元素的值

找到第i-1个结点,将其指针指向第i+1个结点,并释放第i个结点

  1. //删除指定结点
  2. bool DeleteNode(LNode *p){
  3. if(p==NULL)
  4. return false;
  5. LNode *q=p->next; //令q指向*p的后继结点
  6. p->data=p->next->data; //和后继结点交换数据域
  7. p->next=q->next;
  8. free(q);
  9. return true;
  10. }//如果p结点是最后一个结点,则只能从表头开始依次寻找p的前驱 ,时间复杂度O(n)

 ①找到第i-1个结点

  1. bool ListDelete(LinkList &L, int i, int e){
  2. //判断i的合法性, i是位序号(从1开始)
  3. if(i<1)
  4. return false;
  5. LNode *p; //指针p指向当前扫描到的结点
  6. int j=0; //当前p指向的是第几个结点
  7. p = L; //L指向头结点,头结点是第0个结点(不存数据)
  8. //循环找到第i-1个结点
  9. while(p!=NULL && j<i-1){ //如果i>lengh, p最后会等于NULL 寻找第i-1个结点
  10. p = p->next; //p指向下一个结点
  11. j++;
  12. }

②LNode *q = p->next;

定义指针q指向p结点的下一个,即第i结点

 ③e=q->data;

接下来q结点里的数据元素复制到e里面去(e是引用型,前面要加&)

④  p->next=q->next;

p的next指向q的next ,即NULL????????

⑤  free(q);

释放q的内存空间

最好时间度O(1)

最坏时间度O(n)

 6.2 指定结点的删除

DeleteNode(LNode *p)

  1. //删除指定结点
  2. bool DeleteNode(LNode *p){
  3. if(p==NULL)
  4. return false;
  5. LNode *q=p->next; //令q指向*p的后继结点
  6. p->data=p->next->data; //和后继结点交换数据域
  7. p->next=q->next;
  8. free(q);
  9. return true;
  10. }//如果p结点是最后一个结点,则只能从表头开始依次寻找p的前驱 ,时间复杂度O(n)

 p->data=p->next->data——将p后继结点的数据复制到p的数据域里面

再让p结点的后继节点指向q的后继节点

 释放归还q结点

 七、单链表的查找

7.1 按位查找

GetElem(L,i)——获取表L中第i个位置的元素的值;

  1. LNode * GetElem(LinkList L, int i){
  2. if (i<0){
  3. return NULL;
  4. }
  5. LNode *p;
  6. int j = 0;
  7. p=L;
  8. while (p!= NULL && j<i){
  9. p = p->next;
  10. j++;
  11. }
  12. return p;
  13. }

在单链表的按位插入和删除中已经实现了第i-1个结点的查找

7.2 按值查找

  1. LNode * LocateElem(LinkList L, int e){
  2. LNode *p = L->next;
  3. while (p != NULL && p->data != e){
  4. p = p->next;
  5. }
  6. return p;
  7. }

①LNode *p = L->next——头指针p指向头结点的下一个结点

②while (p != NULL && p->data != e)——p≠NULL并且p的数据域≠e

p = p->next——满足条件让p指针指向下一个结点

 平均时间复杂度O(n)

7.3 求表的长度

  1. int Length(LinkList L){
  2. int len=0;//统计表长
  3. LNode *p=L;
  4. while(p->next !=NULL){
  5. p=p->next;
  6. len++;
  7. }
  8. return len;
  9. }

时间复杂度O(n)

八、单链表的建立

①初始化一个单链表

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. typedef struct LNode //定义单链表结点类型
  4. {
  5. ElemType data; //每个结点存放一个数据元素
  6. struct LNode *next; //指针指向下一个节点
  7. }LNode,*LinkList;
  8. //初始化一个空的单链表(带头结点)
  9. bool InitList(LinkList &L)
  10. {
  11. L=(LNode*)malloc(sizeof(LNode)); //分配一个头节点
  12. if(L==NULL)
  13. return false;
  14. L->next=NULL;
  15. return true;
  16. }
  17. void test()
  18. {
  19. LinkList L; //声明一个指向单链表的指针
  20. //初始化一个空表
  21. InitList(L);
  22. }

(55条消息) 单链表的初始化代码_啊哈哈哈哈大魔王的博客-CSDN博客_初始化单链表的代码

②每次取一个数据元素插入到表尾/表头

8.1 尾插法

位序插入

每次都要从头开始遍历,时间复杂度为O(n^2)

设置一个表尾指针r,每次就都从表尾插入

  1. LinkList List_TailInsert(LinkList &L){
  2. int x;
  3. L = (LinkList ) malloc(sizeof (LNode));
  4. LNode *s,*r=L;
  5. scanf("%d", &x);//输入节点的值
  6. while (x!=9999){//输入9999表示结束(可为任意值)
  7. s=(LNode *) malloc(sizeof (LNode));
  8. s->data=x;
  9. r->next=s;
  10. r = s;
  11. scanf("%d", &x);
  12. }
  13. r->next =NULL;
  14. return L;
  15. }

  L = (LinkList ) malloc(sizeof (LNode))——malloc申请一个头结点,即初始化单链表的操作

LNode *s,*r=L——申明两个指针s,r都指向头结点

 s=(LNode *) malloc(sizeof (LNode))——申请新结点,让s指向新结点
 s->data=x——把新结点的数值设为x(输入值)


 r->next=s——把r的下一个指针指向s结点


r = s——让r指针指向s结点

........

 

8.2 头插法

  1. LinkList List_HeadInsert(LinkList &L){//逆向建立单链表
  2. LNode *s;
  3. int x;
  4. L = (LinkList) malloc(sizeof (LNode));//创建头结点
  5. L->next = NULL;//初始为空链表
  6. scanf("%d", x);//输入结点值
  7. while (x!= 9999){//输入9999表示结束
  8. s=(LNode *) malloc(sizeof (LNode));//创建新结点
  9. s->data = x;
  10. s->next = L->next;
  11. L->next = s;//将新结点插入表中,L为头指针
  12. scanf("%d", x);
  13. }
  14. return L;
  15. }

 涉及其他知识点:

bool

返回值True or  False

(55条消息) C++中定义一个函数为bool类型的作用_&Mr.Gong的博客-CSDN博客_c++bool函数怎么用

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

闽ICP备14008679号