当前位置: 首页 > news >正文

leetcode46.全排列

一、题目描述

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

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

二、题目解析

1、回溯法
采用试错思想,每次从剩余元素中取一个没取过的元素,
形成一个排列后,逐层向上回溯,寻找下一个排列。
在这里插入图片描述
递归的终止条件是: 一个排列中的数字已经选够了 ,因此我们需要一个变量来表示当前程序递归到第几层,我们把这个变量叫做 depth,或者命名为 index ,表示当前要确定的是某个全排列中下标为 index 的那个数是多少;
布尔数组 used,初始化的时候都为 false 表示这些数还没有被选择,当我们选定一个数的时候,就将这个数组的相应位置设置为 true ,这样在考虑下一个位置的时候,就能够以 O(1) 的时间复杂度判断这个数是否被选择过,这是一种「以空间换时间」的思想。

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;public class Solution {public List<List<Integer>> permute(int[] nums) {int len = nums.length;// 使用一个动态数组保存所有可能的全排列List<List<Integer>> res = new ArrayList<>();if (len == 0) {return res;}boolean[] used = new boolean[len];Deque<Integer> path = new ArrayDeque<>(len);dfs(nums, len, 0, path, used, res);return res;}private void dfs(int[] nums, int len, int depth,Deque<Integer> path, boolean[] used,List<List<Integer>> res) {if (depth == len) {//这里不可直接添加path,java中path是值传递,是列表对象的地址,递归过程中始终指向的是同一块内存。path指向的对象在不断地增加和删除元素,深度优先遍历完成后回到了根结点,成为空列表,故最终res的结果是5个空列表。//我们需要的是快照,所以这里需要拷贝一下res.add(new ArrayList<>(path));return;}//// 在非叶子结点处,产生不同的分支,这一操作的语义是:在还未选择的数中依次选择一个元素作为下一个位置的元素,这显然得通过一个循环实现。for (int i = 0; i < len; i++) {if (!used[i]) {path.addLast(nums[i]);used[i] = true;System.out.println("  递归之前 => " + path);dfs(nums, len, depth + 1, path, used, res);//// 注意:下面这两行代码发生 「回溯」used[i] = false;path.removeLast();System.out.println("递归之后 => " + path);}}}
}

时间复杂度:O(n×n!),其中 n 为序列的长度。
(我们需要将当前答案使用 O(n) 的时间复制到答案数组中,相乘得时间复杂度为 O(n×n!))
空间复杂度:O(n),其中 n 为序列的长度。除答案数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(n)。

在这里插入图片描述
2、插入法
从第一个元素开始,每加入一个元素,穷举新加入的元素能出现的所有位置,一直加到最后一个元素。

以 {1, 2, 3} 为例,先取1,全排列为{1},2进来后,全排列变为 {{1,2},{2,1}},3进来后,全排列变为{{3,1,2},{1,3,2},{1,2,3}, {3,2,1}, {2,3,1}, {2,1,3}},这就是最后的结果,这样应该更好理解一些(后一次的全排列就是让新元素在原来的全排列的每个排列中的所有位置都出现一次)。

class Solution {public List<List<Integer>> permute(int[] nums) {List<List<Integer>> result = new ArrayList<>();List<Integer> list = new ArrayList<>();list.add(nums[0]);result.add(list);//初始排列中仅包含nums[0], 然后挨个将nums中的其它元素加入排列for (int i = 1; i < nums.length; i++) {permute(result, nums[i]);}return result;}private void permute(List<List<Integer>> result/*当前元素个数下的所有排列*/, int newNum/*新加入的元素*/) {int n = result.size();//算一下当前有多少种排列,然后从后往前删除for (int i = n - 1; i >= 0; i--) {List<Integer> subList = result.get(i);int subSize = subList.size();//将新元素挨个插入当前排列的 0~len ,就是新元素在当前排列种可能出现的所有位置,然后将这些新的排列也加入result,作为下一新元素进来时的基础while (subSize >= 0) {List<Integer> newList = new ArrayList<>(subList);newList.add(subSize--, newNum);result.add(newList);}result.remove(i);}}
}

在这里插入图片描述

http://www.dtcms.com/a/362205.html

相关文章:

  • java web 练习 简单增删改查,多选删除,包含完整的sql文件demo。生成简单验证码前端是jsp
  • (Mysql)MVCC、Redo Log 与 Undo Log
  • C#知识学习-012(修饰符)
  • Python OpenCV图像处理与深度学习:Python OpenCV边缘检测入门
  • FastLED库完全指南:打造炫酷LED灯光效果
  • 【Excel】将一个单元格内​​的多行文本,​​拆分成多个单元格,每个单元格一行​​
  • 【设计模式】--重点知识点总结
  • C++ Bellman-Ford算法
  • Linux并发与竞争实验
  • 软件使用教程(四):Jupyter Notebook 终极使用指南
  • 数据分析编程第八步:文本处理
  • 设计模式-状态模式 Java
  • 华清远见25072班I/O学习day2
  • PostgreSQL备份指南:逻辑与物理备份详解
  • 椭圆曲线群运算与困难问题
  • 【数据分享】多份土地利用矢量shp数据分享-澳门
  • AI产品经理面试宝典第81天:RAG系统架构演进与面试核心要点解析
  • Qt中的信号与槽机制的主要优点
  • 自动化测试时,chrome浏览器启动后闪退的问题
  • 【趣味阅读】Python 文件头的秘密:从编码声明到 Shebang
  • VisionProC#联合编程相机实战开发
  • 【云存储桶安全】怎么满足业务需求,又最大程度上满足信息安全要求呢?
  • 1792. 最大平均通过率
  • 学习:uniapp全栈微信小程序vue3后台-暂时停更
  • 本地没有公网ip?用cloudflare部署内网穿透服务器,随时随地用自定义域名访问自己应用端口资源
  • 液态神经网络:智能制造的新引擎
  • 【跨境电商】上中下游解释,以宠物行业为例
  • 洛谷 c++ P1177 【模板】排序 题解
  • AutoSar RTE介绍
  • 特征增强方法【特征构建】