Hard

题目描述

给你一个二维整数数组 edges,表示一个有 n 个节点的无向图,其中 edges[i] = [ui, vi] 表示节点 uivi 之间有一条边。

构造一个满足以下条件的二维网格:

  • 网格的单元格包含从 0 到 n - 1 的所有节点,每个节点恰好出现一次。
  • 当且仅当两个节点在 edges 中有边连接时,它们才应该在网格中相邻(水平或垂直相邻)。

题目保证给定的 edges 可以形成满足条件的二维网格。

返回满足上述条件的二维整数数组。如果有多个解,返回其中任何一个。

示例 1:

输入:n = 4, edges = [[0,1],[0,2],[1,3],[2,3]]
输出:[[3,1],[2,0]]

示例 2:

输入:n = 5, edges = [[0,1],[1,3],[2,3],[2,4]]
输出:[[4,2,3,1,0]]

示例 3:

输入:n = 9, edges = [[0,1],[0,4],[0,5],[1,7],[2,3],[2,4],[2,5],[3,6],[4,6],[4,7],[6,8],[7,8]]
输出:[[8,6,3],[7,4,2],[1,0,5]]

约束:

  • 2 <= n <= 5 * 10^4
  • 1 <= edges.length <= 10^5
  • edges[i] = [ui, vi]
  • 0 <= ui < vi < n
  • 所有边都是不同的
  • 输入保证 edges 可以形成满足条件的二维网格

解题思路

这道题的核心思路是通过分析节点的度数来确定网格的结构:

  1. 度数分析:在二维网格中,角落节点度数为2,边缘节点度数为3,内部节点度数为4。特殊情况下,如果是一行或一列,端点度数为1,中间节点度数为2。

  2. 确定网格尺寸

    • 如果存在度数为1的节点,说明是一行,行数为1,列数为n
    • 否则,找到度数为2的节点(角落节点),通过BFS确定行列数
  3. 构造第一行

    • 从一个角落节点开始,通过BFS找到第一行的所有节点
    • 利用度数信息确定下一个节点(在第一行中,除了端点外,其他节点都有一个未访问的邻居在下一行)
  4. 逐行构造

    • 对于后续的每一行,通过当前行节点的未访问邻居来构造
    • 使用BFS或DFS确保正确的顺序
  5. 实现细节

    • 建立邻接表存储图结构
    • 使用visited数组标记已访问节点
    • 通过度数判断网格的几何形状

这种方法充分利用了网格图的结构特性,时间复杂度主要由图遍历决定,空间复杂度主要用于存储邻接表和结果网格。

代码实现

class Solution {
public:
    vector<vector<int>> constructGridLayout(int n, vector<vector<int>>& edges) {
        vector<vector<int>> adj(n);
        vector<int> degree(n, 0);
        
        for (auto& edge : edges) {
            adj[edge[0]].push_back(edge[1]);
            adj[edge[1]].push_back(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }
        
        // Find nodes with minimum degree
        int minDegree = *min_element(degree.begin(), degree.end());
        vector<int> candidates;
        for (int i = 0; i < n; i++) {
            if (degree[i] == minDegree) {
                candidates.push_back(i);
            }
        }
        
        // If minimum degree is 1, it's a single row
        if (minDegree == 1) {
            vector<int> row(n);
            vector<bool> visited(n, false);
            int start = candidates[0];
            row[0] = start;
            visited[start] = true;
            
            for (int i = 1; i < n; i++) {
                for (int neighbor : adj[row[i-1]]) {
                    if (!visited[neighbor]) {
                        row[i] = neighbor;
                        visited[neighbor] = true;
                        break;
                    }
                }
            }
            return {row};
        }
        
        // Find corner (degree 2) and construct grid
        int corner = candidates[0];
        vector<bool> visited(n, false);
        
        // Build first row starting from corner
        vector<int> firstRow = {corner};
        visited[corner] = true;
        
        // Find the direction for first row
        int current = corner;
        while (true) {
            int next = -1;
            for (int neighbor : adj[current]) {
                if (!visited[neighbor]) {
                    // Check if this neighbor can be in the same row
                    // A node is in the same row if adding it doesn't create a vertical connection yet
                    bool canBeInRow = true;
                    for (int nn : adj[neighbor]) {
                        if (!visited[nn] && nn != current) {
                            // Check if nn could be below neighbor
                            int commonNeighbors = 0;
                            for (int cn : adj[nn]) {
                                if (visited[cn] || cn == neighbor) commonNeighbors++;
                            }
                            if (commonNeighbors == 1) {
                                canBeInRow = true;
                                break;
                            }
                        }
                    }
                    if (next == -1) next = neighbor;
                    else {
                        // Choose the one that extends the row
                        if (degree[neighbor] <= degree[next]) {
                            next = neighbor;
                        }
                    }
                }
            }
            if (next == -1) break;
            firstRow.push_back(next);
            visited[next] = true;
            current = next;
            if (degree[current] == 2) break; // reached another corner
        }
        
        int cols = firstRow.size();
        int rows = n / cols;
        vector<vector<int>> result(rows, vector<int>(cols));
        
        // Fill first row
        for (int j = 0; j < cols; j++) {
            result[0][j] = firstRow[j];
        }
        
        // Build remaining rows
        for (int i = 1; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                for (int neighbor : adj[result[i-1][j]]) {
                    if (!visited[neighbor]) {
                        result[i][j] = neighbor;
                        visited[neighbor] = true;
                        break;
                    }
                }
            }
        }
        
        return result;
    }
};
class Solution:
    def constructGridLayout(self, n: int, edges: List[List[int]]) -> List[List[int]]:
        from collections import defaultdict, deque
        
        adj = defaultdict(list)
        degree = [0] * n
        
        for u, v in edges:
            adj[u].append(v)
            adj[v].append(u)
            degree[u] += 1
            degree[v] += 1
        
        min_degree = min(degree)
        candidates = [i for i in range(n) if degree[i] == min_degree]
        
        # If minimum degree is 1, it's a single row
        if min_degree == 1:
            row = [0] * n
            visited = [False] * n
            start = candidates[0]
            row[0] = start
            visited[start] = True
            
            for i in range(1, n):
                for neighbor in adj[row[i-1]]:
                    if not visited[neighbor]:
                        row[i] = neighbor
                        visited[neighbor] = True
                        break
            return [row]
        
        # Find corner and construct grid
        corner = candidates[0]
        visited = [False] * n
        
        # Build first row starting from corner
        first_row = [corner]
        visited[corner] = True
        
        current = corner
        while True:
            next_node = -1
            for neighbor in adj[current]:
                if not visited[neighbor]:
                    if next_node == -1:
                        next_node = neighbor
                    elif degree[neighbor] <= degree[next_node]:
                        next_node = neighbor
            
            if next_node == -1:
                break
                
            first_row.append(next_node)
            visited[next_node] = True
            current = next_node
            
            if degree[current] == 2:  # reached another corner
                break
        
        cols = len(first_row)
        rows = n // cols
        result = [[0] * cols for _ in range(rows)]
        
        # Fill first row
        for j in range(cols):
            result[0][j] = first_row[j]
        
        # Build remaining rows
        for i in range(1, rows):
            for j in range(cols):
                for neighbor in adj[result[i-1][j]]:
                    if not visited[neighbor]:
                        result[i][j] = neighbor
                        visited[neighbor] = True
                        break
        
        return result
