当前位置:   article > 正文

线性表(顺序表、单链表、双链表、循环表)_顺序表的按照值查找序号操作程序流程图

顺序表的按照值查找序号操作程序流程图

目录

自己敲的时候出现的问题

顺序表

顺序表的分配

顺序表的初始化(静态动态)

动态分配+增加长度:

插入操作✅(动态的也一样)

 删除操作✅

按值查找和按位查找 ✅

单链表

定义结点及各名词的解释

增加一个新结点:

初始化单链表

头插法建立单链表

尾插法建立单链表

按序号查找结点值

插入结点(p从头开始遍历时)

删除结点

双链表 

循环单、双链表

静态链表​​​​​​​


自己敲的时候出现的问题

1、sequence:顺序、按顺序。

初始化顺序表的函数名:InitList,形参:(Sqlist *L)

插入操作时,形参不仅只有SeqList *L,ElemType e,还应该有int i,这个不要在函数内部定义。

2、在插入函数里,没有判断i>Maxsize的情况,忘了下标是0-(n-1)。

3、删除操作看准:要删除的元素e已知

插入和删除的函数类型都是bool。

提前令 *e  = L -> data[i-1];

顺序表

顺序表的分配

  1. typedef int ElemType;
  2. //静态分配
  3. #define MaxSize 10 //定义最大长度
  4. typedef struct
  5. { int data[MaxSize];//用静态的“数组”存放数据元素
  6. int length; //顺序表的当前长度
  7. }SqList; //顺序表的类型定义(静态分配方式)
  8. //动态分配
  9. #define InitSize 10 //定义顺序表的初始长度
  10. typedef struct{
  11. ElemType *data; //指示动态分配数组的指针
  12. int MaxSize; //顺序表的最大容量
  13. int length; //顺序表的当前长度
  14. }SeqList; //顺序表的类型定义(动态分配)

C语言里,动态内存申请:

C语言里初始动态分配语句为:

L.data = (Elemtype*)malloc(sizeof(Elemtype)*InitSize);

在上面的动态分配中:data是动态分配出的数组的名字,length代表当前已经分配的长度,MaxSize代表最多分配的长度,等于或超过这个长度就要扩容。 

顺序表的初始化(静态动态)

  1. //初始化静态分配的顺序表。初始化的过程:将元素值、表长都初始化为0
  2. void InitList(SqList *L)
  3. {
  4. for (int i = 0; i < MaxSize; i++)
  5. {
  6. L->data[i] = 0;
  7. }
  8. L->length = 0;
  9. }
  10. //顺序表的初始化——动态分配
  11. void InitList(SeqList *L)
  12. {
  13. L->data = (int *)malloc(InitSize * sizeof(int));
  14. L->length = 0;
  15. L-> MaxSize = InitSize;
  16. }

动态分配+增加长度:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define InitSize 10 //定义最大长度
  4. //要想使用malloc函数和free函数时,必须有stdlib.h头文件。
  5. typedef struct
  6. {
  7. int *data;//注意看,这里是指针域
  8. int MaxSize;
  9. int length;
  10. }SeqList;
  11. //每个数据元素分配空间大小 MaxSize* sizeof(ElemType);
  12. //顺序表的初始化——动态分配
  13. void InitList(SeqList *L)
  14. {
  15. L->data = (int *)malloc(InitSize * sizeof(int));
  16. L->length = 0;
  17. L-> MaxSize = InitSize;
  18. }
  19. //增加动态数组的长度
  20. void IncreaseSize(SeqList *L,int len)
  21. {
  22. int *p = L->data;//p指针指向原来的地方,接下来,data会指向另一个空间,免得找不到。
  23. L->data = (int *) malloc((L->MaxSize+len)*sizeof(int));//重新在别的地方动态分配一片更大的空间。
  24. for (int i = 0; i < L->length; i++)
  25. {
  26. L->data[i] = p[i];//将原来的数据复制到新区域
  27. }
  28. L->MaxSize = L->MaxSize+len;//顺序表最大长度增加len
  29. free(p); //释放原来的内存空间,系统自动回收。
  30. }
  31. int main()
  32. {
  33. SeqList L; //声明一个顺序表
  34. InitList(&L);//初始化顺序表
  35. //…省略一些代码,如:向表中填满。
  36. IncreaseSize(&L,5);
  37. return 0;
  38. }

