import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stac

2023-05-16

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;

/*
  Created by Flynnon on 17-2-25.
  对二叉树的递归定义、前序、后序、中序、层序遍历方法的归纳
 */

/**
 * 定义节点类
 * 为了简单就不定义getter/setter方法了
 */
class Node {
    public int value;
    public Node left;
    public Node right;

    public Node() {
        this(0);
    }

    public Node(int v) {
        this.value = v;
        this.left = null;
        this.right = null;
    }
}

/**
 * 对二叉树进行操作的工具类
 */
class PrintBinaryTree {

    //私有化构造函数
    private PrintBinaryTree(){
        throw new RuntimeException("该工具类不应该被实例化");
    }

    /**
     * 层序遍历二叉树(每一行从左到右,整体上从上到下)
     * 主要思路:利用队列先进先出的性质保存顺序
     * @param root 要遍历的二叉树的根节点
     */
    public static void levelTraversal(Node root) {
        Queue<Node> q = new LinkedList<>();
        q.add(root);
        while (!q.isEmpty()) {
            Node temp = q.poll();
            if (temp != null) {
                System.out.print(temp.value + "  ");
                q.add(temp.left);
                q.add(temp.right);
            }
        }
    }

    /**
     * 前序遍历二叉树(递归) root->left->right
     * @param root 要遍历的二叉树的根节点
     */
    public static void preOrderRec(Node root) {
        if (root == null) {
            return;
        }
        System.out.print(root.value + "  ");
        preOrderRec(root.left);
        preOrderRec(root.right);
    }

    /**
     * 前序遍历二叉树(非递归) root->left->right
     * 主要思路:利用栈保存未打印的节点,然后逐个出栈处理,在此过程中更新栈
     * @param root 要遍历的二叉树的根节点
     */
    public static void preOrderUnRec(Node root) {
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        Node temp;
        while (!stack.empty()) {            //root==null时,只会空转一个循环,因此无需判断
            temp = stack.pop();
            if (temp != null) {
                System.out.print(temp.value + "  ");
                stack.push(temp.right);     //注意:这里的入栈顺序是先right后left
                stack.push(temp.left);      //     以保证从栈中取出时为先left后right
            }
        }
    }

    /**
     * 后序遍历二叉树(递归)
     * @param root 要遍历的二叉树的根节点
     */
    public static void postOrderRec(Node root) {
        if (root == null) {
            return;
        }
        postOrderRec(root.left);
        postOrderRec(root.right);
        System.out.print(root.value + "  ");
    }

    /**
     * 后序遍历二叉树(非递归) left->right->root
     * 主要思路:利用栈保存未打印的节点,然后逐个出栈,进行判断,并根据需要更新栈
     *         因为是处理完左右子树后,再处理根(回溯),所以需要一个记录上一个被打印的节点的引用
     * @param root 要遍历的二叉树的根节点
     */
    public static void postOrderUnRec(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        //cur:当前节点  pre:上一个被打印的节点
        Node cur, pre = null;
        while (!stack.empty()) {
            //查看(不是取出)栈顶的结点
            cur = stack.peek();
            //如果当前结点没有孩子结点(叶子节点)
            //或者孩子节点都已被打印过(这里不可能出现有两个子节点却只打印了其中一个的情况)
            //说明该打印当前节点了
            if ((cur.left == null && cur.right == null) ||
                    (pre != null && (pre == cur.left || pre == cur.right))) {
                System.out.print(cur.value + "  ");  //打印当前结点
                stack.pop();                         //被打印的节点(当前节点)出栈
                pre = cur;                           //更新pre的值
            } else {
                if (cur.right != null)          //若未轮到当前节点,将当前节点的右节子点、左子节点入栈
                    stack.push(cur.right);      //注意:这里的入栈顺序是先right后left
                if (cur.left != null)           //     以保证从栈中取出时为先left后right
                    stack.push(cur.left);
            }
        }
    }

