17/100全排列 18/100电话号码的字母组合 19/100四数之和

2023-12-26 19:35:08

题目:17/100全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
在这里插入图片描述

题解:

回溯算法:维护走过的路径,当前可以做的选择列表,当触发结束条件时,将路径记入结果集 ,回溯函数(记录的路径,可以选择的路径) res是个全局的变量

class Solution {
public:
    void backtrack(vector<vector<int>> &res,vector<int>& nums,  vector<int> tarck)
    {
        //结束条件
        if(tarck.size() == nums.size())
        {
            res.push_back(tarck);//一个进去
        }
        //回溯的套路 遍历选择列表
        for(int i = 0; i<nums.size(); i++)
        {
            if(std::find(tarck.begin(), tarck.end(), nums[i]) != tarck.end()) //已经存在 跳过
                continue;
            //加入
            tarck.push_back(nums[i]);
            //进入下一层循环树
            backtrack(res, nums, tarck);
            tarck.pop_back();
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> tarck;//记录的路径
        vector<vector<int>> res;//保存的路径
        backtrack(res, nums, tarck);//nums选择的路径
        return res;
    }

};

18/100电话号码的字母组合

在这里插入图片描述

题解:

需要遍历所有解,所以还是回溯的做法
结束条件是到digits的最后一个,每一个字符完成就是一个track(保存的路径),注意这个遍历是要添加下一个字符对应数据中的数字,所以要注意digits中下标的更新,感觉做回溯就是不要想太多(也可能是我理解还没有太到位,不然就容易陷进去,递归的痛==)

class Solution {
public:
 unordered_map<char, string> phoneMap{
            {'2', "abc"},
            {'3', "def"},
            {'4', "ghi"},
            {'5', "jkl"},
            {'6', "mno"},
            {'7', "pqrs"},
            {'8', "tuv"},
            {'9', "wxyz"}
        };

    void backtrack(vector<string>& res,string tarck, string digits, int i)
    {
        //结束条件 digits遍历完
        if(i == digits.size())
        {
            res.push_back(tarck);
        }
        
        char digit = digits[i];
        string letters = phoneMap[digit];
        //遍历可以选择的路径
        for(auto letter : letters)
        {
            tarck.push_back(letter);//加
            backtrack(res, tarck, digits, i+1);//下一个决策树
            tarck.pop_back();//撤销
        }

    }

    vector<string> letterCombinations(string digits) {
        //回溯 寻找所有可行解
        vector<string> res;
        if(digits.empty())
            return res;
       
        string tarck;//记录的值
        // int i = 0;
        backtrack(res, tarck, digits, 0);
        return res;

    }
};

19/100四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

题解:

跟三数之和一样,首先避免重复那么需要先排序,遇到重复元素跳过;
利用双指针减少一次循环,所以思路就是前两个数循环,后两个数用双指针,双指针所指的数大了,右边左移,小了左边右移
参考题解可以加一些剪枝操作:1最前面几个已经大于target了 下一个2 最后几个数相加依然小于target 下一个

代码思路还是挺清晰的,但是要注意程序溢出的问题 intlong
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
         if (nums.size() < 4) {
            return res;
        }
        sort(nums.begin(), nums.end());
        int n = nums.size();
        for(int i = 0; i<nums.size()-3; i++)
        {
            if(i>0 && nums[i] == nums[i-1])//与三数之和一样 依然是和上一个比  相同则跳过
            {
                continue;
            }
            //一些剪枝操作
            if((long)nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target)
                continue;
            if((long)nums[i] + nums[n-3] + nums[n-1] + nums[n-2] < target)
                continue;
            
            //循环另一个数
            for(int j = i+1; j<n-2; j++)
            {
                if(j>i+1 && nums[j]==nums[j-1])
                {
                    continue;
                }
                //剪枝
                if((long)nums[i] + nums[j] + nums[j+1] + nums[j+2] > target)
                    break;
                if((long)nums[i] + nums[j] + nums[n-1] + nums[n-2] < target)
                    continue;
                int left = j+1; int right = n-1;
                while(left < right)
                {
                    long curSum = (long)nums[i] + nums[j] + nums[left] + nums[right];
                    if(curSum == target)
                    {
                        res.push_back({nums[i], nums[j], nums[left], nums[right]});
                        //当前组ok 开始下一组
                        //先把重复的元素去掉
                        while(left< right && nums[left]==nums[left+1])
                        {
                            left++;
                        }
                        left++;//下一个元素不一样了 到不一样的元素这儿
                        while(left<right && nums[right] == nums[right-1])
                        {
                            right--;
                        }
                        right--;//right肯定也要变 不变的话这三个数固定了 另一个数肯定是原来的left
                    }

                    //当前组不满足 那么根据当前和进行指针的移动
                    if(curSum>target)
                    {
                        right--;
                    }
                    else if(curSum<target)
                    {
                        left++;
                    }
                }
            }
        }
        return res;
    }
};

//[-2 -1 0 0 1 2] 排序有什么好处 如何去重
//"为了避免枚举到重复,需要保证每一重循环枚举到的元素 不小于 上一重循环枚举到的元素  且在同一重循环中不能多次枚举到相同的元素"?
//使用两重循环分别枚举前两个数,再使用双指针枚举剩下的两个数
//剪枝操作

文章来源:https://blog.csdn.net/qq_37299596/article/details/135099192
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。