LeetCode 刷题8

第一题:岛屿数量
用一个二维数组代表一张地图,这张地图由字符“0”与字符“1”组成,其中“0”代表水域,“1”代表小岛土地,小岛“1”被水"0"所包围,当小岛土地“1”在水平和垂直方向相连接时,认为是同一块土地。求这张地图中小岛的数量。Leetcode 200

思考:给定该二维地图grid,与一个二维标记数组mark(初始化为0),mark数组的每个位置都与grid对应,设计一个搜索算法(BFS和DFS),从该地图中的某个岛的某个位置触发,探索该岛的全部土地,将探索到的位置在mark数组中标记为1.
在这里插入图片描述
DFS:
1.标记当前搜索位置已被搜索(标记当前位置的mark数组为1)
2.按照方向数组得到的4个方向,拓展4个新位置newx、newy。
3.若新位置不在地图范围内,则忽略
4.如果新位置未曾到达过(mark[newx][newy]为0)、且是陆地(grid[newx][newy]为“1”),继续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,1};
	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')
			DFS(mark,grid,x, y);           //继续深搜
	}
}

BFS:
1.设置搜索队列Q,标记mark[x][y]=1,并将待搜索的位置(x,y)push进入队列Q;
2.只要队列不空,即取队头元素,按照方向数组的4个方向,拓展4个新位置newx,newy。
3.若新位置不在地图范围内,则忽略。
4.如果新位置未曾到达(mark[newx][newy]为0)、且是陆地(grid[newx][newy]为“1”);将该新位置push进入队列,并标记mark[newx][newy]=1。

void BFS(vector<vector<int>> &mark,vector<vector<char>> &grid , int x, int y){
	queue<pair<x,y>> Q;
	static const int dx[]={-1,1,0,1};
	static const int dy[]={0,0,-1,1};   //方向数组
	Q.push(make_pair(x,y));
	mark[x][y]=1;
	while(!Q.empty()){
		int x = Q.front().first;
		int 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(make_pair(newx,newy));
				 mark[newx][newy]=1;
			}
		}
	}
}

整体算法思路:
1.设置岛屿数量island_num=0;
2.设置mark数组,并初始化.
3.遍历地图grid上所有的点,如果当前点是陆地,且未被访问过,调用搜索接口search(mark,grid,i,j);search可以是DFS或BFS;完成搜索之后islan_num++;

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);  //或BFS
						island_num++;
				}
			}
	}
}

第二题:
已知两个单词(分别是起始单词与结束单词),一个单词词典,根据转换规则计算从起始单词到结束单词的最短转换步数。
转换规则如下:Leetcode 127
1.在转换时,只能转换单词中的一个字符
2.转换得到的新单词,必须在单词词典中。

思路:单词与单词之间的转换。可以理解为一张图,图的顶点为单词,若两单词之间可以互相转换,则这两个单词所代表的顶点间有一条边,求图中节点hit(beginword)到节点cog(endWord)的所有路径中,最少包括多少个节点。即图的宽度优先搜索。
在这里插入图片描述
使用map构造邻接表表示的图,map定义为string为key(代表图的顶点),vector为value(代表图的各个顶点邻接二代顶点)。如下图所示:
在这里插入图片描述
将bgeinword push进入wordlist。遍历wordList,对任意两个单词wordList[i]与wordList[j],若wordList[i]与wordList[j]只差一个字符,则将其相连。

 bool connect(const string & word1,const string &word2){
 			int cnt=0;
			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])){
				graph[wordList[i]].push_back(wordList[j]);
				graph[wordList[j]].push_back(wordList[i]);
			}
		}
	}
}

图得到宽度遍历:
给定图的起点beginword,终点endWord,图graph,从beginword开始宽短优先搜索图graph,搜索过程中记录到达的步数;
1.设置搜索队列Q,队列节点为pair<顶点,步数>;设置集合visit,记录搜索过的顶点;将<beginword,1>添加至队列;
2.只要队列不空,取出队列头部元素:
(1).若取出的队列头部元素为wndword,返回到达当前节点的步数;
(2).否则拓展该节点,将于该节点相邻的且未添加到visit中的节点与步数同时添加至队列Q,并将拓展节点假如visit;
3.最终都无法搜索到endword,返回0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int BFS(string &beginword,string &endword,map<string,vector<string>>& graph){
		vector<string> visit;
		queue<pair<string,int>> Q;   // 顶点,步数
		Q.push(make_pair(begin,1));
		visit.insert(beginword);
		while(!Q.empty()){
				string node = Q.front().first;
				int step = Q.front().second;
				Q.pop();
				if(node==endword)  
					return step;
				vector<string> neighbors = graph[node];
				for(int i=0;i<neighbors.size();i++){
					if(visit.find(heighbors[i])==visit.end()){
							Q.push(make_pair(heighbors[i],step+1));
							visit.insert(heighbors[i]);
					}
				}
		}
		return 0;
}

int ladderLength(string beginword,string endword, vector<string>& wordList){
	map<string,vector<string>> graph;
	construct_graph(beginword, wordList,graph);
	return BFS(beginword,endword, graph);
}

第三题:火柴棍摆正方形
已知一个数组,保存了n个(n<=15)火柴棍,问可否使用这n个火柴棍摆成1个正方形?Leetcode 473
在这里插入图片描述
n个火柴杆(n<=15),每个火柴杆可以属于正方形的4个边中的其中1个。故,暴力搜索有4^15种可能

算法如下:
想象正方形的四条边即为四个桶,将每个火柴杆回溯的放置在每个桶中,在放完n个火柴杆后,检查四个桶中的火柴杆长度和是否相同,相同返回真,否则返回假;在回溯过程中,如果当前所有可能向后的回溯,都无法满足条件,即递归函数最终返回假。
但是这个肯定会超时,所以我们需要对算法进行优化。

优化1:n个火柴杆的总和对4取余需要为0,否则返回假。
优化2:火柴杆按照从大到小的顺序排序,先尝试大的减少回溯可能。
优化3:每次放置时,每条边上不可放置超过总和的1/4长度的火柴杆。

在这里插入图片描述

bool generate(int i,int target,vector<int>& nums,int buckets[]){
	if(i>=nums.size())
		return buckets[0]==target &&  buckets[1]==target &&  buckets[2]==target &&  buckets[3]==target;
	for(int j=0;j<4;j++){
		if(bucket[j]+nums[i]>target)
				continue;
		buckte[j]+=nums[i];
		if(generate(i+1,target, nums,buckets));
		  return true;
		bucket[j]-=nums[i];   //回溯
	}
	return false;
}
bool makesquare(vector<int>& nums){
	if(nums.szie()<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[]={0};
	return generate(0,sum/4,nums,buckets); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值