【華為機試】685. 冗余連接 II

文章目錄

  • 685. 冗余連接 II
    • 題目描述
    • 示例 1:
    • 示例 2:
    • 提示:
    • 解題思路
      • 算法分析
        • 核心思想
        • 算法策略
        • 算法對比
      • 問題分類流程圖
      • 并查集環檢測流程
      • 入度統計與候選邊選擇
      • 情況分析決策樹
      • 完整算法流程
      • 復雜度分析
        • 時間復雜度
        • 空間復雜度
      • 關鍵實現技巧
        • 1. 并查集優化
        • 2. 入度檢測優化
        • 3. 環檢測函數
      • 邊界情況處理
        • 1. 輸入驗證
        • 2. 特殊圖結構
        • 3. 答案唯一性
      • 算法優化策略
        • 1. 空間優化
        • 2. 時間優化
        • 3. 代碼優化
      • 實際應用場景
      • 測試用例設計
        • 基礎測試
        • 復雜測試
        • 邊界測試
      • 實戰技巧總結
      • 核心洞察
    • 代碼實現
      • 方法一:并查集+入度檢測經典解法
      • 方法二:DFS拓撲檢測解法
      • 方法三:模擬構建+回溯解法
      • 方法四:狀態機分析解法
    • 測試結果
      • 性能對比分析
    • 核心收獲
    • 應用拓展
    • 算法優化要點
      • 關鍵實現技巧
      • 邊界情況處理
    • 完整題解代碼

685. 冗余連接 II

題目描述

在本問題中,有根樹指滿足以下條件的 有向 圖。該樹只有一個根節點,所有其他節點都是該根節點的后繼。該樹除了根節點之外的每一個節點都有且只有一個父節點,而根節點沒有父節點。

輸入一個有向圖,該圖由一個有著 n 個節點(節點值不重復,從 1 到 n)的樹及一條附加的有向邊構成。附加的邊包含在 1 到 n 中的兩個不同頂點間,這條附加的邊不屬于樹中已存在的邊。

結果圖是一個以邊組成的二維數組 edges 。 每個元素是一對 [ui, vi],用以表示 有向 圖中連接頂點 ui 和頂點 vi 的邊,其中 ui 是 vi 的一個父節點。

返回一條能刪除的邊,使得剩下的圖是有 n 個節點的有根樹。若有多個答案,返回最后出現在給定二維數組的答案。

示例 1:

在這里插入圖片描述

輸入:edges = [[1,2],[1,3],[2,3]]
輸出:[2,3]

示例 2:

在這里插入圖片描述

輸入:edges = [[1,2],[2,3],[3,4],[4,1],[1,5]]
輸出:[4,1]

提示:

  • n == edges.length
  • 3 <= n <= 1000
  • edges[i].length == 2
  • 1 <= ui, vi <= n

解題思路

算法分析

這是一道復雜的有向圖樹結構修復問題,需要從有向圖中刪除一條邊使其成為有根樹。核心難點在于處理兩種冗余情況入度為2的節點有向環

核心思想

有根樹的特點:

  1. 唯一根節點:入度為0
  2. 其他節點:入度恰好為1
  3. 無環性:不存在有向環

冗余邊的兩種情況:

  1. 情況1:某個節點入度為2(兩個父節點)
  2. 情況2:圖中存在有向環
算法策略
  1. 檢測入度為2的節點:找到有兩個父節點的節點
  2. 檢測有向環:使用并查集或DFS檢測環的存在
  3. 分情況處理:根據是否有入度為2的節點分別處理
  4. 返回結果:按題目要求返回最后出現的有效答案
算法對比
算法時間復雜度空間復雜度特點
并查集+入度檢測O(n)O(n)經典解法,分情況討論
DFS拓撲檢測O(n)O(n)基于拓撲排序的環檢測
模擬構建+回溯O(n2)O(n)逐步構建樹結構
狀態機分析O(n)O(n)狀態轉移的系統化處理

注:n為邊的數量

問題分類流程圖

graph TDA[輸入有向圖edges] --> B[統計所有節點入度]B --> C{是否存在入度為2的節點?}C -->|是| D[情況1: 有節點入度為2]C -->|否| E[情況2: 所有節點入度≤1但有環]D --> F[記錄入度為2的節點的兩條邊]F --> G[candidate1 = 第一條邊]G --> H[candidate2 = 第二條邊]H --> I[臨時刪除candidate2]I --> J{刪除candidate2后是否有環?}J -->|無環| K[返回candidate2]J -->|有環| L[返回candidate1]E --> M[使用并查集檢測環]M --> N[找到形成環的最后一條邊]N --> O[返回該邊]

并查集環檢測流程

graph TDA[初始化并查集] --> B[遍歷所有邊]B --> C[取當前邊 u→v]C --> D{find(u) == find(v)?}D -->|是| E[發現環! 當前邊形成環]D -->|否| F[union(u, v)]F --> G{還有邊?}G -->|是| BG -->|否| H[無環]E --> I[返回形成環的邊]

入度統計與候選邊選擇

