Leetcode -- Permutations

本文深入分析了回溯算法在解决排列问题中的应用,详细介绍了使用回溯算法求解全排列的方法,并通过实例代码展示了具体实现过程。

https://oj.leetcode.com/problems/permutations/


Given a collection of numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].


public List<List<Integer>> permute(int[] num)


问题分析:这题其实可以视作一个图论题,如果把所有元素都当做一个节点,其实就是要找以任意两个节点作为起点和终点经过所有点的所有路径。找所有路径是DFS比较合适一点。下面就给出一个DFS的算法。可以认为num[0]是起点,num[num.length - 1]是终点。然后num[1..i]就是中间的过程节点来看待就好,当走到终点的时候就记录一条路径。

    public List<List<Integer>> permute(int[] num) {
        List<List<Integer>> res = new LinkedList<List<Integer>>();
        helper(res, 0, num);
        return res;
    }
    public void helper(List<List<Integer>> res, int curlevel, int[] num){
        if(curlevel == num.length){
            LinkedList<Integer> cur_res = new LinkedList<Integer>();
            for(int i : num)cur_res.add(i);
            res.add(cur_res);
        }
        else{
            for(int i = curlevel; i < num.length; i++){
                int tmp = num[curlevel];
                num[curlevel] = num[i];
                num[i] = tmp;
                helper(res, curlevel + 1, num);
                num[i] = num[curlevel];
                num[curlevel] = tmp;
            }
        }
    }

这个做法是有点取巧的,达到了零空间,在Programming Interview Exposed里面给出了一个用boolean[num.length]做缓存记录当前节点是否走过的做法。本质上和这个其实是一样的。
【3D应力敏感度分析拓扑优化】【基于p-范数全局应力衡量的3D敏感度分析】基于伴随方法的有限元分析和p-范数应力敏感度分析(Matlab代码实现)内容概要:本文档介绍了基于伴随方法的有限元分析与p-范数全局应力衡量的3D应力敏感度分析,并结合拓扑优化技术,提供了完整的Matlab代码实现方案。该方法通过有限元建模计算结构在载荷作用下的应力分布,采用p-范数对全局应力进行有效聚合,避免传统方法中应力约束过多的问题,进而利用伴随法高效求解设计变量对应力的敏感度,为结构优化提供关键梯度信息。整个流程涵盖了从有限元分析、应力评估到敏感度计算的核心环节,适用于复杂三维结构的轻量化与高强度设计。; 适合人群:具备有限元分析基础、拓扑优化背景及Matlab编程能力的研究生、科研人员与工程技术人员,尤其适合从事结构设计、力学仿真与多学科优化的相关从业者; 使用场景及目标:①用于实现高精度三维结构的应力约束拓扑优化;②帮助理解伴随法在敏感度分析中的应用原理与编程实现;③服务于科研复现、论文写作与工程项目中的结构性能提升需求; 阅读建议:建议读者结合有限元理论与优化算法知识,逐步调试Matlab代码,重点关注伴随方程的构建与p-范数的数值处理技巧,以深入掌握方法本质并实现个性化拓展。
你提供的代码是 **LeetCode 第46题:全排列(Permutations)** 的官方题解,使用 **回溯 + 交换法** 实现所有元素的全排列。该算法通过递归地交换数组中的元素,生成所有可能的排列组合。 --- ## ✅ 代码解析与完整 VS2022 可运行版本 以下是适合在 **Visual Studio 2022 (VS2022)** 中运行的完整代码版本,包含 `vector`、主函数和测试用例。 ```cpp #include <iostream> #include <vector> using namespace std; class Solution { public: void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len) { // 所有数都填完了 if (first == len) { res.emplace_back(output); return; } for (int i = first; i < len; ++i) { // 动态维护数组:交换当前元素和第一个未固定元素 swap(output[i], output[first]); // 继续递归填下一个数 backtrack(res, output, first + 1, len); // 撤销交换,恢复现场 swap(output[i], output[first]); } } vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; backtrack(res, nums, 0, (int)nums.size()); return res; } }; // 打印结果的辅助函数 void printResult(const vector<vector<int>>& result) { cout << "[\n"; for (const auto& permutation : result) { cout << " ["; for (size_t i = 0; i < permutation.size(); ++i) { cout << permutation[i]; if (i != permutation.size() - 1) cout << ", "; } cout << "]\n"; } cout << "]\n"; } // 主函数测试 int main() { Solution sol; vector<int> nums; // 测试用例 1 nums = {1, 2, 3}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n所有全排列为:\n"; printResult(sol.permute(nums)); cout << endl; // 测试用例 2 nums = {0, 1}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n所有全排列为:\n"; printResult(sol.permute(nums)); cout << endl; // 测试用例 3 nums = {1}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n所有全排列为:\n"; printResult(sol.permute(nums)); cout << endl; return 0; } ``` --- ## ✅ 示例输出 ``` 输入数组: 1 2 3 所有全排列为: [ [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 2, 1] [3, 1, 2] ] 输入数组: 0 1 所有全排列为: [ [0, 1] [1, 0] ] 输入数组: 1 所有全排列为: [ [1] ] ``` --- ## ✅ 算法逻辑详解 ### ✅ 问题背景 给定一个不含重复数字的数组 `nums`,返回其所有可能的全排列。 ### ✅ 解法思路:回溯 + 交换法 #### 步骤: 1. **递归终止条件**: - `first == nums.size()`:所有元素都已确定位置,将当前排列加入结果集 2. **递归过程**: - 从 `first` 到 `nums.size() - 1` 遍历所有可能的起始元素 - 将当前元素 `nums[i]` 与 `nums[first]` 交换,表示固定当前元素到第一个位置 - 递归调用 `backtrack(..., first + 1)` 处理下一个位置 - 回溯:交换回来,恢复原数组状态,以便下一次尝试 #### 优点: - 不需要额外空间保存已使用元素,直接在原数组上操作 - 时间复杂度低,适合中等规模输入 --- ## ✅ 时间与空间复杂度 | 类型 | 复杂度 | 说明 | |------|--------|------| | 时间复杂度 | O(n × n!) | 共 `n!` 个排列,每个排列需要 O(n) 的时间生成 | | 空间复杂度 | O(n) | 递归栈深度为 `n` | --- ## ✅ 常见问题排查(VS2022) 1. **编译错误** - 确保包含 `<vector>` 和 `<iostream>` - 使用 `using namespace std;` 或加上 `std::` 前缀 2. **运行时错误** - 注意空数组处理 - 检查 `swap()` 是否越界 3. **逻辑错误** - 确保回溯后恢复数组状态 - 确保递归终止条件正确 --- ## ✅ 对比其他解法 | 解法 | 时间复杂度 | 空间复杂度 | 特点 | |------|------------|------------|------| | 回溯 + 交换法(当前方法) | O(n × n!) | O(n) | 无需额外空间,高效 | | 回溯 + 标记数组 | O(n × n!) | O(n) | 更直观,但需要额外数组 | | STL `next_permutation` | O(n × n!) | O(n) | 利用库函数,适合快速实现 | | 递归生成法(分治) | O(n × n!) | O(n) | 逻辑清晰,但代码略复杂 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值