算法导论|第12章 二叉搜索树

二叉搜索树.begin()

定义:

对于树中

  • 对于任何节点x

    • x的左子树中的所有节点的key值,不大于x。
    • x的右子树中的所有key不小于x.key

性质

  • 二叉搜索树中序遍历是有序的。

二叉搜索树的操作

查找

要查找关键字k

x = root
while x!=NIL and k != x.key {
	if k < x.key
        x = x.left
    else
        x = x.right
}
return x

MAX和MIN

分别沿着左右子树寻找

后继和前驱

按照中序遍历的顺序,一个结点的后继寻找:

  • 如果右子树不为空,返回右子树中最小的。
  • 否则向父辈寻找。
    • 直到找到作为父结点左子树根的结点。返回该根的值。
if x.right!=nullptr
    return tree-min(x.right)
auto y = x.p
while y!=nullptr&&y.right ==x
    x = y
    y = y.p
return y

如果最后y是nullptr,说明x一直在右子树中。没有后继结点。

相同的,要寻找前驱

if x.left!=nullptr
    return tree-max(x.left)
// 如果没有左子树
auto y = x.p
while y!=nullptr&&y.left ==x
    x = y
    y = y.p
return y

插入和删除

/**
 * 
 * @param t 要插入的新值
 */
void insert(T t) requires std::totally_ordered<T> {
  TreeNode<T>* node = new TreeNode{t};  // 要插入的新结点
  TreeNode<T>* y = nullptr;
  TreeNode<T>* x = root_;  // x是node应当插入的位置。
  while (x != nullptr) {
    y = x;
    if (x->val < t) {
      x = x->right;
    } else {
      x = x->left;
    }
  }
  node->parent = y;
  if (node->val < y->val) {
    y->left = node;
  } else {
    y->right = node;
  }
}

插入新的叶结点。

删除结点z分三种情况

  • 若z为叶子结点,直接删除,并修改父结点对应的子结点为nullptr
  • 若z只有一个孩子,则提升该孩子。
  • 若z有两个孩子,找到z的后继y(因为保证有两个孩子,所以后继一定是右子树中的最小结点)。让y代替z的位置。
 /**
  * 让v代替u的位置
  * @param u
  * @param v
  */
 void transplant(TreeNode<T>* u, TreeNode<T>* v) {
   if (u->parent == nullptr) {
     // u本来是根节点的情况。
     root_ = v;
   } else if (u == u->parent->left) {
     u->parent->left = v;
   } else {
     u->parent->right = v;
   }
   if (v != nullptr) {
     v->parent = u;
   }
 }

public:
 TreeNode<T>* min_of_tree(TreeNode<T>* n) {
   if (n == nullptr) {
     return nullptr;
   }
   while (n->left != nullptr) {
     n = n->left;
   }
   return n;
 }
 void delete_node(TreeNode<T>* n) {
   if (n == nullptr) {
     return;
   }
   if (n->left == nullptr) {
     transplant(n, n->right);  // 这里有两种情况:
     // 1. n.right 为nullptr
     // 2. n.right不为nullptr。实际上是一样的,
   } else {
     if (n->right == nullptr) {
       transplant(n, n->left);
     } else {
       auto y = min_of_tree(n->right);  // 找到后继结点。
       if (y->parent != n) {
         // 感觉这里不判断也行,没有影响。
         transplant(y, y->right);
         y->right = n->right;
         y->right->parent = y;
       }
       transplant(n, y);
       y->left = n->left;
       y->left->parent = y;
     }
   }
 }