《算法导论》学习笔记——二叉搜索树

二叉搜索树

什么是二叉搜索树

对于任何节点x,其左子树中的关键字最大不超过\(x.key\),其右子树中的关键字最小不低于\(x.key\)。即:

​ 设x是二叉搜索中的一个结点。如果y是x左子树中的任意结点,那么\(y.key<=x.key\)。如果y是x右子树中的任意结点,那么\(y.key>=x.key\)

中序遍历

中序遍历可以按序输出二叉搜索树中的所有关键字。

public void inorderTreeWalk(Node x){
  if(x != null){
    inorderTreeWalk(x.left);
    System.out.println(x.key);
    inorderTreeWalk(x.right);
  }
}

查询二叉搜索树

高度为h的二叉搜索树上进行SEARCH(普通查询)、MINIMUM(最大关键字)、MAXIMUM(最小关键字)、SUCCESSOR(后继)和PREDECESSOR(前驱)等查询操作,都可以在\(O(h)\)世间内完成。

  • 普通查找
 /**
	 * 递归实现的普通查找
	 * @param x   二叉搜索树的根结点
	 * @param key 查询结点的关键字
	 */
	public Node treeSearch(Node x, int key) {
		if (x == null || key == x.key)
			return x;
		if (key < x.key) {
			return treeSearch(x.left, key);
		} else {
			return treeSearch(x.right, key);
		}
	}
	/**
	 * 迭代实现的普通查找
	 */
	public Node iterativeTreeSearch(Node x, int key) {
		while (x != null && key != x.key) {
			if (key < x.key) {
				x = x.left;
			} else {
				x = x.right;
			}
		}
		return x;
	}
  • 最大关键字元素和最小关键字元素
  /**
	 * 获取最小关键字元素
	 * @param x 查询二叉树根结点
	 */
	public Node treeMinimum(Node x) {
		while (x.left != null) {
			x = x.left;
		}
		return x;
	}
	/**
	 * 获取最大关键字元素
	 */
	public Node treeMaximum(Node x) {
		while (x.right != null) {
			x = x.right;
		}
		return x;
	}
  • 后继和前驱

    按中序遍历次序查找一个结点的后继和前驱。一个结点x的后继是大于x.key的最小关键字的结点,前驱是小于x.key的最大关键字的结点。

	/**
	 * 返回一个结点的后继
	 */
	public Node successor(Node x) {
		if (x.right != null) {
			return treeMinimum(x.right);
		}
		Node y = x.p;
		while (y != null && x == y.right) {
			x = y;
			y = y.p;
		}
		return y;
	}
  • 练习

12.2-5 证明:如果一颗二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子。

证明如下: 根据二叉搜索树的性质,如果一个结点存在右子结点,则这个结点的后继是其右子树中的最小结点;假设这个结点的后继有左孩子,说明该后继结点不是右子树中的最小结点,即假设不成立,即这个结点的后继没有左孩子。同理可证明这个结点前驱没有右孩子。

插入和删除

  • 插入
	/**
	 * 插入一个结点
	 *
	 * @param tree 二叉搜索树
	 * @param z    待插入的结点
	 */
	public void treeInsert(BinarySearchTree tree, Node z) {
		Node y = null;
		Node x = tree.root;
		while (x != null) {
			y = x;
			if (z.key < x.key) {
				x = x.left;
			} else {
				x = x.right;
			}
		}
		z.parent = y;
		if (y == null) {
			tree.root = z; // 二叉搜索树为空
		} else if (z.key < y.key) {
			y.left = z;
		} else {
			y.right = z;
		}
	}
  • 删除
 /**
	 * 在tree中删除结点z
	 */
	public void treeDelete(BinarySearchTree tree, Node z) {
		if (z.left == null) {
			transplant(tree, z, z.right);
		} else if (z.right == null) {
			transplant(tree, z, z.left);
		} else {
			Node y = treeMinimum(z.right);
			if (y != z.right) {
				transplant(tree, y, y.right);
				y.right = z.right;
				y.right.parent = y;
			}
			transplant(tree, z, y);
			y.left = z.left;
			y.left.parent = y;
		}
	}
	/**
	 * 在tree中,用一棵以v为根的子树替换一棵以u为根的子树
	 * 替换完后,结点u的父结点变为结点v的父结点,并且结点v成为u的父结点的相应子结点
	 * 注意,这里并没有处理v的子结点更新;这些更新由此方法调用者来负责。
	 */
	public void transplant(BinarySearchTree tree, Node u, Node v) {
		if (u.parent == null) {
			tree.root = v;
		} else if (u.parent.left == u) {
			u.parent.left = v;
		} else {
			u.parent.right = v;
		}
		if (v != null) {
			v.parent = u.parent;
		}
	}
posted @ 2021-01-03 15:54  余快  阅读(137)  评论(0编辑  收藏  举报