❤️《画解数据结构》之 顺序表八大算法总结❤️(建议收藏)
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️《画解数据结构》之 顺序表八大算法总结❤️(建议收藏)相关的知识,希望对你有一定的参考价值。
零、前言
这篇文章,作者将用 「 七张动图 」 来阐述一种最基础的顺序结构
「 顺序表 」
相信看我文章的大多数都是 「 大学生 」,能上大学的都是 「 精英 」,那么我们自然要 「 精益求精 」,如果你还是 「 大一 」,那么太好了,你拥有大把时间,当然你可以选择 「 刷剧 」,然而, 「 学好算法 」,三年后的你自然 「 不能同日而语 」。
那么这里,我整理了 「 几十个基础算法 」 的分类,点击开启:
🌌《算法入门指引》🌌
如果链接被屏蔽,或者有权限问题,可以私聊作者解决。大致题集一览:
为了让这件事情变得有趣,以及 「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为 「 夜深人静写算法 」专家团 的一员。
不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上, 「 不会索取任何 」。
🙉放心成神!自律使你自由!🙉
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
LeetCode 太难?先看简单题! 🧡《C语言入门100例》🧡
数据结构难?不存在的! 🌳《画解数据结构》🌳
闭关刷 LeetCode,剑指大厂Offer! 🌌《算法入门指引》🌌
LeetCode 太简单?算法学起来! 💜《夜深人静写算法》💜
说了这么多,学习之前,我们来看下 「 枚举系列 」 中几个经典的算法,将算法之前,我会介绍一下 C语言 中一种最简单的数据结构,即 数组。首先来看,内容提要:
一、概念
1、顺序存储
顺序存储结构,是指用一段地址连续的存储单元依次存储线性表的数据元素。
2、存储方式
在编程语言中,用一维数组来实现顺序存储结构,在C语言中,把第一个数据元素存储到下标为 0 的位置中,把第 2 个数据元素存储到下标为 1 的位置中,以此类推。
3、长度和容量
数组的长度指的是数组当前有多少个元素,数组的容量指的是数组最大能够存放多少个元素。如果数组元素大于最大能存储的范围,在程序上是不允许的,可能会产生意想不到的问题,实现上是需要规避的。
如上图所示,数组的长度为 5,即红色部分;容量为 8,即红色 加 蓝色部分。
4、数据结构定义
#define MAXN 1024
#define DataType int // (1)
struct SeqList {
DataType data[MAXN]; // (2)
int length; // (3)
};
-
(
1
)
(1)
(1) 数组类型为
DataType
,定义为int
; -
(
2
)
(2)
(2)
SeqList
定义的就是一个最多存放MAXN
个元素的数组,MAXN
代表数组容量; -
(
3
)
(3)
(3)
length
代表数组长度,即当前的元素个数。
二、常用接口实现
1、只读接口
1)索引
索引 就是通过 数组下标 寻找 数组元素 的过程。C语言实现如下:
DataType SeqListIndex(struct SeqList *sq, int i) {
return sq->data[i]; // (1)
}
- ( 1 ) (1) (1) 调用方需要注意 i i i 的取值必须为非负整数,且小于数组最大长度。否则有可能导致异常,引发崩溃。
- 索引的算法时间复杂度为
O
(
1
)
O(1)
O(1)。
2)查找
查找 就是通过 数组元素 寻找 数组下标 的过程,是索引的逆过程。
对于有序数组,可以采用 二分 进行查找,时间复杂度为
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n);对于无序数组,只能通过遍历比较,由于元素可能不在数组中,可能遍历全表,所以查找的最坏时间复杂度为
O
(
n
)
O(n)
O(n)。
简单介绍一个线性查找的例子,实现如下:
DataType SeqListFind(struct SeqList *sq, DataType dt) {
int i;
for(i = 0; i < sq->length; ++i) { // (1)
if(sq->data[i] == dt) {
return i; // (2)
}
}
return -1; // (3)
}
- ( 1 ) (1) (1) 遍历数组元素;
- ( 2 ) (2) (2) 对数组元素 和 传入的数据进行判等,一旦发现相等就返回对应数据的下标;
-
(
3
)
(3)
(3) 当数组遍历完还是找不到,说明这个数据肯定是不存在的,直接返回
−
1
-1
−1。
3)获取长度
获取 数组的长度 指的是查询当前有多少元素。可以直接用结构体的内部变量。C语言代码实现如下:
DataType SeqListGetLength(struct SeqList *sq) {
return sq->length;
}
2、可写接口
1)插入
插入接口定义为:在数组的第 k k k 个元素前插入一个数 v v v。由于数组是连续存储的,那么从 k k k 个元素往后的元素都必须往后移动一位,当 k = 0 k=0 k=0 时,所有元素都必须移动,所以最坏时间复杂度为 O ( n ) O(n) O(n)。C语言代码实现如下:
int SeqListInsert(struct SeqList *sq, int k, DataType v) {
int i;
if(sq->length == MAXN) {
return 0; // (1)
}
for(i = sq->length; i > k; --i) {
sq->data[i] = sq->data[i-1]; // (2)
}
sq->data[k] = v; // (3)
sq->length ++; // (4)
return 1; // (5)
}
- ( 1 ) (1) (1) 当元素个数已满时,返回 0 0 0 代表插入失败;
- ( 2 ) (2) (2) 从第 k k k 个数开始,每个数往后移动一个位置,注意必须逆序;
- ( 3 ) (3) (3) 将第 k k k 个数变成 v v v;
- ( 4 ) (4) (4) 插入了一个数,数组长度加一;
- ( 5 ) (5) (5) 返回 1 1 1 代表插入成功;
2)删除
插入接口定义为:将数组的第 k k k 个元素删除。由于数组是连续存储的,那么第 k k k 个元素删除,往后的元素势必要往前移动一位,当 k = 0 k=0 k=0 时,所有元素都必须移动,所以最坏时间复杂度为 O ( n ) O(n) O(n)。C语言代码实现如下:
int SeqListDelete(struct SeqList *sq, int k) {
int i;
if(sq->length == 0) {
return 0; // (1)
}
for(i = k; i < sq->length - 1; ++i) {
sq->data[i] = sq->data[i+1]; // (2)
}
sq->length --; // (3)
return 1; // (4)
}
- ( 1 ) (1) (1) 返回0代表删除失败;
- ( 2 ) (2) (2) 从前往后;
- ( 3 ) (3) (3) 数组长度减一;
- ( 4 ) (4) (4) 返回1代表删除成功;
三、优缺点
1、优点
1)无须为表示表中元素逻辑关系而增加额外的存储空间;
2)随机存取元素时可以达到
O
(
1
)
O(1)
O(1),效率高;
2、缺点
1)插入和删除时需要移动大量元素;
2)必须一开始就确定存储空间的容量;
四、数组相关算法
1、线性枚举
1)问题描述
给定一个长度为 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \\le n \\le 10^5) n(1≤n≤105) 的整型数组,求所有数组元素中的其中的最小值。
2)动图演示
3)示例说明
蓝色的数据代表的是数组数据,红色的数据代表当前枚举到的数据,这样就可以遍历所有的数据进行逻辑处理了。
4)算法描述
遍历数组,进行条件判断,条件满足则执行逻辑。这里的条件就是 枚举到的数 是否小于 当前最小值,执行逻辑为 将 当前枚举到的数 赋值给 当前最小值;
5)源码详解
int findMin(int* nums, int numsSize){
int i, min = 100000;
for(i = 0; i < numsSize; ++i) { // (1)
if(nums[i] < min) { // (2)
min = nums[i];
}
}
return min; // (3)
}
- ( 1 ) (1) (1) 遍历数组中所有的数;
-
(
2
)
(2)
(2) 如果 当前枚举到的数 比记录的变量
min
小,则将它赋值给min
;否则,不做任何处理; -
(
3
)
(3)
(3) 最后,
min
中存储的就是整个数组的最小值。
2、前缀和差分
1)问题描述
给定一个 n ( n ≤ 1 0 5 ) n (n \\le 10^5) n(n≤105) 个元素的整型数组 a i a_i ai,再给出 m ( m ≤ 1 0 5 ) m(m \\le 10^5) m(m≤105) 次询问,每次询问是一个区间 [ l , r ] [l, r] [l,r],求 h ( l , r ) = ∑ k = i j a k h(l,r) = \\sum_{k=i}^j a_k h(l,r)=∑k=ijak
2)动图演示
3)样例分析
如上图所示,只需要记录一个前缀和,然后就可以通过一次减法将区间的值计算出来。时间复杂度 O ( 1 ) O(1) O(1)。这种就是差分的思想。
4)算法描述
第一个枚举,利用一个数组sum
,存储前
i
i
i 个元素的和。
第二个枚举,读入
m
m
m 组数据
l
,
r
l, r
l,r,对每组数据,通过
O
(
1
)
O(1)
O(1) 获取答案,即
s
u
m
r
−
s
u
m
l
−
1
sum_r - sum_{l-1}
sumr−suml−1。
5)源码详解
int sum[maxn];
int* prefixSum(int* nums, int numsSize, int m, int *l, int *r){
int i;
int *ret;
for(i = 0; i < numsSize; ++i) {
sum[i] = nums[i];
if(i)
sum[i] += sum[i-1]; // (1)
}
ret = (int *) malloc( m * sizeof(int<以上是关于❤️《画解数据结构》之 顺序表八大算法总结❤️(建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章
❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)
❤️《画解数据结构》两万字,十张动图,画解双端队列❤️(建议收藏)