数据结构研究的内容:
研究非数值计算的程序设计问题中计算机的操作对象以及它们之间的关系和操作。
一、数据结构定义
1.与数据有关的定义
- 数据:输入到计算机中的描述客观事物的符号。
- 数据元素:数据的基本单位,也称结点或记录。
- 数据项:有独立含义的数据最小单位,也称域。
三者之间的关系:数据>数据元素>数据项 例:学生表>个人记录>学号、姓名(某一组内容)
- 数据对象:相同特性数据元素的集合,是数据的子集
2.数据结构的定义
相互之间存在一种或多种特定关系的数据元素的集合
3.数据结构三要素
(1)逻辑结构
数据元素之间抽象化的相互关系,与数据的存储无关,独立于计算机存在。
例:集合、线性表、树、图都是逻辑结构。
(2)存储结构
数据元素及其关系在计算机存储器中的存储方式
例:顺序存储、链式存储、索引存储、散列存储
1.有序表属于逻辑结构(可链表可数组)
2.数据的逻辑结构独立于存储结构
3.链式存储设计时,结点内的存储单元地址连续,内存不连续。
(3)数据的运算
为了获取一些预期结果,来进行的一些操作
例:插入、删除、修改、查找、排序、取值。
二、抽象数据类型(ADT)
ADT=(D S P)
- 类型名称:这个数据类型的名字
- 数据对象集:要操作的数据集合(集合、矩阵、数组等)
- 操作集:一些数据计算的操作(一般以函数形式呈现)
例:“矩阵”的抽象数据类型定义
类型名称:Matrix
数据对象集:一个MN的矩阵 A。
操作集:对于任意矩阵A、B、C in Matrix,以及正整数i、j、M、N,以下仅列出几项有代表性的操作。
1)Matrix Create( int M, int N ):返回一个M×N的空矩阵;
2) int GetMaxRow( Matrix A ):返回矩阵A的总行数;
3)int GetMaxCol( Matrix A ):返回矩阵A的总列数;
4)ElementType GetEntry( Matrix A, int i, int j ):返回矩阵A的第i行、第j列的元素;
5)Matrix Add( Matrix A, Matrix B ):如果A和B的行、列数一致,则返回矩阵C=A+B,否则返回错误标志;
6) Matrix Multiply( Matrix A, Matrix B ):如果A的列数等于B的行数,则返回矩阵C = AB,否则返回错误标志;
三、算法
1.定义
一个算法是解决某一类问题的步骤的描述。一般而言,算法应该符合以下五项要求:
- 输入:它接受一些输入(有些情况下不需要输入);
- 输出:至少产生一个输出;
- 确定性:算法的每一步必须有充分明确的含义,不可以有歧义;
- 有限性(有穷性):算法是一个有限指令集,并一定在有限步骤之后终止;
- 可行性:算法的每一步必须在计算机能处理的范围之内
上面是算法的五大要素,此外,一个算法是不依赖于任何语言存在的。(可用伪代码表示)
2.算法复杂度
-
空间复杂度S(n) ——根据算法写成的程序在执行时占用存储单元的长度。
-
时间复杂度 T(n) ——根据算法写成的程序在执行时耗费时间的长度。
本质我们描述时间复杂度的时候,可以通过描述进行的次数来对比
在研究时间复杂度和空间复杂度时,通常有下面的定义方式。 -
[定义1.1] T (n) = O(f(n)) 表示存在常数c > 0, n0 > 0 ,
使得当 n ≥ n0 时有 T (n) ≤ c f(n) 最常用 -
[定义1.2] T (n) = Ω(g(n)) 表示存在常数c > 0, n0 > 0 ,
使得当 n ≥ n0 时有 T (n) ≥ c g(n) -
[定义1.3] T (n) = Θ(h(n)) 表示
T (n) = O(h(n)) 同时T (n) = Ω(h(n))
一些常用的算法的时间复杂度快慢如下:
O(1) < O(㏒n) < O(n) < O(n㏒n) < O(n²) < O(n³)<O(2的n次方) < O(n!) < O(n的n次方)
下面用一些实例来体会时间复杂度
(1)for循环执行n次,时间复杂度为n
for(i=0;i<n;i++)
k++;
时间复杂度为O(N)
(2)若干层嵌套循环时间复杂度等于各层循环次数的乘积再乘以循环体代码的复杂度。
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{ x=y*x + z; k++;}
时间复杂度为O(N²)
(3)if-else 结构的复杂度取决于if的条件判断复杂度和分枝的复杂度,总体复杂度取最大。
if (P1) /* P1的复杂度为 O(f1) */
P2; /* P2的复杂度为 O(f2) */
else
P3; /* P3的复杂度为 O(f3) */
总复杂度为 max( O(f1), O(f2), O(f3) )
(4)有时可以通过设未知数解方程求次数
i=1;
while(i<=n)
i*=2;
设次数为k,那么就有2的k次方≤n
解得k≤㏒n 取最大值时间复杂度为O(㏒n)
(5)递归函数的时间复杂度,可写出递推方程来分析。
例:求T(N)=2T(N/2)+O(N)的时间复杂度
T(N)=2*[2T((N/2)/2)+O(N/2)]+O(N)
=…=2的k次方×T(N/2的k次方)+K×O(N)
当2的k次方=N时,有k=logN
上式=N×T(1)+logN×O(N)=O(NlogN)
实现数据结构,通常从不同的应用中抽象出共性的数据组织与操作方法。
四、实现抽象数据类型的思路
1.构建抽象数据类型
例:在日常数据处理中经常碰到的问题是需要对一组数据进行基本的统计分析。比如,分析一个课程班学生的平均成绩、最高成绩、最低成绩、中位数、标准差等。同样的统计要求也可能发生在其他领域。
创建的抽象数据类型如下:
- 类型名称:对数据集合的统计
- 数据对象集:集合S(存储着所有待处理数据)
- 操作集:
ElementType Average(S):求S中元素的平均值;
ElementType Max(S):求S中元素的最大值;
ElementType Min(S):求S中元素的最小值;
ElementType Median(S):求S中元素的中位数。
2.用程序语言实现
(1)数据存储方式:数组?链表?
数据结构的存储实现跟所需要的操作密切相关
(2)操作实现
通过构造出相关的功能函数,达到操作的封装
例:求集合{ 6 5 9 8 2 1 7 3 4 } 的中位数。
- 算法[1]:集合排序后,取第[n/2]个元素。(方括号为取整函数)
- 算法[2]:递归思想,寻找第K大的数
当K=[n/2]时,集合的第K大整数就是中位数。
伪代码如下:
Element find_k_number(Element s[],int k)
{
选取第一个数为index;//这是一个基准数
将大于等于index的数组成集合S1,小于index组成集合S2;
if(|S1|>=k)
find_k_number(S1,k);
else if(|S1|<k-1)
find_k_number(S2,k-|S1|)
}