leetcode-Recover Binary Search Tree

Two elements of a binary search tree (BST) are swapped by mistake.

Recover the tree without changing its structure.

Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?

confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.

題目說一個二叉搜索樹的兩個節點被錯誤交換,要求不改變樹的結構恢復正常的二叉搜索樹。

解法一:

O(n)的空間複雜度的解決辦法是比較容易的,我們可以把二叉搜索樹按中序遍歷賦值到數組中,然後直接掃描數組元素記錄相鄰兩個數字逆序的位置,相鄰兩個數字逆序的對數可能有一對,也可能有兩對。假設元素按順序排序是1、2、3、4、5.一對的情況,如:1、2、4、3、5,這樣我們直接交換4和3的值即可。如果是兩對的情況,例如1、5、3、4、2,我們查找到逆序位置的元素5和2,交換5和2即可。

但是如何根據這個信息去交換二叉樹中的節點呢?我們可以存儲元素值與樹節點指針的映射,用STL的map容器,key爲元素值,value爲節點指針。

代碼如下:


/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode *root) {
        vector<int> v;
        map<int, TreeNode*> mm;
        //inorder 
        stack<TreeNode*> s;
        s.push(root);
        while (!s.empty()) {
            TreeNode *tmp = s.top();
            while (tmp->left!=NULL) {
                s.push(tmp->left);
                tmp = tmp->left;
            }
            v.push_back(tmp->val);
            mm[tmp->val] = tmp;
            s.pop();
            while (tmp->right==NULL && !s.empty()) {
                tmp = s.top();
                v.push_back(tmp->val);
                mm[tmp->val] = tmp;
                s.pop();
            }
            if (tmp->right!=NULL)
                s.push(tmp->right);
        }
        int p1=-1, p2 = -1;
        for (int i=1; i<v.size(); ++i) {
            if (v[i]<v[i-1]) {
                if (p1==-1) {
                    p1 = i-1;
                } else {
                    p2 = i-1;
                }
            }
        }

        if (p2==-1) {
            swap(mm[v[p1]]->val, mm[v[p1+1]]->val);
        } else {
            swap(mm[v[p1]]->val, mm[v[p2+1]]->val);
        }
    }
private:
    void swap(int &a, int &b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
};

解法二:

認真考慮二叉搜索樹的性質,左子樹的值比根節點小,右子樹的值比根節點大,如果兩個節點值被交換,有下面四種情況,一是左右子樹的兩個節點被交換,二是根節點與左子樹一節點被交換,三是根節點與右子樹一節點被交換,四是左子樹或者右子樹裏面的兩個節點被交換。

情況一時,我們可以在左子樹中找比根節點值大的節點值,在右子樹中找比根節點值小的節點值,然後交換找到的兩個節點的值;

情況二時,我們要找到左子樹中比根節點值最大的節點,然後交換根節點與找到的節點;

情況三時,我們要找到右子樹中比根節點值最小的節點,然後交換根節點與找到的節點;

情況四時,我們對根節點的左右子樹再遞歸調用這樣的操作。

那麼如何判斷是哪種情況發生了呢?通過在左子樹中找比根節點大的最大節點x,在右子樹中找比根節點值小的最小節點y,如果x和y都存在,說明是x和y被交換了;如果x存在而y不存在,只能是根節點與x被交換了;類似,如果y存在而x不存在,只能是根節點與y被交換了;如果都不存在,那麼是左子樹或右子樹裏的兩個節點被交換了。


/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode *root) {
        if (root==NULL)
            return;
        //找左子樹孩子的最大值節點
        TreeNode *leftBiggest = root;
        find(root->left, leftBiggest, "largest"); 
        //找右子樹孩子的最小值節點
        TreeNode *rightSmallest = root;
        find(root->right, rightSmallest, "smallest");
        
        // root的兩個左右子孫誤交換了
        if (leftBiggest!=root && rightSmallest!=root) {
            swap(leftBiggest->val, rightSmallest->val);
        } else if(leftBiggest != root) {  //右子樹正常,root與leftBiggest誤交換了
            swap(root->val, leftBiggest->val);
        } else if (rightSmallest != root) {  //左子樹正常,root與rightSmallest誤交換了
            swap(root->val, rightSmallest->val);
        } else {   // 左子樹或右子樹裏的兩個節點誤交換
            recoverTree(root->left);
            recoverTree(root->right);
        }
    }
private:
    void find(TreeNode *root, TreeNode *&p, const string& relation) {
        if (root == NULL)
            return;
        if (relation=="smallest" && root->val<p->val)
            p = root;
        else if (relation=="largest" && root->val>p->val)
            p = root;
        find(root->left, p, relation);
        find(root->right, p, relation);
    }
    void swap(int &a, int &b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
};





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章