八、搜索(小象)

目录

200、岛屿的个数

127、单词接龙(求最短路径长度)

126、单词接龙II(求最短路径)

473、火柴拼正方形

407、接雨水II(带优先级的宽度优先搜索,堆)


200、岛屿的个数

思考:题目实际上是一个给定了一些点,求这些点有几个连同分支。

DFS:

void DFS(vector<vector<int>> &mark,
	vector<vector<char>> &grid,
	int x, int y) {
	mark[x][y] = 1;
	//在函数中定义静态变量 为了让他在函数结束时,这个变量的值还是存在。
	//就是下次函数调用仍然还是这个值。
	static const int dx[] = { -1,1,0,0 };
	static const int dy[] = { 0,0,1,-1 };
	for (int i = 0; i < 4; i++) {
		int newx = x + dx[i];
		int newy = y + dy[i];
		//地图矩阵并不是方阵
        //如果是4个,那么范围是0 1 2 3 ,这个条件错了!
		if (newx < 0 || newx >= mark.size() ||
			newy < 0 || newy >= mark[newx].size()) {
			continue;
		}
		if (!mark[newx][newy] && grid[newx][newy] == '1') {
			DFS(mark, grid, newx, newy);
		}
	}
}
class Solution {
public:
  int numIslands(vector<vector<char>>& grid) {
		int island_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 (grid[i][j] == '1'&& !mark[i][j]) {
					DFS(mark, grid, i, j);
					island_num++;
				}
			}
		}
		return island_num;
	}
};

BFS:设置一个搜索队列,里面存放的是某个搜索点的坐标,用pair<int,int> 来表示。

操作:在读取队列的首部之后,要把首部进行弹出,然后再进行它所有连接的点(上下左右四个方向的点,满足要求的点)的进队列。

void BFS(vector<vector<int>> &mark,
	vector<vector<char>> &grid,
	int x, int y) {
	static const int dx[] = { -1,1,0,0 };
	static const int dy[] = { 0,0,1,-1 };
	queue<pair<int, int>>  Q;
	Q.push(make_pair(x, y));
	mark[x][y] = 1;
	while (!Q.empty()) {
		x = Q.front().first;
		y = Q.front().second;
		Q.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 (!mark[newx][newy] && grid[newx][newy]=='1') {
				Q.push(make_pair(newx, newy));
				mark[newx][newy] = 1;
			}
		}
	}
}
class Solution {
public:
  int numIslands(vector<vector<char>>& grid) {
		int island_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 (grid[i][j] == '1'&& !mark[i][j]) {
					BFS(mark, grid, i, j);
					island_num++;
				}
			}
		}
		return island_num;
	}
};

127、单词接龙(求最短路径长度)

 

思考:每次走一步,就相当于是只能走邻接的结点,根据题目当中给的关系“只允许一个字符不一样”,来构建它们之间的连接关系,构成一个无向图。

图是一种逻辑概念,最开始是用指针来构造,其实用什么构造无所谓,我们可以用一个map,key代表string,和哪些顶点相连,用一个vector<string>来表示。

bool connect(string word1,string word2){
    int cnt = 0;
    for(int i = 0; i< word1.size();i++){
        if(word1[i] != word2[i]){
            cnt++;
        }
    }
    return cnt==1;
}
void construct_Graph(string beginWord, vector<string>& wordList,
                   map<string,vector<string>>& Graph) {
        wordList.push_back(beginWord) ;
        for(int i = 0 ; i <wordList.size();i++ ){
        //map建立映射关系
        //vector<string>() 无参数的默认构造函数
             Graph[wordList[i]] = vector<string>();
        }
        /*for(auto item1 :wordList ){
            for(auto item2 :wordList){
                if(connect(item1,item2)){
                    Graph[item1].second.push_back(item2);
                    Graph[item2].second.push_back(item1);
                }
            }
        }*/
        for(int i = 0 ;i <wordList.size();i++ ){
          for(int j = i +1 ; j <wordList.size();j++ ){
                if(connect(wordList[i],wordList[j])){
                    //queue 才是first 和second 并且 queue不是push_back 而是 push
                    // map 就相当于是数组当应用
                     /*Graph[wordList[i]].second.push_back(wordList[j]);
                     Graph[wordList[j]].second.push_back(wordList[i]);*/
                    Graph[wordList[i]].push_back(wordList[j]);
                    Graph[wordList[j]].push_back(wordList[i]);
          }
         }
        }
    }
class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
         map<string,vector<string>> Graph;
         construct_Graph(beginWord,wordList,Graph);
         set<string> visit;
         queue<pair<string,int>> Q;
         Q.push(make_pair(beginWord,1));
         visit.insert(beginWord);
         while(!Q.empty()){
            string word = Q.front().first;
            int step =  Q.front().second;
            Q.pop();
            if(word ==endWord ){
               return step;
            }
            const vector<string> &neighbors = Graph[word];
            for(int i = 0 ; i < neighbors.size();i++){
                if(visit.find(neighbors[i])==visit.end()){
                    Q.push(make_pair(neighbors[i],step +1));
                    visit.insert(neighbors[i]);
                }
            }
         }
         return 0;  
    }
};

