剑指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. iOS 隐藏导航条分割线

    // 导航条分割线 @property (nonatomic, strong) UIView *navSeparateView; // 获取导航条分割线 UIView *backgroundView ...

  2. 前端工程师:电信专业转前端是如何拿到阿里、腾讯offer的?

    1.个人情况 ● 211本科 985硕士 电信专业 女生 ● 16年3月开始学习前端 ● 16年7月开始实习,共五家实习经历(不是特别厉害的厂) ● 秋招拿到两个offer(阿里.腾讯).没错只有这两 ...

  3. Linux 下查看tomcat版本

    1.echo $JAVA_HOME 2.进入jdk路径 3.java -version

  4. Echarts 中国地图各个省市自治区自定义颜色

    前言 最近接了一个外包的项目,其中有个需求是这样的, 需要展示一个中国的统计地图,要求每个省市区都是不一样的颜色,必须特别区分开.以及隐藏南海部分. 看了Echats相关文档,发现有类似的demo,但 ...

  5. ajax跨域请求问题及解决办法总结

    1.浏览器的同源策略及规避方法 目前,所有浏览器都实行同源政策.即协议.域名.端口都相同的URI称为"同源".不同源的url之间: a.无法读取cookie.localstorag ...

  6. Python中如何自定义一个计时器

    import time as t class MyTimer(): # 初始化构造函数 def __init__(self): self.prompt = "未开始计时..." s ...

  7. python类(class)中参数self的解释说明

    python类(class)中参数self的简单解释 1.self只有在类的方法中才会有,其他函数或方法是不必带self的. 2.在调用时不必传入相应的参数.3.在类的方法中(如__init__),第 ...

  8. delphi 验证码识别(XE8源码)

    如题:源码下载

  9. Node.js 常用工具util

    util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. util.inherits util.inherits(constructor ...

  10. NDK编程的一个坑—Arm平台下的类型转换

    最近在做DNN定点化相关的工作,DNN定点化就是把float表示的模型压缩成char表示,虽然会损失精度,但是由于DNN训练的模型值比较接近且范围较小,实际上带来的性能损失非常小.DNN定点化的好处是 ...