    /**
     * 中序遍历二叉树(递归)
     * @param root 要遍历的二叉树的根节点
     */
    public static void inOrderRec(Node root) {
        if (root == null) {
            return;
        }
        inOrderRec(root.left);
        System.out.print(root.value + "  ");
        inOrderRec(root.right);
    }

    /**
     * 中序遍历二叉树(非递归) left->root->right
     * 主要思路:模拟递归的过程,将左子树节点不断的压入栈,直到左叶子,然后处理栈顶节点的右子树
     * @param root 要遍历的二叉树的根节点
     */
    public static void inOrderUnRec(Node root) {
        Stack<Node> stack = new Stack<>();
        Node cur = root;                    //纯粹是为了好看...JVM会优化
        while (cur != null || !stack.isEmpty()) {  //当root==null时,不会进入循环,因此无需判断
            while (cur != null) {           //从当前节点开始,从上到下将最左边的那一列节点入栈
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();              //取出栈顶的节点(该节点左节点为null,因此现在该打印它)
            System.out.print(cur.value + "  ");
            cur = cur.right;                //定位到已打印的节点的右子节点
        }
    }

    /**
     * 前序递归构造二叉树 root->left->right
     * @param scanner 输入流,用于读取节点值
     * @return 构造完成的二叉树的根节点
     */
    public static Node createTreeNode(Scanner scanner) {
        assert scanner!=null;
        Node root = null;                 //声明当前根节点
        int data = scanner.nextInt();
        if (data > 0) {                             //若当前节点存在(这里为了简单以负数为占位符)
            root = new Node(data);                  //使用其它顺序构造二叉树,只需更改这三句即可
            root.left = createTreeNode(scanner);
            root.right = createTreeNode(scanner);
        }
        return root;
    }
}

/**
 * 测试类
 */
public class BinaryTree{
    // 这里以节点值分别为1-7的满二叉树为例
    // 1 2 4 -1 -1 5 -1 -1 3 6 -1 -1 7 -1 -1
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Node root = PrintBinaryTree.createTreeNode(sc);
        sc.close();
        System.out.print("层序遍历:");
        PrintBinaryTree.levelTraversal(root);
        System.out.print("\n中序递归遍历:");
        PrintBinaryTree.inOrderRec(root);
        System.out.print("\n中序非递归遍历:");
        PrintBinaryTree.inOrderUnRec(root);
        System.out.print("\n前序递归遍历:");
        PrintBinaryTree.preOrderRec(root);
        System.out.print("\n前序非递归遍历:");
        PrintBinaryTree.preOrderUnRec(root);
        System.out.print("\n后序递归遍历:");
        PrintBinaryTree.postOrderRec(root);
        System.out.print("\n后序非递归遍历:");
        PrintBinaryTree.postOrderUnRec(root);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stac 的相关文章

随机推荐

  • 灵活的按键处理程序 FlexibleButton,C程序编写,无缝兼容任意的处理器,支持任意 OS 和 non-OS

    灵活的按键处理程序 FlexibleButton 前言概述获取测试DEMO 程序说明程序入口用户初始化代码事件处理代码 FlexibleButton 代码说明按键事件定义按键注册接口按键事件读取接口按键扫描接口 问题和建议 前言 正好工作中
  • http请求头header、请求体body、请求行介绍

    HttpServletRequest对象代表客户端的请求 当客户端通过http协议请求访问 服务器的时候 http请求头的所有信息都封装在这个对象中 xff0c 通过这个对象 xff0c 可以获取客户端请求的所有信息 http请求包含请求行
  • 理解字节序 大端字节序和小端字节序

    以下内容参考了 http www ruanyifeng com blog 2016 11 byte order html https blog csdn net yishengzhiai005 article details 3967252
  • opencvSharp 学习笔记(二)

    参考文章 xff1a https github com shimat opencvsharp samples tree master SamplesCS Samples 参考opencvsharp的官方sample xff0c 在vs201
  • C++局部对象的析构

