You are given a 0-indexed 2D integer array pairs
where pairs[i] = [starti, endi]
. An arrangement of pairs
is valid if for every index i
where 1 <= i < pairs.length
, we have endi-1 == starti
.
Return any valid arrangement of pairs
.
Note: The inputs will be generated such that there exists a valid arrangement of pairs
.
Example 1:
Input: pairs = [[5,1],[4,5],[11,9],[9,4]] Output: [[11,9],[9,4],[4,5],[5,1]] Explanation: This is a valid arrangement since endi-1 always equals starti. end0 = 9 == 9 = start1 end1 = 4 == 4 = start2 end2 = 5 == 5 = start3
Example 2:
Input: pairs = [[1,3],[3,2],[2,1]] Output: [[1,3],[3,2],[2,1]] Explanation: This is a valid arrangement since endi-1 always equals starti. end0 = 3 == 3 = start1 end1 = 2 == 2 = start2 The arrangements [[2,1],[1,3],[3,2]] and [[3,2],[2,1],[1,3]] are also valid.
Example 3:
Input: pairs = [[1,2],[1,3],[2,1]] Output: [[1,2],[2,1],[1,3]] Explanation: This is a valid arrangement since endi-1 always equals starti. end0 = 2 == 2 = start1 end1 = 1 == 1 = start2
Constraints:
1 <= pairs.length <= 105
pairs[i].length == 2
0 <= starti, endi <= 109
starti != endi
pairs
.This problem involves finding a valid arrangement of pairs, where each pair's end element equals the next pair's start element. This is essentially finding an Eulerian path (or circuit) in a directed graph.
Approach:
Build an Adjacency List: Create a directed graph where each unique number in the pairs
array represents a node. An edge from node u
to node v
exists if the pair [u, v]
is present. We use a dictionary (or hash map) to store the adjacency list efficiently.
Find the Starting Node: Determine the starting node for our path. This is the node with an in-degree (number of incoming edges) less than its out-degree (number of outgoing edges). This is because in an Eulerian path, the starting node has one more outgoing edge than incoming edges. If all nodes have equal in-degree and out-degree, then we have an Eulerian circuit and any node can be the start.
Depth-First Search (DFS): Perform a Depth-First Search (DFS) to traverse the graph and construct the valid arrangement. During the DFS, we keep track of the visited edges.
Reverse the Path: The DFS will give us a path in reverse order. Reverse the resulting path to obtain the correct valid arrangement.
Time Complexity Analysis:
pairs
.Therefore, the overall time complexity is dominated by the DFS and is O(N).
Space Complexity Analysis:
The overall space complexity is O(N).
Code Implementation (Python):
from collections import defaultdict
def validArrangement(pairs):
graph = defaultdict(list)
in_degree = defaultdict(int)
out_degree = defaultdict(int)
nodes = set()
for u, v in pairs:
graph[u].append(v)
out_degree[u] += 1
in_degree[v] += 1
nodes.add(u)
nodes.add(v)
start_node = next((node for node in nodes if out_degree[node] > in_degree[node]),list(nodes)[0]) #Finds a starting node, or picks the first if it is an Eulerian circuit
path = []
visited = set()
def dfs(node):
for neighbor in graph[node]:
edge = (node, neighbor)
if edge not in visited:
visited.add(edge)
dfs(neighbor)
path.append(node)
dfs(start_node)
return [[path[i], path[i+1]] for i in range(len(path)-1)][::-1]
Code Implementation (Java):
import java.util.*;
class Solution {
public int[][] validArrangement(int[][] pairs) {
Map<Integer, List<Integer>> graph = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();
Map<Integer, Integer> outDegree = new HashMap<>();
Set<Integer> nodes = new HashSet<>();
for (int[] pair : pairs) {
int u = pair[0];
int v = pair[1];
graph.computeIfAbsent(u, k -> new ArrayList<>()).add(v);
outDegree.put(u, outDegree.getOrDefault(u, 0) + 1);
inDegree.put(v, inDegree.getOrDefault(v, 0) + 1);
nodes.add(u);
nodes.add(v);
}
int startNode = nodes.iterator().next(); //Arbitrary start if Eulerian circuit, otherwise below line would find a better one
for (int node : nodes) {
if (outDegree.getOrDefault(node, 0) > inDegree.getOrDefault(node, 0)) {
startNode = node;
break;
}
}
List<Integer> path = new ArrayList<>();
Set<Pair> visited = new HashSet<>();
dfs(startNode, graph, visited, path);
Collections.reverse(path);
int[][] result = new int[path.size() -1][2];
for (int i = 0; i < path.size() - 1; i++) {
result[i][0] = path.get(i);
result[i][1] = path.get(i + 1);
}
return result;
}
private void dfs(int node, Map<Integer, List<Integer>> graph, Set<Pair> visited, List<Integer> path) {
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
Pair edge = new Pair(node, neighbor);
if (!visited.contains(edge)) {
visited.add(edge);
dfs(neighbor, graph, visited, path);
}
}
path.add(node);
}
private static class Pair {
int u;
int v;
Pair(int u, int v) {
this.u = u;
this.v = v;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Pair other = (Pair) obj;
return u == other.u && v == other.v;
}
@Override
public int hashCode() {
return Objects.hash(u, v);
}
}
}
This detailed explanation and code examples should provide a clear understanding of how to solve the "Valid Arrangement of Pairs" problem efficiently. Remember to handle edge cases (like empty input) in a production-ready solution.