|
54 | 54 |
|
55 | 55 | <!-- 这里可写通用的实现逻辑 -->
|
56 | 56 |
|
| 57 | +**方法一:拓扑排序** |
| 58 | + |
| 59 | +我们先遍历数组 $group$,对于每个项目,如果其不属于任何小组,则将其新建一个小组,编号为 $m$,并将 $m$ 的值加一。这样我们就能保证所有项目都属于某个小组了。然后,我们用一个数组 $groupItems$ 记录每个小组包含的项目,数组下标为小组编号,数组值为包含的项目列表。 |
| 60 | + |
| 61 | +接下来,我们需要建图。对于每个项目,我们需要建立两种图,一种是项目间的图,一种是小组间的图。我们遍历数组 $group$,对于当前项目 $i$,它所在的小组为 $group[i]$,我们遍历 $beforeItems[i]$,对于其中的每个项目 $j$,如果 $group[i] = group[j]$,说明 $i$ 和 $j$ 属于同一小组,我们在项目图中添加一条 $j \to i$ 的边,否则说明 $i$ 和 $j$ 属于不同的小组,我们在小组间的图中添加一条 $group[j] \to group[i]$ 的边,并且更新对应的入度数组。 |
| 62 | + |
| 63 | +接下来,我们先对小组间的图进行拓扑排序,得到排序后的小组列表 $groupOrder$,如果排序后的列表长度不等于小组总数,说明无法完成排序,返回空列表。否则,我们遍历 $groupOrder$,对于其中的每个小组 $gi$,我们将该小组包含的项目列表 $groupItems[gi]$ 进行拓扑排序,得到排序后的项目列表 $itemOrder$,如果排序后的列表长度不等于该小组包含的项目总数,说明无法完成排序,返回空列表。否则,我们将 $itemOrder$ 中的项目加入答案数组中,最终返回该答案数组即可。 |
| 64 | + |
| 65 | +时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是项目总数和小组总数。 |
| 66 | + |
57 | 67 | <!-- tabs:start -->
|
58 | 68 |
|
59 | 69 | ### **Python3**
|
60 | 70 |
|
61 | 71 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
62 | 72 |
|
63 | 73 | ```python
|
| 74 | +class Solution: |
| 75 | + def sortItems( |
| 76 | + self, n: int, m: int, group: List[int], beforeItems: List[List[int]] |
| 77 | + ) -> List[int]: |
| 78 | + def topo_sort(degree, graph, items): |
| 79 | + q = deque(i for _, i in enumerate(items) if degree[i] == 0) |
| 80 | + res = [] |
| 81 | + while q: |
| 82 | + i = q.popleft() |
| 83 | + res.append(i) |
| 84 | + for j in graph[i]: |
| 85 | + degree[j] -= 1 |
| 86 | + if degree[j] == 0: |
| 87 | + q.append(j) |
| 88 | + return res if len(res) == len(items) else [] |
| 89 | + |
| 90 | + idx = m |
| 91 | + group_items = [[] for _ in range(n + m)] |
| 92 | + for i, g in enumerate(group): |
| 93 | + if g == -1: |
| 94 | + group[i] = idx |
| 95 | + idx += 1 |
| 96 | + group_items[group[i]].append(i) |
64 | 97 |
|
| 98 | + item_degree = [0] * n |
| 99 | + group_degree = [0] * (n + m) |
| 100 | + item_graph = [[] for _ in range(n)] |
| 101 | + group_graph = [[] for _ in range(n + m)] |
| 102 | + for i, gi in enumerate(group): |
| 103 | + for j in beforeItems[i]: |
| 104 | + gj = group[j] |
| 105 | + if gi == gj: |
| 106 | + item_degree[i] += 1 |
| 107 | + item_graph[j].append(i) |
| 108 | + else: |
| 109 | + group_degree[gi] += 1 |
| 110 | + group_graph[gj].append(gi) |
| 111 | + |
| 112 | + group_order = topo_sort(group_degree, group_graph, range(n + m)) |
| 113 | + if not group_order: |
| 114 | + return [] |
| 115 | + ans = [] |
| 116 | + for gi in group_order: |
| 117 | + items = group_items[gi] |
| 118 | + item_order = topo_sort(item_degree, item_graph, items) |
| 119 | + if len(items) != len(item_order): |
| 120 | + return [] |
| 121 | + ans.extend(item_order) |
| 122 | + return ans |
65 | 123 | ```
|
66 | 124 |
|
67 | 125 | ### **Java**
|
68 | 126 |
|
69 | 127 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
70 | 128 |
|
71 | 129 | ```java
|
| 130 | +class Solution { |
| 131 | + public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) { |
| 132 | + int idx = m; |
| 133 | + List<Integer>[] groupItems = new List[n + m]; |
| 134 | + int[] itemDegree = new int[n]; |
| 135 | + int[] groupDegree = new int[n + m]; |
| 136 | + List<Integer>[] itemGraph = new List[n]; |
| 137 | + List<Integer>[] groupGraph = new List[n + m]; |
| 138 | + Arrays.setAll(groupItems, k -> new ArrayList<>()); |
| 139 | + Arrays.setAll(itemGraph, k -> new ArrayList<>()); |
| 140 | + Arrays.setAll(groupGraph, k -> new ArrayList<>()); |
| 141 | + for (int i = 0; i < n; ++i) { |
| 142 | + if (group[i] == -1) { |
| 143 | + group[i] = idx++; |
| 144 | + } |
| 145 | + groupItems[group[i]].add(i); |
| 146 | + } |
| 147 | + for (int i = 0; i < n; ++i) { |
| 148 | + for (int j : beforeItems.get(i)) { |
| 149 | + if (group[i] == group[j]) { |
| 150 | + ++itemDegree[i]; |
| 151 | + itemGraph[j].add(i); |
| 152 | + } else { |
| 153 | + ++groupDegree[group[i]]; |
| 154 | + groupGraph[group[j]].add(group[i]); |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + List<Integer> items = new ArrayList<>(); |
| 159 | + for (int i = 0; i < n + m; ++i) { |
| 160 | + items.add(i); |
| 161 | + } |
| 162 | + var groupOrder = topoSort(groupDegree, groupGraph, items); |
| 163 | + if (groupOrder.isEmpty()) { |
| 164 | + return new int[0]; |
| 165 | + } |
| 166 | + List<Integer> ans = new ArrayList<>(); |
| 167 | + for (int gi : groupOrder) { |
| 168 | + items = groupItems[gi]; |
| 169 | + var itemOrder = topoSort(itemDegree, itemGraph, items); |
| 170 | + if (itemOrder.size() != items.size()) { |
| 171 | + return new int[0]; |
| 172 | + } |
| 173 | + ans.addAll(itemOrder); |
| 174 | + } |
| 175 | + return ans.stream().mapToInt(Integer::intValue).toArray(); |
| 176 | + } |
| 177 | + |
| 178 | + private List<Integer> topoSort(int[] degree, List<Integer>[] graph, List<Integer> items) { |
| 179 | + Deque<Integer> q = new ArrayDeque<>(); |
| 180 | + for (int i : items) { |
| 181 | + if (degree[i] == 0) { |
| 182 | + q.offer(i); |
| 183 | + } |
| 184 | + } |
| 185 | + List<Integer> ans = new ArrayList<>(); |
| 186 | + while (!q.isEmpty()) { |
| 187 | + int i = q.poll(); |
| 188 | + ans.add(i); |
| 189 | + for (int j : graph[i]) { |
| 190 | + if (--degree[j] == 0) { |
| 191 | + q.offer(j); |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + return ans.size() == items.size() ? ans : List.of(); |
| 196 | + } |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +### **C++** |
| 201 | + |
| 202 | +```cpp |
| 203 | +class Solution { |
| 204 | +public: |
| 205 | + vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) { |
| 206 | + int idx = m; |
| 207 | + vector<vector<int>> groupItems(n + m); |
| 208 | + vector<int> itemDegree(n); |
| 209 | + vector<int> groupDegree(n + m); |
| 210 | + vector<vector<int>> itemGraph(n); |
| 211 | + vector<vector<int>> groupGraph(n + m); |
| 212 | + for (int i = 0; i < n; ++i) { |
| 213 | + if (group[i] == -1) { |
| 214 | + group[i] = idx++; |
| 215 | + } |
| 216 | + groupItems[group[i]].push_back(i); |
| 217 | + } |
| 218 | + for (int i = 0; i < n; ++i) { |
| 219 | + for (int j : beforeItems[i]) { |
| 220 | + if (group[i] == group[j]) { |
| 221 | + ++itemDegree[i]; |
| 222 | + itemGraph[j].push_back(i); |
| 223 | + } else { |
| 224 | + ++groupDegree[group[i]]; |
| 225 | + groupGraph[group[j]].push_back(group[i]); |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | + vector<int> items(n + m); |
| 230 | + iota(items.begin(), items.end(), 0); |
| 231 | + auto topoSort = [](vector<vector<int>>& graph, vector<int>& degree, vector<int>& items) -> vector<int> { |
| 232 | + queue<int> q; |
| 233 | + for (int& i : items) { |
| 234 | + if (degree[i] == 0) { |
| 235 | + q.push(i); |
| 236 | + } |
| 237 | + } |
| 238 | + vector<int> ans; |
| 239 | + while (!q.empty()) { |
| 240 | + int i = q.front(); |
| 241 | + q.pop(); |
| 242 | + ans.push_back(i); |
| 243 | + for (int& j : graph[i]) { |
| 244 | + if (--degree[j] == 0) { |
| 245 | + q.push(j); |
| 246 | + } |
| 247 | + } |
| 248 | + } |
| 249 | + return ans.size() == items.size() ? ans : vector<int>(); |
| 250 | + }; |
| 251 | + auto groupOrder = topoSort(groupGraph, groupDegree, items); |
| 252 | + if (groupOrder.empty()) { |
| 253 | + return vector<int>(); |
| 254 | + } |
| 255 | + vector<int> ans; |
| 256 | + for (int& gi : groupOrder) { |
| 257 | + items = groupItems[gi]; |
| 258 | + auto itemOrder = topoSort(itemGraph, itemDegree, items); |
| 259 | + if (items.size() != itemOrder.size()) { |
| 260 | + return vector<int>(); |
| 261 | + } |
| 262 | + ans.insert(ans.end(), itemOrder.begin(), itemOrder.end()); |
| 263 | + } |
| 264 | + return ans; |
| 265 | + } |
| 266 | +}; |
| 267 | +``` |
| 268 | +
|
| 269 | +### **Go** |
| 270 | +
|
| 271 | +```go |
| 272 | +func sortItems(n int, m int, group []int, beforeItems [][]int) []int { |
| 273 | + idx := m |
| 274 | + groupItems := make([][]int, n+m) |
| 275 | + itemDegree := make([]int, n) |
| 276 | + groupDegree := make([]int, n+m) |
| 277 | + itemGraph := make([][]int, n) |
| 278 | + groupGraph := make([][]int, n+m) |
| 279 | + for i, g := range group { |
| 280 | + if g == -1 { |
| 281 | + group[i] = idx |
| 282 | + idx++ |
| 283 | + } |
| 284 | + groupItems[group[i]] = append(groupItems[group[i]], i) |
| 285 | + } |
| 286 | + for i, gi := range group { |
| 287 | + for _, j := range beforeItems[i] { |
| 288 | + gj := group[j] |
| 289 | + if gi == gj { |
| 290 | + itemDegree[i]++ |
| 291 | + itemGraph[j] = append(itemGraph[j], i) |
| 292 | + } else { |
| 293 | + groupDegree[gi]++ |
| 294 | + groupGraph[gj] = append(groupGraph[gj], gi) |
| 295 | + } |
| 296 | + } |
| 297 | + } |
| 298 | + items := make([]int, n+m) |
| 299 | + for i := range items { |
| 300 | + items[i] = i |
| 301 | + } |
| 302 | + topoSort := func(degree []int, graph [][]int, items []int) []int { |
| 303 | + q := []int{} |
| 304 | + for _, i := range items { |
| 305 | + if degree[i] == 0 { |
| 306 | + q = append(q, i) |
| 307 | + } |
| 308 | + } |
| 309 | + ans := []int{} |
| 310 | + for len(q) > 0 { |
| 311 | + i := q[0] |
| 312 | + q = q[1:] |
| 313 | + ans = append(ans, i) |
| 314 | + for _, j := range graph[i] { |
| 315 | + degree[j]-- |
| 316 | + if degree[j] == 0 { |
| 317 | + q = append(q, j) |
| 318 | + } |
| 319 | + } |
| 320 | + } |
| 321 | + return ans |
| 322 | + } |
| 323 | + groupOrder := topoSort(groupDegree, groupGraph, items) |
| 324 | + if len(groupOrder) != len(items) { |
| 325 | + return nil |
| 326 | + } |
| 327 | + ans := []int{} |
| 328 | + for _, gi := range groupOrder { |
| 329 | + items = groupItems[gi] |
| 330 | + itemOrder := topoSort(itemDegree, itemGraph, items) |
| 331 | + if len(items) != len(itemOrder) { |
| 332 | + return nil |
| 333 | + } |
| 334 | + ans = append(ans, itemOrder...) |
| 335 | + } |
| 336 | + return ans |
| 337 | +} |
| 338 | +``` |
| 339 | + |
| 340 | +### **TypeScript** |
72 | 341 |
|
| 342 | +```ts |
| 343 | +function sortItems( |
| 344 | + n: number, |
| 345 | + m: number, |
| 346 | + group: number[], |
| 347 | + beforeItems: number[][], |
| 348 | +): number[] { |
| 349 | + let idx = m; |
| 350 | + const groupItems: number[][] = new Array(n + m).fill(0).map(() => []); |
| 351 | + const itemDegree: number[] = new Array(n).fill(0); |
| 352 | + const gorupDegree: number[] = new Array(n + m).fill(0); |
| 353 | + const itemGraph: number[][] = new Array(n).fill(0).map(() => []); |
| 354 | + const groupGraph: number[][] = new Array(n + m).fill(0).map(() => []); |
| 355 | + for (let i = 0; i < n; ++i) { |
| 356 | + if (group[i] === -1) { |
| 357 | + group[i] = idx++; |
| 358 | + } |
| 359 | + groupItems[group[i]].push(i); |
| 360 | + } |
| 361 | + for (let i = 0; i < n; ++i) { |
| 362 | + for (const j of beforeItems[i]) { |
| 363 | + if (group[i] === group[j]) { |
| 364 | + ++itemDegree[i]; |
| 365 | + itemGraph[j].push(i); |
| 366 | + } else { |
| 367 | + ++gorupDegree[group[i]]; |
| 368 | + groupGraph[group[j]].push(group[i]); |
| 369 | + } |
| 370 | + } |
| 371 | + } |
| 372 | + let items = new Array(n + m).fill(0).map((_, i) => i); |
| 373 | + const topoSort = ( |
| 374 | + graph: number[][], |
| 375 | + degree: number[], |
| 376 | + items: number[], |
| 377 | + ): number[] => { |
| 378 | + const q: number[] = []; |
| 379 | + for (const i of items) { |
| 380 | + if (degree[i] === 0) { |
| 381 | + q.push(i); |
| 382 | + } |
| 383 | + } |
| 384 | + const ans: number[] = []; |
| 385 | + while (q.length) { |
| 386 | + const i = q.pop()!; |
| 387 | + ans.push(i); |
| 388 | + for (const j of graph[i]) { |
| 389 | + if (--degree[j] === 0) { |
| 390 | + q.push(j); |
| 391 | + } |
| 392 | + } |
| 393 | + } |
| 394 | + return ans.length === items.length ? ans : []; |
| 395 | + }; |
| 396 | + const groupOrder = topoSort(groupGraph, gorupDegree, items); |
| 397 | + if (groupOrder.length === 0) { |
| 398 | + return []; |
| 399 | + } |
| 400 | + const ans: number[] = []; |
| 401 | + for (const gi of groupOrder) { |
| 402 | + items = groupItems[gi]; |
| 403 | + const itemOrder = topoSort(itemGraph, itemDegree, items); |
| 404 | + if (itemOrder.length !== items.length) { |
| 405 | + return []; |
| 406 | + } |
| 407 | + ans.push(...itemOrder); |
| 408 | + } |
| 409 | + return ans; |
| 410 | +} |
73 | 411 | ```
|
74 | 412 |
|
75 | 413 | ### **...**
|
|
0 commit comments