备注:c语言中没有bool,我下面出现的bool是自己定义的,相关定义如下
//需要包含的头文件
#include"stdio.h"
#include"stdlib.h"//malloc函数
//定义静态顺序表的最大长度和动态顺序表的初始默认最大容量
#define Maxsize 10//静态顺序表的最大长度(容量)
#define Initsize 10//动态顺序表的初始默认最大容量
//自定义c语言的bool变量
#define bool char
#define true 1
#define false 0
//定义单个元素的数据类型
typedef int ElemType;
1.存储方式:2种,分别是静态分配和动态分配,二者的结构体定义如下。
//1.静态分配
typedef struct{
ElemType data[Maxsize];//用静态数组存放当前数据元素
int length;//顺序表当前长度
}Sqlist;
//2.动态分配
typedef struct{
ElemType *data;//用动态指针分配数组
int length;//当前长度
int maxsize;//最大容量
}Sqelist;
2.初始化操作:
初始化操作就是初始化(上图的两种分配方式的结构体所示的)变量,给变量赋初始值,静态分配的有2个变量(一个数组,一个变量length),因为当前顺序表还未存储任何元素故,静态存储方式的顺序表初始装数据的数组的操作可以省略(要初始也可以不过会占用内存,增加时间复杂度)。而动态分配的三个变量都要初始化,尤其是data是一个指针,不初始化的话就是野指针,这样就很麻烦。下面的这段代码是1.初始表长:(L->length=0;) 2.初始最大容量:(L->maxsize=Initsize)也就是令当前的最大容量为最大,Initsize我之前已经#define过了,设定的是50。
//1.静态分配的初始化
bool InitSqlist(Sqlist *L){
L->length=0;//初始化表长
//初始化数据元素(静态分配的这步可省略!因为当前表长为0,最好还是不用初始化数据元素,会耗费时间复杂度)
//int i;
//for(i=0;i<MAXSIZE;i++){
// L->data[i]=0;
//}
}
//2.动态分配初始化
bool InitSqelist(Sqelist *L){
L->length=0;//初始化表长
//初始化最大容量
L->maxsize=Initsize;
//初始化动态分配顺序表的数据元素:当前*data未分配空间是野指针,需要为它分配一段连续的存储空间
L->data=(ElemType*)malloc(sizeof(ElemType)*Initsize);
return true;
}
3.顺序表的输出
动态的输出和静态的输出都是一样的,首先这是一个输出操作不设计修改或者交换表中的元素,所以函数体只需要void就行了,函数体传入参数的是整条链表的元素。算法思想大概就是,1.你需要判空:如果L的表长为0就表示表内没有元素(L.length==0) ,如果表长不为0就表示有元素直接输出就行了。2.不管是静态还是动态,存入的数据都是条的也就是类似一个数组,想要输出数组种的元素就用一个for循环就行了。
void Printlist(Sqlist L/Sqelist L){
//判空:如果当前表长为0,返回false(0)
if(L.length==0){
printf("当前顺序表为空!");
//return false;
}
//for输出当前表内元素
int i;
printf("Sqlist为:\n");
for(i=0;i<L.length;i++){
printf("->%d",L.data[i]);
// return true;
}
printf("\n");
}
4.顺序表的按位插入:在顺序表的第i个位置插入元素e
1.静态的插入:插入操作首先需要判断插入位置i的合法性,合法性主要是位置的合法性和大小的合法性。如果要插入的位置比这里静态定义的表长length还大的话或者是i<1,插入位置是负数都不行(数组下标最小为0)。判断完插入位置的合法性后,还需要判断当前存储空间是否已满,二者均符合就可以开始正式的插入操作。插入操作的本质是把要插入位置上的元素和它后面的所有元素都后移一位,然后把需要插入的元素e赋给该位置i,最后因为多了一个元素,所以链表的长度需要加1。
//1.静态分配的插入
bool InsertSqlist(Sqlist *L,int i,ElemType e){
//int i;
//定义判断插入位置是否合法时表示数组下标的i
int j;
//定义插入移动元素时表示数组下标的j
//[1]判断插入操作是否合法:1.插入位置是否正确,2.顺序表是否已经满了
//1.插入位置不正确
if(i<1||i>L->length+1){
printf("当前插入位置是无效的,插入操作不合法!");
return false;
}
//2.顺序表已经满了
if(L->length>=Maxsize){
printf("当前顺序表已满!");
return false;
}
//[2]元素合法则将所有元素后移一位(空出待插入的位置):前一位赋值给后一位
//将第i位和第i位后面的所有元素后移一位
for(j=L->length;j>=i;j--){
L->data[j]=L->data[j-1];//后一位(j)等于前一位(j-1),空出一个位置
}
//[3]将新元素e插入待插入的位置(i)
L->data[i-1]=e;
//[4]表长加1,返回true表示插入成功
L->length++;
return true;
}
2.动态的插入:动态的插入和静态插入本质同理,只是动态的插入在判断表的容量上是否满时不同,静态的是(L->length>=Maxsize)时候表示满,动态是(L->length>=L->maxsize)时候表示满,其中maxsize在动态初始话时候已经设置为刚开始定义的#define Initsize 10 ,但二者只是写法不同,表示的含义上是相同的,都是要插入的时候不能当前表的容量已满。
//2.动态分配的插入
bool InsertSqelist(Sqelist *L,int i,ElemType e){
// int i;//定义判断插入位置是否合法时表示数组下标的i
int j;//定义插入移动元素时表示数组下标的j
//[1]判断插入操作是否合法:1.插入位置是否正确,2.顺序表是否已经满了
//1.插入位置不正确
if(i<1||i>L->length+1){
printf("当前插入位置是无效的,插入操作不合法!");
return false;
}
//2.顺序表已经满了(这里的判断依据与静态分配的顺序表Maxsize不同,是L->maxsize,是动态分配顺序表的最大容量,它的值在初始化里面写了,是Initsize)
if(L->length>=L->maxsize){
printf("当前顺序表已满!");
return false;
}
//[2]元素合法则将所有元素后移一位(空出待插入的位置):前一位赋值给后一位
//将第i位和第i位后面的所有元素后移一位
for(j=L->length;j>=i;j--){
L->data[j]=L->data[j-1];//后一位(j)等于前一位(j-1),空出一个位置
}
//[3]将新元素e插入待插入的位置(i)
L->data[i-1]=e;
//[4]表长加1,返回true表示插入成功
L->length++;
return true;
}
5.顺序表按位删除:将顺序表L的第i位删除,并把删除元素的值返回给e
按位删除的操作,静态和动态的都是一样的,该算法本质上和上面的插入差不多就是先判断删除位置i是否合法,合法就先把那个元素的值赋给e,然后将i前所有的元素往前移一位就行了,往前移动一位就少一个元素,长度就减1。
bool DleteSqlist(Sqlist *L/Sqelist *L,int i,ElemType *e){
//[1]判断操作的合法性
//1.插入位置不正确
if(i<1||i>L->length+1){
printf("当前删除位置是无效的,删除操作不合法!");
return false;
}
//2.顺序表是否为空
if(L->length<=0){
printf("当前顺序表为空,删除操作不合法!");
return false;
}
//[2]合法就先将待删除的元素赋给e
*e=L->data[i-1];
//[3]将第i位元素及后面的元素都往前移动一位
int j;
for(j=i;j<L->length;j++){
L->data[j-1]=L->data[j];
}
//[4]将表长减一
L->length--;
return true;
}
6.顺序表的按值查找:元素按位查找并返回位序e
按值查找,静态和动态分配也是一样的,都是用for循环遍历所有元素,if-else语句判断,如果和e的值一样就返回位序号i+1即可(数组首下标是0,所以第i号元素的数组下标是i+1)。
bool Sqlistlocated(Sqlist L/Sqelist L,ElemType e){
int i;
for(i=0;i<L.length;i++){
if(L.data[i]==e){
return i+1;//i+1就是所在的数组下标
}
else printf("该值不存在!\n");
}
return false;
}
7.顺序表的动态扩容(只有动态存储的才有)
大致就是有4步,第一步就是首先重新生成一个指针,第二步就是给这个指针赋比原来大+len或者*len倍的空间(这里是+len),然后就是转移数据,用一个for循环把原先data[]里面的数据弄到新开的p[]里面,转移完了需要把原先的最大长度和容量也改了。改完就扩容完成了,你这空间用完了还需要销毁最后一步就是free(p)就行了。
bool IncreaseSqeslist(Sqelist *L,int len){
//[1]生成指向原来存储空间的指针
ElemType *p=L->data;
//[2]为顺序表开辟一块更大的空间
L->data=(ElemType *)malloc(sizeof(ElemType)*(L->maxsize+len));//l->maxsize是原来的最大容量,扩容后加上len
//[3]转移数据,修改顺序表的最大长度,其值+len(扩容后需要转移数据)
int i;
//转移数据
for(i=0;i<L->length;i++){
L->data[i]=p[i];//p[i]相当于*p+i
}
//修改顺序表的最大长度
L->maxsize +=len;//L->maxsize= L->maxsize+len
//[4]释放原来的存储空间,如果扩容成功return true
free(p);
return true;
}