线性表文档之顺序表
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表文档之顺序表相关的知识,希望对你有一定的参考价值。
顺序表
定义
概念
线性表的顺序存储称为顺序表,它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
第 1
个元素存储在线性表的起始位置,第 i
个元素的存储位置后面紧接着存储的是第 i+1
个元素,称 i
为元素 ai
在线性表中的位序。
所谓线性表中任一数据元素可以随机存取,就是可以通过常数访问或者修改线性表中的数据元素,通常用高级程序设计语言中的数组来描述线性表的顺序表存储结构。
结构体
线性表的结构体定义如下:
#define MAXSIZE 100 // 这里定义了一个整型常量 MAXSIZE,值为 100,表示数组长度为 100
typedef struct
int data[MAXSIZE];// 存放顺序表元素的数组,默认是 int 类型,可换成其他类型
int length;// 存放顺序表实际元素个数,而非 MAXSIZE
SeqList;// 顺序表类型的定义
如图,一个顺序表包括一个存储表中元素的数组 data[]
和一个指示元素个数的变量 length
:
注:下面的内容是补充,通常都使用静态分配,考研知道静态分配结构体定义就行,下面的内容了解即可。
一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间已经固定,一旦空间占满,再加入新的数据就会产生溢出,进而导致程序奔溃。而int data[MAXSIZE]
就是静态分配的,通常使用的是静态分配。
而在动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就可以另外开辟一块更大的存储空间,用来替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性划分所有空间。而动态分配的结构体定义语句如下:
#define INIT_SIZE 100 // 顺序表长度的初始定义
typedef struct
int *data;// 设置数据类型是整型,可以修改为其他类型。指定动态分配数组的指针
int MAXSIZE;// 数组的最大容量
int length;// 顺序表实际元素个数
SeqList;// 动态分配数组顺序表的类型定义
C 语言初始动态分配语句为(该语句通常写在初始化函数 init
中):
L.data = (int*)malloc(sizeof(int)*INIT_SIZE);
而如果想要扩容,即重新为 L.data
分配更大的空间就可以了。
特点
顺序表的主要特点:
- 顺序表最主要的特点是随机访问,即可以通过首地址和元素序号可以在时间
O(1)
内找到指定的元素。如数组L = [1, 2, 3, 4, 5]
可以通过L[2]
访问到 L 中的第 3 个元素。 - 顺序表的存储密度高,每个节点只存储数据元素。
- 顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素。
基本操作
注,完整代码请参考:
概述
顺序表的常见基本操作如下:
void init(SeqList *L)
:初始化顺序表。int findByIndex(SeqList L, int index, int *ele)
:通过下标查找顺序表中的元素,保存到ele
中。int findByEle(SeqList L, int ele, int *index)
:通过指定值ele
在顺序表中查找元素,并将元素的下标保存到index
中。int insert(SeqList *L, int index, int ele)
:在顺序表中指定index
位置插入新元素ele
。int add(SeqList *L, int ele)
:在顺序表的尾部添加新元素ele
。removeByIndex(SeqList *L, int index, int *ele)
:删除指定index
位置的元素,并将被删除元素保存到ele
中。size(SeqList L)
:获取顺序表中实际元素个数。isEmpty(SeqList L)
:判断顺序表是否为空。print(SeqList L)
:打印顺序表中所有元素。
init
初始化顺序表操作。即将顺序表的 length
字段值置为 0,初始时顺序表中没有任何元素,所以为零。
实现代码如下:
/**
* 初始化顺序表,仅需要将 length 置为 0 即可
* @param list 待初始化的顺序表
*/
void init(SeqList *list)
// 仅需要将 length 置为 0 即可
(*list).length = 0;
// 或者可以用下面的语法
// list->length=0;
注意,如果顺序表结构体定义中 data
是动态分配的,则需要在 init
函数中队 data
进行初始化分配。
findByIndex
在顺序表中查找指定位序(用数组表示顺序表,所以从 0 开始)的元素,而用 ele
存放查找的元素。如果查找成功则返回 1,否则返回 0。
实现步骤:
- 参数校验,如果下标
index
小于零或者大于MAXSIZE
则无法获取到元素;如果下标大于等于顺序表的实际元素个数length
也会返回 0。 - 然后通过下标直接访问
data
数组中的元素即可,无论是最好的情况还是最坏的情况,时间复杂度都是O(1)
。
实现代码如下:
/**
* 查找顺序表中指定索引所表示的元素
* @param list 待查询的顺序表
* @param index 索引,即数组的下标,从 0 开始
* @param ele 保存查找到的元素
* @return 如果查询成功则返回 1,如果查找失败则返回 0
*/
int findByIndex(SeqList list, int index, int *ele)
// 0.参数校验
// 0.1 如果索引小于 0 或者大于等于设定的最大长度,则无法获取到元素
if (index < 0 || index >= MAXSIZE)
return 0;
// 0.2 如果索引在 [0, MAXSIZE) 范围内,但超过了实际元素个数,也是无法获取到的
if (index >= list.length)
return 0;
// 1.查找指定索引所表示的元素
// 1.1 直接返回下标为 index 的元素值即可,不需要遍历循环
*ele = list.data[index];
return 1;
findByEle
在顺序表 list
中查找第一个元素值等于 ele
的元素,并返回它在顺序表中的位序(即数组下标)。如果顺序表中存在元素 ele
则函数返回 1,如果顺序表中不存在该元素则返回 0。
实现步骤:
- 遍历顺序表所有元素,然后与指定值
ele
进行比较,如果相等则用index
保存该元素在顺序表中的位序(即下标)。并且返回 1 退出程序。 - 遍历完顺序表发现不存在与指定值
ele
相等的元素,则返回 0。
实现代码如下:
/**
* 查找指定元素在顺序表中的索引
* @param list 待查询的顺序表
* @param ele 指定元素
* @param index 保存查询成功的索引
* @return 如果查询成功则返回 1,查询失败则返回 0
*/
int findByEle(SeqList list, int ele, int *index)
// 循环顺序表中的所有元素
for (int i = 0; i < list.length; i++)
// 比较迭代的每一个元素的值是否等于传入的参数值,如果相等则返回对应的索引(下标)
if (list.data[i] == ele)
// 保存索引
*index = i;
// 结束程序
return 1;
return 0;
insert
在顺序表 list
的第 index
(0<=index<list.length
)个位置插入新元素 ele
。若 index
的输入不合法,则返回 0,表示插入失败;否则,将第 index
个元素及其后的所有元素依次往后移动一个位置,腾出一个空位置插入新元素 ele
,顺序表的长度 length
增加 1,表示插入成功,返回 0。
实现步骤:
- 参数校验,如果
index
小于 0 或者大于MAXSIZE
都不是有效参数;如果顺序表已经满了,也无法进行插入操作;如果index
超过顺序表实际元素个数也无法插入成功,都返回 0 表示插入失败。 - 将顺序表中
index
及之后的所有元素向后移动一位。 - 然后将空出来的
index
位置填入待插入的元素ele
。 - 最后修改顺序表的
length
增加 1。
实现代码如下:
/**
* 向顺序表指定索引处插入一个新元素。原索引处的元素及之后的所有元素都向后移动一位。
* @param list 顺序表
* @param index 指定插入位置
* @param ele 待插入的新元素
* @return 如果插入成功则返回 1,如果插入失败则返回 0
*/
int insert(SeqList *list, int index, int ele)
// 0.参数校验
// 0.1 如果索引小于 0 或者大于等于设定的最大长度,则插入失败
if (index < 0 || index >= MAXSIZE)
return 0;
// 0.2 向顺序表中插入元素要检查顺序表是否已经满了,如果已经满了则不能再插入新元素则插入失败
if (list->length == MAXSIZE)
return 0;
// 0.3 如果插入的索引超过了数组长度的范围也不行
if (index > list->length)
return 0;
// 1.如果顺序表没有满,则继续插入元素
// 1.1 循环顺序表,从后往前遍历,将指定索引及之后的所有元素(包括指定索引)向后移动一位
for (int i = list->length - 1; i >= index; i--)
list->data[i + 1] = list->data[i];
// 1.2 将移动后空出来的位置(即指定索引所在的位置)插入新元素
list->data[index] = ele;
// 1.3 不要忘记将 length 加 1 表示顺序表新增一个元素
list->length++;
return 1;
add
为了弥补 insert
方法无法在顺序表尾部插入的问题(因为参数校验,所以不能插入,当然可以修改代码让它能够通过 insert
方法插入)。直接在顺序表的尾部插入新元素 ele
。
实现步骤:
- 参数校验,如果顺序表已满,则返回 0 表示插入失败。
- 直接在顺序表的
list->length
位置插入新元素ele
,因为该位置为空,所以直接赋值即可。 - 插入新元素后,将顺序表的
length
加 1。 - 返回 1 表示插入成功。
实现代码如下:
/**
* 直接添加新元素到顺序表的尾部
* @param list 顺序表
* @param ele 待添加的新元素
* @return 如果插入成功则返回 1,否则返回 0
*/
int add(SeqList *list, int ele)
// 0.校验
// 0.1 向顺序表中插入元素要检查顺序表是否已经满了,如果已经满了则不能再插入新元素则添加失败
if (list->length == MAXSIZE)
return 0;
// 1.插入新元素
// 1.1 直接获取顺序表的 length,然后将新元素的值赋予到 length 位置即可
list->data[list->length] = ele;
// 1.2 注意修改 length
list->length++;
return 1;
removeByIndex
删除顺序表 list
中第 index
(0<=index<list.length
)个位置(表示数组下标)的元素,用引用变量 ele
存放被删除元素的值。若输入的 index
不合法则返回 0 表示删除失败;否则,将被删元素赋给引用变量 ele
,并将第 index+1
个元素及其后的所有元素依次往前移动一个位置,返回 1 表示删除成功。
实现步骤:
- 参数校验。如果下标
index
小于零或者大于MAXSIZE
表示超出范围,返回 0 表示删除失败;如果顺序表为空则无法删除元素,返回 0 表示删除失败;如果下标index
大于等于顺序表长度list->length
则也无法删除,返回 0 表示删除失败。 - 将待删元素
list->data[index]
保存到引用变量ele
中。 - 然后将
index+1
及其后的所有元素都向前移动一位,即用后面的元素覆盖前面的元素。 - 循环完成后,将顺序表的实际元素个数
length
减去 1,表示已经删除一个元素。 - 返回 1 表示删除成功。
实现代码如下:
/**
* 移除顺序表中指定索引的元素
* @param list 顺序表
* @param index 指定索引,即下标,从 0 开始
* @param ele 被删除元素的数据值
* @return 如果删除成功则返回 1,删除失败则返回 0
*/
int removeByIndex(SeqList *list, int index, int *ele)
// 0.参数校验
// 0.1 判断输入的索引是否超出范围
if (index < 0 || index >= MAXSIZE)
return 0;
// 0.2 在删除顺序表元素之前,要判断顺序表是否为空,如果为空则不能进行删除
if (list->length == 0)
return 0;
// 0.3 判断输入的索引虽然在数组范围内,但是否存在元素
if (index >= list->length)
return 0;
// 1.删除指定索引的元素
// 1.1 保存待删除索引所表示的元素的数据值
*ele = list->data[index];
// 1.2 循环遍历顺序表,从前往后,将指定索引之后的所有元素(不包括指定索引)向前移动一步
for (int i = index; i < list->length; i++)
list->data[i] = list->data[i + 1];// 后面的元素覆盖前面的元素
// 1.3 将记录数组实际元素个数的 length 减去 1,表示已经删除了一个元素
list->length--;
return 1;
size
获取顺序表的实际元素个数,而非 MAXSIZE
。在顺序表中已经有一个 length
维护了顺序表的实际元素个数,所以直接返回即可。
实现步骤:
- 直接返回顺序表的
length
属性即可。
实现代码如下:
/**
* 获取顺序表的长度
* @param list 顺序表
* @return 顺序表中实际元素个数
*/
int size(SeqList list)
return list.length;
isEmpty
判断顺序表是否为空,如果顺序表为空则返回 1;如果顺序表不为空则返回 0。
实现步骤:
- 判断顺序表的
length
属性值是否等于 0,如果等于 0 表示顺序表实际元素个数为 0,即为空表;否则不是空表。
实现代码如下:
/**
* 顺序表是否为空
* @param list 顺序表
* @return 如果为空则返回 0,不为空则返回 1
*/
int isEmpty(SeqList list)
return list.length == 0;
print
打印顺序表所有元素。
实现步骤:
- 遍历顺序表所有元素,按顺序输出元素值。
实现代码如下:
/**
* 打印顺序表
* @param list 待打印的顺序表
*/
void print(SeqList list)
printf("[");
for (int i = 0; i < list.length; i++)
printf("%d", list.data[i]);
if (i != list.length - 1)
printf(", ");
printf("]\\n");
注意事项
顺序表是否为空
顺序表结构体 SeqList
中维护了一个字段 length
专门用来记录顺序表中实际存储的元素个数。而可以根据 list.length==0
来判断顺序表是否为空。
练习题
以下是一些顺序表的练习题:
- Example004-将顺序表中所有元素逆置
- Example005-删除顺序表中
[i, j]
之间的所有元素 - Example008-移动顺序表中小于和大于表头的元素
- Example010-使得由前m个递增有序元素和后n个递增有序元素组成的顺序表整个有序
- Example013-只用一个变量来求个位数字组成的正整数数组中的最小值
- Example014-比较两个带有最大公共前缀的顺序表
- Example016-将顺序表循环左移
p
个位置 - Example017-找出顺序表中的主元素
- Example021-删除顺序表中的最后一个元素
- Example022-使用
O(n)
时间复杂度删除顺序表中所有值等于x
的元素 - Example023-从有序顺序表种删除值在
(s, t)
之间的所有元素 - Example024-从顺序表种删除值在
[s, t]
之间的所有元素 - Example025-从有序顺序表中删除所有其值重复的元素
- Example026-将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表
- Example027-将一个
A[m+n]
的线性表中的后n
个元素放在前m
个元素的前面 - Example028-用二分查找在表中查找数值为
x
的元素,如果找到则与后继元素相交换,如果未找到则插入到顺序表中正确位置 - Example029-求两个等长升序序列 A 和 B 的中位数
- Example030-找出数组中未出现的最小正整数
- Example031-找出给定 3 个非空整数集合中所有可能的三元组中的最小距离
以上是关于线性表文档之顺序表的主要内容,如果未能解决你的问题,请参考以下文章