线性表
一、线性表定义
线性表是最常见且简单的的一种线性结构。
由n个(n为线性表的长度,当n=0时,线性表为空表)数据特性相同的元素构成的有限序列称为线性表。
二、线性表特点
对于非空线性表或者线性结构,其特点有:
**
1).表中数据元素个数有限,数据类型同类,有先后次序。
2).表中都是数据元素,每个元素都是单个元素。
3). 除第一个数据元素外,其余元素都有前驱
4). 除最后一个数据元素外,其余元素都有后继
(前驱和后继:比如排队,在你前面的就可以称为前驱,在你后面的,称为后继)**
三、基本操作
InitList(&L):初始化线性表,构造一个空线性表。
Length(L):求表的长度,返回线性表L的长度,即L中数据元素个数。
LocateElem(L,e):按值查找。在L表中查找e元素。
GetElem(L,i):按位查找。在L表中查找在第i个位置的元素。
ListInsert(&L,i,e):插入元素。在L表中的第i个位置插入e元素。
List_Delete(&L,i,&e):删除线性表中第i个元素,并把删除的元素返回给e。
PrintList(L):输出操作。按从前后顺序输出L表中的元素值。
Empty(L):判空操作。若L为空表,则返回ture.否则返回false。
DestroyList(&L):销毁表操作。销毁线性表病,并释放表占用的空间。
四、顺序存储线性表
1.顺序表的定义
线性表的顺序存储又称顺序表,用一组地址连续存储单元依次存储线性表中数据元素。
存储结构:逻辑上相邻的两个元素在物理位置上也相同。【我的理解:在一个记录中(比如一个表中,存有有不同学生信息,含有姓名,身份证号等信息,这个称为记录),如果这个表按照姓名英文大小写排序下去的,那么存在线性表中,会安排相邻地址存储这些数据】
2.顺序表特点
a. 顺序表可以随机访问,并且在时间O(1)内找到指定的元素。
b. 顺序表的存储密度高,每个节点只存储数据元素。
c. 顺序表逻辑上相邻的元素,物理上也相邻,所以插入和删除需要移动大量元素。
d. 扩展容量不方便。
3.静态顺序存储定义线性表
1).静态顺序存储定义线性表的思想
使用静态数组实现。
其实就是存储在数组中。
大小一旦确定就无法改变。
2).静态顺序存储定义线性表的结构体
#define max_size 50 //定义数组最大存储容量
typedef struct{ //建立结构体--静态顺序存储的线性表
ElemType elem[max_size]; //定义一个数组存储线性表数据
int lenght; //线性表当前长度
}SqList;
对 ElemType理解:
ElemType是数据结构的书上为了说明问题而用的一个词。它是element type(“元素的类型”)的简化体。 因为数据结构是讨论抽象的数据结构和算法的,一种结构中元素的类型不一定是整型、字符型、浮点型或者用户自定义类型,为了不重复说明,使用过程中用“elemtype”代表所有可能的数据类型,简单明了的概括了整体。在算法中,除特别说明外,规定ElemType的默认是int型。
3).静态定义线性表的基本操作
代码中 &L 和 L 的理解:当我们要修改表中值时,就要带地址符号,实际使用指针也是一样的。【我的理解:在函数模块中,执行过程是临时存储的,执行完函数之后,该函数模块就自动释放空间,数据就会被清除,如果只是传入了数据,对数据进行改动,等操作结束后,原本的数据是不会被改动的。相反,传入带地址的数据,对数据进行改动,执行结束后,数据才会得到改动,因为函数中对数据的改动是改动地址中的数据。总结:要对数据进行改动就必须传入数据地址】
InitList(&L):初始化线性表,构造一个空线性表。
bool InitList(SqList &L){ //构建一个空线性表
L.lenght = 0; //空表长度为0
return 1;
}
LocateElem(L,e):按值查找。在L表中查找e元素。
思想:从表头开始依次往后查询元素。
int LocateElem(SqList L,ElemType e){
int i; //定义循环变量
for(i = 0;i < L.length;i++)//从表头开始查找
if(L.elem[i] == e) //判断是否找到
return i+1; //查找成功返回其位序i+1
return 0;//循环结束表示查找失败
}
算法分析:
最好的情况:元素在表头,时间复杂度为O(1)。
最坏情况下:元素在表尾,需要比较n次,时间复杂度为O(n)。
平均情况:假设pi是查找元素在i个位置上的概率,则在长度为n的线性表中查找值为e的元素所需要比较的平均次数为:
线性表按值查找算法平均时间复杂度为O(n)。
ListInsert(&L,i,e):插入元素。在L表中的第i(位序)个位置插入e元素。
思想:找到第i个位置,把第i个位置以后的元素依次后移,空出位置角标在i-1处,把元素放进角标i-1的位置中。
bool ListInsert(SqList &L,int i,ElemType e)
{
int j;
//i的合法值为1 <= i <= L.lenght + 1
if((i < 1) || (i > L.lenght + 1)) //判断i值是否在有效范围
return false; //i值无效返回错误
if(L.lenght >= max_size) //判断表是否存满
return false; //存满返回错误,不能存储
for(j=L.lenght;j >= i;j--) //把从第i个元素以后的元素都后移一位
{
L.elem[j]=L.elem[j-1]; //往后移动一个元素
}
L.elem[i-1] = e; //把e中元素赋给空出来的位置
L.lenght++;//存储成功,表的长度加1
return true; //操作结束返回true
}
算法分析:
最好情况:在表尾直接插入,其他元素不移动,时间复杂度为O(1)。
最坏情况:在表头插入,所有元素都要移动,假设移动语句执行n次,则时间复杂度为O(n)。
平均情况:假设pi是在第 i 个位置插入元素的概率,则长度为n的线性表中插入一个节点时,所需移动节点的平均次数为:
所以,线性表的插入算法时间复杂度为O(n)。
List_Delete(&L,i,&e):删除线性表中第i个元素,并把删除的元素返回给e。
思想:在i值有效情况下(1<= i <=L.lenght),把elem[i-1]赋值给e再依次把第i个元素之后的元素依次往前移动一位。
bool ListDelete(SqList &L,int i,ElemType &e)
{
int j;
if((i < 1) || (i > L.lenght)) //判断i值是否在有效范围
return false; //i值无效返回错误
e = L.elem[i-1]; //把角标i-1(就是第i个元素)的元素赋值给e
for(j=i-1;j<L.lenght-1;j++) //把角标i-1之后的元素依次向前移动一位
{
L.elem[j]=L.elem[j+1];
}
return true; //操作结束返回true
}
算法分析:
最好情况:删除表尾,其他元素不移动,时间复杂度为O(1)。
最坏情况:删除表头元素,所有元素都要移动,假设移动语句执行n次,则时间复杂度为O(n)。
平均情况:假设pi是在第 i 个位置删除元素的概率,则长度为n的线性表中插入一个节点时,所需移动节点的平均次数为:
所以,线性表的删除算法时间复杂度为O(n)。
Empty(L):判空操作。若L为空表,则返回ture.否则返回false。
查看表长度即可。
bool Empty(L)
{
if(L.length == 0)
return 0;//返回0代表为空表
else
return 1;//返回1代表为非空表
}
4.动态顺序存储定义线性表
1).动态顺序存储定义线性表的思想
数组动态分配,利用malloc函数动态分配空间。使用realloc函数扩展空间,使用free函数释放空间。
2).动态顺序存储定义线性表
typedef struct{ //建立结构体--动态顺序存储的线性表
ElemType *elem; //定义指针
int lenght; //当前长度
int listsize; //当前分配的存储容量
}SqList;
lenght_DT 变量是为了方便查看存了多少个数据元素。
listsize 变量是查看当前可以存储多少数据元素。
3).动态顺序存储定义线性表的基本操作
InitList(&L):初始化线性表,构造一个空线性表。
#define OVERFLOW 0
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
bool InitList(SqList &L){ //构建一个空线性表
L.elem = (ElemType *) malloc(LIST_INIT_SIZE * sizeof(ElemType));
if(!L.elem) exit(OVERFLOW); //存储分配失败
L.lenght = 0; //空表长度为0
L.listsize = LIST_INIT_SIZE; //初始存储容量
return 1;
}
下面链接是帮助malloc函数的理解:
https://blog.csdn.net/Jack11998/article/details/107341507?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
exit函数的理解:
函数名: exit()
所在头文件:stdlib.h
功 能: 关闭所有文件,终止正在执行的进程。
exit(1)表示异常退出.这个1是返回给操作系统的。
exit(x)(x不为0)都表示异常退出
exit(0)表示正常退出。
理解来自链接中的文章。
https://blog.csdn.net/Rex_WUST/article/details/88372481?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
LocateElem(L,e):按值查找。在L表中查找e元素。
int LocateElem(SqList L,ElemType e)
{
ElemType *p,*q; //定义两个指针变量
int i=0;
q = L.elem + L.lenght-1; //(l.elme 是首地址)使指针q指向表尾
for(p = L.elem;p <= q;p++) //从表头开始查找
{
i++;
if(*p == e)
return i; //查到元素,返回元素位序
}
return 0; //没查到,返回0
}
【动态数组的查找元素代码,也可以使用静态数组值的查找的代码,这里是我自己写的,亲测,无误】
ListInsert(&L,i,e):插入元素。在L表中的第i个位置插入e元素。
bool ListInsert(SqList &L,int i,ElemType e)
{
ElemType *p,*q;
ElemType *newbase;//定义一个
//i的合法值为1 <= i <= L.lenght + 1
if(i < 1 || i > L.lenght + 1) //判断i值是否在有效范围
return false; //i值无效返回错误
if(L.lenght >= L.listsize) //查看表是否存满
{
newbase = (ElemType *) realloc(L.elem,(L.listsize + LISTINCREMENT)* sizeof(ElemType)); //延伸存储空间
if(!newbase) exit(OVERFLOW); //存储分配失败
L.elem = newbase; //新的基址
L.listsize += LISTINCREMENT; //增加存储容量
}
q = &(L.elem[i-1]); //把要插入元素地址赋给指针q
for(p = &(L.elem[L.lenght]);p >= q;--p)//把从表尾依次把表尾前面元素向后移动一位
*(p + 1 )= *p;//把元素向后移动一位
*q = e;//把要插入元素赋值到移动空出来的位置
++L.lenght;//表长加一
return true;
}
realloc的理解:
void realloc(void *ptr,size_t new_size); ptr是指向原来地址的指针,这个函数用于修改一个原先已经分配内存块的大小。
使用:
1.可以使一块内存扩大或缩小(原来的内存块可以扩大缩小)
(1.)如果是扩大一个内存块,则将原来的内存块保留在他的后边新增一块内存块(但是新增的内存块并未初始化)
(2.)如果是缩小一块内存块,则将该内存块的后半部分直接拿掉,剩余部分内存块及其内容保留。
2.原来的内存块无法扩大缩小 如果是着这种情况,realloc会重新开辟一个新的内存空间,并把原来的内存空间的内容拷贝到新的内存空间里。
注意:再调用完realloc后就不能使用指向就内存的指针,而是用返回的新的指针。
理解内容来自下面链接中的文章: https://blog.csdn.net/kakahaoma/article/details/87874947?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen.
List_Delete(&L,i,&e):删除线性表中第i个元素,并把删除的元素返回给e。
bool ListDelete(SqList &L,int i,ElemType &e)
{
ElemType *p,*q; //定义两个指针变量
if(i < 1 || i > L.lenght) //判断i值是否在有效范围
return false; //i值无效返回错误
p = &(L.elem[i-1]); //把要删除元素的地址赋给p
e = *p; //把p中值赋值给e
q = L.elem + L.lenght-1; //(l.elme 是首地址)使指针q指向表尾
for(++p;p <= q;++p) //把p地址之后的元素依次往前移动一位
*(p-1) = *p;
--L.lenght; //表长减 1
return true;
插一段根据自己需求编写调试的代码
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
#define OVERFLOW 0
typedef int ElemType ;
typedef struct{ //建立结构体--动态顺序存储的线性表
ElemType *elem;
int lenght; //当前长度
int listsize; //当前分配的存储容量
}SqList;
void InitList(SqList &L){ //构建一个空线性表
L.elem = (ElemType *) malloc(LIST_INIT_SIZE * sizeof(ElemType));
if(!L.elem) exit(OVERFLOW); //存储分配失败
L.lenght = 0; //空表长度为0
L.listsize = LIST_INIT_SIZE; //初始存储容量
}
bool ListInsert(SqList &L,int i,ElemType e)
{
ElemType *p,*q;
int k = 0;
ElemType *newbase;//定义一个临时存储变量
if(i < 1 || i > L.lenght+1) //判断i值是否在有效范围
{
printf("i值不合法\n");
return false;
}
if(L.lenght >= L.listsize) //查看表是否存满
{
newbase = (ElemType *) realloc(L.elem,(L.listsize + LISTINCREMENT)* sizeof(ElemType)); //延伸存储空间
if(!newbase) exit(OVERFLOW); //存储分配失败
L.elem = newbase; //新的基址
L.listsize += LISTINCREMENT; //增加存储容量
}
q = &(L.elem[i-1]); //把要插入元素位置地址赋值给q
for(p = &(L.elem[L.lenght]);p >= q;--p)//把从表尾依次把表尾前面元素向后移动一位
*(p + 1 )= *p;
*q = e;//把要插入数据赋值到q指向的地址中
++L.lenght;//表长加一
printf("%d 插入成功\n",e);
return true;
}
bool ListDelete(SqList &L,int i,ElemType &e)
{
ElemType *p,*q; //定义两个指针变量
if(i < 1 || i > L.lenght) //判断i值是否在有效范围
{
printf("删除失败\n");
return false; //i值无效返回错误
}
p = &(L.elem[i-1]); //把要删除元素的地址赋给p
e = *p; //把p中值赋值给e
q = L.elem + L.lenght-1; //(l.elme 是首地址)使指针q指向表尾
for(++p;p <= q;++p) //把p地址之后的元素依次往前移动一位
*(p-1) = *p;
--L.lenght; //表长减 1
printf("删除成功\n");
return true;
}
//查找元素是否在线性表中
int LocateElem(SqList L,ElemType e)
{
ElemType *p,*q; //定义两个指针变量
int i=0;
q = L.elem + L.lenght-1; //(l.elme 是首地址)使指针q指向表尾
for(p = L.elem;p <= q;p++)
{
i++;
if(*p == e)
return i; //查到元素,返回元素位序
}
return 0; //没查到,返回0
}
void Pint(SqList L)//遍历线性表函数
{
int i;
printf("遍历线性表\n");
for(i = 0;i < L.lenght;i++)
{
printf("%d\n",L.elem[i]);
}
}
int main()
{
SqList W;
InitList(W);
int i,n,k; //i循环条件的变量,n插入数据的个数,K临时存储插入的数据
int p,a; //p为删除元素的位序,a为第p位地数据
int g,z;// g为查找的数据,z为查到数据后返回数据的位序。
printf("请输入要存入的元素个数:\n");
scanf("%d",&n);
for(i = 1;i <= n;i++)
{
printf("请输入插入元素:\n");
scanf("%d",&k);
ListInsert(W,i,k); //调用插入函数
}
printf("请输入要删除第几位数据:\n");
scanf("%d",&p);
ListDelete(W,p,a); //调用删除函数
printf("删除的元素为 %d \n",a);
Pint(W); //调用遍历函数
printf("输入想要查找的数据:\n");
scanf("%d",&g);
z = LocateElem(W,g); //调用查找函数,查找
if(z == 0) //判断位序是否合法
printf("表中不存在该元素\n");
else
printf("查找数据在第%d位",z);
}