1. 程式人生 > 其它 >LeetCode 2097. 合法重新排列數對

LeetCode 2097. 合法重新排列數對

LeetCode 2097. 合法重新排列數對

題目描述

給你一個下標從 0 開始的二維整數陣列 pairs,其中 pairs[i] = [start_i, end_i]。如果 pairs 的一個重新排列,滿足對每一個下標 i1 <= i < pairs.length)都有 end_{i-1} == start_{i},那麼我們就認為這個重新排列是 pairs 的一個 合法重新排列

請你返回 任意一個 pairs 的合法重新排列。

注意:資料保證至少存在一個 pairs 的合法重新排列。

樣例

輸入:pairs = [[5,1],[4,5],[11,9],[9,4]]
輸出:[[11,9],[9,4],[4,5],[5,1]]
解釋:
輸出的是一個合法重新排列,因為每一個 end_{i-1} 都等於 start_{i}。
end0 = 9 == 9 = start1 
end1 = 4 == 4 = start2
end2 = 5 == 5 = start3


輸入:pairs = [[1,3],[3,2],[2,1]]
輸出:[[1,3],[3,2],[2,1]]
解釋:
輸出的是一個合法重新排列,因為每一個 end_{i-1} 都等於 start_{i}。
end0 = 3 == 3 = start1
end1 = 2 == 2 = start2
重新排列後的陣列 [[2,1],[1,3],[3,2]] 和 [[3,2],[2,1],[1,3]] 都是合法的。
示例 3:

輸入:pairs = [[1,2],[1,3],[2,1]]
輸出:[[1,2],[2,1],[1,3]]
解釋:
輸出的是一個合法重新排列,因為每一個 end_{i-1} 都等於 start_{i}。
end0 = 2 == 2 = start1
end1 = 1 == 1 = start2

限制

  • 1<= pairs.length <= 10^5
  • pairs[i].length == 2
  • 0 <= start_i, end_i <= 10^9
  • start_i != end_i
  • pairs 中不存在一模一樣的數對。
  • 至少 存在 一個合法的 pairs 重新排列。

演算法

(有向圖的尤拉路徑) \(O(n)\)

尤拉路徑與歐拉回路性質回顧:

  1. 對於無向圖,所有邊都是連通的。
    • 存在尤拉路徑的充分必要條件:度數為奇數的點只能有0或2個
    • 存在歐拉回路的充分必要條件:度數為奇數的點只能有0個
  2. 對於有向圖,所有邊都是連通。
    • 存在尤拉路徑的充分必要條件:要麼所有點的出度均等於入度;要麼除了兩個點之外,其餘所有點的出度等於入度,剩餘的兩個點:一個滿足出度比入度多1(起點),另一個滿足入度比出度多1(終點)
    • 存在歐拉回路的充分必要條件:所有點的出度均等於入度。

尤拉路徑的遍歷演算法描述:

dfs(u){
	while 從u的所有出邊:
        刪邊;
		dfs(v);//擴充套件
	seq<-u;// 儲存的是歐拉回路的倒序
}

最終 seq[]中存下的就是尤拉路徑的倒序,為什麼u再最後才加入?因為需要遍歷完u的所有子節點才能把u壓入進來,u是後面所有點的起點。

有向圖:
每用一條邊 刪掉
無向圖:
每用一條邊標記對應的反向邊(XOR技巧)
a→b
b→a

陣列模擬連線表:編號成對的(0, 1) (2, 3)(4, 5)......,編號的0的反向邊為0^1 = 1,編號的1的反向邊為1^1 = 0, 編號2的反向邊為2^1 = 3,編號的2的反向邊為3^1 = 2 ......

i的反向邊為 i^1

本題可以轉化為輸出整個有向圖的尤拉路徑,輸入路徑上的編號組成答案。

構圖:對每個數對 \(e\_i = (x, y)\),構造一條有向邊 \(x \to y\),屬性為 \(i\)。注意需要離散化一下。

因為題意本身就有答案,起點的出度減入度等於 1 的點或者任意一點即可。從起點開始深度優先遍歷整個圖即可。

時間複雜度

  • 遍歷所有點和邊一次,故總時間複雜度為 \(O(n)\)

空間複雜度

  • 需要 \(O(n)\) 的額外空間儲存圖,遞迴的系統棧,以及尤拉路相關的資料結構。

C++程式碼

const int N = 100005;
class Solution {
public:
    vector<int> alls;
    vector<pair<int, int>> g[N];
    vector<int> path;
    int deg[N];
    void dfs(int u, int prev){
        while(!g[u].empty()){
            int v = g[u].back().first, e = g[u].back().second;
            g[u].pop_back();
            dfs(v, e);
        }
        if(prev != -1){
            path.push_back(prev);
        }
    }
    vector<vector<int>> validArrangement(vector<vector<int>>& pairs) {
        for(auto x : pairs){
            alls.push_back(x[0]);
            alls.push_back(x[1]);
        }
        sort(alls.begin(), alls.end());
        alls.erase(unique(alls.begin(), alls.end()), alls.end());
        
        for(int i = 0; i < pairs.size(); i++){
            int x = lower_bound(alls.begin(), alls.end(), pairs[i][0]) - alls.begin();
            int y = lower_bound(alls.begin(), alls.end(), pairs[i][1]) - alls.begin();
            g[x].push_back({y, i});
            deg[x]++;
            deg[y]--;
        }
        int first = 0;
        for(int i = 0; i < alls.size(); i++){
            if(deg[i] == 1){
                first = i;
                break;
            }
        }
        dfs(first, -1);
        vector<vector<int>> res;
        for(int i = path.size() - 1; i >= 0; i--){
            res.push_back(pairs[path[i]]);
        }
        return res;
    }
};