3.8 二叉树中结点最大的距离 重建二叉树 顺序遍历二叉树

2024-05-28 15:58

本文主要是介绍3.8 二叉树中结点最大的距离 重建二叉树 顺序遍历二叉树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述

这里写图片描述

这里写图片描述

这里写图片描述

3. 问题分析

对于树的相关问题, 递归是非常常见的

对于第一个问题 : 可以通过先递归获取每一个结点的最大左子树的深度, 以及最大右子树的深度, 然后在递归一次获取(node.maxLeft + node.maxRight)的最大值即为所求

对于第二个问题, 因为我们现在有一个先序, 中序的遍历序列, 所以我们可以确定root结点即为先序遍历的第一个结点, 然后根据中序序列来确定root结点的左右子树, 然后在递归建树即可

对于第三个问题 , 通常是使用一个队列来维护需要遍历的结点队列, 遍历一个结点之后, 就将其左右孩子结点添加到队列中, 遍历啊遍历, 直到队列为空


对于上面的描述, 可能比较抽象, 下面是几幅图, 希望可以帮助理解

问题一 :
1 给定的树
这里写图片描述

2 递归获取树的最大左右子树深度
这里写图片描述

3 递归获取树的结点最大距离
这里写图片描述

问题二 :
1 前序和中序
firstOrder : 这里写图片描述
infixOrder : 这里写图片描述

2 判断根节点, 左右子树
根据firstOrder判断根节点 : 这里写图片描述
根据中序判断左右子树 : 这里写图片描述
在根据左右子树的节点数, 分解前序中的左右子树 : 这里写图片描述

确定左右子树的前序, 中序遍历序列之后, 即可递归建树了

问题三 :
1 给定的树
这里写图片描述

2 将当前结点的左右结点添加到队列, 进队的顺序
这里写图片描述

进队的顺序就保证了层次关系, 因而层次遍历二叉树得以实现

4. 代码

备注 : 先序构建树, “#“结点表示空节点, 上面的问题三的图形对应的数的表示为 : a b # d # # c f # # e # #
代码清单 :
1 先序建树
2 先序 + 中序, 中序 + 后序, 重建二叉树 [一种思路是上面的思路, 另一种思路是先补全其先序序列[加上空结点], 然后在利用先序建树(1)[注意 : 中序 + 后序重建树, 需要先构建右子树, 然后在构建左子树 ], 不过原理和第一种基本上一致 ]
3 判断给定的先序 + 中序, 是否能够构建一颗二叉树
4 获取指定层级的结点
5 层次遍历, 层次自底向上遍历二叉树

