二叉搜索树(BST)

BST 的基本功能
public class BST<E extends Comparable<E>> {private Node root;private int size;public BST(){root=null;size=0;}public int size(){return size;}public boolean isEmpty(){return size==0;}private class Node{public E e;public Node left,right;public Node(E e){this.e=e;this.left=null;this.right=null;}}}
添加元素
思路
- 对于如下的 BST,向其中添加 28:

- 28 与根结点 41 比较,应该插入左子树

- 28 与左子树的根结点 22 比较,应该插入该结点的右子树

- 28 与 33 比较,应该插入该结点的左子树,该结点的左子树为空,则将值为 28 的结点添加到该结点的左子树中

- 最终将 28 添加到该 BST 中

代码
//向BST中添加新元素epublic void add(E e){if(root==null){root=new Node(e);size++;return;}else{add(root,e);}}//向以node为根节点的BST树种插入元素eprivate void add(Node node,E e){if(e.equals(node.e)){//插入元素与根节点相等,直接返回return;}else if(e.compareTo(node.e)<0 && node.left==null){node.left=new Node(e);size++;return;}else if(e.compareTo(node.e)>0 && node.right==null){node.right=new Node(e);size++;return;}if(e.compareTo(node.e)<0){add(node.left,e);}else{ //e.compareTo(node.e)>0add(node.right,e);}}
改进添加元素
前文中的添加操作,对 root==null 的情况进行了特殊处理,并且 add(Node,E) 中的递归条件也太冗长了。
这里我们写一个 add() 方法,返回插入新节点后该 BST 的根节点
//向BST中添加新元素epublic void add(E e){root=add(root,e);}//向以node为根节点的BST树种插入元素e//返回插入新元素后该BST的根private Node add(Node node,E e){if(node==null){size++;return new Node(e);}//注意:对于e.equals(node.e)不做处理if(e.compareTo(node.e)<0){node.left=add(node.left,e);}else if(e.compareTo(node.e)>0){node.right=add(node.right,e);}return node;}
查询元素
//查看BST中是否包含元素epublic boolean contains(E e){return contains(root,e);}//查看以node为根节点的BST中是否包含元素eprivate 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.left,e);}else{return contains(node.right,e);}}
遍历元素(递归形式)
- 前序遍历
//BST的前序遍历public void preOrder(){preOrder(root);}private void preOrder(Node node){if(node==null){return;}System.out.println(node.e);preOrder(node.left);preOrder(node.right);}
/*** 利用前序遍历打印 BST*/@Overridepublic String toString() {StringBuilder res=new StringBuilder();generateBST(root,0,res);return res.toString();}//生成以node为根节点,深度为depth的描述二叉树的字符串(利用前序遍历)private void generateBST(Node node,int depth,StringBuilder res){if(node==null){res.append(generateDepth(depth)+"null\n");return;}res.append(generateDepth(depth)+node.e+"\n");generateBST(node.left,depth+1,res);generateBST(node.right,depth+1,res);}private String generateDepth(int depth){StringBuilder res=new StringBuilder();for(int i=0;i<depth;i++){res.append("--");}return res.toString();}
- 中序遍历
//BST的中序遍历public void inOrder(){inOrder(root);}private void inOrder(Node node){if(node==null){return;}inOrder(node.left);System.out.println(node.e);inOrder(node.right);}
- 后序遍历
//BST的后序遍历public void postOrder(){postOrder(root);}private void postOrder(Node node){if(node==null){return;}postOrder(node.left);postOrder(node.right);System.out.println(node.e);}
遍历元素(非递归形式)
//枚举命令,GO表示访问元素,COUT表示打印元素private enum Command{GO,COUT};private class StackNode{Command command;Node node;StackNode(Command command,Node node){this.command=command;this.node=node;}}
- 前序遍历
//BST的前序遍历(非递归方式)public void preOrderNR(){preOrderNR(root);}private void preOrderNR(Node node){if(node==null){return;}Stack<StackNode> stack=new Stack<>();stack.push(new StackNode(Command.GO,node));while(!stack.isEmpty()){StackNode stackNode=stack.pop();Node n=stackNode.node;Command command=stackNode.command;if(command==Command.COUT){System.out.println(stackNode.node.e);}else{if(n.right!=null){stack.push(new StackNode(Command.GO,n.right));}if(n.left!=null){stack.push(new StackNode(Command.GO,n.left));}stack.push(new StackNode(Command.COUT,n));}}}
- 中序遍历
//BST的中序遍历(非递归方式)public void inOrderNR(){inOrderNR(root);}private void inOrderNR(Node node){if(node==null){return;}Stack<StackNode> stack=new Stack<>();stack.push(new StackNode(Command.GO,node));while(!stack.isEmpty()){StackNode stackNode=stack.pop();Node n=stackNode.node;Command command=stackNode.command;if(command==Command.COUT){System.out.println(stackNode.node.e);}else{if(n.right!=null){stack.push(new StackNode(Command.GO,n.right));}stack.push(new StackNode(Command.COUT,n));if(n.left!=null){stack.push(new StackNode(Command.GO,n.left));}}}}
- 后序遍历
//BST的后序遍历(非递归方式)public void postOrderNR(){postOrderNR(root);}private void postOrderNR(Node node){if(node==null){return;}Stack<StackNode> stack=new Stack<>();stack.push(new StackNode(Command.GO,node));while(!stack.isEmpty()){StackNode stackNode=stack.pop();Node n=stackNode.node;Command command=stackNode.command;if(command==Command.COUT){System.out.println(stackNode.node.e);}else{stack.push(new StackNode(Command.COUT,n));if(n.right!=null){stack.push(new StackNode(Command.GO,n.right));}if(n.left!=null){stack.push(new StackNode(Command.GO,n.left));}}}}
层序遍历
//BST的层序遍历public void levelOrder(){Queue<Node> q=new LinkedList<>();q.add(root);while(!q.isEmpty()){Node node=q.remove();System.out.println(node.e);if(node.left!=null){q.add(node.left);}if(node.right!=null){q.add(node.right);}}}
删除元素
删除最小值和最大值
- 寻找 BST 中的最小元素和最大元素
//寻找BST中的最小元素public E min(){if(size==0){throw new IllegalArgumentException("BST is emmpty");}return min(root).e;}private Node min(Node node){if(node.left==null){return node;}return min(node.left);}//寻找BST中的最大元素public E max(){if(size==0){throw new IllegalArgumentException("BST is emmpty");}return max(root).e;}private Node max(Node node){if(node.right==null){return node;}return max(node.right);}
- 删除BST中的最大值和最小值
情况一:最小值是叶子节点。直接删除该节点
情况二:最小值有右子树。删除该节点,再将该节点的右子树连接到原来的 BST 中

