package com.yc.tree;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
*
* @author wb
*
* 排序二叉树是一种特殊的二叉树,通过它可以非常方便地对树中的所有的节点进行排序和检索。
*
* 排序二叉树要么是一棵空树,要么就是满足以下几个性质:
* (1)若它的左子树不为空,则左子树上所有的节点的值均小于它的根节点的值。
* (2)若它的右子树不为空,则右子树上所有的节点的值均大于它的根节点的值。
* (3)它的左、右子树也为排序二叉树。
*
* 如下图显示了一棵排序二叉树:
* 10
* ││
* 3 ────┘└─────18
* ││ ││
* 2 ─────┘└─── 4 13 ──┘└────21
* │
* └───9
* ││
* 8───┘└─── 9
* 图11.21 排序二叉树
*
* 对于排序二叉树而言,按照中序遍历即可得到有小到大的有序序列,比如上图,中序遍历的:
* [2, 3, 4, 8, 9, 9, 10, 13, 18, 21]
*
* 创建排序二叉树的步骤,就是不断地向排序二叉树添加新节点的过程,具体如下:
* ①以根节点为当前节点开始搜索。
* ②拿新节点的值和当前节点的值比较。
* ③如果新节点的值更大,则以当前节点的右子节点作为新的当前节点;如果新节点的值更小,则以当前节点的左子节点作为新的当前节点。
* ④重复第二和第三两个步骤,知道搜索到合适的叶子节点。
* ⑤将新节点添加为第四步找到的叶子节点的子节点,如果新节点更大,则添加为右子节点;否则,添加为左子节点。
*
* 当程序从排序二叉树中删除一个节点之后,为了让他依然保持为排序二叉树,必须对该排序二叉树进行维护。维护可以分为如下几种情况:
* (1)被删除的节点是叶子节点,只需将它从父节点中删除。
* (2)被删除的节点p只有左子树或者只有右子树。如果p是它的父节点的左子节点,则将p的左子树或者右子树添加成p节点的父节点的左子节点即可;
* 如果p是它的父节点的右子节点,则将p的左子树或者右子树添加成p节点的父节点的右子节点即可。简单来说,如果要删除的节点只有一个子节点,
* 即可用它的子节点来代替要删除的节点即可。
* 如下图要删除的节点只有左子树的情况:
* 18 18
* ││ ↓p ││
* 12 ──────┘└────── 30 12 ──────┘└────── 23
* │ │ ---> │ │
* └───14 23──┘ └───14 └──26
* │ │ │
* 13──┘ └──26 13──┘
* 图11.22 被删除节点只有左子树
*
* 18 18
* ↓p ││ ││
* 12 ──────┘└────── 30 14 ──────┘└────── 30
* │ │ ---> │ │
* └───14 23──┘ 13──┘ 23──┘
* │ │ │
* 13──┘ └──26 └──26
* 图11.23 被删除节点只有右子树
*
* (3)若被删除节点p的左、右子树均非空,则有以下两种做法:
* ①将pL设为p的父节点q的左或右子节点(取决于p是其父节点q左、右子节点),将pR设为p节点的中序前趋节点s的右子节点
* (s节点是pL最右下的节点,也就是pL子树中最大的节点)。采用这种方法删除节点的示意图如下:
*
* q→ 5 q→ 5
* ││ ↓p ││
* 3 ──────┘└─────20 3────┘└─────10
* ││ -----> ││
* 10 ───┘└─────30 8───┘└─────15
* ││ 将p左子节点设为q的子节点 │
* 8 ────┘└─────15 将p右子节点设为pL子树中最大节点的右子节点 └────30
* 图11.24 被删除的节点既有左子树,又有右子树
* ②以p节点的中序前趋或后继代替p所指节点,然后从排序二叉树中删除前趋或后继节点。简单来讲,就是用大于
* p节点的最小节点或小于p节点的最大节点代替p节点。采用这种方式删除节点的示意图如下:
*
* q→ 5
* ││ ↓p
* ------> 3────┘└─────15
* ││
* q→ 5 10 ───┘└─────30
* ││ ↓p │
* 3 ──────┘└─────20 8────┘
* ││ 用被删除节点的前驱来代替被删除节点
* 10 ───┘└─────30
* ││ q→ 5
* 8 ────┘└─────15 ││ ↓p
* 3 ──────┘└─────30
* │
* 10 ───┘
* ------> ││
* 8 ────┘└─────15
* 用被删除节点的后继来代替被删除节点
* 图11.25被删除的节点既有左子树,又有右子树
*
* 掌握了上面的理论,使用如下Java程序来实现排序二叉树。实现排序二叉树的删除时采用图11.25中的用被删除节点左子树中
* 的最大节点与被删除节点交换的方式维护。
*
*/
public class SortedBinTree <T extends Comparable>{
public class Node{
Object data;
Node parent;
Node left;
Node right;
public Node(Object data, Node parent, Node left, Node right){
this.data = data;
this.parent = parent;
this.left = left;
this.right = right;
}
public String toString(){
return "[data:" + data + "]";
}
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj){
return true;
}
if(getClass() == obj.getClass()){
Node target = (Node)obj;
return data.equals(target.data) &&
parent == target.parent &&
left == target.left &&
right == target.right;
}
return false;
}
}
private Node root;
public SortedBinTree(){
root = null;
}
public SortedBinTree(T data){
root = new Node(data, null, null, null);
}
/**
* 向这棵排序二叉树添加节点
* @param data
*/
@SuppressWarnings("unchecked")
public void add(T data){
//如果根节点为null,那么新添加的节点就作为根节点
if(root == null){
root = new Node(data, null, null, null);
}
else{
//从根节点开始搜索
Node current = root;
Node parent = null;
int cmp = 0;
do{
parent = current;
cmp = data.compareTo(current.data);
if(cmp > 0){//若干新添加的节点只大于当前节点的值
current = current.right;
}else{
current = current.left;
}
}while(current != null);
Node newNode = new Node(data, parent, null, null);
if(cmp > 0){
parent.right = newNode;
}else{
parent.left = newNode;
}
}
}
/**
* 删除指定元素的节点
* @param data
*/
public void remove(T data){
Node target = getNode(data);
if(target == null){//如果为找到
return;
}
if(target.left == null && target.right == null){//如果要删除节点的左、右子树为空(叶子节点)
if(target == root){
root = null;
}else{
if(target == target.parent.left){
target.parent.left = null;
}else{
target.parent.right = null;
}
//向上引用也赋空
target.parent = null;
}
}else if(target.left != null && target.right == null){ //如果要删除的节点只有左子树
//如果要删除的节点是根节点
if(root == target){
root = target.left;
target.left.parent = null; //不能让它指回去
}
else{
//如果要删除节点是其父节点的左子节点
if(target == target.parent.left){
target.parent.left = target.left;
}else{
target.parent.right = target.left;
}
target.left.parent = target.parent; //指回去
}
}else if(target.right != null && target.left == null){ //如果要删除的节点只有右子树
if(root == target){
root = target.right;
}
else{
if(target == target.parent.left){
target.parent.left = target.right;
}else{
target.parent.right = target.right;
}
target.right.parent = target.parent;
}
}else if(target.left != null && target.right != null){ //如果要删除元素的节点既有右子树,也有左子树
if(root == target){
Node leftMaxNode = target.left;
while(leftMaxNode.right != null){
leftMaxNode = leftMaxNode.right;
}
root = leftMaxNode;
if(target.left.data.equals(leftMaxNode.data)){
leftMaxNode.right = target.right;
leftMaxNode.parent = null;
target.right = target.parent = target.left = null;
}else{
leftMaxNode.parent.right = null;
leftMaxNode.parent = null;
leftMaxNode.left = target.left;
target.right = target.parent = target.left = null;
target.left = target.right = null;
}
}else{
Node leftMaxNode = target.left;
while(leftMaxNode.right != null){
leftMaxNode = leftMaxNode.right;
}
if(target.left.data.equals(leftMaxNode.data)){
leftMaxNode.right = target.right;
leftMaxNode.parent = target.parent;
if(target == target.parent.left){
target.parent.left = leftMaxNode;
}else{
target.parent.right = leftMaxNode;
}
target.right = target.parent = target.left = null;
}else{
leftMaxNode.parent.right = null;
if(target == target.parent.left){
target.parent.left = leftMaxNode;
}else{
target.parent.right = leftMaxNode;
}
leftMaxNode.parent = target.parent;
leftMaxNode.left = target.left;
leftMaxNode.right = target.right;
target.right = target.parent = target.left = null;
}
}
}
}
//在排序二叉树中查找指定元素的节点
@SuppressWarnings({ "unchecked" })
private Node getNode(T data){
Node node = root;
while(node != null){
int tmp = data.compareTo(node.data);
if(tmp > 0){ //指定元素节点的值大于当前节点的值
node = node.right;
}else if( tmp < 0){
node = node.left;
}else{
return node;
}
}
return null;
}
/**
* 广度优先遍历排序二叉树
* @return
*/
public List<Node> breadthFirstSearch(){
List<Node> nodes = new ArrayList<Node>();
Deque<Node> deque = new ArrayDeque<>();
deque.offer(root);
while( !deque.isEmpty()){
Node tmp = deque.poll();
nodes.add(tmp);
if(tmp.left != null){
deque.offer(tmp.left);
}
if(tmp.right != null){
deque.offer(tmp.right);
}
}
return nodes;
}
}
测试代码如下:
package com.yc.test;
import com.yc.tree.SortedBinTree;
public class SortedBinTreeTest {
public static void main(String[] args) {
SortedBinTree<Integer> tree = new SortedBinTree<Integer>();
//添加节点
tree.add(5);
tree.add(20);
tree.add(10);
tree.add(3);
tree.add(8);
tree.add(15);
tree.add(30);
System.out.println( tree.breadthFirstSearch());
/*tree.remove(5);
System.out.println( tree.breadthFirstSearch());*/
tree.add(5);
tree.add(9);
System.out.println(tree.breadthFirstSearch());
tree.remove(10);
System.out.println( tree.breadthFirstSearch());
}
}
测试结果如下:
[[data:5], [data:3], [data:20], [data:10], [data:30], [data:8], [data:15]]
[[data:5], [data:3], [data:20], [data:5], [data:10], [data:30], [data:8], [data:15], [data:9]]
[[data:5], [data:3], [data:20], [data:5], [data:9], [data:30], [data:8], [data:15]]