126、单词接龙II(求最短路径)

构建图的时候:由于wordlist可能存在beginword,直接将beginword push到wordlist可能会出现重复的情况。

比如[hot,dot,dog,lot,log,cog]其中已经存在beginword = hot,此时如果不再处理直接添加进入wordlist,那么wordlist变成:[hot,dot,dog,lot,log,cog,hot],此时建立图之间的邻接矩阵:lot->hot|dot|log|hot,当中hot出现了两次。

struct Qitem{
    string node;
    int pre_pos;
    int step;
    Qitem(string Node, int Pre_pos,int Step):node(Node),pre_pos(Pre_pos),step(Step){}
};
bool connect(string word1,string word2){
    int cnt = 0;
    for(int i = 0 ;i < word1.size();i++){
        if(word1[i] !=word2[i]){
            cnt++;
        }
    }
    return cnt == 1;
}
void construct_Graph(string beginWord,vector<string>& wordList,map<string,vector<string>>&Graph){
    int has_word = 0;
    for(int i = 0 ;i < wordList.size();i++){
        if(beginWord == wordList[i]){
                has_word++;
        }
        Graph[wordList[i]] = vector<string>();
    }
    for(int i = 0 ;i < wordList.size();i++){
        for(int j = i +1 ; j <wordList.size();j++ ){
           if(connect(wordList[i],wordList[j])){
                Graph[wordList[i]].push_back(wordList[j]);
                Graph[wordList[j]].push_back(wordList[i]);
           }
        } 
        if(has_word == 0 && connect(wordList[i],beginWord)){
            Graph[beginWord].push_back(wordList[i]);
        }
    }
}
void BFS_Graph(string beginWord, string endWord, vector<string>& wordList,
              map<string,vector<string>>&Graph,vector<Qitem> &Q,vector<int> & endword_pos){
    map<string,int> visit; //map 和set 都可以使用find
    int min_step = 0;
    Q.push_back(Qitem(beginWord,-1,1));
    visit[beginWord] = 1;
    int front = 0;
    
    /*while(!Q.empty())*/ //!!!不好好思考
    while(front != Q.size()){
        const string &node = Q[front].node;
        int step = Q[front].step;
        if(min_step && min_step > step){
            break;
        }
        if(node == endWord){
            min_step = step;
            endword_pos.push_back(front);
        }
        const vector<string> &neighbors = Graph[node];
        for(int i = 0; i < neighbors.size();i++ ){
            if(visit.find(neighbors[i]) == visit.end() ||visit[neighbors[i]] == step +1 ){
                Q.push_back(Qitem(neighbors[i],front,step + 1));
                visit[neighbors[i]] = step +1 ;
            }
        }
        front++;
    }
}
class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        map<string,vector<string>> Graph;
        construct_Graph(beginWord, wordList,Graph);
        vector<Qitem> Q;
        map<string, int> visit;
        vector<int> endword_pos;
        BFS_Graph( beginWord, endWord, wordList, Graph,Q,endword_pos);
        vector<vector<string>> result;
        for(int i = 0 ; i <endword_pos.size();i++ ){
            int pos = endword_pos[i];
            vector<string> path;
            while(pos != -1){
              path.push_back(Q[pos].node);
              pos = Q[pos].pre_pos;
            }
            //本来是invalid use void expression 
            //就是错误的使用没有返回值的函数
            reverse(path.begin(),path.end());
            result.push_back(path);
        }
        return result;
    }
};

473、火柴拼正方形

思考:

1、要构成正方形,首先火柴棍长度之和肯定是4的倍数,所以要先判断对4取余。

2、每一条边放的火柴棍总长度肯定是小于总长/4。

3、类似于贪心算法,如果要拼接的时候,先抽出大的,存放到每个边,再用小的去弥补,这样可以大大降低回溯的可能性。

把存放到四条边记作放在四个桶之中,进行回溯试探,如果这个棍子可以放在当前的桶就放进去,然后去试探下一个桶,如果不行,就返回,把这个棍子从这个桶拿出来。

bool generate(int i,vector<int>& nums,int target,int buckets[]){
    if(i >= nums.size() ){
        return target == buckets[0] &&target == buckets[1]
            &&target == buckets[2]&&target == buckets[3];
    }
    for(int j = 0; j < 4 ; j++){
        if(buckets[j] + nums[i] > target){
           continue;
        }
         buckets[j] += nums[i];
        if(generate(i+1,nums,target,buckets)){
            return true;
        }
        buckets[j] -= nums[i];
    }
    return false;
}
class Solution {
public:
    bool makesquare(vector<int>& nums) {
    if(nums.size() < 4){
        return false;
    }
    int sum = 0;
    for(int i = 0 ; i < nums.size();i++){
        sum += nums[i];
    }
    if(sum % 4){
        return false;
    }
    sort(nums.rbegin(),nums.rend());
    int buckets[4] = {0};
    return  generate(0,nums,sum / 4,buckets);
    }
};

