回溯,遞迴,歸併演算法及使用場景
一、求子集a
1、題目
已知一組數(其中無重複元素),求這組數可以組成的所有子集;
結果中不可有重複的子集
例如:
nums[] =[1,2,3]
結果為:【[], [1] , [1,2], [1,2,3], [1,3], [2] ,[2,3], [3] 】
解題思路:
利用棧(vector)來遞迴實現;
2、程式實現
#include<iostream>
#include<vector>
using namespace std;
class solution
{
public:
std::vector<std::vector<int >> subsets(std::vector<int>& nums)
{
std::vector<std::vector<int>> result;
std::vector<int> item;
result.push_back(item);
generate(0, nums, item,result);
return result;
}
private:
void generate(int i ,
std ::vector<int>&nums,
std::vector<int>&item,
std::vector<std::vector<int>>&result)
{
if(i >= nums.size())
{
return ;
}
item.push_back(nums[i]);
result.push_back(item);
generate( i+1 , nums, item, result);
item.pop_back();
generate( i+1 ,nums, item,result);
}
};
int main()
{
std::vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(3);
std::vector<std::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");
}
return 0;
}
3、結果展示
二、求子集b
1、題目
已知一組數(其中有重複元素),求這組數可以組成的所有子集,結果中無重複的子集。
如:nums[] = [2,1,2,2]
結果為:【[] , [1] , [1,2],[1,2,2], [1,2,2,2], [2],[2,2],[2,2,2]】
解題思路:有兩種重複原因:
(1)不同位置的元素組成的集合是同一個子集,順序相同
(2)不同位置的元素組成的集合是同一子集,順序不同。(集合是無序的)
將原陣列排序,再使用set去檢視;
[2,1,2,2]排序後[1,2,2,2],無論如何,都只會出現[1,2,2]的情況
2、程式實現
#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
class solution
{
public:std::vector<std::vector<int>> subsetWithDup(std::vector<int>& nums)
{
std::vector<int> item;
std::vector<std::vector<int>> result;
std::set<std::vector<int>> res_set;
std::sort(nums.begin(),nums.end());
result.push_back(item);
generate(0, nums, result, item, res_set);
return result;
}
private:
void generate(int i ,
std::vector<int>&nums,
std::vector<std::vector<int>> &result,
std::vector<int> &item,
std::set<std::vector<int>>& res_set)
{
if(i >= nums.size())
{
return ;
}
item.push_back(nums[i]);
if(res_set.find(item) == res_set.end())//在set中沒找到item,則插入
{
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);
}
};
int main()
{
std::vector<int> nums;
nums.push_back(2);
nums.push_back(1);
nums.push_back(2);
nums.push_back(2);
std::vector<std::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");
}
return 0;
}
3、結果展示
三、組合數之和
1、題目
已知一陣列(其中有相同元素),求這組數可以組成的所有子集中,
子集中的各個元素和為整數target的子集,結果中無重複子集
如:nums[] = [10,1,2,7,6,1,5], target = 8;
結果為:【[1,7],[1,2,5] , [2,6] ,[1,1,6]】
解題思路:
利用剪枝操作;在搜尋回溯過程中進行剪枝操作,遞迴呼叫時,計算已選擇元素的和sum;
若sum>target,就不再進行更深的搜尋,直接返回。
2、程式實現
#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
class solution
{
public:
std::vector<std::vector<int>> combinationsum(std::vector<int>& nums,int target)
{
std::vector<std::vector<int>> result;
std::vector<int> item;
std::set<std::vector<int>> res_set;
std::sort(nums.begin(),nums.end());
generate( 0 , nums, result, item , res_set, 0 , target);
return result;
}
private:
void generate(int i ,
std::vector<int>&nums,
std::vector<std::vector<int>>& result,
std::vector<int>& item,
std::set<std::vector<int>>res_set,
int sum,
int target
)
{
if(sum > target || i >= nums.size())
{
return ;
}
sum += nums[i];
item.push_back(nums[i]);
if(sum == target &&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()
{
std::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);
std::vector<std::vector<int>> result;
solution solve;
result = solve.combinationsum(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");
}
return 0;
}
3、結果展示
四、生成括號
1、題目
已知n組括號,開發一個程式,生成這n組括號所有的合法的組合可能;
解題思路:
n組括號,有2^2n種可能
例如:有一組括號:“((”,“))”,“()”,“)(”
2、程式實現
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class solution
{
public:
std::vector<std::string> generateParenthesis(int n)
{
std::vector<std::string> result;
generate("",n,n,result);
return result;
}
private:
void generate(std::string item , int left,int right,std::vector<std::string>&result)
{
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;
std::vector<std::string > result;
result = solve.generateParenthesis(4);
for(int i = 0;i <result.size(); i++)
{
printf("%s\n",result[i].c_str());
}
return 0;
}
3、結果展示
五、歸併排序
1、題目
已知兩個已排序的陣列,將這兩個已排序的數組合併為一個排序陣列
解題思路:
在兩個陣列中,分別設定一個指標,指向陣列的首元素,將兩個指標對應的元素相比較,符合條件的存放在總陣列中,迴圈上述步驟即可
2、程式實現
#include<iostream>
#include<vector>
using namespace std;
class solution
{
public:
void Merge_two_vec(std::vector<int>& vec1,
std::vector<int>& vec2,
std::vector<int>& vec)
{
int i = 0;
int j = 0;
while(i<vec1.size() && j<vec2.size())
{
if(vec1[i] < vec2[j])
{
vec.push_back(vec1[i]);
i++;
}
else
{
vec.push_back(vec2[j]);
j++;
}
}
for(; i<vec1.size();i++)
{
vec.push_back(vec1[i]);
}
for(;j<vec2.size();j++)
{
vec.push_back(vec2[j]);
}
}
};
int main()
{
int test[] = {1,3,5,7,9};
int test2[] = {2,4,6,8,10,13,15};
std::vector<int> vec1;
std::vector<int> vec2;
std::vector<int> vec;
for(int i = 0;i<5;i++)
{
vec1.push_back(test[i]);
}
for(int i = 0;i< 7 ; i++)
{
vec2.push_back(test2[i]);
}
solution solve;
solve.Merge_two_vec(vec1, vec2,vec);
for(int i = 0;i<vec.size();i++)
{
printf("[%d]",vec[i]);
}
printf("\n");
return 0;
}
3、結果展示