二叉查找树的结构特点:
1.新插入的节点的一定是作为某个叶子节点的子节点。
2.某个节点左侧的所有节点都小于它,右侧的所有节点都大于它。
3.找后继(前驱)节点,一直往上找,找到右拐(左拐)后的那个节点
package study; import java.util.Comparator; /** * Created by 刘逗逼 on 2016/2/25. */ public class BSTree <T extends Comparable<T>>{ //树,只有根节点,然后根据根节点去操作 private BNode<T> root; public class BNode<T extends Comparable<T>>{ //节点,有左右孩子 private T key; private BNode<T> left; private BNode<T> right; private BNode<T> parent; public BNode(T key,BNode left,BNode right,BNode parent){ this.key=key; this.left=left; this.right=right; this.parent=parent; } public T getKey(){ return key; } public String toString(){ return "key is "+key; } } public BSTree(){ root=null; } public void preOrder(BNode<T> tree){ //先序遍历 if(tree!=null){ System.out.println(tree.toString()); preOrder(tree.left); preOrder(tree.right); } } public void preOrder(){ //默认为对整颗树进行排序 preOrder(root); } public void inOrder(BNode<T> tree){ if (tree!=null){ inOrder(tree.left); System.out.println(tree.toString()); inOrder(tree.right); } } public void inOrder(){ inOrder(root); } public void postOrder(BNode<T> tree){ if (tree!=null){ postOrder(tree.left); postOrder(tree.right); System.out.println(tree.toString()); } } public void postOrder(){ postOrder(root); } //递归查找二叉树中键值为key的节点 public BNode<T> search(BNode<T> tree,T key){ if(tree==null)return tree; //此为树为空或找不到键值为key的节点的情况 int cmp=key.compareTo(tree.getKey()); if(cmp<0){ return search(tree.left,key); }else if(cmp>0){ return search(tree.right,key); }else return tree; } public BNode<T> search(T key){ return search(root,key); } //(非递归实现)查找二叉树中键值为key的节点 非递归就是循环 public BNode<T> iterativeSearch(BNode<T> tree, T key){ while (tree!=null){ int cmp=key.compareTo(tree.getKey()); if(cmp<0){ tree=tree.left; }else if(cmp>0){ tree=tree.right; }else return tree; } return tree; } public BNode<T> iterativeSearch(T key){ return iterativeSearch(root,key); } //查找最小结点:返回tree为根结点的二叉树的最小结点。 public BNode<T> minnum(BNode<T> tree){ if(tree==null)return tree; while (tree.left!=null){ tree=tree.left; } return tree; } public BNode<T> minnum(){ return minnum(root); } //查找最大结点:返回tree为根结点的二叉树的最大结点。 public BNode<T> maxnum(BNode<T> tree){ if(tree==null)return tree; while (tree.right!=null){ tree=tree.right; } return tree; } public BNode<T> maxnum(){ return maxnum(root); } //找结点tree的前驱结点。即查找二叉树中数据值小于该结点的最大结点。 public BNode<T> predecessor(BNode<T> tree) { // 如果tree存在左孩子,则tree的前驱结点为 tree以其左孩子为根的子树的最大结点。 if (tree.left != null) return maxnum(tree.left); // 如果tree没有左孩子。则tree有以下两种可能: // 1. tree是右孩子,则tree的前驱结点为它的父结点。 // 2. tree是左孩子,则查找tree的最低的父结点,并且该父结点要具有右孩子,即tree在父节点的左侧才行,找到的这个 // 最低的父结点就是tree的前驱结点。 BNode<T> parent = tree.parent; while ((parent!=null) && (tree==parent.left)) { //当parent.left=tree退出循环时说明tree在此父节点的左侧,而且在此前循环所经过 tree = parent; // 的节点都小于tree,因为tree一直在这些节点的右边说明tree比他们大 parent = parent.parent; } return parent; } //找结点tree的后继结点。即,查找二叉树中数据值大于该结点的最小结点。 public BNode<T> successor(BNode<T> tree){ // 如果tree存在右孩子,则tree的后继结点为 以其右孩子为根的子树的最小结点。 if(tree.right!=null){ return minnum(tree.right); } // 如果tree没有右孩子。则tree有以下两种可能: // 1.tree是左孩子,根据二叉树的规律,其父结点即为后继节点。 // 2.tree是右孩子,则查找tree的最低的父结点,并且该父结点要具有左孩子,即tree在父节点的左侧才行, // 找到的这个最低的父结点就是tree的后继结点。 BNode<T> parent=tree.parent; while (parent!=null&&parent.right==tree){ //当parent.left=tree退出循环时说明tree在此父节点的左侧,而且在此前循环所经过 // 的节点都小于tree,因为tree一直在这些节点的右边说明tree比他们大 tree=parent; parent=parent.parent; } return parent; } public void insert(BSTree<T> tree,BNode<T> node){ int cmp; BNode<T> child=tree.root; BNode<T> parent=null; //找到插入位置,肯定是位于一个父节点和子节点之间 while (child!=null){ parent=child; //父节点是被抛弃的,关键是看下一步是往子节点的左边还是右边走 cmp=node.getKey().compareTo(child.getKey()); if(cmp<0){ child=child.left; }else{ child=child.right; } } //最后的插入位置一定是作为某个叶子节点的子节点 node.parent=parent; if(parent==null){ //树为空 tree.root=node; }else { cmp=node.getKey().compareTo(parent.getKey()); if(cmp<0){ //左节点 parent.left=node; }else { parent.right=node; } } } //新建结点(key),并将其插入到二叉树中 public void insert(T key){ BNode<T> node=new BNode<T>(key,null,null,null); if (node!=null) { insert(this,node); } } //删除结点node //若要删除任意节点,需要考虑如下三种情况: // (1)需要删除的节点下并没有其他子节点。 删了没影响 // (2)需要删除的节点下有一个子节点(左或右)。 让被删节点的父节点和子节点建立关系,类似于链表的删除节点 // (3)需要删除的节点下有两个子节点(既左右节点都存在)。 让后继或前继节点代替被删节点,然后删除后继节点 //递归实现 public boolean remove(T key){ BNode<T> goal= search(key); if (goal != null){ if (doRemove(goal)){ goal=null; return true; } } return false; } public boolean doRemove(BNode<T> goal){ BNode<T> replaceNode=null; //用来替代被删除节点的 if(goal!=null){ boolean isRoot=goal==root; //判断是否被删除的是根节点 if(goal.left==null&&goal.right==null){ //没有子节点,是叶子节点 if(isRoot){ root=null; return true; } if(goal==goal.parent.left){ //如果被删除的节点是左节点的话 goal.parent.left=null; return true; }else { goal.parent.right=null; return true; } }else if(goal.left!=null&&goal.right!=null){ //如果被删除的节点有两个子节点的话 replaceNode=successor(goal); if(replaceNode==null){ replaceNode=predecessor(goal); //如果有后继节点的话否则用前继节点 } goal.key=replaceNode.key; //替换并递归删除替代的节点 return doRemove(replaceNode); }else { //如果只有一个子节点 BNode<T> needLinkedNode=null; if(goal.left==null&&goal.right!=null){ //判断是左还是右节点是需要连接的节点 needLinkedNode=goal.right; }else { needLinkedNode=goal.left; } if(isRoot){ root=needLinkedNode; } if(goal==goal.parent.left){ //判断需要连接的节点连到左边还是右边 goal.parent.left=needLinkedNode; needLinkedNode.parent=goal.parent.left; }else { goal.parent.right=needLinkedNode; } return true; } }else return false; } //非递归实现 public boolean iterativeRemove(BSTree<T> tree,T key){ BNode<T> node=search(key); if(node==null)return false; //不存在该节点 BNode<T> child=null; BNode<T> parent=null; if(node.left==null||node.right==null){ //若只有一边 parent=node; }else { parent=successor(node); //找到其后继节点 } //若它只有一边的话就可以确定 若是有两边此时parent为叶子节点child也为空,不影响 if(parent.left!=null){ child=parent.left; }else { child=parent.right; } if(child!=null){ //对应被删除的节点或后继节点不是叶子节点的情况,否则没有child child.parent=parent.parent; //使要替换被删除节点的节点的父节点指向被删除节点的父节点 } if(parent.parent==null){ //对应树只有这个节点或被删除的是根节点 tree.root=child; }else if(parent==parent.parent.left){ //被删除的节点是左节点 parent.parent.left=child; }else parent.parent.right=child; if(parent!=node){ //对应找到后继节点的情况 node.key=parent.key; } if(search(key)==null){ //删除成功 return true; }else return false; } /* * 打印"二叉查找树" *key和direction都是为了知道上个节点和其与当前节点的关系 * key -- 节点的键值 * direction -- 0,表示该节点是根节点; * -1,表示该节点是它的父结点的左孩子; * 1,表示该节点是它的父结点的右孩子。 */ public void print(BNode<T> tree,T key,int direction){ if(tree!=null){ //先序打印 if(direction==0){ System.out.println(tree.key+" is root"); }else { String relationship=direction==1?"right child":"left child"; System.out.println(tree.key+" is "+key+"`s "+relationship); } print(tree.left,tree.key,-1); print(tree.right,tree.key,1); } } public void print(){ print(root,root.key,0); } /* * 销毁二叉树,必须从叶子节点开始清空,只能是后序,否则太麻烦 */ public void destroy(BNode<T> tree){ if(tree==null){ return; } destroy(tree.left); destroy(tree.right); tree=null; } public void clear() { destroy(root); root = null; } }
纯粹地读书,只为好奇心