【二、线性表】2.2线性表的顺序表示

目录

一、顺序表的定义

1.定义

2.实现方法

3.顺序表的实现

4.顺序表访问第i个元素

5.线性表的顺序存储类型描述

6.顺序表特点

二、顺序表上基本操作的实现

1.插入操作

2.删除操作

3.按值查找(顺序查找)


一、顺序表的定义

1.定义

线性表的顺序存储叫作顺序表

用一组地址连续的存储单元依次存储线性表的数据元素

也称作线性表的顺序存储结构顺序映像

2.实现方法

在内存中找了块地儿,通过占位的形式,把一定内存空间给占了,然后把相同数据类型的数据元素依次存放在这块空地中

数组实现

3.顺序表的实现

逻辑相邻的两个元素物理也相邻

把线性表中所有元素按照逻辑顺序依次存储到从指定位置开始的一块连续存储空间

第i+1个元素的存储位置紧接在第i个元素的存储位置后面

顺序表可以实现随机存取

4.顺序表访问第i个元素

准备知识:

LOC(L):表示线性表L存储的起始位置,也是a1的存储位置,即LOC(L)=LOC(a1)刚好也数组名所指向的位置

sizeof(ElemType):表示线性表中每个元素所占用的存储空间大小

线性表中第i个元素地址:

LOC(ai)=LOC(L)+(i-1)*sizeof(ElemType)

LOC(ai)=LOC(a1)+(i-1)*sizeof(ElemType)

LOC(ai) = LOC(ai-1)+sizeof(ElemType)

访问顺序表中任意一个数据元素,时间复杂度为O(1), 因为可以直接通过公式计算得到地址,这就是随机存取

注意数组下标和线性表位序的对应:

线性表的位序从1开始;实际存储到数组中是从下标0开始的

例子:

#include <iostream>
using namespace std;
int main()
{
	int L[] = { 1,2,3,4 };
	cout << "LOC(L) = " << L << endl;
	cout << "LOC(a1) = " << &L[0] << endl;
	cout << "a4的地址 = " << &L[3] << endl;
	cout << "LOC(a4)=LOC(L) + (4-1)*sizeof(int) = " << L + (4 - 1) << endl;
	//注:指针做加法隐含有乘sizeof(ElemType)
	// L + (4-1)==>转换成十六进制加法:L的地址值 + (4-l)*sizeof(int)
	return 0;
}

代码运行结果截图如下

5.线性表的顺序存储类型描述

一维数组静态分配:

#define MAXSIZE 50   //线性表的最大长度
typedef struct {
	ElemType data[MAXSIZE]; //顺序表的元素
							//ElemType是由typedef定义的数据元素的类型
	int length;             //顺序表的当前长度
}SqList;                    //使用typedef重新定义的顺序表的类型

静态分配特点为数组的大小空间已经固定(MAXSIZE);空间满时,再加入新数据会导致溢出

注意代码中的ElemType根据具体需求确定,比如int,则再加一句typedef int ElemType;

一维数组的动态分配:

#define InitSize 100    //表长的初始定义
typedef struct {
	ElemType* data; //指示动态分配数组的指针
	                //ElemType是由typedef定义的数据元素类型
	int length,     //顺序表的当前长度
		MaxSize;    //动态数组的最大容量
}SqList;            //使用typedef重新定义的顺序表的类型
 
//动态分配空间
L.data = (ElemType*)malloc(InitSize * sizeof(ElemType));

动态分配特点为在执行过程中根据需要,动态分配空间满时,可以开辟另外一块更大空间,达到扩充目的还是一次性选择一块大的空间,只不过可以在程序运行时动态指定空间大小

注意动态分配不是链式存储,分配的空间依然是连续的,依然采用随机存取方式

区分MaxSize与length:

MaxSize是数组的最大容量:数组长度是创建顺序表的时候就定下来的

length是线性表的当前长度:每存放一个数据元素,线性表长加一,每删除一个数据元素,线性表长减一,length<=MaxSize

6.顺序表特点

随机访问

存储密度高(不用额外存储指示信息)

逻辑相邻在物理上也相邻,插删需要移动大量元素

二、顺序表上基本操作的实现

1.插入操作

例子:

 

算法思想:

在顺序表L的第i(1<=i<=L.length+1)个位置插入新元素e

如果i的输入不合法,则返回false,表示插入失败

否则将顺序表的第i个元素以及其后的所有元素右移一个位置(从后向前处理),腾出一个空位置插入新元素e,顺序表长度增加1,插入成功,返回true

