【数据结构】错题总结


选择题

eg1

在长度为 X 的顺序表下标为 i 的位置前插入一个元素( 1 ≤ i ≤ X+1 ),元素的移动次数为( )
A. X - i + 1
B. X - i
C. X
D. X-1

答案解析:B
1.顺序表插入元素,需要移动元素,这里需要把[i, X - 1]区间的元素全部向后移动一次,故移动的次数为X - 1 - i +1
2.举一个例子:顺序表中元素为{ 1,2,3,4,5,6,7}总共7个元素,i=3(4的位置)在第i个位置前插入元素data需要搬移元素个数, 需要移动, 7, 6, 5, 4,才能在在原本4之前空出位置插入data, 也符合X-i, 即 7(顺序表长度) -3(i的位置) = 4(移动4次)
综上:选择B选项

eg2

现有一循环队列,其队头指针为 front ,队尾指针为 rear ;循环队列长度为 N 。其队内有效长度为?(假设队头不存放数据)( )
A. (rear - front + N) % N + 1
B. (rear - front + N) % N
C. (rear - front) % (N + 1)
D. (rear - front + N) % (N - 1)

答案解析:B
这道题是考虑循环队列,对于循环队列,空间长度为N是固定的
举个简单例子:循环队列为 1,2,3,4,5,6, 空间长度为6
本题中 front 不存数据, 这个要敲黑板
1.如果front <= rear,则(rear-front)> 0, 实际空间长度就是(rear-front)。
举例 front = 1,rear = 4
2.如果front > rear,则(rear-front)< 0,实际长度就是(rear+N-front)。
举例front = 5,rear= 2
结论:为了统一两种情况 所以给出的结果为(rear-front+N)% N
扩展:如果front中也存放数据时,则结果为:(rear-front+1+N)% N
综上:选择B选项

eg3

若用一个大小为 6 的数组来实现循环队列,且当 rear 和 front 的值分别为 0 和 3 。当从队列中删除一个元素,再加入两个元素后,rear 和 front 的值分别为( )
A.1和5
B.2和4
C.4和2
D.5和1
答案解析:B
1.队列添加元素是在对尾,删除元素是在对头;
2.添加元素,尾指针rear+1;删除元素,头指针front+1;
3.本题中,删除一个元素,front+1,也就是3+1=4;添加2个元素,rear+2,也就是0+2=2;
可以参考下面的图解:
在这里插入图片描述

eg4

一个栈的入栈序列为 1,2,3,…,n ,其出栈序列是 p 1 ,p 2 ,p 3 ,…p n 。若 p 2 = 3 ,则 p 3 可能取值的个数是( )
A. n-3
B. n-2
C. n-1
D.无法确定

答案解析:C
首先,栈的先进后出原则大家应该是知道的。
根据题意 p 2 = 3,可以知道 p1 的可能情况有三种:1,2 或 4 。(看到有些人只想到了 1,2)
为啥这样想呢?这里估计还有一个关键是要考虑到 n 的大小。
当 n = 3 时, p2 = 3 的话,那么 p 1 有两种情况 1 和 2 。
如果 p 1 = 1 , 那么 p 3 = 2 ;
如果 p 1 = 2 ,那么 p 3 = 1 ;
此时的话我们就可以看到 p 3 只有两种可能 1 或者 2 (n - 1)个。
当 n > 3 时: p 2 = 3 的话,那么 p 1 有三种情况 1 , 2 和 4 。
如果 p 1 = 1 , 那么 p 3 = 2,4,5,… n (n - 2)个
如果 p 1 = 2 ,那么 p 3 = 1,4,5,… n (n - 2)个
如果 p 1 = 4 ,那么 p 3 = 2,5,6,… n (n - 3)个
此时的话我们就可以看到 p 3 的情况有 1,2,4,5,… n (n - 1)个。
综上所述就是 p 3 可能取值的个数是 (n - 1)个。
综上:选择C选项

eg5

一棵有 n 个结点的二叉树,按层次从上到下、同一层从左到右顺序存储在一维数组 A[1…n] 中,则二叉树中第 i个结点(i从1开始用上述方法编号)的右孩子在数组 A 中的位置是( )
A. A[2i](2i <= n)
B. A[2i + 1](2i + 1 <= n)
C. A[i - 2]
D. 条件不充分,无法确定

答案解析:D
必须是完全二叉树才能确定,若是,选择B选项。如下图所示:
根据二叉树的性质5:
1.对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
1.1 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
1.2 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
1.3 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
上述的性质的节点从0开始编号, 而题目当中是从1开始编号, 所有右孩子的序号为 2i+1

在这里插入图片描述

eg6

已知-算术表达式的中缀表达式为 a-(b+c/d)*e , 其后缀形式为( )
A. -a+b*c/d
B. -a+b*cd/e
C. -+*abc/de
D. abcd/+e*-

