《数据结构与算法 C语言版》—— 2.2线性表的顺序表示与实现

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
简介:

本节书摘来自华章出版社《数据结构与算法 C语言版》一 书中的第2章,第2.2节,作者:徐凤生,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.2线性表的顺序表示与实现

2.2.1线性表的顺序表示

线性表的顺序存储是指在内存中用一组地址连续的存储单元依次存储线性表的数据元素,用这种存储方式存储的线性表称为顺序表。顺序表中数据元素之间的逻辑关系通过其“存储位置相邻”来表示,如图21所示。
screenshot

如果顺序表的数据元素是按照递增或递减顺序存放的,则称其为有序顺序表。
假设线性表的每个数据元素需占用l个存储单元,其第一个元素的存储地址,即数组的基地址记为LOC(a1),则线性表中第i个数据元素的存储地址LOC(ai)为
LOC(ai)=LOC(a1)+(i-1)*l
线性表中第i+1个数据元素的存储地址和第i个数据元素的存储地址之间具有以下关系:
LOC(ai+1)=LOC(ai)+l
由此可见,只要确定了线性表中第一个元素的存储位置,其他元素的存储位置就可以确定了,因此线性表的顺序存储结构是一种随机存取的存储结构。
由于高级程序设计语言中的数组具有随机存取的特性,因此,通常用数组来描述数据结构中的顺序存储结构。在此,由于线性表的长度可变,且所需最大存储空间随问题的不同而不同,因此在C语言中可采用动态分配的一维数组,描述如下:

//------------线性表的动态分配顺序存储结构-------------------
#define List_Size 100//线性表存储空间的初始分配量
#define ListIncrement 10//线性表存储空间的分配增量
typedef struct{
ElemType *elem;//存放线性表的数组基地址
int length;//线性表的当前长度
int listsize;//线性表当前分配的存储容量
}SqList;

2.2.2线性表的顺序实现

下面讨论在顺序存储结构下,线性表的基本操作是如何实现的。
1.顺序表的初始化
顺序表的初始化即构造一个空的线性表。设置length域的值为0表示线性表中没有元素,即为空表。算法描述如下:

void InitSqList(SqList &L){
L.elem=new ElemType[List_Size];
if(!L.elem)exit(OVERFLOW);
L.length=0;
L.listsize=List_Size;
}

2.建立顺序表
方法是依次将n个数据元素存入顺序表中。算法描述如下:

void CreateSqList(SqList &L,int n){//创建n个元素的顺序表
int i;
for(i=0;i<n;i++){
scanf("%d",&L.elem[i]);
L.length++;
}
}

3.销毁顺序表
该运算的结果是释放顺序表L占用的内存空间。算法描述如下:

void DeatroySqList(SqList &L){//销毁顺序表
delete L.elem;
}

4.插入操作
设线性表L=(a1,a2,…,ai-1,ai,ai+1,…,an),在第i个位置插入x是指在第i-1个数据元素和第i个数据元素之间插入一个新的数据元素x,即L变为(a1,a2,…,ai-1,x,ai,ai+1,…,an)。
数据元素x的插入使得ai-1和ai的逻辑关系发生了变化,并且L的长度由n变为n+1。显然,插入位置i满足1≤i≤n+1。如果i=n+1,则只需将x插入到an之后即可;如果1≤i≤n,则需将an~ai依次向后移动一个位置,然后将x插入到空出的第i个位置。算法描述如下:

int SqListInsert(SqList &L,int i,ElemType x){
int j;
ElemType *p;
if(i<1||i>L.length+1)return ERROR;//插入位置不合法
if(L.length>=L.listsize){//当前存储空间已满,增加存储空间
p=(ElemType *)realloc(L.elem,(L.listsize+ListIncrement)*sizeof(ElemType));
if(!p)exit(OVERFLOW);
L.elem=p;
L.listsize=L.listsize+ListIncrement;
}
for(j=L.length-1;j>=i-1;j--)L.elem[j+1]=L.elem[j];//插入位置之后的元素依次后移
L.elem[i-1]=x;//插入元素
++L.length;//表长增1
return OK;
}

