目录
1.1数据结构
需要从数据入手来分析并得到解决问题的方法
. 数据是描述客观事物的数、字符以及所有能输入到计算机中并被计算机程序处理的符号的集合。
· 数据元素是数据的基本单位(例如,A班中的每个学生记录都是一个数据元素),也就是说数据元素是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理
· 数据项是具有独立含义的数据最小单位,也称为成员或域(例如,A班中每个数据元素即学生记录是由学号、姓名、性别和班号等数据项组成)。
·数据对象是性质相同的有限个数据元素的集合,它是数据的一个子集。如大写字母数据对象是集合C=['A','B',.....,'C',…,'Z'];1~100的整数数据对象是集合N=[1,2,…,100]。默认情况下,数据结构中的数据都指的是数据对象。
·数据结构是指所涉及的数据元素以及数据元素之间的关系,可以看作是相互之间存在着特定关系的数据元素的集合。可时把数据结构看成是带结构的数据元素的集合。
逻辑结构
数据元素之间具有的逻辑关系(结构)
线性结构:如线性表、堆栈、队列、串、文件等
非线性结构:如图、二叉树等
储存结构
数据结构在计算机中的表示(或称映像)称为数据的储存结构,又称为物理结构。
顺序存储方法(顺序存储结构)
链接存储方法(链式存储结构)
索引存储方法
散列存储方法
1.2复杂度分析
1.2.1时间复杂度
随着输入规模的扩大,执行时间的这一变化趋势可表示为输入规模的一个函数,称作该算法的时间复杂度(time complexity)。规模并不能唯一确定具体的输入,规模相同的输入通常都有多个,而算法对其进行处理所需时间也不尽相同。以排序为例,由n个元素组成的输入序列有n!种,有时所有元素都需交换,有时却无需任何交换。即从保守预估估的角度出发,在规模为n的所有输入中选择执行时间最长的作为T(n),并以T(n)度量该算法的时间复杂度。
1.2.2渐进复杂度
在处理更大规模的问题时,效率的细微差异都将对实际执行效果产生巨大的影响。这种着眼长远、更为注重时间复杂度的总体变化趋势和增长速度的策略与方法,即所谓的渐进分析(asymptotic analysis)。
大O记号
出于保守的估计(最坏情况),我们首先关注T(n)的渐进上界。此时可引入"大O记号"(big-O notation)。当存在正的常数c和函数f(n)(f(n)表示每行代码执行的次数总和),使得对任何n >> 2都有 T(n) ≤ c·f(n) 则可认为在n足够大之后,f(n)给出了T(n)增长速度的一个渐进上界。此时,记之为:
T(n) = O(f(n))
由这一定义,可导出大O记号的以下性质:
(1)对于任一常数c > 0,有O(f(n)) = O(c·f(n))
(2)对于任意常数a > b > 0, 有0(n^a + n^b)= O(n^a)
前一性质意味着,在大0记号的意义下,函数各项正的常系数可以忽略并等同于1。后一性质则意味着,多项式中的低次项均可忽略,只需保留最高次项。
大Ω记号
为了对算法的复杂度最好情况做出估计,我们首先关注T(n)的渐进上界。此时可引入"大Ω记号"(big-omega notation)。当存在正的常数c和函数g(n),使得对于任何n >> 2都有 T(n) ≥ c·g(n)就可以认为,在n足够大之后,g(n)给出了T(n)的一个渐进下界。此时,我们记之为:
T(n)= Ω(g(n))
大Θ记号
借助大O记号、大Ω记号,可以对算法的时间复杂度作出定量的界定,亦即,从渐进的趋势看,T(n)介于Ω(g(n))与Θ(f(n))之间,会恰巧出现g(n)= f(n)的情况。此时可引入"大Θ记号"(big-theta notation)。当存在正的常数c1< c2和函数h(n),使得对于任何n >> 2都有 c1·h(n) ≤ T(n) ≤ c2·h(n)就可以认为在n足够大之后,h(n)给出了T(n)的一个确界。此时,我们记之为:
T(n) = Θ(h(n))
1.2.3空间复杂度
除了执行时间的长短,算法所需存储空间的多少也是衡量其性能的一个重要方面,此即所谓的空间复杂度(space complexity)。时间复杂度所引入的几种渐进记号,除了执行时间的长短,算法所需存储空间的多少也是衡量其性能的一个重要方面,所以也适用于对空间复杂度的度量。
需要注意的是除非特别申明,空间复杂度通常并不计入原始输入本身所占用的空间,反之,其它(如转储、中转、索引、映射、缓冲等)各个方面所消耗的空间,则都应计入。
1.2.4常见复杂度
常数O(1)
常量级时间复杂度,通常不含循环、分支、子程序调用等
对数O(logn)
//统计整数二进制展开中位数1 的总数
int countOnes(unsigned int n){
int ones=0; //计数器复位
while(0<n){
ones+=(1&n); //检查最低位,若为1则计数
n>>1; //右移一位
}
return ones; //返回计数值
}
线性O(n)
//数组求和
int sumA(int a[],int n){
int sum=0; //初始化累加器
for(int i=0;i<n;i++){ //循环n次
sum+=a[i];
}
return sum; //返回累加和
}
多项式O(polynomial(n))
一般为多重循环嵌套,注意观察每层循环条件
指数O(2^n)
//幂的2^n算法(蛮力迭代版)
long long pow2(int n){
long long=1; //累积器初始化为1
while(1<n--){ //迭代n轮
pow<<=1; //左移一位
}
return pow; //返回结果
}
例题
1.算法分析的两个主要方面是时间复杂度和空间复杂度的分析。 ( √)
2.将长度分别为m,n的两个单链表合并为一个单链表的时间复杂度为O(m+n)。 (×)
3.the lowest upper bound of the time complexity is O(N^4). ( √)
if ( A > B ){
for ( i=0; i<N*4; i++ )
for ( j=N*N; j>i; j-- )
C += A;
}
else {
for ( i=0; i<N*N/20; i++ )
for ( j=3*N; j>i; j-- )
for ( k=0; k<N*3; k++)
C += B;
}
4.斐波那契数列FN的定义为:F0=0, F1=1, FN=FN−1+FN−2, N=2, 3, …。用递归函数计算FN的空间复杂度是O(N)。 (×)
5.在具有N个结点的单链表中,访问结点和增加结点的时间复杂度分别对应为O(1)和O(N)。 (×)
6.对于某些算法,随着问题规模的扩大,所花的时间不一定单调增加。 ( √)
7.NlogN^2和NlogN具有相同的增长速度。 ( √)
8.2^N和N^N具有相同的增长速度。 (×)
9.算法可以没有输入,但是必须有输出。 ( √)
10.将N个数据按照从小到大顺序组织存放在一个单向链表中。如果采用二分查找,那么查找的平均时间复杂度是O(logN)。 (×)
11.对于顺序存储的长度为N的线性表,访问结点和增加结点的时间复杂度分别对应为O(1)和O(N)。 ( √)
12.斐波那契数列FN的定义为:F0=0, F1=1, FN=FN−1+FN−2, N=2, 3, …。用递归函数计算FN的时间复杂度是O(N!)。 (×)
13.对n个整数排序,在最坏的情况下,不能保证以少于O(n)的时间完成。 ( √)
14.下面代码段的时间复杂度是( O(n^2) )。
s=0;
for ( i=0; i<n; i++ )
for( j=0; j<n; j++ )
s+=B[i][j];
sum=s;
15 .下面程序的时间复杂度为( O(m*n) )。
for(i = 0; i < m; i++)
for(j = 0; j < n; j++ )
A[i][j] = i*j;
16.与数据元素本身的形式、内容、相对位置、个数无关的是数据的( 逻辑结构 )。
17.下面代码段的时间复杂度是( O(log3n) )。
i=1;
while( i<=n )
i=i*3;
18.以下说法正确的是( D )。
A.数据元素是数据的最小单位
B.数据项是数据的基本单位
C.数据结构是带有结构的各数据项的集合
D.一些表面上很不相同的数据可以有相同的逻辑结构
19.下面程序段的时间复杂度是( O(1) )。
x=90;
y=100;
while(y>0)
if(x>100)
{ x=x-10; y--; }
else x++;
20. 下面程序段的时间复杂度是( O(n^2) )。
x=0;
for( i=1; i<n; i++ )
for ( j=1; j<=n-i; j++ )
x++;
21.给定N×N的二维数组A,则在不改变数组的前提下,查找最大元素的时间复杂度是:( O(n^2) )
22. 给定程序时间复杂度的递推公式:T(1)=1,T(N)=2T(N/2)+N。则对该程序时间复杂度最接近的描述是:( O(N*logN) )。
23.要判断一个整数N(>10)是否素数,我们需要检查3到根号N之间是否存在奇数可以整除N。则这个算法的时间复杂度是:( O(根号N) )
24.
在具有N个结点的单链表中,实现下列哪个操作,其算法的时间复杂度是O(N)?( C )
A.在地址为p的结点之后插入一个结点
B.删除开始结点
C.遍历链表和求链表的第i个结点
D.删除地址为p的结点的后继结点