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

前言

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


思路

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

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

首先看图


image.png
  • P表示父结点,N代表子结点。L表示N的左子树,R表示N的右子树。
  • 我们肯定是采用递归的方式。当结点是L的时候,无关。当R != null的时候,我们返回R结点下面的第一个结点,即下一个结点。如果R == null的时候,我们下一个结点肯定是要往上面走,在P != null下的情况,如果NP的左子树,那么下一个结点就是P如果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 MyTreeNode customTree(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(customTree(font.substring(1, index + 1), mid.substring(0, index)));
        root.setRight(customTree(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 static String displayBehindOrder(String font, String mid) {
        if (StringUtils.isEmpty(font)) {
            return "";
        }

        char rootValue = font.charAt(0);
        int index = mid.indexOf(rootValue);

        return displayBehindOrder(font.substring(1, index + 1), mid.substring(0, index))
                + displayBehindOrder(font.substring(index + 1), mid.substring(index + 1)) + rootValue;
    }
}

  • 接着我们根据二叉树,寻找中序遍历时的下一个结点。先一般后特殊,要进行边界控制,每次必须向前推进循环不变式中涉及的变量值。
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) {
        printMidOrder();
    }

    public static void printBehindOrder() {
        MyTreeNode root = MyTreeNodeCreator.customTree("ABDEGCF", "DBGEACF");
        MyTreeNodeCreator.behindOrder(root);
        MyTreeNodeCreator.behindOrder(MyTreeNodeCreator.customTree("ABCD", "ABCD"));
    }

    public static void printMidOrder() {
        MyTreeNode sampleTree = MyTreeNodeCreator.sampleTree();
        display(sampleTree);
        display(MyTreeNodeCreator.customTree("", ""));
        display(MyTreeNodeCreator.customTree("A", "A"));
        display(MyTreeNodeCreator.customTree("AB", "BA"));
        display(MyTreeNodeCreator.customTree("ABCD", "DCBA"));
        display(MyTreeNodeCreator.customTree("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(" ");
    }
}


尾言

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容