答案解析:D
1.先使用中缀表达式构建二叉树
1.1 第一个 “-”则将表达式分为左右子树, 左子树为“a”, 右子树为“(b+c/d)e”
1.2 在1.1中的右子树“(b+c/d)e”, 通过“”将再次分为左右子树, 左子树为“(b+c/d)”, 右子树为“e”
1.3 在1.2中的左子树“(b+c/d)”, 通过“+”将再次分为左右子树, 左子树为“b”, 右子树为“c/d”
1.4 在1.3中的右子树“c/d”, 通过“/”将再次分为左右子树, 左子树为“c”, 右子树为“d”
所以构建出来的二叉树如下图所示, 后缀表达式也就不难求解, 先左, 再右, 再根, 即“abcd/+e
-”
综上:选择D选项

在这里插入图片描述

eg7

设二叉树的先序遍历序列和后序遍历序列正好相反,则该二叉树满足的条件是( )
A. 空或只有一个结点
B. 高度等于其结点数
C. 任一结点无左孩子
D. 任一结点无右孩子

答案解析:B
先序遍历:根左右;后序遍历:左右根
要满足题意,则只有,根左<----->左根,根右<--------->右根
所以高度一定等于节点数
综上: 选择B选项

eg8

具有 1000 个节点的二叉树的最小深度为( )(第一层深度为1)
A. 11
B. 12
C. 9
D. 10

答案解析: D
1.当二叉树为完全二叉树时, 深度最小。
2.根据二叉树的性质“若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log2(n+1), 计算log2(1001)约等于9.97
3.另一种计算方式: 当第一层深度为1时, 深度为n的满完全二叉树节点为2^n - 1。
所以: 2^n - 1 >= 1000; 则n >= 10;
综上: 选择D选项

编程题

eg1

在这里插入图片描述
【解题思路】:

此题主要考察单链表操作,对链表进行遍历提取节点的值,在把该值化为十进制数即可

int getDecimalValue(struct ListNode* head)
{
    int number = 0;

    while(head)
    {
        number = (number << 1) + head->val;//左移一位就是乘以2
        head = head->next;
    }

    return number;
}

eg2

在这里插入图片描述

此题主要考察单链表的反转操作,核心思路主要是借助临时头结点,找到需要翻转链表的前驱位置,然后将要翻转链表的节点摘下进行子链表的头部插入即可,题目不难,主要就是要注意一些边界条件的处理,以及借助临时头结点后,能够使代码处理更简单。

struct ListNode* reverseBetween(struct ListNode* head, int m, int n ) {
    // write code here
    if(head->next == NULL && m == n)
    {
        return head;
    }
    //借助临时头结点,可以统一所有的情况进行处理,尤其是翻转的链表从第一个节点开始
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->next = head;
    struct ListNode* prev = newHead;
    
    for(int i = 1; i < m; i++)
    {
        prev = prev->next;
    }
    head = prev->next;//head指向翻转子链表的首部
    //取head的下一个来头插
    for(int i = m; i < n; i++)
    {
        struct ListNode* cur = head->next;
        head->next = cur->next;
        cur->next = prev->next;
        prev->next = cur;
    }
    
    head = newHead->next;
    
    free(newHead);
    newHead = NULL;
    return head;
}

eg3

在这里插入图片描述

此题主要借助链表考察前缀和的求解,要删去总和值为零的连续链表节点,只需维护每一个节点之前的所有和,此时如果该节点与前面节点的和(即前缀和)相加结果为0,则该节点就可以抵消掉前面的节点,消除的时候就是把next指针指向和的下一个节点,然后在重复该过程,直到整个链表求解结束

struct ListNode* removeZeroSumSublists(struct ListNode* head)
{
    struct ListNode* prev = (struct ListNode*)malloc(sizeof(struct ListNode));
    prev->next = head;
    struct ListNode* p = prev;
    while(p)
    {
        int sum = 0;
        struct ListNode* q = p->next;
        //从p位置找到q位置中间和为0的所有节点
        while(q)
        {
            sum += q->val;
            if(sum == 0)
            {

                struct ListNode* del = p->next;
                p->next = q->next;
                q = q->next;
                //删掉中间节点
                while(del != q)
                {
                    struct ListNode *tmp = del;
                    del = del->next;
                    free(tmp);
                }

            }

            else
            {
                q = q->next;
            }
        }

        p = p->next;
    }

    head = prev->next;
    free(prev);
    prev = NULL;
    return head;
}

eg4

在这里插入图片描述
【解题思路】:

此题变相考察二叉树的遍历,对二叉树进行先序遍历,用哈希数组判断当前值是否出现过(出现过就是1,未出现就是0),每出现一个没有在哈希数组中统计过的元素哈希数组就新增一个值,最后统计哈希数组有多少不为0的值即可。

int hash[1001];
void PrevOrder(struct TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }

    hash[root->val]++;
    PrevOrder(root->left);
    PrevOrder(root->right);
}

