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