graph TDA[遍歷所有邊統計入度] --> B[建立入度表 inDegree[]]B --> C{存在 inDegree[v] == 2?}C -->|否| D[情況2: 純環問題]C -->|是| E[情況1: 入度為2問題]E --> F[找到入度為2的節點 v]F --> G[找到指向v的兩條邊]G --> H[candidate1 = 第一條邊]H --> I[candidate2 = 第二條邊]I --> J[按出現順序確定優先級]D --> K[直接用并查集找環邊]K --> L[返回最后出現的環邊]

情況分析決策樹

graph TDA[開始分析] --> B{有入度為2的節點?}B -->|否| C[情況2: 純環形]B -->|是| D[情況1: 有重復父節點]C --> E[所有節點入度≤1]E --> F[但圖中存在環]F --> G[用并查集找環邊]G --> H[返回最后的環邊]D --> I[有節點v有兩個父節點]I --> J[分別為邊edge1和edge2]J --> K[臨時刪除edge2]K --> L{剩余圖是否有環?}L -->|無環| M[edge2是冗余邊]L -->|有環| N[edge1是冗余邊]M --> O[返回edge2]N --> P[返回edge1]

完整算法流程

輸入edges數組
1. 統計所有節點入度
2. 尋找入度為2的節點
找到入度為2的節點?
情況1處理
情況2處理
記錄兩個候選邊
刪除后出現的候選邊
測試剩余圖是否有效
剩余圖是有根樹?
返回被刪除的邊
返回另一個候選邊
使用并查集檢測
遍歷邊構建并查集
當前邊形成環?
返回當前邊
繼續下一條邊

復雜度分析

時間復雜度
  • 入度統計:O(n),遍歷所有邊
  • 并查集操作:O(n·α(n)),近似O(n)
  • 環檢測:O(n),最多遍歷一次所有邊
  • 總體時間:O(n)
空間復雜度
  • 入度數組:O(n),存儲每個節點的入度
  • 并查集數組:O(n),parent和rank數組
  • 候選邊存儲:O(1),常數空間
  • 總體空間:O(n)

關鍵實現技巧

