实现二分搜索树
Posted skychmz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现二分搜索树相关的知识,希望对你有一定的参考价值。
二分搜索树具有以下特点:
1 二分搜索树是一颗二叉树
2 二分搜索树每个节点的左子树的值都小于该节点的值,每个节点右子树的值都大于该节点的值
3 任意一个节点的每棵子树都满足二分搜索树的定义
基于第二点我们的需要插入树的元素实现Comparable接口,下面实现二分搜索树的原型,这个类具有二分搜索树的基本成员变量和方法,之后再实现对元素的操作方法。
public class BST<E extends Comparable<E>> { private class Node{ public E e; public Node left, 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 int size(){ return size; } public boolean isEmpty(){ return size == 0; } }
二分搜索树的添加
向二分搜索树添加元素的方法使用递归很好实现,终止递归的条件是当前节点为null
// 向二分搜索树中添加新的元素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) node.right = add(node.right, e); return node; }
二分搜索树的查询
查询树中是否包含某元素的方法和插入的思路是一样的,用递归实现,当前节点为null时代表递归到底仍然没有找到,返回false,否则一旦找到将结束递归返回true
// 看二分搜索树中是否包含元素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.left, e); else // e.compareTo(node.e) > 0 return contains(node.right, 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); }
中序和后序只需要改变遍历语句的位置即可实现
// 二分搜索树的中序遍历 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); } // 二分搜索树的后序遍历 public void postOrder(){ postOrder(root); } // 后序遍历以node为根的二分搜索树, 递归算法 private void postOrder(Node node){ if(node == null) return; postOrder(node.left); postOrder(node.right); System.out.println(node.e); }
二分搜索树的层序遍历
层序遍历,顾名思义就是一层一层遍历,从根节点开始,然后第二层第三层以此类推,这里不需要递归,使用队列即可实现
把每层元素入队,然后出队的时候再把出队节点的下一层子节点入队
// 二分搜索树的层序遍历 public void levelOrder(){ Queue<Node> q = new LinkedList<>(); q.add(root); while(!q.isEmpty()){ Node cur = q.remove(); System.out.println(cur.e); if(cur.left != null) q.add(cur.left); if(cur.right != null) q.add(cur.right); } }
查找最大与最小节点
首先需要知道一点,根据左子树一定比根节点小、右子树一定比根节点大这个原则可以知道最大节点的右子树必定为null,而最小节点的左子树也必定为null,知道这两点后查到最大最小节点的方法就非常容易实现,也是使用递归
// 寻找二分搜索树的最小元素 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); }
删除最大与最小节点
上面提到了最大节点的右子树必定为null,而最小节点的左子树也必定为null,所以删除最大节点的时候直接用其左子树代替被删节点,删除最小节点的时候直接用其右子树代替即可,用递归实现
// 从二分搜索树中删除最小值所在节点, 返回最小值 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; }
删除任意节点
删除任意节点的情况比较复杂, 当被删节点存在左右子树的时候是用什么元素替代被删节点的位置呢?这里我们使用被删节点的后继节点,即比被删节点右子树的最小节点,我们先把用上面的minimum()方法右子树后继节点找出来,然后用removeMin()方法删除后继节点并把删除后的右子树赋给后继节点作为后继节点的右子树,然后把被删节点的左子树原封不动的赋给后继节点作为后继节点的左子树,最后返回该树即可,被删节点的左右子树记得置空,让jvm回收。
// 从二分搜索树中删除元素为e的节点 public void remove(E e){ root = remove(root, e); } // 删除掉以node为根的二分搜索树中值为e的节点, 递归算法 // 返回删除节点后新的二分搜索树的根 private 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.left = node.right = null; return successor; } }
以上是关于实现二分搜索树的主要内容,如果未能解决你的问题,请参考以下文章