算法与数据结构之二分搜索树(BST)

二分搜索树

二分搜索树是一颗二叉树,二分搜索树的每个节点的值:大于其左子树的所有节点的值,小于其右子树的所有节点的值,每一颗子树也是二分搜索树,二分搜索树存储的元素必须具有可比较性

二分搜索树的定义
public class BST<E extends Comparable<E>> {
	
	private class Node {
		public E e;
		public Node left;
		public Node right;
		
		public Node(E e) {
			this.e = e;
			left = null;
			right = null;
		}
	}
	
	private Node root;
	private int size;
	
	public BST() {
		root = null;
		size = 0;
	}
	
	
	//二分搜索树是否为空
	public boolean isEmpty() {
		return size == 0;
	}
	
	//返回二分搜索树元素个数
	public int size() {
		return size;
	}
}
向二分搜索树中添加元素
//向二分搜索树中添加新的元素e
	public void add(E e) {
		root = add(root, e);
	}
	
	//向以node为根的二分搜索树中插入元素e,递归算法
	//返回插入新节点后二分搜索树的根
	private Node add(Node node, E e) {
		if (node == null) {
			size++;
			return new Node(e);
		}
		
		if (e.compareTo(node.e) < 0) {
			node.left = add(node.left, e);
		} else if (e.compareTo(node.e) > 0) {//之所以是else if,忽略了两者相等的情况,如果相等则什么都不做
			node.right = add(node.right, e);
		}
		
		return node;
	}
二分搜索树的查询操作
//看二分搜索树中是否包含元素e
	public boolean contains(E e) {
		return contains(root, e);
	}
	
	//看以node为根的二分搜索树中是否包含元素e,递归算法
	private boolean contains(Node node, E e) {
		if (node == null) {
			return false;
		}
		
		if (e.compareTo(node.e) == 0) {
			return true;
		} else if (e.compareTo(node.e) > 0) {
			return contains(node.right, e);
		} else {
			return contains(node.left, e);
		}
	}
二分搜索树的前序遍历
public void preOrder() {
		preOrder(root);
	}
	//前序遍历以node为根的二分搜索树,递归算法
	private void preOrder(Node node) {
		if (node == null) {
			return ;
		}
		
		System.out.println(node.e);
		preOrder(node.left);
		preOrder(node.right);
	}
为二分搜索树添加打印函数,以前序遍历为例
@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		generateBSTString(root, 0, res);
		return res.toString();
	}
	
	//生成以node为根节点,深度为depth的描述二叉树的字符串
	private void generateBSTString(Node node, int depth, StringBuilder res) {
		if (node == null) {
			res.append(generateDepthString(depth) + "null\n");
			return;
		}
	
		res.append(generateDepthString(depth) + node.e + "\n");
		generateBSTString(node.left, depth + 1, res);
		generateBSTString(node.right, depth + 1, res);
	}
	
	private String generateDepthString(int depth) {
		StringBuilder res = new StringBuilder();
		for (int i = 0; i < depth; i++) {
			res.append("--");
		}
		
		return res.toString();
	}
	
二分搜索树的中序和后序遍历

二分搜索树的中序遍历的结果就是二分搜索树排序后的结果

public void inOrder() {
		inOrder(root);
	}
	//中序遍历以node为根的二分搜索树,递归算法
	private void inOrder(Node node) {
		if (node == null) {
			return ;
		}
		
		inOrder(node.left);
		System.out.println(node.e);
		inOrder(node.right);
	}

后序遍历的一个应用:为二叉树释放内存,比如在C++这些需要手动释放内存空间的语言来说,对二叉树内存的释放就需要使用后序遍历

public void posOrder() {
		posOrder(root);
	}
	//中序遍历以node为根的二分搜索树,递归算法
	private void posOrder(Node node) {
		if (node == null) {
			return ;
		}
		
		posOrder(node.left);
		posOrder(node.right);
		System.out.println(node.e);
	}
对前序,中序和后序的深入理解