插入操作✅(动态的也一样)

  1. //顺序表的基本操作——插入和删除
  2. #include <stdio.h>
  3. #include <stdbool.h>
  4. #define MaxSize 10 //定义最大长度
  5. typedef int ElemType;
  6. //采用静态分配
  7. typedef struct {
  8. ElemType data[MaxSize];
  9. int length;
  10. }SqList;
  11. void InitList(SqList *L)
  12. {
  13. for (int i = 0; i < MaxSize; i++)
  14. {
  15. (*L).data[i] = 0;//将所有数据元素设置为默认初始值
  16. }
  17. (*L).length = 0; //顺序表初始长度为0
  18. }
  19. //将e插入data[i]处。
  20. void ListInsert(SqList *L,int i,int e)
  21. {
  22. for (int j = L->length; j >= i; j--)
  23. {
  24. //将第i个元素及之后的元素后移
  25. L->data[j] =L->data[j-1];
  26. }
  27. L->data[i-1] = e;
  28. //插入e,题目中会说将e插在i处,是插在数组下标为i-1处。
  29. L->length ++; //长度加1
  30. }
  31. //改进一下,解决问题:是否越界等
  32. bool ListInsert1(SqList *L, int i, int e){
  33. if (i < 1 || i > L->length + 1)
  34. {
  35. return false;
  36. //判断值范围是否有效
  37. }
  38. if (L->length >= MaxSize)
  39. {
  40. //当前存储空间已满,不能插入
  41. return false;
  42. }
  43. for (int j = L->length; j >= i; j--)
  44. {
  45. L->data[j] = L->data[j-1];
  46. }
  47. L->data[i-1] = e;
  48. L->length ++;
  49. return true;
  50. }
  51. int main()
  52. {
  53. SqList L;//声明结构体变量L.
  54. InitList(&L);//初始化
  55. //将数据填入顺序表的过程省略
  56. //ListInsert(&L,3,3);//插入
  57. ListInsert1(&L,3,3);//改进版
  58. return 0;
  59. }

 删除操作✅

  1. bool ListDelete(SqList *L, int i, int *e)
  2. {
  3. if (i < 1 || i > L->length)
  4. {
  5. return false;
  6. }
  7. *e = L->data[i-1];//选择删除数组下标为i-1的值赋给e
  8. for (int j= i; j < L->length; j++)
  9. {
  10. //删除后,后面的元素依次前移
  11. L->data[j-1] = L->data[j];
  12. }
  13. L->length --;
  14. return true;
  15. }
  16. int main()
  17. {
  18. SqList L;
  19. int e = -1;
  20. if (ListDelete(&L,3,&e))
  21. {
  22. printf("已经删除第3个元素,删除元素值为%d\n",e);
  23. }
  24. else{
  25. printf("位序不合法,删除失败\n");
  26. }
  27. return 0;
  28. }

按值查找和按位查找 ✅

  1. //按位查找(静态分配的动态分配的都一样)
  2. ElemType GetElem(SqList L,int i)
  3. {
  4. return L.data[i-1];
  5. }
  6. //在顺序表L中查找第一个元素值等于e的元素,并返回其位置。
  7. int LocateElem(SqList L, ElemType e)
  8. {
  9. for (int i = 0; i < L.length; i++)
  10. {
  11. if (L.data[i] == e)
  12. {
  13. return i+1;//数组下标加一等于位序。
  14. }
  15. }
  16. return 0;//退出循环,说明查找失败。
  17. }
  18. //结构体的比较
  19. /*
  20. C语言里是不可以用 == 来比较两个结构体的,
  21. 需要依次对比各个分量来判断两个结构体是否相等。
  22. 假设有结构体a和b,有成员变量num和people
  23. */
  24. if (a.num == b.num && a.people == b.people)
  25. {
  26. printf("相等");
  27. }
  28. else
  29. {
  30. printf("不相等");
  31. }

