I. 问答题

  1. 容器适配器是什么?举个例子。

    • 答案:容器适配器(Container Adapter)是对其他容器类型的封装,它提供了一些简化的接口来完成特定操作。容器适配器通常不直接暴露底层容器的所有功能,而是只提供一部分操作,像栈(stack)、队列(queue)和优先级队列(priority_queue)就是容器适配器的例子。
    • 解析:容器适配器在STL中对底层容器(如dequevector)进行了封装,提供了特定的数据结构接口。例如,stack使用deque作为底层容器,但只提供栈操作(如pushpop)。
  2. C++声明一个list容器的迭代器,元素类型是string

    • 答案
      1
      list<string>::iterator it;
    • 解析list<string>表示一个元素类型为stringlist容器,iterator是用来遍历容器的迭代器类型。
  3. C++声明一个set容器对象BATT,元素类型为整数,并且按降序排列。

    • 答案
      1
      set<int, greater<int>> BATT;
    • 解析set<int, greater<int>>声明一个按降序排列的set容器,greater<int>是一个比较器,它反转了默认的升序排列。
  4. 编写递归算法height(t),计算二叉树t的高度。

    • 答案
      1
      2
      3
      4
      5
      6
      int height(TreeNode* t) {
      if (t == nullptr) return 0;
      int leftHeight = height(t->left);
      int rightHeight = height(t->right);
      return max(leftHeight, rightHeight) + 1;
      }
    • 解析:递归计算二叉树的高度。如果树为空,返回0。否则,计算左子树和右子树的高度,取最大值加1即为当前树的高度。
  5. 计算循环for(i=1; i<=n; i+=2) sum+=i;的时间复杂度。

    • 答案O(n/2),简化为O(n)
    • 解析:这个循环每次增加2,因此会循环大约n/2次。时间复杂度是O(n),常数项可以省略。

II. 分析与绘图题


题目1:删除76节点后的二叉搜索树

  • 原树

    1
    2
    3
    4
    5
       40
    / \
    30 76
    \ / \
    35 60 130
  • 答案树(删除76后,将60提升为根):

    1
    2
    3
    4
    5
       40
    / \
    30 60
    \ \
    35 130

删除76节点后的二叉搜索树

  • 答案
    • 删除节点76后,树的结构会发生变化。根据二叉搜索树的删除规则,删除节点76后,树需要进行相应的调整。具体步骤包括找到替代节点并重新链接树的各个部分。
    • 解析:可以通过绘制树的图示来展示删除操作后的结构。

二叉搜索树的性质

二叉搜索树(Binary Search Tree, BST)是一种具有以下性质的数据结构:

  1. 节点顺序性质

    • 每个节点有一个键值(key),并且满足以下条件:
      • 左子树中的所有节点的键值都比当前节点的键值小。
      • 右子树中的所有节点的键值都比当前节点的键值大。
  2. 唯一性

    • 每个节点的键值在整棵树中都是唯一的。
  3. 递归性质

    • 对于树中的每个节点,它的左子树和右子树也必须是二叉搜索树。
  4. 查找效率

    • 对于任意节点,其左子树所有节点的值都小于该节点的值,右子树所有节点的值都大于该节点的值,因此能够通过递归方式快速查找。
  5. 遍历方式

    • 中序遍历(In-order traversal):对二叉搜索树进行中序遍历时,会得到一个有序的节点值序列(从小到大)。
  6. 插入与删除操作

    • 插入和删除节点时,需要保持树的二叉搜索树性质。在插入时,新的节点会被放入合适的位置;在删除节点时,如果节点有两个子树,通常需要找到右子树中的最小节点或左子树中的最大节点来替代被删除的节点。
  7. 高度与平衡性

    • 在最坏的情况下(例如插入的节点按顺序排列),二叉搜索树的高度可能是树的节点数 n,即退化为链表,导致查找效率下降到 O(n)。
    • 在平衡的二叉搜索树(如AVL树或红黑树)中,树的高度是 O(log n),查找、插入和删除操作的时间复杂度为 O(log n)。

