Leetcode学习之递归、回溯与分治(2)

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


Leetcode学习之递归、回溯与分治(2)



1、生成括号(递归设计)Leetcode 22.

题目来源: L e e t c o d e   22.   S u b s e t s Leetcode \ 22. \ Subsets Leetcode 22. Subsets
题目描述已知n组括号,开发一个程序,生成这N组括号的所有合法的组合可能
要求描述
在这里插入图片描述分析
在这里插入图片描述
预备知识思路递归生成所有可能
在这里插入图片描述
测试代码:

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

//item生成括号字符串,n为组数,result为最终结果
void generate(string item, int n, vector<string> &result) {
	if (item.size() == 2*n) {//字符串括号的长度为2n,结束递归
		result.push_back(item);
		return;
	}
	generate(item + '(', n, result);//添加'('字符,继续递归
	generate(item + ')', n, result);//添加')'字符,继续递归
}

int main() {
	vector<string> result;
	generate("", 2, result);
	for (int i = 0; i < result.size(); i++) {
		printf("%s", result[i].c_str());
		printf("\n");
	}
	system("pause");
	return 0;
}

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

#include <string>
#include <vector>
using namespace std;

class Solution {
public:
	vector<string> generateParentthesis(int n) {
		vector<string> result;
		generate("", n,n,result);//n,n表示放置左括号的数目,放置右括号的数目
		return result;
	}
private:
	void generate(string item, int left, int right, vector<string> &result) {//item表示生成的字符串
		if (left == 0 && right == 0) {
			result.push_back(item);
			return;
		}

		if (left > 0) {  //左边
			generate(item + '(', left - 1, right, result);
		}

		if (left < right) { //左边的数量 <= 右边的数量,才可以进行有括号的放置
			generate(item + ')', left, right-1, result);
		}
	}
};

int main() {
	Solution solve;
	vector<string> result = solve.generateParentthesis(3);
	for (int i = 0; i < result.size(); i++) {
		printf("%s\n", result[i].c_str());
	}
	system("pause");
	return 0;
}

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


2、N皇后(回溯法)LeetCode 51.

题目来源: L e e t c o d e   51.   N − Q u e e n s Leetcode \ 51. \ N-Queens Leetcode 51. NQueens
题目描述将N个皇后放在N*N个棋盘中,互相不可攻击,有多少摆放方式,每种摆放方式具体是怎么样的
要求描述传出 N 皇后的所有结果,每个结果是一个棋盘,每个棋盘均使用字符串向量表示
在这里插入图片描述
分析棋盘与皇后表示
在这里插入图片描述
在这里插入图片描述
测试代码:

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

void put_down_the_queen(int x, int y, vector<vector<int>> &mark) {
	//方向数组
	static const int dx[] = { -1,1,0,0,-1,-1,1,1 };
	static const int dy[] = { 0,0,-1,1,-1,1,-1,1 };
	mark[x][y] = 1;//(x,y)位置上放置皇后,进行标记
	for (int i = 1; i < mark.size(); i++) {//8个方向,每个方向向外延伸1至N-1
		for (int j = 0; j < 8; j++) {
			int new_x = x + i*dx[j];//新的位置向8个方向延伸,每个方向最多N-1
			int new_y = y + i*dy[j];
			//检查新位置是否还在棋盘里
			if (new_x >= 0 && new_x < mark.size() && new_y >= 0 && new_y < mark.size()) {
				mark[new_x][new_y] = 1;
			}
		}
	}
}

接下来
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试代码:

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

class Solution {
public:
	vector<vector<string>> solveNQueens(int n) {
		vector<vector<string>> result;//存储最终结果的数组
		vector<vector<int>> mark;//标记棋盘是否可以放置皇后的二维数组
		vector<string> location;//存储某个摆放结果,当完成一次递归找到结果后,将location push进入result
		//二维的vector进行push_back

		/*这边网上的例子是:
		  vector<vector<POINT>> a;
		 for(int i = 0; i < 5; i++){
			 vector<POINT> b;
			 a.push_back(b);
			 for(int j = 0; j < 4; j++){
             POINT c = {i, j};
             a[i].push_back(c);
             }
         }
		*/
		for (int i = 0; i < n; i++) { //这边n=4
			mark.push_back(vector<int>());  //这边先将一维vector进行push_back进去
			for (int j = 0; j < n; j++) {
				mark[i].push_back(0);
			}
			location.push_back("");
			location[i].append(n, '.');
		}

		generate(0, n, location, result, mark);
		return result;
	}
private:
	void put_down_the_queen(int x, int y, vector<vector<int>> &mark) {
		//方向数组
		static const int dx[] = { -1,1,0,0,-1,-1,1,1 };
		static const int dy[] = { 0,0,-1,1,-1,1,-1,1 };
		mark[x][y] = 1;//(x,y)位置上放置皇后,进行标记
		for (int i = 1; i < mark.size(); i++) {//8个方向,每个方向向外延伸1至N-1
			for (int j = 0; j < 8; j++) {
				int new_x = x + i*dx[j];
				int new_y = y + i*dy[j];
				//检查新位置是否还在棋盘里
				if (new_x >= 0 && new_x < mark.size() && new_y >= 0 && new_y < mark.size()) {
					mark[new_x][new_y] = 1;
				}
			}
		}
	}

