剑指Offer——二叉树

前言

数据结构通常是编程面试中考察的重点。在参加面试之前,应聘者需要熟练掌握链表、树、栈、队列和哈希表等数据结构,以及它们的操作。本片博文主要讲解二叉树操作的相关知识,主要包括二叉树的建立、遍历方法的循环和递归写法。

二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。

二叉树的java实现

首先创建一棵二叉树如下图,然后对这颗二叉树进行遍历操作(遍历操作的实现分为递归实现和非递归实现),同时还提供一些方法如获取双亲结点、获取左孩子、右孩子等。

package cn.edu.ujn.nk;

import java.util.Stack;

/**
 * 二叉树的链式存储
 * @author WWX
 */
public class BinaryTree {  

    private TreeNode root=null;  

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

    /**
     * 创建一棵二叉树
     * <pre>
     *           A
     *     B          C
     *  D     E            F
     *  </pre>
     * @param root
     * @author WWX
     */
    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=newNodeE;
        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;	// 递归结束:空树高度为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 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){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        TreeNode node=p;
        while(node!=null||stack.size()>0){
            while(node!=null){
                visted(node);
                stack.push(node);
                node=node.leftChild;
            }
            while(stack.size()>0){
                node=stack.pop();
                node=node.rightChild;
            }
        }
    }  

    //中序遍历的非递归实现
    public void nonRecInOrder(TreeNode p){
        Stack<TreeNode> stack =new Stack<BinaryTree.TreeNode>();
        TreeNode node =p;
        while(node!=null||stack.size()>0){
            //存在左子树
            while(node!=null){
                stack.push(node);
                node=node.leftChild;
            }
            //栈非空
            if(stack.size()>0){
                node=stack.pop();
                visted(node);
                node=node.rightChild;
            }
        }
    }  

    //后序遍历的非递归实现
    public void noRecPostOrder(TreeNode p){
        Stack<TreeNode> stack=new Stack<BinaryTree.TreeNode>();
        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.empty())
                    return;
                p=stack.pop();
            }
            //处理右子树
            stack.push(p);
            p=p.rightChild;
        }
    }
    public void visted(TreeNode subTree){
        subTree.isVisted=true;
        System.out.println("key:"+subTree.key+"--name:"+subTree.data);;
    }  

    /**
     * 二叉树的节点数据结构
     * @author WWX
     */
    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(){}  

        /**
         * @param key  层序编码
         * @param data 数据域
         */
        public TreeNode(int key,String data){
            this.key = key;
            this.data = data;
            this.leftChild = null;
            this.rightChild = null;
        }
    }  

    //测试
    public static void main(String[] args) {
        BinaryTree bt = new BinaryTree();
        bt.createBinTree(bt.root);
        System.out.println("the size of the tree is " + bt.size());
        System.out.println("the height of the tree is " + bt.height());  

        System.out.println("***递归实现****(前序遍历)[ABDECF]遍历*****************");
        bt.preOrder(bt.root);  

        System.out.println("***递归实现****(中序遍历)[DBEACF]遍历*****************");
        bt.inOrder(bt.root);  

        System.out.println("***递归实现****(后序遍历)[DEBFCA]遍历*****************");
        bt.postOrder(bt.root);  

        System.out.println("***非递归实现****(前序遍历)[ABDECF]遍历*****************");
        bt.nonRecPreOrder(bt.root);  

        System.out.println("***非递归实现****(中序遍历)[DBEACF]遍历*****************");
        bt.nonRecInOrder(bt.root);  

        System.out.println("***非递归实现****(后序遍历)[DEBFCA]遍历*****************");
        bt.noRecPostOrder(bt.root);
    }
}

美文美图

 


剑指Offer——二叉树的更多相关文章

  1. 剑指offer 二叉树中和为某一个值的路径

    剑指offer 牛客网 二叉树中和为某一个值的路径 # -*- coding: utf-8 -*- """ Created on Tue Apr 9 15:53:58 2 ...

  2. 剑指offer 二叉树的层序遍历

    剑指offer 牛客网 二叉树的层序遍历 # -*- coding: utf-8 -*- """ Created on Tue Apr 9 09:33:16 2019 @ ...

  3. JS数据结构与算法 - 剑指offer二叉树算法题汇总

    ❗❗ 必看经验 在博主刷题期间,基本上是碰到一道二叉树就不会碰到一道就不会,有时候一个下午都在搞一道题,看别人解题思路就算能看懂,自己写就呵呵了.一气之下不刷了,改而先去把二叉树的基础算法给搞搞懂,然 ...

  4. 剑指offer——二叉树的镜像

    题目:操作给定的二叉树,将其变换为源二叉树的镜像. 思路:前序(根左右的顺序)遍历一棵树,在存储的时候将其左右树进行交换,最后按照处理后的树还原,即得到其镜像. /** public class Tr ...

  5. 剑指Offer 二叉树中和为某一值的路径(dfs)

    题目描述 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.     思路: 递归,然后深搜,因为题目定义的, ...

  6. 剑指Offer 二叉树的镜像

    题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ ...

  7. 剑指Offer——二叉树的下一个结点

    题目描述: 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 分析: 如果该结点存在右子树,那么返回右子树的最左结 ...

  8. 剑指Offer——二叉树的深度

    题目描述: 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 分析: 二叉树的深度等于其左子树的深度和右子树的深度两个中最大的深 ...

  9. 剑指Offer——二叉树中和为某一值的路径

    题目描述: 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径. 分析: 先序遍历二叉树,找到二叉树中结点值的和 ...

随机推荐

  1. ●POJ 1113 Wall

    题链: http://poj.org/problem?id=1113 题解: 计算几何,凸包 题意:修一圈围墙把给出的点包围起来,且被包围的点距离围墙的距离不能小于L,求围墙最短为多少. 答案其实就是 ...

  2. AtCoder Beginner Contest 071 D - Coloring Dominoes

    Problem Statement We have a board with a 2×N grid. Snuke covered the board with N dominoes without o ...

  3. bzoj1233[Usaco2009Open]干草堆tower 单调队列优化dp

    1233: [Usaco2009Open]干草堆tower Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 983  Solved: 464[Submi ...

  4. bzoj4665小w的喜糖 dp+容斥

    4665: 小w的喜糖 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 120  Solved: 72[Submit][Status][Discuss] ...

  5. Linux下实现普通用户免密码登录【超详细】

    现有需求,需要把所有服务器的root和密码登录都禁用,只开放普通用户登录,这时需要给普通用户配置秘钥文件,实现无密码登录 如果普通用户需要root权限,在root用户下执行命令:visudo [roo ...

  6. css3中-moz、-ms、-webkit各什么意思

    1.-moz代表firefox浏览器私有属性 2.-ms代表ie浏览器私有属性 3.-webkit代表safari.chrome私有属性 这些是为了兼容老版本的写法,比较新版本的浏览器都支持直接写:b ...

  7. c#默认类的修饰符。

    类的默认修饰符是internal  当其修饰符为internal时,其他模块不能访问该类, 所以class类的修饰符一般为public

  8. C# IE浏览器

    引用Microsoft HTML Object Library 引用 -> com -> Microsoft HTML Object Library 引用后,显示如图 1.判断ie浏览器是 ...

  9. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

  10. @RequestBody注解用法

    做Java已经有8个多月了,但是基本没有学习过Java语言,因此在项目中写代码基本靠的是其他语言的基础来写Java代码,写出来的很多代码虽然能用,但是感觉很不地道,虽然从来没有同事说过,但是我自己觉得 ...