- 代码:
//删除BST中的最小值public E delMin(){E res=min();delMin(root);return res;}//删除以node为根结点的BST中的最小值元素//这里考虑最小值只有右子树的情况,因为叶子节点是其特例private Node delMin(Node node){if(node.left==null){Node nodeRight=node.right;node.right=null;size--;return nodeRight;}node.left=delMin(node.left);return node;}//删除BST中的最大值public E delMax(){E res=max();delMax(root);return res;}//删除以node为根结点的BST中的最大值元素private Node delMax(Node node){if(node.right==null){Node nodeLeft=node.left;node.left=null;size--;return nodeLeft;}node.right=delMax(node.right);return node;}
删除任意元素
- 删除只有左孩子的节点。直接删除即可

- 删除只有右孩子的节点。直接删除即可

- 删除左右孩子都有的节点

Habbard-Deletion:
d 是待删除的节点,s 节点是 d 的后继,也就是 d 的右子树的最小值所在的节点。
注意:这里选择左子树的最大值所在的节点效果也是相同的。

使用 s 代替 d

最后删除 d 节点
- 代码
//删除BST中任意元素public void del(E e){root=del(root,e);}private Node del(Node node,E e){if(node==null){return null;}if(e.compareTo(node.e)<0){node.left=del(node.left,e);return node;}else if(e.compareTo(node.e)>0){node.right=del(node.right,e);return node;}else{//节点node就是要删除的节点//该节点只右有子树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 s=min(node.right);s.right=delMin(node.right);s.left=node.left;//删除nodenode.left=node.right=null;return s;}}
