二叉树

  • 简述
  • 递归序(递归访问)
  • 非递归访问
  • 直观打印树
  • 题目一:求树的最大宽度(广度优先)
    • 层次遍历
  • 题目二:二叉树相关概念判断
    • 1. 如何判断是否为搜索二叉树(排序二叉树)
    • 2. 如何判断是否为完全二叉树
    • 3. 如何判断是否为满二叉树
    • 4. 如何判断是否为平衡二叉树
    • 5. 二叉树递归套路(树型DP,动态规划)
  • 题目三:俩节点最低公共祖先
  • 题目四:树的后继节点查找(含父亲节点)
  • 题目五:二叉树的序列化和反序列化
  • 题目六:折纸问题

简述

递归序(递归访问)

  • 递归:访问次序

递归序:1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1
先序:第一次在递归序出现就打印,第二,三次访问时无操作:1,2,4,5,3,6,7
中序:第二次在递归序出现就打印,第一,三次访问时无操作:4,2,5,1,6,3,7
后序:第三次在递归序出现就打印,第一,二次访问时无操作:4,5,2,6,7,3,1

public static void preOrderRecur(Node head){if (head == null){return;}System.out.println(head.value + " ");preOrderRecur(head.left);preOrderRecur(head.right);}public static void inOrderRecur(Node head){if (head == null){return;}inOrderRecur(head.left);System.out.println(head.value + " ");inOrderRecur(head.right);}public static void posOrderRecur(Node head){if (head == null){return;}posOrderRecur(head.left);posOrderRecur(head.right);System.out.println(head.value + " ");}

非递归访问

  • 非递归:通过手写栈实现
    1 先序:
    (1)从栈中弹出一个结点
    (2)打印(处理)该结点
    (3)先右后左(如果有)压入栈中(保证出栈是左,右)
    (4)重复,直至栈为空
