<p>You are given a <strong>0-indexed</strong> 2D integer array <code>pairs</code> where <code>pairs[i] = [start<sub>i</sub>, end<sub>i</sub>]</code>. An arrangement of <code>pairs</code> is <strong>valid</strong> if for every index <code>i</code> where <code>1 &lt;= i &lt; pairs.length</code>, we have <code>end<sub>i-1</sub> == start<sub>i</sub></code>.</p>

<p>Return <em><strong>any</strong> valid arrangement of </em><code>pairs</code>.</p>

<p><strong>Note:</strong> The inputs will be generated such that there exists a valid arrangement of <code>pairs</code>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> pairs = [[5,1],[4,5],[11,9],[9,4]]
<strong>Output:</strong> [[11,9],[9,4],[4,5],[5,1]]
<strong>Explanation:
</strong>This is a valid arrangement since end<sub>i-1</sub> always equals start<sub>i</sub>.
end<sub>0</sub> = 9 == 9 = start<sub>1</sub> 
end<sub>1</sub> = 4 == 4 = start<sub>2</sub>
end<sub>2</sub> = 5 == 5 = start<sub>3</sub>
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> pairs = [[1,3],[3,2],[2,1]]
<strong>Output:</strong> [[1,3],[3,2],[2,1]]
<strong>Explanation:</strong>
This is a valid arrangement since end<sub>i-1</sub> always equals start<sub>i</sub>.
end<sub>0</sub> = 3 == 3 = start<sub>1</sub>
end<sub>1</sub> = 2 == 2 = start<sub>2</sub>
The arrangements [[2,1],[1,3],[3,2]] and [[3,2],[2,1],[1,3]] are also valid.
</pre>

<p><strong class="example">Example 3:</strong></p>

<pre>
<strong>Input:</strong> pairs = [[1,2],[1,3],[2,1]]
<strong>Output:</strong> [[1,2],[2,1],[1,3]]
<strong>Explanation:</strong>
This is a valid arrangement since end<sub>i-1</sub> always equals start<sub>i</sub>.
end<sub>0</sub> = 2 == 2 = start<sub>1</sub>
end<sub>1</sub> = 1 == 1 = start<sub>2</sub>
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= pairs.length &lt;= 10<sup>5</sup></code></li>
	<li><code>pairs[i].length == 2</code></li>
	<li><code>0 &lt;= start<sub>i</sub>, end<sub>i</sub> &lt;= 10<sup>9</sup></code></li>
	<li><code>start<sub>i</sub> != end<sub>i</sub></code></li>
	<li>No two pairs are exactly the same.</li>
	<li>There <strong>exists</strong> a valid arrangement of <code>pairs</code>.</li>
</ul>

# 合法重新排列数对

#  0 开始的二维整数数组 pairs
#  pairs[i] = [starti, endi]

#  如果 pairs 的一个重新排列，满足对每一个下标 i （ 1 <= i < pairs.length ）都有 endi-1 == starti ，
#  那么我们就认为这个重新排列是 pairs 的一个 合法重新排列 。

#  返回 任意一个 pairs 的合法重新排列。
#  数据保证至少存在一个 pairs 的合法重新排列。

# 欧拉路径和欧拉回路的定义

# 欧拉路径：在一个图中，欧拉路径是一条经过图中每条边恰好一次的路径。
# 对于一个有向图，如果存在欧拉路径，必须满足：

# 最多有一个节点的出度比入度多1（这个节点是欧拉路径的起点）。
# 最多有一个节点的入度比出度多1（这个节点是欧拉路径的终点）。
# 其他所有节点的出度和入度相等。

# 欧拉回路：如果路径的起点和终点相同，则称为欧拉回路。在这种情况下，所有节点的入度和出度都相等。

# 为什么不能选择入度为0的节点作为起点
# 入度为0的节点不一定存在：并不是所有节点都有入度为0的情况，特别是在一个图中没有孤立节点的情况下。
# 例如，如果图是连通的且存在欧拉路径，那么大多数节点的入度和出度都是相等的。

# 欧拉路径的起点要求：欧拉路径的起点是出度比入度多1的节点，而不是入度为0的节点。选择入度为0的节点可能会导致无法正确覆盖所有的边。
# 例如，如果起点选错了，DFS 可能会提早结束，从而未能遍历所有边。

# 正确的起点选择
# 根据欧拉路径的性质：

# 欧拉路径起点：应该是出度比入度多1的节点。
# 默认起点：如果不存在出度比入度多1的节点（即所有节点的出度和入度相等），那么可以选择任意一个节点作为起点。

#  合法排列 -> end == start

# Hierholzer -> 遞歸做 DFS, 直到終點 -> res.append(airport)
# graph -> defaultdict {"frm": deque(["to"])}
# in_degree = defaultdict(int)
# out_degree = defaultdict(int)

# start
# out_degree[node] - in_degree[node] == 1:
# start = pairs[0][0]

# time
# 代码中首先遍历了所有的 pairs，即每对 [frm, to]。DFS。O(E)
# 寻找起点 O(V)

# space
# 邻接表存储, res： O(E)
# in_degree 和 out_degree DFS  O(V)

# Hierholzer's, Eulerian, DFS, defaultdict(deque)
# O(V+E), s:O(V+E) 
from collections import defaultdict, deque
class Solution:
    def validArrangement(self, pairs: List[List[int]]) -> List[List[int]]:
        graph = defaultdict(deque)
        in_degree = defaultdict(int)
        out_degree = defaultdict(int)

        for frm, to in pairs:
            graph[frm].append(to)
            out_degree[frm] += 1
            in_degree[to] += 1

        res = []
        def dfs(airport):
            while graph[airport]:
                next_airport = graph[airport].popleft()
                dfs(next_airport)
            res.append(airport)

        ## Case_#1:
        # There is eular circuit in graph, any arbitrary node can be start point, 
        # here we use source node of first edge just for convenience        
        start = pairs[0][0]

        # case # 2
        # there is no eular circut, there is eular path
        # 寻找起点：出度 - 入度 = 1 的节点作为起点
        for node in graph:
            if out_degree[node] - in_degree[node] == 1:
                start = node
                break

        dfs(start)
        res.reverse()      
        return [[res[i], res[i + 1]] for i in range(len(res) - 1)]          # 不要用zip, 因為 int not interable

2070