《大话数据结构》----二叉树----二叉树简单实现---初始化插入删除中序遍历算法

在此特别感谢二叉搜索树的java实现 帖子的思路,更快吸收和实现这个知识点.

二叉树性质

  1. 在二叉树中的第i层至多有2i-1个节点(i>=1)
    例如 一层1个,二层2个,三层4个,4层8个…
  2. 深度为k的二叉树至多有2k-1个节点(k>=1)
    例如一层1个,二层22-1=3个,三层23-1=15个…
  3. 对任何一颗二叉树T,如果其终端节点数为n0,度为2 的节点数为n2,则n0=n2+1.
    (怕忘,截个图先)
    怕忘
  4. 具有N个节点的完全二叉树的深度为|log2n|+1
  5. 太多了.截个图…
    在这里插入图片描述

代码实现

主要复杂在添加时找节点

因为二叉树搜索树满足根节点值大于左节点,小于右节点,需要将插入的值,先同根节点比较,若大,则往右子树中进行查找,若小,则往左子树中进行查找。直到某个子节点。

/**
* 平淡无奇node类
**/
public class Node{
        Node parent;
        Node leftChild;
        Node rightChild;
        int val;
        public Node(Node parent, Node leftChild, Node rightChild,int val) {
            super();
            this.parent = parent;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.val = val;
        }
        public Node(int val){
            this(null,null,null,val);
        }
        public Node(Node node,int val){
            this(node,null,null,val);
        }
    }
package ***;
/**
 * @Author: wsh
 */
public class BiTreeManage {

    Node  root;
    private int size;

    public boolean putVal(Integer value) {
        if (root == null) {
            root = new Node(value);
            size++;
            return true;
        }
        //找到插入的父节点
        Node node = getAdapterNode(root, value);
        Node newNode = new Node(value);

        if (node.val > value) {
            node.leftChild = newNode;
        }else if(node.val<value){
            node.rightChild = newNode;
        }else{
            System.out.println("插入了相等的值"+value);
        }
        newNode.parent=node;
        size++;
        return true;
    }

    public void print(){
        print(root);
    }
    //前序 中序 后续其实就三行换换位置...
    private void print(Node root){
        if(root != null){
            print(root.leftChild);
            System.out.println(root.val);// 位置在中间,则中序,若在前面,则为先序,否则为后续
            print(root.rightChild);
        }
    }


    /**
     * 查找要插入的父节点
     */
    private Node getAdapterNode(Node node, int value) {

        //不会走,因为调用方法已处理node==null
//        if (node == null) {
//            return node;
//        }
        //如果该节点的值大于值, 且左边允许插入
        if (node.val > value && node.leftChild == null) {
            System.out.println(String.format("在插入%s找到父节点%s的左侧为null",value,node.val));
            return  node;
        }
        if (node.val < value && node.rightChild == null) {
            System.out.println(String.format("在插入%s找到父节点%s的右侧为null",value,node.val));
            return node;
        }
        //暂不详
        if (node.leftChild == null && node.rightChild == null) {
            System.out.println("什么时候能执行呢");
            return node;
        }
        if (node.val > value && node.leftChild != null) {
            System.out.println(String.format("在插入%s时,父节点%s左侧不为null,继续找下一个节点",value,node.val));
            return getAdapterNode(node.leftChild, value);
        } else if (node.val < value && node.rightChild != null) {
            System.out.println(String.format("在插入%s时,父节点%s右侧不为null,继续找下一个节点",value,node.val));
            return getAdapterNode(node.rightChild, value);
        }else{
            //value相等时
            return node ;
        }
    }
}


执行和输出

package ***;

/**
 * @Author: wsh
 */
public class BiTreeTest {

    public static void main(String[] args) {
        BiTreeManage biTreeManage = new BiTreeManage();
        biTreeManage.putVal(10);
        biTreeManage.putVal(9);
        biTreeManage.putVal(12);
        biTreeManage.putVal(8);
        biTreeManage.putVal(7);
        biTreeManage.putVal(11);
        biTreeManage.putVal(13);
        biTreeManage.putVal(10);
        biTreeManage.print();

    }
}


在插入9找到父节点10的左侧为null
在插入12找到父节点10的右侧为null
在插入8,父节点10左侧不为null,继续找下一个节点
在插入8找到父节点9的左侧为null
在插入7,父节点10左侧不为null,继续找下一个节点
在插入7,父节点9左侧不为null,继续找下一个节点
在插入7找到父节点8的左侧为null
在插入11,父节点10右侧不为null,继续找下一个节点
在插入11找到父节点12的左侧为null
在插入13,父节点10右侧不为null,继续找下一个节点
在插入13找到父节点12的右侧为null
错误 :插入了相等的值10
7
8
9
10
11
12
13

删除操作

删除情况分析

删除时也有些复杂, 这里也引用一下链接中的图
在这里插入图片描述

  1. 简单情况: 最后节点(无左无右图中DEG) / 仅有一侧子节点(左节点或右节点 图 BF)
    处理比较简单,直接替换上去就好了.感觉和双向链表操作差不多
  2. 复杂情况: 左右都有节点
    要删除的话,引用了一个后继节点概念,在网上找了找两个说明,结合一起看

1.一个节点在整棵树中的后继节点必满足,大于该节点值得所有节点集合中值最小的那个节点,即为后继节点
2.后继节点:就是中序遍历排序中相对的后一个

找该节点找右分支(都比它大)上里面最小的值,都在右节点的左节点,
比如A点,先找比它大的CEFG,再从里面找最小的,你会发现只有E