public static void preOrderRecur(Node head){if (head != null){Stack<Node> stack = new Stack<>();stack.push(head);while (!stack.isEmpty()){Node p = stack.pop();System.out.println(p.value);if (p.right != null){//先压入右子树stack.push(p.right);}if (p.left != null){stack.push(p.left);}}}}

2 中序:
(1)每颗子树的左边界进栈
(2)依次弹出的过程中打印(处理),弹出结点的右子树(及其左边界)进栈
(3)重复1,2

public static void inOrderRecur(Node head){if (head != null){Stack<Node> stack = new Stack<Node>();Node p = head;while (!stack.isEmpty() || p != null){if (p != null){stack.push(p);p = p.left;}else {p = stack.pop();System.out.println(p.value + " ");p = p.right;}}}}

3 后序:需要一个收集栈
(1)从栈中弹出一个结点
(2)将该结点放入收集栈(父节点在收集栈底)
(3)先左后右(如果有)压入栈中(保证收集栈里是右,左)
(4)重复,直至栈为空
(5)打印(处理)收集栈(出栈是左,右,父)

public static void preOrderRecur(Node head){if (head != null){Stack<Node> stack = new Stack<>();Stack<Node> temp = new Stack<>(); //收集栈stack.push(head);while (!stack.isEmpty()){Node p = stack.pop();temp.push(p); //压入收集栈if (p.left != null){//先压入左子树stack.push(p.left);}if (p.right != null){stack.push(p.right);}}while (!temp.isEmpty()){Node p = temp.pop();System.out.println(p.value + " ");}}}

直观打印树

public static class Node{int value;Node left;Node right;public Node(int date){//构造函数this.value = date;}}public static void printTree(Node head){System.out.println("Binary Tree");printInOrder(head, 0, "H", 12);System.out.println();}public static void printInOrder(Node head, int height, String to, int len){//head 头结点//height 目前深度//to 表示关系,H:头结点;↖:表示父节点在其左上;↙:表示父节点在其左下//len 树显示长度的控制,可自行调整if (head == null){return;}printInOrder(head.right, height + 1, "↙", len);String val = to + head.value + to;int lenM = val.length();int lenL = (len - lenM) / 2;int lenR = len - lenM - lenL;val = getSpace(lenL) + val + getSpace(lenR);System.out.println(getSpace(height * len) + val);printInOrder(head.left, height + 1, "↖" , len);}public static String getSpace(int num){String space = " ";StringBuilder buf = new StringBuilder(" ");for (int i = 0; i < num; i++){buf.append(space);}return buf.toString();}public static void main(String[] args) {Node head = new Node(1);head.left = new Node(2);head.right = new Node(3);head.left.left = new Node(4);head.left.right = new Node(5);head.right.left = new Node(6);head.right.right = new Node(7);head.left.right.right = new Node(8);head.right.right.left = new Node(9);printTree(head);}

理想图:

效果图:H:头结点;↖:表示父节点在其左上;↙:表示父节点在其左下

题目一:求树的最大宽度(广度优先)

层次遍历

public static void levelOrder(Node head){if (head == null){return;}Queue<Node> queue = new LinkedList<Node>();Node p = head;queue.add(p);while (!queue.isEmpty()){p = queue.poll();if (p.left != null){queue.add(p.left);}if (p.right != null){queue.add(p.right);}}}
  • 笔试代码:
    (1)思路:三个值,当前结点所在层,每层中的结点个数,树中结点最多的层
    (2)实现方法:结合层次遍历,使用HashMap使得每个结点与对应的层绑定。出队时,将对应层数的结点加一,最后找到结点最多的层即可
public static int levelMax(Node head){if (head == null){return 0;}Queue<Node> queue = new LinkedList<Node>();int level = 1; //层数int levelNum = 0; //该层的结点数int nodeMax = 0; //最多结点个数HashMap<Node, Integer> map = new HashMap<>();Node p = head;map.put(p, 1);queue.add(p);while (!queue.isEmpty()){p = queue.poll();if (map.get(p) == level){//当前结点层与要记录层相同levelNum++;}else {//当前结点层与要记录层不相同,进入下一层,更新数据nodeMax = Math.max(nodeMax, levelNum); //最大值更新level++; //层数更新levelNum = 1; //重置对应层的结点数}if (p.left != null){map.put(p.left, level + 1);queue.add(p.left);}if (p.right != null){map.put(p.right, level + 1);queue.add(p.right);}}return  nodeMax;}
  • 面试思路:
    (1)思路:四个值,当前层的结束结点,当前层下一次的结束结点,每层中的结点个数,树中结点最多的层
    (2)实现方式:结合层次遍历,第一层,该层的结束结点就是head,到了第二层将在第一层记录的下一层结束结点作为当前层的结束结点,在出,入对的同时更新下一次的结束结点,同时记录每层中的结点个数。
public static int levelMax(Node head){if (head == null){return 0;}Queue<Node> queue = new LinkedList<Node>();Node levelEnd = head; //本层结束结点Node levelNextEnd = null; //下一次结束结点int levelNum = 0; //该层的结点数int nodeMax = 0; //最多结点个数Node p = head;queue.add(p);while (!queue.isEmpty()){p = queue.poll();if (p.left != null){//时刻更新levelNextEndlevelNextEnd = p.left;queue.add(p.left);levelNum++;}if (p.right != null){levelNextEnd =p.right;queue.add(p.right);levelNum++;}if (p == levelEnd){//遍历到该层最后一个结点,数据更新levelEnd = levelNextEnd;nodeMax = Math.max(levelNum, nodeMax);levelNum = 0;levelNextEnd = null;}}return  nodeMax;}

题目二:二叉树相关概念判断

1. 如何判断是否为搜索二叉树(排序二叉树)

思路:中序遍历二叉树得到的序列,若为升序序列则为搜索二叉树

递归中序:public static void inOrderRecur(Node head){if (head == null){return;}inOrderRecur(head.left);System.out.println(head.value + " ");inOrderRecur(head.right);}
代码修改:
public static int preValue = Integer.MIN_VALUE;public static boolean checkBST(Node head){if (head == null){return true;}if (!checkBST(head.left)){//递归左子树return false;}if (head.value <= preValue){//右边 <= 左边,不满足BSTreturn false;}else {preValue = head.value;}return checkBST(head.right);}
非递归中序:
public static void inOrderRecur(Node head){if (head != null){Stack<Node> stack = new Stack<Node>();Node p = head;while (!stack.isEmpty() || p != null){if (p != null){stack.push(p);p = p.left;}else {p = stack.pop();System.out.println(p.value + " ");p = p.right;}}}}
代码修改:
public static boolean checkBST(Node head){if (head != null){Stack<Node> stack = new Stack<Node>();Node p = head;int preValue = Integer.MIN_VALUE;while (!stack.isEmpty() || p != null){if (p != null){stack.push(p);p = p.left;}else {p = stack.pop();if (p.value <= preValue){return false;}else {preValue = p.value;}p = p.right;}}}return true;}

总结:两种均为动态检查,也可先将二叉树的序列存入list类的集合中,在判断序列是否有序

2. 如何判断是否为完全二叉树

思路:广度优先(层次遍历),判断1:只要有结点是有右孩子,而无左孩子就返回false;判断2:从第一个子树不完整的结点开始,其后结点都要为叶子结点

public static boolean isCBT(Node head){if (head == null){return true;}Queue<Node> queue = new LinkedList<Node>();boolean flag = false; //表示是否遇到了子树不完整的结点Node p = head;queue.add(p);while (!queue.isEmpty()){p = queue.poll();if ((p.left == null && p.right != null) || flag && (p.left != null && p.right != null)){//不为满二叉树的条件return false;}if (p.left != null){queue.add(p.left);}if (p.right != null){queue.add(p.right);}if (p.left == null || p.right == null){//子树不完整flag = true;}}return true;}

3. 如何判断是否为满二叉树

思路:统计二叉树的深度L,节点数为N,若N = 2 ^ L - 1,则为满二叉树
方法:递归调用,求左子树的L,N;求右子树的L,N;求该树的L,N

public static boolean isFull(Node head){ReturnType returnType = process(head);//1 << x, 2的x次方return returnType.num == (1 << returnType.height - 1);}public static class ReturnType{//自定义类型public int height; //树的高度public int num; //节点数public ReturnType(int height, int num){//构造函数this.height = height;this.num = num;}}public static ReturnType process(Node head){if (head == null){return new ReturnType(0, 0);}ReturnType leftTree = process(head.left); //递归调用ReturnType rightTree= process(head.right);int height = Math.max(leftTree.height, rightTree.height) + 1; //高度int num = leftTree.num + rightTree.num + 1; //节点数return new ReturnType(height, num);}

4. 如何判断是否为平衡二叉树

思路:平衡二叉树条件:(1)左子树平衡,左高度;(2)右子树平衡,右高度;(3)|左高度 - 右高度| <= 1
结合递归实现

    public static boolean isBalancedTree(Node head){return process(head).isBalanced;}public static class ReturnType{//自定义类型public boolean isBalanced; //树是否平衡public int height; //树高public ReturnType(boolean isBalanced,int height){//构造函数this.isBalanced = isBalanced;this.height = height;}}public static ReturnType process(Node head){if (head == null){//空树平衡,高度0return new ReturnType(true, 0);}ReturnType leftTree = process(head.left); //递归调用ReturnType rightTree= process(head.right);int height = Math.max(leftTree.height, rightTree.height) + 1;boolean isBalanced = leftTree.isBalanced && rightTree.isBalanced&& Math.abs(leftTree.height - rightTree.height) < 2;return new ReturnType(isBalanced, height);}

5. 二叉树递归套路(树型DP,动态规划)

思路:在判断二叉树是什么树时?分解为左子树是否满足,右子树是否满足,左右子树合并后是否满足(树型DP)

  1. 二叉树递归套路:判断是否为搜索二叉树
    (1)左树搜索,左的max,min;(2)右树搜索,右的min,max;(3)左max < head.value < 右min
 public static boolean isBSTree(Node head){return process(head).isBST;}public static class ReturnType{//自定义类型public boolean isBST; //树是否为搜索树public int min; //树的最小值public int max; //树的最大值public ReturnType(boolean isBST,int min, int max){//构造函数this.isBST = isBST;this.min = min;this.max = max;}}public static ReturnType process(Node head){if (head == null){//空树的min,max不好设置,直接设置为nullreturn null;}ReturnType leftTree = process(head.left); //递归调用ReturnType rightTree= process(head.right);int min = head.value; //min,max的设置int max = head.value;if (leftTree != null){min = Math.min(min, leftTree.min);max = Math.max(max, leftTree.max);}if (rightTree != null){min = Math.min(min, rightTree.min);max = Math.max(max, rightTree.max);}boolean isBST = true; //isBST设置if (leftTree != null && (leftTree.isBST || leftTree.max > head.value)){//左不为空的条件下:左子树不是搜索二叉树或左的max > head,value//不满足搜索二叉树isBST = false;}if (rightTree != null && (rightTree.isBST || rightTree.min < head.value)){isBST = false;}return new ReturnType(isBST, min, max);}
  1. 在判断满二叉树,平衡二叉树时都是此方法

题目三:俩节点最低公共祖先

  • 笔试思路:
    (1)使用HashMap建立,每个节点与其父节点的关系
    (2)使用HashSet存储,node1所有的祖先节点
    (3)依次找node2的祖先节点,并在node1的HashSet中寻找是否有相同的,当遇到第一个相同的便是最低公共祖先节点
    public static Node lowCommonAncestor(Node head, Node node1, Node node2){HashMap<Node, Node> fatherMap = new HashMap<Node,Node>();fatherMap.put(head, head); //头节点的父节点为自己process(head, fatherMap);HashSet<Node> setNode1 = new HashSet<Node>(); //建立node1的祖先节点集Node p = node1;while (p != fatherMap.get(head)){//从当前节node1点从下往上遍历头节点setNode1.add(p);p = fatherMap.get(p);}p = node2;while (p != fatherMap.get(head)){//从当前节node2点从下往上遍历头节点if (setNode1.contains(p)){//有相同就是最低公共祖先节点return p;}p = fatherMap.get(p);}return null;}public static void process(Node head, HashMap<Node, Node> fatherMap){//节点与父节点关系的建立if (head == null){return;}fatherMap.put(head.left, head);fatherMap.put(head.right, head);process(head.left, fatherMap);process(head.right, fatherMap);}
  • 面试思路:
    情况分析:
    (1)node1是node2的祖先节点,或node2是node1的祖先节点
    (2)node1与node2不互为祖先节点,需要向上遍历才能找到
    方法:遍历树节点,碰到node1,node2返回对应节点,其余的节点碰到都只是返回空
    情况一:

情况二:

    public static Node lowCommonAncestor(Node head, Node node1, Node node2){if (head == null || head == node1 || head == node2){//node1,node2时返回对应值return head;}Node left = lowCommonAncestor(head.left, node1, node2);Node right = lowCommonAncestor(head.right, node1, node2);if (left != null && right != null){//情况二,两个节点互不为公共祖先//则同时左右返回为node1,node2的就是最低公共祖先return head;}//碰到node1,node2的其中一个时返回不空的return left != null ? left : right;}

题目四:树的后继节点查找(含父亲节点)


后继节点:在中序遍历的序列中,node的下一个节点叫作node的后继节点
情况分析:
(1)node有右子树,则后继节点就是右子树最左边的节点
(2)node无右子树,则后继结点就是看node是那个结点左子树最右边的结点(判断当前节点是否是其祖先节点的左子树)

    public static Node getSuccessorNode(Node node){if (node == null){return node;}if (node.right != null){//当前节点存在右子树return getLeftNode(node);}else {//无右子树,通过父节点查找Node parent = node.parent;while (parent != null && parent.left != node){//该节点是否为父节点的左子树node = parent;parent = node.parent;}return parent;}}public static Node getLeftNode(Node node){if (node == null){return node;}while (node.left != null){node = node.left;}return node;}

题目五:二叉树的序列化和反序列化


思路:使用 “" 表示相隔节点之间的表示方式;使用 "#” 表示空结点

先序序列:序列化public static String serialByPre(Node head){if (head == null){return "#_";}String str = head.value + "_";str += serialByPre(head.left);str += serialByPre(head.right);return str;}
反序列化public static Node reconByPreString(String preStr){//将字符串分割,存入队列String[] values = preStr.split("_");Queue<String> queue = new LinkedList<String>();for (int i = 0; i < values.length; i++){queue.add(values[i]);}return reconPreOrder(queue);}public static Node reconPreOrder(Queue<String> queue){//通过队列,还原树String value = queue.poll();if (value.equals("#")){return null;}Node head = new Node(Integer.valueOf(value));head.left = reconPreOrder(queue);head.right = reconPreOrder(queue);return head;}

题目六:折纸问题


问题分析:

思路:找到折纸的规律,使用树的方式来表示。父节点与左子树都是凹,右子树都是凸
方法:给定N,确定树的高度,模拟树的中序遍历便可以得到折痕方向

    public static void printAllFolds(int N){printProcess(1, N, true);}public static void printProcess(int height, int N, Boolean down){//模拟树的中序遍历//height当前高度,N为树高//down = true -> 凹,down = false ->凸if (height > N){return;}printProcess(height + 1, N, true);System.out.println(down ? "凹" : "凸");printProcess(height + 1, N, false);}

算法设计与分析 二叉树相关推荐

  1. 算法设计与分析:二叉树问题的解决思路

    文章目录 前言 二叉树的基本思想 简单题 合并二叉树 二叉树的坡度 中等题 二叉树剪枝 总结 前言 二叉树是数据结构中使用频率非常高的一种了,凭借高实用性和效率成为数据结构中必须攻克的一环.关于二叉树 ...

  2. C/C++ 算法设计与分析实验报告

    算法设计与分析实验报告 算法实验整体框架的构建 菜单代码块 选择函数代码块 主函数代码块 实验模块Ⅰ:算法分析基础--Fibonacci序列问题 实验解析 Fibonacci序列问题代码块 实验模块Ⅱ ...

  3. 算法设计与分析-----贪心法

    算法设计与分析-----贪心法(c语言) 一.贪心法 1.定义 2.贪心法具有的性质 1.贪心选择性质 2.最优子结构性质 3.贪心法的算法框架 5.求解活动安排问题 6.求解最优装载问题 二. 贪心 ...

  4. 【算法】算法设计与分析试题(含答案)

    算法设计与分析试题 (中国科学院大学-陈玉福-2011秋) 一. 回答下列问题: (每小题5分) 1.陈述算法在最坏情况下的时间复杂度和平均时间复杂度:这两种评估算法复杂性的方法各自有什么实际意义? ...

  5. 算法设计与分析——树

    分类目录:<算法设计与分析>总目录 本文中,我们专门讨论用链式数据结构表示有根树的问题.我们将首先讨论二叉树,然后给出针对结点的孩子数任意的有根树的表示方法. 树的结点用对象表示.与链表类 ...

  6. 算法设计与分析基础-笔记-上

    算法设计与分析基础 绪论 什么是算法 一系列解决问题的明确指令,对于符合一定规范的输入,能够在有限的时间内获得要求的输出. 例子:最大公约数:俩个不全为0 的非负整数 m m m和 n n n的最大公 ...

  7. 天津理工大学研究生学位课《算法设计与分析》期末大作业

    2022- 2023学年度第一学期 研究生学位课< 算法设计与分析 > 期末大作业 2022级电子信息天理研究生 一.简答题 1.若,写出用Θ.Ω和О描述f(n) 的渐进表达.(7分) 答 ...

  8. 算法设计与分析课程的时间空间复杂度

    算法设计与分析课程的时间空间复杂度: 总结 算法 时间复杂度 空间复杂度 说明 Hanoi $ O(2^n) $ $ O(n) $ 递归使用 会场安排问题 \(O(nlogn)\) \(O(n)\) ...

  9. 哈工大威海算法设计与分析_计算机算法设计与分析第一章 算法概述

    晓强Deep Learning的读书分享会,先从这里开始,从大学开始.大家好,我是晓强,计算机科学与技术专业研究生在读.我会不定时的更新我的文章,内容可能包括深度学习入门知识,具体包括CV,NLP方向 ...

最新文章

  1. php工程模式,PHP设计模式(八):工厂模式
  2. Mybatis+Spring SqlSessionTemplate注入学习--1
  3. python动态心形代码-Python数学方程式画心型图案源码示例
  4. 微信小程序外卖增长402%,茶饮下单最活跃
  5. 内存泄露一个经典例子
  6. python gdb coredump_Linux段错误及GDB Coredump调试方法
  7. 深入理解 JVM Class文件格式(五)
  8. Codeforces Round #741 (Div. 2) E. Rescue Niwen! 字符串 + dp
  9. vue实现进度条隐藏_实现带有进度条的Vue延迟加载
  10. BZOJ 1030 [JSOI2007]文本生成器(ac自动机+dp)
  11. 大喇叭疫情防控广播解决方案
  12. 7-2统计素数并求和(20分)
  13. java把图片导出ppt_ppt转图片程序(java实现)
  14. 使用tftpd32.exe升级IPC
  15. OSPF-LSA详解
  16. 阿木P230无人机指点飞行实验记录
  17. 如何实现视觉识别颜色
  18. 创智汇集,汉韵流芳!大创智国风汉服赏与您相约十月
  19. Linux学习:网站服务
  20. JAVA 基础 / 第十九课: 数组 / JAVA 如何创建一个数组

热门文章

  1. NICO dataset
  2. python图灵_python图灵机器人
  3. 超轻薄笔记本电脑软件测试,2009年度-13英寸超轻薄笔记本年度横向评测
  4. RISC-V 和 Cortex-M 的异同
  5. 计算机中丢失ubiorbitapi,我的刺客信条出现无法启动此程序,因为计算机中丢失ubiorbitapi_r2_loader.dll,,怎么处理。。...
  6. dnn降噪_芯片量产已超百万,「探境科技」发布AI双麦降噪语音识别方案
  7. Lucene之——搜索实例
  8. 分析数据的软件有哪些?这几款数据分析软件不用会后悔
  9. 仙侠游戏改编电视剧为什么很火?
  10. maven安装到私服和从私服下载