/*** file name : Test09FindMaxDisInBinaryTree.java* created at : 8:56:46 AM May 29, 2015* created by 970655147*/package com.hx.test04;public class Test09FindMaxDisInBinaryTree {// 1. 获取一颗二叉树的两个节点的最大距离// 2. 重建二叉树// 3. 判断给定的(前序, 中序)[(中序, 后序) ]  是否能够成一个合法的二叉树// 4. 获取二叉树的某一层的元素// 5. 顺序遍历二叉树 层次遍历二叉树public static void main(String []args) {//      BinTree bt = BinTree.createABinTree();
//      Log.log(bt);// -----------获取一颗二叉树的两个节点的最大距离---------------  String data = "58 34 12 # # 42 # # 78 68 69 # # # 99 # #";BinTree bt = BinTree.createABinTree(data);Log.log(bt);List<Integer> deepth = new ArrayList<Integer>();bt.headTraverseForMaxDepth(bt.root, 0, deepth);Log.log(deepth );// 相信能获取到这个 数据的话应该知道  该怎么获取最大深度了吧,,int maxDis = bt.getMaxDis(bt.root);Log.log("maxDistance : " + maxDis);// -----------重建二叉树---------------  String data02 = "a b # d # # c f # # e # #";String firstOrder = "a b d c e f";String infixOrder = "d b a e c f";String epilogue = "d b e f c a";//      BinTree bt02 = BinTree.rebuildForFirstOrderAndInfixOrder(firstOrder, infixOrder);
//      BinTree bt02 = BinTree.rebuildForFirstOrderAndInfixOrder02(firstOrder, infixOrder);
//      BinTree bt02 = BinTree.rebuildForInfixOrderAndEpilogue(infixOrder, epilogue);BinTree bt02 = BinTree.rebuildForInfixOrderAndEpilogue02(epilogue, infixOrder);Log.log(bt02);// -----------判断给定的(前序, 中序)[(中序, 后序) ]  是否能够成一个合法的二叉树---------------    String[] first = firstOrder.split("\\s+");String[] infix = infixOrder.split("\\s+");Log.log(BinTree.canCompleteForFirstOrderAndInfixOrder(first, infix, 0, 0, first.length) );// -----------获取二叉树的某一层的元素---------------BinTree.getSpecifiedLayerElements(bt.root, 2);Log.enter();// -----------顺序遍历二叉树 层次遍历二叉树---------------BinTree.traverseInOrder(bt.root);BinTree.traverseInOrder02(bt.root);BinTree.traverseInOrderInversely(bt.root);BinTree.traverseInOrderInversely02(bt.root);// -----------splits---------------}// 一颗二叉树public static class BinTree {// 根节点Node root;// 初始化public BinTree() {root = new Node();}public BinTree(Node root) {this.root = root;}// 获取root节点public Node root() {return root;}// 判断结点的左结点是否为空[#也算为空]public static boolean isNodeNull(Node node) {return (node == null) || (Node.NULL.equals(node.getData()) );}// 根据用户的输入创建一颗二叉树public static BinTree createABinTree() {BinTree bt = new BinTree();createNode(bt.root, "root");return bt;}// 根据指定格式的字符串创建一颗二叉树public static BinTree createABinTree(String data) {return createABinTreeReversely(data);}// 根据指定格式的字符串创建一颗二叉树  先建立左子树  在建立右子树public static BinTree createABinTreeReversely(String data) {BinTree bt = new BinTree();String[] splits = data.split("\\s+");idx = 0;createNodeReversely(bt.root, splits);return bt;}// 根据指定格式的字符串创建一颗二叉树  先建立右子树  在建立左子树public static BinTree epilogueOrderCreateABinTreeInversely(String data) {BinTree bt = new BinTree();String[] splits = data.split("\\s+");idx = 0;epilogueOrderCreateNodeInversely(bt.root, splits);return bt;}// assistMethod  根据用户的输入建树private static void createNode(Node node, String notice) {Scanner scan = Tools.getScanner();Log.logWithoutLn("please input " + notice + " : ");node.data = scan.next();if(!Node.NULL.equals(node.data) ) {node.left = new Node();node.right = new Node();createNode(node.left, notice + "'s leftChild");createNode(node.right, notice + "'s rightChild");}}// assistMethod  根据指定格式的字符串建树static int idx;// 先建立左子树  在建立右子树private static void createNodeReversely(Node node, String[] data) {node.data = data[idx ++];if(!Node.NULL.equals(node.data) ) {node.left = new Node();node.right = new Node();createNodeReversely(node.left, data);createNodeReversely(node.right, data);}}// 先建立右子树  在建立左子树private static void epilogueOrderCreateNodeInversely(Node node, String[] data) {node.data = data[idx ++];if(!Node.NULL.equals(node.data) ) {node.left = new Node();node.right = new Node();epilogueOrderCreateNodeInversely(node.right, data);epilogueOrderCreateNodeInversely(node.left, data);}}// 根据先序, 中序 重新建树// 思路 : 先补全树的字符串表示, 在建树[左 -> 右]public static BinTree rebuildForFirstOrderAndInfixOrder(String firstOrder, String infixOrder) {String[] first = firstOrder.split("\\s+");String[] infix = infixOrder.split("\\s+");StringBuilder sb = new StringBuilder();completeForFirstOrderAndInfixOrder(first, infix, 0, 0, first.length, sb);return createABinTree(sb.toString());}// 根据先序, 中序 重新建树  递归建树public static BinTree rebuildForFirstOrderAndInfixOrder02(String firstOrder, String infixOrder) {String[] first = firstOrder.split("\\s+");String[] infix = infixOrder.split("\\s+");Node root = rebuildForFirstOrderAndInfixOrder020(first, infix, 0, 0, first.length);return new BinTree(root);}// 根据中序, 后序 重新建树// 思路 : 先补全树的逆序字符串表示, 在逆序建树[右 -> 左]public static BinTree rebuildForInfixOrderAndEpilogue(String infixOrder, String epilogueOrder) {String[] epilogue = epilogueOrder.split("\\s+");String[] infix = infixOrder.split("\\s+");StringBuilder sb = new StringBuilder();completeForInfixOrderAndEpilogue(epilogue, infix, 0, 0, epilogue.length, sb);return epilogueOrderCreateABinTreeInversely(sb.toString());}// 根据先序, 中序 重新建树  递归建树public static BinTree rebuildForInfixOrderAndEpilogue02(String epilogueOrder, String infixOrder) {String[] epilogue = epilogueOrder.split("\\s+");String[] infix = infixOrder.split("\\s+");Node root = rebuildForInfixOrderAndEpilogueOrder020(epilogue, infix, 0, 0, infix.length);return new BinTree(root);}// 根据先序和中序  补全 "#"子节点private static void completeForFirstOrderAndInfixOrder(String[] first, String[] infix, int start01, int start02, int len, StringBuilder sb) {sb.append(first[start01] + " ");int idx = getIdxForStr(first[start01], infix, start02, len);int leftNumMinus1 = idx - start02, rightNumMinus1 = start02 + len - idx - 1;if(leftNumMinus1 == 0) {sb.append("# ");} else {completeForFirstOrderAndInfixOrder(first, infix, start01+1, start02, leftNumMinus1, sb);}if(rightNumMinus1 == 0) {sb.append("# ");} else {completeForFirstOrderAndInfixOrder(first, infix, start01+leftNumMinus1+1, idx+1, rightNumMinus1, sb);}}// 根据先序和中序, 递归构造左右子树private static Node rebuildForFirstOrderAndInfixOrder020(String[] first, String[] infix, int start01, int start02, int length) {Node node = new Node(first[start01]);int idx = getIdxForStr(first[start01], infix, start02, length);int leftNumMinus1 = idx - start02, rightNumMinus1 = start02 + length - idx - 1;if(leftNumMinus1 > 0) {node.left = rebuildForFirstOrderAndInfixOrder020(first, infix, start01+1, start02, leftNumMinus1);} else {node.left = new Node(Node.NULL);}if(rightNumMinus1 > 0) {node.right = rebuildForFirstOrderAndInfixOrder020(first, infix, start01+1+leftNumMinus1, idx+1, rightNumMinus1);} else {node.right = new Node(Node.NULL);}return node;}// 根据先序和中序  补全 "#"子节点private static void completeForInfixOrderAndEpilogue(String[] epilogue, String[] infix, int start01, int start02, int len, StringBuilder sb) {int endIdx = start01 + len - 1;sb.append(epilogue[endIdx] + " ");int idx = getIdxForStr(epilogue[endIdx], infix, start02, len);int leftNumMinus1 = idx - start02, rightNumMinus1 = start02 + len - idx - 1;if(rightNumMinus1 == 0) {sb.append("# ");} else {completeForInfixOrderAndEpilogue(epilogue, infix, endIdx-rightNumMinus1, idx+1, rightNumMinus1, sb);}if(leftNumMinus1 == 0) {sb.append("# ");} else {completeForInfixOrderAndEpilogue(epilogue, infix, start01, start02, leftNumMinus1, sb);}}// 根据中序和后序, 递归构造左右子树private static Node rebuildForInfixOrderAndEpilogueOrder020(String[] epilogue, String[] infix, int start01, int start02, int length) {int endIdx = start01 + length - 1;Node node = new Node(epilogue[endIdx]);int idx = getIdxForStr(epilogue[endIdx], infix, start02, length);int leftNumMinus1 = idx - start02, rightNumMinus1 = start02 + length - idx - 1;if(rightNumMinus1 > 0) {node.right = rebuildForInfixOrderAndEpilogueOrder020(epilogue, infix, start01+leftNumMinus1, idx+1, rightNumMinus1);} else {node.right = new Node(Node.NULL);}if(leftNumMinus1 > 0) {node.left = rebuildForInfixOrderAndEpilogueOrder020(epilogue, infix, start01, start02, leftNumMinus1);} else {node.left = new Node(Node.NULL);}return node;}// 判断给定的前序和中序 是否合理  是否能构成一颗合法的二叉树public static boolean canCompleteForFirstOrderAndInfixOrder(String[] first, String[] infix, int start01, int start02, int len) {int idx = getIdxForStr(first[start01], infix, start02, len);int leftNumMinus1 = idx - start02, rightNumMinus1 = start02 + len - idx - 1;boolean isLeftOk = false, isRightOk = false;if(leftNumMinus1 == 0) {isLeftOk = true;} else {isLeftOk = canCompleteForFirstOrderAndInfixOrder(first, infix, start01+1, start02, leftNumMinus1);}if(rightNumMinus1 == 0) {isRightOk = true;} else {isRightOk = canCompleteForFirstOrderAndInfixOrder(first, infix, start01+leftNumMinus1+1, idx+1, rightNumMinus1);}return isLeftOk && isRightOk;}// 获取level层的元素 递归实现public static void getSpecifiedLayerElements(Node node, int level) {if(Node.NULL.equals(node.data) ) {return ;}if(level == 0) {Log.logWithoutLn(node.data + " ");}getSpecifiedLayerElements(node.left, level-1);getSpecifiedLayerElements(node.right, level-1);}// 顺序遍历二叉树    方向从左至右public static void traverseInOrder(Node node) {Deque<Node> que = new LinkedList<Node>();que.addLast(node);while(que.size() > 0) {Node tmp = que.pollFirst();Log.logWithoutLn(tmp.data + " ");if(!Node.NULL.equals(tmp.left.data) ) {que.addLast(tmp.left);}if(!Node.NULL.equals(tmp.right.data) ) {que.addLast(tmp.right);}}Log.enter();}// 顺序遍历二叉树    方向从右至左public static void traverseInOrder02(Node node) {Deque<Node> que = new LinkedList<Node>();que.addLast(node);while(que.size() > 0) {Node tmp = que.pollFirst();Log.logWithoutLn(tmp.data + " ");if(!Node.NULL.equals(tmp.right.data) ) {que.addLast(tmp.right);}if(!Node.NULL.equals(tmp.left.data) ) {que.addLast(tmp.left);}}Log.enter();}// 顺序自底向上遍历二叉树   方向从左至右public static void traverseInOrderInversely(Node node) {Deque<Node> que = new LinkedList<Node>();Deque<Node> inv = new LinkedList<Node>();que.addLast(node);while(que.size() > 0) {Node tmp = que.pollFirst();inv.push(tmp);if(!Node.NULL.equals(tmp.right.data) ) {que.addLast(tmp.right);}if(!Node.NULL.equals(tmp.left.data) ) {que.addLast(tmp.left);}}while(inv.size() > 0) {Node tmp = inv.pop();Log.logWithoutLn(tmp.data + " ");}Log.enter();}// 顺序自底向上遍历二叉树   方向从右至左public static void traverseInOrderInversely02(Node node) {Deque<Node> que = new LinkedList<Node>();Deque<Node> inv = new LinkedList<Node>();que.addLast(node);while(que.size() > 0) {Node tmp = que.pollFirst();inv.push(tmp);if(!Node.NULL.equals(tmp.left.data) ) {que.addLast(tmp.left);}if(!Node.NULL.equals(tmp.right.data) ) {que.addLast(tmp.right);}}while(inv.size() > 0) {Node tmp = inv.pop();Log.logWithoutLn(tmp.data + " ");}Log.enter();}// 获取arr中与str相同的字符串的索引private static int getIdxForStr(String str, String[] arr, int start, int len) {int idx = -1, end = start + len;for(int i=start; i<end; i++) {if(str.equals(arr[i]) ) {idx = i;return idx;}}return idx;}// 先序遍历public void headTraverse(Node node) {if(node != null) {Log.logWithoutLn(node.data + " ");headTraverse(node.left);headTraverse(node.right);}}// assistMethod   toString时使用  先序遍历二叉树  获取当前二叉树的字符串表示public void headTraverseForString(Node node, StringBuilder sb) {if(node != null) {sb.append(node.data + " ");
//              sb.append(node.maxLeft + " - " + node.maxRight + "; ");headTraverseForString(node.left, sb);headTraverseForString(node.right, sb);}}// 先序遍历二叉树获取各个叶子节点的深度public void headTraverseForMaxDepth(Node node, int cnt, List<Integer> deepth) {if(! node.data.equals(Node.NULL) ) {headTraverseForMaxDepth(node.left, cnt+1, deepth);headTraverseForMaxDepth(node.right, cnt+1, deepth);}  else {deepth.add(cnt);return ;}}// 先序遍历二叉树   获取每一个节点的最深左子树的深度, 和最深右子树的深度private void headTraverseForDepth(Node node, int cnt) {if(!node.data.equals(Node.NULL) ) {headTraverseForDepth(node.left, cnt+1);headTraverseForDepth(node.right, cnt+1);node.maxLeft = getMax(node.left.maxLeft + 1, node.left.maxRight + 1);node.maxRight = getMax(node.right.maxLeft + 1, node.right.maxRight + 1);}  else {node.maxLeft = -1;node.maxRight = -1;return ;}}// 获取node子树的可能的最大距离 // 先递归获取每一个结点的最大左子树的深度, 以及最大右子树的深度, 然后在递归一次获取(node.maxLeft + node.maxRight)的最大值即为所求public int getMaxDis(Node node ) {headTraverseForDepth(node, 0);return getMaxDis0(node);}// 获取node子树的可能的最大距离   sum表示(maxLeft + maxRight)// 如果node.sum大于node.left.sum, node.right.sum   则结果去node.sum// 否则  去较大的子树结点  递归private int getMaxDis0(Node node ) {int nodeSum = node.maxLeft + node.maxRight;int nodeLeftSum = node.left.maxLeft + node.right.maxRight;int nodeRightSum = node.right.maxLeft + node.right.maxRight;int max = getMax(nodeSum, nodeLeftSum);max = getMax(max, nodeRightSum);if(max == nodeSum) {return nodeSum;} else if(max == nodeLeftSum) {return getMaxDis(node.left);} else if(max == nodeRightSum) {return getMaxDis(node.right);}return -1;}// 获取x, y的较大值private int getMax(int x, int y) {return x > y ? x : y;}// Debugpublic String toString() {StringBuilder sb = new StringBuilder();headTraverseForString(root, sb);return sb.toString();}}// 树的结点public static class Node {// 空节点  表示叶子节点final static String NULL = "#";// data  存放该节点的数据, left, right 存放左/ 右子树的索引// maxLeft, maxRight 表示左/ 右子树的最深深度String data;Node left;Node right;int maxLeft;int maxRight;// 初始化public Node() {}   public Node(String data) {this.data = data;}// getter & setter public String getData() {return data;}   public Node getLeft() {return left;}public Node getRight() {return right;}public void setLeft(Node left) {this.left = left;}public void setRight(Node right) {this.right = right;}// Debugpublic String toString() {return data;}}}

5. 运行结果

这里写图片描述

6. 总结

树是一种递归的结构, 所以涉及的算法很多都涉及递归, 通过这几个算法, 相信我们对于二叉树的了解又会深一层

这篇关于3.8 二叉树中结点最大的距离 重建二叉树 顺序遍历二叉树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1011022

相关文章

poj 3723 kruscal,反边取最大生成树。

题意: 需要征募女兵N人,男兵M人。 每征募一个人需要花费10000美元,但是如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱。 给出若干的男女之间的1~9999之间的亲密关系度,征募某个人的费用是10000 - (已经征募的人中和自己的亲密度的最大值)。 要求通过适当的招募顺序使得征募所有人的费用最小。 解析: 先设想无向图,在征募某个人a时,如果使用了a和b之间的关系

poj 3258 二分最小值最大

题意: 有一些石头排成一条线,第一个和最后一个不能去掉。 其余的共可以去掉m块,要使去掉后石头间距的最小值最大。 解析: 二分石头,最小值最大。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <c

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 2594 二分图最大独立集

题意: 求一张图的最大独立集,这题不同的地方在于,间接相邻的点也可以有一条边,所以用floyd来把间接相邻的边也连起来。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <sta

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 3692 二分图最大独立集

题意: 幼儿园里,有G个女生和B个男生。 他们中间有女生和女生认识,男生男生认识,也有男生和女生认识的。 现在要选出一些人,使得这里面的人都认识,问最多能选多少人。 解析: 反过来建边,将不认识的男生和女生相连,然后求一个二分图的最大独立集就行了。 下图很直观: 点击打开链接 原图: 现图: 、 代码: #pragma comment(

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

最大流=最小割=最小点权覆盖集=sum-最大点权独立集

二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。 在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。   二分图最小点权覆盖     从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。 建模:     原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t