对于一个二叉树的遍历来说,抛开打印的情况,也就是,先遍历左子树,在遍历右子树,在真正的递归过程中,会来到每个节点三次,何谓三次?比如根节点为5,左孩子为3,右孩子为4,首先来到5,接着访问5的左孩子3,访问3的左孩子,返回到3,在访问3的右孩子,在返回到3,依次类推,来到每个节点三次也就是说会进以这个节点为参数的
函数三次,前序遍历就是第一次访问该节点时,打印该节点的值,中序就是第二次访问该节点时,打印该节点值,后序就是第三次访问该节点的时候打印该节点的值。

递归和非递归方式实现二叉树的先中后序遍历
二分搜索树的层序遍历

层序遍历的应用:更快的找到问题的解,常用于和图有关的最短路径的算法设计中

public void levelOrder() {
		Queue<Node> queue = new LinkedList<>();
		queue.offer(root);
		while(!queue.isEmpty()) {
			Node cur = queue.poll();
			System.out.print(cur.e + " ");
			if (cur.left != null) {
				queue.offer(cur.left);
			}
			if (cur.right != null) {
				queue.offer(cur.right);
			}
		}
		System.out.println();
	}
删除二分搜索树的最小值和最大值
//寻找二分搜索树的最小元素
	public E minimum() {
		if (size == 0) {
			throw new IllegalArgumentException("BST is empty!");
		}
		return minimum(root).e;
	}
	
	//返回以Node为根的二分搜索树的最小值所在的节点
	private Node minimum(Node node) {
		if (node.left == null) {
			return node;
		}
		
		return minimum(node.left);
	}
	
	//寻找二分搜索树的最大元素
	public E maximum() {
		if (size == 0) {
			throw new IllegalArgumentException("BST is empty!");
		}
		return maximum(root).e;
	}
	
	//返回以Node为根的二分搜索树的最大值所在的节点
	private Node maximum(Node node) {
		if (node.right == null) {
			return node;
		}
		
		return maximum(node.right);
	}
	
	//从二分搜索树中删除最小值所在的节点,返回最小值
	public  E removeMin() {
		E ret = minimum();
		root = removeMin(root);
		return ret;
	}
	
	//删除掉以node为根的二分搜索树中的最小节点
	//返回删除节点后新的二分搜索树的根
	private Node removeMin(Node node) {
		if (node.left == null) {
			Node rightNode = node.right;
			node.right = null;
			size--;
			return rightNode;
		}
		
		node.left = removeMin(node.left);
		return node;
	}
	
	//从二分搜索树中删除最大值所在的节点,返回最大值
	public  E removeMax() {
		E ret = maximum();
		root = removeMax(root);
		return ret;
	}
	
	//删除掉以node为根的二分搜索树中的最大节点
	//返回删除节点后新的二分搜索树的根
	private Node removeMax(Node node) {
		if (node.right == null) {
			Node leftNode = node.left;
			node.left = null;
			size--;
			return leftNode;
		}
		
		node.right = removeMax(node.right);
		return node;
	}
删除二分搜索树的任意元素
//从二分搜索树中删除元素为e的节点
	public void remove(E e) {
		root = remove(root, e);
	}
	
	//删除以node为根的二分搜索树中值为e的节点,递归算法
	//返回删除节点后新的二分搜索树的根
	public Node remove(Node node, E e) {
		if (node == null) {
			return null;
		}
		
		if (e.compareTo(node.e) < 0) {
			node.left = remove(node.left, e);
			return node;
		} else if (e.compareTo(node.e) > 0) {
			node.right = remove(node.right, e);
			return node;
		} else {//e.compareTo(node.e) == 0
			//待删除节点左子树为空的情况
			if (node.left == null) {
				Node rightNode = node.right;
				node.right = null;
				size--;
				return rightNode;
			}
			
			//待删除节点右子树为空的情况
			if (node.right == null) {
				Node leftNode = node.left;
				node.left = null;
				size--;
				return leftNode;
			}
			
			//待删除左右节点都不为空的情况
			//找到比待删除节点大的最小节点,即待删除节点右子树的最小节点
			//用这个节点顶替待删除节点的位置
			Node successor = minimum(node.right);
			successor.right = removeMin(node.right);
			successor.left = node.left;
			node.right = null;
			node.left = null;
			return successor;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值