Leetcode - Recover Binary Search Tree

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int index = 0;
    public void recoverTree(TreeNode root) {
        if (root == null)
            return;
        ArrayList<Integer> tracker = new ArrayList<Integer>();
        in_order(root, tracker);
        Collections.sort(tracker);
        in_order_define(root, tracker);
        return;
    }
    
    private void in_order(TreeNode root, ArrayList<Integer> tracker) {
        if (root.left != null)
            in_order(root.left, tracker);
        tracker.add(root.val);
        if (root.right != null)
            in_order(root.right, tracker);
    }
    
    private void in_order_define(TreeNode root, ArrayList<Integer> tracker) {
        if (root.left != null)
            in_order_define(root.left, tracker);
        root.val = tracker.get(index++);
        if (root.right != null)
            in_order_define(root.right, tracker);
        return;
    }
}

這道題目一開始想復(fù)雜了。覺得要交換結(jié)點(diǎn)。所以兩個(gè)錯(cuò)誤結(jié)點(diǎn),還必須要把他們對(duì)應(yīng)的兩個(gè)父親找出來。也就是四個(gè)結(jié)點(diǎn),會(huì)很麻煩。
后來才明白,可以直接swap value rather than swapping nodes.
看了答案之后寫出了最簡單的一個(gè)版本。
復(fù)雜度是 O(n logn), 空間復(fù)雜度是 O(n)

后來看了 geekforgeeks 的改進(jìn)版,
時(shí)間復(fù)雜度是O(n), 空間復(fù)雜度是 O(1)

自己重寫了下。
關(guān)鍵在于, swap 錯(cuò)誤,有幾種類型。
有兩種。
一種是, 錯(cuò)誤的結(jié)點(diǎn),在中序遍歷中,正好相鄰。
一種是,錯(cuò)誤的結(jié)點(diǎn),在中序遍歷中,隔著好幾個(gè)結(jié)點(diǎn)。
比如一棵樹的中序遍歷是,
3, 5, 7, 8, 10, 15, 20, 25

如果是錯(cuò)誤1:
3, 25, 7, 8, 10, 15, 20, 5

如果是錯(cuò)誤2
3, 5, 8, 7, 10, 15, 20, 25

那么我只需要找到一種方法,可以對(duì)這兩種情況都可以處理,并且可以自我分別出,是哪個(gè)方法錯(cuò)誤了。

方法如下,
設(shè)置四個(gè)指針:
first, second, third. 用來指示錯(cuò)誤的結(jié)點(diǎn)。
prev,用來指示中序遍歷中當(dāng)前結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn)。
然后,碰到某個(gè)結(jié)點(diǎn),當(dāng)他, val < min,時(shí),你就知道了,這里肯定有一個(gè)結(jié)點(diǎn)錯(cuò)誤了。但錯(cuò)誤類型你不清楚。可能是連著錯(cuò),可能是后面還有個(gè)錯(cuò)的。
你先記錄下來。
first = prev;
second = curr; // 當(dāng)前結(jié)點(diǎn)

然后更新 min = curr.val;
prev = curr;
記住,min必須時(shí)刻更新至當(dāng)前值,即使 curr.val < min

然后第二次如果又碰到一個(gè) curr.val < min的化,
只用記錄下這個(gè)結(jié)點(diǎn),
third = curr;
如果沒有,那就可能是之前連著錯(cuò)了。

結(jié)束中序遍歷后,判斷 third 是不是null。
如果不是,那么就表示是錯(cuò)誤類型1.
于是直接交換 first, third
如果是,那就是錯(cuò)誤類型2,
于是直接交換 first, second

參考網(wǎng)址如下:
http://www.geeksforgeeks.org/fix-two-swapped-nodes-of-bst/

其實(shí)這道題目不是很難。但我一開始就想著,怎么swap node 而不是怎么 swap values

還有,這里我自己找出來的一個(gè)小技巧。
in-order 遞歸實(shí)現(xiàn)中,如果保存當(dāng)前結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn)。
就是,

dfs(curr.left);
visit(curr);
prev = curr;
dfs(curr.right);

那么在先序遍歷中是如何做得呢?

visit(curr);
prev = curr;
dfs(curr.left);
dfs(curr.right);

那么在后序遍歷中是如何實(shí)現(xiàn)的呢?

