数据结构相关习题

在这里插入图片描述

线性结构

线性表

1、某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用 _______存储方式最节省运算时间。
A.单链表
B.仅有头指针的单循环链表
C.双链表
D.仅有尾指针的单循环链表

解答:D
A、B、C:单链表只能单向遍历,只能由链表头向链表尾遍历,因此要找到最后一个元素必须遍历整个表;
D:要插入结点,只要改变一下指针即可,要删除头结点,只要将指针移动到头结点即可。

2、设一个链表最常用的操作是在末尾插入结点和删除尾结点,则选用()最节省时间。

A.单链表
B.单循环链表
C.带尾指针的单循环链表
D.带头结点的双循环链表

解答:D
在链表的末尾插入结点或删除尾结点时,需要修改其相邻结点的指针域,因此需要寻找尾结点和尾结点的前驱节点。
A、B:对于单链表或单循环链表,寻找尾结点的时间复杂度均为O(n);
C :对于带尾指针的单循环链表,可以直接通过尾指针定位到尾结点进行插入,时间复杂度为O(1),但在删除时还是需要遍历表来找到尾结点的直接前驱结点,时间复杂度为O(n);
D:对于带头结点的双循环链表,可以通过时间复杂度O(1)直接定位到尾结点或尾结点的前驱节点

3、已知两个长度分别为m 和 n 的升序链表,若将它们合并为一个长度为 m+n 的降序链表,则最坏情况下的时间复杂度是( )。
A.O(n)
B.O(m+n)
C.O(min(m ,n))
D.O(max(m,n))

解答:D
该算法的思想和有序表合并的算法思想一样,两个表比较,一个表比较到头了,另一个表直接插入表尾。

while(pa && pb)
{
    if(pa->data <= pb->data)
        pc->next=pa;
        pc=pa;
        pa=pa->next;
    else
        pc->next=pb;
        pc=pb;
        pb=pb->next;
}
pc->next=pa?pa:pb; //pa存在就把A表链接到最后,否则链接B表

最好的情况就是较短的表先到头了,长的表不用比较直接插入到最后,此时时间复杂度为O(min(m ,n));
最坏的情况就是较长的表先到头了,短的表不用比较直接插入到最后,此时的时间复杂度为O(max(m ,n));

4、若长度为n的线性表采用顺序存储结构,在其第i个位置插入一个新元素的算法的时间复杂度为( )(1<=i<=n+1)。
A.O(1)
B.O(n)
C.O(n*n)
D.O(√n)

解答:B

5、判断:插入和删除操作是线性表的基本操作。这两种操作在数组中也经常使用。

解答:错。数组本身不能进行插入和删除,因为数组的长度是不可变的。(在某些语言中)

6、线性表有两种存储结构:一是顺序表,二是链表。试问:
(1)如果有 n个线性表同时并存,并且在处理过程中各表的长度会动态变化,线性表的总数也会自动地改变。在此情况下,应选用哪种存储结构? 为什么?
(2)若线性表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取线性表中的元素,那么应采用哪种存储结构?为什么?

(1)应采用链表。因为链表适合进行频繁的插入删除操作,更加灵活。 (2) 应采用顺序表结构。因为顺序表适合进行随机存取,可以根据序号进行查找。

7、在单链表中设置头结点的作用是什么?

(1)便于首元结点的处理。首元结点的地址保存在了头结点的指针域中,则对链表的第一个数据元素的操作就可以和其他数据元素的操作一样,无需特殊化处理。 (2)便于空表和非空表的统一处理。无论是空表还是非空表,头指针都是指向头节点的非空指针。

栈和队列

1、数组Q[n]用来表示一个循环队列,front为当前队列头元素的前一位置,rear为队尾元素的位置,假定队列元素中的元素个数小于n,计算队列中元素个数的公式为()
A.r-f
B.(n+f-r)%n
C.n+r-f
D.(n+r-f)%n

解答:D
当rear>front时,循环队列的长度:Q.rear-Q.front;
当rear<front时,循环队列的长度:分为两类计算 0+Q.rear和MAXSIZE-Q.front即Q.rear-Q.front+MAXSIZE;
总的来说,总长度是(Q.rear-Q.front+MAXSIZE)%MAXSIZE

2、若用一个大小为6的数组来实现循环队列,且当rear和front的值分别是0和3,当从队列中删除一个元素,再加入两个元素后, rear 和 front 的值分别为()
A.1 和 5
B.2 和 4
C.4 和 2
D.5 和 1

解答:B

3、判断:栈和链表是两种不同的数据结构。

解答:错。 栈是逻辑结构的概念,链表是物理存储结构,二者不是一类事物

