数据结构课程体系:数据逻辑结构设计 + 存储结构实现+ 运算接口实现
逻辑结构的设计来源于算法的设计,后两者的实现实际上是算法的实现
递归的时间复杂度的计算
写时间复杂度的递推式
减治:return sum(n-1)+A[n]
T
(
N
)
=
T
(
N
−
1
)
+
1
T(N)=T(N-1)+1
T(N)=T(N−1)+1
分治:return sum(A, lo, mi) + sum(A, mi+1, hi)
T
(
N
)
=
2
∗
T
(
N
/
2
)
+
O
(
1
)
,
T
(
N
)
=
O
(
N
)
T(N)=2*T(N/2)+O(1),T(N)=O(N)
T(N)=2∗T(N/2)+O(1),T(N)=O(N)
向量
线性表中的元素放在连续的存储空间中;可以用一维数组表示
与数组的区别:泛化(支持更多数据类型 可以非基本) ,自带操作接口
自动扩充
size大于capacity时,自动扩充到现在capacity的两倍
相比数组来说,是动态空间管理 实现比较复杂 但是避免了上溢 空间浪费等问题
扩容(分摊时间复杂度 也就是平均每次操作的时间复杂度)时间复杂度分析:
将所有的操作中用于数组扩容的时间累加起来,除以n,只要n足够大,这一事件就是用于扩容处理的分摊时间成本
每次扩容一倍
O
(
2
)
O(2)
O(2)
∑
n
=
i
l
o
g
2
N
2
n
N
=
O
(
2
)
\frac{\sum_{n=i}^{log_2N}2^n}{N}=O(2)
N∑n=ilog2N2n=O(2)
每次扩充固定容量
O
(
N
/
2
X
)
O(N/2X)
O(N/2X)
x
∑
i
=
1
i
=
N
/
x
i
N
=
O
(
N
)
\frac{x\sum_{i=1}^{i=N/x}i}{N}=O(N)
Nx∑i=1i=N/xi=O(N)
排序
归并排序
T
(
N
)
=
2
∗
T
(
N
2
)
+
O
(
N
)
T(N)=2*T(\frac{N}{2})+O(N)
T(N)=2∗T(2N)+O(N)
T
(
N
)
=
O
(
N
l
o
g
(
N
)
)
T(N)=O(Nlog(N))
T(N)=O(Nlog(N))
最佳最差平均的时间复杂度都是一样的 不受输入数据的影响
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
//不能整除则向上取整
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
//为避免传参的麻烦直接在此处对有序序列进行排序
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
需要注意的细节:
start2=mid+1
所以两个序列左右都是闭区间;因此在最前面需要判断对于只有一个元素的序列直接返回,不做处理。
唯一化
对于无需序列:
O
(
N
2
)
O(N^2)
O(N2)
对有序向量的唯一化优化:
从前向后遍历,找到与上一个不相同的往前放
O
(
N
)
O(N)
O(N)
查找
直接查找 最好
O
(
1
)
O(1)
O(1) 最差
O
(
N
)
O(N)
O(N) , 平均
O
(
N
)
O(N)
O(N)
(插入删除的也相同 )
二分查找 为
O
(
l
o
g
(
N
)
)
O(log(N))
O(log(N))
// a[],int e
int search(int e,int begin,int end)
{
int mid=(begin+end)>>1;
if(e>a[mid]) search(mid+1,end);
else if(e==a[mid]) return mid;
else search(begin ,mid-1);
}
平均查找长度(ASL):平均查找表中的元素个数
A
S
L
=
∑
p
i
c
i
ASL=\sum p_i c_i
ASL=∑pici
顺序查找:成功
N
−
1
2
\frac{N-1}{2}
2N−1;失败
O
(
N
)
O(N)
O(N)
二分查找:成功
∑
i
=
1
i
=
l
o
g
2
(
N
+
1
)
2
i
−
1
∗
i
N
=
l
o
g
2
(
N
+
1
)
−
1
\frac{\sum_{i=1}^i=log_2(N+1)2^{i-1}*i}{N}=log_2(N+1)-1
N∑i=1i=log2(N+1)2i−1∗i=log2(N+1)−1
查找不成功要看叶子节点伸出的外部节点,认为其是等概率的,然后再求平均。
对二分查找的优化:
插值或者斐波那契 都是变化其分点的位置
列表
存储与构建
列表是按照链式结构实现存储,数据元素的物理存储位置可以随便,不一定要线性
顺序存储结构是静态空间的管理策略,通过链式存储结构实现动态空间管理。
但是在访问上需要通过局部邻域的关系去访问,时间复杂度为
O
(
n
)
O(n)
O(n);顺序存储空间可以通过下标去直接访问(随机存取)
(因此链表无法根据秩去直接访问无法采取二分查找)
一个地址需要4个字节去存储
插入/删除节点
插入的时间复杂度为
O
(
1
)
O(1)
O(1)(头部),
O
(
n
)
O(n)
O(n)(中间及尾部);相比向量来说,尾部的时间复杂度不一样(向量为
O
(
1
)
O(1)
O(1))
头节点需要特殊增加,里面不存数据,但是方便运算的实现。尾哨兵的创建也是为了不处理特殊情况
其他节点的加入或者删除都需要知道前驱节点是谁
做题时要看具体要插入删除谁
唯一化
若列表无顺序,则需要类比无序向量唯一化的手段,双重循环,
O
(
N
2
)
O(N^2)
O(N2)
若列表有顺序,依然类比有序向量唯一化的手段,向后遍历,找到不同的再添加。
O
(
N
)
O(N)
O(N)
(具体实现,P指前,Q指P后的节点,若相同则删除Q,然后再比较,不相同再都向后移动)
(每次比较的都是P与P的后继)
归并排序
不需要额外开空间,用begin->previous
作为开头一个个去接后续链表就可以实现