[一篇读懂]C语言八讲:数据结构概述
- 1. 与408关联解析及本节内容介绍
- 1 与408关联解析
- 2 本节内容介绍
- 2. 逻辑结构与存储结构
- 1 逻辑结构
- 2 存储结构
- 顺序存储
- 链式存储
- 3 顺序存储与链式存储分析
- 顺序存储优缺点
- 链式存储优缺点
- 3. 时间复杂度与空间复杂度
- 1 算法定义
- 2 时间复杂度
- 【示例程序1】
- 【示例程序2】
- 【示例程序3】
- 【示例程序4】
- 【示例程序5】
- 【示例程序6】
- 【思考题】
- 3 空间复杂度
- 总结
- 2
- 2.2
- 2.3
- 3.1
- 3.2
- 3.3
1. 与408关联解析及本节内容介绍
1 与408关联解析
【2009年真题单选第一题】
- 为解决计算机主机与打印机之间速度不匹配问题,通常设置一个打印数据缓冲区,主机将要输出的数据依次写入该缓冲区,而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是______。
A.栈
B.队列
C.树
D.图
【2014年真题单选第一题】
- 下列程序段的时间复杂度是_____。
count = 0;
for(k = 1; k <= n; k *= 2)
for(j = 1; j <= n; j++)
count++;
A.
O
(
log
2
n
)
O\left( \log _2n \right)
O(log2n)
B.
O
(
n
)
O\left( n \right)
O(n)
C.
O
(
n
log
2
n
)
O\left(n\log _2n \right)
O(nlog2n)
D.
O
(
n
2
)
O\left( n^2 \right)
O(n2)
【2017年真题单选第一题】
- 下列函数的时间复杂度是
int func(int n)
{
int i = 0, sum = 0;
while(sum < n) sum += ++i;
return i;
}
A.
O
(
log
n
)
O\left( \log n \right)
O(logn)
B.
O
(
n
1
/
2
)
O\left( n^{1/2} \right)
O(n1/2)
C.
O
(
n
)
O\left(n \right)
O(n)
D.
O
(
n
log
n
)
O\left( n\log n \right)
O(nlogn)
【2019年真题单选第一题】
- 设n是描述问题规模的非负整数,下列程序段的时间复杂度是
x=0;
while(n >= (x + 1) * (x + 1))
x = x + 1;
A.
O
(
log
n
)
O\left( \log n \right)
O(logn)
B.
O
(
n
1
/
2
)
O\left( n^{1/2} \right)
O(n1/2)
C.
O
(
n
)
O\left(n \right)
O(n)
D.
O
(
n
2
)
O\left( n^2 \right)
O(n2)
以及综合应用题也要求说明设计的算法的时间复杂度,或者空间复杂度。
2 本节内容介绍
本节分为两小节讲解。
- 第一小节主要讲解什么是逻辑结构,逻辑结构有哪些,什么是存储结构,存储结构有哪些逻辑结构和存储结构之间有什么关系。
- 第二小节主要讲解什么是时间复杂度,时间复杂度如何计算,各种例子实战时间复杂度的计算,什么是空间复杂度。
2. 逻辑结构与存储结构
两者对比:
- 逻辑结构:数据元素之间的逻辑关系 - 抽象的
对人友好 - 存储结构:数据结构在计算机中的表示 - 具体的
对计算机友好
1 逻辑结构
2 存储结构
- 计算机中任何逻辑结构,都能由顺序存储和链式存储来实现。
顺序存储
- 典型的顺序存储 - 数组
C语言实现:
int Array[6]={ l,2,3,4,5,6};//定义数组并初始化
printf ("%d\n", Array[3])//随机访问第4个元素
- 可以任意访问某个地址,时间复杂度相等
链式存储
- 每个元素不连续,通过指针指向下一个节点。
C语言实现:
Typdef struct Lnode
{
ElemType data;
struct Lnode *next;
}Lnode,*LinkList;
Lnode *L;
L = (LinkList)malloc(sizeof(Lnode));
A -> next = B; B -> next = C
3 顺序存储与链式存储分析
顺序存储优缺点
优点:
- 可以实现随机存取。
- 每个元素占用最少的空间。
缺点:
- 只能使用整块的存储单元,会产出较多的碎片。
链式存储优缺点
优点:
- 充分利用所有存储单元,不会出现碎片现象。
缺点:
- 需要额外的存储空间用来存放下一结点的指针。
- 只能实现顺序存取。
3. 时间复杂度与空间复杂度
1 算法定义
- 算法的定义:对特定问题求解步骤的描述。
- 算法的特性:有穷;确定;可行;输入;输出。
2 时间复杂度
- 时间复杂度指算法中所有语句的频度(执行次数)之和。
- 记为:
T
(
n
)
=
O
(
f
(
n
)
)
T(n)=O(f(n))
T(n)=O(f(n))
其中, n n n是问题的规模; f ( n ) f(n) f(n)是问题规模 n n n的某个函数。
表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同。
- 常见的时间复杂度:
O ( 1 ) < O ( log 2 n ) < O ( n ) < O ( n log 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) O\left( 1 \right) <O\left( \log _2n \right) <O\left( n \right) <O\left( n\log _2n \right) <O\left( n^2 \right) <O\left( n^3 \right) <O\left( 2^n \right) <O\left( n! \right) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)
最高阶数越小,说明算法的时间性能越好。
常见的时间复杂度量级:
【示例程序1】
int sum = 0; //执行一次
sum = n *(n+1)/2; //执行一次
printf("%d", sum); //执行一次
算法的执行次数等于3。
时间复杂度为
T
(
n
)
=
O
(
1
)
T(n)=O(1)
T(n)=O(1)
表示不会随n的增长而增长。
并没有循环,程序只执行了一次。
执行次数是固定的。
不存在O(2)、O(3)等等。
【示例程序2】
【2011年计算机联考真题】
int x = 2;
while(x < n / 2)
x = 2 * x;
执行频率最高的语句为“x=2*x”。
设该语句共执行了t次,则
2
t
+
1
<
n
/
2
2^{t+1}<n/2
2t+1<n/2,故
t
=
l
o
g
2
(
n
/
2
)
−
1
=
l
o
g
2
n
−
2
t=log_2(n/2)-1=log_2n-2
t=log2(n/2)−1=log2n−2
时间复杂度
T
(
n
)
=
O
(
l
o
g
2
n
)
T(n)=O(log_2n)
T(n)=O(log2n)
x的值为2、4、8、16……,即2的幂次增长。
运行t次,x为 2 t + 1 2^{t+1} 2t+1要 < n / 2 <n/2 <n/2,两边同时取对数。
得到t。
【示例程序3】
int sum = 0, i = 1;
while(i < n)
{
sum = sum + i;
i++;
}
printf("%d",sum);
执行频率最高的语句是while循环体中的代码。
一共执行n-1次。
时间复杂度
T
(
n
)
=
O
(
n
)
T(n)=O(n)
T(n)=O(n)
【示例程序4】
int i, x = 2;
for(i = 0; i < n; i++)
{
x = 0;
while(x < n / 2)
x = 2 * x;
}
执行频率最高的语句为“x=2*x”。
设该语句内层循环执行了
l
o
g
2
n
次
log_2n次
log2n次,外层执行了n次,因此总计执行次数为
n
l
o
g
2
n
nlog_2n
nlog2n次。
时间复杂度
T
(
n
)
=
O
(
n
l
o
g
2
n
)
T(n)=O(nlog_2n)
T(n)=O(nlog2n)
【示例程序5】
int i, j;
for(i = 0; i < n; i++)
{
for(j = 0; j < m; j++)
sum = sum + 1;
}
对于外层循环,相当于内部时间复杂度为O(m)的语句再循环n次。
所以时间复杂度
T
(
n
)
=
O
(
m
×
n
)
T(n)=O(m×n)
T(n)=O(m×n)
如果m=n,则时间复杂度
T
(
n
)
=
O
(
n
2
)
T(n)=O(n^2)
T(n)=O(n2)
时间复杂度的乘法规则
【示例程序6】
int sum1 = 0, sum2 = 0, i, j;
for(i = 0; i < n; i++)
sum1 = sum1 + i;
for(j = 0; j < m; j++)
sum2 = sum2 + j;
printf("%d, %d", sum1, sum2);
}
两个循环没有嵌套,串行执行。
所以时间复杂度
T
(
n
)
=
O
(
n
)
+
O
(
m
)
T(n)=O(n)+O(m)
T(n)=O(n)+O(m)
取最大的,即时间复杂度
T
(
n
)
=
m
a
x
(
O
(
n
)
,
O
(
m
)
)
T(n)=max(O(n),O(m))
T(n)=max(O(n),O(m))
时间复杂度的加法规则
【思考题】
如果一个算法的执行次数为 3 n 3 + 5 n 3n^3+5n 3n3+5n,那么该算法的时间复杂度为多少?
答案是 O ( n 3 ) O(n^3) O(n3)
- 时间复杂度计算忽略高阶项系数和低阶项。
3 空间复杂度
-
空间复杂度S(n)指算法运行过程中所使用的辅助空间的大小。
-
记为:
S ( n ) = O ( f ( n ) ) S(n)=O(f(n)) S(n)=O(f(n)) -
除了需要存储算法本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元。
-
若输入数据所占空间只取决于问题本身,和算法无关,这样只需分析该算法在实现时所需的辅助单元即可。
-
算法原地工作是指算法所需的辅助空间是常量,即 O ( 1 ) O(1) O(1)。
空间复杂度 O ( 1 ) O(1) O(1)
例如,n个元素数组排序,不使用额外的空间(随着n的增长而增长的空间),空间复杂度就是 O ( 1 ) O(1) O(1)。
总结
2
- 逻辑结构:数据元素之间的逻辑关系 - 抽象的
对人友好 - 存储结构:数据结构在计算机中的表示 - 具体的
对计算机友好
2.2
- 计算机中任何逻辑结构,都能由顺序存储和链式存储来实现。
2.3
顺序存储优缺点:
优点:
- 可以实现随机存取。
- 每个元素占用最少的空间。
缺点:
- 只能使用整块的存储单元,会产出较多的碎片。
链式存储优缺点:
优点:
- 充分利用所有存储单元,不会出现碎片现象。
缺点:
- 需要额外的存储空间用来存放下一结点的指针。
- 只能实现顺序存取。
3.1
- 算法的定义:对特定问题求解步骤的描述。
- 算法的特性:有穷;确定;可行;输入;输出。
3.2
- 时间复杂度指算法中所有语句的频度(执行次数)之和。
- 记为:
T
(
n
)
=
O
(
f
(
n
)
)
T(n)=O(f(n))
T(n)=O(f(n))
其中, n n n是问题的规模; f ( n ) f(n) f(n)是问题规模 n n n的某个函数。
表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同。
- 常见的时间复杂度:
O ( 1 ) < O ( log 2 n ) < O ( n ) < O ( n log 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) O\left( 1 \right) <O\left( \log _2n \right) <O\left( n \right) <O\left( n\log _2n \right) <O\left( n^2 \right) <O\left( n^3 \right) <O\left( 2^n \right) <O\left( n! \right) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)
最高阶数越小,说明算法的时间性能越好。
- 时间复杂度计算忽略高阶项系数和低阶项。
3.3
-
空间复杂度S(n)指算法运行过程中所使用的辅助空间的大小。
-
记为:
S ( n ) = O ( f ( n ) ) S(n)=O(f(n)) S(n)=O(f(n)) -
除了需要存储算法本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元。
-
若输入数据所占空间只取决于问题本身,和算法无关,这样只需分析该算法在实现时所需的辅助单元即可。
-
算法原地工作是指算法所需的辅助空间是常量,即 O ( 1 ) O(1) O(1)。
空间复杂度 O ( 1 ) O(1) O(1)
例如,n个元素数组排序,不使用额外的空间(随着n的增长而增长的空间),空间复杂度就是 O ( 1 ) O(1) O(1)。