【数据结构】第一章:绪论基础
章节纲要:
- 【1】数据结构三要素
-
- 逻辑结构
-
-
- 线性结构:线性表,栈,队列
-
-
-
- 非线性结构:树,图,集合
-
-
- 存储结构
-
- 数据的运算
- 【2】算法效率分析(重点)
1. 数据结构的基本概念
1.1 基本术语
- 数据元素:数据的基本单位
- 数据对象:具有相同性质的数据元素的集合,是数据的子集
- 数据类型:原子类型,结构类型,抽象数据类型
1.2 逻辑结构:
我们把逻辑结构大体分为:线性结构和非线性结构。
线性结构:一般线性表,受限线性表,线性表推广
- 受限线性表:栈和队列
- 线性表推广:数组
非线性结构:树结构,集合,图结构
线性:数据元素之间一对一
集合:数据元素除了”同属一个集合内“,再无其他关系
树:存在一对多的关系
图:存在多对多的关系
1.3 存储结构
也叫物理结构
- 顺序存储:逻辑相连物理也相连,这样可以实现随机存取,每个元素占用最少的空间,但缺点是可能产生很多外部碎片。
- 链式存储:逻辑相连物理上不一定相连,通过物理地址指向来指示元素之间的逻辑关系,优点是不会产生碎片,但存储指针会产生额外的空间,只能实现顺序存取。
- 索引存储:增加索引表,优点是检索快,缺点是索引表占空间,而且增删也要修改索引表,耗时大
- 散列存储:也叫Hash存储,检索,增加,删除都很快,但可能会出现冲突(就是不可靠,如果散列函数不好的话),解决冲突会增加时间和空间开销。
- 抽象的数据类型能定义出一个完整的数据结构
- 数据的逻辑结构可以独立于存储结构
- 链式存储的时候,结点内的存储单元地址一定是连续的。(强调了结点就是逻辑结构咯)
- 循环队列是用顺序表表示的队列,既可以表示逻辑结构,也可以表示存储结构。但栈,就只是逻辑结构上的概念了
思考:线性表在用顺序存储和链式存储的方式实现的时候,插入和删除元素各自的时间复杂度?
顺序存储,如果我们要插入和删除,我们就需要遍历元素指定位置,因此只需要:O(n)O(n)O(n),但是在链式存储的方式下,我们访问的时候只涉及到地址,因此,只需要O(1)O(1)O(1)
2. 算法评价计算(重点)
算法是指:问题求解的步骤。我们可以通过时间复杂度和空间复杂度来对算法的好坏进行评价。
2.1 时间复杂度:
我们必须深刻理解:OOO的含义。
若T(n)T(n)T(n)和f(n)f(n)f(n)是定义在正整数集合上的两个函数,则 ∃\exists∃ 正常数CCC和n0n_0n0,使得当n≥n0n\geq n_0n≥n0时,都满足0≤T(n)≤Cf(n)0\leq T(n) \leq Cf(n)0≤T(n)≤Cf(n),OOO为T(n)T(n)T(n)的数量级。
计算时间复杂度,我们不仅考虑:问题本身的规模,还需要考虑待输入数据元素的初始状态。
例:假如我们需要在数组A[0...n−1]A[0...n-1]A[0...n−1]中查找定值k的算法流程:
(1)i=n-1;
(2)while(i>=0&&(A[i]!=k))
(3)i--;
(4)return i;
- 假如AAA当中没有和k相等的元素,那么f(n)=nf(n)=nf(n)=n
- 假如AAA的最后一个元素是k,那么f(n)=0f(n)=0f(n)=0
因此,我们需要区分:最坏时间复杂度,最好时间复杂度,平均时间复杂度,我们常说的时间复杂度是指:最坏时间复杂度。
时间复杂度运算我们需要掌握两条法则:
加法法则:
T(n)=T(n1)+T(n2)=O(f(n))+O(g(n))=O(max(f(n),g(n)))T(n)=T(n_1)+T(n_2)=O(f(n))+O(g(n))=O(max(f(n),g(n)))T(n)=T(n1)+T(n2)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
乘法法则:
T(n)=T(n1)∗T(n2)=O(f(n))O(g(n))=O(f(n)g(n))T(n)=T(n_1)*T(n_2)=O(f(n))O(g(n))=O(f(n)g(n))T(n)=T(n1)∗T(n2)=O(f(n))O(g(n))=O(f(n)g(n))
另外,我们需要熟记以下的渐进对比:
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)O(1)<O(\log_2n)<O(n)<O(n\log_2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
2.2 空间复杂度
就是改算法所耗费的存储空间,这个空间指的是额外的辅助空间。
专题:时间复杂度
# 题型1:while型
对于while型的,我们需要设执行次数,然后解不等式就可以了
例题:请问以下算法的时间复杂度是?
void fun(int n){
int i =1;
while(i<=n){
i = i*2;
}
}
解:
设我们执行的次数是 t,我们的值通项:i=1+2ti=1+2^ti=1+2t,列出不等式:i≤n⇒t≤log2ni\leq n\Rightarrow t\leq
log_2ni≤n⇒t≤log2n
因此算法复杂度是O(log2n)O(\log_2n)O(log2n)
例题:判别以下程序片段的时间复杂度
x=2;
while(x<n/2){
x = 2*x;
}
解:
设执行的次数是ttt,那么我们的x通项是:x=2∗2t=2t+1x=2*2^t=2^{t+1}x=2∗2t=2t+1
解不等式:2t+1<n22^{t+1}<\frac{n}{2}2t+1<2n
解得:t<log2n−2t<\log_2n-2t<log2n−2,因此,时间复杂度是:O(log2n)O(\log_2n)O(log2n)
例题:计算下面函数的时间复杂度
int func(int n){
int i=0, sum=0;
while(sum<n)
sum+=++i;
return i;
}
解:我们设执行次数是ttt,sum=0+1+2+....+tsum=0+1+2+....+tsum=0+1+2+....+t,找出不等式:sum<nsum<nsum<n
(t−1)(t+1)2<n\frac{(t-1)(t+1)}{2}<n2(t−1)(t+1)<n
解得:O(n1/2)O(n^{1/2})O(n1/2)
例题:以下算法的时间复杂度是?
void func(int n){
int i=0;
while(i*i*i<=n){
i++;
}
}
解:设执行次数ttt,i=ti=ti=t,t3<=nt^3<=nt3<=n
所以是O(n13)O(n^{\frac{1}{3}})O(n31),这个和上面的那题不一样,你注意不等式的条件是谁。
例题:问以下的程序的时间复杂度?
x=0;
while(n>=(x+1)*(x+1)){
x=x+1;
}
解:设执行次数ttt,x=tx=tx=t
(t+1)2<=n(t+1)^2<=n(t+1)2<=n
解得:t≤n12−1t\leq n^{\frac{1}{2}}-1t≤n21−1,因此时间复杂度是O(n1/2)O(n^{1/2})O(n1/2)
例题:计算下面两个代码的时间复杂度
代码1:
i=1,k=0;
while(i<n-1){
k=k+10*i;
i++;
}
代码2:
y=0;
while((y+1)*(y+1)<=n){
y=y+1;
}
解:
(1) 设执行次数是ttt,i=ti=ti=t,t<n−1t<n-1t<n−1,因此,O(n)O(n)O(n)
(2) 设执行次数是ttt,y=ty=ty=t,(y+1)2<=n(y+1)^2<=n(y+1)2<=n,因此,O(n1/2)O(n^{1/2})O(n1/2)
# 题型2:for 型
逐层分析
例题:计算下面程序的时间复杂度:
count = 0;
for(k=1;k<=n;k*=2)
for(j=1;j<=n;j++)
count++;
解:
设第一层的执行次数是t1t_1t1
k=1∗2t1≤n∴t1≤log2nk=1*2^{t_1}\leq n \\ \therefore t_1\leq \log_2nk=1∗2t1≤n∴t1≤log2n
第一层的复杂度是O1(log2n)O_1(\log_2n)O1(log2n)
设第二层的执行次数是t2t_2t2
j=t2≤nj=t_2\leq nj=t2≤n
第二层的复杂度是O2(n)O_2(n)O2(n)
总复杂度是:O1∗O2=O(nlog2n)O_1*O_2=O(n\log_2n)O1∗O2=O(nlog2n)
例题:计算下面程序的时间复杂度:(最坏的情况)
for(i=n-1;i>1;i--)
for(j=1;j<i;j++)
if(A[j]>A[j+1])
swap(A[j],A[j+1])
解:
设第一层的执行次数是t1t_1t1
t<nt<nt<n
所以第一层是O1(n)O_1(n)O1(n)
第二层的执行次数是t2t_2t2
t2<it_2<it2<i
第二层是O2(n)O_2(n)O2(n)
所以总的是O(n2)O(n^2)O(n2)
例题:计算下面算法执行的次数:
int m=0;
for(i=1;i<=n;i++)
for(j=1;j<=2*i;j++)
m++;
解:
第一层设执行次数是nnn,第二层的总执行次数:2+4+6+...+2∗n2+4+6+...+2*n2+4+6+...+2∗n
因此总的执行次数是第二层的总执行次数:n(n+1)n(n+1)n(n+1)
例题:计算下面算法的时间复杂度:
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
for(k=1;k<=j;k++)
x++;
解:
第一层的算法复杂度是:O1(n)O_1(n)O1(n)
第二层的算法复杂度是:O2(i)O_2(i)O2(i)
第三层的算法复杂度是:O3(j)O_3(j)O3(j)
我们知道:i∗K1=ni*K_1=ni∗K1=n,j∗K2=ij*K_2=ij∗K2=i,常数倍数系数不对数量级别产生影响
因此:O(n3)O(n^3)O(n3)
例题:计算以下算法的时间复杂度:
for(i=0;i<n;i++)
for(j=0;j<m;j++)
a[i][j]=0;
解:
第一层的复杂度:O1(n)O_1(n)O1(n)
第二层的复杂度:O2(m)O_2(m)O2(m)
没有证据证明m和n的关系,因此总的时间复杂度是O(mn)O(mn)O(mn)
# 题型3:简单递归型
没有通用的解法,常见的手段:
三类:
【1】aT(n/b)+f(n)aT(n/b)+f(n)aT(n/b)+f(n)型
【2】T(n−1)+T(n−2)T(n-1)+T(n-2)T(n−1)+T(n−2)型
【3】nT(n)nT(n)nT(n)
对于第一类:
背公式,如果推导的话使用数学归纳法。
O(T(n))={O(nlogba),O(nlogba)>O(f(n))O(f(n)∗logn),O(nlogba)=O(f(n))O(f(n)),O(nlogba)<O(f(n))O(T(n))=\begin{cases}O(n^{\log_ba}),O(n^{\log_ba})>O(f(n))\\O(f(n)*\log n),O(n^{\log_ba})=O(f(n))\\O(f(n)),O(n^{\log_ba})<O(f(n))\end{cases}O(T(n))=⎩⎪⎨⎪⎧O(nlogba),O(nlogba)>O(f(n))O(f(n)∗logn),O(nlogba)=O(f(n))O(f(n)),O(nlogba)<O(f(n))
例题1:已知下面的算法是一个递归方程,n是2的整数次幂,求出该算法的时间复杂度:
T(n)={1,(n=1)2T(n/2)+n,(n>1)T(n)=\begin{cases}1,(n=1)\\2T(n/2)+n,(n>1)\end{cases}T(n)={1,(n=1)2T(n/2)+n,(n>1)
解:
套公式了,O(f(n))=nO(f(n))=nO(f(n))=n,O(nlog22)=nO(n^{\log_2 2})=nO(nlog22)=n
属于第二种情况:O(nlog2n)O(n\log_2n)O(nlog2n)
本文介绍了数据结构的基础概念,包括逻辑结构、存储结构和数据运算,并深入探讨了算法效率分析,涉及时间复杂度和空间复杂度的计算方法,以及不同算法的时间复杂度评估。
3159

被折叠的 条评论
为什么被折叠?



