Leetcode学习之深度优先搜索与宽度优先搜索(1)

开宗明义:本系列基于小象学院林沐老师课程《面试算法 LeetCode 刷题班》,刷题小白,旨在理解和交流,重在记录,望各位大牛指点!


Leetcode学习之深度优先搜索与宽度优先搜索(1)



1、岛屿数量(深搜、宽搜) LeetCode 200.

题目来源 L e e t C o d e   200.   N u m b e r   o f   I s l a n d s LeetCode \ 200. \ Number \ of \ Islands LeetCode 200. Number of Islands
描述用一个二维数组代表一张地图,这张地图由字符 '0’ 或者 ‘1’ 组成。其中 '0’ 代表水域,‘1’ 代表小岛土地。我们认为 '1’ 被 ‘0’ 所包围,当小岛土地 ‘1’ 在水平和垂直方向相连接时,认为是同一块土地。求这张地图中小岛的数量
在这里插入图片描述
分析
在这里插入图片描述


方法一:DFS

分析
在这里插入图片描述
在这里插入图片描述
测试代码:

#include <stdio.h>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

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 = dx[i] + x;
		int newy = dy[i] + y;

		if (newx < 0 || newx >= mark.size() ||	//超出数组边界
			newy < 0 || newy >= mark[newx].size()) {
			continue;
		}

		if (mark[newx][newy] == 0 && grid[newx][newy] == '1') {  //深搜的情况:grid为1且mark没有搜到
			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 (mark[i][j] == 0 && grid[i][j] == '1') {
					DFS(mark, grid, i, j);
					island_num++;
				}
			}
		}
		return island_num;
	}
};

int main() {
	vector<vector<char>> grid;
	char str[10][10] = { "11100","11000 ","00100","00011" };
	for (int i = 0; i < 4; i++) {
		grid.push_back(vector<char>());
		for (int j = 0; j < 5; j++) {
			grid[i].push_back(str[i][j]);
		}
	}
	Solution solve;
	printf("%d\n", solve.numIslands(grid));
	system("pause");
	return 0;
}

效果图
在这里插入图片描述


方法二:BFS

分析
在这里插入图片描述
在这里插入图片描述
测试代码:

#include <stdio.h>
#include <map>
#include <queue>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

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(std::make_pair(x, y));	//将(x,y)push进入队列,并做标记
	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 = dx[i] + x;	//拓展四个方向
			int newy = dy[i] + y;

			if (newx < 0 || newx >= mark.size() || newy<0 || newy>mark[newx].size()) {	//忽略越过地图边界的位置
				continue;
			}	
			//如果当前位置未搜索,且为陆地
			if (mark[newx][newy] == 0 && grid[newx][newy] == '1') {
				Q.push(std::make_pair(newx, newy));	//将新位置push进入队列
				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 (mark[i][j] == 0 && grid[i][j] == '1') {
					BFS(mark, grid, i, j);
					island_num++;
				}
			}
		}
		return island_num;
	}
};

int main() {
	vector<vector<char>> grid;
	char str[10][10] = { "11100","11000 ","00100","00011" };
	for (int i = 0; i < 4; i++) {
		grid.push_back(vector<char>());
		for (int j = 0; j < 5; j++) {
			grid[i].push_back(str[i][j]);
		}
	}
	Solution solve;
	printf("%d\n", solve.numIslands(grid));
	system("pause");
	return 0;
}

效果图
在这里插入图片描述


2、词语阶梯a(宽搜、图、哈希表)LeetCode 127.

题目来源 L e e t C o d e   127.   W o r d   L a d d e r LeetCode \ 127. \ Word \ Ladder LeetCode 127. Word Ladder
描述已知两个单词(起始单词 b e g i n W o r d beginWord beginWord、结束单词 e n d W o r d endWord endWord),一个单词词典 w o r d L i s t wordList wordList ,根据转换规则计算从起始单词到结束但系的最短转换步数
转换规则如下

  • 在转换时,只能转换单词中的1个字符
  • 转换得到的新单词,必须在单词词典中
    在这里插入图片描述
    分析
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

测试代码:

#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <queue>
#include <set>
using namespace std;

//判断word1与word2相差是否只有一个字符
bool connect(const string &word1, const string &word2) {
	int cnt = 0;//记录word1与word2不相等字符的个数
	for (int i = 0; i < word1.length(); 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++) {
		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])){  //判断vector<string>里面两个string是否只相差一个字符
				graph[wordList[i]].push_back(wordList[j]);
				graph[wordList[j]].push_back(wordList[i]);	//然后map映射
			}
		}	//对任意两个单词wordlist[i]与wordlist[j],若wordlist[i]与wordlist[j]只相差一个字符,则将他们相连。
	}
}

//图的宽度遍历
int BFS_graph(string &beginWord, string &endWord, map<string, vector<string>>	&graph) {
	//给定图的起点beginWord,终点endWord,图graph
	queue<pair<string, int>> Q;	//搜索队列  pair<顶点,步数>
	set<string> visit;	//记录已访问的顶点
	Q.push(make_pair(beginWord, 1));  //添加起始点,起始点步数为1  队列初始化

	visit.insert(beginWord);	//标记起点已访问

	while (!Q.empty()) {  //队列不空,就不断进行搜索
		string node = Q.front().first;	//取队列头部节点与步数
		int step = Q.front().second;
		Q.pop();	//每搜索完成一个节点,即从队列弹出

		if (node == endWord) {	//找到终点,返回步数
			return step;
		}
		const vector<string> &neighbors = graph[node];	//取node的全部临接点
		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]);
			}	//标记neighbors[i]已添加至队列 
		}
	}
	return 0;
}

