"二叉树"-实现数据结构算法,完全解析,通俗易懂的图文及代码讲解

来源:互联网 发布:弗林认罪 知乎 编辑:程序博客网 时间:2024/05/21 13:58

二叉树的完全理解


1.概念:

1.每个节点(除了根节点)都有父节点2.每个节点最多有两个节点

2.优点:

1.索引:提高查询效率,就是B树索引(Binary Tree)2.比如全国十几亿个人的姓名和身份证,有同名的,想找一个人的信息,如果从头到尾逐条就太慢了.按正规来讲,在数据库中每个记录都有uid,给名字就找到对应的uid,1234...然后就可以找到同名字的各个身份证号;3.因为二叉树是排列好的,所以可以直接确定在哪个位置.

3.二叉树的排列方式

无论哪一种,左节点肯定在右节点前面,”某”序根据的是”根”节点所在位置:
这里写图片描述
1.前序:(根节点在”前”)

    根->左->右    7->3->1->5->9->11->21

2.中序:(根节点在”中”)

    左->根->右    1->3->5->7->11->9->21

3.后序:(根节点在”后”)

    左->右->根    1->5->3->11->21->9->7

4.层序(从上到下按层级)

    最简单的分层    7->3->9->1->5->11->21

现在我们要实现一个标准的二叉树:1.有序排列的(中序),2.无重复元素的

一棵大树就一个根:root
一个节点的构成有:4部分

1.parent:父节点2.Object:数据3.left:左节点4.right:右节点

节点属性图:
这里写图片描述


节点连接图:
这里写图片描述

添加数据方法add(Object data):

1.如果已经包含了,就不加入了,所以①写一个判断是否的方法.

接着:
1.创建节点;
2.给节点放入数据数据;
3.节点放入二叉树
4.如果根节点为空,根节点就等于这个节点;

如果不存在,就看应该放在哪个位置. 找它的父节点,所以②写一个找父节点的方法.

1.从根节点开始找,用传入的数据和根节点的数据比较2.比根节点数据大就继续右边查找,小就是左边查找3.比较"大小"注意点:是否实现了比较器 data instantsof Compale        实现了就强转为Comparable c1        没实现: c1 = data.toString();对比他们的字符方法,因为toString实现了;        最后:根据 compareTo得出那个大.

以上是完成数据的添加方法的逻辑.


接下来是打印的方法:(重点讲一下,因为涉及到递归)

打印的方法,因为是中序所以左->中->右打印
see(Note root){
note!=null;
//是个递归的过程,重要,相当于打完”每一个”点的左边才到中间才到右边
see(root.left);
system.out.println(root);
see(root.right);
}
相当于: 根的左->根的左->根的左然后才递上来,递归过程挺清晰的;
因为是中序遍历,所以左中右

接下来讲一下移除的方法,也是该算法的重点和难点

只要掌握以下几点要素即可完全理解

移除的点我们分为两种:

1.该点是根节点        1.根节点没左儿子,也没右儿子            结论:那么根就为空了,因为就一个点        2.根节点只有左儿子            结论:左儿子就继位了,根赋值为左儿子        3.根节点只有右儿子            结论:右儿子继位了,根赋值为右儿子        4.根节点同时有左右儿子            (可以选择左儿子继位,也可以选右儿子,我们随便选个就选左儿子吧)            结论:左儿子继位,那么根据中序的原理:右儿子就变成了左儿子的最右边的节点:2.该点是非根节点        1.此节点没左儿子,也没右儿子        2.此节点只有左儿子        3.此节点只有右儿子        4.此节点同时有左右儿子

存在左右儿子,选左儿子继位图:
这里写图片描述

下面给出详细代码

