# 诗经·秦风·无衣
岂曰无衣,与子同袍。王于兴师,修我戈矛。与子同仇。
岂曰无衣,与子同泽。王于兴师,修我矛戟。与子谐作。
岂曰无衣,与子同裳。王于兴师,修我甲兵。与子谐行。
#把正确的时间拿来看看书而不是低级趣味。。。
0.摘要
\qquad
递归的原则:当前步骤处理当前的问题,递归处理后续问题;
\qquad
回溯:探索到某一步时,发现原先选择达不到目标,则后退一步重新选择,走不通就退回再走;
\qquad
分治:将原问题分解为若干个**“不相关”**的子问题。
1.回溯法
1.1 基于暴力的二分树
#include<vector>
vector<vector<int>> subsets(vector<int>& nums)
{
vector<vector<int>> result;
vector<int> item;
result.push_back(item); //先将空集入v
generate(0,nums,item,result);
return result;
}
void generate(int i, vector<int>& item, vector<vector<int>>& reuslt)
{
if(i==nums.size()) return;
item.push_back(nums[i]);
result.push_back(item); //选择第i个元素,并写入结果
generate(i+1,nums,item,result); //递归后续元素
item.pop_back(); //不选择第i个元素
generate(i+1,nums,item,result); //递归后续元素
}
利用二进制数模拟三个元素【A,B,C】左右位置表示进制
vector<vector<int>> subsets(vector<int>&nums)
{
vector<vector<int>> result;
int iterations = 1<<nums.size(); //匹配总可能数
for(int i=0; i<iterations; ++i)
{
vector<int> item;
//将每一个位置的j赋予底幂,匹配是否在item中
for(int j=0; j<nums.size(); ++j)
{
if(i&&(1<<j))
{
item.push_back(nums[j]);
}
result.push_back(item);
}
}
return result;
}
1.2递归树剪枝,降低复杂度
A:对于明显不满足要求的结果,提前终止递归
#include<vector>
#include<set>
#include<algorithm>
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
sort(candiates.begin(),candidates.end());
vector<int> item;
vector<vector<int>> result;
set<vector<int>> res_set;
generate(0,candidates,item,result,res_set,0,target);
return result;
}
void generate(int i, vector<int>&candidates,vector<int>&item,vector<vector<int>>& result,set<vector<int>>&res_set,int sum, int target)
{
if(sum>target || i==num.size()) return;
item.push_back(candidate[i]);
sum += candidate[i];
if(sum==target && res_set.find(item)==res_set.end())
{
res_set.insert(item);
result.push_back(item);
}
generate(i+1,candidates,item,result,res_set,sum,target);
sum -= candidates[i];
item.pop_back();
generate(i+1,candidates,item,result,res_set,sum,target);
}
A:在递归生成时,判断左右括号匹配进行剪枝
vector<string> generateParenthesis(int n)
{
vector<string> result;
generate("",n,n,result);
return result;
}
void generate(string item,,int left,int right,vector<string>& result)
{
if(left==0&&right==0)
{
result.push_back(item);
return;
}
if(left>0)
generate(item+'(',left-1,right,result);
if(right>left)
genertae(item+')',left,right-1,result);
}
2.N皇后问题
#include<iostream>
using namespace std;
#include<vector>
#include<string>
void change_flag(int x, int y, vector<vector<int> >&flag)
{
int d_x[] = {-1,-1,-1,0,0,1,1,1};
int d_y[] = {-1,0,1,-1,1,-1,0,1};
flag[x][y] = 1;
for(int i=1;i<flag.size();++i)
{
for(int j=0; j<8; ++j)
{
int newx = x + i*d_x[j];
int newy = y + i*d_y[j];
if(newx>=0 && newx<flag.size() && newy>=0 && newy<flag.size())
{
flag[newx][newy] = 1;
}
}
}
}
void generate(int k, int n, vector<vector<int> >&flag, vector<string>&item, vector<vector<string> >&result)
{
if(k==n)
{
result.push_back(item);
return;
}
for(int i=0; i<n; ++i)
{
if(flag[k][i]==0)
{
vector<vector<int> > tep = flag;
item[k][i] = 'Q';
change_flag(k,i,flag);
cout<<flag[1][1];
generate(k+1,n,flag,item,result);
flag = tep;
item[k][i] = '.';
}
}
}
vector<vector<string> > solveNQueens(int n)
{
vector<string> item;
vector<vector<string> > result;
vector<vector<int> > flag;
for(int i=0; i<n;++i)
{
flag.push_back(vector<int>());
item.push_back("");
for(int j=0; j<n; ++j)
{
flag[i].push_back(0);
item[i].push_back('.');
}
}
generate(0,n,flag,item,result);
return result;
}
3.分治算法
\qquad 将一个规模为N的问题分解为K个规模较小的子问题,这些子问题之间相互独立且与原问题性质相同,求出子问题之后再合并,就可以得到原问题的解。
3.1归并排序
#include<vector>
void merge_sort(vector<int>&nums)
{
if(nums.size()<2) return;
int mid = nums.size()>>1;
vector<vector<int>> sub_left_nums;
vector<vector<int>> sub_right_nums;
for(int i=0; i<mid; ++i)
{
sub_left_nums.push_back(nums[i]);
}
for(int i=mid; i<nums.size(); ++i)
{
sub_right_nums.push_back(nums[i]);
}
merge_sort(sub_left_nums);
merge_sort(sub_right_nums);
nums.clear();
merge_two_sub(sub_left_nums, sub_right_nums, nums);
}
void merge_two_sub(vector<int>&sub_left_nums,vector<int>&sub_right_nums,vector<int>&nums)
{
int i=0;
int j=0;
while(i<sub_left_nums.size() && j<sub_right_nums.size())
{
if(sub_left_nums[i]<sub_right_nums[j])
{
nums.push_back(sub_left_nums[i]);
}
else
{
nums.push_back(sub_right_nums[j]);
}
}
for(;i<sub_left_nums[i];++i)
{
nums.push_back(sub_left_nums[i]);
}
for(;j<sub_right_nums[j];++j)
{
nums.push_back(sub_right_nums[j]);
}
}
3.2改造归并排序,求逆序数
class Solution {
public:
vector<int> countSmaller(vector<int>& nums)
{
vector<pair<int, int>> vec;
vector<int> count;
for(int i=0; i<nums.size(); ++i)
{
vec.push_back(make_pair(nums[i],i));
count.push_back(0);
}
merge_sort(vec,count);
return count;
}
private:
void merge_sort_two(vector<pair<int, int>>& sub_vec1, vector<pair<int, int>>& sub_vec2, vector<pair<int,int>>&vec, vector<int>& count)
{
int i=0;
int 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;
vec.push_back(sub_vec1[i]);
++i;
}
else
{
vec.push_back(sub_vec2[j]);
++j;
}
}
for(; i<sub_vec1.size();++i)
{
count[sub_vec1[i].second] += j;
vec.push_back(sub_vec1[i]);
}
for(; j<sub_vec2.size();++j)
{
vec.push_back(sub_vec2[j]);
}
}
void merge_sort(vector<pair<int,int>>& vec, vector<int>&count)
{
if(vec.size()<2) return;
int mid = vec.size()>>1;
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_two(sub_vec1,sub_vec2,vec,count);
}
};
对于分治和回溯,目前来看都是基于递归树【二叉树或者多叉树】的构建思路,在复杂度较高的情况下,考虑生成剪枝算法抑制树的形状。