4、栈是一种线性表,它的特点是 ( A ) 。设用一维数组A[1,…,n]来表示一个栈,A[n]为栈底,用整型变量T指示当前栈顶位置,A[T]为栈顶元素。往栈中推入(PUSH)一个新元素时,变量T的值 ( B ) ;从栈中弹出(POP)一个元素时,变量T的值( C ) 。设栈空时,有输入序列a,b,c,经过PUSH,POP,PUSH,PUSH,POP操作后,从栈中弹出的元素的序列是 (D ) ,变量T的值是( E ) 。
供选择的答案:
A: ① 先进先出 ②后进先出 ③进优于出 ④出优于进 ⑤ 随机进出
B,C: ① 加1 ②减1 ③不变 ④清0 ⑤ 加2 ⑥减2
D: ① a,b ②b,c ③c,a ④b,a ⑤ c,b ⑥ a,c
E: ① n+1 ②n+2 ③ n ④ n-1 ⑤ n-2

解答: ② ② ① ⑥ ④
栈是一种线性表,它的特点是 后进先出 。设用一维数组A[1,…,n]来表示一个栈,A[n]为栈底,用整型变量T指示当前栈顶位置,A[T]为栈顶元素。往栈中推入(PUSH)一个新元素时,变量T的值 减1 ;从栈中弹出(POP)一个元素时,变量T的值 加一 。设栈空时,有输入序列a,b,c,经过PUSH,POP,PUSH,PUSH,POP操作后,从栈中弹出的元素的序列是 a,c ,变量T的值是 n-1

5、在做进栈运算时,应先判别栈是否( A ) ;在做退栈运算时,应先判别栈是否( B ) 。当栈中元素为n个,做进栈运算时发生上溢,则说明该栈的最大容量为( C ) 。
为了增加内存空间的利用率和减少溢出的可能性,由两个栈共享一片连续的内存空间时,应将两栈的( D )分别设在这片内存空间的两端,这样,只有当( E )时,才产生上溢。
供选择的答案:
A,B:①空 ② 满 ③ 上溢 ④ 下溢
C: ①n-1 ② n ③ n+1 ④ n/2
D: ① 长度 ②深度 ③ 栈顶 ④ 栈底
E:①两个栈的栈顶同时到达栈空间的中心点 ②其中一个栈的栈顶到达栈空间的中心点
③两个栈的栈顶在达栈空间的某一位置相遇 ④两个栈均不空,且一个栈的栈顶到达另一个栈的栈底

解答:② ① ② ④ ③
在做进栈运算时,应先判别栈是否 ;在做退栈运算时,应先判别栈是否 。当栈中元素为n个,做进栈运算时发生上溢,则说明该栈的最大容量为 n
为了增加内存空间的利用率和减少溢出的可能性,由两个栈共享一片连续的内存空间时,应将两栈的 栈底 分别设在这片内存空间的两端,这样,只有当 两个栈的栈顶在达栈空间的某一位置相遇 时,才产生上溢。

6、 设有4个数据元素a1、a2、a3和a4,对他们分别进行栈操作或队操作。在进栈或进队操作时,按a1、a2、a3、a4次序每次进入一个元素。假设栈或队的初始状态都是空。
现要进行的栈操作是进栈两次,出栈一次,再进栈两次,出栈一次;这时,第一次出栈得到的元素是 ( A ),第二次出栈得到的元素是 ( B ) ;类似地,考虑对这四个数据元素进行的队操作是进队两次,出队一次,再进队两次,出队一次;这时,第一次出队得到的元素是 ( C ) ,第二次出队得到的元素是 ( D ) 。经操作后,最后在栈中或队中的元素还有 ( E ) 个。
供选择的答案: A~D:①a1 ②a2 ③ a3 ④a4 E: ①1 ②2 ③ 3 ④ 0

解答:② ④ ① ② ②
设有4个数据元素a1、a2、a3和a4,对他们分别进行栈操作或队操作。在进栈或进队操作时,按a1、a2、a3、a4次序每次进入一个元素。假设栈或队的初始状态都是空。
现要进行的栈操作是进栈两次,出栈一次,再进栈两次,出栈一次;这时,第一次出栈得到的元素是 a2 ,第二次出栈得到的元素是 a4 ;类似地,考虑对这四个数据元素进行的队操作是进队两次,出队一次,再进队两次,出队一次;这时,第一次出队得到的元素是 a1 ,第二次出队得到的元素是 a2 。经操作后,最后在栈中或队中的元素还有 2 个。

7、假设一个数组squ[m]存放循环队列的元素。若要使这m个分量都得到利用,则需另一个标志tag,以tag为0或1来区分尾指针和头指针值相同时队列的状态是“空”还是“满”。试编写相应的入队和出队的算法。

