算法基础 典型题(八)搜索

记录算法基础题思路:
step1:

岛屿数量:https://leetcode-cn.com/problems/number-of-islands/

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。

public:
    int numIslands(vector<vector<char>>& grid) {
        /* 核心算法在于设计出算法从图中任何一个点出发,
        探索出地图该岛屿的全部土地,并标记(宽度或深度搜索))。
        遍历一次地图,如果为陆地,且未被mark就开始探索*/
        int land_num = 0;
        vector<vector<int>> mark;
        for (int i = 0; i < grid.size(); i++) {
            mark.push_back(vector<int>());
            for (int j = 0; j < grid[i].size(); j++) {
                mark[i].push_back(0);
            }
        }

        for (int i = 0; i < grid.size(); i++) {
            for (int j = 0; j < grid[i].size(); j++) {
                if (mark[i][j] == 0 && grid[i][j] == '1') {
                    //DFS(mark, grid, i, j); //方法一:深度搜索
                    BFS(mark, grid, i, j); //方法二:(广)宽度搜索
                    land_num++;
                }
            }
        }
        return land_num;
    }

    void DFS(vector<vector<int>>& mark, vector<vector<char>>& grid,
        int x, int y) {
        /*  方法一 深度搜索(DFS):标记当前位置-》往4个方向拓展-》
        若新位置在地图范围内/是土地/未被探索标记-》
        以此新地方为位置继续DFS */

        mark[x][y] = 1;//标记当前探索位置
        const int dx[] = {-1, 1, 0, 0};//方向数组
        const int dy[] = {0, 0, -1, 1};
        for (int i = 0; i < 4; i++) {
            int newx = x + dx[i];
            int newy = y + dy[i];
            /* 校验边界*/
            if (newx < 0 || newx >= mark.size() ||
                newy < 0 || newy >= mark[newx].size()) {
                continue;
            }
            /* 满足土地和未被探索条件 */
            if (grid[newx][newy] == '1' && mark[newx][newy] == 0) {
                DFS(mark, grid, newx, newy);
            }
        }
    }

    void BFS(vector<vector<int>>& mark, vector<vector<char>>& grid,
        int x, int y) {
        /*  方法二 广(宽)度搜索(BFS):标记当前位置送que-》
        只要队列不空 取出头元素,按4个方向拓展新位置-》
        若新位置在地图范围内/是土地/未被探索标记 继续入que*/
        mark[x][y] = 1;//标记当前探索位置

        const int dx[] = {-1, 1, 0, 0};//方向数组
        const int dy[] = {0, 0, -1, 1};
        queue <pair<int, int>> pos_que;
        mark[x][y] = 1;
        pos_que.push(make_pair(x, y));
        while (!pos_que.empty()) {
            /* 取点 */
            x = pos_que.front().first;
            y = pos_que.front().second;
            pos_que.pop();
            for (int i = 0; i < 4; i++) {
                int newx = x + dx[i];
                int newy = y + dy[i];
                /* 校验边界*/
                if (newx < 0 || newx >= mark.size() ||
                    newy < 0 || newy >= mark[newx].size()) {
                    continue;
                }
                /* 满足土地和未被探索条件 */
                if (grid[newx][newy] == '1' && mark[newx][newy] == 0) {
                    mark[newx][newy] = 1;
                    pos_que.push(make_pair(newx, newy));                   
                }
            }
        }
    }

词语阶梯:https://leetcode-cn.com/problems/word-ladder/

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        /* 先宽度搜索找到最小路径,
            遍历的过程记录下begin到每个word到的step, 以及每个word前一个word(可能多个),
            然后再深度查找 从 endword 所有可能的路径,
            从endword往前找,直到找到begin,就把过程记录放到res中 */
        vector<vector<string>> res;
        bool can_search = false;
        for (auto a : wordList) {
            if (a == endWord) {
                can_search = true;
            }
        }
        if (can_search == false) return res;
        unordered_map <string, int> dist_to_begin; // 存储起始点到每个点距离
        unordered_map<string, unordered_set<string>> from; // 记录前一个单词(距离相同)
        queue <pair<string, int>> node_que; // pair为节点和到起点距离
        unsigned int min_step = 0;
        node_que.push({ beginWord, 0 });

        dist_to_begin[beginWord] = 0;
        from[beginWord] = unordered_set<string>{ "" };
        while (!node_que.empty()) {
            for (int i = node_que.size(); i > 0; i--) {
                string node = node_que.front().first;
                int step = node_que.front().second;
                node_que.pop();
                for (auto &word : wordList) {
                    if (conect(node, word)) {
                        if (dist_to_begin.count(word) == 0) {  //如果没有达到过
                            node_que.push({ word, step + 1 });
                            dist_to_begin[word] = step + 1;
                            from[word].insert(node);
                        } else if (dist_to_begin[word] == step + 1) {
                            from[word].insert(node);
                        }
                        if (word == endWord) {
                            min_step = step + 1;
                        }
                    }
                }
            }
            if (min_step != 0) {
                break;
            }
        }
        vector<string> path;
 		dfs_for_path(from, res, endWord, path);
		for (auto &i : res) {
			reverse(i.begin(), i.end());
		}
        return res;    
    }
