Java数据结构与算法(3) 寻找中序遍历时的下一个结点
2017-11-04 10:22
573 查看
前言
今天一天没有什么状态,学习效率太低了。今天重新温习了一下树的遍历,如何寻找中序遍历的下一个结点。接下来的计划是学习Spring Boot和 算法与数据结构。
思路
算法与数据结构是我最薄弱的一环。每次写关于算法的代码时,都无法下手,经常陷入到逻辑的死胡同里。真心感觉自己的逻辑能力好差,思路混乱。程序员最重要的是思考和逻辑能力,只有把思路理清楚了,代码才能一气呵成。中序遍历:首先按照中序遍历的方式去访问根结点的左子树,然后访问根结点,最后按照中序遍历的方式去访问根结点的右子树。
首先看图
![](http://upload-images.jianshu.io/upload_images/4636177-35d3231bbb6f9da9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
P表示父结点,
N代表子结点。
L表示
N的左子树,
R表示
N的右子树。
我们肯定是采用递归的方式。当结点是
L的时候,无关。当
R != null的时候,我们返回
R结点下面的第一个结点,即下一个结点。如果
R == null的时候,我们下一个结点肯定是要往上面走,在
P != null下的情况,如果
N是
P的左子树,那么下一个结点就是
N。如果
N不是
P的左子树的话,我们需要一直往父亲结点走,直到是某一个结点的左子树,下一个结点即为所求。
代码实现
定义一个MyTreeNode.java。包含以下属性:结点的值,左子树,右子树,父亲结点。
public class MyTreeNode { private final char value; private MyTreeNode left; private MyTreeNode right; private MyTreeNode parent; public MyTreeNode(char value) { super(); this.value = value; this.left = null; this.right = null; this.parent = null; } public MyTreeNode getLeft() { return left; } public void setLeft(MyTreeNode left) { this.left = left; if (left != null) { this.left.setParent(this); } } public MyTreeNode getRight() { return right; } public void setRight(MyTreeNode right) { this.right = right; if (right != null) { this.right.setParent(this); } } public MyTreeNode getParent() { return parent; } private void setParent(MyTreeNode parent) { this.parent = parent; } public char getValue() { return value; } }
我们自己手动去创建一根这样的树。
![](http://upload-images.jianshu.io/upload_images/4636177-fcde3bf48f5d94eb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
显而易见,前序遍历是
ABDEGCF,中序遍历是
DBGEACF,后序遍历是
DGEBFCA。
如何通过前序遍历和中序遍历推出树的结构呢?其实很简单,前序遍历中第一个元素肯定是根结点。我们在从中序遍历中找到该根结点,那么根结点左边的元素就是左子树,右边的元素就是右子树呢。然后递归的给每一个结点设置左子树和右字数,一根完整的二叉树就形成了。简单轻松,贴上代码。
public class MyTreeNodeCreator { public static MyTreeNode sampleTree() { MyTreeNode root = new MyTreeNode('A'); root.setLeft(new MyTreeNode('B')); root.setRight(new MyTreeNode('C')); root.getLeft().setLeft(new MyTreeNode('D')); root.getLeft().setRight(new MyTreeNode('E')); root.getLeft().getRight().setLeft(new MyTreeNode('G')); root.getRight().setRight(new MyTreeNode('F')); return root; } public static String displayBehindTree(String font, String mid) { if (StringUtils.isEmpty(font)) { return ""; } char rootValue = font.charAt(0); int index = mid.indexOf(rootValue); return displayBehindTree(font.substring(1, index + 1), mid.substring(0, index)) + displayBehindTree(font.substring(index + 1), mid.substring(index + 1)) + rootValue; } public static MyTreeNode behindTree(String font, String mid) { if (StringUtils.isEmpty(font)) { return null; } char rootValue = font.charAt(0); int index = mid.indexOf(rootValue); MyTreeNode root = new MyTreeNode(rootValue); root.setLeft(behindTree(font.substring(1, index + 1), mid.substring(0, index))); root.setRight(behindTree(font.substring(index + 1), mid.substring(index + 1))); return root; } public static void behindOrder(MyTreeNode node) { if (node == null) { return; } behindOrder(node.getLeft()); behindOrder(node.getRight()); System.out.print(node.getValue() + " "); } }
接着我们根据二叉树,寻找中序遍历时的下一个结点。先一般后特殊,要进行边界控制,每次必须向前推进循环不变式中涉及的变量值。
public class InOrder { public MyTreeNode next(MyTreeNode node) { if (node == null) { return null; } if (node.getRight() != null) { return first(node.getRight()); } else { while (node.getParent() != null && node.getParent().getLeft() != node) { node = node.getParent(); } return node.getParent(); } } /** * Gets first node * * @param node * @return */ public MyTreeNode first(MyTreeNode node) { if (node == null) { return null; } MyTreeNode curNode = node; while (curNode.getLeft() != null) { curNode = curNode.getLeft(); } return curNode; } }
核心代码完成,我们开始写测试
demo。我们需要编写测试用例,要遵守
BCDE原则,以保证被测试模块的交付质量。
B:
Border,边界值测试,包括循环边界,特殊取值,特殊时间点,数据顺序等。
C:
Correct,正确的输入,并得到预期的结果。
D:
Design,与设计文档相结合,来编写单元测试。
E:
Error,强制错误信息的输入(如:非法数据,异常流程,非业务允许输入等),并得到预期的结果。
运行
Demo,输出和我们预期一样的结果。
![](http://upload-images.jianshu.io/upload_images/4636177-e8aec7ba1bc5c9bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
public class Demo { private static InOrder inOrder = new InOrder(); public static void main(String[] args) { printMidTree(); } public static void printBehindTree() { MyTreeNode root = MyTreeNodeCreator.behindTree("ABDEGCF", "DBGEACF"); MyTreeNodeCreator.behindOrder(root); MyTreeNodeCreator.behindOrder(MyTreeNodeCreator.behindTree("ABCD", "ABCD")); } public static void printMidTree() { MyTreeNode sampleTree = MyTreeNodeCreator.sampleTree(); display(sampleTree); display(MyTreeNodeCreator.behindTree("", "")); display(MyTreeNodeCreator.behindTree("A", "A")); display(MyTreeNodeCreator.behindTree("AB", "BA")); display(MyTreeNodeCreator.behindTree("ABCD", "DCBA")); display(MyTreeNodeCreator.behindTree("ABCD", "ABCD")); } public static void display(MyTreeNode sampleTree) { for (MyTreeNode root = inOrder.first(sampleTree); root != null; root = inOrder.next(root)) { System.out.print(root.getValue()); } System.out.println(" "); } }
尾言
我感觉数据结构和算法,思路是最重要的。只要有思路了,代码就水到渠成。没有思路,任何华丽的代码都是徒劳的。虽然有些数据结构和算法已经掌握了,但是想要简单形象的表达出来,对于我来说还是十分困难的。继续加油。
相关文章推荐
- 设计一个算法,通过一趟遍历在单链表确定最大的结点
- 使用广度遍历算法寻找两点之间的最短路径
- 【链表】C++链表操作大全:创建链表、遍历链表、删除链表结点、寻找环入口、链表加法、判断回文等
- 给一个单链表,不知道结点N的值,怎样只遍历一次就可以求出中间结点,写出算法
- 结点遍历C语言实现二叉树的常用的算法(递归与非递归实现遍历)
- ASP.NET 2.0中TreeView递归遍历及设置结点展开算法
- 算法-寻找两个链表的第一个公共结点
- 树的遍历 迭代算法——思路:初始化stack,pop stack利用pop的node,push new node to stack,可以考虑迭代一颗树 因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次是难点
- 算法:寻找树中两个结点的最低公共祖先
- 算法系列——二叉树下一个结点
- 寻找二叉查找树中的下一个结点
- 17.寻找下一个结点
- 笔试算法题(36):寻找一棵二叉树中最远节点的距离 & 根据二叉树的前序和后序遍历重建二叉树
- 程序员面试金典: 检查是否为BST、 寻找下一个结点
- 寻找下一个结点
- 设计一个算法,找出二叉查找树中指定结点的“下一个“结点(也即中序后继)。可以假定每个结点都含有指向父结点的连接。
- 寻找下一个结点
- 018给定二叉查找树的一个结点, 写一个算法查找它的“下一个”结点“(keep it up)
- 算法-寻找二叉树两个结点的最低共同父节点(OC实现)
- 【算法】求二叉树的下一个结点