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。