当前位置:   article > 正文

【数据结构】顺序表(C语言)_顺序表c语言

顺序表c语言

顺序表

一、顺序表的基本概念

了解顺序表,首先要了解线性表的概念。

线性表:对于一组拥有N个数据的线性表:除了首元素a0无直接前驱,以及尾元素无直接后继,其它任何一个元素ai,有且仅有一个直接前驱a(i-1),有且只有一个直接后继。

满足这种数学关系的一组数据,其中的数据是一个挨着一个的,我们称之为一对一关系,是线性的。如果数据之间的关系不是一对一的,就是非线性的。

顺序表:顺序存储的线性表。

顺序表就是将数据存储到一片连续的内存中,在C语言环境下,它可以是具名的栈数组,也可以是匿名的堆数组。

顺序表的存储方式不仅仅要提供数据的存储空间,而且必须要能体现数据之间的逻辑的关系,比如,如果甲乙丙依次排队,乙前面是甲,后面是丙,那么存储位置必须按照这样的逻辑把甲存放在乙后面,把丙排在乙后面。

当采用顺序存储的方式存储数据时,唯一能够表达数据本身逻辑关系的就是存储位置。

通俗地说,顺序表的存储是连续的,即必须从首元素的位置开始按顺序往后存储,不能跳过中间的位置往后存储。

注意:上图只是为了让读者更好的理解顺序表而举的一个例子,并不是说其元素也要按照大小顺序有序存放。顺序表并没有要求存放的元素应该是有序的,它存放的元素可以是有序的,也可以是无序的,关于这一点我相信不少初学者都会有疑惑,所以笔者在这里做个特别说明。

二、顺序表的基本操作

  1. 顺序表的设计

为方便对顺序表进行管理,需要一个专门管理顺序表的结构体,该结构体中一般包含以下几个成员:

(1)顺序表总容量
(2)顺序表当前尾元素下标
(3)顺序表指针

以下是管理结构体示例代码:

  1. typedef struct SequenceList{
  2.     int cap;//顺序表容量,类似于数组的容量
  3.     int last;//当前尾元素下标
  4.     int *data;//存放顺序表数据,以整型数据为例
  5. }SqList;
  6. SqList *SL = NULL;//定义一个顺序表指针
  1. 顺序表的初始化

建立一个空的、不包含元素的顺序表,需要初始化顺序表的容量,尾元素下标,申请分配存储数据的空间,由于一开始顺序表没有元素,尾元素下标置位-1;以下是初始化顺序表的示例代码,以传参的方式初始化顺序表。

  1. //成功返回true,失败返回false
  2. bool SqList_Init(SqList* &SL,int cap)
  3. {
  4.     if(SL = (SqList *)malloc(sizeof(SqList)) == NULL)
  5.         return false;//malloc分配空间失败,返回false
  6.     //内存分配成功
  7.     SL->cap = cap;
  8.     SL->last = -1;
  9.    
  10.     //cap为顺序表元素最大总数,cap * sizeof(int) 为需要分配的存储空间
  11.     if(SL->data = (int *)malloc(cap * sizeof(int)) == NULL)
  12.     {//SL->data申请分配空间失败,即顺序表初始化失败,需要释放SL,并返回false
  13.         free(SL);
  14.         SL = NULL;//防止SL成为野指针
  15.         return false;
  16.     }
  17.     return true
  18. }
  1. 判空

  1. bool SqList_IsEmpty(SqList* SL)
  2. {
  3.     if(SL == NULL || (SL->last == -1))
  4.         return true;
  5.     return false;   
  6. }
  1. 增删节点