public class MyTree {    //节点的属性    private class Node {        Node   parent;//父节点        Object data;//数据        Node   left;  //左节点        Node   right; //右    }    //根    private Node root;    public void add(Object data) {        //存在就不添加        if (contians(data)) {            return;        }        //建一个点        Node node = new Node();        node.data = data;        if (root == null) {//根是空,该点就是根,说成根就是该点更合适            root = node;        } else {            //判断是否存在            //有节点,那就:1.找到该节点的应该的父节点,2.找到了,比较判断该值和父节点的值,应该放左还是右            Node parent = findPrent(data, root);            node.parent = parent;//赋值再双向,确定父节点地址            if (compare(data, parent.data)) {                //大于就放在右边                //node.parent = parent.right;//赋值再双向,这个是错误的,等于parent 的地址,不用加上right                parent.right = node;            } else {                parent.left = node;            }        }    }    /**     * 包含就不添加     */    private boolean contians(Object data) {        //查找到点就说明包含        Node node = findNode(data);        return node != null;    }    //找到该点的父节点    private Node findPrent(Object data, Node node) {        //从以该点为根开始找        Node parent = null;        Node temp = node;        while (temp != null) {            //先等于当前点,然后当左右都为空的时候就找到了            parent = temp;            if (compare(data, temp.data)) {                //比父节点大,继续右边找                temp = temp.right;            } else {                temp = temp.left;            }        }        return parent;    }    /**     * compare只比大小,不比相等,这里相等就是当小于     * 要能比较字符串和数字     * 相等就不要添加     * 然后看ture还是false放左右     */    private boolean compare(Object data1, Object data2) {        Comparable c1;        Comparable c2;        if (data1 instanceof Comparable) {//看看是否传的是实现比较器的,是就转换            c1 = (Comparable) data1;            c2 = (Comparable) data2;        } else {            c1 = data1.toString();//否则就获取他们的toString();因为toString已经实现了Comparable比如数字,字符串都能比            c2 = data1.toString();        }        int i = c1.compareTo(c2);        return i > 0;    }    @Override    public String toString() {        return print();    }    /**     * 打印方法,要采用递归,同汉诺塔原理     * 中序     */    public String print() {        StringBuffer sb = new StringBuffer();        see(root, sb);        String s = sb.toString();        if (s.length() <= 1) {            return "[" + s + "]";        }        s = s.substring(0, s.length() - 1);        return "[" + s + "]";    }    //把根点放入进去    private void see(Node root, StringBuffer sb) {        if (root != null) {            //先打印左            see(root.left, sb);            //再打印中            //System.out.println(root.data);            sb.append(root.data + ",");            //再打印右边            see(root.right, sb);        }    }    /**     * 移除方法     * 判断删除的是根节点还是非根节点     * 然后分别判断该点有无节点;只有左,还是只有右,还是都有,再双向判断     */    public void remove(Object data) {        Node node = findNode(data);        if (node != null) {//存在才去做            //删的是根节点            if (node.parent == null) {//等同node==root;                //没有子节点                if (node.left == null && node.right == null) {                    root = null;//根就就空了                }                //该根节点只有左节点无右                else if (node.right == null) {                    node.parent = null;                    root = node.left;//根就变左节点                }                //该根节点有右节点无左                else if (node.left == null) {                    node.parent = null;                    root = node.right;                }                //该根节点有左也有右                else {                    //找最左儿子的最后边的节点,以此时的左儿子为根然后找到 此时右儿子应该的父节点,画图                    //右儿子变成左儿子的"最右边"节点                    //双向指向                    //把一个点分裂,让左儿子继承即可                    Node left = split(node);                    //新的父节点,变成左儿子                    root = left;                    root.parent = null;                }                //非根节点,同样的判断            } else {                //该点无左无右                if (node.left == null && node.right == null) {                    //判断自己是左还是右,通过对比父节点可知                    if (compare(data, node.parent.data)) {                        //该点是右                        node.parent.right = null;                    } else {                        //是左                        node.parent.left = null;                    }                }                //该点有左无右                else if (node.right == null) {                    //判断该点是左右                    if (compare(data, node.parent.data)) {                        //是右                        node.parent.right = node.left;                    } else {                        node.parent.left = node.left;                    }                }                //该点有右节点                else if (node.left == null) {                    //判断左右                    if (compare(data, node.parent)) {                        //右边                        node.parent.right = node.right;                    } else {                        node.parent.left = node.right;                    }                    //该点有左右                } else {                    //分裂该节点                    Node left = split(node);                    //判断该点是左是右边                    if (compare(data, node.parent.data)) {                        //右                        node.parent.right = left;//删除的位置由左儿子继承                    } else {                        //左                        node.parent.left = left;                    }                    //双向                    left.parent = node.parent;                }            }        }    }    private Node split(Node node) {        Node parentL = findPrent(node.right.data, node.left);//以右边数据为参考,从该点左侧开始找        parentL.right = node.right;//双向;        node.right.parent = parentL;//双向        return node.left;//返回的是继承的左儿子    }    //寻找该数据所在的的点    private Node findNode(Object data) {        //从根节点遍历,先默认为根,不等于的话就接着改变        Node node = root;        while (node != null) {            //相等就获取到了            if (data.equals(node.data) && data.hashCode() == node.data.hashCode()) {                return node;//等同break;            } else {                if (compare(data, node.data)) {                    node = node.right;                } else {                    node = node.left;                }            }        }        return node;    }    //更新方法    public void update(int oldData, int newData) {        //包含才操作        if (contians(oldData)) {            //删除旧的,放新的            remove(oldData);            add(newData);        }    }}

新建测试类进行测试

public class MyTreeTest {    public static void main(String[] args) {        MyTree tree = new MyTree();        System.out.println("--------------------add----------------------");        tree.add(3);        tree.add(4);        tree.add(2);        tree.add(2);//重复        tree.add(21);        tree.add(11);        tree.add(12);        tree.add(7);        System.out.println(tree);        tree.remove(3);        System.out.println("--------------------remove 3----------------------");        System.out.println(tree);        tree.remove(21);        System.out.println("--------------------remove 21----------------------");        System.out.println(tree);        tree.update(12, 77);        System.out.println("--------------------把12 改成 21----------------------");        System.out.println(tree);    }}

打印结果

--------------------add---------------------[2,3,4,7,11,12,21]--------------------remove 3----------------[2,4,7,11,12,21]--------------------remove 21---------------[2,4,7,11,12]--------------------把12 改成 21-------------[2,4,7,11,77]

总结:

对于二叉树数据结构算法,大家只要理解一下几点就可完全明白算法过程:1.节点的4个属性2.选择中序排序的 左->中->右 特点3.各种小方法构成大方法,小方法:    1.根据数据获取节点:findNode(data)    2.包含的方法4.大方法:添加,删除5.最重要的就是逻辑的判断:确定删除的点是否为根节点,再确定删除的点的儿子情况:有无儿子,有哪个儿子,有多少个儿子,选择继承的儿子.

以上就是二叉树算法的完全解析和归纳总结,希望能帮助大家更好的理解

0 0
原创粉丝点击