dfs(curr.left);
dfs(curr.right);
visit(curr);
prev = curr;

發(fā)現(xiàn)規(guī)律沒?
遍歷的遞歸還是那么寫, prev 一定是跟在 訪問當(dāng)前結(jié)點(diǎn) visit(curr) 之后。

這幾天效率真的不是很高。

Anyway, Good luck, Richardo!

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private TreeNode preNode = null;
    private int pre = Integer.MIN_VALUE;
    public void recoverTree(TreeNode root) {
        if (root == null) {
            return;
        }
        
        TreeNode bigger = null;
        TreeNode smaller = null;
        int counter = 0;
        
        Stack<TreeNode> st = new Stack<TreeNode>();
        TreeNode p = root;
        while (p != null) {
            st.push(p);
            p = p.left;
        }
        
        while (!st.isEmpty()) {
            p = st.pop();
            if (pre < p.val) {
                pre = p.val;
                preNode = p;
            }
            else {
                pre = p.val;
                
                counter++;
                if (counter == 1) {
                    bigger = preNode;
                    smaller = p;
                }
                else if (counter == 2) {
                    smaller = p;
                    break;
                }
            }
            if (p.right != null) {
                p = p.right;
                while (p != null) {
                    st.push(p);
                    p = p.left;
                }
            }
        }
        
        if (smaller == null || bigger == null) {
            return;
        }
        else {
            int temp = smaller.val;
            smaller.val = bigger.val;
            bigger.val = temp;
        }
        
    }
}

自己想出來的辦法。
in order traversal, 然后維持兩個(gè)指針,
當(dāng)遇到 pre > curr.val的時(shí)候,就說明有錯(cuò)了。
第一次碰到的時(shí)候,記錄下最大結(jié)點(diǎn)和最小結(jié)點(diǎn)
第二次碰到的時(shí)候,記錄下最小結(jié)點(diǎn)

如果第二次沒碰到,就直接交換第一次的最大結(jié)點(diǎn)和最小結(jié)點(diǎn)的value
否則,就交換第一次最大結(jié)點(diǎn)和第二次最小結(jié)點(diǎn)的value

思路是很清晰的。然后我中序遍歷時(shí)間復(fù)雜度是 O(n),空間復(fù)雜度是 O(n)

題目要求空間復(fù)雜度是O(1)

pardon?
excuse me?

這是我活到現(xiàn)在,第一次知道,原來中序遍歷是可以做到
time: O(n)
space: O(1)

Morris traversal

reference:
http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html

然后我自己又寫了下,總時(shí)間快了 2ms

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private TreeNode preNode = null;
    private int preValue = Integer.MIN_VALUE;
    public void recoverTree(TreeNode root) {
        if (root == null) {
            return;
        }
        
        TreeNode bigger = null;
        TreeNode smaller = null;
        int counter = 0;
        
        TreeNode curr = root;
        while (curr != null) {
            if (curr.left == null) {
                // visit
                if (preValue < curr.val) {
                    preValue = curr.val;
                    preNode = curr;
                }
                else {
                    preValue = curr.val;
                    counter++;
                    if (counter == 1) {
                        bigger = preNode;
                        smaller = curr;
                    }
                    else {
                        smaller = curr;
                    }
                }
                curr = curr.right;
            }
            else {
                TreeNode pre = curr.left;
                while (pre.right != null && pre.right != curr) {
                    pre = pre.right;
                }
                
                if (pre.right == null) {
                    pre.right = curr;
                    curr = curr.left;
                }
                else {
                    // visit
                    if (preValue < curr.val) {
                        preValue = curr.val;
                        preNode = curr;
                    }
                    else {
                        preValue = curr.val;
                        counter++;
                        if (counter == 1) {
                            bigger = preNode;
                            smaller = curr;
                        }
                        else {
                            smaller = curr;
                        }
                    }
                
                    pre.right = null;
                    curr = curr.right;
                }
            }
        }
        
        
        
        if (smaller == null || bigger == null) {
            return;
        }
        else {
            int temp = smaller.val;
            smaller.val = bigger.val;
            bigger.val = temp;
        }
        
    }
}

Anyway, Good luck, Richardo! -- 09/08/2016

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評(píng)論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評(píng)論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,010評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評(píng)論 2 375

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