sheng的学习笔记-二叉树(BST)

二叉树(BST:Binary Search Tree)

平衡二叉树可参考:

sheng的学习笔记-平衡二叉树(AVL)和3+4重构_coldstarry的博客-CSDN博客

动画演示网站

下面是动画网站,用于演示二叉树的查找,插入,删除等操作的过程

http://btv.melezinek.cz/binary-search-tree.html

定义:

二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树 

遍历

前序遍历:根节点->左子树->右子树(根->左->右)  GDAFEMHZ

中序遍历:左子树->根节点->右子树(左->根->右)   ADEFGHMZ

后序遍历:左子树->右子树->根节点(左->右->根)    AEFDHZMG

二叉树的操作时间与高度成正比,a的速度会比b快(因为a的高度小) 

查找数据

  

下面是用递归函数实现 

 扩展为while循环,这个版本会快一些

插入 

 

删除

后继的定义看下面的《其他》章节

 

 

 

 

 

 transplant解释:

 tree-delete解释,其中tree-minimum函数见下面最小关键字元素部分

其他

最小关键字元素和最大关键字元素

最小关键字元素:通过从树根开始沿着left孩子指针直到遇到一个NIL(空),最左下角的那个叶子结点,就是这颗树的最小关键字节点,这一颗树中这个节点的值最小,如图  2  这个节点就是最小关键字元素

最大关键字元素:跟最小关键字元素对称的,最右边的叶子结点就是最大关键字元素

后继和前驱

下面是后继的代码,如果x的右子树非空,找到x的右子树的最左节点(15的后继是17)。如果x的右子树是空的,通过x开始沿着树网上直到遇到其双亲有左孩子的节点(下图3~7行,其中x.p是x的父节点,13的后继是15)

 

优缺点

二叉树的性能损耗,跟二叉树的高度有关,越高的树越慢。在随机数据模型中,查找和插入都是2lgN左右,但如果是顺序插入,二叉树会退化为链表,比如下图,所以出现平衡二叉树,红黑树等改进方案

代码

照着书里的逻辑自己试着写了个,留个纪念

先弄个abstract的类,便于做不同的实现来进行比较

package algorithm.BST;

//二叉树
public abstract class BSTAbstract {
    public int count = 0;

    // 二叉树的根节点
    public MyNode root;

    /**
     * 查询
     *
     * @return
     */
    public abstract MyNode find(int o);

    /**
     * 插入
     *
     * @param o
     */
    public abstract void insert(MyNode o);

    /**
     * 删除
     *
     * @param o
     */
    public abstract boolean delete(MyNode o);

    /**
     * 节点个数
     *
     * @return
     */
    public int count() {
        return this.count;
    }

    /**
     * 打印整个树
     *
     * @return
     */
    public abstract void inorderTreeWalk(MyNode myNode);

}

class MyNode {
    public int data;

    public MyNode leftNode;

    public MyNode rightNode;

    public MyNode parent;

    public MyNode(int key) {
        data = key;
    }

    @Override
    public String toString() {
        return "MyNode{" +
                "data=" + data +
                '}';
    }
}

实现类

package algorithm.BST;

public class MyBST extends BSTAbstract {

    @Override
    public MyNode find(int o) {
        System.out.println("find函数路径为");
        MyNode node = root;
        System.out.println("途径节点:" + node);
        while (node != null && o != node.data) {
            if (o < node.data) {
                node = node.leftNode;
            } else {
                node = node.rightNode;
            }
            System.out.println("途径节点:" + node);
        }
        System.out.println("找到节点:" + node);
        return node;
    }

    @Override
    public void insert(MyNode o) {
        MyNode x = this.root;
        MyNode parent = null;
        while (x != null) {
            parent = x;
            if (o.data < x.data) {
                x = x.leftNode;
            } else {
                x = x.rightNode;
            }
        }
        o.parent = parent;
        // 空列表
        if (parent == null) {
            root = o;
        } else if (o.data < parent.data) {
            parent.leftNode = o;
        } else {
            parent.rightNode = o;
        }

    }

