力扣47. 全排列 II

博客提到在每次for循环里,根节点只会分出不重复的节点,重复的节点会通过continue语句跳过,这涉及到节点去重的处理逻辑。
class Solution {
    boolean[] vis;

    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        List<Integer> perm = new ArrayList<Integer>();
        vis = new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums, ans, 0, perm);
        return ans;
    }

    public void backtrack(int[] nums, List<List<Integer>> ans, int idx, List<Integer> perm) {
        if (idx == nums.length) {
            ans.add(new ArrayList<Integer>(perm));
            return;
        }
        for (int i = 0; i < nums.length; ++i) {
            //我们选择对原数组排序,保证相同的数字都相邻,然后每次填入的数一定是这个数所在重复数集合中「从左往右第一个未被填过的数字
            // vis[i]判重, 若使用过该元素则跳过
            if(!vis[i]){
                if (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1]) {
                    continue;
                }
                perm.add(nums[i]);
                vis[i] = true;
                backtrack(nums, ans, idx + 1, perm);
                vis[i] = false;
                perm.remove(idx);
            }
        }
    }
}

每次for循环,根节点只会分出不重复的节点出来,重复的节点会被continue跳过
在这里插入图片描述

<think>嗯,用户想让我讲解力扣全排列的C语言代码。首先,我需要确认题目是力扣中的哪一道全排列题。全排列问题通常指LeetCode 46题,也就是给定一个不含重复数字的数组,返回所有可能的全排列。用户需要C语言的实现,但C语言在处理动态数组和返回结果时比较麻烦,我得回忆一下怎么处理。 首先,全排列的常用方法是回溯算法。回溯的基本思路是递归地尝试每一个位置的可能数字,通过交换元素来生成不同的排列。对于C语言来说,需要手动管理内存,尤其是结果数组和路径数组的分配。这可能会比较复杂,因为需要计算有多少种排列,然后分配足够的空间。 那具体的步骤应该是怎样的呢?首先确定问题规模,比如数组的长度n,全排列的数量是n!,所以结果数组的大小是n! * n。然后,每个排列需要存储n个元素。所以在C中需要动态分配一个二维数组来存储结果,同时需要一个一维数组来暂存当前的路径,或者通过交换原数组的元素来生成排列。 回溯函数需要参数:原数组、当前深度、数组大小、结果数组、返回的行数指针。每次递归到深度等于数组大小时,将当前排列存入结果。否则,从当前深度开始,依次交换元素,递归下去,再交换回来恢复状态。 不过,C语言处理二维数组和动态内存分配容易出错,需要特别注意。比如,使用指针的指针来存储结果,每个结果是一个int数组。同时,需要计算n!来确定需要分配多少行,每行n个int。这可能会涉及到计算阶乘的函数,或者通过回溯时自动计算数量。 另外,用户可能希望代码有详细的注释,解释每一步的作用。比如,swap函数的作用,回溯函数的递归终止条件,以及如何通过交换元素生成不同的排列。 可能遇到的问题包括内存分配错误,数组越界,或者没有正确复制当前排列到结果中。例如,在每次找到一种排列时,必须将当前数组的元素复制到结果中的一行,而不是直接引用,因为后续的交换会改变原数组的内容。 还需要考虑测试用例,比如输入为空的情况,或者数组只有一个元素的情况,确保代码的鲁棒性。 总结下来,步骤大致如下: 1. 实现交换元素的函数。 2. 编写回溯函数,递归生成排列。 3. 在每次递归到底层时,将当前排列保存到结果数组。 4. 主函数中计算n!,分配结果数组的内存,调用回溯函数,返回结果。 需要特别注意内存的分配和释放,避免内存泄漏。但因为力扣的题目通常只需要返回结果,可能不需要手动释放,不过实际编写时可能需要根据题目要求处理。 现在,把这些思路整理成结构化的讲解,分步骤说明,并附上代码示例。同时指出代码中的关键点,比如交换和回溯的过程,以及如何避免重复。对于C语言来说,因为没有内置的动态数组,必须预先计算好所需内存,这在代码中需要体现。</think>### 力扣全排列问题(LeetCode 46)C语言实现详解 #### 一、问题描述 给定一个 **没有重复数字** 的整数数组,返回其所有可能的全排列。例如: 输入:`[1,2,3]` 输出: `[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]` --- #### 二、核心思路:回溯算法 1. **递归交换**:通过交换数组中不同位置的元素,生成所有可能的排列。 2. **终止条件**:当递归深度等于数组长度时,保存当前排列。 3. **路径回退**:每次递归后恢复数组状态,避免影响后续分支。 --- #### 三、C语言代码实现 ```c #include <stdio.h> #include <stdlib.h> // 交换两个整数 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // 回溯函数 void backtrack(int* nums, int numsSize, int depth, int** res, int* returnSize) { if (depth == numsSize) { // 终止条件:生成一个完整排列 res[*returnSize] = (int*)malloc(numsSize * sizeof(int)); for (int i = 0; i < numsSize; i++) { res[*returnSize][i] = nums[i]; } (*returnSize)++; return; } // 从当前深度开始,尝试所有可能的交换 for (int i = depth; i < numsSize; i++) { swap(&nums[depth], &nums[i]); // 交换,固定当前深度的元素 backtrack(nums, numsSize, depth + 1, res, returnSize); swap(&nums[depth], &nums[i]); // 恢复,保证后续分支正确性 } } // 主函数 int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { // 计算全排列总数 n! int total = 1; for (int i = 1; i <= numsSize; i++) total *= i; *returnSize = 0; // 分配结果数组内存 int** res = (int**)malloc(total * sizeof(int*)); *returnColumnSizes = (int*)malloc(total * sizeof(int)); for (int i = 0; i < total; i++) { (*returnColumnSizes)[i] = numsSize; } // 回溯生成排列 backtrack(nums, numsSize, 0, res, returnSize); return res; } ``` --- #### 四、代码关键点解析 1. **交换与回溯** - 通过 `swap` 函数修改数组元素顺序,逐步生成不同排列。 - 递归后必须恢复数组状态(再次 `swap`),否则会影响其他分支。 2. **内存管理** - 结果数组 `res` 需要预先分配 `n!` 行,每行存储一个排列。 - 每生成一个排列时,动态分配内存并复制当前数组内容。 3. **复杂度分析** - 时间复杂度:$O(n \times n!)$,共有 $n!$ 种排列,每次复制需 $O(n)$。 - 空间复杂度:$O(n \times n!)$,存储所有排列的结果。 --- #### 五、测试示例 ```c int main() { int nums[] = {1, 2, 3}; int numsSize = 3; int returnSize; int* returnColumnSizes; int** res = permute(nums, numsSize, &returnSize, &returnColumnSizes); for (int i = 0; i < returnSize; i++) { for (int j = 0; j < numsSize; j++) { printf("%d ", res[i][j]); } printf("\n"); free(res[i]); // 释放每行内存 } free(res); // 释放结果数组 free(returnColumnSizes); return 0; } ``` --- #### 六、总结 通过递归和交换实现回溯,是解决全排列问题的经典方法。C语言需要手动管理内存,需特别注意: 1. 结果数组的预分配和释放。 2. 每次递归后恢复数组状态。 3. 深拷贝当前排列到结果中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值