下面对插入算法的时间复杂度进行分析。在顺序表中的某个位置插入一个数据元素,其时间主要消耗在移动元素上,而移动元素的个数取决于插入的位置和线性表的长度。
假设pi是在第i个位置插入一个元素的概率,则在长度为n的线性表中插入一个元素时所需移动元素的次数的期望值为
Eis=∑n+1i=1pi(n-i+1)
不失一般性,我们假设在线性表中的任何位置插入元素是等概率的,即pi=1n+1,则
Eis=∑n+1i=1pi(n-i+1)=∑n+1i=11n+1(n-i+1)=n2

所以,在顺序表中的插入操作约需移动一半的数据元素,其时间复杂度为O(n)。
5.删除操作
设线性表L=(a1,a2,…,ai-1,ai,ai+1,…,an),删除第i个元素ai,则L变为(a1,a2,…,ai-1,ai+1,…,an)。删除ai使得ai-1、ai和ai+1的逻辑关系发生了变化,并且L的长度由n变为n-1。显然,删除的位置i满足1≤i≤n。如果i=n,则只需删除an即可;如果1≤i≤n-1,则需将ai+1~an依次向前移动一个位置。算法描述如下:

int SqListDelete(SqList &L,int i){
int j;
if(i<1||i>L.length)return ERROR;//删除位置不合法
for(j=i-1;j<L.length-1;j++)L.elem[j]=L.elem[j+1];//删除位置之后的元素依次前移
--L.length;//表长减1
return OK;
}

下面对删除算法的时间复杂度进行分析。删除算法的主要操作仍是移动元素,而移动元素的个数取决于删除的位置和线性表的长度。
假设qi是在第i个位置删除一个元素的概率,则在长度为n的线性表中删除一个元素时所需移动元素的次数的期望值为
Edl=∑ni=1qi(n-i)
我们假设在线性表中的任何位置删除元素是等概率的,即pi=1n,则
Edl=∑ni=1pi(n-i)=∑n+1i=11n(n-i)=n-12

所以,在顺序表中的删除操作约需移动一半的数据元素,其时间复杂度为O(n)。
6.按值查找
线性表中按值查找是指在线性表中查找与给定值x相等的数据元素。其方法是:从第一个元素开始依次与x进行比较,直到找到一个与x相等的数据元素,并返回它在顺序表中的位序;若查遍顺序表都没有找到与x相等的数据元素,则返回ERROR。算法描述如下:

int LocateElem(SqList L,ElemType x){
int i=1;
while(i<=L.length&&L.elem[i-1]!=x)i++;
if(i<=L.length)return i;
else return ERROR;
}

查找算法的主要操作是比较元素。在查找成功的情况下,最好情况是要找的是第一个元素,比较次数是1;最坏的情况下是要找的是第n个元素,比较次数是n。如果查找第i个元素的概率是pi,找到第i个元素所需的数据比较次数为ci,则查找成功的平均期望值为∑ni=1pici。在等概率的情况下,平均期望值为∑ni=11ni=(1+n)/2。所以按值查找算法的时间复杂度为O(n)。

7.依次输出线性表中的所有元素

void DispSqList(SqList L){//输出顺序表
int i;
for(i=0;i<L.length;i++)printf("%d",L.elem[i]);
printf("\\\\n");
}

2.2.3顺序表的应用举例

例1将顺序表La=(a1,a2,…,ai-1,ai,ai+1,…,an)逆置。
解要想将La逆置,只需将第一个元素和最后一个元素交换,第二个元素和倒数第二个元素交换,以此类推,直到没有元素交换为止。算法描述如下:

void Contrary_Sq(SqList &La){//将顺序表逆置
int temp;
for(i=0;i<La.length/2;i++){
temp=La.elem[i];
La.elem[i]=La.elem[La.length-1-i];
La.elem[La.length-1-i]=temp;
}
}

