一、問題分析
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)+ 哈希表(映射表) 實現,核心邏輯:
- 哈希表記錄映射關係:鍵為原節點指針,值為克隆節點指針,避免重複創建;
- DFS 遍歷 + 克隆:
- 若當前節點已克隆(哈希表中存在),直接返回克隆節點;
- 若未克隆,創建新節點,存入哈希表;
- 遞歸克隆當前節點的所有鄰居,添加到新節點的鄰居列表;
- 無向圖處理:遞歸過程中自動處理雙向引用(如克隆 1 的鄰居 2 時,會同步克隆 2 的鄰居 1)。
#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)。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。