二叉排序树结合了数组和链表的优点
查询和添加都很快相对来说数组的头插需要往后移动整个数组而链表查找数据则需要迭代
但是二叉排序树截然不同
二叉排序树相同的数据对应的二叉排序树不一样
就是一样的数据排序方式是不一样的,二叉排序树的中序遍历得到的值都是一个递增的序列
假如说中序添加二叉排序树:取得一个值作为根结点,那么将要插入的数据和根结点进行对比
如果数据要比它小那就把数据放到结点的左子树如果左子树有数据那么继续和左子树进行比较
再决定放到左子树的左边还是右边
创建一个二叉排序树:
先创建一个树的结点
public class Node {
/*
value是一个结点的值
left和right是左右子树
*/
private int value;
private Node left;
private Node right;
public Node(int value) {
this.value = value;
}
/*
添加结点
*/
public void add(Node node){
if(node == null) return;//判断结点是否为空 作为递归的结束条件
if(node.value<this.value){//判断传入的值会否比该结点的值小
/*
如果小于该结点的值那就把结点放到该结点的左子树
如果它的左子树没有值则直接放入左子树
*/
if(this.left==null){
this.left=node;
}else{//如果它的左子树有值则递归它的左子树看一下它的左子树
this.left.add(node);
}
}else{//如果传入的值大于等于该结点的值则放入右子树
if(this.right==null){
this.right=node;
}else{
this.right.add(node);
}
}
}
/*
中序遍历二叉树
*/
public void preSelect(){
if(this.left!=null){this.left.preSelect();}
System.out.println(this);
if(this.right!=null){this.right.preSelect();}
}
/*
传入一个int属性的val值查找该值在树中的结点并返回
*/
public Node SelectVal(int val){
if(this.value==val) return this;//先判断传入的值是否和该结点的值一样 如果一样则返回
if(val<this.value) {//如果值小于该结点的值那就查看他的左子树 因为他左子树的值一定比这个结点的值要小
if(this.left==null) {//如果左子树为空那这个值就不存在 返回空
return null;
}else{//如果有值那就把它丢给左子树的查询方法递归上面的步骤
return this.left.SelectVal(val);
}
}else{//如果这个值大于该结点的值那这个值要么就在右子树要么没有
if(this.right==null){
return null;
}else{
return this.right.SelectVal(val);
}
}
}
/*
查找一个传入的val值结点的父结点
返回的就是这个结点的父结点
*/
public Node ParentSelect(int val){
/*
1,判断左子树的值是否等于这个值 如果相等则返回此结点 因为此结点就是需要找的结点的父结点
2,如果左子树不是查找的结点那就判断一下 如果这个数是小于结点的值那这个值就在左子树 并且要判断左子树是否不为空
不为空才可以递归查找
*/
if(this.left!=null&&this.left.value==val){
return this;
}else if(this.left!=null&&val<this.value){
return this.left.ParentSelect(val);
}
/*
3,判断右子树的值是否为等于这个值
4,如果右子树也不是 那就判断这个值是否比此结点的值要大 则判断右子树是否为空 那就递归右子树
*/
if(this.right!=null&&this.right.value==val){
return this;
}else if(this.right!=null&&val>this.value){
return this.right.ParentSelect(val);
}
//如果左右子树判断完都没找到那就返回空
//还有根结点也是没有父结点的
return null;
}
public int getValue() {return value;}
public void setValue(int value) {this.value = value;}
public Node getLeft() {return left;}
public void setLeft(Node left) {this.left = left;}
public Node getRight() {return right;}
public void setRight(Node right) {this.right = right;}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
在创建一个二叉排序树:
public class BinaryTree {
/*
设置一个二叉排序树的根结点
*/
private Node root;
public BinaryTree() {
}
public void setRoot(Node root) {
this.root = root;
}
/**
* 查询
* 调用根结点的查询函数
* @param val
* @return
*/
public Node SelectVal(int val){
if(this.root==null){
return null;
}else{
return this.root.SelectVal(val);
}
}
/**
* 查询父结点
* 调用根结点的查询函数
* @param val
* @return
*/
public Node ParentSelect(int val){
if(this.root==null) return null;
return this.root.ParentSelect(val);
}
/**
* 添加结点
* 调用根结点的add函数
* @param node
*/
public void add(Node node){
if(this.root==null) return;
this.root.add(node);
}
/**
* 遍历二叉树
*/
public void InfixSelect(){
if(this.root==null) return;
this.root.preSelect();
}
/**
* 此方法是查询一个子树的最小叶子结点 删除方法会用到这个方法
* @param node
* @return
*/
public int FindMinNode(Node node){
Node target = node;//创建一个迭代结点
while(target.getLeft()!=null){//循环迭代找到一棵树的最左结点叶子结点
target=target.getLeft();
}
//找到了结点需要把这个结点的值赋值到下面方法要删除的值 那这个结点就需要删除了
this.DelNode(target.getValue());
//返回该结点的值
return target.getValue();
}
/**
* 删除结点
* @param val
*/
public void DelNode(int val){
if(this.root==null) {//先判断根结点是否为空 防止出错
return;
}
//拿到这个val值在树中的结点
Node target = this.SelectVal(val);
//判断这个结点是否为空
if(target==null){
return;
/*
判断这个树是否只有一个根结点 只需要看它的左右子树是否为空就行
*/
}else if(this.root.getLeft()==null&&this.root.getRight()==null){
this.root=null;
return;
}
/*
到这里其他的异常情况都排除了
首先找到这个结点的父结点 方便删除并且用其他结点替代这个结点
*/
Node ParentNode = this.ParentSelect(val);
//到这里判断他是否为叶子结点 没有左右子树
if(target.getLeft()==null&&target.getRight()==null){
if(ParentNode.getLeft()!=null&&ParentNode.getLeft()==target){
//如果他是叶子结点那就判断这个结点是他父结点的左或右结点
//如果父结点的左子树有东西并且==target这个结点那就把它父结点的左子树置为空即可删除成功
ParentNode.setLeft(null);
}else if (ParentNode.getRight()!=null&&ParentNode.getRight()==target){
//想对如果是父结点的右子树是它那就把父结点的右子树置为空即可
ParentNode.setRight(null);
}
/*
到这里说明他不是叶子结点
那就判断这个结点是不是有左右子树 这里先判断他是否同时拥有左右子树
*/
}else if(target.getLeft()!=null&&target.getRight()!=null){//如果该结点的左右子树都存在那就删除
//那就找到他的右子树的最小值 来替代这个结点即可完成删除
int Min = this.FindMinNode(target.getRight());//寻找子树左子树最小值的方法,注意传入的是该结点的右子树
target.setValue(Min);//将最小结点的值赋值给这个结点即可完成替换
}else{
/*
到这里就还剩一种情况 就是要删除的这个结点 只有左子树 或者 只有右子树
*/
if(target.getLeft()!=null){//判断左子树不为空说明有左子树
if(ParentNode!=null) {//判断是否为根结点 根结点是没有父结点的直接把根结点的左子树赋值给根结点就可以了
if(ParentNode.getLeft().getValue()==val){//判断父结点的左子树的值等于这个值吗 如果等于 说明这个结点是一个左子树
ParentNode.setLeft(target.getLeft());//那就把父结点的左子树改为要删除的结点的左子树
}else if(ParentNode.getRight().getValue()==val){//如果这个结点是它父结点的右子树 就把父结点的右子树设置为这个结点的左子树
ParentNode.setRight(target.getLeft());
}
}else{
this.root=target.getLeft();
}
}else if(target.getRight()!=null){//同上 如果这个结点只有一个右子树 那就判断这个结点是父结点的左还是右结点 再把父结点的左或右结点设置为该结点的右子树即可
if(ParentNode!=null) {
if(ParentNode.getLeft().getValue()==val){
ParentNode.setLeft(target.getRight());
}else if(ParentNode.getRight().getValue()==val){
ParentNode.setRight(target.getRight());
}
}else{
this.root=target.getRight();
}
}
}
}
}