开宗明义:本系列基于小象学院林沐老师课程《面试算法 LeetCode 刷题班》,刷题小白,旨在理解和交流,重在记录,望各位大牛指点!
Leetcode学习之递归、回溯与分治(1)
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件。 当函数在一直递推,直到遇到结束条件后返回。
递归两要素:①结束条件;②递推关系;
测试代码:
#include
#include
void compute_sum_3(int i, int &sum) {//i=3,将sum=sum+3
sum += i;
}
void compute_sum_2(int i, int &sum) {//i=2,将sum=sum+2,调用compute_sum_3
sum += i;
compute_sum_3(i + 1, sum);
}
void compute_sum_1(int i, int &sum) {//i=1,将sum=sum+1,调用compute_sum_2
sum += i;
compute_sum_2(i + 1, sum);
}
int main() {
int sum = 0;
compute_sum_1(1, sum); //计算1+2+3
printf("sum = %d\n", sum);//将结果存储至sum,并打印sum
system("pause");
return 0;
}
上述代码存在冗余,我们可以写成递归形式:
#include
#include
void compute_sum(int i, int &sum) {
if (i > 3) {
return;//递归结束条件,i>3
}
sum += i;//sum累加i
compute_sum(i + 1, sum);//递归调用,下一次调用会累加i+1
}
int main() {
int sum = 0;
compute_sum(1, sum);
printf("sum = %d\n", sum);
system("pause");
return 0;
}
再来个链表递归例子:
#include
#include
#include
using namespace std;
struct ListNode { //链表的数据结构
int val;
ListNode *next;
ListNode(int x):val(x),next(NULL){}
};
void add_to_vector(ListNode *head, vector<int> &vec) {
if (!head) {//如果head为空则结束递归
return;
}
vec.push_back(head->val);//将当前遍历的节点值push进入vec
add_to_vector(head->next, vec);//继续递归后续链表
}
int main() {
ListNode a(1), b(2), c(3), d(4), e(5);
a.next = &b;
b.next = &c;
c.next = &d;
d.next = &e;
vector<int> vec;
add_to_vector(&a, vec);
for (int i = 0; i < vec.size(); i++) {
printf("[%d]", vec[i]);
}
system("pause");
return 0;
}
回溯法又称为试探法,但当探索到某一步时,发现原先选择达不到目标,就退回一步重新选择,这种走不通就退回再走的方法称为回溯法。
题目来源: L e e t c o d e 78. S u b s e t s Leetcode \ 78. \ Subsets Leetcode 78. Subsets
题目描述:已知一组数(其中无重复元素),求这组数可以组成的所有子集,结果中不可有重复的子集。
要求描述:
测试代码:
#include //nums[]=[1,2,3],只打印这三个subsets=[[1],[1,2],[1,2,3]]
#include
using namespace std;
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(3);//总数组
vector<int> item;//生成各个子集的数组
vector<vector<int>> result;//subsets最终形态
for (int i = 0; i < nums.size(); i++) {//当i=0时,item=[1]
item.push_back(nums[i]);//当i=1时,item=[1,2]
result.push_back(item);//当i=2时,item=[1,2,3]
}
for (int i = 0; i < result.size(); i++) {
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
测试代码:
#include
#include //nums[]=[1,2,3],将子集[[1],[1,2],[1,2,3]]递归的加入result,每次递归的将下一个nums元素放入生成子集的数组item,产生对应的子集
using namespace std;
void generate(int i, vector<int> &nums, vector<int> &item,//输入的数,生成子集的数组
vector<vector<int>> &result) {//最终结果数组
if (i >= nums.size()){//递归结束条件
return;
}
item.push_back(nums[i]);
result.push_back(item);
generate(i + 1, nums, item,result);
}
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(3);
vector<int> item;
vector<vector<int>> result;
generate(0, nums, item, result);
for (int i = 0; i < result.size(); i++) {
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
#include
using namespace std;
class Solution {
public:
vector<vector<int>> subsets(vector<int> &nums) {//输入一个vector数组,返回是一个vector的vector
vector<vector<int>> result;//存储最终结果的result
vector<int> item;//回溯时,产生各个子集的数组
result.push_back(item);//将空集push进去
generate(0, nums,item,result);//计算各个子集,开头,里面再分两个
return result;
}
private:
// i,输入的数组,参生各个子集的数组,最终结果的数组
void generate(int i, vector<int> &nums, vector<int> &item, vector<vector<int>> &result) {
if (i >= nums.size()) {//递归结束的条件
return;
}
item.push_back(nums[i]);
result.push_back(item);//将当前生成的子集添加到result中
generate(i + 1, nums, item, result);//第一次递归调用
item.pop_back();
generate(i + 1, nums, item, result);//第二次递归调用
}
};
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(3);
vector<vector<int>> result;
Solution solve;
result = solve.subsets(nums);//调用
for (int i = 0; i < result.size(); i++) {
if (result[i].size() == 0) {
printf("[]");
}
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
#include
using namespace std;
class Solution {
public:
vector<vector<int>> subsets(vector<int> &nums) {//输入一个vector数组,返回是一个vector的vector
vector<vector<int>> result;//存储最终结果的result
int all_set = 1 << nums.size();//设置全部集合的最大值加1,这边也就是1<<3,也就是0001->1000,变成1<
for (int i = 0; i < all_set; i++) {//遍历所有集合
vector<int> item;
for (int j = 0; j < nums.size(); j++) {//3位,如nums=[1,2,3]
if (i&(1 << j)) {//这边的i就代表集合(A,B,C)对应的所有整数,而(1<
item.push_back(nums[j]);//上述条件为真就放入构造的子集
}
}
result.push_back(item);
}
return result;
}
};
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(3);
vector<vector<int>> result;
Solution solve;
result = solve.subsets(nums);//调用
for (int i = 0; i < result.size(); i++) {
if (result[i].size() == 0) {
printf("[]");
}
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
题目来源: L e e t c o d e 90. S u b s e t s I I Leetcode \ 90. \ Subsets \ II Leetcode 90. Subsets II
题目描述:已知一组数(其中有重复元素),求这种数可以组成的所有子集,结果中无重复的子集。
要求描述:
分析:
思路:
测试代码:
#include
#include
#include
using namespace std;
class Solution {
public:
vector<vector<int>> subsetWithDup(vector<int> &nums){
vector<vector<int>> result;//储存最终结果
vector<int> item;//子集
set<vector<int>> res_set;//去重使用的集合set
sort(nums.begin(), nums.end());//对nums数组进行排序
result.push_back(item);//赋空值
generate(0, nums, result, item, res_set);
return result;
}
private:
void generate(int i, vector<int>&nums, vector<vector<int>>&result, vector<int> &item, set<vector<int>>res_set) {
if (i >= nums.size()) {//递归结束条件
return;
}
item.push_back(nums[i]);//放入
if (res_set.find(item) == res_set.end()) { //如果res_set集合中找不到item
result.push_back(item);//将item放入result里面
res_set.insert(item);//将item放入去重集合res_set中
generate(i + 1, nums, result, item, res_set);
}
item.pop_back();//弹出,回溯
generate(i + 1, nums, result, item, res_set);
}
};
int main() {
vector<int> nums;
nums.push_back(2);
nums.push_back(1);
nums.push_back(2);
nums.push_back(2);
vector<vector<int>> result;//存储最后结果
Solution solve;
result = solve.subsetWithDup(nums);
for (int i = 0; i < result.size(); i++) {
if (result[i].size() == 0) {
printf("[]");
}
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
题目来源: L e e t c o d e 40. C o m b i n a t i o n S u m I I Leetcode \ 40. \ Combination \ Sum \ II Leetcode 40. Combination Sum II
题目描述:已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子集中的各个元素和为整数target的子集,子集需要无重复。
要求描述:
分析:明显上面程序里面加个 if 判断语句,也可以完成。但是时间复杂度太高了。
思路:
测试代码:
#include
#include
#include
using namespace std;
class Solution {
public:
vector<vector<int>> combintionSum2(vector<int> &candidatas, int target) {
vector<vector<int>> result;
vector<int> item;
set<vector<int>> res_set;
sort(candidatas.begin(), candidatas.end());//排序
generate(0, candidatas, result, item, res_set, 0, target);
return result;
}
private:
void generate(int i, vector<int> &nums, vector<vector<int>> &result, vector<int> &item,
set<vector<int>>&res_set, int sum, int target) {
//递归结束的条件
if (i >= nums.size() || sum > target) {
return;//当元素已经选完或者item中的元素和sum已超过target
}
sum += nums[i];//求和
item.push_back(nums[i]);//push进入
//当item的元素的和为target且该结果未添加
if (target == sum && res_set.find(item) == res_set.end()) {//添加符合情况的
result.push_back(item);
res_set.insert(item);
}
generate(i + 1, nums, result, item, res_set, sum, target);
sum = sum - nums[i];
item.pop_back();
generate(i + 1, nums, result, item, res_set, sum, target);
}
};
int main() {
vector<int> nums;
nums.push_back(10);
nums.push_back(1);
nums.push_back(2);
nums.push_back(7);
nums.push_back(6);
nums.push_back(1);
nums.push_back(5);
vector<vector<int>> result;
Solution solve;
result = solve.combintionSum2(nums,8);
for (int i = 0; i < result.size(); i++) {
if (result[i].size() == 0) {
printf("[]");
}
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}