题目2:插入130后的红黑树

  • 原红黑树(插入前,带有红色标记的节点):

    1
    2
    3
    4
    5
           110 (黑)
    / \
    50 (红) 125 (黑)
    / \
    30(黑) 80(黑)
  • 答案红黑树(插入130后进行修复,确保红黑树的性质):

    1
    2
    3
    4
    5
           110 (黑)
    / \
    50 (红) 125 (黑)
    / \ \
    30(黑) 80(黑) 130(红)

插入130到红黑树后的修复过程

  • 答案
    • 插入130后,根据红黑树的特性,插入节点可能会破坏红黑树的性质,因此需要进行修复。通常会涉及旋转和颜色调整,确保树满足红黑树的五个性质。
    • 解析:这类问题要求分析并绘制红黑树插入后的修复过程,重点是节点颜色的改变和旋转操作。

III. 程序分析与问题解答

  1. 二叉搜索树操作分析
    • 答案
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      BinSearchTree<string> words;
      BinSearchTree<string>::Iterator itr1;
      string word;
      cout << "Please enter a word; the sentinel is ***: ";
      cin >> word;
      while (word != "***") {
      words.insert(word);
      cout << "Please enter a word; the sentinel is ***: ";
      cin >> word;
      }
      // 遍历并输出树中的所有单词
      for (itr1 = words.begin(); itr1 != words.end(); itr1++) {
      cout << *itr1 << endl;
      }
      words.erase(words.find("Grey")); // 删除节点"Grey"
      words.erase(words.begin()); // 删除树的第一个元素
      for (itr1 = words.begin(); itr1 != words.end(); itr1++) {
      cout << *itr1 << endl;
      }
    • 解析:该代码段通过输入单词并插入到二叉搜索树中,直到输入***为止。然后,它删除单词"Grey"和树的第一个元素,并输出修改后的树。通过遍历器(Iterator)来遍历树。

IV. 算法描述或证明

  1. STL List容器的实现结构和insert操作步骤

    • 答案list容器是双向链表的实现,每个元素包含指向前后元素的指针。insert操作的步骤包括:找到插入位置,创建新节点,修改前后节点的指针,并将新节点插入到链表中。
    • 解析list容器是通过双向链表来实现的,因此插入和删除操作的时间复杂度为O(1),而查找操作的时间复杂度为O(n)
  2. 证明完全二叉树的高度与节点数的关系

    • 答案:设树的高度为h,则树的节点数为2^(h+1) - 1
    • 解析:完全二叉树的节点数公式来源于树的层数,每一层的节点数是2^ii为层数),所有节点数即为2^h - 1

V. 编程题

  1. 实现基于STL容器的栈

    • 答案
      1
      2
      3
      4
      stack<int> s;
      s.push(1); // 入栈
      s.push(2);
      s.pop(); // 出栈

    当然这样很弱智,正常来说应该是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    #include <iostream>
    #include <vector>

    template <typename T>
    class Stack {
    private:
    std::vector<T> data;

    public:
    // 判断栈是否为空
    bool empty() const {
    return data.empty();
    }

    // 获取栈顶元素
    T top() const {
    return data.back();
    }

    // 压入元素
    void push(const T& value) {
    data.push_back(value);
    }

    // 弹出栈顶元素
    void pop() {
    if (!empty()) {
    data.pop_back();
    }
    }

    // 获取栈的大小
    size_t size() const {
    return data.size();
    }
    };

    int main() {
    Stack<int> s;

    s.push(10);
    s.push(20);
    s.push(30);

    std::cout << "栈顶元素: " << s.top() << std::endl; // 输出30
    s.pop();
    std::cout << "弹出栈顶元素后, 栈顶元素: " << s.top() << std::endl; // 输出20

    return 0;
    }

  2. 实现二叉搜索树的递归前序遍历

    • 答案
      1
      2
      3
      4
      5
      6
      void preorder_traversal(TreeNode* node) {
      if (node == nullptr) return;
      cout << node->val << " "; // 访问当前节点
      preorder_traversal(node->left); // 遍历左子树
      preorder_traversal(node->right); // 遍历右子树
      }
    • 解析:前序遍历的顺序是:先访问根节点,再遍历左子树,最后遍历右子树。递归实现时,通过先访问当前节点,再递归遍历左右子树。

    我本人使用的技巧是,x序遍历的顺序,就是把根节点放在左、右的什么位置。