二叉树

二叉树(binary tree)是一棵树,其中每个节点都不能有多于两个的儿子。

下图显式一颗由一个根和两颗字数组成的二叉树,子树TL和TR均可能为空。

1330447-20190120161410310-112385384.png

二叉树的一个性质是一颗平均二叉树的深度要比节点个数N小得多,这个性质有时很重要。分析表明,其平均深度为O(\(\sqrt{N}\)),而对于特殊类型的二叉树,即二叉查找树(binary search tree),其深度的平均值是O(\(\log{N}\))。

二叉树的遍历

对于二叉树来讲最主要、最基本的运算是遍历。

遍历二叉树,是指以一定的次序访问二叉树中的每个节点。所谓访问节点是指节点进行各种操作的简称。例如,查询节点数据域的内容,或输出它的值,或找出节点位置,或是执行对节点的其他操作。遍历二叉树的过程实质是把二叉树的节点进行线性排列的过程。假设遍历二叉树时访问节点的操作就是输出节点数据域的值,那么遍历的结果得到一个线性序列。

从二叉树的递归定义可知,一颗非空的二叉树由根节点及左、右子树这三个基本部分组成。因此,在任意给定节点上,可以按某种次序执行三个操作:

  1. 访问节点本身(N)
  2. 遍历该节点的左子树(L)
  3. 遍历该节点的右子树(R)

以上三种操作有六种执行次序:
 NLR、LNR、LRN、NRL、RNL、RLN。

注意:前三种次序与后三种次序对称,故只讨论先左后右的前三种次序。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

实现

因为一个二叉树节点最多有两个子节点,所以我们可以保存直接连接道它们的链。树节点的声明在结构上类似于双链表的声明,在声明中,节点就是由element(元素)的信息加上两个到其他节点的引用(left和right)组成的结构。

import java.util.LinkedList;

/**
 * @author: Tu9ohost
 */
public class binaryTree {
    // 二叉树的节点数据结构
    private class TreeNode{
        private int key = 0;
        private String data = null;
        private boolean isVisted = false;
        private TreeNode leftChild = null;
        private TreeNode rightChild = null;
        public TreeNode(){}
        public TreeNode(int key, String data){
            this.key = key;
            this.data = data;
            this.leftChild = null;
            this.rightChild = null;
        }
    }

    private TreeNode root = null;

    public binaryTree(){
        root = new TreeNode(1,"rootNode(A)");
    }

    // 创建一颗二叉树
    public void createBinTree(TreeNode root){
        TreeNode newNodeB = new TreeNode(2,"B");
        TreeNode newNodeC = new TreeNode(3,"C");
        TreeNode newNodeD = new TreeNode(4,"D");
        TreeNode newNodeE = new TreeNode(5,"E");
        TreeNode newNodeF = new TreeNode(6,"F");
        root.leftChild = newNodeB;
        root.rightChild = newNodeC;
        root.leftChild.leftChild = newNodeD;
        root.leftChild.rightChild = newNodeD;
        root.rightChild.rightChild = newNodeF;
    }

    public boolean isEmpty(){
        return root == null;
    }

    // 树的高度
    public int height(){
        return height(root);
    }

    // 节点个数
    public int size(){
        return size(root);
    }

    private int height(TreeNode subTree){
        if (subTree == null){
            return 0;
        }
        else {
            int i = height(subTree.leftChild);
            int j = height(subTree.rightChild);
            return (i < j) ? (j + 1) : (i + 1);
        }
    }

    private int size(TreeNode subTree){
        if (subTree == null){
            return 0;
        }
        else{
            return 1 + size(subTree.leftChild)
                    + size(subTree.rightChild);
        }
    }

    public TreeNode parent(TreeNode element){
        return (root == null || root == element) ? null : parent(root,element);
    }

    public TreeNode parent(TreeNode subTree,TreeNode element){
        if (subTree == null){
            return null;
        }
        if (subTree.leftChild == element || subTree.rightChild == element){
            // 返回父节点地址
            return subTree;
        }
        TreeNode p;
        // 先在左子树中找,如果左子树中没有找到,才到右子树去找
        if ((p = parent(subTree.leftChild,element)) != null){
            // 递归在左子树中搜索
            return p;
        }else {
            // 递归在右子树中搜索
            return parent(subTree.rightChild,element);
        }
    }

