数据结构笔记目录:
1. 绪论、时间复杂度
2. 线性表
3. 树
4. 图
5. 查找
6. 排序

值+操作的集合
ADT
一个数学模型及定义在该模型上的一组操作
ADT{
数据对象:<数据对象的定义> D
数据关系:<数据关系的定义> S
基本操作:<基本操作的定义> P
}
存在一种或多种特定关系(逻辑结构)的数据元素集合
四种逻辑结构
两种存储结构

特定问题的求解步骤
时间复杂度的简单计算方法
参考数据结构实现的黑书
算法的渐进时间复杂度,简称时间复杂度
事前分析
将核心操作的次数与输入规模关联
给定两个函数 f ( n ) f(n) f(n) 和 g ( n ) g(n) g(n) ,如果存在一个整数 N N N 使得对于所有 n > N n >N n>N , f ( n ) f(n) f(n) 总是比 g ( n ) g(n) g(n) 大,那么称 f ( n ) f(n) f(n) 的渐进增长快于 g ( n ) g(n) g(n)

语句总的执行次数 T ( n ) T(n) T(n) 是关于问题规模 n n n 的函数,进而分析 T ( n ) T(n) T(n) 随着 n n n 的变化情况并确定 T ( n ) T(n) T(n) 的量级。记为 T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))
表示随着问题规模 n n n 的增大,算法执行时间的增长率和 f ( n ) f(n) f(n) 的增长率相同
f ( n ) f(n) f(n) 表示问题规模 n 的某个函数
执行次数 ⟺ \iff ⟺ 执行时间
规则
1-100求和
# 累加
int main(){
int sum = 0;//执行一次
int n = 100;//执行一次
for(int i = 0; i < n;++i){//判断语句执行n+1次
sum += i;//执行n次数
}
printf("sum=%d",sum);
}
# 执行 2n+3 次
# 大O记法 O(n)
# -------------------------------------------
# 高斯公式
void main(){
int sum = 0;//执行1次
int n = 100;//执行1次
sum = (n+1)n/2;//执行1次
printf("sum=%d",sum);
}
# 执行3次
# 大O记法 O(1)
直接查找
int search(int num){
int arr[] = [11,10,8,9,7,22,23,0];
for(int i = 0;i < arr.length;++i){
if(num == arr[i])
return i;
}
return -1;
}
最好情况:
最坏情况:
平均情况:
O ( 1 ) < O ( l o g n ) < O ( n l o g n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1) < O(logn) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n) O(1)<O(logn)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
线性阶
非嵌套线性阶,随输入规模的扩大,呈 线性增长
int sum = 0;//执行一次
int n = 100;//执行一次
for(int i = 0; i <= n;++i){//执行n+1次
sum += i;//执行n次数
}
平方阶
二重循环嵌套
int sum = 0;
int n = 100;
for(int i = 1;i <= n;++i){//执行 n+1 次
for (j = 1;j <= n;++j){//执行 n*(n+1) 次
sum += i;//执行 n^2 次
}
}
立方阶
三重循环嵌套
对数阶
对数阶,由于输入规模n的增大,不管底数多少,增长率都相同,所以忽略底数
int i=1,n=100;
while(i < n){
i = i*2;
}
循环次数:
x
=
l
o
g
∗
2
n
x = log*2n
x=log∗2n
时间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
常数阶
void main(){
int n = 100;
for(int i = 0;i < n;++i){
show(i); // O(n)
}
}
void show(int i){
printf("%d",i);
}
void main(){
int n = 100;
for(int i = 0;i < n;++i){
show(i); //O(n^2)
}
}
void show(int i){
for(int j = 0;j < i;++j){
printf("%d",i);
}
}
for 循环for 循环至多是该 for 循环包含的 内语句运行次数 乘 迭代次数for 循环if/else 语句找出主体语句中与 T ( n ) T(n) T(n) 成正比的循环变量,将之代入条件运算
int i = 1;
while(i <= n){
i = i*2;
}
int y = 5;
while((y+1)*(y+1)<=n){
y = y+1;
}
int BinarySearch(const ElementType A[],ElementType X,int N){
int Low,Mid,High;
Low = 0,High = N-1;
while(Low <= High){
Mid = (Low+High)/2;
if(A[Mid] < X){
Low = Mid + 1;
}else{
if(A[Mid] > X){
High = Mid-1;
else
return Mid; //Found
}
}
return NotFound; //NotFound is defined as -1
}
分析:提供了 O ( l o g N ) O(logN) O(logN) 以内的查找操作
定理:如果 M > N M > N M>N ,则 KaTeX parse error: Undefined control sequence: \mbox at position 3: M \̲m̲b̲o̲x̲{ mod N} < \fra…
unsigned int Gcd(unsigned int M,unsigned int N){
unSigned int Rem;
while(N > 0){
Rem = M % N;
M = N;
N = Rem;
}
return M;
}
long int Pow(long int X,unsigned int N){
if(N == 0)
return 1;
if(N == 1)
return X;
if(isEven(N))
return Pow(X*X,N/2);
else
return Pow(X*X,N/2) * X;
}
采用 归纳法 或 累计循环次数
多层循环从内到外分析,忽略单步语句、条件判断语句,只关注主体语句的执行次数
主方法 :大小为
n
n
n 的原问题分成若干个大小为
n
b
\frac{n}{b}
bn 的子问题,其中
a
a
a 个子问题需要求解,而
c
n
k
cn^k
cnk 是合并各个子问题需要的工作量。此时问题可表示为:
T
(
N
)
=
{
c
n
=
1
a
T
(
N
b
)
+
c
n
k
n
>
1
T(N)={cn=1aT(Nb)+cnkn>1
则其时间复杂度可相应的表示为:
T
(
N
)
=
{
O
(
n
l
o
g
b
a
)
a
>
b
k
O
(
n
k
l
o
g
b
n
)
a
=
b
k
O
(
n
k
)
a
<
b
k
T(N)={O(nlogba)a>bkO(nklogbn)a=bkO(nk)a<bk
已知
{
T
(
1
)
=
1
T
(
N
)
=
2
T
(
N
2
)
+
O
(
N
)
{T(1)=1T(N)=2T(N2)+O(N)
1. 等号右边连续代入递归关系
T
(
N
)
=
2
T
(
N
2
)
+
N
=
2
[
2
T
(
N
4
)
+
N
2
]
+
N
=
2
{
2
[
2
T
(
N
8
)
+
N
4
]
+
N
2
}
+
N
=
.
.
.
=
2
k
T
(
N
2
k
)
+
k
N
T(N)=2T(N2)+N=2[2T(N4)+N2]+N=2{2[2T(N8)+N4]+N2}+N=...=2kT(N2k)+kN
由 k = l o g N k = logN k=logN 得
T ( n ) = N T ( 1 ) + N l o g N = N l o g ( N ) + N T(n) = NT(1)+NlogN = Nlog(N) + N T(n)=NT(1)+NlogN=Nlog(N)+N
2. 叠缩求和
用 N 去除递归关系中的两边,不断替换
T
(
N
)
N
=
T
(
N
2
)
N
2
+
1
T
(
N
2
)
N
2
=
T
(
N
4
)
N
4
+
1
T
(
N
4
)
N
4
=
T
(
N
8
)
N
8
+
1
⋮
⋮
T
(
2
)
2
=
T
(
1
)
1
+
1
T(N)N=T(N2)N2+1T(N2)N2=T(N4)N4+1T(N4)N4=T(N8)N8+1⋮⋮T(2)2=T(1)1+1
将等号左边的所有相相加等于右边所有项的和,结果为
T
(
N
)
N
=
T
(
1
)
1
+
l
o
g
N
=
N
l
o
g
(
N
)
+
N
T(N)N=T(1)1+logN=Nlog(N)+N
| 描述 | 增长的数量级 | 说明 | 举例 |
|---|---|---|---|
| 常数级 | 1 | 普通语句 | 将两个数相加 |
| 对数级 | l o g N logN logN | 二分策略 | 二分查找 |
| 线性级 | N N N | 单层循环 | 找出最大元素 |
| 线性对数级 | N l o g N NlogN NlogN | 分治思想 | 归并排序 |
| 平方级 | N 2 N^2 N2 | 双层循环 | 检查所有元素对 |
| 立方级 | N 3 N^3 N3 | 三层循环 | 检查所有三元组 |
| 指数级 | 2 N 2^N 2N | 穷举查找 | 检查所有子集 |
int MaxSubSequenceSum(const int A[],int N){
int ThisSum,MaxSum,i,j,k;
MaxSum = 0;
for(i = 0;i < N;++i){
for(j = i;j < N;++j){//遍历所有子串
ThisSum = 0;
for(k = i;k <= j;++k)//当前子串的所有子序列求和
ThisSum += A[k];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
∑
i
=
0
N
−
1
∑
j
=
i
N
−
1
∑
k
=
i
j
1
∑
k
=
i
j
1
=
j
−
i
+
1
∑
j
=
i
N
−
1
j
−
i
+
1
=
(
N
−
i
+
1
)
(
N
−
i
)
2
∑
i
=
0
N
−
1
(
N
−
i
+
1
)
(
N
−
i
)
2
=
∑
i
=
1
N
(
N
−
i
+
1
)
(
N
−
i
)
2
=
1
2
∑
i
=
1
N
i
2
−
(
N
+
3
2
)
∑
i
=
1
N
i
+
1
2
(
N
2
+
3
N
+
2
)
∑
i
=
1
N
1
=
N
3
+
3
N
2
+
2
N
6
N−1∑i=0N−1∑j=ij∑k=i1j∑k=i1=j−i+1N−1∑j=ij−i+1=(N−i+1)(N−i)2N−1∑i=0(N−i+1)(N−i)2=N∑i=1(N−i+1)(N−i)2=12N∑i=1i2−(N+32)N∑i=1i+12(N2+3N+2)N∑i=11=N3+3N2+2N6
时间复杂度为 O ( N 3 ) O(N^3) O(N3)
∑ k = i j A k = A j + ∑ k = i j − 1 A k \sum_{k = i}^{j}{A_k} = A_j+\sum_{k = i}^{j-1}{A_k} k=i∑jAk=Aj+k=i∑j−1Ak
int MaxSubSequenceSum(const int A[],int N){
int ThisSum,MaxSum,i,j,k;
MaxSum = 0;
for(i = 0;i < N;++i){
ThisSum = 0;
for(j = i;j < N;++j){//遍历所有子串
ThisSum += A[k];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
}
}
return MaxSum;
}
时间复杂度为 O ( N 2 ) O(N^2) O(N2)
将序列分成大致相等的两部分。
最大子序列和可能在三处出现:
int MaxSubSum(const int A[],int Left,int Right){
int MaxLeftSum,MaxRightSum;
int MaxLeftBorderSum,MaxRightBorderSum;
int LeftBorderSum,RightBorderSum;
int Center,i;
if(Left == Right){//只有一个元素
if(A[Left] > 0)//该元素非负即为最大和
return A[Left];
else{
return 0;
}
}
Center = (Left+Right) / 2;
MaxLeftSum = MaxSubSum(A,Left,Center);
MaxRightSum = MaxSubSum(A,Center,Right);
MaxLeftBorderSum = 0,LeftBorderSum = 0;
for(i = Center;i >= Left;i--){
LeftBorderSum += A[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
MaxRightBorderSum = 0,RightBorderSum = 0;
for(i = Center;i >= Left;i--){
RightBorderSum += A[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}
int MaxSubSequenceSum(Const A[],int N){
return MaxSubSum(A,0,N-1);
}
{
T
(
1
)
=
1
T
(
N
)
=
2
T
(
N
/
2
)
+
O
(
N
)
{T(1)=1T(N)=2T(N/2)+O(N)
时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)
分治法 时间复杂度一般都是若干个代价为 l o g N logN logN 的子问题与一个处理函数的代价和
int MaxSubSequenceSum(const int A[],int N){
int ThisSum,MaxSum,j;
ThisSum = MaxSum = 0;
for(j = 0;j < N;++j){
ThisSum += A[k];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
}else if(ThisSum < 0){
ThisSum = 0;
}
}
return MaxSum;
}
时间复杂度为 O ( N ) O(N) O(N)
算法所需的辅助空间数量级
算法所需的辅助空间为常量