【数据结构】第一章:绪论基础

本文介绍了数据结构的基础概念,包括逻辑结构、存储结构和数据运算,并深入探讨了算法效率分析,涉及时间复杂度和空间复杂度的计算方法,以及不同算法的时间复杂度评估。

【数据结构】第一章:绪论基础

章节纲要

  • 【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 正常数CCCn0n_0n0,使得当n≥n0n\geq n_0nn0时,都满足0≤T(n)≤Cf(n)0\leq T(n) \leq Cf(n)0T(n)Cf(n)OOOT(n)T(n)T(n)的数量级。

计算时间复杂度,我们不仅考虑:问题本身的规模,还需要考虑待输入数据元素的初始状态。

例:假如我们需要在数组A[0...n−1]A[0...n-1]A[0...n1]中查找定值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(log⁡2n)<O(n)<O(nlog⁡2n)<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_2nintlog2n
因此算法复杂度是O(log⁡2n)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=22t=2t+1

解不等式:2t+1<n22^{t+1}<\frac{n}{2}2t+1<2n

解得:t<log⁡2n−2t<\log_2n-2t<log2n2,因此,时间复杂度是:O(log⁡2n)O(\log_2n)O(log2n)

例题:计算下面函数的时间复杂度

int func(int n){
	int  i=0, sum=0;
	while(sum<n)
		sum+=++i;
	return i;
}

:我们设执行次数是tttsum=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(t1)(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++;
	}
}

:设执行次数ttti=ti=ti=tt3<=nt^3<=nt3<=n

所以是O(n13)O(n^{\frac{1}{3}})O(n31),这个和上面的那题不一样,你注意不等式的条件是谁。

例题:问以下的程序的时间复杂度?

x=0while(n>=(x+1)*(x+1)){
	x=x+1;
}

:设执行次数tttx=tx=tx=t

(t+1)2<=n(t+1)^2<=n(t+1)2<=n
解得:t≤n12−1t\leq n^{\frac{1}{2}}-1tn211,因此时间复杂度是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) 设执行次数是ttti=ti=ti=tt<n−1t<n-1t<n1,因此,O(n)O(n)O(n)
(2) 设执行次数是ttty=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≤log⁡2nk=1*2^{t_1}\leq n \\ \therefore t_1\leq \log_2nk=12t1nt1log2n

第一层的复杂度是O1(log⁡2n)O_1(\log_2n)O1(log2n)

设第二层的执行次数是t2t_2t2
j=t2≤nj=t_2\leq nj=t2n

第二层的复杂度是O2(n)O_2(n)O2(n)

总复杂度是:O1∗O2=O(nlog⁡2n)O_1*O_2=O(n\log_2n)O1O2=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+...+2n
因此总的执行次数是第二层的总执行次数: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=niK1=nj∗K2=ij*K_2=ijK2=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(n1)+T(n2)
【3】nT(n)nT(n)nT(n)

对于第一类:
背公式,如果推导的话使用数学归纳法。

O(T(n))={O(nlog⁡ba),O(nlog⁡ba)>O(f(n))O(f(n)∗log⁡n),O(nlog⁡ba)=O(f(n))O(f(n)),O(nlog⁡ba)<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))=nO(nlog⁡22)=nO(n^{\log_2 2})=nO(nlog22)=n

属于第二种情况:O(nlog⁡2n)O(n\log_2n)O(nlog2n)


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值