单链表

定义结点及各名词的解释

  1. typedef struct LNode
  2.  //定义单链表结点类型,结构体名为LNode
  3. {
  4.     ElemType data; //数据域,ElemType是element type(“元素的类型”)的意思
  5.     struct LNode *next; //指针域  
  6. }LNode,*LinkList; 
  7. /**
  8. * LNode,*LinkList;两个都是是struct LNode的别名 
  9.  *   展开即:
  10. * typedef struct LNode LNode;
  11. * typedef struct LNode* LinkList;
  12. * *Linklist是当前这个结点的地址,这个结点里存储数据data和下一个结点的地址
  13. * linklist:线性表
  14. * LNode:逻辑结点,结点的意思
  15. * 为什么要用两个别名呢?因为LNode * 强调连接关系,LinkList是头结点的意思,LinkList强调整个链表,通常用头指针代表整个链表。
  16. 使用LNode时,必须用 LNode *X,使用LinkList时必须用 LinkList X;
  17. * *next指向该结点下一个结点的地址 
  18. */

因为数据结构讨论的是抽象的数据结构和算法,大多数不针对某一类型进行讨论。一种结构中元素的类型不一定是整型、字符型、浮点型或者用户自定义类型,为了不重复说明,使用过程中用“elemtype”代表所有可能的数据类型,简单明了的概括了整体。在算法中,除特别说明外,规定ElemType的默认是int型。

增加一个新结点:

注意别忘了头文件。

这是一个模板,申请新结点都这样做:

  1. struct LNode *p = (struct LNode *) malloc (sizeof (struct LNode));
  2. //在内存中申请一个结点所需的空间,并用指针P指向这个结点

【重要】

1、如果是涉及某一结点,就用 LNode * 函数名(LinkList L,…),如果是在整个链表上进行的操作,那么就是LinkList 函数名(LinkList L,…),例如:插入、查找值等都是LNode *函数名;头插法、尾插法建立单链表是LinkList 函数名()。

2、在涉及头部、尾部操作的时候,要注意一点:看看是否需要将插入的结点s p=s,L=s,什么的。

3、每次在对第i个进行操作时,要注意i是否合法,先判断合法性,再进行操作。

初始化单链表

  1. //初始化一个空的单链表(不带头结点)
  2. bool InitList(LinkList *L)
  3. {
  4. L = NULL;//空表,暂时还没有任何结点,这里L是链表名
  5. return true;
  6. }
  7. //初始化一个单链表(带头结点)
  8. bool InitList1(LinkList *L)
  9. {
  10. L = (LNode *) malloc (sizeof(LNode));//分配一个头结点,这里L是头结点。
  11. if (L == NULL)
  12. {
  13. //内存不足,分配失败,这里用的是==,其余地方用=
  14. return false;
  15. }
  16. L -> next = NULL; //头结点之后暂时还没有结点
  17. return true;
  18. }

头插法建立单链表

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

需要注意的是:

1、函数类型是LinkList。

2、每次新增加结点都需要动态申请一下结点。

3、一开始输入x的值是为插入找一个结束点,并不是真的值,后面建立好s结点【以后会再次输入x的值,覆盖掉。