解答:大致思路:设置tag标志,当队满时tag=1;队空时tag=0。
初始时,令tag=0,如果有元素入队,执行Q.rear=(Q.rear+1)%MAXSIZE操作,操作执行完毕后Q.rear==Q.front,则证明队列为满,修改tag=1。
如果有元素出队,执行Q.front=(Q.front+1)%MAXSIZE操作,操作执行完毕后Q.rear=Q.front,则证明队空,修改tag=0。

简单来说,一开始队空,tag=0,若从rear一端增加到和front指针相同时,表示入队已满,则令tag=1;
若从front一端加到与rear指针相同时,则令tag=0,表示出队已空。

8、将编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。当第0号栈的栈顶指针top[0]=-1时该栈为空;当第1号栈的栈顶指针top[1]=m时,该栈为空。两个栈均从两端向中间增长。试编写双栈初始化,判断栈空、栈满、进栈和出栈等算法的函数。双栈数据结构定义如下:

typedef struct
{
    int top[2],bot[2];       //栈顶和栈底指针
    SElemType *V;            //栈数组
    int m;                   //栈可容纳的最大元素
}DblStack;

在这里插入图片描述
解答:栈顶指针指向的是栈顶元素,因此大致思路:
栈空时:top[0]=-1;top[1]=m;
栈满时:top[0]+1=top[1];
左栈进栈:S.top[0]++;S.V[S.top[0]]=e; 左栈出栈:e=S.V[S.top[0]];S.top[0]–;
右栈进栈:S.top[1]–;S.V[S.top[1]]=e; 右栈出栈:e=S.V[S.top[1]];S.top[1]++;

//判断栈空
bool isEmpty(DblStack S,int x)
{//x表示选择左栈还是右栈
    //-------左栈-------
    if(x==0 && S.top[0]==-1)
        return true;
    //-------右栈-------
    else if(x==1 && S.top[1]==m)
        return true;
    else
        return false;
}

//判断栈满
bool ifFull(DblStack S)
{
    if(S.top[0]+1==S.top[1])
        return true;
    else
        return false;
}

//进栈
Status Push(DblStack &S,int e,int x)
{//e表示要进栈的元素,x表示选择左栈还是右栈
    //-------左栈-------
    if(x==0)
    {
        if(isFull(S)) return ERROR;
        S.top[0]++;
        S.V[S.top[0]]=e; 
        return OK;
    }
    //-------右栈-------
    else if(x==1)
    {
        if(isFull(S)) return ERROR;
        S.top[1]--;
        S.V[S.top[1]]=e;
        return OK;
    }
    else
        return ERROR;
}

//出栈
Status Pop(DblStack &S,int e,int x)
{//e用来接收出栈的元素,x表示选择左栈还是右栈
    //-------左栈-------
    if(x==0)
    {
        if(isEmpty(S,x)) return ERROR;
        e=S.V[S.top[0]];
        S.top[0]--;
        return OK;
    }
    //-------右栈-------
    else if(x==1)
    {
        if(isEmpty(S,x)) return ERROR;
        e=S.V[S.top[1]];
        S.top[1]++;
        return OK;
    }
    else 
        return ERROR;
}

串和数组、广义表

1、串是一种特殊的线性表,其特殊性体现在()
A.数据元素是一个字符
B.可以顺序存储
C.数据元素可以是多个字符
D.可以链接存储
解答:A
串是一种特殊的、类型受限的线性表,其特殊性体现在数据元素是一个字符。而线性表中每一个元素是属于某个数据对象的(可以是字符集、实数集等)。
B、D:串可以使用顺序存储,也可用链表来存储。由于串的数据元素是一个字符,它只有8位二进制数, 因此用链表存储时,通常一个结点中存放的不是一个字符,而是一个子串,例如: 在编辑系统中,整个文本编辑区可以看成是一个串,每一行是一个子串,构成一个结点。

2、设矩阵 A 是一个对称矩阵,为了节省存储,将其下三角部分按行序存放在一维数组 B[ 1,n(n-1)/2 ] 中,对下三角部分中任一元素 a i,j (i≥j), 在一维数组 B 中下标 k 的值是( )。
A.i(i-1)/2+j-1
B.i(i-1)/2+j
C.i(i+1)/2+j-1
D.i(i+1)/2+j
解答:B
下标从1开始,下三角的元素前面有i-1行,元素个数为1+2+3+……+i-1=i(i-1)/2;位于第j列,本行前面又有j个元素,故一共有i(i-1)/2+j个元素。

3、串 ‘ababaaababaa’ 的naxt数组为()
A.012345678999
B.012121111212
C.011234223456
D.0123012322345

解答: C

4、串 ‘ababaabab’ 的nextval为( )
A.010104101
B.01002101
C.010100011
D.010101011

解答:A

5、有一个100阶的三对角矩阵M,其元素mij(1<=i<=100,1<=j<=100)按行优先次序压缩存入下标从0开始的一维数组IV中,元素m30,30在IV中的下标是:()
A. 86
B. 87
C. 88
D. 89