    @Override
    public boolean delete(MyNode o) {
        MyNode current = root;
        MyNode parent = null;
        //当前节点是否为左节点
        boolean isLeftNode = false;

        // 定位需要删除的节点
        if (current == null) {
            return false;
        }
        while (current.data != o.data) {
            if (o.data < current.data) {
                isLeftNode = true;
                current = current.leftNode;
            } else {
                isLeftNode = false;
                current = current.rightNode;
            }
            if (current == null) {
                return false;
            }
        }
        parent = current.parent;

        // 如果待删除节点是叶子结点,将节点改为空
        if (current.leftNode == null && current.rightNode == null) {
            if (current == root) {
                root = null;
            } else if (isLeftNode) {
                parent.leftNode = null;
            } else {
                parent.rightNode = null;
            }
            return true;
        }
        // 当前有一个节点,如果左孩子是空,将右孩子替代节点
        if (current.leftNode == null) {
            transfer(current, current.rightNode);
            return true;
        } else if (current.rightNode == null) {
            transfer(current, current.leftNode);
            return true;
        }

        // 当前节点有两个子节点(有左孩子和右孩子),此处一定会进入这个分支,if其实可以不写,不过为减少上下文依赖,写这个if
        if (current.leftNode != null && current.rightNode != null) {
            MyNode successor = minimum(current.rightNode);
            // 如果右子树的最小值(就是current的后继节点)不是current的右孩子,
            // 需要将后继节点successor的右孩子交换到后继节点位置,然后将后继节点替代删除节点current
            if (successor.parent != current) {
                //让后继节点的右孩子替代后继节点
                transfer(successor, successor.rightNode);
                //删除节点current的右孩子给后继节点的右孩子
                successor.rightNode = current.rightNode;
                successor.rightNode.parent = successor;
            }
            //将后继节点successor替代删除节点current
            transfer(current, successor);
            successor.leftNode = current.leftNode;
            successor.leftNode.parent = successor;

            return true;
        }

        return false;
    }

    // 将new节点替换old节点
    public void transfer(MyNode oldNode, MyNode newNode) {
        // 如果被替代的节点是根节点(父节点是空),将root改为新的节点
        if (oldNode.parent == null) {
            root = newNode;
        } else if (oldNode == oldNode.parent.leftNode) {
            // 如果被替换的节点,是父节点的左孩子,将父节点的左孩子改为新节点
            oldNode.parent.leftNode = newNode;
        } else {
            // 如果被替换的节点是父节点的右孩子,将父节点的右孩子换为新节点
            oldNode.parent.rightNode = newNode;
        }
        //将老节点的父节点,赋给新节点的父节点
        newNode.parent = oldNode.parent;
    }

    // 找到一个节点树的最小左孩子
    public MyNode minimum(MyNode myNode) {
        while (myNode.leftNode != null) {
            myNode = myNode.leftNode;
        }
        return myNode;
    }

    @Override
    public void inorderTreeWalk(MyNode myNode) {
        if (myNode != null) {
            inorderTreeWalk(myNode.leftNode);
            System.out.println(myNode.data);
            inorderTreeWalk(myNode.rightNode);
        }
    }

    @Override
    public int count() {
        return super.count();
    }
}

跑个demo测一下,构造二叉树,查询,遍历,插入,删除都测了下

package algorithm.BST;

import java.util.ArrayList;
import java.util.Arrays;

public class BSTDemo {
    public static void main(String[] args) {
        BSTAbstract myBST = new MyBST();

        ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100),
                (int) (Math.random() * 100), (int) (Math.random() * 100)));
        System.out.println(list);
        for (int e : list) {
            myBST.insert(new MyNode(e));
        }
        System.out.println("==============遍历二叉树");
        myBST.inorderTreeWalk(myBST.root);

        System.out.println("==============根节点:" + myBST.root);

        System.out.println("==============找到最后一个元素:" + list.get(list.size() - 1));
        myBST.find(list.get(list.size() - 1));

        System.out.println("==============删除倒数第四个元素:" + list.get(list.size() - 4));
        myBST.delete(myBST.find(list.get(list.size() - 4)));
        System.out.println("==============删除后的结果");
        myBST.inorderTreeWalk(myBST.root);

    }
}

结果:

[7, 72, 67, 29, 83, 3, 79, 84, 68, 39, 81, 83, 37, 31, 90, 76]
==============遍历二叉树
3
7
29
31
37
39
67
68
72
76
79
81
83
83
84
90
==============根节点:MyNode{data=7}
==============找到最后一个元素:76
find函数路径为
途径节点:MyNode{data=7}
途径节点:MyNode{data=72}
途径节点:MyNode{data=83}
途径节点:MyNode{data=79}
途径节点:MyNode{data=76}
找到节点:MyNode{data=76}
==============删除倒数第四个元素:37
find函数路径为
途径节点:MyNode{data=7}
途径节点:MyNode{data=72}
途径节点:MyNode{data=67}
途径节点:MyNode{data=29}
途径节点:MyNode{data=39}
途径节点:MyNode{data=37}
找到节点:MyNode{data=37}
==============删除后的结果
3
7
29
31
39
67
68
72
76
79
81
83
83
84
90

Process finished with exit code 0
 


参考文章:

书:算法

书:算法导论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值