数据结构-线性表顺序存储结构上的基本运算实现(详解)
查找操作
算法思想
查找运算可采用顺数查找,即从第一个元素开始,依次将表中的的元素与所要查找的元素进行比较,如果相等,则查找成功。如果查找成功输出相应的提示信息,反之也给予相应的提示信息。
算法实现
#include
#include
#define MAX 20
typedef struct{
int *elem;
int length;
int listsize;
}SqList;
void CreatList(SqList &L)
{//建立一个线性表
L.elem=(int *)malloc(MAX * sizeof(int)); //动态数组空间分配
if(!L.elem) //! 是“非”的意思,如果没被分配空间 就结束程序。
return;//exit(0)
L.listsize=MAX;
printf("输入表的长度:");
scanf("%d",&L.length);
printf("输入%d个数:",L.length);
for(int i=0;i
运行演示
算法小结
- 首先要先创建一个线性表。
- 第二就是对线性表进行遍历,查找后输出。
L.elem=(int *)malloc(MAX * sizeof(int));
动态数组空间分配, 因为要静态的数组的空间只要定义好了之后就不能再被分配了。我们这里是数组的长度是我们输入来实现的,所以是动态的。(MAX * sizeof(int))
这剧代码返回的是整数型的数据,所以指针用的是int *
定义指针 的原因是因为malloc
函数只能返回第一个字节的地址,但是这个地址是没有含义的,为什么说是没有意义的呢?因为只知道这个地址,但是后面有几个字节是不知道。void CreatList(SqList &L)
&
代表引用,使用&
后所传参数会改变,仔细观察你会发现,凡是对链表有改动的,如删除函数,增加函数,形参前面都会有&符号,那是因为调用了这个函数后,所传链表(结构体)会改变。
插入操作
算法思想
查找运算可采用顺数查找,即从第一个元素开始,依次将表中的的元素与所要查找的元素进行比较,如果相等,则查找成功。如果查找成功输出相应的提示信息,反之也给予相应的提示信息。
算法实现
#include
#include
#define MAX 20
typedef struct{
int *elem;
int length;
int listsize;
}SqList;
void CreatList(SqList &L)
{//建立一个线性表
L.elem=(int*)malloc(MAX *sizeof(int));
if(!L.elem)
return;//exit(0)
L.listsize=MAX;
printf("输入表的长度:");
scanf("%d",&L.length);
printf("输入%d个数:",L.length);
for(int i=0;iL.length+1) return; //i的合法位置为1<=i<=ListLength(L)+1
int *p,*q;
q=&(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;--p)*(p+1)=*p;
*q=e;
++L.length;
return;
}
int main(){
SqList L;
CreatList(L);
Traverse(L);
ListInsert(L);
Traverse(L);
return 0;
}
运行演示
算法小结
q=&(L.elem[i-1]);
表示从链表的第i个元素开始一直到最后一个元素往后移一位.p=&L.elem[L.length-1]
意思是p赋初值为链表的最后一个元素地址,p>=q表示循环知道p的时候结束,--p是使p指针的指向往前移一位.
删除操作
算法思想
用顺序表作为线性表的存储结构时,由于结点的物理顺序必须和结点的逻辑顺序保持一致,因此当需要删除第i个元素时,必须将表中位置相似i+1,i+2,...,n-1,n上的节点,依次前移到位置i,i+1,...n-1(其中n为L的表长度)。
算法实现
#include
#include
#define MAX 20
typedef struct{
int *elem;
int length;
int listsize;
}SqList;
void CreatList(SqList &L)//对链表有改动 形参有&符号
{//建立一个线性表
L.elem=(int*)malloc(MAX *sizeof(int));
if(!L.elem)
return;//exit(0)
L.listsize=MAX;
printf("输入表的长度:");
scanf("%d",&L.length);
printf("输入%d个数:",L.length);
for(int i=0;iL.length)) return 0;//i的合法值为1<=i<=ListLength(L)+1
else{
int* p,* q;
p=&(L.elem[i-1]);//取回链表中每一个元素的地址赋值给p,则指针p指向的内容就是*p
e=*p;//找到表中要被删除的元素“e”(也就是*p)
q=L.elem+L.length-1;//尾元素的位置,更多解释看说明
for(++p;p<=q;++p)//初始化指针并移动,更多解释看说明
{
*(p-1)=*p; //被删除元素之后的元素左移
}
--L.length;
printf("元素被删除");
}
return 0;
}
int main(){
SqList L;
CreatList(L);
Traverse(L);
ListDelete(L);
Traverse(L);
return 0;
}
运行演示
算法小结
-
q=L.elem+L.length-1
顺序存储结构(实际上就是数组)中,l.elem
表示线性表l中存储数据(数组)的基地址(起始地址)这个也就是一开始建立顺序表的原因,其实本质上就是进行了用程序模拟了c语言中的数组,之所一开始int* elem
的原因就是 和 数组中的的定义一样,例如数组a[2]
a
就是a[0]
的地址,即a == &a[0]
,l.length
是表的长度(数据元素个数),q
是指针通过上式计算后指向尾元素和数组的情况一样,例如:int a[10],*p=a;
//p指向第一个元素
p=a+1;
//指向第二个元素.
例如:p=a+10-1;
指向最后一个数组元素,即a[9]
. -
for(++p;p<=q;++p)
p是一个被初始化过的指针,按上面代码应该指向某类型的数组,为超表达方便,数组记为x(i)。for循环首先把p从当前位置x(k)移动到x(k+1)作为初值,只要指针没到q指向的位置,就继续循环,循环每次递增一个数据。循环体将数组当前位置数据拷贝到前一个位置。总之,初始时,如果p指向x(m),q指向x(n),n应该大于m。最终运行结果是x(m)=x(m+1)=...=x(n)。 -
删除操作中函数的返回值是
int
型的,当然也可以是void
的型的,如果是void
型的话,把return 0;
写成return;
就好了。
顺序表合并
有两个顺序表LA 和 LB,其元素均为非递减有序排列,可设两个指针i、j 分别指向表LA 和 LB 中的元素,若LA.elem[i]>LB.elem[j],则当前先将LB.elem[j]插入到表LC中,若LA.elem[i]<=LB.elem[j],则当前先将LA.elem[i]插入到表LC中,如此进行下去,其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。其中个一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。
算法演示
#include
#include
#define MAX 20
typedef struct{
int *elem;
int length;
int listsize;
}SqList;
void CreatList(SqList &L)
{//建立一个线性表
L.elem=(int*)malloc(MAX *sizeof(int));
if(!L.elem)
return;//exit(0)
L.listsize=MAX;
printf("输入La表的长度:");
scanf("%d",&L.length);
printf("输入%d个数:",L.length);
for(int i=0;i
运行演示
算法小结
int* pa_last = la.elem + la.length - 1;
这段代码就是求表中最后一个元素的地址,而地址是按一定的幅度递增的,如下图
-
while (pa <= pa_last && pb <= pb_last)
这里面的符号,一定不要写错了,一开始就是因为<= 写成了< 导致程序运行失败,并没有进行排序
就直接存到Lc中了。 -
*pc++ = *pa++;
等价于 *pc = *pa; pa++; pc++;注意此处的 pa++ 不可以写成 (++pa),因为是先把pa 赋值给 pc ,所以不能写反了,如果写反了就是先p+1 ,然后才赋值给pc,这样程序逻辑会出错。
线性顺序表优缺点
优点
1.无须为表示结点间的逻辑关系而增加额外的存储空间(因为逻辑上相邻的元素其存储的物理位置也是相邻的)。
2.可以方便的随机存取表中的任一个元素。
缺点
1.插入和删除运算不方便,除表尾的位置外,在表的其他位置上进行插入或者删除操作都必须移动大量的结点,其效率低。
2.由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因为当表变化较大时,难以确定合适的存储规模。若按可能达到最大长度预先分配表空间,则可能造成一波分空间长期闲置而得不到充分利用;若事先对表长估计不足,则插入操作可能使表长超过预先分配的空间而造成溢出。
参考文献
- 数据结构-用C语言描述(第二版)[耿国华]
- C语言数据结构之线性表的基本操作
- 数据结构(C语言版)[严蔚敏,吴伟民]