int numColor(struct TreeNode* root)
{
    memset(hash, 0, sizeof(hash));//全部初始化位0
    PrevOrder(root);
    int num = 0;
    //统计次数
    for(int i = 1; i < 1001; i++)
    {
        if(hash[i] != 0)
        {
            num++;
        }
    }

    return num;
}

eg5

在这里插入图片描述

此题主要考察二叉树的深度遍历, pathVal: 计算各路径和; sum: 统计各路径和;由于结点值为0,1;表示二进制数,利用二进制计算方法由最高位至最低位: pathVal(pathVal<<1)+root->val ;先序遍历树结点,用 pathVal计算至当前节点的值,当该节点为叶节点则达到路径末尾;此时统计 *sum+=pathVal

void dfs(struct TreeNode* root, int pathVal, int* sum)
{
    if(root == NULL)
    {
        return;
    }

    pathVal = (pathVal << 1) + root->val;
    if(root->left == NULL && root->right == NULL)
    {
        *sum += pathVal;
    }

    dfs(root->left, pathVal, sum);
    dfs(root->right, pathVal, sum);
}

int sumRootToLeaf(struct TreeNode* root)
{
    int sum = 0;
    dfs(root, 0, &sum);
    return sum;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 可以发现,深度相同的结点权值之和是相等的,因此我们只需要计算每一层的权值之和,然后比较大小即可。 对于一棵完全二叉树,第k层有2^(k-1)个结点,因此第k层的权值之和为2^(k-1) * wk。 我们可以遍历每一层,计算出每一层的权值之和,然后比较大小,找出最大值所在的深度即可。 具体实现可以使用一个变量max_sum记录当前最大的权值之和,以及一个变量max_depth记录当前最大权值之和所在的深度。遍历每一层时,计算该层的权值之和sum,如果sum大于max_sum,则更新max_sum和max_depth。 时间复杂度为O(logn),因为完全二叉树的深度为logn。 ### 回答2: 首先,我们需要了解什么是完全二叉树。完全二叉树是一种特殊的二叉树,除了最后一层外,每一层都被填满,最后一层从左至右填入结点,不允许出现空缺。根据这个定义,我们可以算出这棵树的深度为log2(n)+1。 接下来考虑如何求深度为d的结点权值之和。我们首先需要考虑完全二叉树的性质,即对于任意一个结点i,其左儿子结点编号为2i,右儿子结点编号为2i+1。根据这个性质,我们可以得出深度为d的结点的编号范围为[2^(d-1), 2^d-1]。 有了编号范围,我们可以直接计算深度为d的结点权值之和。具体地,我们将处于该编号范围内的结点权值求和即可。这个过程可以用一个简单的循环实现,时间复杂度为O(n/logn),n为树的结点数。 最后,我们需要遍历所有深度,找到权值之和最大的深度。这个过程可以在遍历深度时同时计算出每个深度的权值之和,并不断更新最大值。遍历深度的时间复杂度为O(logn),因此整个算法的时间复杂度为O(n/logn)。 总结起来,给定一棵完全二叉树,计算其中哪个深度的结点权值之和最大,可以按照以下步骤进行: 1. 计算树的深度为log2(n)+1。 2. 遍历每个深度,计算对应深度的结点权值之和。 3. 按照深度权值之和的大小排序,找到最大的深度。 ### 回答3: 首先,要知道什么是完全二叉树。完全二叉树一棵二叉树,除了最底层,其他层的结点个数都是满的,最后一层结点都靠左排列。根据这个性质,我们可以很容易地计算出每一层结点数量。 然后,我们考虑哪个深度的结点权值之和最大。可以发现,对于一棵完全二叉树,深度越大的结点权值之和一定越大,因为深度越大的结点数量越少,但是它们的权值都比较大。 设最大的深度为d,那么我们需要计算每一层的权值之和,并比较它们的大小。具体地,对于第k层,我们把该层的结点权值之和乘上2^(d-k),即可得到深度为d的总权值之和。最后,再比较每一层的权值之和,找出最大值所在的深度即可。 代码实现如下: int n; // 结点数 vector<int> w; // 存储每个结点的权值 // 计算深度为d的结点权值之和 int getSum(int d) { int cnt = (1 << (d - 1)); // 第d层的结点数 int sum = 0; for (int i = cnt; i < min(n+1, cnt*2); i++) { // 第d层的结点编号从2^(d-1)到2^d-1 sum += w[i]; } return sum * (1 << (n-d)); // 计算深度为d的总权值 } // 查找最大权值所在的深度 int findMaxDepth() { int maxSum = 0, maxDepth = -1; for (int d = 1; d <= n; d++) { int sum = getSum(d); if (sum > maxSum) { maxSum = sum; maxDepth = d; } } return maxDepth; } 时间复杂度为O(nlogn),因为要计算深度为d的结点权值之和,需要遍历第d层的所有结点,但是每个结点只会被遍历一次。总共有logn层,所以时间复杂度为O(nlogn)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小唐学渣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值