例2设顺序表La中的数据元素递增有序。试编写算法,将x插入到顺序表的适当位置上,以保持该表的有序性。
解从顺序表La中的最后一个元素开始与x进行比较,若该元素大于x,则将该元素后移一个位置,否则将x插入到该元素的下一个位置。算法描述如下:

int Insert_OrderSq(SqList &La,ElemType x){
int i;
ElemType *p;
if(La.length>=La.listsize){//当前存储空间已满,增加分配
p=(ElemType*)realloc(La.elem,(La.listsize+ListIncrement)*sizeof(ElemType));
if(!p)exit(OVERFLOW);
La.elem=p;
La.listsize=La.listsize+ListIncrement;
}
for(i=La.length-1;x<La.elem[i]&&i>=0;i--)La.elem[i+1]=La.elem[i];
La.elem[i+1]=x;//插入元素x
++La.length;
return OK;
}

例3
有两个顺序表La和Lb,其元素均按从小到大升序排列。编写一个算法将它们合并成一个顺序表Lc,要求Lc的元素也是从小到大升序排列。
解1)初始化Lc为空表;2)分别从La和Lb中取得当前元素Laelem[i]和Lbelem[j];3)若Laelem[i]≤Lbelem[j],则将Laelem[i]插入到Lc中,并取La中的下一个元素;否则将Lbelem[j]插入到Lc中,并取Lb中的下一个元素;4)重复步骤3直至La或Lb中元素被取完为止;5)将La表或Lb表中剩余元素插入到Lc表中。算法描述如下:

void mergelist_Sq(SqList La,SqList Lb,SqList &Lc){//两个有序表的归并
int i,j,k;
InitSqList(Lc);
i=0;j=0;k=0;
while(i<Lalength&&j<Lblength){
if(Laelem[i]<=Lbelem[j]) Lc.elem[k++]=La.elem[i++];
else Lc.elem[k++]=Lb.elem[j++];
}
while(i<La.length)Lc.elem[k++]=La.elem[i++];//将La中剩余元素插入到Lc中
while(j<Lb.length)Lc.elem[k++]=Lb.elem[j++];//将Lb中剩余元素插入到Lc中
Lc.length=La.length+Lb.length;
}

由于上述算法的基本操作是元素赋值,所以算法的时间复杂度为O(La.length+Lb.length)。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
349 1
|
存储 C语言
数据结构中的线性表链式存储介绍及其基本操作
链式存储是线性表的一种重要存储方式,它通过节点和指针的结构,实现了灵活的动态存储管理。本文介绍了单向链表的基本操作,并提供了相应的C语言代码示例。理解和掌握链表的操作对学习和应用数据结构具有重要意义。希望这篇博客能帮助你更好地理解线性表的链式存储。
434 2
|
11月前
|
存储 算法 测试技术
【C++数据结构——线性表】求集合的并、交和差运算(头歌实践教学平台习题)【合集】
本任务要求编写程序求两个集合的并集、交集和差集。主要内容包括: 1. **单链表表示集合**:使用单链表存储集合元素,确保元素唯一且无序。 2. **求并集**:遍历两个集合,将所有不同元素加入新链表。 3. **求交集**:遍历集合A,检查元素是否在集合B中存在,若存在则加入结果链表。 4. **求差集**:遍历集合A,检查元素是否不在集合B中,若满足条件则加入结果链表。 通过C++代码实现上述操作,并提供测试用例验证结果。测试输入为两个集合的元素,输出为有序集合A、B,以及它们的并集、交集和差集。 示例测试输入: ``` a c e f a b d e h i ``` 预期输出:
315 7
|
11月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
530 5
|
11月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
371 5
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
170 6
|
存储
【数据结构】线性表和顺序表
【数据结构】线性表和顺序表
128 1
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
01(数据结构考研)线性表相关操作代码
01(数据结构考研)线性表相关操作代码
193 0
|
存储 C语言
数据结构之线性表的初始化及其操作
数据结构之线性表的初始化及其操作
212 0

热门文章

最新文章