算法4-----综合训练(1)

一:找出所有子集的异或总和再求和

题目:给出一个整数数组,然后计算它的所有子集的异或和,最后返回其所有的异或总和

示例:

给一个整数数组nums[1,2,3],子集为[1],[2],[3],[1 2],[1 3],[1 2 3],[]。它们的子集异或后的值为:1,2,3,3,2,0,0,故而计算总和为11,进而最后结果为11

方法:

1:画出决策树(与之前求所有子集的决策树一致,不在这里展开,详情请看算法3)

2:算法原理

大致思路与求所有子集一致,全局变量不同变为:

int sum;(存放最后结果的总和)

int path;(存放每一条路径的异或和)

dfs函数设计与求子集的设计一致,无需设计递归出口

class Solution {
    int sum;//最后要求的总和
    int path;//记录每一个节点的异或
public:
    int subsetXORSum(vector<int>& nums) {
        dfs(nums, 0);
        return sum;
    }
    void dfs(vector<int>& nums, int pos)
    {
        sum += path;//将每个叶子的异或值加入到sum中
        //进行枚举,求子集,并且对其进行异或
        for (int i = pos; i < nums.size(); i++)
        {
            path ^= nums[i];//得到异或的值,符号不要弄错谢谢
            dfs(nums, i + 1);
            //回溯,再将path进行异或
            path ^= nums[i];
        }
    }
};

二:全排列2

题目:给出一个含有可重复数字的整数数组,返回其所有的排列

示例:

给出一个整数数组nums[1,1,2]。它所有的全排列为:[1,1,2],[1,2,1],[2,1,1]

方法:

1:画出决策树

(与之前无重复数的思路一致,不过重点在于剪枝)

2:算法原理

首先,由于存在重复数字的情况,将数组重新排列后再进行分析,可大大简化我们的思路

按照之前不含有重复数字的方法,我们可以得到所有的全排列,但是会存在重复,可以利用剪枝,减去多余的情况!

在进行思考时,要得到所有不重复的全排列,要注意维持两个情况:

1)同一个数字不可以被重复使用

(2)在同一层,不可以有相同的数字同时被选择(最开始进行排序,也是为了更好地达到这一目的)

全局变量:

int[][] ret;(存放所有的路径)

int[] path;(记录每一条的路径)

bool check[];(记录当前是否为被使用过的路径)

剪枝:

不合法路径:当前位置已被使用,或者当前数与前一个数相同并且前一个数未被使用并且i!=0

check[i] ==true||(i!=0&&nums[i] ==nums[i-1]&&check[i-1]==false)

合法路径:当前位置未被使用并且满足i==0,或者当前数不等于前一个数或者前一个数已被使用

check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true

递归出口

位置到了整数数组的长度

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    bool check[8];//这个数组要给出大小
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
     sort(nums.begin(),nums.end());
     dfs(nums,0);
     return ret;
    }

    void dfs(vector<int>&nums,int pos)
    {
        if(pos==nums.size())
        {
            ret.push_back(path);
            return;
        }
        //进行枚举
       for(int i = 0;i<nums.size();i++)
       {
        if(check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true))
        {
            path.push_back(nums[i]);
            check[i] = true;
            dfs(nums,pos+1);
            path.pop_back();
            check[i] = false;
        }
       }
    }
};

三:电话号码的字母组合

题目:给出一个只包含2~9的字符串,返回所有它能表示的字母组合,每一个字符有着各自的映射

2~“abc”,3~“def”,4~“ghi”,5~“jkl”,6~“mno”,7~“pqrs”,8~“tuv”,9~“wxyz”

示例:

给出字符串digits[2,3],表示出来的所有字符串为:“ad”,“ae”,“af”,"bd",“be”,“bf”,“cd”,“ce”,“cf”

方法:

1:画出决策树

2:算法原理

首先,要将字符串数组与要进行排序的字符串进行映射,得到它们之间的关系,可以用一个全局变量数组搞定

全局变量:

string harsh[10] = {“”,“”,“abc”,“def”,“ghi”,“jkl”,“mno”,“pqrs”,“tuv”,“wxyz”]

vector<string> ret;(最终存放的字符串数组)

string path;(记录的字符串)

dfs函数的设计:

将每个数字字符对应的字符串进行遍历,然后将其加到path的后面,进行递归即可

(本次由于是字符串,故而使用范围for进行遍历更加简便)

递归出口:

当位置等于digits的长度时,可以将path加到ret的后面,然后返回

class Solution {
    string harsh[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> ret;
    string path;
public:
    vector<string> letterCombinations(string digits) {
    if(digits.size()==0)  return ret;
    dfs(digits,0);
    return ret;
    }

    void dfs(const string& digits,int pos)
    {
        if(pos==digits.size())
        {
            ret.push_back(path);
            return;
        }
        for(auto ch:harsh[digits[pos]-'0'])
        {
            path.push_back(ch);
            dfs(digits,pos+1);
            path.pop_back();
        }
    }
};

四:括号生成

题目:数字n代表括号的对数,设计一个函数,使其可以返回所有可能且有效的括号组合

示例:

n = 3;可有{((()))},{()(())},{(())()},{(()())},{()()()}

方法:

有效的括号组合符合下列条件:

(1)左括号数量等于右括号数量

(2)从头开始的任意一个子串,左括号数量>右括号数量

1:画出决策树

2:算法原理

全局变量:

int left;(左括号的数量)

int right;(右括号的数量)

vector<string> ret;

string path;

dfs函数:

当左括号的数量大于n,不可以再添加左括号;

当右括号的数量大于左括号的数量,不可以再添加右括号

递归出口:

右括号的数量等于n

class Solution {
    int left,right,_n;
    vector<string> ret;
    string path;
public:
    vector<string> generateParenthesis(int n) {
      _n = n;
      dfs();
      return ret;
    }

    void dfs()
    {
        if(right==_n)
        {
            ret.push_back(path);
            return;
        }

        if(left<_n)
        {
            path.push_back('(');
            left++;
            dfs();
            path.pop_back();
            left--;
        }
        if(right<left)
        {
            path.push_back(')');
            right++;
            dfs();
            path.pop_back();
            right--;
        }
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值