diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 551c39bf..1c7ff0cd 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -214,6 +214,7 @@ class Solution: return result ``` +贪心法 ```python class Solution: def maxSubArray(self, nums): @@ -226,8 +227,18 @@ class Solution: if count <= 0: # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和 count = 0 return result - - +``` +动态规划 +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + dp = [0] * len(nums) + dp[0] = nums[0] + res = nums[0] + for i in range(1, len(nums)): + dp[i] = max(dp[i-1] + nums[i], nums[i]) + res = max(res, dp[i]) + return res ``` ### Go 贪心法 diff --git a/problems/0070.爬楼梯完全背包版本.md b/problems/0070.爬楼梯完全背包版本.md index 07e0261e..c51a590b 100644 --- a/problems/0070.爬楼梯完全背包版本.md +++ b/problems/0070.爬楼梯完全背包版本.md @@ -184,6 +184,16 @@ if __name__ == '__main__': ### Go: ```go +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + func climbStairs(n int, m int) int { dp := make([]int, n+1) dp[0] = 1 diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index d1300a39..eb81f4b6 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -467,9 +467,37 @@ class Solution: num = int(s[start:end+1]) return 0 <= num <= 255 +回溯(版本三) - - +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + result = [] + self.backtracking(s, 0, [], result) + return result + + def backtracking(self, s, startIndex, path, result): + if startIndex == len(s): + result.append('.'.join(path[:])) + return + + for i in range(startIndex, min(startIndex+3, len(s))): + # 如果 i 往后遍历了,并且当前地址的第一个元素是 0 ,就直接退出 + if i > startIndex and s[startIndex] == '0': + break + # 比如 s 长度为 5,当前遍历到 i = 3 这个元素 + # 因为还没有执行任何操作,所以此时剩下的元素数量就是 5 - 3 = 2 ,即包括当前的 i 本身 + # path 里面是当前包含的子串,所以有几个元素就表示储存了几个地址 + # 所以 (4 - len(path)) * 3 表示当前路径至多能存放的元素个数 + # 4 - len(path) 表示至少要存放的元素个数 + if (4 - len(path)) * 3 < len(s) - i or 4 - len(path) > len(s) - i: + break + if i - startIndex == 2: + if not int(s[startIndex:i+1]) <= 255: + break + path.append(s[startIndex:i+1]) + self.backtracking(s, i+1, path, result) + path.pop() ``` ### Go diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index f8071333..a4339ac3 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -609,10 +609,13 @@ class Solution: while stack: node = stack.pop() if node: - stack.append(node) + stack.append(node) # 中 stack.append(None) - if node.left: stack.append(node.left) - if node.right: stack.append(node.right) + # 采用数组进行迭代,先将右节点加入,保证左节点能够先出栈 + if node.right: # 右 + stack.append(node.right) + if node.left: # 左 + stack.append(node.left) else: real_node = stack.pop() left, right = height_map.get(real_node.left, 0), height_map.get(real_node.right, 0) diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index 7ac9f0f9..5cf50b3e 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -158,7 +158,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i 如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。 -区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。 +区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择起始位置了。 **那么局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置**。 diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index a0005198..bf486bdc 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -475,7 +475,45 @@ class Solution: words = words[::-1] # 反转单词 return ' '.join(words) #列表转换成字符串 ``` +(版本四) 将字符串转换为列表后,使用双指针去除空格 +```python +class Solution: + def single_reverse(self, s, start: int, end: int): + while start < end: + s[start], s[end] = s[end], s[start] + start += 1 + end -= 1 + def reverseWords(self, s: str) -> str: + result = "" + fast = 0 + # 1. 首先将原字符串反转并且除掉空格, 并且加入到新的字符串当中 + # 由于Python字符串的不可变性,因此只能转换为列表进行处理 + s = list(s) + s.reverse() + while fast < len(s): + if s[fast] != " ": + if len(result) != 0: + result += " " + while s[fast] != " " and fast < len(s): + result += s[fast] + fast += 1 + else: + fast += 1 + # 2.其次将每个单词进行翻转操作 + slow = 0 + fast = 0 + result = list(result) + while fast <= len(result): + if fast == len(result) or result[fast] == " ": + self.single_reverse(result, slow, fast - 1) + slow = fast + 1 + fast += 1 + else: + fast += 1 + + return "".join(result) +``` ### Go: 版本一: diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 5e80e702..2e94f7a7 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -454,7 +454,11 @@ impl Solution { p: Option>>, q: Option>>, ) -> Option>> { - if root == p || root == q || root.is_none() { + if root.is_none() { + return root; + } + if Rc::ptr_eq(root.as_ref().unwrap(), p.as_ref().unwrap()) + || Rc::ptr_eq(root.as_ref().unwrap(), q.as_ref().unwrap()) { return root; } let left = Self::lowest_common_ancestor( diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 9ee7bef3..f1a146b7 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -337,6 +337,29 @@ pub fn length_of_lis(nums: Vec) -> i32 { } ``` +### Cangjie: + +```cangjie +func lengthOfLIS(nums: Array): Int64 { + let n = nums.size + if (n <= 1) { + return n + } + + let dp = Array(n, item: 1) + var res = 0 + for (i in 1..n) { + for (j in 0..i) { + if (nums[i] > nums[j]) { + dp[i] = max(dp[i], dp[j] + 1) + } + } + res = max(dp[i], res) + } + return res +} +``` +

diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 7295627f..3ba23e52 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -243,6 +243,29 @@ class Solution { } } ``` +贪心 +```Java +class Solution { + public int integerBreak(int n) { + // with 贪心 + // 通过数学原理拆出更多的3乘积越大,则 + /** + @Param: an int, the integer we need to break. + @Return: an int, the maximum integer after breaking + @Method: Using math principle to solve this problem + @Time complexity: O(1) + **/ + if(n == 2) return 1; + if(n == 3) return 2; + int result = 1; + while(n > 4) { + n-=3; + result *=3; + } + return result*n; + } +} +``` ### Python 动态规划(版本一) diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index bee1c102..2be8922b 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -177,7 +177,7 @@ KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一 ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913110841.png) -这里有录友就想:如果字符串s 是有是有最小重复子串p组成,最长相等前后缀就不能更长一些? 例如这样: +这里有录友就想:如果字符串s 是由最小重复子串p组成,最长相等前后缀就不能更长一些? 例如这样: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913114348.png) @@ -884,3 +884,4 @@ public int[] GetNext(string s) + diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index 19d34e4f..08724609 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -706,6 +706,57 @@ class Solution: ``` ### Go +二维dp +```go +func findTargetSumWays(nums []int, target int) int { + sum := 0 + for _, v := range nums { + sum += v + } + if math.Abs(float64(target)) > float64(sum) { + return 0 // 此时没有方案 + } + if (target + sum) % 2 == 1 { + return 0 // 此时没有方案 + } + bagSize := (target + sum) / 2 + + dp := make([][]int, len(nums)) + for i := range dp { + dp[i] = make([]int, bagSize + 1) + } + + // 初始化最上行 + if nums[0] <= bagSize { + dp[0][nums[0]] = 1 + } + + // 初始化最左列,最左列其他数值在递推公式中就完成了赋值 + dp[0][0] = 1 + + var numZero float64 + for i := range nums { + if nums[i] == 0 { + numZero++ + } + dp[i][0] = int(math.Pow(2, numZero)) + } + + // 以下遍历顺序行列可以颠倒 + for i := 1; i < len(nums); i++ { // 行,遍历物品 + for j := 0; j <= bagSize; j++ { // 列,遍历背包 + if nums[i] > j { + dp[i][j] = dp[i-1][j] + } else { + dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]] + } + } + } + return dp[len(nums)-1][bagSize] +} +``` + +一维dp ```go func findTargetSumWays(nums []int, target int) int { sum := 0 diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md index 360da582..70231212 100644 --- a/problems/0518.零钱兑换II.md +++ b/problems/0518.零钱兑换II.md @@ -288,6 +288,7 @@ class Solution: ### Go: +一维dp ```go func change(amount int, coins []int) int { // 定义dp数组 @@ -306,6 +307,29 @@ func change(amount int, coins []int) int { return dp[amount] } ``` +二维dp +```go +func change(amount int, coins []int) int { + dp := make([][]int, len(coins)) + for i := range dp { + dp[i] = make([]int, amount + 1) + dp[i][0] = 1 + } + for j := coins[0]; j <= amount; j++ { + dp[0][j] += dp[0][j-coins[0]] + } + for i := 1; i < len(coins); i++ { + for j := 1; j <= amount; j++ { + if j < coins[i] { + dp[i][j] = dp[i-1][j] + } else { + dp[i][j] = dp[i][j-coins[i]] + dp[i-1][j] + } + } + } + return dp[len(coins)-1][amount] +} +``` ### Rust: diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index cebb552b..57a38404 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -492,6 +492,25 @@ int findLengthOfLCIS(int* nums, int numsSize) { } ``` +### Cangjie + +```cangjie +func findLengthOfLCIS(nums: Array): Int64 { + let n = nums.size + if (n <= 1) { + return n + } + let dp = Array(n, repeat: 1) + var res = 0 + for (i in 1..n) { + if (nums[i] > nums[i - 1]) { + dp[i] = dp[i - 1] + 1 + } + res = max(res, dp[i]) + } + return res +} +``` diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index 6c8e7101..1391926a 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -581,6 +581,25 @@ int findLength(int* nums1, int nums1Size, int* nums2, int nums2Size) { } ``` +### Cangjie + +```cangjie +func findLength(nums1: Array, nums2: Array): Int64 { + let n = nums1.size + let m = nums2.size + let dp = Array(n + 1, {_ => Array(m + 1, item: 0)}) + var res = 0 + for (i in 1..=n) { + for (j in 1..=m) { + if (nums1[i - 1] == nums2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1 + } + res = max(res, dp[i][j]) + } + } + return res +} +```

diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md index 7fa7bb68..25f32838 100644 --- a/problems/1143.最长公共子序列.md +++ b/problems/1143.最长公共子序列.md @@ -399,6 +399,25 @@ int longestCommonSubsequence(char* text1, char* text2) { } ``` +### Cangjie + +```cangjie +func longestCommonSubsequence(text1: String, text2: String): Int64 { + let n = text1.size + let m = text2.size + let dp = Array(n + 1, {_ => Array(m + 1, repeat: 0)}) + for (i in 1..=n) { + for (j in 1..=m) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1 + } else { + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + } + } + } + return dp[n][m] +} +```

diff --git a/problems/kamacoder/0053.寻宝-Kruskal.md b/problems/kamacoder/0053.寻宝-Kruskal.md index cb24fd17..6a227985 100644 --- a/problems/kamacoder/0053.寻宝-Kruskal.md +++ b/problems/kamacoder/0053.寻宝-Kruskal.md @@ -549,6 +549,62 @@ if __name__ == "__main__": ### Javascript +```js +function kruskal(v, edges) { + const father = Array.from({ length: v + 1 }, (_, i) => i) + + function find(u){ + if (u === father[u]) { + return u + } else { + father[u] = find(father[u]) + return father[u] + } + + } + + function isSame(u, v) { + let s = find(u) + let t = find(v) + return s === t + } + + function join(u, v) { + let s = find(u) + let t = find(v) + if (s !== t) { + father[s] = t + } + } + + edges.sort((a, b) => a[2] - b[2]) + let result = 0 + for (const [v1, v2, w] of edges) { + if (!isSame(v1, v2)) { + result += w + join(v1 ,v2) + } + } + console.log(result) +} + + +async function main() { + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [v, e] = (await readline()).split(" ").map(Number) + const edges = [] + for (let i = 0 ; i < e ; i++) { + edges.push((await readline()).split(" ").map(Number)) + } + kruskal(v, edges) +} + + +main() +``` + ### TypeScript ### PhP diff --git a/problems/kamacoder/0053.寻宝-prim.md b/problems/kamacoder/0053.寻宝-prim.md index c71624b5..a8dad4cb 100644 --- a/problems/kamacoder/0053.寻宝-prim.md +++ b/problems/kamacoder/0053.寻宝-prim.md @@ -693,6 +693,55 @@ if __name__ == "__main__": ### Rust ### Javascript +```js +function prim(v, edges) { + const grid = Array.from({ length: v + 1 }, () => new Array(v + 1).fill(10001)); // Fixed grid initialization + const minDist = new Array(v + 1).fill(10001) + const isInTree = new Array(v + 1).fill(false) + // 建構鄰接矩陣 + for(const [v1, v2, w] of edges) { + grid[v1][v2] = w + grid[v2][v1] = w + } + // prim 演算法 + for (let i = 1 ; i < v ; i++) { + let cur = -1 + let tempMinDist = Number.MAX_VALUE + // 1. 尋找距離生成樹最近的節點 + for (let j = 1 ; j < v + 1 ; j++) { + if (!isInTree[j] && minDist[j] < tempMinDist) { + tempMinDist = minDist[j] + cur = j + } + } + // 2. 將節點放入生成樹 + isInTree[cur] = true + // 3. 更新非生成樹節點與生成樹的最短距離 + for (let j = 1 ; j < v + 1 ; j++) { + if (!isInTree[j] && grid[cur][j] < minDist[j]) { + minDist[j] = grid[cur][j] + } + } + } + console.log(minDist.slice(2).reduce((acc, cur) => acc + cur, 0)) +} + + +async function main() { + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [v, e] = (await readline()).split(" ").map(Number) + const edges = [] + for (let i = 0 ; i < e ; i++) { + edges.push((await readline()).split(" ").map(Number)) + } + prim(v, edges) +} + + +main() +``` ### TypeScript diff --git a/problems/kamacoder/0100.岛屿的最大面积.md b/problems/kamacoder/0100.岛屿的最大面积.md index ea62edc2..51bfc57f 100644 --- a/problems/kamacoder/0100.岛屿的最大面积.md +++ b/problems/kamacoder/0100.岛屿的最大面积.md @@ -480,7 +480,84 @@ const bfs = (graph, visited, x, y) => { })() ``` +```javascript +// 深搜版 + +const r1 = require('readline').createInterface({ input: process.stdin }); +// 创建readline接口 +let iter = r1[Symbol.asyncIterator](); +// 创建异步迭代器 +const readline = async () => (await iter.next()).value; + +let graph // 地图 +let N, M // 地图大小 +let visited // 访问过的节点 +let result = 0 // 最大岛屿面积 +let count = 0 // 岛屿内节点数 +const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 + +// 读取输入,初始化地图 +const initGraph = async () => { + let line = await readline(); + [N, M] = line.split(' ').map(Number); + graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) + visited = new Array(N).fill(false).map(() => new Array(M).fill(false)) + + for (let i = 0; i < N; i++) { + line = await readline() + line = line.split(' ').map(Number) + for (let j = 0; j < M; j++) { + graph[i][j] = line[j] + } + } +} + +/** + * @description: 从(x, y)开始深度优先遍历 + * @param {*} graph 地图 + * @param {*} visited 访问过的节点 + * @param {*} x 开始搜索节点的下标 + * @param {*} y 开始搜索节点的下标 + * @return {*} + */ +const dfs = (graph, visited, x, y) => { + for (let i = 0; i < 4; i++) { + let nextx = x + dir[i][0] + let nexty = y + dir[i][1] + if(nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue + if(!visited[nextx][nexty] && graph[nextx][nexty] === 1){ + count++ + visited[nextx][nexty] = true + dfs(graph, visited, nextx, nexty) + } + } +} + +(async function () { + + // 读取输入,初始化地图 + await initGraph() + + // 统计最大岛屿面积 + for (let i = 0; i < N; i++) { + for (let j = 0; j < M; j++) { + if (!visited[i][j] && graph[i][j] === 1) { //遇到没有访问过的陆地 + // 重新计算面积 + count = 1 + visited[i][j] = true + + // 深度优先遍历,统计岛屿内节点数,并将岛屿标记为已访问 + dfs(graph, visited, i, j) + + // 更新最大岛屿面积 + result = Math.max(result, count) + } + } + } + console.log(result); +})() +``` ### TypeScript diff --git a/problems/kamacoder/0101.孤岛的总面积.md b/problems/kamacoder/0101.孤岛的总面积.md index 006484de..26c92c07 100644 --- a/problems/kamacoder/0101.孤岛的总面积.md +++ b/problems/kamacoder/0101.孤岛的总面积.md @@ -307,6 +307,71 @@ for i in range(n): print(count) ``` + +```python +direction = [[1, 0], [-1, 0], [0, 1], [0, -1]] +result = 0 + +# 深度搜尋 +def dfs(grid, y, x): + grid[y][x] = 0 + global result + result += 1 + + for i, j in direction: + next_x = x + j + next_y = y + i + if (next_x < 0 or next_y < 0 or + next_x >= len(grid[0]) or next_y >= len(grid) + ): + continue + if grid[next_y][next_x] == 1 and not visited[next_y][next_x]: + visited[next_y][next_x] = True + dfs(grid, next_y, next_x) + + +# 讀取輸入值 +n, m = map(int, input().split()) +grid = [] +visited = [[False] * m for _ in range(n)] + +for i in range(n): + grid.append(list(map(int, input().split()))) + +# 處理邊界 +for j in range(m): + # 上邊界 + if grid[0][j] == 1 and not visited[0][j]: + visited[0][j] = True + dfs(grid, 0, j) + # 下邊界 + if grid[n - 1][j] == 1 and not visited[n - 1][j]: + visited[n - 1][j] = True + dfs(grid, n - 1, j) + +for i in range(n): + # 左邊界 + if grid[i][0] == 1 and not visited[i][0]: + visited[i][0] = True + dfs(grid, i, 0) + # 右邊界 + if grid[i][m - 1] == 1 and not visited[i][m - 1]: + visited[i][m - 1] = True + dfs(grid, i, m - 1) + +# 計算孤島總面積 +result = 0 # 初始化,避免使用到處理邊界時所產生的累加值 + +for i in range(n): + for j in range(m): + if grid[i][j] == 1 and not visited[i][j]: + visited[i][j] = True + dfs(grid, i, j) + +# 輸出孤島的總面積 +print(result) +``` + ### Go ``` go diff --git a/problems/kamacoder/0108.冗余连接.md b/problems/kamacoder/0108.冗余连接.md index 2c133782..18a86ad6 100644 --- a/problems/kamacoder/0108.冗余连接.md +++ b/problems/kamacoder/0108.冗余连接.md @@ -178,6 +178,45 @@ int main() { ### Python +```python +father = list() + +def find(u): + if u == father[u]: + return u + else: + father[u] = find(father[u]) + return father[u] + +def is_same(u, v): + u = find(u) + v = find(v) + return u == v + +def join(u, v): + u = find(u) + v = find(v) + if u != v: + father[u] = v + +if __name__ == "__main__": + # 輸入 + n = int(input()) + for i in range(n + 1): + father.append(i) + # 尋找冗余邊 + result = None + for i in range(n): + s, t = map(int, input().split()) + if is_same(s, t): + result = str(s) + ' ' + str(t) + else: + join(s, t) + + # 輸出 + print(result) +``` + ### Go ### Rust diff --git a/problems/kamacoder/0109.冗余连接II.md b/problems/kamacoder/0109.冗余连接II.md index fd834357..2bd4eac6 100644 --- a/problems/kamacoder/0109.冗余连接II.md +++ b/problems/kamacoder/0109.冗余连接II.md @@ -250,9 +250,193 @@ int main() { ## 其他语言版本 ### Java +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +public class Main { + static int n; + static int[] father = new int[1001]; // 并查集数组 + + // 并查集初始化 + public static void init() { + for (int i = 1; i <= n; ++i) { + father[i] = i; + } + } + + // 并查集里寻根的过程 + public static int find(int u) { + if (u == father[u]) return u; + return father[u] = find(father[u]); // 路径压缩 + } + + // 将 v->u 这条边加入并查集 + public static void join(int u, int v) { + u = find(u); + v = find(v); + if (u != v) { + father[v] = u; // 合并两棵树 + } + } + + // 判断 u 和 v 是否有同一个根 + public static boolean same(int u, int v) { + return find(u) == find(v); + } + + // 在有向图里找到删除的那条边,使其变成树 + public static void getRemoveEdge(List edges) { + init(); // 初始化并查集 + for (int i = 0; i < n; i++) { // 遍历所有的边 + if (same(edges.get(i)[0], edges.get(i)[1])) { // 如果构成有向环了,就是要删除的边 + System.out.println(edges.get(i)[0] + " " + edges.get(i)[1]); + return; + } else { + join(edges.get(i)[0], edges.get(i)[1]); + } + } + } + + // 删一条边之后判断是不是树 + public static boolean isTreeAfterRemoveEdge(List edges, int deleteEdge) { + init(); // 初始化并查集 + for (int i = 0; i < n; i++) { + if (i == deleteEdge) continue; + if (same(edges.get(i)[0], edges.get(i)[1])) { // 如果构成有向环了,一定不是树 + return false; + } + join(edges.get(i)[0], edges.get(i)[1]); + } + return true; + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + List edges = new ArrayList<>(); // 存储所有的边 + + n = sc.nextInt(); // 顶点数 + int[] inDegree = new int[n + 1]; // 记录每个节点的入度 + for (int i = 0; i < n; i++) { + int s = sc.nextInt(); // 边的起点 + int t = sc.nextInt(); // 边的终点 + inDegree[t]++; + edges.add(new int[]{s, t}); // 将边加入列表 + } + + List vec = new ArrayList<>(); // 记录入度为2的边(如果有的话就两条边) + // 找入度为2的节点所对应的边,注意要倒序,因为优先删除最后出现的一条边 + for (int i = n - 1; i >= 0; i--) { + if (inDegree[edges.get(i)[1]] == 2) { + vec.add(i); + } + } + + // 情况一、情况二 + if (vec.size() > 0) { + // vec里的边已经按照倒叙放的,所以优先删 vec.get(0) 这条边 + if (isTreeAfterRemoveEdge(edges, vec.get(0))) { + System.out.println(edges.get(vec.get(0))[0] + " " + edges.get(vec.get(0))[1]); + } else { + System.out.println(edges.get(vec.get(1))[0] + " " + edges.get(vec.get(1))[1]); + } + return; + } + + // 处理情况三:明确没有入度为2的情况,一定有有向环,找到构成环的边返回即可 + getRemoveEdge(edges); + } +} +``` ### Python +```python +from collections import defaultdict + +father = list() + + +def find(u): + if u == father[u]: + return u + else: + father[u] = find(father[u]) + return father[u] + + +def is_same(u, v): + u = find(u) + v = find(v) + return u == v + + +def join(u, v): + u = find(u) + v = find(v) + if u != v: + father[u] = v + + +def is_tree_after_remove_edge(edges, edge, n): + # 初始化并查集 + global father + father = [i for i in range(n + 1)] + + for i in range(len(edges)): + if i == edge: + continue + s, t = edges[i] + if is_same(s, t): # 成環,即不是有向樹 + return False + else: # 將s,t放入集合中 + join(s, t) + return True + + +def get_remove_edge(edges): + # 初始化并查集 + global father + father = [i for i in range(n + 1)] + + for s, t in edges: + if is_same(s, t): + print(s, t) + return + else: + join(s, t) + + +if __name__ == "__main__": + # 輸入 + n = int(input()) + edges = list() + in_degree = defaultdict(int) + + for i in range(n): + s, t = map(int, input().split()) + in_degree[t] += 1 + edges.append([s, t]) + + # 尋找入度為2的邊,並紀錄其下標(index) + vec = list() + for i in range(n - 1, -1, -1): + if in_degree[edges[i][1]] == 2: + vec.append(i) + + # 輸出 + if len(vec) > 0: + # 情況一:刪除輸出順序靠後的邊 + if is_tree_after_remove_edge(edges, vec[0], n): + print(edges[vec[0]][0], edges[vec[0]][1]) + # 情況二:只能刪除特定的邊 + else: + print(edges[vec[1]][0], edges[vec[1]][1]) + else: + # 情況三: 原圖有環 + get_remove_edge(edges) +``` + ### Go ### Rust