解答:B
对三角矩阵除了第一行和最后一行是每行2个元素外,中间的每行都是三个元素,如图:

因此m30,30=2+3*28+2=88。由于下标从0开始,因此m30,30在IV中的下标是88-1=87

6、将一个三对角矩阵A[1……100,1……100]按行优先存入一维数B[1…298]中,A中元素A66,65(即该元素下标i=66,j=65),在B数组中的位置k为()。
A.198
B.195
C.197
D.196

解答:B
图同上题。k=2+64*3+1=195

7、假设以行优先顺序存储三维数组 R[6][9][6],其中元素 R[0][0][0]的地址为 2100,且每个元 素占 4 个存储单元,则存储地址为 2836 的元素是( )。
A.R[3][3][3]
B. R[3][3][4]
C. R[4][3][5]
D. R[4][3][4]

解答:

8、从供选择的答案中,选出应填入下面叙述 ( ? )内的最确切的解答,把相应编号写在答卷的对应栏内。

有一个二维数组A,行下标的范围是0到8,列下标的范围是1到5,每个数组元素用相邻的4个字节存储。存储器按字节编址。假设存储数组元素A[0,1]的第一个字节的地址是0。

存储数组A的最后一个元素的第一个字节的地址是 ( A ) 。若按行存储,则A[3,5]和A[5,3]的第一个字节的地址分别是 ( B ) 和 ( C ) 。若按列存储,则A[7,1]和A[2,4]的第一个字节的地址分别是 ( D ) 和 ( E ) 。
供选择的答案:
A~E:①28 ② 44 ③ 76 ④ 92 ⑤ 108 ⑥ 116 ⑦ 132 ⑧ 176 ⑨ 184 ⑩ 188

解答:⑧ ③ ⑤ ① ⑥
A:占用的总的存储空间=(8-0+1)*(5-1+1)*4=180
最后一个元素的第一个字节的地址=180-4=176
B:按行存储,Loc(3,5)=0 +【(3-0)*5+(5-1)】*4=76
C:按行存储,Loc(5,3)=0 +【(5-0)*5+(3-1)】*4=108
D:按列存储,Loc(7,1)=0 +【(1-1)*9+(7-0)】*4=28
E:按列存储,Loc(2,4)=0 +【(4-1)*9+(2-0)】*4=116

9、从供选择的答案中,选出应填入下面叙述 ( ? ) 内的最确切的解答,把相应编号写在答卷的对应栏内。

有一个二维数组A,行下标的范围是1到6,列下标的范围是0到7,每个数组元素用相邻的6个字节存储,存储器按字节编址。那么,这个数组的体积是 ( A )个字节。假设存储数组元素A[1,0]的第一个字节的地址是0,则存储数组A的最后一个元素的第一个字节的地址是 ( B ) 。若按行存储,则A[2,4]的第一个字节的地址是 ( C ) 。若按列存储,则A[5,7]的第一个字节的地址是 ( D ) 。
供选择的答案:
A~D:①12 ②66 ③72 ④96 ⑤114 ⑥120 ⑦156 ⑧234 ⑨276 ⑩282 (11)283 (12)288

解答:(12)⑩ ③ ⑨
A:体积=(6-1+1)*(7-0+1)*6=288
B:Loc(6,7)=266-6=282
C:Loc(2,4)=0 +【(2-1)*8+(4-0)】*6=72
D:Loc(5,7)=0 +【(7-0)*6+(5-1)】*6=276

10、编写一个实现串的置换操作Replace (&S, T, V)的算法。
解答:
大致思路:将S中的子串逐个提取出来和T比较,如果提取出的子串和T相等,就把子串之前的串、串V、子串之后的串链接在一起。

void Replace(StringType &S, StringType T, StringType V)
//以串 v 置换串 s 中出现的所有和串 t 相同的非空串
{
    int n,m,k,i;
    StringType sub;
    InitStr(sub);
    n = StrLength(S);
    m = StrLength(T);
    k = StrLength(V);
    i = 1;
    while(i <= n-m+1){
        //将串S中的子串逐个提取出来与串T进行匹配
        StrAssign(sub,SubString(S,i,m));//将串S中的子串赋值给sub
        if(StrCompare(sub,T) == 0){//sun和T匹配相等时
            InitStr(sub);//初始化sub

            Concat(sub,SubString(S,1,i-1));//将匹配相等的子串的前面的子串连接到串sub后
            Concat(sub,V);//将替换串连接到串sub后
            Concat(sub,SubString(S,m+i,n-(m+i)+1));//将匹配相等的子串的后面的子串连接到串sub后
            StrAssign(S,sub);//将sub赋值给S
            i += k;//从匹配串之后的一个位置重新开始匹配
            n = StrLength(S);//操作完一次之后,串S已经改变,更新S的长度
        }
        else
        {//如果不匹配,就i向后移动
            i++;
        }
    }
}

