线性表定义和特点
具有相同类型的数据元素的有限序列
初始结点有后继无前驱,终端结点有前驱无后继,中间结点,内部结点有且仅有一个直接前驱和一个直接后继。
归结为:关系线性化,结点顺序存
内存分配函数
使用以下函数需要加载头文件<stdlib.h>
- malloc(m)函数:分配m个字节的地址空间,并返回这段空间的首地址
- sizeof(x)函数:计算变量x的长度
- free§:释放指针p所指变量的存储空间,即彻底删除一个变量
例如:L.data=(int*)malloc(sizeof(int)*MaxSize)
通过sizeof函数计算int型占4个字节,再乘以最大结点个数MaxSize,通过malloc分配4*MaxSize个空间。
假如MaxSize=100,这时我们知道了需要400个字节,但是不知道是400个char,还是100个int或者50个double,所以在前面要加强制类型转换(int)说明是整形,当data是指针的时候就(int*)说明是指向整形的指针。
常用预定义变量
顺序表示和实现
逻辑连续的同时存储也连续。任一元素可以随意存取,不需要一个一个遍历过去。但是插入和删除运算不方便,除了表尾元素操作外,其他位置都需要移动大量元素。
存储分配职能预先进行静态分配,因此表长变化较大时,难以确定合适的存储规模,且长度不可动态定义。
元素存储位置的计算
存储类型描述
由于增删不太方便,我们在定义线性表每个结点的同时,还需要保存当前的表长。
ElemType是替代“int”,“float”,“char”等数据类型…
#define maxsize 100 //宏定义线性表能达到的最大长度
typedef struct{
ElemType elem[maxsize]; //存放线性表内存放的数据元素(每个结点)
int length; //记录当前线性表中元素个数
}seqlist;
seqlist L;
L.data=(ElemType*)malloc(sizeof(ElemType)*10)
此处elem是有100个结点的数组,length是当前表长,这个顺序表类型名(我称之为“表型”)是seqlist,即表内含有100个结点。
使用seqlist L
或seqlist *L
就创建了:名为L,类型为seqlist的顺序表
- 最基本的结构体
typedef struct{
int data[10];//十个结点
int length;//元素个数即表长
}seqlist;
seqlist L;
由于声明了结点个数,因此不需要申请内存分配。
- 如下图这种每个结点内两个元素
先声明一个类型,再嵌套进别的结构体内使用
typedef struct{
float p; //系数
int e; //阶数
}poly;//定义这种存系数+阶数的类型叫poly
typedef struct{
poly *elem;
/*使用上面声明的类型去定义顺序表首地址,动态定义首地址(也可以使用 poly elem[100],静态定义)*/
int length; //表长
}seqlist; //仅声明变量时还不分配空间
seqlist L;
L.elem=(poly*)malloc(sizeof(ElemType)*MaxSize) //需要给结点分配空间
查找操作
按值查找并返回位置
一个一个对比,使用flag标志决定查没查到
int locate(seqlist L,int find){ //查找某个数字,返回下标
int i,number;
int flag=0;
for(i=0;i<L.length;i++){
if(L.data[i]==find){
flag=1;
number=i+1;
}
}
if(flag==1)
return number;
else
return -1;
}
算法分析
查找次数和关键字位置有关,平均查找长度类似数学中的期望:
每个被查找到的概率都是1/n,化简后是 (n+1)/2
插入操作
插入元素到某个位置之前(挤开,占用原有位置)
关键:
- 插入位置之后的元素,依次后移一位腾出位置,然后插入。
- 检查插入位置,满足情况:在下标>0且下标<表长+1
- 检查表长,若表长==MaxSize,则不能插入
- 插入快结束时,给表长+1
seqlist add(seqlist L,int local,int num) //在local位置插入数字num
{
int i,j;
if((local<1)||(local>L.length+1)){
printf("\n输入位置不合法"); //插入位置不合法
}
else if(L.length==MaxSize){
printf("\n插入位置不够"); //位置满了
}
else{
for(i=L.length-1;i>=local-1;i--){
/***从最大下标length+1到目标下标local,并 包含 目标下标,向后赋值 ***/
L.data[i+1]=L.data[i];
}
L.data[local-1]=num; //定位赋值
}
L.length++;
printf("\n");
for(i=0;i<L.length;i++){ //打印检查
printf("%d ",L.data[i]);
}
}
该代码存在变量传递问题,即在函数接收不到插入结果
删除操作
删除第i个位置的元素
关键:
-
检查插入位置,满足情况:在下标>0且下标<表长+1
-
删除本位置元素,后面依次前移一位补上位置
-
插入快结束时,给表长-1
seqlist del(seqlist L,int local){//删除local位置的元素
int i;
if(local>L.length||local<1)
printf("\n输入位置无元素");
else{
for(i=local-1;i<L.length;i++){
L.data[i]=L.data[i+1]; //删除元素之后的元素前移
}
}
L.length--;
printf("\n");
for(i=0;i<L.length;i++){ //打印检查
printf("%d ",L.data[i]);
}
}
算法分析
每个元素被选中概率1/n,移动次数之和:(n-1)n/2,最后得到的删除期望: (n-1)/2
合并操作
为适用西北大学耿国华教授讲解,将结构体进行一些修改(也可以不修改,将下标last用表长length-1表示):
typedef struct{
int data[10];
int last;
}seqlist;
合并操作其实就是两个顺序表中较小的优先排入新顺序表,当有一个表使用结束后,另一个表全部排入新表。(此处默认递增顺序)
/* i,j,k分别指向L1,L2,L3 */
while(i<=L1.last&&j<=L2.last){
if(L1.data[i]<=L2.data[j]) //哪个小就把哪个放进新线性表里
{
L3.data[k]=L1.data[i];
i++;
k++;
}
else{
L3.data[k]=L2.data[j];
j++;
k++;
}
}
while(i<=L1.last){ //若i仍没有结束,则将L1全部放进L3
L3.data[k]=L1.data[i];
i++;
k++;
}
while(j<=L2.last){
L3.data[k]=L2.data[j];
j++;
k++;
}
摘自: 王卓老师:https://www.bilibili.com/read/cv3285768
摘自:耿国华老师 https://www.bilibili.com/video/BV1kx411h7pF?p=10