Java數(shù)據(jù)結(jié)構(gòu)與算法(3) 尋找中序遍歷時的下一個結(jié)點

前言

今天一天沒有什么狀態(tài),學(xué)習(xí)效率太低了腕唧。今天重新溫習(xí)了一下樹的遍歷或辖,如何尋找中序遍歷的下一個結(jié)點。接下來的計劃是學(xué)習(xí)Spring Boot 和 算法與數(shù)據(jù)結(jié)構(gòu)枣接。


思路

算法與數(shù)據(jù)結(jié)構(gòu)是我最薄弱的一環(huán)颂暇。每次寫關(guān)于算法的代碼時,都無法下手但惶,經(jīng)常陷入到邏輯的死胡同里耳鸯。真心感覺自己的邏輯能力好差,思路混亂膀曾。程序員最重要的是思考和邏輯能力县爬,只有把思路理清楚了,代碼才能一氣呵成添谊。

  • 中序遍歷:首先按照中序遍歷的方式去訪問根結(jié)點的左子樹捌省,然后訪問根結(jié)點,最后按照中序遍歷的方式去訪問根結(jié)點的右子樹碉钠。

首先看圖


image.png
  • P表示父結(jié)點纲缓,N代表子結(jié)點。L表示N的左子樹喊废,R表示N的右子樹祝高。
  • 我們肯定是采用遞歸的方式。當結(jié)點是L的時候污筷,無關(guān)工闺。當R != null的時候,我們返回R結(jié)點下面的第一個結(jié)點瓣蛀,即下一個結(jié)點陆蟆。如果R == null的時候,我們下一個結(jié)點肯定是要往上面走惋增,在P 叠殷!= null下的情況,如果NP的左子樹诈皿,那么下一個結(jié)點就是P林束。如果N不是P的左子樹的話像棘,我們需要一直往父親結(jié)點走,直到是某一個結(jié)點的左子樹壶冒,下一個結(jié)點即為所求缕题。

代碼實現(xiàn)

  • 定義一個MyTreeNode.java。包含以下屬性:結(jié)點的值胖腾,左子樹烟零,右子樹,父親結(jié)點咸作。
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;
    }
}
  • 我們自己手動去創(chuàng)建一根這樣的樹瓶摆。

    image.png

    顯而易見,前序遍歷是ABDEGCF性宏,中序遍歷是DBGEACF群井,后序遍歷是DGEBFCA

  • 如何通過前序遍歷和中序遍歷推出樹的結(jié)構(gòu)呢毫胜?其實很簡單书斜,前序遍歷中第一個元素肯定是根結(jié)點。我們在從中序遍歷中找到該根結(jié)點酵使,那么根結(jié)點左邊的元素就是左子樹,右邊的元素就是右子樹呢口渔。然后遞歸的給每一個結(jié)點設(shè)置左子樹和右子樹样屠。一根完整的二叉樹就形成了。簡單輕松攻礼,貼上代碼礁扮。

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;
    }
}

  • 接著我們根據(jù)二叉樹知举,尋找中序遍歷時的下一個結(jié)點。先一般后特殊太伊,要進行邊界控制雇锡,每次必須向前推進循環(huán)不變式中涉及的變量值。
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原則,以保證被測試模塊的交付質(zhì)量欲账。
    • BBorder,邊界值測試,包括循環(huán)邊界芭概,特殊取值赛不,特殊時間點,數(shù)據(jù)順序等罢洲。
    • CCorrect踢故,正確的輸入,并得到預(yù)期的結(jié)果惹苗。
    • DDesign殿较,與設(shè)計文檔相結(jié)合,來編寫單元測試桩蓉。
    • EError淋纲,強制錯誤信息的輸入(如:非法數(shù)據(jù),異常流程院究,非業(yè)務(wù)允許輸入等)洽瞬,并得到預(yù)期的結(jié)果。

運行Demo业汰,輸出和我們預(yù)期一樣的結(jié)果伙窃。

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


尾言

我感覺數(shù)據(jù)結(jié)構(gòu)和算法,思路是最重要的样漆。只要有思路了为障,代碼就水到渠成。沒有思路放祟,任何華麗的代碼都是徒勞的鳍怨。

雖然有些數(shù)據(jù)結(jié)構(gòu)和算法已經(jīng)掌握了,但是想要簡單形象的表達出來跪妥,對于我來說還是十分困難的京景。繼續(xù)加油。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骗奖,一起剝皮案震驚了整個濱河市确徙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌执桌,老刑警劉巖鄙皇,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仰挣,居然都是意外死亡伴逸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門膘壶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來错蝴,“玉大人洲愤,你說我怎么就攤上這事∏昝蹋” “怎么了柬赐?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長官紫。 經(jīng)常有香客問我肛宋,道長,這世上最難降的妖魔是什么束世? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任酝陈,我火速辦了婚禮,結(jié)果婚禮上毁涉,老公的妹妹穿的比我還像新娘沉帮。我一直安慰自己,他們只是感情好贫堰,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布遇西。 她就那樣靜靜地躺著,像睡著了一般严嗜。 火紅的嫁衣襯著肌膚如雪粱檀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天漫玄,我揣著相機與錄音茄蚯,去河邊找鬼。 笑死睦优,一個胖子當著我的面吹牛渗常,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汗盘,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皱碘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了隐孽?” 一聲冷哼從身側(cè)響起癌椿,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菱阵,沒想到半個月后踢俄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡晴及,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年都办,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡琳钉,死狀恐怖势木,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歌懒,我是刑警寧澤啦桌,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站歼培,受9級特大地震影響震蒋,放射性物質(zhì)發(fā)生泄漏茸塞。R本人自食惡果不足惜躲庄,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钾虐。 院中可真熱鬧噪窘,春花似錦、人聲如沸效扫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽供璧。三九已至佩伤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間济丘,已是汗流浹背谱秽。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留摹迷,地道東北人疟赊。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像峡碉,于是被迫代替她去往敵國和親近哟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容