11、写出将字符串反序的递推或递归算法,例如字符串为“abcsxw”,反序为“wxscba”
解答:
大致思路:

void transform(StringType &S)
{
    int n = StrLength(S);
    for(int i=0;i<n/2;i++)
    {
        StringType tmp;
        StrAssign(tmp,SubString(S,i,1)); 
        StrAssign(SubString(S,i,1),SubString(S,n-i-1,1));
        StrAssign(SubString(S,n-i-1,1),tmp);
    }
}

非线性结构

1、对n(n大于等于2)个权值均不相同的字符构成哈夫曼树,关于该树的叙述中,错误的是()
A. 该树一定是一棵完全二叉树
B. 树中一定没有度为1的结点
C. 树中两个权值最小的结点一定是兄弟结点
D. 树中任一非叶结点的权值一定不小于下一任一结点的权值

解答:A
A.哈夫曼树是一颗二叉树,但并不一定是完全的二叉树。
B.哈夫曼树中只有度为2的结点和度为0的叶子结点。
C.哈夫曼树的构造是从下到上,从小到大,所以最小权值的两个结点一定用于底部,是兄弟结点。
D.哈夫曼树必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点,任一非叶子结点的权值是等于其自己孩子结点权值之和,因此一定大于或等于下一层的任一结点的权值。

2、判断:若二叉树用二叉链表作存贮结构,则在n个结点的二叉树链表中只有n-1个非空指针域。

解答:对
n个节点的二叉链表一共有2n个链域,n个节点,分支数为n-1(除了根节点每个结点都有一个分支进入),即非空链域有n-1个,那么空链域有2n-(n-1)=n+1个

3、已知一棵完全二叉树的第6层(设根为第1层)有8个叶结点,则完全二叉树的结点个数最多是()
A.39
B.52
C.111
D.119
解答:C
第6层有8个叶子结点,如果要结点个数最多,那么就要完全二叉树一共有7层,前6层是满的,那么第6层一共有32个结点,其中8个叶子结点,则第7层有(32-8)*2=48个结点。则完全二叉树的结点个数最多是1+2+4+…+32+48=111

4、将森林转换为对应的二叉树,若在二叉树中,结点u是结点v的父结点的父结点,则在原来的森林中,u和v可能具有的关系是 ()
I.父子关系
II.兄弟关系
III. u的父结点与v的父结点是兄弟关系
A. 只有II
B. I和II
C. I和III
D. I、II和III

解答:B
将森林转换成二叉树的步骤:1、树中所有相邻兄弟结点之间架一条连线;2、对树中的每个结点,只保留其与第一个孩子结点之间的连线,删除其他连线;3、调整树的形态和角度。
因此,如果在二叉树中结点u是结点v的父结点的父结点,那么在森林中结点u和结点v是兄弟结点;结点u是结点v的双亲结点

5、已知一棵有2011个结点的树,其叶结点个数为116,该树对应的二叉树中无右孩子的结点个数是()
A.115
B.116
C.1895
D.1896

解答:

6、 编写递归算法,计算二叉树中叶子结点的数目。

解答:
大致思路:

//编写递归算法,计算二叉树中叶子结点的数目
int LeafCount(BiTree T)
{
    if(!T)
        return 0;
   else
   {
       if(T->lchild==NULL && T->rchild==NULL)
           return 1;
       else
           return LeafCount(T->lchild)+LeafCount(T->rchild);
   }
}

7、 写出求二叉树深度的算法。

解答:
大致思路:

//求二叉树深度
int Depth(BiTree T)
{
    if(!T)
        return 0;
    else
    {
        m=Depth(T->lchild);
        n=Depth(T->rchild);
        if(m>n)
            return m+1;
        else
            return n+1;
    }
}

8、编写按层次顺序(同一层自左至右)遍历二叉树的算法。

解答:
大致思路:借助队列。如果结点存在的话,将其入队。如果队不为空,证明仍有结点未被访问到,就将队头元素出队,访问队头元素。如果队头元素,即刚刚访问过的结点有左右孩子,就将其左右孩子按顺序入队;如果没有左右孩子,就继续判断是否存在没有访问过的结点,即判断队是否为空。

//按层次顺序(同一层自左至右)遍历二叉树的算法
void cengxu(BiTree T)
{
    SqQueue Q;
    InitQueue(Q);
    BiNode p=T;
    if(p)
    {
        EnQueue(Q,p);
        while(!EmpytQueue(Q))
        {
            DeQueue(Q,p);
            cout<<p->data;
            if(p->lchild)
                EnQueue(Q,p->lchild);
            if(p->rchild)
                EnQueue(Q,p->rchild);
        }
    }
}

