zoukankan      html  css  js  c++  java
  • 【LeetCode-回溯】全排列

    题目描述

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

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

    题目链接: https://leetcode-cn.com/problems/permutations/

    思路

    使用回溯来做。回溯也是在模拟人做排列的方法。例如做[1,2,3]的排列:

    1. 第一步有3个数[1,2,3]可以选,先选1,此时结果为[1];
    2. 接下来还有两个数[2,3]可以选,先放2,此时结果为[1,2];
    3. 接下来还有一个数[3]可以选,选3,此时结果为[1,2,3],结果的长度等于数字的个数,说明找到了一个排列。
    4. 回到第2步,有两个数[2,3]可以选,这一次选3,结果为[1,3];
    5. 接下来还有一个数[2]可选,选2,结果为[1,3,2],结果的长度等于数字的个数,说明找到了一个排列。
    6. 以1开头的排列已经搜索结束;回到第1步,有3个数可以选,这一次选[2],结果为[2];
    7. 使用类似[1]开头的步骤即可(步骤2到步骤5).

    上面的过程可以画成一棵树:

    该图来自于这篇文章。回溯就是从树的深层返回到浅层的过程,通过返回浅层,我们可以尝试不同的结果。在做排列的过程中,比如从[1,2,3]中添加1到排列中,那在下一步中1就不能选了,所以还需要一个visit数组来标记一个数字是否已经被添加到了排列当中。代码如下:

    class Solution {
    public:
        vector<vector<int>> ans;
        vector<vector<int>> permute(vector<int>& nums) {
            if(nums.empty()) return {{}};
    
            vector<int> track;
            vector<int> visit(nums.size(), 0);
            dfs(nums, 0, track, visit);
            return ans;
        }
    
        void dfs(vector<int> nums, int start, vector<int> track, vector<int> visit){
            if(track.size()==nums.size()){
                ans.push_back(track);
                return;
            }
    
            for(int i=0; i<nums.size(); i++){
                if(!visit[i]){
                    track.push_back(nums[i]);
                    visit[i] = 1;
                    dfs(nums, i, track, visit);
                    track.pop_back();
                    visit[i] = 0;
                }
            }
        }
    };
    

    其实上面的代码的dfs函数是不需要start参数的,因为每次都是从数组头开始迭代,这样写是为了和子集这题的写法保持一致,也便于比较。去掉start的写法:

    class Solution {
    public:
        vector<vector<int>> ans;
        vector<vector<int>> permute(vector<int>& nums) {
            if(nums.empty()) return {{}};
    
            vector<int> track;
            vector<int> visit(nums.size(), 0);
            dfs(nums, track, visit);
            return ans;
        }
    
        void dfs(vector<int> nums, vector<int> track, vector<int> visit){
            if(track.size()==nums.size()){
                ans.push_back(track);
                return;
            }
    
            for(int i=0; i<nums.size(); i++){
                if(!visit[i]){
                    track.push_back(nums[i]);
                    visit[i] = 1;
                    dfs(nums, track, visit);
                    track.pop_back();
                    visit[i] = 0;
                }
            }
        }
    };
    

    总结

    子集这一题和求全排列的这题很像,也是使用回溯,但也有区别。把这两题做了,想透了,我觉得应该能对回溯有更清晰的理解。

  • 相关阅读:
    基于webpack的react脚手架
    关于密码的简单加密
    移动端日期控件
    JS中如何巧妙的用事件委托
    JS中关于正则的巧妙操作
    call,apply,bind
    vue常用笔记
    高性能的js第三方库——lodash、 Underscore、async、md5及moment
    Nightwatch——自动化测试(端对端e2e)
    for循环的耗时问题
  • 原文地址:https://www.cnblogs.com/flix/p/12771867.html
Copyright © 2011-2022 走看看