    public TreeNode getLeftChildNode(TreeNode element){
        return element != null ? element.leftChild : null;
    }

    public TreeNode getRightChildNode(TreeNode element){
        return element != null ? element.rightChild : null;
    }

    public TreeNode getRoot(){
        return root;
    }

    // 在释放某个结点时,该结点的左右子树都已经释放,
    // 所以应该采用后续遍历,当访问某个结点时将该结点的存储空间释放
    public void destroy(TreeNode subTree){
        // 删除根为subTree的子树
        if (subTree != null){
            // 删除左子树
            destroy(subTree.leftChild);
            // 删除右子树
            destroy(subTree.rightChild);
            // 删除根节点
            subTree = null;
        }
    }

    public void traverse(TreeNode subTree){
        System.out.println("key:" + subTree.key + "--name:" + subTree.data);
        traverse(subTree.leftChild);
        traverse(subTree.rightChild);
    }

    public void visted(TreeNode subTree){
        subTree.isVisted = true;
        System.out.println("key:" + subTree.key + "--name" + subTree.data);
    }

    // 递归的前序遍历
    // 根结点 ---> 左子树 ---> 右子树
    public void preOrder(TreeNode subTree){
        if (subTree != null){
            visted(subTree);
            preOrder(subTree.leftChild);
            preOrder(subTree.rightChild);
        }
    }

    // 递归的中序遍历
    // 左子树---> 根结点 ---> 右子树
    public void inOrder(TreeNode subTree){
        if (subTree != null){
            inOrder(subTree.leftChild);
            visted(subTree);
            inOrder(subTree.rightChild);
        }
    }

    // 递归的后序遍历
    // 左子树 ---> 右子树 ---> 根结点
    public void postOrder(TreeNode subTree){
        if (subTree != null){
            postOrder(subTree.leftChild);
            postOrder(subTree.rightChild);
            visted(subTree);
        }
    }

    // 无递归的前序遍历
    public void nonRecPreOrder(TreeNode p){
        LinkedList<TreeNode> stack = new LinkedList<>();
        TreeNode node = p;
        while (node != null || !stack.isEmpty()){
            while (node != null){
                visted(node);
                stack.push(node);
                node = node.leftChild;
            }
            while (!stack.isEmpty()){
                node = stack.pop();
                node = node.rightChild;
            }
        }
    }

    // 无递归的中序遍历
    public void nonRecInOrder(TreeNode p){
        LinkedList<TreeNode> stack = new LinkedList<>();
        TreeNode node = p;
        while (node != null || !stack.isEmpty()){
            while (node != null){
                stack.push(node);
                node = node.leftChild;
            }
            if (!stack.isEmpty()){
                node = stack.pop();
                visted(node);
                node = node.rightChild;
            }
        }
    }

    // 无递归的后序遍历
    public void nonRecPostOrder(TreeNode p){
        LinkedList<TreeNode> stack = new LinkedList<>();
        TreeNode node = p;
        while (p != null){
            for (; p.leftChild != null; p = p.leftChild){
                stack.push(p);
            }
            while (p != null && (p.rightChild == null || p.rightChild == node)){
                visted(p);
                node = p;
                if (stack.isEmpty()) {
                    return;
                }
                p = stack.pop();
            }
            stack.push(p);
            p = p.rightChild;
        }
    }

    // 深度优先遍历
    public void depthFirst(TreeNode p){
        if (p == null){
            return;
        }
        LinkedList<TreeNode> stack = new LinkedList<>();
        stack.push(p);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            System.out.println(node.data + " ");
            if (node.rightChild != null){
                stack.push(node.rightChild);
            }
            if (node.leftChild != null){
                stack.push(node.leftChild);
            }
        }
    }

    // 层序遍历
    public void levelFirst(TreeNode p){
        if (p == null) {
            return;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(p);
        while (!queue.isEmpty()){
            TreeNode node = queue.poll();
            System.out.println(node.data + " ");
            if (node.leftChild != null){
                queue.offer(node.leftChild);
            }
            if (node.rightChild != null){
                queue.offer(node.rightChild);
            }
        }
    }
}

参考链接:https://blog.csdn.net/wuwenxiang91322/article/details/12231657
参考书籍:
《数据结构与算法分析》

转载于:https://www.cnblogs.com/Tu9oh0st/p/10295247.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值