数据结构与算法-递归分治
往期内容
1-链表
2-栈与队列
3-树与图
4-哈希表
5-查找
6-排序
7-贪心
8-递归与分治
9-动态规划
递归的套路
递归程序的模板
void recursion(type level(层数:梦境中的第几层),param1,param2,...)
{
//递归的终止条件
if(...)
{
...
return;
}
//处理数据
process(...);
//递归到下一层
recursion(level+1,p1,p2,...);
//下一层处理完成,来到此层,进行收尾的工作。
reverse_satae(level);
}
分治的套路
//分支的递归模板
void divide_conquer(problem,param1,param2,...)
{
//递归的终止条件
if problem is None;
print_result;
return;
//处理数据
data=prepare_data(problem)
subproblems=split_problem(problem,data)
//分治子问题
subresult1=divide_conquer(subproblem[0],p1,p2,...)
subresult2=divide_conquer(subproblem[1],p1,p2,...)
subresult3=divide_conquer(subproblem[2],p1,p2,...)
result=process_result(subresult1,subresult2,subresult3);
}
1.子集
Leetcode-78 子集
思路:对于每个元素都要两种情况:放还是不放,定义个addr存在中间结果。
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
vector<int> addr;
result.push_back(addr);
generate1(0,nums,addr,result);
return result;
}
void generate(int i,vector<int>& nums,vector<int>& addr,vector<vector<int>> &res)
//i表示第i个元素放还是不放
{
if(i>=nums.size())
return;
//放入当前元素(递)
addr.push_back(nums[i]);
res.push_back(addr);
generate(i+1,nums,addr,res);
//不放入当前元素(回溯)(归)
addr.pop_back();
generate(i+1,nums,addr,res);
}
};
2.子集2
Leetcode-90 子集ii
思路:使用sort进行排序,在用set进行去重,最后调用递归
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
//排序,set去重
vector<vector<int>> result;
vector<int> item;
set<vector<int>> res_set;
result.push_back(item);
sort(nums.begin(),nums.end(),[](int &a,int &b){
return a<b;
});
generate(0,nums,result,item,res_set);
return result;
}
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())
{
result.push_back(item);
res_set.insert(item);
}
generate(i+1,nums,result,item,res_set);
item.pop_back();
generate(i+1,nums,result,item,res_set);
}
};
3.组合总和ii
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> result;
vector<int> item;
set<vector<int>> res_set;
int sum=0;
sort(candidates.begin(),candidates.end(),[](int &a,int &b){
return a<b;
});
generate(0,candidates,target,result,item,res_set,sum);
return result;
}
void generate(int i,vector<int>& candidates,int target,
vector<vector<int>> &result,vector<int> &item,set<vector<int>> &res_set,int &sum)
{
if(i>=candidates.size() || sum>target)
{
return;
}
sum+=candidates[i];
item.push_back(candidates[i]);
if(sum==target && res_set.find(item)==res_set.end())
{
result.push_back(item);
res_set.insert(item);
}
generate(i+1,candidates,target,result,item,res_set,sum);
sum-=candidates[i];
item.pop_back();
generate(i+1,candidates,target,result,item,res_set,sum);
}
};
4.括号生成器
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string item="";
int left=n;
int right=n;
generate(item,result,n,n);
return result;
}
void generate(string item,vector<string> &result,int left,int right)
{
if(left==0 && right==0)
{
result.push_back(item);
return;
}
if(left>0)
{
generate(item+"(",result,left-1,right);
}
if(right>left)
{
generate(item+")",result,left,right-1);
}
}
};
5.n皇后
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> result;//存放最终结果数组
vector<vector<int>> mark;//是否可以放置皇后数组
vector<string> location;//存放结果
//初始化必要的变量
for(int i=0;i<n;i++){
mark.push_back(vector<int>());//匿名变量
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;
}
void generate(int row,int n,vector<string> &location,vector<vector<string>> &result,vector<vector<int>> &mark)
{
if(row==n){
result.push_back(location);
return;
}
for(int i=0;i<n;i++)
{
if(mark[row][i]==0)//可以存放(注意row对应的是x坐标,n列对应的是纵坐标)
{
//保存当前的状态
vector<vector<int>> temp_mark=mark;
location[row][i]='Q';
put_down_the_queue(row,i,mark);
generate(row+1,n,location,result,mark);//递归下一状态
mark=temp_mark;
location[row][i]='.';
}
}
}
private:
void put_down_the_queue(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)放置皇后,进行标记(注意数据的x和y的表示)
for(int i=1;i<mark.size();i++)
{
for(int j=0;j<8;j++)//8个方向
{
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;
}
}
}
}
};
6. 归并排序
//9.归并排序
//归并排序由三个函数组成
// 归并排序主要由如下的三个函数组成
// void MergeSort(int arr[],int length);
// void MSort(int arr[],int begin,int end);
// void Merge(int arr[], int low,int mid,int high);
void Merge(int arr[],int begin,int mid,int end)
{
int* tempArr=(int *)malloc((end-begin+1)*sizeof(int));
// int length=end-begin+1;
// int tempArr[length];
int i=begin;//前半部分
int j=mid+1;//后半部分
int k=0;//前半数据和后半数据,合并到新的数组的下标
while(i<=mid && j<=end)
{
if(arr[i]<=arr[j])
{
tempArr[k++]=arr[i++];
}
else
{
tempArr[k++]=arr[j++];
}
}
while(i<=mid)
{
tempArr[k++]=arr[i++];
}
while(j<=end)
{
tempArr[k++]=arr[j++];
}
//将tempArr中排序完成的数据,从新拷贝到
//begin到end的区间上去
for(i=begin,k=0;i<=end;i++,k++)
{
arr[i]=tempArr[k];
}
delete[] tempArr;
}
void MSort(int arr[],int begin,int end)
{
if(begin>=end)
return ;
int mid=(begin+end)/2;
MSort(arr,begin,mid);
MSort(arr,mid+1,end);
Merge(arr,begin,mid,end);
}
void Mergesort(int arr[],int length)
{
MSort(arr,0,length-1);
}
7. 其他
50.pow(x,n)
方法1:库
方法2:暴力
方法3:分治:二分,偶数:y*y 奇数:y*y*x
169 多数元素
方法1:暴力
方法2:map
方法3:暴力
方法3:分治
贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择有回退功能。
122 买卖股票
方法1:深度优先遍历DFS,所有情况
方法2:贪心
方法3:DP
并查集:
伪代码:
function MakeSet(x)
x.parent=x;
//查询
function Find(x)
if(x.parent==x)
return x
else
return Find(x.parent);
//合并
function Union(x,y)
X_root=find(x);
Y_root=find(y);
X_root.parent =Y_root;
优化1:深度降低一些
优化2: 进行路径压缩
二分查找:(背)
前提:有序,有界,可以通过索引访问
69 x的平方根
1.二分法
2.牛顿迭代法
trie 树
位运算:
X&1==1 OR ==0 判断奇偶性(X%2==1)
X= X&(X-1) 清零最低位的1
X& -X 得到最低位的1
191 二进制数有多少个为1
方法1:%2 计算count
方法2:X= X&(X-1) 清零最低位的1 计数
231.power of two
方法:二进制位有且仅有一个1e
338 counting bits
方法:count[i]=count[i&(i-1)]+1