private:
    bool conect(string &a, string &b) {
        int diff_cnt = 0;
        for (int i = 0; i < a.length(); i++) {
            if (a[i] != b[i]) {
                diff_cnt++;
                if (diff_cnt > 1) { //优化时间
                    return false;
                }
            }
        }
        return diff_cnt == 1;
    }
	void dfs_for_path(unordered_map<string, unordered_set<string>>& from,
        vector<vector<string>>& res, string word, vector<string> path) {
        /* 若当前节点的上一级节点为空,表明到达了起始节点,
        那么将这个节点推入path中,然后将完整的path推入答案中 */
		if (from[word] == unordered_set<string>{ "" }) {
			path.push_back(word);
			res.push_back(path);
			return;
		}
        //每遍历到一个节点那么就将它放入路径中
		path.push_back(word);
        //然后去深度优先遍历他的每一个上层节点
		for (auto i : from[word])
			dfs_for_path(from, res, i, path);
	}
};

火柴摆正方形:https://leetcode-cn.com/problems/matchsticks-to-square/submissions/

还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

public:
    bool makesquare(vector<int>& nums) {
        /* 递归回溯 + 剪枝
            回溯:每个火柴都在4边尝试放置,如果放完后四边相同即true,否则为false
            剪枝:1、入口条件 和不为4整数倍 2、某一边超过target 3、先放大的试错(排序后) */
            int sum = 0;
            for (auto i: nums) {
                sum += i;
            }
            if (nums.size() < 4 || sum % 4 != 0) {
                return false;
            }
            sum /= 4;
            sort(nums.rbegin(), nums.rend()); //剪枝
            int side_sum[4] = { 0 };
            return backtrace_node(0, nums, side_sum, sum);

    }
private:
    bool backtrace_node(int index, vector<int>& nums, int side_sum[], int target) {
        if (index >= nums.size()) {
            return side_sum[0] == target && side_sum[1] == target &&
                side_sum[2] == target && side_sum[3] == target;
        }
 
        for (int i = 0; i < 4; i++) { // 把nums尝试放到每一边
            if (side_sum[i] + nums[index] > target) {
                continue; // 剪枝
            }
            side_sum[i] += nums[index];
            if (backtrace_node(index + 1, nums, side_sum, target) == true) {
                return true;
            }
            side_sum[i] -= nums[index];      
        }
        return false;
    }

收集雨水2:https://leetcode-cn.com/problems/trapping-rain-water-ii/submissions/

给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
示例:
给出如下 3x6 的高度图:
[
  [1,4,3,1,3,2],
  [3,2,1,3,2,4],
  [2,3,3,2,3,1]
]
返回 4

struct Qitem {
    int x;
    int y;
    int h;
    Qitem(int _x, int _y, int _h) :
     x(_x), y(_y), h(_h){ }
};
struct cmp {
    bool operator() (const Qitem &a, const Qitem &b){
        return a.h > b.h;
    }
};
public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        /* 使用最小堆搜索, 越底越优先,从四周的点开始 宽度优先遍历,
         使用一个二维数组堆点进行标记,遍历过改点后不再访问呢,
         只要堆中不空,就取出顶点进行访问,按上下左右方向进行拓展,
         某点(x,y,h)如果h > heightMap[newx][newy], 
            最终结果+= h-heightMap[newx][newy],
            heightMap[newx][newy]赋值为h(升高水面),
            然后将(newx newy heightMap[newx][newy])入堆,并标记 */
        if (heightMap.size() < 3 || heightMap[0].size() < 3) {
            return 0; // 行或者列小于3无法存水
        }
        int row = heightMap.size();
        int column = heightMap[0].size();
        vector <vector <int>> mark;//标记哪些点已经访问
        priority_queue <Qitem, vector<Qitem>, cmp> min_heap;
        /* 构建mark */
        for (int i = 0; i < row; i++) {
            mark.push_back(vector<int>());
            for (int j = 0; j < column; j++) {
                mark[i].push_back(0);
            }
        }
        /* 把四周的点都先入堆 */
        for (int i = 0; i < row; i++) {
            min_heap.push(Qitem(i, 0, heightMap[i][0]));
            mark[i][0] = 1;
            min_heap.push(Qitem(i, column - 1, heightMap[i][column - 1]));
            mark[i][column - 1] = 1;                     
        }
        for (int i = 1; i < column - 1; i++) {
            min_heap.push(Qitem(0, i, heightMap[0][i]));
            mark[0][i] = 1;
            min_heap.push(Qitem(row - 1, i, heightMap[row - 1][i]));
            mark[row - 1][i] = 1;             
        }
        static const int dx[] = { -1, 1, 0, 0 };
        static const int dy[] = { 0, 0, -1, 1 };
        int res = 0;
        while (!min_heap.empty()) {
            int x = min_heap.top().x;
            int y = min_heap.top().y;
            int h = min_heap.top().h;
            min_heap.pop();
            for (int i = 0; i < 4; i++) {
                int newx = x + dx[i];
                int newy = y + dy[i];
                if (newx < 0 || newx >= row || newy < 0 || newy >= column || mark[newx][newy]) {
                    continue; // 超出边界或者已经访问的点不再访问
                }
                if (h > heightMap[newx][newy]) { //如果新的遍历点 比当前点底,可以存h - newh
                    res += h - heightMap[newx][newy];
                    heightMap[newx][newy] = h; // 更新新的这个点h(水位已经到了h)
                }
                min_heap.push(Qitem(newx, newy, heightMap[newx][newy]));
                mark[newx][newy] = 1;
            }
        }
        return res;
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值