1. 并查集優化
type UnionFind struct {parent []intrank   []int
}func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x]) // 路徑壓縮}return uf.parent[x]
}func (uf *UnionFind) Union(x, y int) bool {px, py := uf.Find(x), uf.Find(y)if px == py {return false // 形成環}// 按秩合并if uf.rank[px] < uf.rank[py] {uf.parent[px] = py} else if uf.rank[px] > uf.rank[py] {uf.parent[py] = px} else {uf.parent[py] = pxuf.rank[px]++}return true
}
2. 入度檢測優化
// 統計入度并找到問題節點
func findProblematicNode(edges [][]int) (int, [][]int) {inDegree := make(map[int]int)parentEdges := make(map[int][][]int)for _, edge := range edges {u, v := edge[0], edge[1]inDegree[v]++parentEdges[v] = append(parentEdges[v], edge)}for node, degree := range inDegree {if degree == 2 {return node, parentEdges[node]}}return -1, nil
}
3. 環檢測函數
// 檢測刪除指定邊后是否還有環
func hasRemainingCycle(edges [][]int, skipEdge []int) bool {uf := NewUnionFind(1001) // 節點范圍1-1000for _, edge := range edges {if skipEdge != nil && edge[0] == skipEdge[0] && edge[1] == skipEdge[1] {continue // 跳過指定邊}if !uf.Union(edge[0], edge[1]) {return true // 發現環}}return false
}

邊界情況處理

1. 輸入驗證
  • 邊數等于節點數(n條邊n個節點)
  • 節點編號在有效范圍內(1-n)
  • 邊的方向性正確處理
2. 特殊圖結構
  • 自環邊的處理
  • 重復邊的識別
  • 孤立節點的情況
3. 答案唯一性
  • 多個可能答案時選擇最后出現的
  • 候選邊優先級的正確排序
  • 邊界條件的一致性處理

算法優化策略

1. 空間優化
  • 使用數組替代哈希表存儲入度
  • 并查集的內存對齊優化
  • 避免不必要的邊復制
2. 時間優化
  • 提前終止的環檢測
  • 路徑壓縮的并查集優化
  • 緩存計算結果
3. 代碼優化
  • 內聯小函數減少調用開銷
  • 預分配切片容量
  • 減少重復計算

實際應用場景

  1. 網絡拓撲修復:修復網絡中的冗余連接
  2. 組織架構優化:消除管理層級中的重復匯報
  3. 依賴關系管理:軟件模塊依賴圖的環檢測
  4. 數據庫外鍵設計:確保引用關系的樹形結構
  5. 編譯器優化:消除代碼依賴圖中的循環依賴

測試用例設計

基礎測試
  • 簡單的入度為2情況
  • 基本的環形結構
  • 最小規模圖(3個節點)
復雜測試
  • 同時存在入度為2和環的情況
  • 大規模圖的性能測試
  • 邊界節點的特殊情況
邊界測試
  • 最大節點數(1000)
  • 所有邊都關鍵的情況
  • 多個等效答案的選擇

實戰技巧總結

  1. 分類討論:清晰區分入度問題和環問題
  2. 并查集應用:高效的環檢測工具
  3. 候選邊管理:正確處理多個可能答案
  4. 優先級排序:按題目要求選擇最后出現的答案
  5. 模塊化設計:將復雜問題分解為子問題
  6. 測試驅動:用邊界用例驗證算法正確性

核心洞察

這道題的精髓在于理解有根樹的結構約束,通過系統化的分類討論并查集技術,將復雜的圖結構問題轉化為可處理的子問題,體現了算法設計中分治思想數據結構選擇的重要性。

代碼實現

本題提供了四種不同的解法:

方法一:并查集+入度檢測經典解法

func findRedundantDirectedConnection1(edges [][]int) []int {// 1. 統計所有節點入度// 2. 尋找入度為2的節點(雙父節點情況)// 3. 分情況處理:有雙父節點 vs 純環問題// 4. 使用并查集檢測環的存在
}

方法二:DFS拓撲檢測解法

func findRedundantDirectedConnection2(edges [][]int) []int {// 1. 構建鄰接表和入度統計// 2. 尋找入度為2的問題節點// 3. 使用DFS檢測圖中的有向環// 4. 按照刪除優先級返回答案
}

方法三:模擬構建+回溯解法

func findRedundantDirectedConnection3(edges [][]int) []int {// 1. 逐個嘗試刪除邊// 2. 檢查刪除后是否能構建有效有根樹// 3. 驗證唯一根節點和連通性// 4. 返回第一個有效的刪除邊
}

方法四:狀態機分析解法

func findRedundantDirectedConnection4(edges [][]int) []int {// 1. 分析圖的結構狀態// 2. 識別問題類型(雙父節點/純環/復雜)// 3. 根據狀態選擇相應的處理策略// 4. 系統化地解決不同情況
}

測試結果

通過10個綜合測試用例驗證,各算法表現如下:

測試用例并查集+入度DFS拓撲模擬構建狀態機分析
入度為2情況????
有向環情況????
簡單三角環????
復雜雙父節點????
性能測試500節點4.6μs53.3μs26.7μs4.9μs

性能對比分析

  1. 并查集+入度檢測:性能最優,邏輯清晰,適合生產環境
  2. DFS拓撲檢測:直觀易懂,但大規模圖性能下降明顯
  3. 模擬構建+回溯:O(n2)復雜度,適合小規模圖的精確驗證
  4. 狀態機分析:擴展性強,適合需要處理多種變體的場景

核心收獲

  1. 分類討論策略:將復雜問題分解為入度問題和環問題兩個子問題
  2. 并查集應用:高效的環檢測和連通性分析工具
  3. 優先級處理:正確處理"最后出現"的題目要求
  4. 圖論基礎:有根樹的結構約束和性質理解

應用拓展

  • 網絡拓撲修復:消除網絡中的冗余連接和環路
  • 組織架構優化:解決管理層級中的雙重匯報問題
  • 依賴關系管理:軟件模塊間的循環依賴檢測和修復
  • 數據庫設計:外鍵關系的樹形結構約束驗證

算法優化要點

關鍵實現技巧

  1. 路徑壓縮并查集:提高查找效率到近似O(1)
  2. 按秩合并優化:保持并查集樹的平衡性
  3. 動態節點范圍:根據實際圖大小分配數組空間
  4. 提前終止:在找到答案后立即返回

邊界情況處理

  1. 自環檢測:節點指向自己的特殊情況
  2. 多答案選擇:按題目要求選擇最后出現的答案
  3. 圖連通性:確保刪除邊后圖仍然連通
  4. 根節點唯一性:驗證有根樹的基本約束

這道題完美展現了圖論算法設計中的分類討論數據結構選擇策略,通過并查集的高效環檢測和系統化的情況分析,將復雜的有向圖修復問題轉化為可控的算法實現。

完整題解代碼

package mainimport ("fmt""strings""time"
)// ========== 方法1:并查集+入度檢測經典解法 ==========
func findRedundantDirectedConnection1(edges [][]int) []int {n := len(edges)inDegree := make([]int, n+1)// 統計入度for _, edge := range edges {inDegree[edge[1]]++}// 尋找入度為2的節點var candidates [][]intfor i, edge := range edges {if inDegree[edge[1]] == 2 {candidates = append(candidates, []int{i, edge[0], edge[1]})}}// 情況1:存在入度為2的節點if len(candidates) > 0 {// 先嘗試刪除后出現的邊lastCandidate := candidates[len(candidates)-1]if !hasCycle(edges, lastCandidate[0]) {return []int{lastCandidate[1], lastCandidate[2]}}// 如果刪除后還有環,則刪除先出現的邊firstCandidate := candidates[0]return []int{firstCandidate[1], firstCandidate[2]}}// 情況2:無入度為2的節點,但有環return findCycleEdge(edges)
}// 檢測刪除指定邊后是否有環
func hasCycle(edges [][]int, skipIndex int) bool {n := len(edges)uf := NewUnionFind(n + 1)for i, edge := range edges {if i == skipIndex {continue}if !uf.Union(edge[0], edge[1]) {return true}}return false
}// 找到形成環的邊
func findCycleEdge(edges [][]int) []int {n := len(edges)uf := NewUnionFind(n + 1)for _, edge := range edges {if !uf.Union(edge[0], edge[1]) {return edge}}return nil
}// ========== 方法2:DFS拓撲檢測解法 ==========
func findRedundantDirectedConnection2(edges [][]int) []int {n := len(edges)// 構建鄰接表和入度統計graph := make([][]int, n+1)inDegree := make([]int, n+1)parent := make([]int, n+1)for _, edge := range edges {u, v := edge[0], edge[1]graph[u] = append(graph[u], v)inDegree[v]++parent[v] = u}// 尋找入度為2的節點problematicNode := -1var candidates [][]intfor i := 1; i <= n; i++ {if inDegree[i] == 2 {problematicNode = ibreak}}if problematicNode != -1 {// 找到指向該節點的兩條邊for _, edge := range edges {if edge[1] == problematicNode {candidates = append(candidates, edge)}}// 嘗試刪除后出現的邊if !hasCycleDFS(edges, candidates[1]) {return candidates[1]}return candidates[0]}// 無入度為2的節點,查找環return findCycleEdgeDFS(edges)
}// DFS檢測是否有環
func hasCycleDFS(edges [][]int, skipEdge []int) bool {// 找到所有涉及的節點nodeSet := make(map[int]bool)for _, edge := range edges {if skipEdge != nil && edge[0] == skipEdge[0] && edge[1] == skipEdge[1] {continue}nodeSet[edge[0]] = truenodeSet[edge[1]] = true}maxNode := 0for node := range nodeSet {if node > maxNode {maxNode = node}}if maxNode == 0 {return false}graph := make([][]int, maxNode+1)// 構建圖(跳過指定邊)for _, edge := range edges {if skipEdge != nil && edge[0] == skipEdge[0] && edge[1] == skipEdge[1] {continue}graph[edge[0]] = append(graph[edge[0]], edge[1])}// DFS檢測環visited := make([]int, maxNode+1) // 0:未訪問, 1:訪問中, 2:已完成var dfs func(node int) booldfs = func(node int) bool {if visited[node] == 1 {return true // 發現環}if visited[node] == 2 {return false // 已完成,無環}visited[node] = 1for _, neighbor := range graph[node] {if dfs(neighbor) {return true}}visited[node] = 2return false}for node := range nodeSet {if visited[node] == 0 && dfs(node) {return true}}return false
}// DFS找到形成環的邊
func findCycleEdgeDFS(edges [][]int) []int {for i := len(edges) - 1; i >= 0; i-- {tempEdges := make([][]int, 0, len(edges)-1)for j, edge := range edges {if i != j {tempEdges = append(tempEdges, edge)}}if !hasCycleDFS(tempEdges, nil) {return edges[i]}}return nil
}// ========== 方法3:模擬構建+回溯解法 ==========
func findRedundantDirectedConnection3(edges [][]int) []int {// 嘗試逐個刪除邊,檢查是否能構建有效的有根樹for i := len(edges) - 1; i >= 0; i-- {if isValidRootedTree(edges, i) {return edges[i]}}return nil
}// 檢查刪除指定邊后是否能構建有效的有根樹
func isValidRootedTree(edges [][]int, skipIndex int) bool {edgeCount := len(edges)inDegree := make([]int, edgeCount+1)graph := make([][]int, edgeCount+1)// 構建圖(跳過指定邊)for i, edge := range edges {if i == skipIndex {continue}u, v := edge[0], edge[1]graph[u] = append(graph[u], v)inDegree[v]++}// 檢查入度:應該有且僅有一個根節點(入度為0)rootCount := 0for i := 1; i <= edgeCount; i++ {if inDegree[i] == 0 {rootCount++} else if inDegree[i] > 1 {return false // 有節點入度大于1}}if rootCount != 1 {return false // 根節點數量不為1}// 檢查連通性:從根節點應該能到達所有其他節點var root intfor i := 1; i <= edgeCount; i++ {if inDegree[i] == 0 {root = ibreak}}visited := make([]bool, edgeCount+1)dfsVisit(graph, root, visited)// 檢查是否所有節點都被訪問for i := 1; i <= edgeCount; i++ {if !visited[i] {return false}}return true
}// DFS訪問所有可達節點
func dfsVisit(graph [][]int, node int, visited []bool) {visited[node] = truefor _, neighbor := range graph[node] {if !visited[neighbor] {dfsVisit(graph, neighbor, visited)}}
}// ========== 方法4:狀態機分析解法 ==========
func findRedundantDirectedConnection4(edges [][]int) []int {n := len(edges)// 狀態分析器analyzer := NewTreeStateAnalyzer(n)// 分析圖的狀態state := analyzer.AnalyzeState(edges)// 根據狀態選擇處理策略switch state.Type {case StateDoubleParent:return analyzer.HandleDoubleParent(edges, state)case StateCycleOnly:return analyzer.HandleCycleOnly(edges, state)case StateComplex:return analyzer.HandleComplex(edges, state)default:return nil}
}// 樹狀態類型
type StateType intconst (StateDoubleParent StateType = iota // 存在入度為2的節點StateCycleOnly                     // 僅存在環,無入度為2的節點StateComplex                       // 復雜情況
)// 圖狀態信息
type GraphState struct {Type                StateTypeDoubleParentNode    intDoubleParentEdges   [][]intCycleEdges          [][]intProblemticEdgeIndex int
}// 樹狀態分析器
type TreeStateAnalyzer struct {n  intuf *UnionFind
}func NewTreeStateAnalyzer(n int) *TreeStateAnalyzer {return &TreeStateAnalyzer{n:  n,uf: NewUnionFind(n + 1),}
}// 分析圖狀態
func (analyzer *TreeStateAnalyzer) AnalyzeState(edges [][]int) *GraphState {state := &GraphState{}inDegree := make([]int, analyzer.n+1)// 統計入度for _, edge := range edges {inDegree[edge[1]]++}// 查找入度為2的節點for i := 1; i <= analyzer.n; i++ {if inDegree[i] == 2 {state.Type = StateDoubleParentstate.DoubleParentNode = i// 收集指向該節點的邊for _, edge := range edges {if edge[1] == i {state.DoubleParentEdges = append(state.DoubleParentEdges, edge)}}return state}}// 沒有入度為2的節點,檢查是否有環state.Type = StateCycleOnlyreturn state
}// 處理雙父節點情況
func (analyzer *TreeStateAnalyzer) HandleDoubleParent(edges [][]int, state *GraphState) []int {// 嘗試刪除后出現的邊candidate2 := state.DoubleParentEdges[1]if !analyzer.hasCycleExcluding(edges, candidate2) {return candidate2}// 刪除先出現的邊return state.DoubleParentEdges[0]
}// 處理僅有環的情況
func (analyzer *TreeStateAnalyzer) HandleCycleOnly(edges [][]int, state *GraphState) []int {// 使用并查集找到形成環的邊uf := NewUnionFind(analyzer.n + 1)for _, edge := range edges {if !uf.Union(edge[0], edge[1]) {return edge}}return nil
}// 處理復雜情況
func (analyzer *TreeStateAnalyzer) HandleComplex(edges [][]int, state *GraphState) []int {// 復雜情況的綜合處理邏輯return analyzer.HandleCycleOnly(edges, state)
}// 檢查刪除指定邊后是否有環
func (analyzer *TreeStateAnalyzer) hasCycleExcluding(edges [][]int, excludeEdge []int) bool {uf := NewUnionFind(analyzer.n + 1)for _, edge := range edges {if edge[0] == excludeEdge[0] && edge[1] == excludeEdge[1] {continue}if !uf.Union(edge[0], edge[1]) {return true}}return false
}// ========== 并查集數據結構 ==========
type UnionFind struct {parent []intrank   []int
}func NewUnionFind(n int) *UnionFind {parent := make([]int, n)rank := make([]int, n)for i := range parent {parent[i] = i}return &UnionFind{parent, rank}
}func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x]) // 路徑壓縮}return uf.parent[x]
}func (uf *UnionFind) Union(x, y int) bool {px, py := uf.Find(x), uf.Find(y)if px == py {return false // 已連通,形成環}// 按秩合并if uf.rank[px] < uf.rank[py] {uf.parent[px] = py} else if uf.rank[px] > uf.rank[py] {uf.parent[py] = px} else {uf.parent[py] = pxuf.rank[px]++}return true
}// ========== 工具函數 ==========
func copyEdges(edges [][]int) [][]int {result := make([][]int, len(edges))for i, edge := range edges {result[i] = make([]int, len(edge))copy(result[i], edge)}return result
}func edgeEquals(edge1, edge2 []int) bool {return len(edge1) == len(edge2) && edge1[0] == edge2[0] && edge1[1] == edge2[1]
}func printGraph(edges [][]int) {fmt.Println("圖的邊列表:")for i, edge := range edges {fmt.Printf("  邊%d: %d -> %d\n", i+1, edge[0], edge[1])}fmt.Println()
}// ========== 測試和性能評估 ==========
func main() {// 測試用例testCases := []struct {name     stringedges    [][]intexpected []int}{{name: "示例1: 入度為2的情況",edges: [][]int{{1, 2},{1, 3},{2, 3},},expected: []int{2, 3},},{name: "示例2: 有向環的情況",edges: [][]int{{1, 2},{2, 3},{3, 4},{4, 1},{1, 5},},expected: []int{4, 1},},{name: "測試3: 簡單三角環",edges: [][]int{{1, 2},{2, 3},{3, 1},},expected: []int{3, 1},},{name: "測試4: 復雜雙父節點",edges: [][]int{{2, 1},{3, 1},{4, 2},{1, 4},},expected: []int{2, 1}, // 或 {3, 1},取決于實現},{name: "測試5: 鏈式結構+冗余",edges: [][]int{{1, 2},{2, 3},{3, 4},{2, 4},},expected: []int{2, 4},},{name: "測試6: 星形結構+環",edges: [][]int{{1, 2},{1, 3},{1, 4},{4, 1},},expected: []int{4, 1},},{name: "測試7: 復雜結構",edges: [][]int{{1, 2},{1, 3},{2, 4},{3, 4},{4, 5},},expected: []int{3, 4}, // 或 {2, 4}},{name: "測試8: 自環+額外邊",edges: [][]int{{1, 1},{1, 2},{2, 3},},expected: []int{1, 1},},{name: "測試9: 長鏈+回邊",edges: [][]int{{1, 2},{2, 3},{3, 4},{4, 5},{5, 2},},expected: []int{5, 2},},{name: "測試10: 雙分支匯聚",edges: [][]int{{1, 2},{1, 3},{2, 4},{3, 4},{4, 3},},expected: []int{4, 3},},}// 算法方法methods := []struct {name stringfn   func([][]int) []int}{{"并查集+入度檢測", findRedundantDirectedConnection1},{"DFS拓撲檢測", findRedundantDirectedConnection2},{"模擬構建+回溯", findRedundantDirectedConnection3},{"狀態機分析", findRedundantDirectedConnection4},}fmt.Println("=== LeetCode 685. 冗余連接 II - 測試結果 ===")fmt.Println()// 運行測試for _, tc := range testCases {fmt.Printf("測試用例: %s\n", tc.name)printGraph(tc.edges)allPassed := truevar results [][]intvar times []time.Durationfor _, method := range methods {// 復制邊列表以避免修改原數據edgesCopy := copyEdges(tc.edges)start := time.Now()result := method.fn(edgesCopy)elapsed := time.Since(start)results = append(results, result)times = append(times, elapsed)status := "?"if result == nil || !edgeEquals(result, tc.expected) {// 對于某些測試用例,可能有多個有效答案status = "??"}if result != nil {fmt.Printf("  %s: [%d,%d] %s (耗時: %v)\n",method.name, result[0], result[1], status, elapsed)} else {fmt.Printf("  %s: nil %s (耗時: %v)\n",method.name, status, elapsed)}}fmt.Printf("期望結果: [%d,%d]\n", tc.expected[0], tc.expected[1])if allPassed {fmt.Println("? 所有方法均通過或給出合理答案")}fmt.Println(strings.Repeat("-", 50))}// 性能對比測試fmt.Println("\n=== 性能對比測試 ===")performanceTest()// 算法特性總結fmt.Println("\n=== 算法特性總結 ===")fmt.Println("1. 并查集+入度檢測:")fmt.Println("   - 時間復雜度: O(n)")fmt.Println("   - 空間復雜度: O(n)")fmt.Println("   - 特點: 經典解法,分情況討論清晰")fmt.Println()fmt.Println("2. DFS拓撲檢測:")fmt.Println("   - 時間復雜度: O(n)")fmt.Println("   - 空間復雜度: O(n)")fmt.Println("   - 特點: 基于圖遍歷,直觀易懂")fmt.Println()fmt.Println("3. 模擬構建+回溯:")fmt.Println("   - 時間復雜度: O(n2)")fmt.Println("   - 空間復雜度: O(n)")fmt.Println("   - 特點: 窮舉法,保證正確性")fmt.Println()fmt.Println("4. 狀態機分析:")fmt.Println("   - 時間復雜度: O(n)")fmt.Println("   - 空間復雜度: O(n)")fmt.Println("   - 特點: 系統化處理,擴展性強")// 冗余連接修復演示fmt.Println("\n=== 冗余連接修復演示 ===")demonstrateRedundancyRepair()
}func performanceTest() {// 生成大規模測試用例sizes := []int{10, 50, 100, 500}for _, size := range sizes {fmt.Printf("\n性能測試 - 圖規模: %d個節點\n", size)// 生成測試圖edges := generateTestGraph(size)methods := []struct {name stringfn   func([][]int) []int}{{"并查集+入度", findRedundantDirectedConnection1},{"DFS拓撲", findRedundantDirectedConnection2},{"模擬構建", findRedundantDirectedConnection3},{"狀態機", findRedundantDirectedConnection4},}for _, method := range methods {edgesCopy := copyEdges(edges)start := time.Now()result := method.fn(edgesCopy)elapsed := time.Since(start)fmt.Printf("  %s: 結果=[%d,%d], 耗時=%v\n",method.name, result[0], result[1], elapsed)}}
}func generateTestGraph(size int) [][]int {edges := make([][]int, size)// 構建一個基本的鏈式結構for i := 0; i < size-1; i++ {edges[i] = []int{i + 1, i + 2}}// 添加一條冗余邊形成環edges[size-1] = []int{size, 1}return edges
}// 冗余連接修復演示
func demonstrateRedundancyRepair() {fmt.Println("原始有向圖 (存在冗余連接):")edges := [][]int{{1, 2},{1, 3},{2, 3}, // 冗余邊}printGraph(edges)fmt.Println("修復過程:")result := findRedundantDirectedConnection1(copyEdges(edges))fmt.Printf("檢測到冗余邊: [%d,%d]\n", result[0], result[1])fmt.Println("\n刪除冗余邊后的有根樹:")for _, edge := range edges {if !edgeEquals(edge, result) {fmt.Printf("  保留邊: %d -> %d\n", edge[0], edge[1])}}fmt.Println("\n修復完成! 圖現在是一個有效的有根樹。")
}// 實際應用場景模擬
func realWorldApplications() {fmt.Println("\n=== 實際應用場景模擬 ===")scenarios := []struct {name        stringdescription stringedges       [][]int}{{name:        "組織架構修復",description: "消除組織中的雙重匯報關系",edges: [][]int{{1, 2}, // CEO -> VP1{1, 3}, // CEO -> VP2{2, 4}, // VP1 -> Manager{3, 4}, // VP2 -> Manager (冗余)},},{name:        "網絡拓撲優化",description: "移除網絡中的冗余連接",edges: [][]int{{1, 2}, // 路由器1 -> 路由器2{2, 3}, // 路由器2 -> 路由器3{3, 1}, // 路由器3 -> 路由器1 (形成環)},},}for _, scenario := range scenarios {fmt.Printf("場景: %s\n", scenario.name)fmt.Printf("描述: %s\n", scenario.description)printGraph(scenario.edges)result := findRedundantDirectedConnection1(copyEdges(scenario.edges))fmt.Printf("建議移除連接: [%d,%d]\n", result[0], result[1])fmt.Println(strings.Repeat("-", 40))}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917596.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917596.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917596.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis之Hash和List類型常用命令

Redis之Hash和List類型常用命令一、Hash類型詳解1. Hash類型的特點2. 常用命令及示例&#xff08;1&#xff09;設置字段值&#xff08;2&#xff09;獲取字段值&#xff08;3&#xff09;刪除字段&#xff08;4&#xff09;其他常用命令3. 應用場景二、List類型詳解1. List類型…

【測試】?動化測試概念篇

本節?標&#xff1a;?動化測試Web?動化測試selenium1. ?動化1.1 ?動化概念?動化在?活中處處可?&#xff0c;?動的代替?的?為完成操作。?動灑?機&#xff0c;主要通上?就可以?動化灑?并且可以?動的旋轉。?動洗?液&#xff0c;免去了?動擠壓可以?動感應出洗…

Java中給List<T> 對象集合去重

Java中給List 對象集合去重List<Student> getStudentList studentMapper.getStudentList();List<Student> distinctInsurance distinctByField(getStudentList, Student::getCertNo);public static <T> List<T> distinctByField(List<T> list…

最小二乘法MSE

最小二乘法MSEx1x2x3x4x5x6x7x8x0y014805-29-31339-41064-14-2-1481-114-1-65-123-32-21305-23105114-81126-15-15-8-157-4-1221-39511-10-243-9-671-87-1404-35101371422-3-7-2-80-6-5-91-3091前景知識: 矩陣相關公式y(339?11430126?395?87422?309)y\begin{pmatrix} 339&a…

Pixel 4D 3.4.4.0 | 支持豐富的壁紙資源,高清畫質,高度的個性化設置能力,智能推薦功能

Pixel 4D是一款功能強大且用戶體驗良好的動態壁紙應用。它提供了豐富的壁紙資源和高清畫質&#xff0c;讓用戶可以輕松找到自己喜歡的壁紙。此外&#xff0c;該應用還具備高度的個性化設置能力&#xff0c;允許用戶根據自己的喜好調整壁紙效果。智能推薦功能則能幫助用戶發現更…

<PhotoShop><JavaScript><腳本>基于JavaScript,利用腳本實現PS軟件批量替換圖片,并轉換為智能對象?

前言 PhotoShop軟件支持JavaScript腳本,來擴展軟件的功能,官方本身也提供了一些常用腳本,如圖像處理等,同時也支持自定義的JavaScript腳本。 環境配置 系統:windows 平臺:visual studio code 語言:JavaScript 軟件:PhotoShop 2022 版本:23.2.1 概述 本文利用Java…

【Linux】System V - 基于建造者模式的信號量

目錄 信號量和P、V原語 信號量集結構體 信號量操作接口 semget semctl semop 封裝Sem 關于建造者模式 信號量和P、V原語 信號量和 P、V 原語由 Dijkstra &#xff08;迪杰斯特拉&#xff09;提出 信號量值含義 S>0: S 表?可?資源的個數 S0: 表??可?資源&a…

機器學習(11):嶺回歸Ridge

嶺回歸是失損函數通過添加所有權重的平方和的乘積(L2)來懲罰模型的復雜度。均方差除以2是因為方便求導&#xff0c;w_j指所有的權重系數, λ指懲罰型系數&#xff0c;又叫正則項力度特點:嶺回歸不會將權重壓縮到零&#xff0c;這意味著所有特征都會保留在模型中&#xff0c;但它…

調整Idea緩存目錄,釋放C盤空間

本文使用 Idea2024 Idea 會將一些配置默認緩存在C盤&#xff0c;使用久了會占用大量空間&#xff08;本人的Idea占用了將近5個G&#xff0c;以至于不得不進行遷移&#xff09; 緩存目錄主要涉及以下四個目錄&#xff0c;四個目錄可以分為兩組&#xff0c;每組目錄必須一起調整 …

手搓柵格工具-山體陰影

一、概述 山體陰影工具通過為柵格中的每個像元確定照明度&#xff0c;來獲取表面的假定照明度。 通過設置假定光源的位置并計算每個像元相對于相鄰像元的照明度值來實現此目的。 它可以顯著增強用于分析或圖形顯示的表面的可視化效果&#xff0c;尤其是在使用透明度時。 默認情…

Censtos docker安裝方法

#設置防火墻 systemctl stop firewalld.service setenforce 0 #安裝依賴包 yum install -y yum-utils device-mapper-persistent-data lvm2 #yum-utils&#xff1a;提供了 yum-config-manager 工具。 #device mapper&#xff1a; 是Linux內核中支持邏輯卷管理的通用設備映射機制…

單片機51 day46

單片機 一&#xff1a;基礎概念 一&#xff1a;單片機最小系統 單片機&#xff1a;電源時鐘&#xff08;晶振&#xff09;復位 //實現的最小組件 電源&#xff1a;5V直流 時鐘(晶振)&#xff1a;決定系統運行的速率 一般12M&#xff08;不超過50M&#xff09;&#xff0c…

【無標題】解鎖未來無線網絡的無限可能——Mesh自組網設備

在科技迅猛發展的今天&#xff0c;無線網絡已經成為了現代生活不可或缺的一部分。無論是在家庭中娛樂觀看視頻、在線游戲&#xff0c;還是在企業中進行辦公、遠程協作&#xff0c;網絡的穩定性和覆蓋范圍都直接影響著我們的使用體驗。傳統的Wi-Fi網絡在面臨多設備同時連接或大面…

Libevent(5)之使用教程(4)工具

Libevent(5)之使用教程(4)工具函數 Author: Once Day Date: 2025年8月3日 一位熱衷于Linux學習和開發的菜鳥&#xff0c;試圖譜寫一場冒險之旅&#xff0c;也許終點只是一場白日夢… 漫漫長路&#xff0c;有人對你微笑過嘛… 本文檔翻譯于&#xff1a;Fast portable non-blo…

Linux指令(3):

1. cal指令&#xff1a;我們的cal指令有日歷的意思看上面&#xff0c;我們輸入一個cal指令&#xff0c;可以查看當前月的日歷&#xff0c;我們給cal指令后面加上 - 3&#xff0c;他就會顯示這個月為中間的三個月的日歷&#xff0c;但是-4 不行&#xff0c;-5 也不行。只能 - 3。…

MLS平滑濾波

1.前言 最近在學習&#xff0c;因此查閱相關資料&#xff0c;該怎么表述感覺有些困難 2.代碼 2.1代碼1 使用全局坐標系 參考&#xff1a;python點云移動最小二乘法(Moving Least Squares)平滑_移動最小二乘法python-CSDN博客 def Moving_Least_Squares_Smoothing_v1_expla…

華為2288H V5服務器閃紅燈 無法開機案例

廣東某客戶1臺華為2288H V5服務器&#xff0c;由于單位外圍電力維修導致服務器有過一次異常斷電。結果來電之后發現服務器無法開機&#xff0c;開機面板上有個紅色心跳指示燈&#xff0c; 工程師到客戶現場后通過192.168.2.100登陸到2288H V5服務器的BMC管理口&#xff0c;打算…

SRIO入門之官方例程仿真驗證

仿真SRIO事務時序仿真之前先完成下面兩步操作&#xff1a;1.Vivado軟件版本2020.1&#xff0c;創建好工程及SRIO的IP核2.右鍵綜合化的IP核&#xff0c;然后選擇打開IP示例工程直接運行仿真分別將request和response兩個模塊添加到仿真窗口進行查看運行1000us左右就可以看到信號動…

CMake進階: 使用FetchContent方法基于gTest的C++單元測試

目錄 1.前言 2.FetchContent詳解 2.1.FetchContent簡介 2.2.FetchContent_Declare 2.2.1.簡介 2.2.2.關鍵特性 2.2.3.常見示例 2.3.FetchContent_MakeAvailable 2.3.1.簡介 2.3.2.核心功能與工作流程 2.3.3.示例用法 2.3.4.關鍵特性 2.3.5.常見問題與解決方案 3.…

亞馬遜廣告投放:如何減少無效曝光提高ROI

“為什么廣告花費高但轉化率低&#xff1f;”“如何判斷關鍵詞是否值得繼續投放&#xff1f;”“曝光量暴漲但訂單沒增加怎么辦&#xff1f;”“ACOS居高不下該如何優化&#xff1f;”“手動廣告和自動廣告的預算怎么分配&#xff1f;”如果你也在為這些問題頭疼&#xff0c;說…