4、注意L是一直指向头结点的,代表整个链表。

  1. LinkList List_HeadInsert(LinkList L)
  2. {
  3. int x;
  4. LNode *s;
  5. L= (LinkList) malloc(sizeof(LNode));
  6. L -> next = NULL;
  7. scanf("%d",&x);
  8. while (x <!= 999)
  9. {
  10. s = (LNode *)malloc(sizeof(LNode));
  11. s->data = x;
  12. s->next = L->next;
  13. L->next = s;
  14. scanf("%d",&x);
  15. }
  16. return L;
  17. }

尾插法建立单链表

优点是结点插入的顺序和链表的顺序一致。该方法是将新结点插入到当前链表的表尾,因此必须有一个尾指针r。

需要注意的是

1、因为是尾插法,所以需要另外设置一个尾指针r来指向尾部。所以有三个指针,一个头指针指向头部,表示整个链表。一个尾指针指示待插入的部分,一个待插入的结点指针s。

2、每次插入完成后,都需要令r=s。也就是让尾指针后移指向最后的结点。别忘了rear->next = NULL。

  1. LinkList List_RearInsert(LinkList L)
  2. {
  3. int x;
  4. LNode *s,*r=L;
  5. L = (LinkList)malloc(sizeof(LNode));
  6. L->next = NULL;
  7. scanf("%d",&x);
  8. while (x != 999)
  9. {
  10. s= (LinkList)malloc(sizeof(LNode));
  11. s->data = x;
  12. scanf("%d",&x);
  13. r->next = s;
  14. r=s;
  15. }
  16. r->next = NULL;
  17. return L;
  18. }

按序号查找结点值

在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。

需要注意的是:

1、因为是涉及结点,所以函数类型是LNode,如果用LNode那么函数名前面要加*。

2、记住该代码块的步骤!!!其他的都很类似。

3、按值查找:略。

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

插入结点(p从头开始遍历时)

不带头结点

  1. //第i个位置插入元素e,将要插入的结点称为s
  2. bool ListInsert(LinkList *L,int i,ElemType e)
  3. {
  4. if (i < 1)
  5. {
  6. return false;
  7. }
  8. if (i == 1)
  9. {
  10. //由于不带头结点,所以不存在所谓的第0个结点,因此i=1时需要特殊处理
  11. LNode *s = (LNode *) malloc (sizeof (LNode));
  12. s->data = e;
  13. s->next = L;//也就是在第一个结点的前面插入一个结点
  14. L = s;//头指针指向新结点
  15. return true;
  16. }
  17. LNode *p;//指针p当前扫描到的结点
  18. int j = 1;//当前p指向的是第几个结点
  19. p = L; // L指向头结点,头结点是第0个结点(存数据)
  20. while (p != NULL && j < i-1)
  21. {
  22. //循环找到第i-1个结点
  23. p=p->next;
  24. j ++;
  25. }
  26. if (p == NULL)
  27. {
  28. return false;
  29. }
  30. LNode *s = (LNode *) malloc(sizeof(LNode));
  31. s->data = e;
  32. s->next = p->next;
  33. p->next = s;
  34. return true;
  35. }

插入结点(p指向中间某结点)略

删除结点

1、查找结点,调用前面的函数。

2、不要忘了free结点。

  1. p = GetElem(L,i-1);
  2. q=p->next;
  3. p->next = q ->next;
  4. free(q);

双链表 

和单链表的操作几乎一致。略。

定义:

  1. typedef int ElemType;
  2. //双链表定义
  3. typedef struct DNode
  4. {
  5. ElemType data;
  6. struct DNode *prior, *next;
  7. }DNode,*DLinkList;

删除和插入操作与单链表的不同。注意!!!(希望每次有要插入删除的时候,自己在纸上画一画)。

循环单、双链表

静态链表

  1. #define MaxSize 50
  2. typedef int ElemType;
  3. typedef struct
  4. {
  5. ElemType data;
  6. int next;//不是指针类型,是下一个元素的数组下标。
  7. }SLinkList[MaxSize];

结束标准:next =-1.

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

闽ICP备14008679号