LeetCode-算法(四)——DFS和BFS

第七天-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
合并两颗树的结点,对两棵树同时进行深度遍历,则一共有以下两种情况

  1. 两棵树都结点存在:则值相加得到新结点返回
  2. 仅一棵树结点存在,则直接返回该结点即可
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();
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家一级假勤奋研究牲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值