今天的主题是:数据结构与算法;按老规矩给一张知识导图:
1.设n是描述问题规模的非负整数,下列程序段的时间复杂度是()。
x=0;
while(n>=(x+1) * (x+1) )
x=x+1;
A.O(log2^(n)) B.O(n^1/2) C.O(n) D.O(n^2)
解析:循环条件是n>=(x+1)^2,循环执行的次数是[n^1/2],因此时间复杂度是O(n^1/2)。
2.下列函数的时间复杂度是()。
int func(int n){
int i=0,sum=0;
while(sum<n)
sum+=++i;
return i;
}
A.O(log2^(n)) B.O(n^1/2) C.O(n) D.O(nlog2^(n))
解析:sum+=++i可以分解为两条语句,即++i和sum=sum+i,则该while循环中,1+2+3+……+i<n,即i(i+1)/2<n,因此时间复杂度是O(n^1/2)。
3.下列程序段的时间复杂度是()。
count=0;
for(k=1;k<=n;k∗=2)
for(j=1;j<=n;j++)
count++;
A.O(log2^(n)) B.O(n) C.O(nlog2^(n)) D.O(n^2)
解析:这是一个双重for循环嵌套的程序。在内层循环中,每次循环j都自增1,每次内层循环执行n次,时间复杂度是O(n);在外层循环中,每次循环的增量定义为k∗=2,时间复杂度是O(log2^(n))。因此这个程序段的时间复杂度是O(n)∗O(log2^(n)),即O(nlog2^(n))。
4.下列T(n)表示各算法中最耗时操作的执行次数,n表示数据量,请按照时间复杂度从小到大排列()。
T1(n)=100n+200log2^(n) T2(n)=3n^2 T3(n)=10000000 T4(n)=300log2^(n)
解析:当n充分大时,函数的增长关系为c<log2^(n)<n<n^2(c是与n无关的任意整数),因此T3<T4<T1<T2。
5.下列关于数据的逻辑结构的叙述中,不正确的是()。
A.数据的逻辑结构是数据间关系的描述
B.线性表是典型的线性结构
C.数据的逻辑结构分为线性结构和非线性结构
D.数据的逻辑结构不仅反映数据间的逻辑关系,而且包含其在计算机中的存储方式
解析:数据的逻辑是依据具体问题的需要而建立的数据元素和它们之间的关系,它只抽象地反映数据元素之间的逻辑关系,而与其在计算机中的存储方式无关。数据的存储结构是依据具体问题所要求的响应速度,处理时间,修改时间,存储空间等来实现数据的逻辑结构,是指数据逻辑结构的存储方式。数据的逻辑结构分为线性结构和非线性结构。线性结构包括线性表,栈,队列等。
6.关于数据结构的描述,正确的是()。
A.数据的逻辑结构可以划分为线性结构,树形结构和索引结构
B.一种逻辑结构可采用多种存储结构实现
C.一种存储结构只能实现一种逻辑结构
D.现实世界中数据对象的一对多联系可以采用线性结构表达
解析:数据的逻辑结构分为线性结构,树形结构,图形结构和集合;一种逻辑结构可采用多种不同的存储结构实现,如线性表可采用顺序存储结构和链式存储结构实现;一种存储结构可以实现多种逻辑结构,如链式存储结构可以实现单链表,二叉链表等;线性结构的元素之间是一对一联系,而树形结构的元素之间是一对多联系,图状结构的元素之间是多对多联系。
7.已知表头元素为c的单链表在内存中的存储状态如下表所示。
地址 | 元素 | 链接地址 |
1000H | a | 1010H |
1004H | b | 100CH |
1008H | c | 1000H |
100CH | d | NULL |
1010H | e | 1004H |
1014H |
现将f存放于1014H处并插入到单链表中,若f在逻辑上位于a和e之间,则a,e,f的“链接地址”依次是()。
解析:单链表的每个结点包含数据域和指针域,其中链接地址存放在指针域中,即下一个结点的地址。如图所示:
在插入f之后,a指向f,f指向e,e指向b。a的链接地址为f的内存地址,即1014H;e的链接地址为b的内存地址,即1004H;所以“链接地址依次是1014H,1004H,1010H”。
8.已知一个带有表头结点的双向循环链表L,结点结构为
prev | data | next |
其中,prev和next分别是指向其直接前驱和直接后继结点的指针。现要删除指针p所指的结点,正确的语句序列是()。
A.p->next->prev=p->prev; p->prev->next=p->prev; free(p);
B.p->next->prev=p->next; p->prev->next=p->next; free(p);
C.p->next->prev=p->next; p->prev->next=p->prev; free(p);
D.p->next->prev=p->prev; p->prev->next=p->next; free(p);
解析:双向循环链表的删除与单链表类似,都是使得链表不断开。双向循环链表删除指针p所指的结点,先将p所指的结点的直接后继的前驱指针指向p所指结点的直接前驱,即p->next->prev=p->prev;再将p所指结点的直接前驱的后继指针指向p所指结点的直接后继,即p->prev->next=p->next;最后再删除指针p所指的结点,即free(p);
9.某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用()存储方式最节省运算时间。
A.单链表 B.仅有头指针的单循环链表 C.双链表 D.仅有尾指针的单循环链表
解析:采用带有尾指针链表可以快速找到尾结点,方便在线性表的最后一个元素之后插入一个元素。采用仅有尾指针的单循环链表,尾结点的下一个结点就是头节点,可以快速删除第一个元素。因此,采用仅有尾指针的单循环链表时,在最后一个元素之后插入一个元素和删除第一个元素的时间性能都是O(1),最节省运算时间。
10.指针p1和p2分别指向两个无头结点的非空单循环链表中的尾结点,要将两个链表链接成一个新的单循环链表,应执行的操作为()。
A.p1->next=p2->next; p2->next=p1->next;
B.p2->next=p1->next; p1->next=p2->next;
C.p=p2->next; p1->next=p; p2->next=p1->next;
D.p=p1->next; p1->next=p2->next; p2->next=p;
解析:引入指针p用于保存p1所指结点的后继结点,即p=p1->next; 再将p1所指结点的后继指针指向p2所指结点的后继结点,即p1->next=p2->next; 最后将p2所指结点的后继指针指向p所指结点p2->next=p;。这样就可以将两个非空单循环链表链接成一个新的单循环链表。
11.定义三元组(a,b,c)(a,b,c均为整数)的距离D=|a-b|+|b-c|+|c-a|。给定义3个非空整数集合S1,S2和S3,按升序分别存储在3个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1,b∈S2,c∈S3)中的最小距离。例如,S1={-1,0,9},S2={-25,-10,10,11},S3={2,9,17,30,41},则最小距离为2,相应的三元组为(9,10,9)。
要求:(1)给出算法的基本设计思想。(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
(3)说明你所设计算法的时间复杂度和空间复杂度。
解析:(1)算法的基本设计思想如下:
- 定义int型变量min用于存放当前所有已处理过的三元组中的最小距离,初值为C语言能表示的最大整数INT_MAX。
- 使用数组A,B,C分别存储非空整数集合S1,S2,S3,3个集合的元素个数(即数组的长度)分别为x,y,z;3个数组的下标变量分别为i,j,k,初值均为0.
- 当i<x且j<y且k<z且min>0时,循环执行4~6。
- 计算三元组(A[i],B[j],C[k])的距离D。
- 若D<min,则min=D,使min始终存放最小距离。
- 将A[i],B[j],C[k]中的最小值的下标加1。
- 返回最小距离min,算法结束。
(2)采用C语言描述算法,代码如下:
(3)将集合S1,S2,S3的元素个数(即数组的长度)分别记为x,y,z,时间复杂度是O(x+y+z),空间复杂度是O(1)。
12.设线性表L=(a1,a2,a3,……,an)采用带头结点的单链表保存,链表中结点定义如下:
typedef struct node{
int data;
struct node ∗next;
}NODE;
请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各节点,得到线性表L'=(a1,an,a2,a(n-1),a3,…)。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或者C++语言描述算法,关键之处给出注释。
(3)说明你所设计的算法的时间复杂度。
解析:(1)算法的基本设计思想如下:
- 设置两个指针p和q交替前行,寻找单链表的中间结点。
- 将单链表的后半段链表原地逆置。
- 从单链表前,后段中依次各取一个结点按要求排列。
(2)采用C语言描述算法,代码如下:
如果对单链表原地逆置不了解,可以看一位博主的文章:单链表原地逆置 - 潭影空心 - 博客园 (cnblogs.com)
(3)算法的时间复杂度为O(n)。