Java数据结构与算法(3) 寻找中序遍历时的下一个结点

来源:互联网 发布:淘宝e客服怎么设置 编辑:程序博客网 时间:2024/06/11 16:10

前言

今天一天没有什么状态,学习效率太低了。今天重新温习了一下树的遍历,如何寻找中序遍历的下一个结点。接下来的计划是学习Spring Boot 和 算法与数据结构。


思路

算法与数据结构是我最薄弱的一环。每次写关于算法的代码时,都无法下手,经常陷入到逻辑的死胡同里。真心感觉自己的逻辑能力好差,思路混乱。程序员最重要的是思考和逻辑能力,只有把思路理清楚了,代码才能一气呵成。

  • 中序遍历:首先按照中序遍历的方式去访问根结点的左子树,然后访问根结点,最后按照中序遍历的方式去访问根结点的右子树。

首先看图
image.png

  • P表示父结点,N代表子结点。L表示N的左子树,R表示N的右子树。
  • 我们肯定是采用递归的方式。当结点是L的时候,无关。当R != null的时候,我们返回R结点下面的第一个结点,即下一个结点。如果R == null的时候,我们下一个结点肯定是要往上面走,在P != null下的情况,如果NP的左子树,那么下一个结点就是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;    }}
  • 我们自己手动去创建一根这样的树。
    image.png
    显而易见,前序遍历是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原则,以保证被测试模块的交付质量。
    • BBorder,边界值测试,包括循环边界,特殊取值,特殊时间点,数据顺序等。
    • CCorrect,正确的输入,并得到预期的结果。
    • DDesign,与设计文档相结合,来编写单元测试。
    • EError,强制错误信息的输入(如:非法数据,异常流程,非业务允许输入等),并得到预期的结果。

运行Demo,输出和我们预期一样的结果。
image.png

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(" ");    }}

尾言

我感觉数据结构和算法,思路是最重要的。只要有思路了,代码就水到渠成。没有思路,任何华丽的代码都是徒劳的。

虽然有些数据结构和算法已经掌握了,但是想要简单形象的表达出来,对于我来说还是十分困难的。继续加油。

阅读全文
0 0
原创粉丝点击