9、编写算法判别给定二叉树是否为完全二叉树。

解答:
大致思路:

//判别给定二叉树是否为完全二叉树
bool isCompleteBiTree(BiTree T)//判断以head为头节点的二叉树是否为完全二叉树
{
    BiTree p=T;
    SqQueue Q;
    InitQueue(Q);
    
    if(p==NULL)
        return true;
        
//只要当前结点的左右孩子都为空或者左孩子不为空,右孩子为空时,flag=true,那么以后访问到的结点必须都是叶结点
    bool flag=false;
    
    EnQueue(Q,p);
    while (!EmptyQueue(Q))
    {
        DeQueue(Q,p);
        /*如果flag=true,但是当前结点有左孩子或者有右孩子
        如果flag=true,但是当前结点有左孩子但是没有有右孩子*/
        if((flag && (p->lchild || p->rchild)) || (!p->lchild && p->rchild))
            return false ;
            
        if(p->lchild)//左孩子不为空,加入到队列中去
            EnQueue(Q,p->lchild);
            
        if(p->rchild)//右孩子不为空,加入到队列中去
            EnQueue(Q,p->rchild);
            
        //如果当前结点存在左孩子且没有右孩子,或者当前结点左右孩子都没有,则flag=true,即后续访问到的结点必须是叶子结点
        if((p->lchild && !p->rchild) || (!p->lchild && !p->rchild))
            flag=true;
    }
    return true;
}

1、图的BFS生成树的树高比DFS生成树的树高()
A.小
B.相等
C.小或相等
D.大或相等

解答:C
对于一些特殊的图,比如只有一个顶点的图,其BFS生成树的树高和DFS生成树的树高相等。而对于一般的图,(BFS)广度优先搜索的树高小于(DFS)深度优先搜索的树高。

2、若无向图G=(V,E)中含有7个顶点,要保证图G在任何情况下都是连通的,则需要的边数最少是()
A.6
B.15
C.16
D.21

解答:C
要保证无向图G在任何情况下都是连通的,即任意变动图G中的边,G始终保持连通。考虑极端的情况,图G的6个顶点构成完全连通子图G1,即每个点能够支出的边是满的,这样 6 个点的情况下,边和点的关系是满的。则G1有6*5/2=15条边,再添一条边第7个顶点必然与G1构成连通图,所以边数最小为16。

3、下列关于最小生成树的说法中,正确的是()
Ⅰ.最小生成树的代价唯一
Ⅱ.权值最小的边一定会出现在所有的最小生成树中
Ⅲ.用普里姆算法从不同顶点开始得到的最小生成树一定相同
Ⅳ.使用普里姆算法和克鲁斯卡尔算法的得到的最小生成树总不相同
A.仅Ⅰ
B.仅Ⅱ
C.仅Ⅰ、Ⅲ
D.仅Ⅱ、Ⅳ

解答:A
(1)最小生成树的形状不唯一,因为可能存在权值相同的边,但是代价一定是唯一的。
(2)如果所有边权值都相同且当边数>顶点数-1时,则总有权值最小的边不能出现在最小生成树中。
(3)如果多条边权值相同,那么从不同顶点出发得到的最小生成树就不一定相同。
(4)可能相同也可能不相同。

4、下图所示的AOV网表示一项包含8个活动的工程。通过同时加快若干活动的进度可以缩短整个工程的工期。下列选项中,加快其进度就可以缩短工程工期的是()
A.c 和 e
B. d 和 e
C. f和 d
D. f 和 h
在这里插入图片描述
解答:C
一共有三条关键路径,bfh、bdeh和bdcg,三条均为27。只有所有关键路径上的活动时间同时减少,才能缩短工期。缩短f可以缩短bfh这条路径,缩短d则可以缩短bdcg和bdeh这两条路径,其他选项无法使三条一起缩短。

5、如果n个顶点的图是一个环,则它有____ 棵生成树。

解答:n
n个顶点形成的环有n条边,若得到生成树只需要删除这n条边中的任意一条即可,所以得到n棵生成树。

6、编写算法,由依次输入的顶点数目、弧的数目、各顶点的信息和各条弧的信息建立有向图的邻接表。

解答:

//---------------------邻接表-----------------------------
//边结点
typedef struct ArcNode
{
    int adjvex; //该边所指向的顶点位置
    struct ArcNode * nextarc;  //指向下一条边的指针
}ArcNode;

