赞
踩
了解顺序表,首先要了解线性表的概念。
线性表:对于一组拥有N个数据的线性表:除了首元素a0无直接前驱,以及尾元素无直接后继,其它任何一个元素ai,有且仅有一个直接前驱a(i-1),有且只有一个直接后继。
满足这种数学关系的一组数据,其中的数据是一个挨着一个的,我们称之为一对一关系,是线性的。如果数据之间的关系不是一对一的,就是非线性的。
顺序表:顺序存储的线性表。
顺序表就是将数据存储到一片连续的内存中,在C语言环境下,它可以是具名的栈数组,也可以是匿名的堆数组。
顺序表的存储方式不仅仅要提供数据的存储空间,而且必须要能体现数据之间的逻辑的关系,比如,如果甲乙丙依次排队,乙前面是甲,后面是丙,那么存储位置必须按照这样的逻辑把甲存放在乙后面,把丙排在乙后面。
当采用顺序存储的方式存储数据时,唯一能够表达数据本身逻辑关系的就是存储位置。
通俗地说,顺序表的存储是连续的,即必须从首元素的位置开始按顺序往后存储,不能跳过中间的位置往后存储。
注意:上图只是为了让读者更好的理解顺序表而举的一个例子,并不是说其元素也要按照大小顺序有序存放。顺序表并没有要求存放的元素应该是有序的,它存放的元素可以是有序的,也可以是无序的,关于这一点我相信不少初学者都会有疑惑,所以笔者在这里做个特别说明。
为方便对顺序表进行管理,需要一个专门管理顺序表的结构体,该结构体中一般包含以下几个成员:
以下是管理结构体示例代码:
- typedef struct SequenceList{
- int cap;//顺序表容量,类似于数组的容量
- int last;//当前尾元素下标
- int *data;//存放顺序表数据,以整型数据为例
- }SqList;
- SqList *SL = NULL;//定义一个顺序表指针
建立一个空的、不包含元素的顺序表,需要初始化顺序表的容量,尾元素下标,申请分配存储数据的空间,由于一开始顺序表没有元素,尾元素下标置位-1;以下是初始化顺序表的示例代码,以传参的方式初始化顺序表。
- //成功返回true,失败返回false
- bool SqList_Init(SqList* &SL,int cap)
- {
- if(SL = (SqList *)malloc(sizeof(SqList)) == NULL)
- return false;//malloc分配空间失败,返回false
- //内存分配成功
- SL->cap = cap;
- SL->last = -1;
-
- //cap为顺序表元素最大总数,cap * sizeof(int) 为需要分配的存储空间
- if(SL->data = (int *)malloc(cap * sizeof(int)) == NULL)
- {//SL->data申请分配空间失败,即顺序表初始化失败,需要释放SL,并返回false
- free(SL);
- SL = NULL;//防止SL成为野指针
- return false;
- }
- return true;
- }
- bool SqList_IsEmpty(SqList* SL)
- {
- if(SL == NULL || (SL->last == -1))
- return true;
- return false;
- }
在顺序表中增加一个数据,可以有多种方式,可以在原数组的头部、尾部或者按下标增删数据,也可以按值删除数据。以下分别为三种方式的增删数据示例代码,以及按值删除数据的代码。
- //从头部增加
- bool SqList_Head_Add(SqList* &SL,int data)
- {
- if(SL->last == (SL->cap-1))//顺序表已满
- return false;
- if(SL->last == -1)//顺序表为空
- {
- SL->data[0] = data;
- SL->last++;
- return true;
- }
- //顺序表不为空
- int i;
- for(i = last;i>=0;i--)//将原有数据全部往后挪一位
- {
- SL->data[i+1] = SL->data[i];
- }
- SL->data[0] = data;
- SL->last++;
- return true;
- }
-
- //从头部删除
- bool SqList_Head_Dele(SqList* &SL)
- {
- if(SL->last == -1)//顺序表为空
- return false;
-
- //不为空
- //1.只有一个元素,直接让尾元素下标自减
- if(SL->last == 0)
- {
- SL->last--;
- return true;;
- }
-
- //2.两个及两个以上的元素
- int i;
- for(i = 0;i<SL->last;i++)//直接将原有数据全部往前挪一位
- {
- SL->data[i] = SL->data[i+1];
- }
- SL->last--;
- return true;
- }
-
- //从尾部增加
- bool SqList_Tail_Add(SqList* &SL,int data)
- {
- if(SL->last == (SL->cap-1))//顺序表已满
- return false;
- SL->last++;
- SL->data[last] = data;
- return true;
- }
-
- //从尾部删除
- bool SqList_Tail_Dele(SqList* &SL)
- {
- if(SL->last == -1)//顺序表为空
- return false;
- SL->last--;
- return true;
- }
-
- //从任意位置增加,参数 pos 为指定增加元素的位置
- bool SqList_Add(SqList* &SL,int data,int pos)
- {
- if(SL->last == (SL->cap-1))//顺序表已满
- return false;
-
- //插入位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
- if(pos > (SL->cap-1) || pos > (SL->last+1) || pos < 0)
- return false;
-
- //在尾元素位置以及尾元素之前的任意位置插入元素
- if(pos <= SL->last)
- {
- int i;
- for(i = SL->last;i >= pos;i--)//第pos个元素到尾元素,整体往后移一位
- SL->data[i+1] = SL->dara[i];
- SL->data[pos] = data;//元素后移之后,在第pos个元素的位置增加新元素
- SL->last++;//增加新元素后,尾元素下标加1
- return true;
- }
-
- //在尾元素之后插入新元素,即 pos = SL->last + 1
- if(pos > SL->last)
- {
- SL->data[pos] = data;//直接在第pos个位置增加新元素
- SL->last++;//尾元素加1
- return true;
- }
- }
-
- //从任意位置删除,参数 pos 为删除指定元素的位置
- bool SqList_Dele(SqList* &SL,int pos)
- {
- if(SL->last == -1)//顺序表为空
- return false;
-
- //删除元素的位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
- if(pos > (SL->cap-1) || pos > SL->last || pos < 0)
- return false;
-
- //在尾元素之前的位置删除元素,不包括尾元素的位置
- if(pos < LS->last)
- {
- int i;
- for(i = pos;i < SL->last;i++)//从第pos位开始(包括第pos位),整体往前移动一位
- SL->data[i] = SL->data[i+1];
- SL->last--;//尾元素下标减1
- return true;
- }
-
- //删除的元素为尾元素
- if(pos == LS->last)
- {
- SL->last--;//直接让尾元素下标减1
- return true;
- }
- }
-
- //按值删除指定元素
- bool SqList_Del_By_Val(SqList* &SL,int val)
- {
- if(SL->last == -1)//顺序表为空
- return false;
- int i,cnt = 0,index = -1;
- for(i = 0;i <= SL->last;i++)
- {
- if(SL->data[i] == val)
- {
- index = i;//记录要删除的值的下标
- cnt++;//有可能存在多个相同的val,记录有多少个Val
- }
- }
- if(index == -1)//没有找到值为val的元素
- return false;
-
- SqList_Dele(SL,index);//按下标删除指定元素
- while(--cnt)//如果有多个相同值,递归调用自己删除值为Val的元素
- {
- SqList_Del_By_Val(SL,val);
- }
- return true;
- }
顺序表不再需要使用的时候,应该释放其所占用的空间,这也叫顺序表的销毁。
- void SqList_Destroy(SqList* &SL)
- {
- if(SL == NULL)//顺序表为空
- return;
-
- //不为空
- //怎么初始化的,就要怎么释放
- free(SL->data);
- SL->data = NULL;
-
- free(SL);
- SL = NULL;
-
- }
由于顺序存储的逻辑关系是用物理位置来表达的,因此增删数据需要成片的移动数据,这对数据节点的增删操作很不友好。当然,优缺点也有其优点,总结顺序表的特点如下:
优点:
缺点:
小伙伴们应该都注意到了,笔者在函数传参时使用了bool SqList_Init(SqList* &SL,int cap)这样形式传递指针参数,初学者可能对此感到困惑,我就不再这里做详细说明了,感兴趣的小伙伴可以到以下链接学习解惑:https://blog.csdn.net/weixin_46637351/article/details/126046567
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。