一、为什么要使用二叉排序树
对于线性存储的数组来说,通过二分查找等算法其查找效率为log2n是不错的。但是其插入删除数据的效率是比较低的,为了维护数组的有序性,插入删除时必须将一些数据"挪"位置。此算法复杂度为O(n)。如果不需要维护数组的有序性的话,添加数据直接在数组尾添加,删除数据时,也只需要用数组尾数据覆盖删除的数据,并使长度-1即可,但是无序的序列对于查找来说,是十分不好的。所以引入了二叉排序树,这种数据结构插入删除效率都还可以,并且查找效率也不错。
二叉排序树中序遍历的结果为关键字从小到大排序的结果。
package suanfa;
import java.util.Stack;
class Node{
int data;
Node leftChild;
Node rightChild;
public Node(int data, Node leftChild, Node rightChild) {
super();
this.data = data;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
public class Main {
private static Node tree;
public static void main(String[] args) {
int[] arr ={62,88,58,47,35,73,51,99,37,93};
tree = new Node(arr[0], null, null);
for(int i=1;i<arr.length;i++){
insert(tree,arr[i]);
}
inTranverse();
}
public static Node find(int key){
return find(tree,key);
}
//递归构建二叉排序树
public static Node insert(Node node,int key){
//递归结束条件, 找到了插入的位置,进行插入操作
if(node == null){
node = new Node(key, null, null);
return node;
}
//key小于node的关键字的话,应放在node的左子树里,左子树情况未知,按给定递归逻辑处理即可
if(node.data > key){
//递归处理左子树
node.leftChild = insert(node.leftChild, key);
}else if(node.data < key){
//递归处理右子树
node.rightChild = insert(node.rightChild, key);
}
return node;
}
//查找操作
private static Node find(Node node,int key){
if(node==null) return null;
if(node.data > key){
return find(node.leftChild,key);
}else if(node.data < key){
return find(node.rightChild,key);
}else {
return node;
}
}
//非递归中序遍历(深度优先思想)
public static void inTranverse(){
Node tempTree = tree;
if(tempTree == null) return;
Stack<Node> stack = new Stack<Node>();
//tempTree!=null,表示右子树可以开始左子树遍历
//右子树为空的话,!stack.isEmpty()条件继续对路径上的点回溯
while(!stack.isEmpty()||tempTree!=null){
//最左子树,并经过的路径
while(tempTree!=null){
stack.push(tempTree);
tempTree = tempTree.leftChild;
}
if(!stack.isEmpty()){
Node node = stack.pop();
System.out.println(node.data);
//进入右子树,开始新的一轮左子树遍历(这是递归的自我实现)
tempTree = node.rightChild;
}
}
}
//后序遍历 非递归
public static void afterTranverse(){
if(tree == null) return;
Node currentTree = tree;
Node preNode = null;
//保存经过的路径
Stack<Node> stack = new Stack<Node>();
//来到最左子树,并保存经过的路径
while(currentTree!=null){
stack.push(currentTree);
currentTree = currentTree.leftChild;
}
//弹出走过的路径
while(!stack.isEmpty()){
currentTree = stack.pop();
//若当前根节点的右子树为空或者刚遍历完其右子树,则输出当前根节点
if(currentTree.rightChild==null||currentTree.rightChild == preNode){
System.out.println(currentTree.data);
preNode = currentTree;
}else{//当前节点有右子树需要遍历
//把当前根节点先还回栈去,并递归遍历右子树
stack.push(currentTree);
Node node = currentTree.rightChild;
while(node!=null){
stack.push(node);
node = node.leftChild;
}
}
}
}
//前序遍历非递归(深度优先思想)
public static void preTranverse(){
if(tree == null) return;
Node tempTree = tree;
//保存经过的路径
Stack<Node> stack = new Stack<Node>();
//tempTree is or not is null 判断右子树是否可以继续递归遍历
//stack.isEmpty()继续回溯路径
while(!stack.isEmpty()||tempTree!=null){
while(tempTree!=null){
System.out.println(tempTree.data);
stack.push(tempTree);
tempTree = tempTree.leftChild;
}
if(!stack.isEmpty()){
Node node = stack.pop();
tempTree = node.rightChild;
}
}
}
//递归中序遍历
public static void inTranverse(Node tree){
if(tree == null) return;
inTranverse(tree.leftChild);
System.out.println(tree.data);
inTranverse(tree.rightChild);
}
public static void buildTree(int key){
//若根节点为空,则建立根节点
if(tree == null){
tree = new Node(key,null,null);
return;
}
//根节点不为空
Node temp = tree;
while(true){
//要插入的关键字小于根节点,则插到左子树去
if(temp.data > key){
//若左子树为空的话,直接插入
if(temp.leftChild == null){
Node node = new Node(key,null,null);
temp.leftChild = node;
return;
}
//若左子树不为空,则进入左子树,继续判断
temp = temp.leftChild;
}else if(temp.data < key){
//若右子树为空的话,直接插入
if(temp.rightChild == null){
Node node = new Node(key,null,null);
temp.rightChild = node;
return;
}
//若左子树不为空,则进入左子树,继续判断
temp = temp.rightChild;
}else{
System.out.println("请不要插入相等的值");
return;
}
}
}
}
//删除
public static void deleteTranverse(Node node,int key){
if(tree == null){
return;
}
if(node.data == key){
delete(node);
}else if(node.data > key){
deleteTranverse(node.leftChild,key);
}else{
deleteTranverse(node.rightChild,key);
}
}
public static void delete(Node node){
//要删除的节点的左子树为空,则把指向欲删除的节点的指针指向其右子树的根节点
if(node.leftChild == null){
if(node.isRightChild){
node.parent.rightChild = node.rightChild;
}else{
node.parent.leftChild = node.rightChild;
}
//要删除的节点的右子树为空,则把指向欲删除的节点的指针指向其左子树的根节点
}else if(node.rightChild == null){
if(node.isRightChild){
node.parent.rightChild = node.leftChild;
}else{
node.parent.leftChild = node.leftChild;
}
//要删除的节点的左右子树
}else{
Node preNode = node;
Node tempNode = node.leftChild;
//找到要删除节点的左子树的最右子树tempNode,preNode是tempNode的上一个节点
while(tempNode.rightChild!=null){
preNode= tempNode;
tempNode = tempNode.rightChild;
}
node.data = tempNode.data;
//要删除节点的左子树没有右子树
if(node.leftChild == tempNode){
tempNode.parent.leftChild = tempNode.leftChild;
}else{//要删除节点的左子树有右子树
preNode.rightChild = tempNode.leftChild;
}
}
}