//顶点信息
typedef struct VNode
{
    VerTexType data;     //顶点值
    ArcNode * firstarc;  //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];  //AdjList表示邻接表类型

//邻接表
typedef struct
{
    AdjList vertices;
    int vexnum,arcnum;    //图的当前顶点数和边数
}ALGraph;

//有向图的邻接表,依次输入的顶点数目、弧的数目、各顶点的信息和各条弧的信息
Status Build_AdjList(ALGraph &G)
{
    int vnum,anum;
    cout<<"请输入顶点数目:";
    cin>>vnum;
    if(vnum<0)
    {
        cout<<"顶点数不能为负!!"<<endl;
        return ERROR;
    }
    G.vexnum=vnum;
    cout<<"请依次输入顶点的名称:";
    for(int i=0;i<G.vexnum;i++)
    {
        cin>>G.vertices[i].data;
        G.vertices[i].firstarc=NULL; //初始化表头结点的指针域为NULL
    }
//-------------------------------------------------    
    cout<<"请输入弧的数目:";
    cin>>anum;
     if(anum<0)
    {
        cout<<"弧数不能为负!!"<<endl;
        return ERROR;
    }
    G.arcnum=anum;
    cout<<"请依次输入各边依附的两个顶点:"<<endl;
    for(int i=0;i<G.arcnum;i++)
    {
        int v1,v2;
        cin>>v1,v2;
        //找到两顶点在邻接表中的位置
        int i=LocateVex(G,v1);//LocateVex()是返回顶点v在图G中的位置的函数
        int j=LocateVex(G,v2);
        
        ArcNode *p1=new ArcNode; //生成一个新的边结点p1
        p1->adjvex=j;//边p1所指向的顶点位置为j
        //将新结点*p1插入顶点vi的边表头部
        p1->nextarc=G.vertices[i].firstarc;
        G.vertices[i].firstarc=p1;

        ArcNode *p2=new ArcNode;  //生成一个新的边结点p2
        p2->adjvex=i;
        //将新结点*p2插入顶点vj的边表头部
        p2->nextarc=G.vertices[j].firstarc;
        G.vertices[j].firstarc=p2;
    }
    return OK;
}

7、试在邻接矩阵存储结构上实现图的基本操作:DeleteArc(G,v,w) ,即删除一条边的操作。

解答:

//-------------------邻接矩阵----------------------------
typedef struct
{
    VerTexType vexs[MVNum];  //顶点表
    ArcType arcs[MVNum][MVNum]; //邻接矩阵
    int vexnum,arcnum;  //图得当前点数和边数
}AMGraph;

//删除一条边
Status DeleteArc(AMGraph &G,int v,int w)
{
    if(LocateVex(G,v)<0 || !LocateVex(G,w)<0)
        return ERROR;
    G.arc[v][w]=0;
    G.arc[w][v]=0;
    G.arcnum--;
    return OK;
}

8、试基于图的深度优先搜索策略写一算法,判别以邻接表方式存储的有向图中是否存在由顶点vi到顶点vj的路径(i≠j)

解答:
大致思路:可以利用深度优先搜索遍历的算法递归遍历邻接表来进行判断。引入变量level来记录递归的层数,设置visited[n]为访问标志数组,已访问的顶点的值记为true。递归算法结束的条件为顶点vi=vj,表明首位相遇,存在路径。如果递归结束的条件不成立,则从vi边链表的第一个边结点开始,获取vi的邻接点,对vi的尚未访问的邻接点递归调用。递归进行一次,level的值增加1。如果递归之后level的值为1,表明vi到vj不存在路径。

int level=1;
bool visited[MVNum];
int PathDFS(ALGraph G,int i,int j)
{
    if(i==j)
        return 1;
    else
    {
        visited[i]=true;
        for(p=G.vertices[i].firstarc;p;p=p->nextarc,level--)
        {
            level++;
            int k=p->adjvex; //获取该边所指向的顶点的位置
            if(!visited[k] && PathDFS(G,k,j))//如果vk没有被访问过且vk到vj有路径
                return 1;
        }
    }
    if(level==1)
        return 0;
}

数据运算

查找

查找算法平均时间复杂度特点
顺序查找O(n)对表结构无任何要求,既适用于顺序结构,也适用于链式结构,无论记录是否按关键字有序均可应用,但效率较低,不适合n很大的情况
折半查找O( log2(n))比较次数少,查找效率 ,但必须采用顺序存储结构,而且表中的元素按关键字有序排列;不适合于数据元素经常变动的线性表
分块查找O( log2(n))要求块间有序,块内无序 ;既能较快查找,又能适用于动态变化的要求。查询效率介于顺序查找和折半查找之间
二叉排序树查找O( log2(n))数据要是二叉排序树存储 。二叉排序树的查找过程与折半查找过程类似,查找效率在平均情况下相同,但因采用树的二叉链表表示,因此适合经常做插入和删除的动态查找表。二叉排序树在形态均匀时性能最好,而形态为单支树时查找性能退化为与顺序查找相同
哈希查找最理想的情况下:O(1)需要创建哈希表,根据键值方式(Key value)进行查找,通过哈希函数,定位数据元素。

1、对22个记录的有序表进行折半查找,当查找失败时,至少需要比较()次关键字
A.3
B.4
C.5
D.6

解答:B
22 个记录的有序表,其折半查找的判定树深度为 log2(22) + 1=5,且该判定树不是满二叉树,即查找失败时至多比较 5 次,至少比较 4 次

2、在平衡二叉树中插入一个结点后造成了不平衡,设最低的不平衡结点为A,并已知A的左孩子的平衡因子为0右孩子的平衡因子为1,则应作( )型调整以使其平衡。
A.LL
B.LR
C.RL
D.RR

解答:C
1.插入点位于A的左孩子的左子树中:LL 右旋。
2.插入点位于A的右孩子的右子树中:RR 左旋。
3.插入点位于A的左孩子的右子树中:LR 较低的先左旋,再右旋。
4.插入点位于A的右孩子的左子树中:RL 较低的先右旋,再左旋。
在本题中:A是最低的不平衡点,A的左孩子的平衡因子为0,右孩子的平衡因子为1,如果插入一个结点,变成不平衡,那么只能在右孩子的左孩子下面插入结点,使A的右孩子平衡因子变为2。即插入结点的位置符合的是 RL。因而需要做RL调整。

3、在下图所示的平衡二叉树中,插入关键字48后得到一棵新平衡二叉树,在新平衡二叉树中,关键字37所在节点的左边、右子结点中保存的关键字分别是()
在这里插入图片描述
解答:C
插入一个新的结点之后,变成了:
在这里插入图片描述
沿着叶子结点45往上走,查找到的第一个不平衡的结点为24,其平衡因子为-2.53是24的右孩子,37是53的左孩子,因此这是一个RL型不平衡。于是先把53顺时针旋转到37的右孩子,再把24逆时针旋转到37的左孩子。得到:
在这里插入图片描述
剩下的48按照平衡二叉树中序遍历递增的原则,48挂在53的左孩子处。最后得到:
在这里插入图片描述

4、若平衡二叉树的高度为6,且所有非叶子结点的平衡因子均为1,则该平衡二叉树的结点总数为()
A.12
B.20
C.32
D.33

解答:B
平衡二叉树的最小结点数:f(n)=f(n-1)+f(n-2)+1。其中 f(1)=1, f(0)=0。(n为深度)
深度为2时,f(2)=f(1)+f(0)+1=2;f(3)=f(2)+f(1)+1=4;
以此类推得到: f(4)=7,f(5)=12,f(6)=20。

5、折半搜索与二叉排序树的时间性能()
A.相同
B. 完全不同
C. 有时不相同
D. 数量级都是O(log2n)

解答:C
不一定相同。 二叉排序树不一定是平衡树,它是只要求了左右子树与根结点存在大小关系,但是对左右子树之间没有层次差异的约束,因此通过二叉排序树进行查找不一定能够满足logn的,例如一棵只有多层左子树的二叉排序树。 只有是一棵平衡的二叉排序树时,其查找时间性能才和折半查找类似。

6、

7、设哈希( Hash )表的地址范围为 0 ~ 17 ,哈希函数为: H ( K )= K MOD 16 。 K 为关键字,用线性探测法再散列法处理冲突,输入关键字序列:(10,24,32,17,31,30,46,47,40,63,49)造出Hash表,试回答下列问题:
⑴画出哈希表的示意图;
⑵若查找关键字63,需要依次与哪些关键字进行比较?
⑶若查找关键字60,需要依次与哪些关键字比较?
⑷假定每个关键字的查找概率相等,求查找成功时的平均查找长度。

解答:

8、折半查找适不适合链表结构的序列,为什么?用二分查找的查找速度必然比线性查找的速度快,这种说法对吗?

解答:(1)不适合。折半查找只适合顺序存储的有序表,单链表搜索时只能从头结点开始逐步向后搜索。
(2)不对。二分查找的速度在一般情况下会快一些,但是在某些特殊情况下,如要查找的元素位于表中的第一个,这时线性查找的速度就更快些。

排序

在这里插入图片描述
1、对待排序的元素序列进行划分,将其分为左、右两个子序列,再对两个子序列进行同样的排序操作,直到子序列为空或只剩下一个元素为止。这样的排序方法是()。
A. 直接选择排序
B. 直接插入排序
C. 快速排序
D. 冒泡排序

解答:C

2、将 5 个不同的数据进行排序,至多需要比较( )次。
A. 8
B. 9
C.10
D. 25

解答:C
首先随便选择一个数为基数,再选择一个数和它比较就是1次,选择第三个数最多比较2次就可以确定位置,选择第四个数最多比较3次就能够确定位置,最后一个数最多比较4次同样可以确定位置。因此最多一共比较1+2+3+4=10






  • 14
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值