一、题目描述
二、分析
不难发现,这个问题其实就是将不同的数字作为图中的顶点,将所有 pair 的头尾相连,然后找到一条访问所有边恰好一次的通路。
三、算法
1、欧拉通路
上述分析和欧拉通路的定义是相同的。欧拉图相关的定义和算法可以参考 『图论』入门以及 Hierholzer 算法。
该算法的描述如下:
(1)实现
下面的算法记录了所有点的入节点和出节点以比较:
struct Vertex {
unordered_set<int> ins;
unordered_set<int> outs;
};
vector<int> hierholzer(unordered_map<int, Vertex>& vertexes) {
// 确定欧拉回路的起点
int startIndex = vertexes.begin()->first;
int n = vertexes.size();
for (auto& vertex : vertexes) {
if (vertex.second.outs.size() - vertex.second.ins.size() == 1) {
startIndex = vertex.first;
}
}
// 使用 dfs 确定环路的访问逆序
stack<int> s;
function<void(int)> dfs = [&](int index) {
while (1) {
if (vertexes[index].outs.empty()) {
break;
}
// 删除一条相邻边
auto iter = vertexes[index].outs.begin();
int next = *iter;
vertexes[index].outs.erase(iter);
vertexes[next].ins.erase(index);
dfs(next);
}
// 没有相邻边,将顶点入栈
if (vertexes[index].outs.empty()) {
s.push(index);
}
};
dfs(startIndex);
// 逆序返回
vector<int> ret;
while (!s.empty()) {
ret.push_back(s.top());
s.pop();
}
return ret;
}
(2)优化
实际上,除了确定起点时需要比较出度和入度,其余情况下我们只需要节点的出度。因此,我们可以仅保留入度的个数,而无需在过程中时刻维护入度的数量。在只需要更新出度节点的情况下,我们可以使用 vector 来优化删除速度。优化后的实现如下:
vector<int> hierholzer(unordered_map<int, vector<int>>& edges, unordered_map<int, int>& inDegrees, unordered_map<int, int>& outDegrees) {
int startIndex = inDegrees.begin()->first;
for (auto& out : outDegrees) {
int index = out.first;
if (out.second > inDegrees[index]) {
startIndex = index;
break;
}
}
stack<int> s;
function<void(int)> dfs = [&](int index) {
while (!edges[index].empty()) {
auto next = edges[index].back();
edges[index].pop_back();
dfs(next);
}
s.push(index);
};
dfs(startIndex);
vector<int> ret;
while (!s.empty()) {
ret.push_back(s.top());
s.pop();
}
return ret;
}
2、实现
class Solution {
public:
vector<vector<int>> validArrangement(vector<vector<int>>& pairs) {
unordered_map<int, int> inDegrees, outDegrees;
unordered_map<int, vector<int>> edges;
for (auto& p : pairs) {
++outDegrees[p[0]];
++inDegrees[p[1]];
edges[p[0]].push_back(p[1]);
}
auto circle = hierholzer(edges, inDegrees, outDegrees);
int c = circle.size();
vector<vector<int>> ret(c - 1, vector<int>(2));
for (int i = 0; i < circle.size() - 1; ++i) {
ret[i][0] = circle[i];
ret[i][1] = circle[i + 1];
}
return ret;
}
};