class Solution {
public:
	int ladderLength(string beginword, string endword, vector<string>& wordList) {
		map<string, vector<string>> graph;  //映射string ->vector<string>
		construct_graph(beginword, wordList, graph);	//构造图
		return BFS_graph(beginword, endword, graph);
	}
};

int main() {
	string beginword = "hit";
	string endword = "cog";
	vector<string> wordList;
	wordList.push_back("hot");
	wordList.push_back("dot");
	wordList.push_back("dog");
	wordList.push_back("lot");
	wordList.push_back("log");
	wordList.push_back("cog");
	//beginword="hit",endword="cog",wordlist=["hot","dot","dog","lot","log","cog"]
	Solution solve;
	int result = solve.ladderLength(beginword, endword, wordList);
	printf("result = %d\n", result);
	system("pause");
	return 0;
}

效果图
在这里插入图片描述


3、词语阶梯b(路径的宽搜、图、哈希表) LeetCode 126.

题目来源 L e e t C o d e   126.   W o r d   L a d d e r LeetCode \ 126. \ Word \ Ladder LeetCode 126. Word Ladder
描述已知两个单词(起始单词 b e g i n W o r d beginWord beginWord、结束单词 e n d W o r d endWord endWord),一个单词词典 w o r d L i s t wordList wordList ,根据转换规则计算从起始单词到结束但系的最短转换步数
转换规则如下

  • 在转换时,只能转换单词中的1个字符
  • 转换得到的新单词,必须在单词词典中
  • 在这里插入图片描述

思考
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试代码:

#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <queue>
#include <set>
using namespace std;

struct Qitem {
	string node;	//搜索节点
	int parent_pos;   //前驱节点在队列中的位置
	int step;  //到达当前节点的步数
	Qitem(string node, int parent_pos, int step): node(node),parent_pos(parent_pos),step(step){}
};

//判断word1与word2相差是否只有一个字符
bool connect(const string &word1, const string &word2) {
	int cnt = 0;//记录word1与word2不相等字符的个数
	for (int i = 0; i < word1.length(); i++) {
		if (word1[i] != word2[i]) {
			cnt++;
		}
	}
	return cnt == 1;
}

void BFS_graph(string &beginWord, string &endWord,
	map<string, vector<string> > &graph,
	vector<Qitem> &Q,  //使用vector实现队列,里面包含结构体,可保存所有信息
	vector<int> &end_word_pos) {	//终点 endWord 所在队列的位置下标
	map<string, int> visit;	 //<单词,步数>
	int min_step = 0;	//到达 endWord 的最小步数
	Q.push_back(Qitem(beginWord.c_str(), -1, 1));	//起始单词的前驱为-1
	visit[beginWord] = 1;	//标记起始单词步数为1
	int front = 0;	//队列头指针front,指向vector表示的队列头

	while (front!=Q.size()){
		const string &node = Q[front].node;	//front指向Q.size()即vector尾步时,队列为空
		int step = Q[front].step;	//取队头元素
		
		if (min_step != 0 && step > min_step){	//step>min_step时,代表所有到终点的路径都搜索完成	
			break;
		}

		if (node == endWord) {
			min_step = step;	//当搜索到结果时,记录到达终点的最小步数
			end_word_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;	//标记到达临接点neighbors[i]的最小步数
			}
		}
		front++;
	}
}

void construct_graph(string &beginWord,
	vector<string>& wordList,
	map<string, vector<string>> &graph) {

	int has_begin_word = 0;

	for (int i = 0; i < wordList.size(); i++) {
		if (wordList[i] == beginWord) {
			has_begin_word = 1;
		}
		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_begin_word == 0 && connect(beginWord, wordList[i])) {
			graph[beginWord].push_back(wordList[i]);
		}
	}
}

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;	//使用vector实现的队列
		vector<int> end_word_pos;	//endword在搜索队列的位置
		BFS_graph(beginWord, endWord, graph, Q, end_word_pos);
		vector<vector<string>> result;	//最终结果

		for (int i = 0; i < end_word_pos.size(); i++) {
			int pos = end_word_pos[i];
			vector<string> path;
			while (pos != -1) {
				path.push_back(Q[pos].node);
				pos = Q[pos].parent_pos;
			}
			result.push_back(vector<string>());
			for (int j = path.size() - 1; j >= 0; j--) {
				result[i].push_back(path[j]);
			}
		}
		return result;
	}
};

int main() {
	string beginword = "hit";
	string endword = "cog";
	vector<string> wordList;
	wordList.push_back("hot");
	wordList.push_back("dot");
	wordList.push_back("dog");
	wordList.push_back("lot");
	wordList.push_back("log");
	wordList.push_back("cog");
	//beginword="hit",endword="cog",wordlist=["hot","dot","dog","lot","log","cog"]
	Solution solve;
	vector<vector<string>> result = solve.findLadders(beginword, endword, wordList);
	
	for (int i = 0; i < result.size(); i++) {
		for (int j = 0; j < result[i].size(); j++) {
			printf(" [%s] ", result[i][j].c_str());
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

效果图
在这里插入图片描述


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值