    class A span class hljs keyword public span A Func span class hljs attribute span span class hljs attribute span A A spa
  • BIND中基数树的建立

    BIND9新引入了视图的概念 xff0c 简单的来讲就是能根据不同的来源IP来返回不同的数据 其中网段的存储 xff0c 网段的快速匹配都是用基数树来实现的 下面是BIND创建基数树的代码 BIND的IP地址结构 span class hl
  • HTTP协议解析及C/C++代码实现

    超文本传输协议 HTTP 是分布式 协作 超媒体信息系统的应用层协议 这是自 1990 年以来万维网数据通信的基础 HTTP 是一种通用且无状态的协议 xff0c 它可以用于其他目的 xff0c 也可以使用其请求方法 错误代码和标头的扩展
  • C语言发邮件(带账号密码认证),简单的libesmtp实例

    需要安装libesmtp开发环境 xff0c centos下可以用yum安装 yum install libesmtp devel 编译时加上 lesmtp选项 xff0c 账号密码等替换成自己的 gcc o mail mail c les
  • 怎样在Markdown中贴图片

    前言 Markdown真的是一个很优秀的文本标记语言 语法也很简单 熟悉之后基本上可以完全抛弃Word了 用来写文档 一些博客 再好不过了 可是Markdown还是有一个痛点 那就是不大好贴图片 贴图 怎么样在markdown中贴图就不多说
  • 【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析

    1 VLC中有很多demux mux encoder decoder模块 xff0c 因此需要先了解这些模块的加载原理 xff0c 模块的加载原理基本一致 xff0c 因此举例分析MP4解复用模块如何加载完成的 xff0c 主要流程如下 x
  • vs2013 设置不显示输出窗口

    工具 选项 项目与解决方案 常规 取消 在生成开始时显示输出窗口 的勾选
  • @Param注解的用法解析

    实例一 64 Param注解单一属性 dao层示例 Public User selectUser 64 param userName String name 64 param userpassword String password xml
  • mybatis choose标签的使用

    有时候我们并不想应用所有的条件 xff0c 而只是想从多个选项中选择一个 而使用if标签时 xff0c 只要test中的表达式为 true xff0c 就会执行 if 标签中的条件 MyBatis 提供了 choose 元素 if标签是与
  • Socket长连接实现思路

    长连接的正确实现方式 1 不关闭流实现长连接 xff1f 流关闭了而不关闭Socket xff0c 还是无法达到长连接的效果的 xff0c 所以 xff0c 要长连接 xff0c 流必须不能关闭 xff01 那么 xff0c 是不是直接不关
  • com.jacob.com.ComFailException: VariantChangeType failed

    调用jacob组件出错 com jacob com ComFailException VariantChangeType failed 在C Windows System32 config systemprofile下创建文件夹Deskto
  • CRC8校验 java实现

    以下为CRC8的实现 span class hljs keyword package span server span class hljs javadoc CRC8相关计算 encode utf 8 span class hljs jav
  • Java list add方法和addAll方法效率

    结论是 在数据量较小时 add方法配合for循环遍历比addAll来得快 但是在大量数据时 addAll的方法的效率更高 list addAll 是浅拷贝 只是将内存中的地址进行了拷贝 指向了原先list的末尾做了拼接
  • STM32——USART1重映射

    前言 为了使不同器件封装的外设 IO 功能数量达到最优 xff0c 可以把一些复用功能重新映射到其他一些引脚上 STM32 中有很多内置外设的输入输出引脚都具有重映射 remap 的功能 我们知道每个内置外设都有若干个输入输出引脚 xff0
  • Pg数据库比较时间大小

    postgresql 比较两个时间差大于 N个小时 摘要 PG 中时间想减后为interval xff0c 比较两个时间大于某个小时或者分钟等可以直接通过interval来实现 example1 xff1a 判断两个时间差大于4个小时 se
  • import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stac

    span class hljs keyword import span java util LinkedList span class hljs keyword import span java util Queue span class