在顺序表中增加一个数据,可以有多种方式,可以在原数组的头部、尾部或者按下标增删数据,也可以按值删除数据。以下分别为三种方式的增删数据示例代码,以及按值删除数据的代码。

  1. //从头部增加
  2. bool SqList_Head_Add(SqList* &SL,int data)
  3. {
  4.     if(SL->last == (SL->cap-1))//顺序表已满
  5.         return false;
  6.     if(SL->last == -1)//顺序表为空
  7.     {
  8.         SL->data[0] = data;
  9.         SL->last++;
  10.         return true;
  11.     }
  12.     //顺序表不为空
  13.     int i;
  14.     for(i = last;i>=0;i--)//将原有数据全部往后挪一位
  15.     {
  16.         SL->data[i+1] = SL->data[i];
  17.     }
  18.     SL->data[0] = data;
  19.     SL->last++;
  20.     return true;
  21. }
  22. //从头部删除
  23. bool SqList_Head_Dele(SqList* &SL)
  24. {
  25.     if(SL->last == -1)//顺序表为空
  26. return false;
  27.    
  28.     //不为空
  29.     //1.只有一个元素,直接让尾元素下标自减
  30.     if(SL->last == 0)
  31.     {
  32.         SL->last--;
  33.         return true;;
  34.     }
  35.    
  36.     //2.两个及两个以上的元素
  37.     int i;
  38.     for(i = 0;i<SL->last;i++)//直接将原有数据全部往前挪一位
  39.     {
  40.         SL->data[i] = SL->data[i+1];
  41.     }
  42.     SL->last--;
  43.     return true;
  44. }
  45. //从尾部增加
  46. bool SqList_Tail_Add(SqList* &SL,int data)
  47. {
  48.     if(SL->last == (SL->cap-1))//顺序表已满
  49. return false;
  50.     SL->last++;
  51.     SL->data[last] = data;
  52.     return true;
  53. }
  54. //从尾部删除
  55. bool SqList_Tail_Dele(SqList* &SL)
  56. {
  57.     if(SL->last == -1)//顺序表为空
  58. return false;
  59.     SL->last--;
  60.     return true;
  61. }
  62. //从任意位置增加,参数 pos 为指定增加元素的位置
  63. bool SqList_Add(SqList* &SL,int data,int pos)
  64. {
  65.     if(SL->last == (SL->cap-1))//顺序表已满
  66. return false;
  67.    
  68.     //插入位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
  69.     if(pos > (SL->cap-1) || pos > (SL->last+1) || pos < 0)
  70.         return false;
  71.    
  72.     //在尾元素位置以及尾元素之前的任意位置插入元素
  73.     if(pos <= SL->last)
  74.     {
  75.         int i;
  76.         for(i = SL->last;i >= pos;i--)//第pos个元素到尾元素,整体往后移一位
  77.             SL->data[i+1] = SL->dara[i];
  78.         SL->data[pos] = data;//元素后移之后,在第pos个元素的位置增加新元素
  79.         SL->last++;//增加新元素后,尾元素下标加1
  80.         return true;
  81.     }
  82.    
  83.     //在尾元素之后插入新元素,即 pos = SL->last + 1
  84.     if(pos > SL->last)
  85.     {
  86.         SL->data[pos] = data;//直接在第pos个位置增加新元素
  87.         SL->last++;//尾元素加1
  88.         return true;
  89.     }       
  90. }
  91. //从任意位置删除,参数 pos 为删除指定元素的位置
  92. bool SqList_Dele(SqList* &SL,int pos)
  93. {
  94.     if(SL->last == -1)//顺序表为空
  95. return false;
  96.    
  97.     //删除元素的位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
  98. if(pos > (SL->cap-1) || pos > SL->last || pos < 0)
  99. return false;
  100.    
  101.     //在尾元素之前的位置删除元素,不包括尾元素的位置
  102.     if(pos < LS->last)
  103.     {
  104.         int i;
  105.         for(i = pos;i < SL->last;i++)//从第pos位开始(包括第pos位),整体往前移动一位
  106.             SL->data[i] = SL->data[i+1];
  107.         SL->last--;//尾元素下标减1
  108.         return true;
  109.     }
  110.    
  111.     //删除的元素为尾元素
  112.     if(pos == LS->last)
  113.     {
  114.         SL->last--;//直接让尾元素下标减1
  115.         return true;
  116.     }
  117. }
  118. //按值删除指定元素
  119. bool SqList_Del_By_Val(SqList* &SL,int val)
  120. {
  121.     if(SL->last == -1)//顺序表为空
  122. return false;
  123.     int i,cnt = 0,index = -1;
  124.     for(i = 0;i <= SL->last;i++)
  125.     {
  126.         if(SL->data[i] == val)
  127.         {
  128.             index = i;//记录要删除的值的下标
  129.             cnt++;//有可能存在多个相同的val,记录有多少个Val
  130.         }
  131.     }
  132.     if(index == -1)//没有找到值为val的元素
  133.         return false;
  134.    
  135.     SqList_Dele(SL,index);//按下标删除指定元素
  136.     while(--cnt)//如果有多个相同值,递归调用自己删除值为Val的元素
  137.     {
  138.         SqList_Del_By_Val(SL,val);
  139.     }
  140.     return true;
  141. }
  1. 销毁顺序表

顺序表不再需要使用的时候,应该释放其所占用的空间,这也叫顺序表的销毁。

  1. void SqList_Destroy(SqList* &SL)
  2. {
  3.     if(SL == NULL)//顺序表为空
  4.         return
  5.     //不为空
  6.     //怎么初始化的,就要怎么释放
  7.     free(SL->data);
  8.     SL->data = NULL;
  9.    
  10.     free(SL);
  11.     SL = NULL;
  12. }

三、顺序表的优缺点

由于顺序存储的逻辑关系是用物理位置来表达的,因此增删数据需要成片的移动数据,这对数据节点的增删操作很不友好。当然,优缺点也有其优点,总结顺序表的特点如下:

优点:

(1)不需要多余的信息来记录数据之间的关系,存储密度高
(2)所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数据,可以跟数组一样直接访问第i个节点,如顺序表的第i个节点数据为SL->daata[i]。

缺点:

(1)插入、删除时需要保持数据的线性逻辑关系,需要成片地移动数据
(2)当数据节点较多时,需要移动一大片连续内存空间
(3)当频繁操作顺序表的数据节点时,内存的释放和分配不灵活

四、关于传引用

小伙伴们应该都注意到了,笔者在函数传参时使用了bool SqList_Init(SqList* &SL,int cap)这样形式传递指针参数,初学者可能对此感到困惑,我就不再这里做详细说明了,感兴趣的小伙伴可以到以下链接学习解惑:https://blog.csdn.net/weixin_46637351/article/details/126046567

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

闽ICP备14008679号