《数据结构》课程绪论
使用教材:
《数据结构教程》第5版 李春葆 清华大学出版社
参考书:
《数据结构与算法分析 C语言描述》(第2版) Mark Allen Weiss 机械工业出版社
《算法导论》(第3版) Charles E. Leiserson 等 机械工业出版社
《算法》(第4版) Robert Sedgewick Kevin Wayne 人民邮电出版社
《STL源码剖析》 侯捷 华中科技大学出版社
使用语言:C/C++
1.数据结构基本概念
数据与数据结构
- 数据(data):描述客观事物的数和字符的集合。
- 数据元素(data element):数据的基本单位
- 数据项(data item):具有独立含义的数据最小单位,又叫字段或域。
- 数据结构(data structure):所有数据元素及其构成的关系
数据结构包括的三个方面
- 逻辑结构(logical structure)(数据的逻辑关系)
- 存储结构(storage structure)(物理结构,如何在存储器上表示)
- 运算(operation)(对数据实施的操作,增删改查排序等)
逻辑结构的表示方法
- 图表
- 二元组
B=(D,R)
,其中
D
为数据集合,
R 为数据的所有关系的集合。 - 每一个关系是序偶的集合,对于序偶
<x,y>
<script type="math/tex" id="MathJax-Element-8">
</script>(
x,y∈D
),
x
是
y 的前驱(predecessor), y 是x 的后继(successor)。使用尖括号表示 x 与y 是一个偏序关系,圆括号表示为等价关系。
逻辑结构的类型
- 集合
- 线性结构
- 树形结构
- 图型结构
存储结构
处理存储结构,既要保证存储所有的元素
∀x∈D
,也要存储数据之间的逻辑关系
∀r∈R
。
1. 顺序存储结构:所有元素在存储器中都是连续存放的,逻辑相邻的元素也必然物理相邻。支持快速的查找,插入和删除较慢;
2. 链式存储结构:每个逻辑元素使用内存中的结点存储。所有结点的地址不一定是连续的。C/C++使用指针来链接每一个结点。Java等语言使用引用。支持快速的插入和删除,但是不能随机存取,需要额外浪费指针空间。
3. 索引存储结构:利用关键字,地址的方式建立索引,可以利用关键字的有序性快速查找到关键字地址。查找效率高,但是索引表需要额外空间开销。
4. 哈希(散列)存储结构:直接建立值->存储地址的映射,支持快速查找和插入,不存储数据之间的逻辑关系。
2.算法及其分析
算法的特点
- 有穷性
- 确定性
- 可行性
- 有输入输出
算法设计的目标
- 正确性
- 可使用性
- 可读性
- 健壮性
- 高效率、低存储
算法的时间分析
T(n)
表示元操作出现的频度(注意是实际执行的次数,例如求一个数
n
的所有因子,只需要判断
通常用“大O记号”表示其渐进复杂度。渐进复杂度取其最高阶,忽略系数和低阶项。
最好、最坏、平均时间复杂度
设输入规模
n
,所有输入实例的集合为
平均时间复杂度的定义
最好时间复杂度:
B(n)=min{T(n)},I∈D
,
最坏时间复杂度:
W(n)=max{T(n)},I∈D
.
递归算法分析
先求出递归式,然后对其化简,或使用递归树、主定理等方法。
算法复杂度分析举例
二分查找最坏情况
/**
* 二分查找(升序数组中查找)
* @param int[] A 待查找的数组
* @param int length 数组的长度
* @param int value 待查找的元素
* @return int 该元素在数组中的下标(0开始,未找到则返回-1)
*/
int binary_search(int A[], int length, int value) {
int begin = 0, end = length - 1, mid;
while (begin <= end) {
mid = (begin + end) / 2;
if (A[mid] == value)
return mid;
else if (A[mid] > value)
end = mid - 1;
else
begin = mid + 1;
}
return -1;
}
二分查找的核心语句是while循环。
最坏情况下,数组A中没有值为value的元素,不妨设
A={a1,a2,...,an}
,且
a1<a2<...<an
,
v<a1
。于是:
第一次比较后,剩余
N1=⌊n2⌋
个元素(
⌊n2⌋≤n2
)
第二次比较后剩余 N2=⌊⌊n2⌋−12⌋ 个元素( ⌊⌊n2⌋−12⌋≤n22 ),…,
最后一次(第
k
次)比较剩余
因此最坏情况下的时间复杂度为
O(log2n)
。
选择排序
/**
* 选择排序(升序)
* @param int[] A 待排序数组
* @param int length 数组长度
* @return int 函数返回值,0表示成功
*/
int selection_sort(int A[], int length){
int i, j;
int min, tmp;
for (i = 0; i < length - 1; i++) {
min = i;
for (j = i + 1; j < length; j++)
if (A[min] > A[j])
min = j;
tmp = A[i];
A[i] = A[min];
A[min] = tmp;
}
return 0;
}
核心语句是一条if判断语句,
对于外层for循环,执行
n
次;
内层for循环,执行
因此其时间复杂度是
O(n×n22)=O(n2)
。
二路归并排序
/**
* 二路归并排序
* @param int[] A 待排序数组
* @param int length 数组长度
* @return int 函数返回值,0成功
*/
int merge_sort(int A[], int length) {
int * aux = (int *)malloc(sizeof(int) * length);
merge_sort_sub(A, 0, length - 1, aux);
return 0;
}
/**
* 二路归并排序子函数
* @param int[] A 源数组
* @param int begin 当前排序的子数组的起始下标
* @param int end 当前排序的子数组的结束下标
* @param int[] aux 临时数组
*/
int merge_sort_sub(int A[], int begin, int end, int aux[]) {
int midpoint;
int i, j, k;
if (end <= begin)
return;
midpoint = (end + begin) / 2;
merge_sort_sub(A, begin, midpoint, aux);
merge_sort_sub(A, midpoint + 1, end, aux);
for (k = begin; k <= end; k++)
aux[k] = A[k];
i = begin;
j = midpoint + 1;
for (k = begin; k <= end; k++) {
if (i <= midpoint && j <= end) {
if (aux[i] > aux[j])
A[k] = aux[j++];
else
A[k] = aux[i++];
}
else if (i > midpoint) {
A[k] = aux[j++];
}
else if (j > end) {
A[k] = aux[i++];
}
}
return 0;
}
在递归过程中,设merge_sort_sub一个长度为
n
的数组需要的执行频度为
merge_sort_sub(A, begin, midpoint, aux)贡献为
T(⌊n2⌋)
merge_sort_sub(A, midpoint + 1, end, aux)的贡献为
T(⌈n2⌉)
最后需要对
n
个元素进行归并,贡献为
因此有递归式
T(n)=T(⌊n2⌋)+T(⌈n2⌉)+n
假设
n
是
T(n)=2T(n2)+n
所以
2T(n2)=4T(n4)+n
4T(n4)=8T(n8)+n
…
2k−1T(n2k−1)=2kT(n2k)+n
容易知道起始条件
T(1)=1
,那么令
n2k=1
,解得
k=log2n
所以
T(n)=kn+2kT(n2k)=nlog2n+log2n
所以
T(n)=O(nlog2n)
当为一般情况时,可根据主定理的情况2(
a=2,b=2,f(n)=n
),得出渐进复杂度为
O(nlog2n)
。
本文作为《数据结构》课程的绪论,介绍了数据结构的基本概念,如数据、数据元素、数据结构的逻辑和存储结构。讨论了逻辑结构的表示方法和类型,包括集合、线性结构、树形结构和图型结构。同时,阐述了顺序存储结构、链式存储结构、索引存储结构和哈希存储结构的特点。此外,还探讨了算法的时间复杂度分析,包括最好、最坏、平均时间复杂度,并通过二分查找、选择排序和二路归并排序举例说明。
496

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



