java - LeetCode-133-克隆圖 - LeetCodet題解_遞歸

一、問題分析
1. 問題描述

給定無向連通圖中一個節點的引用,要求返回該圖的深拷貝(克隆)。圖中每個節點包含值val和鄰居列表neighbors,需保證:

  • 新圖與原圖結構完全一致,但所有節點都是新創建的(內存獨立);
  • 無向圖的雙向引用需正確複製(如節點 A 的鄰居包含 B,則節點 B 的鄰居也需包含 A 的克隆節點);
  • 避免重複創建節點(防止循環引用導致死循環或重複節點)。
2. 核心難點
  • 循環引用處理:無向圖中節點間的雙向引用(如 1↔2),直接遞歸 / 遍歷會重複訪問節點;
  • 深拷貝保證:必須為每個節點創建新實例,且鄰居列表指向克隆節點而非原節點;
  • 連通性保證:原圖是連通的,需遍歷所有可達節點並完成克隆。
二、題目解析
1. 輸入輸出定義
  • 輸入:無向連通圖的起始節點(值為 1);
  • 輸出:克隆圖的起始節點(新內存地址,結構與原圖一致)。
2. 示例理解

以鄰接列表[[2,4],[1,3],[2,4],[1,3]]為例:

  • 節點 1 的鄰居是 2、4;
  • 節點 2 的鄰居是 1、3;
  • 節點 3 的鄰居是 2、4;
  • 節點 4 的鄰居是 1、3;克隆後需生成新的 4 個節點,鄰居關係與原圖完全一致。
三、算法分析

採用深度優先搜索(DFS)+ 哈希表(映射表) 實現,核心邏輯:

  1. 哈希表記錄映射關係:鍵為原節點指針,值為克隆節點指針,避免重複創建;
  2. DFS 遍歷 + 克隆
  • 若當前節點已克隆(哈希表中存在),直接返回克隆節點;
  • 若未克隆,創建新節點,存入哈希表;
  • 遞歸克隆當前節點的所有鄰居,添加到新節點的鄰居列表;
  1. 無向圖處理:遞歸過程中自動處理雙向引用(如克隆 1 的鄰居 2 時,會同步克隆 2 的鄰居 1)。

java - LeetCode-133-克隆圖 - LeetCodet題解_#leetcode_02

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 圖節點定義
struct Node {
    int val;
    int numNeighbors;
    struct Node** neighbors;
};

// 哈希表節點定義(用於存儲原節點→克隆節點的映射)
typedef struct HashNode {
    struct Node* key;        // 原節點指針
    struct Node* value;      // 克隆節點指針
    struct HashNode* next;   // 鏈表下一個節點
} HashNode;

// 哈希表定義
#define HASH_SIZE 1009       // 哈希表大小(質數,減少衝突)
typedef struct {
    HashNode* table[HASH_SIZE];
} HashMap;

// 哈希函數:將指針轉換為整數,取模
static int hash_func(struct Node* key) {
    return (unsigned long)key % HASH_SIZE;
}

// 初始化哈希表
HashMap* hash_map_init() {
    HashMap* map = (HashMap*)malloc(sizeof(HashMap));
    memset(map->table, 0, sizeof(map->table));
    return map;
}

// 哈希表存入鍵值對
void hash_map_put(HashMap* map, struct Node* key, struct Node* value) {
    int idx = hash_func(key);
    HashNode* node = (HashNode*)malloc(sizeof(HashNode));
    node->key = key;
    node->value = value;
    node->next = map->table[idx];  // 頭插法
    map->table[idx] = node;
}

// 哈希表查詢鍵對應的值
struct Node* hash_map_get(HashMap* map, struct Node* key) {
    int idx = hash_func(key);
    HashNode* cur = map->table[idx];
    while (cur) {
        if (cur->key == key) {
            return cur->value;
        }
        cur = cur->next;
    }
    return NULL;
}

// 釋放哈希表內存
void hash_map_free(HashMap* map) {
    for (int i = 0; i < HASH_SIZE; i++) {
        HashNode* cur = map->table[i];
        while (cur) {
            HashNode* tmp = cur;
            cur = cur->next;
            free(tmp);
        }
    }
    free(map);
}

