深度優先搜索 && unordered_map

前言

這是我結合例題學習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學習編程的時候真的是扎心,看這題沒有思路,看那題也沒有思路,甚至懷疑自己不適合搞技術,但是通過學習大神的題解,然後自己嘗試着寫,總結一下要點,這樣感覺思路清晰,精神上也可以得到滿足,不會感覺啥也沒學到。我深刻覺得以前的學習方法不對,學東西不仔細,感覺在追求速度,只在乎把結果搞出來,忽略掉過程中的一些細節,最後總是感覺自己什麼都不會。

在此感謝:大神

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章