	void generate(int k, int n, //k代表完成几个皇后的放置(正在放置第K行皇后)
		vector<string> &location, //某次结果存储在location中
		vector<vector<string>> &result, //最终结果存储在result中
		vector<vector<int>> &mark) {//表示棋盘的标记数据

		if (k == n) {//当k==n时,代表完成第0至n-1行皇后的位置,所有皇后完成放置后,将记录皇后位置的location数组push进入result
			result.push_back(location);
			return;
		}

		for (int i = 0; i < n; i++) {//这边n=4
			if (mark[k][i] == 0) {//如果maik[k][i]==0,表示可以放置皇后
				vector<vector<int>> tmp_mark = mark;//记录回溯前的mark镜像
				location[k][i] = 'Q';//记录当前皇后的位置
				put_down_the_queen(k, i, mark);//放置皇后
				generate(k + 1, n, location, result, mark);//递归下一行皇后放置
				mark = tmp_mark;//将mark重新赋值为回溯前状态,这边的原因是该次递归结束后,恢复mark数组,并且尝试下一个可能放皇后的列
				location[k][i] = '.';//将当前尝试的皇后位置更新为。
			}
		}
	}
};

int main() {
	vector<vector<string>> result;
	Solution solve;
	result = solve.solveNQueens(4);
	for (int i = 0; i < result.size(); i++) {
		printf("i=%d\n", i);
		for (int j = 0; j < result[i].size(); j++) {
			printf("%s\n", result[i][j].c_str());
		}
		printf("\n");
	}
	system("pause");
	return 0;
}


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


3、归并排序与分治法

3.1 归并排序

题目描述已知两个已排序的数组,将这两个数组合并成为一个排序数组
要求描述
在这里插入图片描述
测试代码:

#include <vector>
using namespace std;

void merge_sort_teo_vec(vector<int> &sub_vec1,//数组1
	vector<int> &sub_vec2,//数组2
	vector<int> &vec) {//合并后的数组
	int i = 0, j = 0;
	while (i < sub_vec1.size() && j < sub_vec2.size()) 
	{
		if (sub_vec1[i]<=sub_vec2[j])
		{
			vec.push_back(sub_vec1[i]);
			i++;
		}
		else
		{
			vec.push_back(sub_vec2[j]);
			j++;
		}
	}
	for (; i < sub_vec1.size(); i++) {//将sub_vec1或者sub_vec2中的剩余元素push进入vec
		vec.push_back(sub_vec1[i]);
	}
	for (; j < sub_vec2.size(); j++) {
		vec.push_back(sub_vec2[j]);
	}
}

