前言
這是我結合例題學習STL的第二篇文章,第一篇文章粗略地瞭解了什麼是STL。今天在leetCode上學習編程,遇到深度優先搜索+STL容器的使用,於是就過來記錄下來。
例題描述
首先給出題目的描述,然後根據題意去了解和學習unordered_map。
題目鏈接:移步看詳細描述
請實現 copyRandomList 函數,複製一個複雜鏈表。在複雜鏈表中,每個節點除了有一個 next 指針指向下一個節點,還有一個 random 指針指向鏈表中的任意節點或者 null。
梳理相關知識
深度優先搜索DFS
比如上圖所示的這個題,假定頭指針指向7,每個節點的結構如下:
int val; //存節點的權值
node *next; //指向下一個節點,鏈表中的next指針
node *random; //指向節點中任意節點,這裏叫它隨機指針
深度優先搜索可以用遞歸實現該過程,從根結點開始一直遞歸遍歷到最後一個節點,遍歷過的節點做標記,又從最後一個節點開始回溯,每回溯一層檢查該節點是否有其他走向,如果這個方向的節點沒有被標記,則往這個方向繼續遍歷。比如上圖搜索過程:
第一步:7->13->11->10->1->NULL,設返回條件是NULL,因此返回到1。
第二步:從1開始遍歷7,但是7已經被標記遍歷過了,所以不再往下遍歷,繼續返回到10。
第三步:從10開始遍歷11,但是11已經被標記遍歷過了,所以不再往下遍歷,繼續返回到11。
第四步:從11開始遍歷1,但是1已經被標記遍歷過了,所以不再往下遍歷,繼續返回到13。
第五步:從13開始遍歷7,但是7已經被標記遍歷過了,所以不再往下遍歷,繼續返回到7。
第五步:從7開始遍歷NULL,NULL是結束條件,所以遞歸結束,最終形成返回路徑7<-13<-11<-10<-1<-NULL。
unordered_map
前面深度遍歷部分說,遍歷過的節點需要標記起來,我們可以使用可以單獨定義一個數組做標記,但是這樣比較麻煩,不如直接使用無序映射來得方便,STL庫給我們封裝好了可以直接調用的算法,通過調用接口來實現自己想要的功能。
unordered_map是map容器中的一個模板類,內部通過哈希表管理數據。關於它的詳細用法可以去官網查看,這裏直接貼出例題代碼,並做解析:unordered_map用法
例題代碼
代碼邏輯
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
unordered_map<Node *, Node *> map;//定義一個指針類型的map管理新舊鏈表
return dfs(map, head);
}
/*
功能:深度優先搜索
參數:
map:引用類型
node:鏈表節點
返回:新鏈表
*/
Node *dfs( unordered_map<Node *, Node *> &map, Node *node)
{
if(node == nullptr)//參數合法性判斷
{
return node;
}
auto it = map.find(node);//auto:自動匹配數據類型,這裏是迭代類型
//判斷節點是否在map中,過濾已經存在map中的節點,避免下次遞歸重複遍歷,
//如果,node存在,find將返回和end一樣的迭代值
if( it != map.end())
{
return it->second;//說明該節點已經存在map裏面了,開始遍歷random隨機指針了,返回新鏈表
}
//否則遍歷鏈表next指針
//新建節點,準備複製鏈表
Node *newNode = new Node(node->val);
//以舊節點作爲key值,新節點作爲映射值存入MAP
//map[node] = newNode;
map.insert(make_pair(node, newNode));
newNode->next = dfs(map,node->next);//深度遍歷
newNode->random = dfs(map,node->random);//回溯搜索沒有遍歷過的節點
return newNode;//回溯結束返回新頭節點指針
}
};
對代碼中用到的幾個庫函數進行說明
- unordered_map初始化定義格式
unordered_map<Node *, Node *> map;
容器的一般定義格式,尖括號裏面是元素類型,類型可以任意。
- auto
這個是自動化數據類型,它會根據右邊數據的類型自動匹配,比如在這裏map.find返回iterator類型,auto就是iterator類型。 - map.find()
幾乎在所有的容器中都有此函數,用於搜索以k爲鍵的元素,如果找到了,就返回一個迭代器,否則就返回unordered_map::end(容器末尾的元素)的迭代器。 - map.end()
end返回的迭代器不指向任何元素,而是指向unordered_map容器中最後一個元素後面的位置(它的結束位置)。因此,返回的值不能取消引用——它通常用於描述一個範圍的開放端,比如[begin,end]。 - map.insert()
插入元素到map中,在這裏向map中插入鍵值對。
感想
技術不是一天搞起來的,靠的是長期的堅持和總結。leetCode學習編程的時候真的是扎心,看這題沒有思路,看那題也沒有思路,甚至懷疑自己不適合搞技術,但是通過學習大神的題解,然後自己嘗試着寫,總結一下要點,這樣感覺思路清晰,精神上也可以得到滿足,不會感覺啥也沒學到。我深刻覺得以前的學習方法不對,學東西不仔細,感覺在追求速度,只在乎把結果搞出來,忽略掉過程中的一些細節,最後總是感覺自己什麼都不會。
在此感謝:大神