或者采用中序遍历,嗯…后继节点和前驱节点(没理解前序中序后续遍历的先去看概念,重复一遍是一遍)

删除代码

BiTreeManage.java中在原有的插入输出putVal()/print()基础上添加即可

    public boolean delete(int val) {
        Node node = getNode(val);
        if (node == null) {
            return false;
        }
        Node parent = node.parent;
        Node leftChild = node.leftChild;
        Node rightChild = node.rightChild;
        //判断不同情况
        //第一种最简单,左右都没有子节点,父节点对应侧置空即可
        if (leftChild == null && rightChild == null) {
            if (parent != null) {
                if (parent.leftChild == node) {
                    parent.leftChild = null;
                } else {
                    //} else if (parent.rightChild == node) {
                    //要么在父节点左边 要么右边  没有其他情况,
                    // 因为本来parent和node就是有关系的
                    parent.rightChild = null;
                }
            } else {
                //父节点为null  根节点
                root = null;
            }
            //可有可无
            node = null;
            return true;
        } else if (leftChild == null && rightChild != null) {
            //左侧为空  右侧不为空  直接类似链表操作
            return leftOrRightNull(val, parent, rightChild);
        } else if (rightChild != null && rightChild == null) {
            //同上一种情况  差不多  就把值换了一下
            return leftOrRightNull(val, parent, leftChild);
        } else if (leftChild != null && rightChild != null) {
            //最复杂的情况,置换方法
            //找到后继节点
            
            Node successor = getSuccessor(node);
            //将最小的删除掉
            //这里既是是root根节点,依旧可以执行.注意给参数是successor.val
            boolean delete = delete(successor.val);
            //把值直接给当前的node
            if (delete) {
                node.val = successor.val;
            }
            successor = null;
            return true;

        }
        return false;
    }

    /**
     * 查找后继节点
     //     10
     //     / \
     //    9   12
     //   /\   / \
     //  7  8 11 13
     *
     * 这里代码和原文https://www.cnblogs.com/qm-article/p/9279655.html
     * 稍微有些不一样,因为删除了部分代码,排除其他拓展情况
     * 原文中 Node parent =node.parent 走不到这里..
     */
    private Node getSuccessor(Node node) {
        Node rightChild = node.rightChild;
        //查找最小左侧节点的元素
        while (rightChild.leftChild != null) {
            //顺着左侧一直找下去,这样是最小的一个
            //结合上面树形注释,假设删除10  找到的最小就是11
            //回到上个置换方法:把10删除,把11放到10位置,二叉树依然成立
            rightChild = rightChild.leftChild;
        }
        return rightChild;
    }

    /**
     * 左右子树 其中有一个为null 的情况
     */
    private boolean leftOrRightNull(int val, Node parent, Node child) {
        if (parent != null && parent.val > val) {
            //收获: 根据判断大小,判断当前删除位置 在父节点的左边
            parent.leftChild = child;
        } else if (parent != null && parent.val < val) {
            //删除的节点在父节点的右边
            parent.rightChild = child;
        } else {
            //父节点为null 根节点,直接顶上就行
            root = child;
        }
        return true;
    }

    /**
     * 查找
     */
    public Node getNode(int val) {
        Node temp = root;
        int t;
        do {
            t = temp.val - val;
            if (t > 0) {
                temp = temp.leftChild;
            } else if (t < 0) {
                temp = temp.rightChild;
            } else {
                return temp;
            }
        } while (temp != null);
        return null;
    }

照常输出来一遍 看看效果,这次把树根据图形化一下…

 public static void main(String[] args) {
        BiTreeManage biTreeManage = new BiTreeManage();
        biTreeManage.putVal(10);
        biTreeManage.putVal(9);
        biTreeManage.putVal(12);
        biTreeManage.putVal(8);
        biTreeManage.putVal(7);
        biTreeManage.putVal(11);
        biTreeManage.putVal(13);
        biTreeManage.putVal(10);
        biTreeManage.print();
        // 依次put以后
        //     10
        //     / \
        //    9   12
        //   /\   / \
        //  7  8 11 13
        System.out.println("删除:");
        biTreeManage.delete(10);
        //删除以后
        //     11
        //     / \
        //    9   12
        //   /\     \
        //  7  8    13
        biTreeManage.print();

}

输出
在插入9找到父节点10的左侧为null
在插入12找到父节点10的右侧为null
在插入8,父节点10左侧不为null,继续找下一个节点
在插入8找到父节点9的左侧为null
在插入7,父节点10左侧不为null,继续找下一个节点
在插入7,父节点9左侧不为null,继续找下一个节点
在插入7找到父节点8的左侧为null
在插入11,父节点10右侧不为null,继续找下一个节点
在插入11找到父节点12的左侧为null
在插入13,父节点10右侧不为null,继续找下一个节点
在插入13找到父节点12的右侧为null
错误 :插入了相等的值10
7
8
9
10
11
12
13
删除:
7
8
9
11
12
13

不足和收获

  • 不足
    • 插入时候需要按照一定顺序插入,才能实现平衡平衡平衡二叉树,要不然就像一个根节点是root的左右两条链表
    • 没有任何思路,完全参考了其他人的想法
  • 收获
    • 一步步全部抄下来,过思路,脑海中各种推演,写完以后思路上至少是顺畅的,虽说盲打一遍肯定有bug,也有条理多了
    • 插入和删除看似思路简单,实操起来,发现有很多顾虑的地方,踏踏实实
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值