int main() {
	int test1[] = { 2,5,8,20 };
	int test2[] = { 1,3,5,7,30,50 };
	vector<int> sub_vec1;
	vector<int> sub_vec2;
	vector<int> sub_vec;

	for (int i = 0; i < 4; i++) {
		sub_vec1.push_back(test1[i]);
	}
	for (int i = 0; i < 4; i++) {
		sub_vec2.push_back(test2[i]);
	}
	merge_sort_teo_vec(sub_vec1, sub_vec2,sub_vec);
	for (int i = 0; i < sub_vec.size(); i++) {
		printf("[%d]", sub_vec[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

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


3.2 分治法

介绍
在这里插入图片描述
复杂度分析
在这里插入图片描述
测试代码:

#include <vector>
using namespace std;

void merge_sort_teo_vec(vector<int> &sub_vec1,//数组1
	vector<int> &sub_vec2,//数组2
	vector<int> &vec) {//合并后的数组
	int i = 0, j = 0;
	while (i < sub_vec1.size() && j < sub_vec2.size()) 
	{
		if (sub_vec1[i]<=sub_vec2[j])
		{
			vec.push_back(sub_vec1[i]);
			i++;
		}
		else
		{
			vec.push_back(sub_vec2[j]);
			j++;
		}
	}
	for (; i < sub_vec1.size(); i++) {//将sub_vec1或者sub_vec2中的剩余元素push进入vec
		vec.push_back(sub_vec1[i]);
	}
	for (; j < sub_vec2.size(); j++) {
		vec.push_back(sub_vec2[j]);
	}
}

void merge_sort(vector<int> &vec) {
	if (vec.size() < 2) {
		return;//当问题小于2时,就不需要分治了
	}
	//对原问题进行分解,即对原数组拆分为两个规模相同的数组,再对它们进行分别求解
	int mid = vec.size() / 2;
	vector<int> sub_vec1;
	vector<int> sub_vec2;
	for (int i = 0; i < mid; i++) {
		sub_vec1.push_back(vec[i]);
	}
	for (int i = mid; i < vec.size(); i++) {
		sub_vec2.push_back(vec[i]);
	}

	merge_sort(sub_vec1);
	merge_sort(sub_vec2);//对拆解后的两个子网络进行递归求解
	vec.clear();
	merge_sort_teo_vec(sub_vec1, sub_vec2, vec);//合并,将子问题的解进行合并
}

int main() {
	vector<int> vec;
	int test[] = { 5,-7,9,8,1,4,-3,10,2,0 };
	for (int i = 0; i <10; i++) {
		vec.push_back(test[i]);
	}
	merge_sort(vec);
	for (int i = 0; i <vec.size(); i++) {
		printf("[%d]", vec[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

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


4、逆序数 Leetcode 315.

题目来源: L e e t c o d e   315.   C o u n t   o f   S m a l l e r   N u m b e r s   A f t e r   S e l f Leetcode \ 315. \ Count \ of \ Smaller \ Numbers \ After \ Self Leetcode 315. Count of Smaller Numbers After Self
题目描述已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比nums[i]小的元素个数
要求描述
在这里插入图片描述
分析
在这里插入图片描述
接着分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试代码:

#include <vector>
using namespace std;

class Solution {
public:
	vector<int> countSmaller(vector<int> &nums) {
		vector<pair<int, int>> vec;//nums和count数组
		vector<int> count;
		for (int i = 0; i < nums.size(); i++) {
			vec.push_back(make_pair(nums[i], i));//这边后面给的是编号
			count.push_back(0);//count一开始全部赋值为0
		}
		merge_sort(vec, count);
		return count;
	}
private:
	void merge_sort(vector<pair<int, int>> &vec, vector<int> &count) {
		if (vec.size() < 2) {
			return;//当问题小于2时,就不需要分治了
		}
		//对原问题进行分解,即对原数组拆分为两个规模相同的数组,再对它们进行分别求解
		int mid = vec.size() / 2;
		vector<pair<int, int>> sub_vec1;
		vector<pair<int, int>> sub_vec2;
		for (int i = 0; i < mid; i++) {
			sub_vec1.push_back(vec[i]);
		}
		for (int i = mid; i < vec.size(); i++) {
			sub_vec2.push_back(vec[i]);
		}

		merge_sort(sub_vec1, count);
		merge_sort(sub_vec2, count);//对拆解后的两个子网络进行递归求解
		vec.clear();
		merge_sort_teo_vec(sub_vec1, sub_vec2, vec, count);//合并,将子问题的解进行合并
	}


	void merge_sort_teo_vec(vector<pair<int, int>> &sub_vec1,//数组1
		vector<pair<int, int>> &sub_vec2,//数组2
		vector<pair<int, int>> &vec,//合并后的数组
		vector<int> &count) {
		int i = 0, j = 0;
		while (i < sub_vec1.size() && j < sub_vec2.size())
		{
			if (sub_vec1[i].first <= sub_vec2[j].first)
			{
				count[sub_vec1[i].second] += j;//这边改变对应的count数组的值,这边加j的原因前面分析了,重要哦!
				vec.push_back(sub_vec1[i]);
				i++;
			}
			else
			{
				vec.push_back(sub_vec2[j]);
				j++;
			}
		}
		for (; i < sub_vec1.size(); i++) {//将sub_vec1或者sub_vec2中的剩余元素push进入vec
			count[sub_vec1[i].second] += j;
			vec.push_back(sub_vec1[i]);
		}
		for (; j < sub_vec2.size(); j++) {
			vec.push_back(sub_vec2[j]);
		}
	}

};

int main() {
	int test[] = { 5,-7,9,1,3,5,-2,1 };
	vector<int> nums;  //输入数据
	for (int i = 0; i < 8; i++) {
		nums.push_back(test[i]);
	}
	Solution solve;
	vector<int> result = solve.countSmaller(nums);
	for (int i = 0; i < result.size(); i++) {
		printf("[%d]", result[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值