二叉查找树(Java实现)

先来API:
在这里插入图片描述

public class BST<Key extends Comparable<Key>, Value> {
	private Node root;

	private class Node {
		private Key key;
		private Value val;
		private Node left, right;
		private int N;

		public Node(Key key, Value val, int N) {
			this.key = key;
			this.val = val;
			this.N = N;
		}
	}

	public int size() {
		return size(root);
	}

	private int size(Node x) {
		if (x == null) {
			return 0;
		} else {
			return x.N;
		}
	}

	public Value get(Key key) {
		return get(root, key);
	}

	private Value get(Node x, Key key) {
		if (x == null) {
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp < 0) {
			return get(x.left, key);
		} else if (cmp > 0) {
			return get(x.right, key);
		} else {
			return x.val;
		}
	}

	public void put(Key key, Value val) {
		root = put(root, key, val);
	}

	private Node put(Node x, Key key, Value val) {
	//此方法实现了二叉树的重要性质
		if (x == null) {
			return new Node(key, val, 1);
		}
		int cmp = key.compareTo(x.key);
		if (cmp < 0) {
			x.left = put(x.left, key, val);
		} else if (cmp > 0) {
			x.right = put(x.right, key, val);
		} else {
			x.val = val;
		}
		x.N = size(x.left) + size(x.right) + 1;
		return x;
	}

	public Key max() {
		return max(root).key;
	}

	private Node max(Node x) {
		if (x.right == null) {
			return x;
		}
		return max(x.right);
	}

	public Key min() {
		return min(root).key;
	}

	private Node min(Node x) {
		if (x.left == null) {
			return x;
		}
		return min(x.left);
	}

	public Key floor(Key key) {
		Node x = floor(root, key);
		if (x == null) {
			return null;
		}
		return x.key;
	}

	private Node floor(Node x, Key key) {
		if (x == null) {
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp == 0) {
			return x;
		}
		if (cmp < 0) {
			return floor(x.left, key);
		}
		Node t = floor(x.right, key);
		if (t != null) {
			return t;
		} else {
			return x;
		}
	}

	public Key ceiling(Key key) {
		Node x = ceiling(root, key);
		if (x == null) {
			return null;
		}
		return x.key;
	}

	private Node ceiling(Node x, Key key) {
		if (x == null) {
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp == 0) {
			return x;
		}
		if (cmp > 0) {
			return floor(x.right, key);
		}
		Node t = floor(x.left, key);
		if (t != null) {
			return t;
		} else {
			return x;
		}
	}

	public Key select(int k) {
		return select(root, k).key;
	}

	private Node select(Node x, int k) {
		if (x == null) {
			return null;
		}
		int t = size(x.left);
		if (t > k) {
			return select(x.left, k);
		} else if (t < k) {
			return select(x.right, k - t - 1);
		} else {
			return x;
		}
	}

	public int rank(Key key) {
		return rank(key, root);
	}

	private int rank(Key key, Node x) {
		if (x == null) {
			return 0;
		}
		int cmp = key.compareTo(x.key);
		if (cmp < 0) {
			return rank(key, x.left);
		} else if (cmp > 0) {
			return 1 + size(x.left) + rank(key, x.right);
		} else {
			return size(x.left);
		}
	}

	public void deleteMin() {
		root = deleteMin(root);
	}

	private Node deleteMin(Node x) {
		if (x.left == null) {
			return x.right;
		}
		x.left = deleteMin(x.left);
		x.N = size(x.left) + size(x.right) + 1;
		return x;
	}

	public void deleteMax() {
		root = deleteMax(root);
	}

	private Node deleteMax(Node x) {
		if (x.right == null) {
			return x.left;
		}
		x.right = deleteMax(x.right);
		x.N = size(x.left) + size(x.right) + 1;
		return x;
	}

	public void delete(Key key) {
		root = delete(root, key);
	}

	private Node delete(Node x, Key key) {
		if (x == null) {
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp < 0) {
			x.left = delete(x.left, key);
		} else if (cmp > 0) {
			x.right = delete(x.right, key);
		} else {
			if (x.right == null) {
				return x.left;
			}
			if (x.left == null) {
				return x.right;
			}
			Node t = x;
			x = min(t.right);
			x.right = deleteMin(t.right);
			x.left = t.left;
		}
		x.N = size(x.left) + size(x.right) + 1;
		return x;
	}

	public Iterable<Key> keys() {
		return keys(min(), max());
	}

	public Iterable<Key> keys(Key lo, Key hi) {
		Queue<Key> queue = new Queue<Key>();
		keys(root, queue, lo, hi);
		return queue;
	}

	private void keys(Node x, Queue<Key> queue, Key lo, Key hi) {
		if (x == null) {
			return;
		}
		int cmplo = lo.compareTo(x.key);
		int cmphi = hi.compareTo(x.key);
		if (cmplo < 0) {
			keys(x.left, queue, lo, hi);
		}
		if (cmplo <= 0 && cmphi >= 0) {
			queue.enqueue(x.key);
		}
		if (cmphi > 0) {
			keys(x.right, queue, lo, hi);
		}
	}

