2021-01-17

二叉查找树的遍历

一、二叉树相关概念介绍

1.二叉树:二叉树就是度不超多2的树,每个结点最多有两个子结点;
2.满二叉树:除了最后一层,每一层的结点都有左右子结点,则该树就是满二叉树。如果一个满二叉树的层数为k,则结点总数为:(2^k)-1;
3. 完全二叉树:叶子结点只能出现在最下层和次下层,且最下面一层的结点都集中在最左边的若干位置;
4. 二叉查找树:一种特殊的二叉树,对于任意非叶子结点,左子结点的值小于该结点值,右子结点值大于该结点值。

二叉树示例如下图所示:
二叉树图片
删除结点前后对比下图所示(用于测试使用数据):
二叉树删除前后对比

二叉树的遍历
1、前序遍历:20-13-9-18-19-26-24-27
2、中序遍历:9-13-18-19-20-24-26-27
3、后序遍历:9-19-18-13-24-27-26-20
3、层序遍历:20-13-26-9-18-24-27-19

二、二叉树查找树实现代码

package mongo.util.dataStructure;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 二叉树
 * <p>
 * 二叉树就是度不超多2的树,每个结点最多有两个子结点
 * <p>
 * 满二叉树:
 * 除了最后一层,每一层的结点都有左右子结点,则该树就是满二叉树
 * 如果一个满二叉树的层数为k,则结点总数为:(2^k)-1
 * <p>
 * 完全二叉树
 * 叶子结点只能出现在最下层和次下层,且最下面一层的结点都集中在最左边的若干位置
 * <p>
 * 二叉查找树
 * 一种特殊的二叉树,对于任意非叶子结点,左子结点的值小于该结点值,右子结点值大于该结点值
 */
public class BinaryTree {

    //根结点
    private Node root;
    private int size;

    public void put(Integer key, String value) {
        root = this.put(key, value, root);
    }

    //给指定树上插入新结点,并返回新树
    private Node put(Integer key, String value, Node target) {
        if (target == null) {
            target = new Node(key, value, null, null);
            size++;
            return target;
        }
        //如果新增结点小于target,则添加到target左子结点
        if (key < target.key) {
            Node node = put(key, value, target.left);
            target.left = node;

        }
        //如果新增结点大于target,则添加到target右子结点
        if (key > target.key) {
            Node node = put(key, value, target.right);
            target.right = node;
        }
        //如果新增结点等于target, 则更新target
        target.vaule = value;

        return target;
    }

    public String get(Integer key) {
        Node node = get(key, root);
        return node == null ? null : node.vaule;
    }

    /**
     * get方法思路,从根结点开始查找
     * 1.如果查询的key小于当前结点的key,则继续查询当前结点的左子结点
     * 2.如果查询的key大于当前结点的key,则继续查询当前结点的右子结点
     * 3.如果查询的key等当前结点的key,则查询命中,返回当前结点的value
     *
     * @return
     */
    private Node get(Integer key, Node node) {
        if (node == null) {
            return null;
        }
        if (key.equals(node.key)) {
            return node;
        }
        if (key < node.key) {
            return get(key, node.left);
        }
        if (key > node.key) {
            return get(key, node.right);
        }
        return null;
    }

    public void delete(Integer key) {
        root = delete(key, root);
    }


    /**
     * 删除结点思路:
     * 1.先查询到被删除的结点X
     * 2.找到结点X的右子树中的最小结点minNode
     * 3.让结点X的左子树成为minNode的左子树,让结点X的右子树成为minNode的右子树
     * 4.让结点X的父结点指向minNode
     *
     * @param key
     * @param node
     */
    private Node delete(Integer key, Node node) {
        if (node == null) {
            return null;
        }
        //如果被删结点小于当前结点node,则继续从node左子结点查询
        if (key < node.key) {
            node.left = delete(key, node.left);
        }
        ///如果被删结点大于当前结点node,则继续从node右子结点查询
        else if (key > node.key) {
            node.right = delete(key, node.right);
        } else {
            //key == node.key,已经找到被删除结点
            Node deleteNode = node;
            //如果被删结点deleteNode无左子结点,则返回deleteNode的右子结点
            if (deleteNode.left == null) {
                return deleteNode.right;
            }
            //如果被删结点deleteNode无右子结点,则返回deleteNode的左子结点
            if (deleteNode.right == null) {
                return deleteNode.left;
            }
            //如果被删结点deleteNode存在左右子树,则找到被删结点deleteNode的右子树中的最小结点minNode
            Node minNode = deleteNode.right;
            if (minNode.left != null) {
                minNode = minNode.left;
            }
            Node n = deleteNode.right;
            while (n.left != null) {
                if (n.left.left == null) {
                    //说明n.left已经为叶子结点,删除n.left
                    n.left = null;
                    minNode = n.left;
                } else {
                    n = n.left;
                }
            }
            if (n.left == null) {
                deleteNode.right = null;
            }
            //最小结点已经删除,让deleteNode的左子树成为minNode的左子树,让deleteNode的右子树成为minNode的右子树
            minNode.left = deleteNode.left;
            minNode.right = deleteNode.right;
            size--;
            //返回新树
            return minNode;
        }
        return node;
    }