public class Solution {
    public int[][] ConstructGridLayout(int n, int[][] edges) {
        List<int>[] adj = new List<int>[n];
        int[] degree = new int[n];
        
        for (int i = 0; i < n; i++) {
            adj[i] = new List<int>();
        }
        
        foreach (var edge in edges) {
            adj[edge[0]].Add(edge[1]);
            adj[edge[1]].Add(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }
        
        int minDegree = degree.Min();
        List<int> candidates = new List<int>();
        for (int i = 0; i < n; i++) {
            if (degree[i] == minDegree) {
                candidates.Add(i);
            }
        }
        
        // If minimum degree is 1, it's a single row
        if (minDegree == 1) {
            int[] row = new int[n];
            bool[] visited = new bool[n];
            int start = candidates[0];
            row[0] = start;
            visited[start] = true;
            
            for (int i = 1; i < n; i++) {
                foreach (int neighbor in adj[row[i-1]]) {
                    if (!visited[neighbor]) {
                        row[i] = neighbor;
                        visited[neighbor] = true;
                        break;
                    }
                }
            }
            return new int[][] { row };
        }
        
        // Find corner and construct grid
        int corner = candidates[0];
        bool[] visited2 = new bool[n];
        
        // Build first row starting from corner
        List<int> firstRow = new List<int> { corner };
        visited2[corner] = true;
        
        int current = corner;
        while (true) {
            int nextNode = -1;
            foreach (int neighbor in adj[current]) {
                if (!visited2[neighbor]) {
                    if (nextNode == -1) {
                        nextNode = neighbor;
                    } else if (degree[neighbor] <= degree[nextNode]) {
                        nextNode = neighbor;
                    }
                }
            }
            
            if (nextNode == -1) break;
            
            firstRow.Add(nextNode);
            visited2[nextNode] = true;
            current = nextNode;
            
            if (degree[current] == 2) break; // reached another corner
        }
        
        int cols = firstRow.Count;
        int rows = n / cols;
        int[][] result = new int[rows][];
        
        for (int i = 0; i < rows; i++) {
            result[i] = new int[cols];
        }
        
        // Fill first row
        for (int j = 0; j < cols; j++) {
            result[0][j] = firstRow[j];
        }
        
        // Build remaining rows
        for (int i = 1; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                foreach (int neighbor in adj[result[i-1][j]]) {
                    if (!visited2[neighbor]) {
                        result[i][j] = neighbor;
                        visited2[neighbor] = true;
                        break;
                    }
                }
            }
        }
        
        return result;
    }
}
var constructGridLayout = function(n, edges) {
    const adj = Array.from({length: n}, () => []);
    const degree = new Array(n).fill(0);
    
    for (const [u, v] of edges) {
        adj[u].push(v);
        adj[v].push(u);
        degree[u]++;
        degree[v]++;
    }
    
    const minDegree = Math.min(...degree);
    const candidates = [];
    for (let i = 0; i < n; i++) {
        if (degree[i]

复杂度分析

指标复杂度
时间-
空间-