方法2:位运算法

找出所有可以组合成需要的边长(sum / 4)的火柴棍组合,两两想&,等于0,说明没有重叠,可以组合成2条边,再类推组合另外两条边,最后再组合成一个正方形。

1、多少种组合数? 1<<nums.size()

2、位运算符 & 和 | ,逻辑运算符 && 和 ||

class Solution {
public:
    bool makesquare(vector<int>& nums) {
       if(nums.size() < 4){
           return false;
       } 
    int sum = 0 ;
        for(int i = 0 ;i < nums.size();i++){
            sum += nums[i];
        }
        if(sum % 4) {
            return false;
        }
        int target = sum / 4;
        int all = 1 << nums.size(); //找出总共有多少组合
        //每一位都存在两种可能 选或者不选
        //位运算符不要忘记括号
        vector<int> OK_set;
        vector<int> half_set;
        for(int i = 0;i < all; i ++){
            int sum = 0;
            for(int j = 0 ; j < nums.size();j++){
                if(i & (1<<j)){
                    sum += nums[j]; 
                }
            }
                if(sum == target){
                    OK_set.push_back(i);
                }
        }
        for(int i = 0 ;i < OK_set.size();i++){
            for(int j = i + 1 ; j < OK_set.size();j++){
                if((OK_set[i] & OK_set[j])== 0){
                    half_set.push_back(OK_set[i] | OK_set[j]);
                }
            }
        }
           for(int i = 0 ;i < half_set.size();i++){
            for(int j = i + 1 ; j < half_set.size();j++){
                if((half_set[i] & half_set[j] )== 0){
                        return true;
                }
            }
           }
        return false;
    }
};

407、接雨水II(带优先级的宽度优先搜索,堆)

 

STL优先级队列

#include<iostream>
#include<queue>
using namespace std;
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;
	}
};
void main() {
	priority_queue<Qitem, vector<Qitem>, cmp> Q;
	Q.push(Qitem(0, 0, 5));
	Q.push(Qitem(1, 3, 2));
	Q.push(Qitem(5, 2, 4));
	Q.push(Qitem(0, 1, 8));
	Q.push(Qitem(6, 7, 1));
	while (!Q.empty()) {
		int x = Q.top().x;
		int y = Q.top().y;
		int h = Q.top().h;
		printf("x = %d y = %d h = %d", x, y, h);
		Q.pop();
	}
}

代码:

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;
    }
};
class Solution {
public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        priority_queue<Qitem,vector<Qitem>,cmp> Q;
 //这样写不行,如果是空vector,此时你已经用了hegihtMap[0],
        /*  int row = heightMap.size();
        int col = heightMap[0].size();
        if(row < 3 || col < 3){
            return 0;
        }*/
        if(heightMap.size() < 3|| heightMap[0].size() < 3){
            return 0;
        }
        int row = heightMap.size();
        int col = heightMap[0].size();
        vector<vector<int>> Mark;
        for(int i = 0 ; i < row; i++){
            Mark.push_back(vector<int>());
            for(int j = 0 ; j < col; j++){
                Mark[i].push_back(0);
            }
        }
        //画图弄清楚 四周到底是什么形状!!!
        for(int i = 0 ; i <row ; i++){
            Q.push(Qitem(i,0,heightMap[i][0]));
            Mark[i][0] = 1 ;
            Q.push(Qitem(i,col - 1 ,heightMap[i][col - 1]));
            Mark[i][col - 1 ] = 1 ;
        }
        for(int j = 1 ; j <col - 1 ; j++){
            Q.push(Qitem(0,j,heightMap[0][j]));
            Mark[0][j] = 1 ;
            Q.push(Qitem(row - 1 ,j,heightMap[row - 1][j]));
            Mark[row - 1][j] = 1 ;
        }
        static const int dx[] = {-1,1,0,0};
        static const int dy[] = {0,0,1,-1};
        int result = 0;
        while(!Q.empty()){
            int x = Q.top().x;
            int y = Q.top().y;
            int h = Q.top().h;
            Q.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 >=col  || Mark[newx][newy]){
                    continue;
                }
                if(h >heightMap[newx][newy] ){
                    result += h - heightMap[newx][newy];
                   heightMap[newx][newy] = h;
                }
                Q.push(Qitem(newx,newy,heightMap[newx][newy]));
                 Mark[newx][newy] = 1 ;
            }
        }
        return result;
    }
};

总结:我的脑子可能已经不够用了....

1、什么时候用深搜,宽搜:

单一的搜索,无回溯和重试,不需要求最短路之类,是层级概念,用深搜和宽搜一样,比如求树节点和图节点全部搜出来。

2、记录路径长度,路径的问题,使用宽度优先搜索。

3、尝试,试探时->回溯 ->深度优先搜索

4、地图搜索的题目,都要有一个对应的二维vector mark。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值