1.数据结构的基础概念
1.1数据(Data)
数据是描述客观事物的数值、字符以及能输入机器且能被处理的各种符号集合。也就是说,数据是对客观事物采用计算机能够识别、存储和处理的形式所进行描述。简而言之,数据就是计算机化的信息。
目前,数据已经由纯碎的数值概念发展为图像、字符、声音等各种符号。
1.2数据元素(Data Element)
数据元素是组成数据的基本单位,是数据集合的个体,在计算机中通常作为一个整体进行考虑和处理。一个数据元素可以由一个或者多个数据项组成,数据项(Data Item)是有独立含义的最小单位。比如学籍表就是一个数据,描述每位同学的基本信息就是数据元素,而像地址之类的就是数据项。
1.3数据对象(Data Object)
数据对象是性质相同的数据元素的集合,是数据的一个子集。比如整数数据对象,字母字符数据对象,符合数据对象(学籍表),他们共同的特点就是性质相同的数据元素的集合。
1.4数据结构(Data Structure)
数据结构是指相互之间存在一种或多种特定关系的数据元素集合,数据结构应该包括数据元素集合以及元素关系的集合。像表结构和树结构都为数据结构。
数据结构包括以下三方面内容:
#数据元素之间的逻辑关系,称为逻辑结构。
#数据元素及其关系在计算机内存中的表示(又称映像),称为数据的物理结构或者存储结构。
#数据的运算和实现,即对数据元素可以施加的操作,以及这些操作在相应的存储结构上的实现。
数据结构的两个层次以及他们的关系
逻辑结构:
#描述数据元素之间的逻辑关系。
#与数据的存储无关,独立于计算机
#是从具体问题抽象出来的数学模型
物理结构:
#数据元素及其关系在计算机存储器中的结构(存储方式)
#是数据结构在计算机中的表示
两者关系:
#物理结构是逻辑关系的映像与元素本身的映像
#逻辑结构是数据结构的映像,存储结构是数据结构的实现
1.5数据类型(Data Type)
数据类型是一组性质相同的值集合以及定义在这个值集合上的一组操作的总称。数据类型中定义了两个集合,即该类型的取值范围以及该类型中可允许使用的一组运算。
1.6抽象数据类型(ADT)
抽象的本质是抽取反映问题的本质点,而忽略非本质的点,它最重要的特点是数据抽象与信息屏蔽。
2.数据结构的内容
数据结构是指相互之间存在一种或多种特定关系的数据元素集合。而数据元素见的相互关系具体应包括三个方面:数据的逻辑结构、数据的存储结构、数据的运算集合。
2.1逻辑结构
数据的逻辑结构是指数据元素之间逻辑关系的描述。
数据结构的形式定义为:数据结构是一个二元组。
根据数据元素之间关系的不同特性,通常由下列四类基本结构。
集合结构:结构中的数据元素之间除了同属于一个集合的关系外,无任何其他关系。
线性结构:结构中的数据元素之间存在着一对一的线性关系
树状结构:结构中的数据元素之间存在着一对多的层次关系
图状结构或网状结构:结构中的数据元素之间存在着多对多的任意关系。
2.2存储结构
存储结构是逻辑结构在计算机中的存储映像,包括数据元素映像和关系映像,它是逻辑结构在计算机中的实现,它包括数据元素的表示和关系的表示。
2.3运算集合
提供对数据元素增删改查等操作就是数据的操作集合。
按某种逻辑关系组织起来的一批数据,按一定的映像方式把他们存放在计算机的存储器中,并在这些数据上定义一个运算的集合。
3.算法
3.1算法定义
算法是规则的有限集合,是为解决特定问题而规定的一系列操作。
3.2算法的特征
#有限性:有限步骤之内正常结束,不能形成无穷循环。
#确定性:算法中的每一个步骤必须由确定的含义,无二义性
#可行性:原则上能精确进行,操作可通过已实现的基本运算执行有限次而完成。
#输入:0或多个输入
#输出:至少有一个或多个输出。
3.3算法设计的要求
#正确性
#可读性
#健壮性
#高效率和底存储量
3.4算法的时间复杂度
若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)得极限为不等于零得常数,则称f(n)是T(n)的同数量级函数。记作:T(n) = O(f(n)).这里称O(f(n))为算法的渐进时间复杂度。
如何计算一个算法的时间复杂度?
算这个时间复杂度实际上只需要遵循如下守则:
用常数1来取代运行时间中所有加法常数;
只要高阶项,不要低阶项;
不要高阶项系数;
2.0:常见的时间复杂度:
按增长量级递增排列,常见的时间复杂度有:
O(1)—常数阶
O(N)—线性阶
O(log2N)—对数阶
O(nlogn)—线性对数阶
O(n^2)—平方阶
3.4.1:O(1)—常数阶
O(1)的算法是一些运算次数为常数的算法。例如:
temp=a;
a=b;
b=temp;
1
2
3
根据守则:
用常数1来取代运行时间中所有加法常数;
上面语句共三条操作,单条操作的频度为1,即使他有成千上万条操作,也只是个较大常数,这一类的时间复杂度为O(1);
3.4.2:O(N)—线性阶
O(n)的算法是一些线性算法。例如:
sum=0;
for(i=0;i<n;i++)
sum++;
1
2
3
上面代码中第一行频度1,第二行频度为n,第三行频度为n,所以f(n)=n+n+1=2n+1。
根据守则:
只要高阶项,不要低阶项目,常数项置为1,去除高阶项的系数:
所以时间复杂度O(n)。这一类算法中操作次数和n正比线性增长。
3.4.3:O(log2N)—对数阶
什么是对数?
a^x = N,(a>0 && a!=1),那么x即是以a为底,N的对数,记作
其中a叫做对数的底数,N叫做真数。
例1:
private static void 对数阶() {
int number = 1;//执行1次
int n = 100;//执行1次
while (number < n) {
number = number * 2; // 执行n/2次
System.out.println("哈哈");//执行1次
}
}
1
2
3
4
5
6
7
8
9
10
假设n为100,number是1,小于100退出循环。
第1次循环,number = 2,2^1。
第2次循环,number = 4, 2^2。
第3次循环,number = 8, 2^3。
第x次循环,number = 2^x
也就是2^x=n得出x=log₂n。因此它的复杂度为O(logn)。
例2:
二分查找;
比如: 1,3,5,6,7,9;找出7
如果全部遍历时间频度为n;
二分查找每次砍断一半,即为n/2;
随着查询次数的提升,频度变化作表:
查询次数 时间频度
1 n/2
2 n/2^2
3 n/2^3
k n/2^k
当最后找到7的时候时间频度则是1;
也就是:
n/2^k = 1;
n = 2^k;
k则是以2为底,n的对数,就是Log2N;
那么二分查找的时间复杂度就是O(Log2N);
3.4.4:O(nlogn)—线性对数阶
上面看了二分查找,是LogN的(LogN没写底数默认就是Log2N);
线性对数阶就是在LogN的基础上多了一个线性阶;
比如这么一个算法流程:
数组a和b,a的规模为n,遍历的同时对b进行二分查找,如下代码:
for(int i =0;i<n;i++)
binary_search(b);
}
1
2
3
4
3.4.5:O(n^2)—平方阶
普通嵌套循环
private static void 普通平方阶(){
int n = 100;
for (int i = 0; i < n; i++) {//执行n次
for (int j = 0; j < n; j++) {//执行n次
System.out.println("哈哈");
}
}
}
1
2
3
4
5
6
7
8
这种就是2层循环嵌套起来,都是执行n次,属于乘方关系,它的时间复杂度为O(n^2)。
等差数列嵌套循环
private static void 等差数列平方阶() {
int n = 100;
for (int i = 0; i < n; i++) {//执行n次
for (int j = i; j < n; j++) {//执行n - i次
System.out.println("哈哈");
}
}
}
1
2
3
4
5
6
7
8
基本式:
i = 0,循环执行次数是 n 次。
i = 1,循环执行次数是 n-1 次。
i = 2,循环执行次数是 n-2 次。
…
i = n-1,循环执行的次数是 1 次。
换算式:
result = n + (n - 1) + (n - 2) … + 1
被加数递减,抽象为一个等差数列求n项和的问题,公差为1,带入公式,Sn = n(a1 + an ) ÷2
result = (n(n+1))/2
result = (n^2+n)/2
result = (n^2)/2 + n/2
粗略计算时间复杂度的三部曲:
1.去掉运行时间中的所有加法常数。
没有加法常数,不考虑。
2.只保留最高阶项。
最高阶参考上面列出的按增长量级递增排列,于是只需要保留result = (n^2)/2
3.如果最高阶项存在且不是1,去掉与这个最高阶相乘的常数得到时间复杂度
除以2相当于是乘以二分之一,去掉它,就得到,result = n^2, 所以这个算法的时间复杂度为O(n^2)。
3.时间复杂度的优劣对比
常见的数量级大小:越小表示算法的执行时间频度越短,则越优;
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)//2的n方<O(n!)<O(nn)//n的n方
3.5算法的空间复杂度
类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。
记作:S(n) = O(f(n)).
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。算法的输入输出数据所占用的存储空间是由要解决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。存储算法本身所占用的存储空间与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。算法在运行过程中临时占用的存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地\"进行的,是节省存储的算法;有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如将在第九章介绍的快速排序和归并排序算法就属于这种情况。
如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为0(10g2n);当一个算法的空I司复杂度与n成线性比例关系时,可表示为0(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。