力扣46题、47题(求排列、递归、回溯)

46.给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

  1. 求排列问题跟求组合问题和分割子串问题最大的不同在于,求组合问题和分割子串问题都需要定义一个整型变量startIndex去控制对数组和字符串的遍历范围(求不同集合之间的组合除外),而求排列问题不需要设置startIndex去控制对数组的遍历范围,每次遍历都需要从数组头开始遍历,即就算之前的数被使用过,后面仍然需要被使用。举例来说,就是元素1在获得排列[1,2]的过程中已经使用过,但是后面再获得排列[2,1]的使用仍然要使用到元素1;而如果是求组合问题,元素1在获得组合[1,2]的时候用过,后面在寻找以2开头的组合的时候,1元素就不会再被使用。
  2. 由于每次从数组中选取元素的过程都是从数组头开始,但是数组中的每个元素在一个排列中只能使用一次,因此这里定义了一个used数组,来记录每个元素是否被使用过,被使用过的元素就不能被再次使用了。
  3. 代码如下:
/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */

void backtracking(int* nums, int numsSize, int** res, int* returnSize, int* path, int pathSize, bool* used) {
    if (pathSize == numsSize) { //找到一个排列
        res[*returnSize] = malloc(sizeof(int) * pathSize);
        for (int i = 0; i < pathSize; i++) {
            res[*returnSize][i] = path[i];
        }
        (*returnSize)++;
        return;
    }
    for (int i = 0; i < numsSize; i++) { //每次都从数组头开始遍历
        if (used[i] == true) //如果当前元素被使用过,则跳过这次循环
            continue;
        used[i] = true;
        path[pathSize++] = nums[i];
        backtracking(nums, numsSize, res, returnSize, path, pathSize, used);
        //回溯
        pathSize--;
        used[i] = false;
    }
}

int factorial(int num) { //求阶乘
    if (num == 1 || num == 0)
        return 1;
    else
        return num * factorial(num - 1);
}


int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;
    int** res = malloc(sizeof(int *) * factorial(numsSize));
    int* path = malloc(sizeof(int) * numsSize); //暂存组合元素
    *returnColumnSizes = malloc(sizeof(int) * factorial(numsSize));
    bool* used = malloc(sizeof(bool) * numsSize); //用于记录数组中的元素是否被使用过
    for (int i = 0; i < numsSize; i++) {
        used[i] = false;
    }
    backtracking(nums, numsSize, res, returnSize, path, 0, used);
    for (int i = 0; i < *returnSize; i++) {
        (*returnColumnSizes)[i] = numsSize;
    }
    return res;
}

47.给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

  1. 47题跟46题的区别在于47题给定的序列是包含重复数字的,那么如果按上面的代码来求排列的话就可能会出现重复的组合,所以这里要去重。这里的去重手段跟40题求组合时的去重手段是一样的,将序列排序,让相同的数都排在相邻的位置,然后定义一个布尔类型的数组used来记录各个元素的使用情况。这里的used数组有两个作用,第一个作用是记录当前取出来的元素是否使用过,因为求排列时序列中的每个元素只能被使用一次,并且每次遍历都是从数组头开始遍历的,因此可能当前取出来的元素是已经使用过的,如果取出来的元素是使用过的,则跳过当前次的循环;第二个作用就是去重了,如果当前取出来的元素nums[i]和上一次取出来的元素nums[i-1]是一样的,并且上一次取出来的元素的下标在used数组中对应的元素为false的话,即used[i - 1] = false,则说明是在横向遍历的时候出现了重复元素,这种情况就是我们要去重的情况,则跳过当前次循环。
/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */

void backtracking(int* nums, int numsSize, int** res, int* returnSize, int* path, int pathSize, bool* used) {
    if (pathSize == numsSize) { //找到一个排列
        res[*returnSize] = malloc(sizeof(int) * pathSize);
        for (int i = 0; i < pathSize; i++) {
            res[*returnSize][i] = path[i];
        }
        (*returnSize)++;
        return;
    }
    for (int i = 0; i < numsSize; i++) {
        if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) //横向出现重复的元素,要去重
            continue;
        if (used[i] == true) //控制序列中每个元素在每个排列中只使用一次
            continue;
        used[i] = true;
        path[pathSize++] = nums[i];
        backtracking(nums, numsSize, res, returnSize, path, pathSize, used);
        // 回溯
        pathSize--;
        used[i] = false;
    }
    
}

//比较函数,作为qsort函数的参数
int cmp_int(const void* _a , const void* _b) {
    int* a = (int*)_a;    //强制类型转换
    int* b = (int*)_b;
    return *a - *b;
}

int factorial(int num) { //求阶乘
    if (num == 1 || num == 0)
        return 1;
    else
        return num * factorial(num - 1);
}

int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;
    int** res = malloc(sizeof(int*) * factorial(numsSize)); // 结果最多有numsSize!个
    int* path = malloc(sizeof(int) * numsSize);
    bool* used = malloc(sizeof(bool) * numsSize); // 记录每个元素的使用情况,用于控制序列中每个元素在每个排列中只使用一次,还用于去重
    for (int i = 0; i < numsSize; i++) {
        used[i] = false;
    }
    qsort(nums, numsSize, sizeof(int), cmp_int); //将给定序列排序
    backtracking(nums, numsSize, res, returnSize, path, 0, used);
    *returnColumnSizes = malloc(sizeof(int) * (*returnSize));
    for (int i = 0; i < *returnSize; i++) {
        (*returnColumnSizes)[i] = numsSize;
    }
    return res;
}
  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FF_y

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值