Leetcode刷题笔记(C++)——深搜广搜
整理一下刷题过程中的思路,在这里进行一下总结与分享。
github地址:https://github.com/lvjian0706/Leetcode-solutions
github项目是刚刚新建的,陆续会将整理的代码以及思路上传上去,代码是基于C++与python的。同时会将基础的排序算法等也一并进行整理上传。
133. 克隆图
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。
class Node {
public int val;
public List neighbors;
}
测试用例格式:
简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1(val = 1),第二个节点值为 2(val = 2),以此类推。该图在测试用例中使用邻接列表表示。
邻接列表 是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。
给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
/*
使用哈希表visited进行深度遍历,深度遍历实际上就是递归遍历图中节点:
1. 当遍历到的节点为空时,返回NULL;
2. 当遍历到的节点已被访问过,直接返回使用该节点创建的新节点;
3. 否则,使用该节点创建新节点,并在visited中进行标记:
3.1 新节点的值为原始节点的值;
3.2 新节点的邻接列表为与原始节点的邻接列表中的节点一一对应的新节点,在对应过程中,如果不存在该节点,递归调用DFS进行创建,如果存在,则为情况2,直接返回该新节点;
*/
Node* DFS(Node* node, map<Node*, Node*> &visited){
if(!node) return NULL;
/*
count()为在哈希表中统计个数的方法,可用于判断是否存在该键
*/
if(visited.count(node)) return visited[node];
Node* new_node = new Node(node->val);
visited[node] = new_node;
for(int i=0; i<node->neighbors.size(); i++){
new_node->neighbors.push_back(DFS(node->neighbors[i], visited));
}
return new_node;
}
/*
图的克隆过程其实就是图的遍历过程,因此可以使用DFS或BFS,又因为图为无向连通图,为了避免重复遍历节点,使用哈希表记录已访问节点;
由于是做深拷贝,所以哈希表中键值对分别为原节点:新节点,用于返回新节点;
*/
Node* cloneGraph(Node* node) {
map<Node*, Node*> visited;
return DFS(node, visited);
}
};
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
/*
使用哈希表visited进行广度遍历:
1. 当节点为空时,返回NULL;
2. 定义队列用来存储当前访问的节点,并将当前节点push进队列中;
3. 将当前节点作为键,使用当前节点创建的新节点作为值存入visited(新节点的邻接列表先不管);
4. 当队列不为空时,循环遍历队列中的节点:
4.1 取出队头元素,循环遍历队头结点的邻接节点:
4.1.1 当节点没有访问过时,创建新节点,将原节点push到队列中,并在visited中进行标记;(由于是先创建后标记,所以可以保证visited中的每一个键节点对应的值节点都已经被创建过)
4.1.2 将队头结点对应的新节点的邻接节点赋值为队头结点的邻接节点对应的新节点;
*/
Node* DFS(Node* node, map<Node*, Node*> &visited){
if(!node) return NULL;
queue<Node*> nodeQ;
nodeQ.push(node);
Node* new_node = new Node(node->val);
visited[node] = new_node;
while(!nodeQ.empty()){
Node* temp = nodeQ.front();
nodeQ.pop();
for(int i=0; i<temp->neighbors.size(); i++){
if(!visited.count(temp->neighbors[i])){
Node* new_temp = new Node(temp->neighbors[i]->val);
visited[temp->neighbors[i]] = new_temp;
nodeQ.push(temp->neighbors[i]);
}
visited[temp]->neighbors.push_back(visited[temp->neighbors[i]]);
}
}
return new_node;
}
/*
图的克隆过程其实就是图的遍历过程,因此可以使用DFS或BFS,又因为图为无向连通图,为了避免重复遍历节点,使用哈希表记录已访问节点;
由于是做深拷贝,所以哈希表中键值对分别为原节点:新节点,用于返回新节点;
*/
Node* cloneGraph(Node* node) {
map<Node*, Node*> visited;
return DFS(node, visited);
}
};
200. 岛屿数量
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:
[
[‘1’,‘1’,‘1’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘0’,‘0’]
]
输出: 1
示例 2:
输入:
[
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘1’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘1’,‘1’]
]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
class Solution {
public:
/*
深度遍历grid:
1. 定义坐标偏移量dx,dy,在遍历过程中,使用x+dx,y+dy来定义四个方向的相邻位置;
2. 将visited中此时遍历到的位置赋为1;
3. 循环遍历相邻的4个位置,当相邻点为1且visited中对应点为0时,可以从该相邻点继续进行递归遍历;
*/
void DFS(vector<vector<char>>& grid, vector<vector<int>> &visited, int x, int y){
vector<int> dx = {
0, 0, -1, 1};
vector<int> dy = {
-1, 1, 0, 0};
visited[x][y] = 1;
for(int i=0; i<4; i++){
int new_x = x + dx[i];
int new_y = y + dy[i];
/*
判断是否越界
*/
if(new_x >= 0 && new_x <= grid.size()-1 && new_y >= 0 && new_y <= grid[0].size()-1){
if(grid[new_x][new_y]=='1' && visited[new_x][new_y]==0){
DFS(grid, visited, new_x, new_y);
}
}
}
}
/*
统计网格中岛屿的数量:图的遍历问题,可以使用DFS和BFS,其中,为了避免重复遍历问题,定义visited数组记录遍历过的位置,遍历过将该位置赋为1,没有遍历过为0;
循环遍历grid,当该点为1且visited中对应点为0时,岛屿数量加1,同时进入DFS或BFS,把相通的点都遍历到,visited对应位置修改为1;
*/
int numIslands(vector<vector<char>>& grid) {
if(grid.size()==0 || grid[0].size()==0) return 0;
int m = grid.size(), n = grid[0].size();
vector<vector<int>> visited(m, vector<int>(n));
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
visited[i][j] = 0;
}
}
int num = 0;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(grid[i][j]=='1' && visited[i][j]==0){
num++;
DFS(grid, visited, i, j);
}
}
}
return num;
}
};
class Solution {
public:
/*
广度遍历grid:
1. 定义坐标偏移量dx,dy,在遍历过程中,使用x+dx,y+dy来定义四个方向的相邻位置;
2. 定义队列存放访问到的grid为1的x,y值,将初始的x,y入队列,同时将visited[x][y]赋为1;
3. 当队列不为空时,循环遍历队列:
3.1 取出队头的x,y,循环遍历4个方向的相邻元素,如果该相邻点为1且visited中对应点为0时,将该相邻点位置入队列,同时将visited[new_x][new_y]赋为1;
*/
void BFS(vector<vector<char>>& grid, vector<vector<int>> &visited, int x, int y){
vector<int> dx = {
0, 0, -1, 1};
vector<int> dy = {
-1, 1, 0, 0};
visited[x][y] = 1;
queue<pair<int, int>> island;
island.push(make_pair(x, y));
while(!island.empty()){
int old_x = island.front().first;
int old_y = island.front().second;
island.pop();
for(int i=0; i<4; i++){
int new_x = old_x + dx[i];
int new_y = old_y + dy[i];
if(new_x >= 0 && new_x < grid.size() && new_y >= 0 && new_y < grid[0].size()){
if(grid[new_x][new_y]=='1' && visited[new_x][new_y]==0){
island.push(make_pair(new_x, new_y));
visited[new_x][new_y] = 1;
}
}
}
}
}
/*
统计网格中岛屿的数量:图的遍历问题,可以使用DFS和BFS,其中,为了避免重复遍历问题,定义visited数组记录遍历过的位置,遍历过将该位置赋为1,没有遍历过为0;
循环遍历grid,当该点为1且visited中对应点为0时,岛屿数量加1,同时进入DFS或BFS,把相通的点都遍历到,visited对应位置修改为1;
*/
int numIslands(vector<vector<char>>& grid) {
if(grid.size()==0 || grid[0].size()==0) return 0;
int m = grid.size(), n = grid[0].size();
vector<vector<int>> visited(m, vector<int>(n));
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
visited[i][j] = 0;
}
}
int num = 0;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(grid[i][j]=='1' && visited[i][j]==0){
num++;
BFS(grid, visited, i, j);
}
}
}
return num;
}
};