代码实现:

/*
* 将元素e插入到顺序表L中位序i的位置
*/
bool ListInsert(SqList &L, int i,ElemType e){
	//下表[1, L.length + 1]合法
	if ( i < 1 || i > L.length + 1) {
		return false;
	}
 
	//当前存储空间已满,不能插入
	if (L.length >= MaxSize)
		return false;
 
	//移位
	for (int j = L.length; j >= i; j--) {
		L.data[j] = L.data[j - 1];
	}
 
	//插入
	L.data[i - 1] = e;
	//表长+1
	L.length++;
 
	return true;
}

L必须需要引用

静态数组实现:涉及修改data中元素的时候必须使用引用,否则形参和实参的data是两个数组

动态数组实现:可以不使用引用,这时形参的data和实参的data指向的地址值都一样

不管是静态数组还是动态数组,形参修改length的时候都必须要引用,实参才能同步变化

复杂度分析:

(1)最好情况:在表尾插入(即i=n+1)元素后移语句将不执行,时间复杂度为O(1)

(2)最坏情况:在表头插入(即i=1)元素后移语句将执行n次,时间复杂度为O(N)

(3)平均情况:假设pi(pi=1/(n+1))是在第i个位置上插入一个结点的概率,有n+1个插入位置(1到length+1),总的移动次数为0+1+2+3+······+n=n(1+n)/2,在每个位置插入的概率相等,则除以n+1,即n(1+n) / 2(n+l) = n/2,因此插入算法的平均复杂度为O(N)

(4)插入位置举例:插入到位序15不需要移动;插入到位序1需要移动14个元素

2.删除操作

例子:

算法思想:

删除顺序表L中第i(1<=i<=L.length)个位置的元素

删除位置非法则返回false

否则将第i个元素赋值给e,将顺序表的第i个元素以及其后的所有元素左移一个位置(从前向后处理),顺序表长度减少1,删除成功,返回true

代码实现:

/*
* L:线性表
* i:待删除元素在线性表的位置
* e:记录删除元素的数据,通过引用返回
*/
bool ListdDelete(SqList &L, int i, ElemType &e) {
	//非法判断
	if ( i < 1 || i > L.length) {
		return false;
	}
 
	//取出需要删除的元素的数据
	e = L.data[i - 1];
 
	//移位
	for (int j = i; j < L.length; j++) {
		L.data[j - 1] = L.data[j];
	}
 
	//长度减一
	L.length--;
	return true;
}

复杂度分析:

(1)最好情况:删除表尾元素(即i=n)无须移动元素,时间复杂度为O(1)

(2)最坏情况:删除表头元素(即i=1)需要移动除第一个元素外的所有元素,时间复杂为O(N)

(3)平均情况:假设pi(pi=1/n)是删除第i个位置上结点的概率,有n个位置可以删除(1到length),总的移动次数为0+1+2+3+······+n-1=(n-1)n/2,在每个位置删除的概率相等,则除以n,即(n-1)n/2n = (n-1)/2,因此删除算法的平均复杂度为O(N)

(4)删除位置举例:删除位序14不需要移动,直接修改表长减少1就行;删除位序1需要移动13个元素

3.按值查找(顺序查找)

算法思想:

在顺序表L中查找第一个元素值等于e的元素,并返回其位序

从头到尾遍历L,在遍历的过程中进行比较(出现了等于e的元素则查找成功,返回位序;遍历完毕都没有出现等于e的元素则查找失败,返回0)

代码实现:

/*
* 查找顺序表中值为e的元素,如果查找成功,返回元素位序,否则返回0
*/
int LocateElem(SqList L, ElemType e) {
	int i;
	for (i = 0; i < L.length; ++i) {
		if (L.data[i] == e) {
			//位序与下标有1的差距
			return i + 1;
		}
	}
 
	//查找失败
	return 0;
}

复杂度分析:

(1)最好情况:查找的元素就在表头,仅需比较一次,时间复杂度为O(1)

(2)最坏情况:查找的元素在表尾(或不存在),需要比较0次,时间复杂度为O(N)

(3)平均情况:假设pi(pi=1/n)是查找的元素在第i个位置上结点的概率,有n个位置可以查找,总的比较次数为1+2+3+······+n=n(1+n)/2,在每个位置比较的概率相等,则除以n,即n(1+n)/2n = (n+1)/2,因此按值查找算法的平均复杂度为O(N)

(4)查找位置举例:查找20,仅比较1次;查找150比较14次

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aspect of Twilight

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值