    /**
     * 前序遍历:先访问根结点,再访问左子树,最后访问右子树
     *
     * @return
     */
    public Queue<Integer> preorderErgodic() {
        Queue<Integer> queue = new LinkedList<>();
        preorderErgodic(root, queue);
        return queue;
    }

    private void preorderErgodic(Node node, Queue<Integer> queue) {
        if (node == null) {
            return;
        }
        //把当前结点放入队列
        queue.add(node.key);
        if (node.left != null) {
            //如果当前结点有左结点,则继续访问左子树
            preorderErgodic(node.left, queue);
        }
        if (node.right != null) {
            //如果当前结点没右左结点,则说明左子树已经访问完毕,再访问右子树
            preorderErgodic(node.right, queue);
        }
    }

    /**
     * 中遍历:先访问左子树,再访问根结点,最后访问右子树
     *
     * @return
     */
    public Queue<Integer> inorderErgodic() {
        Queue<Integer> queue = new LinkedList<>();
        inorderErgodic(root, queue);
        return queue;
    }

    private void inorderErgodic(Node node, Queue<Integer> queue) {
        if (node == null) {
            return;
        }
        if (node.left != null) {
            //如果当前结点有左结点,则继续访问左子树
            inorderErgodic(node.left, queue);
        }
        queue.add(node.key);
        if (node.right != null) {
            //如果当前结点没右左结点,则说明左子树已经访问完毕,再访问右子树
            inorderErgodic(node.right, queue);
        }
    }

    /**
     * 后序遍历:先访问左子树,再访问根结点,最后访问右子树
     *
     * @return
     */
    public Queue<Integer> postorderErgodic() {
        Queue<Integer> queue = new LinkedList<>();
        postorderErgodic(root, queue);
        return queue;
    }

    private void postorderErgodic(Node node, Queue<Integer> queue) {
        if (node == null) {
            return;
        }
        if (node.left != null) {
            //如果当前结点有左结点,则继续访问左子树
            postorderErgodic(node.left, queue);
        }
        if (node.right != null) {
            //如果当前结点没右左结点,则说明左子树已经访问完毕,再访问右子树
            postorderErgodic(node.right, queue);
        }
        queue.add(node.key);
    }


    /**
     * 层序遍历,逐层按顺序输出
     *
     * @return
     */
    public Queue<Integer> layerErgodic() {
        //用于弹出结点队列
        Queue<Node> popQueue = new LinkedList<>();
        //用于添加结点,作为最后的返回结果
        Queue<Integer> enQueue = new LinkedList<>();
        popQueue.add(root);
        /**
         * 实现思路:
         * 1.将当前结点从popQueue中取出,加入到enQueue
         * 2.将当前结点的左右子结点添加到popQueue,继续循环
         */
        while (popQueue.peek() != null) {
            Node node = popQueue.poll();
            if (node.left != null) {
                popQueue.add(node.left);
            }
            if (node.right != null) {
                popQueue.add(node.right);
            }
            enQueue.add(node.key);
        }
        return enQueue;
    }


    public int size() {
        return size;
    }


    /**
     * 构建树的结点
     */
    class Node {
        private Integer key;
        private String vaule;
        private Node left;
        private Node right;

        private Node(Integer key, String vaule, Node left, Node right) {
            this.key = key;
            this.vaule = vaule;
            this.left = left;
            this.right = right;
        }
    }

}

三、结果测试

package mongo.util.dataStructure;

import java.util.Queue;

public class TreeMainTest {

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.put(20, "1");
        tree.put(13, "2");
        tree.put(26, "3");
        tree.put(9, "4");
        tree.put(18, "5");
        tree.put(24, "6");
        tree.put(27, "7");
        tree.put(19, "8");

        System.out.println(tree.size());

        //前序遍历
        Queue<Integer> preorder = tree.preorderErgodic();
        String treeKeys_preorder = "";
        while (preorder.peek() != null) {
            treeKeys_preorder += "--" + preorder.poll();
        }
        System.out.println("前序遍历" + treeKeys_preorder);

        //中序遍历
        Queue<Integer> inorder = tree.inorderErgodic();
        String treeKeys_inorder = "";
        while (inorder.peek() != null) {
            treeKeys_inorder += "--" + inorder.poll();
        }
        System.out.println("中序遍历" + treeKeys_inorder);

        //后序遍历
        Queue<Integer> postorder = tree.postorderErgodic();
        String treeKeys_postorder = "";
        while (postorder.peek() != null) {
            treeKeys_postorder += "--" + postorder.poll();
        }
        System.out.println("后序遍历" + treeKeys_postorder);

        //层序遍历
        Queue<Integer> layerErgodic = tree.layerErgodic();
        String treeKeys_layer = "";
        while (layerErgodic.peek() != null) {
            treeKeys_layer += "--" + layerErgodic.poll();
        }
        System.out.println("层序遍历" + treeKeys_layer);

    }
}

备注:测试数据如上图,删除前后对比图中数据

测试结果:
测试结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值