深度优先搜索

递归好难哦QAQ

题目描述

给定一个二维的 0-1 矩阵,其中 0 表示海洋,1 表示陆地。单独的或相邻的陆地可以形成岛屿,每个格子只与其上下左右四个格子相邻。求最大的岛屿面积。

对于四个方向的遍历,可以创造一个数组 [-1, 0, 1, 0, -1] ,每相 邻两位即为上下左右四个方向之一。
方法一:
一种是先判定是否越界,只有在合法的情况下才进行下一步搜索(即判断放在调用递归函数前);
vector<int> direction{-1, 0, 1, 0, -1};
// 主函数
int maxAreaOfIsland(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int max_area = 0;
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[0].size(); ++j) {
if (grid[i][j] == 1) {
max_area = max(max_area, dfs(grid, i, j));
} } }
return max_area;
}
// 辅函数
int dfs(vector<vector<int>>& grid, int r, int c) {
if (grid[r][c] == 0) return 0;
grid[r][c] = 0;
int x, y, area = 1;
for (int i = 0; i < 4; ++i) {//深搜+回溯
x = r + direction[i], y = c + direction[i+1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size()) {//看下一步是否越界
area += dfs(grid, x, y);//四个方向分别走到底
} }
return area;
}

方法二: 

不管三七二十一先进行下一步搜索,待下一步搜索开始时再判断是否合法(即判断放在辅函数第一行)
// 主函数
int maxAreaOfIsland(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;//该矩阵为空
int max_area = 0;
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[0].size(); ++j) {
max_area = max(max_area, dfs(grid, i, j));
} }
return max_area;
}
// 辅函数
int dfs(vector<vector<int>>& grid, int r, int c) {
if (r < 0 || r >= grid.size() ||//判断下一步是否越界
c < 0 || c >= grid[0].size() || grid[r][c] == 0) {
return 0;
}
grid[r][c] = 0;//表示这个地已经走过了
return 1 + dfs(grid, r + 1, c) + dfs(grid, r - 1, c) +
dfs(grid, r, c + 1) + dfs(grid, r, c - 1);//以此单岛屿开始的一块连接岛屿
}
题目描述
输入是一个一维整数数组,输出是一个二维数组,表示输入数组的所有排列方式。
对于每一个当前位置 i ,我们可以将其于之后的任意位置交换, 然后继续处理位置 i+1 ,直到处理到最后一位。为了防止我们每此遍历时都要新建一个子数组储存位置 i 之前已经交换好的数字,我们可以利用回溯法,只对原数组进行修改,在递归完成后再修改回来
// 主函数
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> ans;
backtracking(nums, 0, ans);
return ans;
}
// 辅函数
void backtracking(vector<int> &nums, int level, vector<vector<int>> &ans) {
if (level == nums.size() - 1) {
ans.push_back(nums);
return; }
for (int i = level; i < nums.size(); i++) {
swap(nums[i], nums[level]); // 修改当前节点状态
backtracking(nums, level+1, ans); // 递归子节点
swap(nums[i], nums[level]); // 回改当前节点状态 } }
题目描述
给定一个整数 n 和一个整数 k ,求在 1 n 中选取 k 个数字的所有组合方法。
类似于排列问题,我们也可以进行回溯。排列回溯的是交换的位置,而组合回溯的是否把当前的数字加入结果中。
// 主函数
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> comb(k, 0);
int count = 0;
backtracking(ans, comb, count, 1, n, k);
return ans;
}
// 辅函数
void backtracking(vector<vector<int>>& ans, vector<int>& comb, int& count, int
pos, int n, int k) {
if (count == k) {
ans.push_back(comb);
return; }
for (int i = pos; i <= n; ++i) {
comb[count++] = i; // 修改当前节点状态
backtracking(ans, comb, count, i + 1, n, k); // 递归子节点
--count; // 回改当前节点状态 } }
输入输出样例
输入是一个二维字符数组和一个字符串,输出是一个布尔值,表示字符串是否可以被寻找到。
不同于排列组合问题,本题采用的并不是修改输出方式,而是修改访问标记。在我们对任意位置进行深度优先搜索时,我们先标记当前位置为已访问,以避免重复遍历(如防止向右搜索后又向左返回);在所有的可能都搜索完成后,再回改当前位置为未访问,防止干扰其它位置搜索 到当前位置。使用回溯法,我们可以只对一个二维的访问矩阵进行修改,而不用把每次的搜索状态作为一个新对象传入递归函数中。
// 主函数
bool exist(vector<vector<char>>& board, string word) {
if (board.empty()) return false;
int m = board.size(), n = board[0].size();
vector<vector<bool>> visited(m, vector<bool>(n, false));
bool find = false;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
backtracking(i, j, board, word, find, visited, 0);
} }
return find;
}
// 辅函数
void backtracking(int i, int j, vector<vector<char>>& board, string& word, bool
& find, vector<vector<bool>>& visited, int pos) {
if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size()) {
return; }
if (visited[i][j] || find || board[i][j] != word[pos]) {
return; }
if (pos == word.size() - 1) {
find = true;
return; }
visited[i][j] = true; // 修改当前节点状态
// 递归子节点
backtracking(i + 1, j, board, word, find, visited, pos + 1);
backtracking(i - 1, j, board, word, find, visited, pos + 1);
backtracking(i, j + 1, board, word, find, visited, pos + 1);
backtracking(i, j - 1, board, word, find, visited, pos + 1);
visited[i][j] = false; // 回改当前节点状态 }
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值