第七天-DFS/BFS
733-图像渲染
思路1:DFS
这道题的思路很明显,搜索,我第一个想到的是深度优先,从给定位置向上下左右搜索,然后进行递归的调用,当对应的格子中的颜色和oldColor不一致时则直接返回。这里需要注意当新旧颜色一致时,可能导致无法结束一直递归,所以我们需要利用记忆性搜索,将搜索过的位置记录下来——当然也可以将这种情况进行特判,新旧颜色一样则不需要操作直接返回即可
class Solution {
public:
bool record[51][51] = {0};//记忆数组
void DFS(vector<vector<int>>& image, int r, int c, int newColor, int oldColor)
{
if(image[r][c]!=oldColor)//当颜色不符合时直接return
return;
else if(!record[r][c])//当前位置没有访问过时
{
image[r][c] = newColor;//更新颜色
record[r][c] = true;//更新访问状态
//四个方向进行深度搜索
if(r+1<image.size())DFS(image, r+1, c, newColor, oldColor);
if(r-1>-1) DFS(image, r-1, c, newColor, oldColor);
if(c+1<image[0].size())DFS(image, r, c+1, newColor, oldColor);
if(c-1>-1) DFS(image, r, c-1, newColor, oldColor);
}
}
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
int oldColor = image[sr][sc];
DFS(image, sr, sc, newColor, oldColor);
return image;
}
};
思路2:BFS
当然这题给定的初始的位置,我们同样可以通过队列来模拟广度搜索来解决
从给定位置image[sr][sc]开始,将行列作为pair入队,出队后依次向四个方向进行搜索
如果颜色和oldColor一致则将其修改为newColor并将其行列入队,队列空则退出
需要注意的是,这里也会存在重复入队的情况,但是可以通过特判进行解决——即当oldColor和newColor一致时,直接返回即可
代码使用的是LeetCode官方题解的代码
class Solution {
public:
const int dx[4] = {1, 0, 0, -1};
const int dy[4] = {0, 1, -1, 0};
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
int currColor = image[sr][sc];
if (currColor == newColor) return image;//新旧颜色一致则直接返回
int n = image.size(), m = image[0].size();
queue<pair<int, int>> que;
que.emplace(sr, sc);
image[sr][sc] = newColor;
while (!que.empty()) {//标准的广搜模板
int x = que.front().first, y = que.front().second;
que.pop();
for (int i = 0; i < 4; i++) {
int mx = x + dx[i], my = y + dy[i];
if (mx >= 0 && mx < n && my >= 0 && my < m && image[mx][my] == currColor) {
que.emplace(mx, my);
image[mx][my] = newColor;
}
}
}
return image;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/flood-fill/solution/tu-xiang-xuan-ran-by-leetcode-solution/
695-岛屿的最大面积
思路1:DFS
看到搜索第一个想到就是深搜……
经典的深度搜索的题目,遍历整个grid,找到非零的元素,则进行深度搜索,将其所有相邻的元素都变为0,统计其大小并返回比较得到最大面积,再往下找非零的元素,直到遍历结束,得到最大岛屿面积。
需要体会深度搜索使用的场合,对于这类题目,一次深搜肯定是不够的
岛屿之间的割裂需要多次调用DFS,则自然可以想到在外面加循环
class Solution {
public:
int cnt = 0;
//DFS模板了没什么好说的
void DFS(vector<vector<int>> &grid, int r, int c, int row, int col)
{
if(grid[r][c]==0)
return;
cnt++;
grid[r][c] = 0;
if(r+1<row)DFS(grid, r+1, c, row, col);
if(r-1>-1)DFS(grid, r-1, c, row, col);
if(c+1<col)DFS(grid, r, c+1, row, col);
if(c-1>-1)DFS(grid, r, c-1, row, col);
}
int maxAreaOfIsland(vector<vector<int>>& grid) {
int row = grid.size();
int col = grid[0].size();
int ans = 0;
//体会这里二重循环的使用
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(grid[i][j]==1)
{
DFS(grid, i, j, row, col);
}
ans = max(ans, cnt);
cnt = 0;
}
}
return ans;
}
};
对于上下左右的判断以及往下递归的条件,也有另一种常见的写法
int row = [1, -1, 0, 0]
int col = [0, 0, 1, -1]
for(int i=0;i<4;i++)
{
x = x+row[i];
y = y+col[i];
……
}
思路2:BFS
同样这题可以利用BFS进行解答,思路和上面图像渲染的思路一致,在广搜模板的基础上外面套个二重循环遍历grid即可
这里也直接放官方题解了
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans = 0;
for (int i = 0; i != grid.size(); ++i) {
for (int j = 0; j != grid[0].size(); ++j) {
int cur = 0;
queue<int> queuei;
queue<int> queuej;
queuei.push(i);
queuej.push(j);
while (!queuei.empty()) {
int cur_i = queuei.front(), cur_j = queuej.front();
queuei.pop();
queuej.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int di[4] = {0, 0, 1, -1};
int dj[4] = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
queuei.push(next_i);
queuej.push(next_j);
}
}
ans = max(ans, cur);
}
}
return ans;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/max-area-of-island/solution/dao-yu-de-zui-da-mian-ji-by-leetcode-solution/
有一点可以改进的地方就是不需要两个队列,利用pair可以在一个队列中存储行列,具体方法可以参考733-图像渲染中的BFS解法——严重怀疑题解不是同一个人写的
第八天-BFS/DFS
617-合并二叉树
思路1:DFS
合并两颗树的结点,对两棵树同时进行深度遍历,则一共有以下两种情况
- 两棵树都结点存在:则值相加得到新结点返回
- 仅一棵树结点存在,则直接返回该结点即可
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
//仅一个结点存在时,返回对应结点
if(root1==nullptr)
return root2;
if(root2==nullptr)
return root1;
//递归构建二叉树
TreeNode* new_node = new TreeNode(root1->val+root2->val);
new_node->left = mergeTrees(root1->left, root2->left);
new_node->right = mergeTrees(root1->right, root2->right);
return new_node;
}
};
思路2:BFS
同样我们可以利用队列来模拟广度搜索,三个队列依次存放第一棵树、第二棵树、合并后的树,同样需要考虑上述的三种情况。(有一棵树非空的情况需要分开来考虑)
这里同样放上官方题解——太懒了我
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == nullptr) {
return t2;
}
if (t2 == nullptr) {
return t1;
}
auto merged = new TreeNode(t1->val + t2->val);
auto q = queue<TreeNode*>();
auto queue1 = queue<TreeNode*>();
auto queue2 = queue<TreeNode*>();
q.push(merged);
queue1.push(t1);
queue2.push(t2);
while (!queue1.empty() && !queue2.empty()) {
auto node = q.front(), node1 = queue1.front(), node2 = queue2.front();
q.pop();
queue1.pop();
queue2.pop();
auto left1 = node1->left, left2 = node2->left, right1 = node1->right, right2 = node2->right;
if (left1 != nullptr || left2 != nullptr) {
if (left1 != nullptr && left2 != nullptr) {
auto left = new TreeNode(left1->val + left2->val);
node->left = left;
q.push(left);
queue1.push(left1);
queue2.push(left2);
} else if (left1 != nullptr) {
node->left = left1;
} else if (left2 != nullptr) {
node->left = left2;
}
}
if (right1 != nullptr || right2 != nullptr) {
if (right1 != nullptr && right2 != nullptr) {
auto right = new TreeNode(right1->val + right2->val);
node->right = right;
q.push(right);
queue1.push(right1);
queue2.push(right2);
} else if (right1 != nullptr) {
node->right = right1;
} else {
node->right = right2;
}
}
}
return merged;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-two-binary-trees/solution/he-bing-er-cha-shu-by-leetcode-solution/
116-填充每个节点的下一个右侧节点
这里需要理解完美二叉树的意思——其实就是完全满二叉树(不知道这样说对不对)
思路1:BFS
首先从图中我们大概就能想到使用BFS,每一层的结点之间从左到右利用next指针进行连接,则先利用BFS将树节点按层次置于vector中,再进行遍历给每一层的前i-1个结点的next指针赋值即可
class Solution {
public:
Node* connect(Node* root) {
if(root==NULL)
return root;
vector<vector<Node*>> ans;
queue<Node*> q;
q.push(root);
while(!q.empty())
{
vector<Node*> temp;
int cur = q.size();
for(int i=0;i<cur;i++)
{
Node * now = q.front();
temp.push_back(now);
q.pop();
if(now->left!=NULL)
q.push(now->left);
if(now->right!=NULL)
q.push(now->right);
}
ans.push_back(temp);
}
for(int i=0;i<ans.size();i++)
{
for(int j=0;j<ans[i].size()-1;j++)
ans[i][j]->next = ans[i][j+1];
int last = ans[i].size()-1;
ans[i][last]->next = NULL;
}
return root;
}
};
思路2:利用已有的next指针进行不同父节点的结点之间的连接
考虑到进阶要求是使用常数空间,利用队列和BFS做不符合要求
对于next的连接存在两种情况:
1. 父节点相同的情况
root->left->next = root->right;
2. 父节点不同的情况
父节点不同但是父节点之间通过next进行了连接,所以
root->right->next = root->next->left;
通过以上关系,则不需要利用额外空间进行队列模拟,在常数空间内便可以解决该问题
需要注意每层更新的是下一层的next,每次进入新层需要从最左边开始遍历,当左子树为NULL时,说明当前是叶子节点的层次,不需要再往下
class Solution {
public:
Node* connect(Node* root) {
if(root==NULL)
return root;
Node * cur = root;
while(cur->left!=NULL)//到叶子节点为止
{
Node * head = cur;
while(head)
{
head->left->next = head->right;
if(head->next!=NULL)//保证存在父节点间的next
{
head->right->next = head->next->left;
}
head = head->next;
}
cur = cur->left;
}
return root;
}
};
DFS-BFS模板
void DFS()
{
if(边界条件)
{
相应操作
}
尝试每一种可能
{
满足条件
标记
下一步DFS()
状态回溯(可能存在需要恢复上一个状态)
}
}
void BFS()
{
queue<>q;
q.push(x);
while(!q.empty())
{
对队列中的元素进行相应的操作
q.pop();
if(满足条件)
{
后续的元素入队
q.push();
}
}
}