LeetCode题解——46. 全排列

题目相关

题目链接

LeetCode中国,https://leetcode-cn.com/problems/permutations/。注意需要登录。

题目描述

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

题目分析

LeetCode 给出本题难度中等。

题意分析

根据给出序列,列出全排列。

样例数据分析

根据样例输入数据 [1,2,3] ,我去,这么简单,不就是回溯算法的经典例题吗。给出 1 ~ n 的全排列。图我就不画了。

搜索函数设计

设计搜索回溯函数可以套用套路。

1、先将函数原型写出来。比如 void dfs()。

2、根据题目要求逐一分析需要几个参数。我们用本题为例。

3、nums 这个数组肯定是要传递的。

4、既然是搜索,肯定需要位置控制。那么这个也是一个数组定义,假设名字为 vis。

5、肯定需要一个变量,表示已经搜索到了几个,假设名字为 cnt。

搜索返回条件

很清晰的知道,这个返回条件就是 cnt 的数据和 nums 数组大小一样。

第一次提交代码

class Solution {
public:
    vector<vector<int> > ans;//答案
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> path;        
        //从0开始搜索,搜索到数据n,一共搜索n个数字
        int n = nums.size();
        vector<bool> vis(n, false);
        dfs(path, vis, n, n);

        return ans;
    }

    //参数1:path存放已经搜索出了几个数据
    //参数2:vis表示
    //参数3:n表示搜索的最大数据
    //参数4:left还剩下几个数没有搜索到
    void dfs(vector<int> &path, vector<bool> &vis, int n, int left) {
        if (0==left) {
            //搜索到了
            ans.push_back(path);
            return;
        }
 
        for (int i=1; i<=n; i++) {
            if (false==vis[i]) {
                vis[i]=true;
                path.push_back(i);
                dfs(path, vis, n, left-1);
                path.pop_back();
                vis[i]=false;
            }
        }
    }
};

然后很开心地等待测试,结果如下,当头一棒。

嗯,大佬你坑我吗。竟然出现 0。于是开始改代码,不就是在数组 nums 里搞鬼,这个我会解决,大不了我先排序。这样我就知道最大值和最小值,然后从最小值到最大值搜索就就可以。

第二次提交代码

class Solution {
public:
    vector<vector<int> > ans;//答案
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> path;        
        //从0开始搜索,搜索到数据n,一共搜索n个数字
        int n = nums.size();
        sort(nums.begin(), nums.end());//排序
        vector<bool> vis(n, false);
        dfs(path, nums, vis, 0);

        return ans;
    }

    //参数1:path存放已经搜索出了几个数据
    //参数2:vis表示
    //参数3:开始
    //参数4:结束
    //参数4:cnt表示找到了几个
    void dfs(vector<int> &path, vector<int>& nums, vector<bool> &vis, int cnt) {
        int n=nums.size();
        if (n==cnt) {
            //搜索到了
            ans.push_back(path);
            return;
        }
 
        for (int i=nums[0]; i<=nums[n-1]; i++) {
            if (false==vis[i]) {
                vis[i]=true;
                path.push_back(i);
                dfs(path, nums, vis, cnt+1);
                path.pop_back();
                vis[i]=false;
            }
        }
    }
};

然后很开心地等待测试,结果如下,懵逼了。什么情况,竟然访问到了 0x0000 地址去了,I 服了 U。

耐心仔细的读了一下输出,看了一下测试用例。我去,这个数据出得太出乎我的意料了。错误原来如下:

输入 [0, -1, 1],排序后得到 [-1, 0, 1],我的搜索代码里循环是 for (int i=nums[0]; i<=nums[n-1]; i++),也就是从 -1 到 1 的循环,而对应的 vis[i],变成了 vis[-1]、vis[0] 和 vis[1]。恭喜出现了数组越界。天啊,这也太坑了。

还是自己没有推敲清楚。所以不怕给耻笑,将整个过程贴了出来。还是老问题,数据要仔细推敲。

既然问题定位了,要解决问题就很容易。只需要修改循环起点和终点就可以了。

AC 参考代码

class Solution {
public:
    vector<vector<int> > ans;//答案
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> path;        
        //从0开始搜索,搜索到数据n,一共搜索n个数字
        int n = nums.size();
        sort(nums.begin(), nums.end());//排序
        vector<bool> vis(n, false);
        dfs(path, nums, vis, 0);

        return ans;
    }

    //参数1:path存放已经搜索出了几个数据
    //参数2:nums表示给定的数据
    //参数3:vis访问控制
    //参数4:cnt表示找到了几个
    void dfs(vector<int> &path, vector<int>& nums, vector<bool> &vis, int cnt) {
        int n=nums.size();
        if (n==cnt) {
            //搜索到了
            ans.push_back(path);
            return;
        }
 
        for (int i=0; i<n; i++) {
            if (false==vis[i]) {
                vis[i]=true;
                path.push_back(nums[i]);
                dfs(path, nums, vis, cnt+1);
                path.pop_back();
                vis[i]=false;
            }
        }
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值