	// Exercise 3.2.6
	public int height() {
		return height(root);
	}

	private int height(Node x) {
		if (x == null) {
			return -1;
		}
		return 1 + Math.max(height(x.left), height(x.right));
	}

	// Exercise 3.2.32
	private boolean isBST() {
		return isBST(root, null, null);
	}

	private boolean isBST(Node x, Key min, Key max) {
		if (x == null) {
			return true;
		}
		if (min != null && x.key.compareTo(min) <= 0) {
			return false;
		}
		if (max != null && x.key.compareTo(max) >= 0) {
			return false;
		}
		return isBST(x.left, min, x.key) && isBST(x.right, x.key, max);
	}

	/**
	 * Exercise 3.2.33
	 * 
	 * @return
	 */
	public boolean isRankConsistent() {
		for (int i = 0; i < size(); i++) {
			if (i != rank(select(i))) {
				return false;
			}
		}
		for (Key key : keys()) {
			if (!key.equals(select(rank(key)))) {
				return false;
			}
		}
		return true;
	}
}

对于二叉树的性质:对于根节点来说,左子树的所有结点都比它本身小,右子树的所有结点都比它本身大

查找过程图:

在这里插入图片描述

插入过程图

在这里插入图片描述

floor方法图示

在这里插入图片描述

select方法图示( 即找出排名为某的键)

在这里插入图片描述

删除图示

在这里插入图片描述
在这里插入图片描述

思想分析:

查找
  • A、如果二叉查找树为空,查找失败(search miss),返回null;

  • B、如果根节点的键等于要查找的键,返回根节点的值(search hit)。

  • C、继续在相应的子树中查找。如果要查找的键小于根节点的键,在左子树中查找;如果要查找的键大于根节点的键,在右子树中查找。

  • D、重复ABC步骤,直至search miss或者search hit。

插入实现
  • A、如果二叉查找树是空的,生成一个新节点,并返回该节点

  • B、如果根节点键和要插入的键相等,更新根节点的值。

  • C、如果要插入的键小于根节点的键,在左子树插入,并将根节点的左链接指向插入后的左子树。

  • D、如果要插入的键小于根节点的键,在右子树插入,并将根节点的右链接指向插入后的右子树。

  • E、更新根节点的size,并返回根节点,作为插入新节点后的二叉树根节点。

  • F、重复ABCD,直至插入或者更新成功。

min实现和max实现
  • A、如果根节点的左链接是null,返回根结点。

  • B、否则继续在左子树中查找。

  • C、重复AB,直至找到一个根节点的左链接是null,返回当前结点。

floor和ceiling实现

思路:
如果给定的键key小于二叉查找树根节点的键,那么小于等于key的最大键floor(key)一定在根节点的左子树中;
如果给定的键key大于二叉查找树根节点的键,那么只有当根结点右子树中存在小于等于key的结点时,小于等于key的最大键才会出现在右子树中,否则根结点就是floor(key)

  • A、如果根节点是null,则直接返回null。

  • B、如果根节点键值大于key,则继续floor(key)在左子树中,所以继续在左子树中查找。

  • C、如果根节点键值刚好等于key,则根节点的键值即为floor(key),直接返回该键。

  • D、如果根节点键值小于key,那么根节点的key有可能就是floor(key),只要右子树中不存在节点的key小于等于输入的key。

  • E、重复ABCD

select实现
size是专门为select和rank等函数准备的。
  • A、如果根节点为null,直接返回null。

  • B、如果左子树的节点个数为t,select(k),如果t=k,那么直接返回根节点的值。

  • C、如果t>k,根节点的排序太大,需要在左子树中继续select(k)。

  • D、如果t<k,根节点的排序太小,需要在右子树中继续select(k-t-1)。

  • E、重复ABCD

rank实现
  • A、如果根节点为null,返回0。

  • B、如果根节点的键刚好等于key,返回左子树的节点个数(刚好只有左子树的所有键小于key)。

  • C、如果根节点的键大于key,则在左子树中rank(key)(只有左子树才有小于key的键)。

  • D、如果根节点的键小于key,则除了左子树所有键都小于key外,右子树也可能有小于key的键,则返回左子树的节点个数+rank(key)在右子树的值+1。

  • E、重复ABCD。

删除最小值和最大值
  • 一直往左走,直到到达左链接为null的节点x(最小值),然后将指向该节点x的链接替换为x.right。
删除节点

如果要删除的节点x只有子节点或者没有子节点,那么可以效仿删除最小值和最大值的做法。

如果有两个子节点呢?应该在右子树中找继承者来替换x,然后再返回x。

  • A、用t保存即将删除的节点x。

  • B、将x指向右子树的最小键节点,也就是继承者。

  • C、x.right = deleteMin(t.right)。

  • D、x.left = t.left。

参考了以下博客:

https://www.cnblogs.com/songdechiu/p/6821168.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值