1. 程式人生 > >回溯,遞迴,歸併演算法及使用場景

回溯,遞迴,歸併演算法及使用場景

一、求子集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、結果展示