深度优先搜索 && 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学习编程的时候真的是扎心,看这题没有思路,看那题也没有思路,甚至怀疑自己不适合搞技术,但是通过学习大神的题解,然后自己尝试着写,总结一下要点,这样感觉思路清晰,精神上也可以得到满足,不会感觉啥也没学到。我深刻觉得以前的学习方法不对,学东西不仔细,感觉在追求速度,只在乎把结果搞出来,忽略掉过程中的一些细节,最后总是感觉自己什么都不会。

在此感谢:大神

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