力扣(LeetCode)46.全排列(C++/C语言)

回溯

2022/11/23 修改,补充C++代码

p a t h path path 保存递归路径, b o o l bool bool 数组记录路径上使用过的数, u u u 指向当前排列的位置,当 u = n u m s . s i z e ( ) u=nums.size() u=nums.size() 刚好组成一个排列,递归弹栈后,恢复现场,即回溯。

C++

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<vector<int>> permute(vector<int>& nums) {//回溯
        bool st[nums.size()];
        memset(st,0,sizeof st);
        dfs(nums,0,st);
        return ans;
    }
    void dfs(vector<int> &nums,int u,bool st[]){//u当前路径是第几个元素
        if(u==nums.size()){
            ans.push_back(path);
        }
        for(int i = 0;i<nums.size();i++){
            if(!st[i]){
                st[i]=true;
                path.push_back(nums[i]);
                dfs(nums,u+1,st);
                st[i]=false;//回溯
                path.pop_back();
            }
        }
    }
};

时间复杂度 O ( n ! × n ) O(n!\times n) O(n!×n) n u m s nums nums 的长度 n n n ,回溯次数是排列总数的常数倍,排列总数 n ! n! n! ,回溯的时间复杂度 O ( n ! ) O(n!) O(n!) p u s h _ b a c k push\_back push_back 的时间复杂度 O ( n ) O(n) O(n) ,二者相乘,总时间复杂度 O ( n ! × n ) O(n!\times n) O(n!×n)

空间复杂度 O ( n ) O(n) O(n) ,递归压栈的最大深度,和排列长度相等 O ( n ) O(n) O(n)

以下是原文。

2022/10/13 首次发布

  • 这道题是博主第一次尝试写的复杂递归题。
  • 分析题意,找一个数的全排列,按照人的思维逻辑就是确定一个首位,再确定第二位,依次往后,当排列到最后,我们可以回退一个元素,看有没有其他没被选择的排列元素。其实计算机和人的思路很相似,我们的dfs就是上面算法的一个补充。
  1. 为了确定当前排列是什么,我们建立一个path指针。为了确定哪些元素没有使用过,我们建立一个used指针。全局变量sz,代表所有排列的个数。答案数组ans,用于保存可能的所有排列。
  2. 看深度优先遍历dfs,传入题目给定的nums,numsSize,我们声明的path,use,ans,再用pos表示当前位置是排列的第几个元素。递归的结束条件,就是pos==numsSize,此时组成一个排列,保存这个排列,回退一个位置。
  3. 看下一个for循环,考虑递归体,一共递归了numsSize次。让nums数组的每一个元素,都当一次排列的首位。建立排列时,只使用排列里还没出现的元素,所以当false==used[i]时,当前元素才可以放入排列。
  4. 当我们把一个数放入排列,我们做如下操作
  • ①用path[pos]记录这个数,比如pos=3的位置,我们放了nums[3]这个数,依此类推。
  • ②我们将used[i]变为true,表明本次递归后,压栈的函数,不能出现当前元素,因为它被使用过。这样就避免了元素重复。
  • ③进入递归,递归调用时,pos的位置+1。
  1. 每一次函数弹栈后,排列往前一步,我们将弹栈前元素的used[i]变为false,之后递归建立排列可以使用该元素。

C

int sz;
void dfs(int *nums,int numsSize,int pos,int *path,bool *used,int **ans){
    if(pos == numsSize){//path列举了所有的数
        int *res = (int*)calloc(numsSize,sizeof(int));
        for(int i =0;i<numsSize;i++){
            res[i]=path[i];
        }
        ans[sz++]=res;
        return;
    }
    for(int i =0;i<numsSize;i++){//每一个数都可以作为首位,组成新的排列
        if(false==used[i]){//nums[i]没用过,才递归调用
            path[pos] = nums[i];
            used[i]=true;//下次递归前,标记当前i已使用
            dfs(nums,numsSize,pos+1,path,used,ans);
            used[i]=false;//弹栈后,标记当前i未使用
        }
    }
}
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    bool *used = (bool*)calloc(numsSize,sizeof(bool));//当前数字是否使用过
    int *path = (int*)calloc(numsSize,sizeof(int));//排列所经过的路径
    int **ans = (int**)calloc(720,sizeof(int*));//最多的排列可能性=6!
    sz = 0;
    dfs(nums,numsSize,0,path,used,ans);
    *returnSize = sz;
    *returnColumnSizes=(int*)calloc(sz,sizeof(int));
    for(int i = 0;i<sz;i++){
        returnColumnSizes[0][i]=numsSize;//每一行对应的元素个数是numsSize
    }
    return ans;
}
致语

理解思路很重要!
欢迎读者在评论区留言,答主看到就会回复的。

AC

AC
AC

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清墨韵染

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

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

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

打赏作者

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

抵扣说明:

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

余额充值