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