首页 > 代码库 > 一步一图一代码:排序二叉树
一步一图一代码:排序二叉树
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong/)
属性:
①若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值。
②若它的右子树不为空,则右子树上所有节点的值均大于它的根节点的值。
③它的左、右子树也都是排序二叉树。
添加操作:
当根节点为空时,添加进的节点作为根节点。然后每次添加节点时,都从根节点过滤,以根节点作为当前节点,如果新节点大于当前节点,则走当前节点的右子节点分支,如果新节点小于当前节点,则走当前节点的左子节点分支。然后再用右子节点或左子节点作为当前节点与新加节点比较,如果新加节点大,则当前节点的右子节点,如果新加节点小,则当前节点的左子节点。依次类推直到左子树或右子树为null时,将该新节点添加在这里。
图解:
代码解析:
public void add(T data){ Node newNode=new Node(data); if(root==null){ root=newNode; }else { add(newNode, root); }}public void add(Node addNode,Node parentNode) { T data=addNode.data; Node currentNode=parentNode; Node parent=null; int flag=0; while(currentNode!=null){ parent=currentNode; flag=data.compareTo(currentNode.data); if(flag>0){ currentNode=currentNode.rightChild; }else { currentNode=currentNode.leftChild; } } if(flag>0){ parent.rightChild=addNode; }else { parent.leftChild=addNode; } addNode.parent=parent; }
添加操作相对简单,麻烦的是删除操作。
删除操作
该操作有很多的情况:
第一:待删除的节点是叶子节点
第二:待删除的节点有左子树没有右子树
第三:待删除的节点没有左子树有右子树
第四:待删除的节点既没有左子树也没有右子树。
在第一大类中我们还可分:
1、该叶子节点就是根节点,即树中只有一个根节点。
这是最简单的情况,直接树的根root指向null即可。
2、否则便要判断该叶子节点是其父节点的左子节点呢还是其父节点的右子节点
下图为叶子节点是其父节点的左子节点,右子节点一样。
在第二大类中我们还可以细分:
1、待删除的节点就是根节点。
只需将根节点的左子节点(因为这是根节点只有左子节点)指向父节点的指针打断,并将该左子节点设为根节点即可。
2、如果待删除的节点不是根节点,那就必须讨论待删除的节点是其父节点的左子节点还是右子节点。
以是其父节点的左子节点为例(右子节点的情况一样),我们需要让待删除节点的父节点的指向其左孩子的指针重新指向待删除节点的左孩子,让待删除节点的左孩子的指向父节点的指针重新指向待删除节点的父节点。
这期间,我们并没有打断图中节点5指向其左孩子节点3的指针,也没有打断节点5指向其父节点6的指针。这是因为,没这个必要。我们不打断这些节点5永远也不会被访问到,而且会被垃圾回收。
第三大类和第二大类一样。
第四大类中
总的思想就是:找到待删除节点左子树中的最大节点(leftMaxNode),然后用leftMaxNode替代待删除的节点即可。这句话看似简单,但是实现起来可不是那么的容易!!!
我们也可以细分:
1、待删除节点就是根节点
1.1 leftMaxNode就是待删除节点的左孩子。即待删除节点左子树中没有右节点。
1.2 如果待删除的节点不是根节点
对于这种情况,我们需要显示显示打断的“指针”只有两个:如下图中,节点3指向其右子节点4(leftMaxNode)的指针,即节点4(leftMaxNode)指向其父节点3的指针。然后用节点4(leftMaxNode)替换节点5(根节点)即可。
2、待删除的节点不是根节点
2.1 被删除节点的左子树中最大节点不是其左子节点
如下图所示:
在这种情况下使用节点节点4替换节点5的时候,对于节点4和节点8、节点9的之间的“指针”创建比较容易,但是节点3与节点4之间的“指针”却不好创建,因为节点4的左子树可能很复杂,我们不知道将节点3插入哪里合适,所以,解决办法为,将节点3利用排序二叉树添加新节点的功能以新节点的形式加入节点4 中。
2.2 被删除节点的左子树中最大节点就是是其左子节点
即,如下情况,
这种情况很简单了,就不详细说明了。
完整代码如下:
package com.yyl.tree;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.List;import java.util.Queue;public class SortedBinaryTree<T extends Comparable<T>> { public class Node{ T data=null; Node parent=null; public Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; } Node leftChild=null; Node rightChild=null; //region【访问器】 public T getData() { return data; } public void setData(T data) { this.data =http://www.mamicode.com/ data; } public Node getLeftChild() { return leftChild; } public void setLeftChild(Node leftChild) { this.leftChild = leftChild; } public Node getRightChild() { return rightChild; } public void setRightChild(Node rightChild) { this.rightChild = rightChild; } //endregion // region 【构造器】 public Node(){} public Node(T data){ this.data=http://www.mamicode.com/data; } public Node(T data, Node leftChild, Node rightChild){ this.data=http://www.mamicode.com/data; this.leftChild=leftChild; this.rightChild=rightChild; } //endregion @Override public boolean equals(Object obj) { if(this.data=http://www.mamicode.com/=obj){ return true; }else if(this.getClass()==obj.getClass()){ Node nodeObj=(Node)obj; return this.data.compareTo(nodeObj.data) ==0 && this.leftChild.equals(nodeObj.leftChild) && this.rightChild.equals(nodeObj); } return false; } public String toString(){ return "[data:"+this.data+"]"; } } Node root=null; public SortedBinaryTree() {} public SortedBinaryTree(Node root){ this.root=root; } //添加节点 public void add(T data){ Node newNode=new Node(data); if(root==null){ root=newNode; }else { add(newNode, root); } } public void add(Node addNode,Node parentNode) { T data=addNode.data; Node currentNode=parentNode; Node parent=null; int flag=0; while(currentNode!=null){ parent=currentNode; flag=data.compareTo(currentNode.data); if(flag>0){ currentNode=currentNode.rightChild; }else { currentNode=currentNode.leftChild; } } if(flag>0){ parent.rightChild=addNode; }else { parent.leftChild=addNode; } addNode.parent=parent; } // 删除节点 public void remove(T data){ if(root==null){ return; }else { Node removeNode=null;//待删除的节点 removeNode = getNode(data); //endregion //如果没有找见抛出异常 if(removeNode==null){ throw new RuntimeException("未找见要删除的节点"); } System.out.println("待删除的节点为:"+removeNode.toString()); //如果待删除的节点既没有左孩子也没有右孩子 if(removeNode.leftChild==null && removeNode.rightChild==null){ if(removeNode==root){//如果待删除的节点就是根节点,即树中只有一个根节点 root=null; }else {//如果待删除的节点不是根节点,这就要考虑这个节点是其父节点的左孩子还是右孩子 if(isLeftNodeOfParent(removeNode)){//如果待删除是其父节点的左孩子,则断开其父节点指向左孩子的“指针” removeNode.parent.leftChild=null; }else {//如果待删除是其父节点的有孩子,则断开其父节点指向右孩子的“指针” removeNode.parent.rightChild=null; } //【注】断开他的指向父节点的“指针” //removeNode.parent=null; } }else if (removeNode.leftChild!=null && removeNode.rightChild==null) {//如果待删除的节点有左孩子但是没有右孩子 if(removeNode==root){//如果待删除的节点就是根节点 //这条语句并不是必须的,因为对于数而言,不管怎么都是以root为起点开始所有操作,所以遍历等操作不会涉及 //到root.leftChild.parent.不将它设为null,同样不影响树的正常运行。但是root.leftChild.parent所引 //用的对象并不会被当成垃圾而被回收,因为从root.leftChild.parent依然可以访问到该Java对象 root.leftChild.parent=null; root=root.leftChild; }else {//如果待删除的节点不是根节点,那就必须讨论待删除的节点是其父节点的左子节点还是右子节点 if(isLeftNodeOfParent(removeNode)){ removeNode.parent.leftChild=removeNode.leftChild; }else { removeNode.parent.rightChild=removeNode.leftChild; } //这条语句不要更精炼,加上会让程序更清晰 //removeNode.parent=null; removeNode.leftChild.parent=removeNode.parent; } //这条语句不要更精炼,加上会让程序更清晰 //removeNode.leftChild=null; } else if (removeNode.leftChild==null && removeNode.rightChild!=null) {//如果待删除的节点没有左孩子但是有右孩子 if(root==removeNode){//如果待删除的节点就是根节点 root=root.rightChild; //释放内存,让垃圾回收removeNode removeNode.rightChild.parent=null; }else {//如果待删除的节点不是根节点 if(isLeftNodeOfParent(removeNode)){ removeNode.parent.leftChild=removeNode.rightChild; }else { removeNode.parent.rightChild=removeNode.rightChild; } removeNode.rightChild.parent=removeNode.parent; //这条语句不要更精炼,加上会让程序更清晰 //removeNode.parent=null; } //这条语句不要更精炼,加上会让程序更清晰 //removeNode.rightChild=null; }else {//如果待删除的节点既有左孩子也有右孩子 Node leftMaxNode=null; //region【寻找被删除节点左子树中最大节点】 Node tmp=removeNode.leftChild; while(tmp.rightChild!=null){ tmp=tmp.rightChild; } leftMaxNode=tmp; //endregion System.out.println("leftmaxnode:"+leftMaxNode.toString()); if(removeNode==root){//如果待删除的节点就是根节点 if(leftMaxNode==removeNode.leftChild){//如果被删除节点的左子树中最大节点就是其左子节点 leftMaxNode.rightChild=root.rightChild; root.rightChild.parent=leftMaxNode; root=leftMaxNode; leftMaxNode.parent=null; //这条语句不要更精炼,加上会让程序更清晰 //root.leftChild=null; }else { leftMaxNode.rightChild=root.rightChild; leftMaxNode.leftChild=root.leftChild; root.rightChild.parent=leftMaxNode; root.leftChild.parent=leftMaxNode; leftMaxNode.parent.rightChild=null; leftMaxNode.parent=null; root=leftMaxNode; } }else {//如果待删除的节点不是根节点 if(leftMaxNode!=removeNode.leftChild){//如果被删除节点的左子树中最大节点不是其左子节点 Node addNode=removeNode.leftChild; removeNode.leftChild=null; addNode.parent=null; add(addNode,leftMaxNode); leftMaxNode.parent.rightChild=null; } leftMaxNode.rightChild=removeNode.rightChild; removeNode.rightChild.parent=leftMaxNode; leftMaxNode.parent=removeNode.parent; if(isLeftNodeOfParent(removeNode)){ removeNode.parent.leftChild=leftMaxNode; }else { removeNode.parent.rightChild=leftMaxNode; } } } } } public Node getNode(T data) { Node removeNode=null; Node current=root;//临时变量,用于辅助查找待删除的节点 //region 【寻找要待删除的节点】 while(current!=null){ if(data.compareTo(current.data)>0){ current=current.rightChild; }else if (data.compareTo(current.data)<0) { current=current.leftChild; }else { removeNode=current; break; } } return removeNode; } private boolean isLeftNodeOfParent(Node node) { return node.data.compareTo(node.parent.data)<=0; } //中序遍历 public List<Node> midIterator(){ return midIterator(root); } private List<Node> midIterator(Node node) { List<Node> list=new ArrayList<Node>(); if(node==null){ return null; } if(node.leftChild!=null){ list.addAll(midIterator(node.leftChild)); } list.add(node); if(node.rightChild!=null){ list.addAll(midIterator(node.rightChild)); } return list; } //先序遍历 public List<Node> preIterator(){ return preIterator(root); } private List<Node> preIterator(Node root) { List<Node> list=new ArrayList<Node>(); if(root==null) return null; list.add(root); if(root.leftChild!=null){ list.addAll(preIterator(root.leftChild)); } if(root.rightChild!=null){ list.addAll(preIterator(root.rightChild)); } return list; } //广度优先遍历 public List<Node> breadthFirst(){ return breadthFirst(root); } private List<Node> breadthFirst(Node root) { Queue<Node> queue=new ArrayDeque<Node>(); List<Node> list=new ArrayList<Node>(); if(root==null) return null; queue.offer(root); while(!queue.isEmpty()){ Node node=queue.poll(); list.add(node); if(node.leftChild!=null){ queue.offer(node.leftChild); } if(node.rightChild!=null){ queue.offer(node.rightChild); } } return list; }}
可能有些地方理解的还是不到位,并且代码的性能不是很高,请大家批评指正。
一步一图一代码:排序二叉树