// 創建新節點(初始化值和鄰居數組)
struct Node* create_node(int val) {
    struct Node* node = (struct Node*)malloc(sizeof(struct Node));
    node->val = val;
    node->numNeighbors = 0;
    node->neighbors = NULL;
    return node;
}

// 遞歸克隆節點(核心DFS邏輯)
struct Node* dfs_clone(struct Node* node, HashMap* map) {
    // 邊界條件:節點為空
    if (node == NULL) return NULL;
    
    // 已克隆過,直接返回
    struct Node* cloned = hash_map_get(map, node);
    if (cloned != NULL) return cloned;
    
    // 創建當前節點的克隆
    cloned = create_node(node->val);
    hash_map_put(map, node, cloned);  // 先存入映射,避免循環引用
    
    // 克隆所有鄰居
    cloned->numNeighbors = node->numNeighbors;
    if (node->numNeighbors > 0) {
        cloned->neighbors = (struct Node**)malloc(sizeof(struct Node*) * node->numNeighbors);
        for (int i = 0; i < node->numNeighbors; i++) {
            // 遞歸克隆鄰居節點
            cloned->neighbors[i] = dfs_clone(node->neighbors[i], map);
        }
    }
    
    return cloned;
}

// 克隆圖主函數
struct Node* cloneGraph(struct Node* node) {
    // 初始化哈希表
    HashMap* map = hash_map_init();
    // DFS克隆
    struct Node* res = dfs_clone(node, map);
    // 釋放哈希表(避免內存泄漏)
    hash_map_free(map);
    return res;
}

// 輔助函數:釋放圖的內存(測試用)
void free_graph(struct Node* node, HashMap* visited) {
    if (node == NULL) return;
    // 避免重複釋放
    if (hash_map_get(visited, node) != NULL) return;
    hash_map_put(visited, node, node);
    
    // 釋放鄰居數組
    if (node->neighbors != NULL) {
        for (int i = 0; i < node->numNeighbors; i++) {
            free_graph(node->neighbors[i], visited);
        }
        free(node->neighbors);
    }
    free(node);
}

int main() {
    // 構建原圖:1→2、4;2→1、3;3→2、4;4→1、3
    struct Node* n1 = create_node(1);
    struct Node* n2 = create_node(2);
    struct Node* n3 = create_node(3);
    struct Node* n4 = create_node(4);
    
    n1->numNeighbors = 2;
    n1->neighbors = (struct Node**)malloc(sizeof(struct Node*) * 2);
    n1->neighbors[0] = n2;
    n1->neighbors[1] = n4;
    
    n2->numNeighbors = 2;
    n2->neighbors = (struct Node**)malloc(sizeof(struct Node*) * 2);
    n2->neighbors[0] = n1;
    n2->neighbors[1] = n3;
    
    n3->numNeighbors = 2;
    n3->neighbors = (struct Node**)malloc(sizeof(struct Node*) * 2);
    n3->neighbors[0] = n2;
    n3->neighbors[1] = n4;
    
    n4->numNeighbors = 2;
    n4->neighbors = (struct Node**)malloc(sizeof(struct Node*) * 2);
    n4->neighbors[0] = n1;
    n4->neighbors[1] = n3;
    
    // 克隆圖
    struct Node* cloned = cloneGraph(n1);
    
    // 驗證克隆結果(輸出克隆節點1的鄰居值)
    printf("克隆節點1的鄰居值:");
    for (int i = 0; i < cloned->numNeighbors; i++) {
        printf("%d ", cloned->neighbors[i]->val);
    }
    printf("\n");  // 應輸出:2 4
    
    // 釋放原圖和克隆圖內存
    HashMap* visited1 = hash_map_init();
    free_graph(n1, visited1);
    hash_map_free(visited1);
    
    HashMap* visited2 = hash_map_init();
    free_graph(cloned, visited2);
    hash_map_free(visited2);
    
    return 0;
}
四、複雜度分析
1. 時間複雜度:O (N + E)
  • N:圖中節點數量;
  • E:圖中邊的數量;
  • 每個節點僅被克隆一次(哈希表去重),每條邊僅被處理兩次(無向圖雙向引用),總操作數為 O (N + E)。
2. 空間複雜度:O (N)
  • 哈希表存儲 N 個節點的映射,空間 O (N);
  • DFS 遞歸棧深度最壞為 O (N)(如鏈式圖);
  • 總空間複雜度為 O (N)。