Leetcode 全排列专题(更新ing)
总览
涉及到的题目有
| 题号 | 名字 | 难度 |
|---|---|---|
| Leetcode 60 | 第k个排列 | 中等 |
| Leetcode 46 | 全排列 | 中等 |
待更新。。。。。。
Leetcode 46 全排列
题目
基础题
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路
很简单就能想到是回溯,这个题也确实是回溯。
但是有两个问题需要注意。
第一,如果要保证数字不重复,那需要一个类似数组的变量去存储这些数字有没有被使用,这是个非常直观的想法。但是这个想法可能会导致严重的错误。因为序列里可能会有负数,而普通的vector<bool>类型是无法索引负数的。所以一旦确定是有负数的,那就要用map<int, bool>来做了。
第二,额外的变量来标记固然简单且直观,但是也会造成额外的开销。所以有一种优化方式,就是把原来的nums分割成两段,比如\([0]\sim [first-1]\)和\([first] \sim [n-1]\),左边代表已经固定的,而右边代表待选的元素。这样通过交换来完成选择的过程就可以了。回撤操作只需要再次交换回来即可。
但是第二种做法有一个严重的问题,就是最后的结果不满足字典序,如果想保持字典序,就必须考虑其他的方式。
首先是第一种方法的代码,使用额外的空间去记录元素是否被访问,如下:
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
if(!nums.size()){
return res_;
}
vector<int> temp_res;
// used不能用vector,因为值可能会是负数
map<int, bool> used;
backTrace(nums, temp_res, used);
return res_;
}
private:
vector<vector<int>> res_;
void backTrace(vector<int>& nums, vector<int> temp_res, map<int, bool>& used){
if(temp_res.size() == nums.size()){
res_.push_back(temp_res);
return;
}
for(auto num : nums){
if(!used[num]){
used[num] = true;
temp_res.push_back(num);
backTrace(nums, temp_res, used);
temp_res.pop_back();
used[num] = false;
}
}
}
};
这种方法非常慢,因为反复赋值非常耗费资源。


可以看到上面的方法仅能超过10%,肯定不能作为最终解。
下面给出的代码是利用第二点优化之后的代码,如下:
// 交换法
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
int first=0;
backTrack(nums, first);
return res_;
}
private:
vector<vector<int>> res_;
void backTrack(vector<int>& nums, int first){
int n = nums.size();
if(first == n){
res_.push_back(nums);
return;
}
for(int i = first; i<n; i++){
swap(nums[first], nums[i]);
backTrack(nums, first+1);
swap(nums[first], nums[i]);
}
}
};
结果:

Leetcode 60 第k个排列
题目
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"
思路
这个题如果是按照48题的做法,直接生成所有,然后选出第k个,是会超时的。
正确的做法是利用数学去推,下面结合官方题解和部分博客的思路总结一下。
首先是如何去推算:

上图是官方题解前一部分的解释,下面举一个\(n = 4,k = 9\)的例子说明一下过程:
n=4时的全排列有:
1234
1243
1324
1342
1423
1432
2134
2143
2314(这里是目标值)
2341
2413
2431
3124
3142
3214
3241
3412
3421
4123
4132
4213
4231
4312
4321
为了后面遍历方便,这里首先让k--
第一个数字:
我们要取的是\(k/(n-1)! = 8/(4-1)! = 1\)(这里的1是下标为1的那个),即{1,2,3,4}中的2(这里下标是从0开始的)。
这时候,问题规模已经缩减了,也就是第一位已经固定,所选择的范围只剩{1,3,4}
2134
2143
2314(这里是目标值)
2341
2413
2431
问题规模缩小了,所以这里k也要变化,方法是\(k \% (n-1)! = 8\%(4-1)! = 8\%6 =2\)。
第二个数字:
接着利用上面计算第一个的公式继续计算第二个数字,注意这时候n需要减小为3了。
这次我们要取的是\(k/(n-1)! = 2/(3-1)! = 1\)(这里的1是下标为1的那个),也就是{1,3,4}中的3了。
这时候范围还剩:
2314(这里是目标值)
2341
k范围缩小,变成\(k \% (n-1)! = 2\%(3-1)! = 2\%2 =0\)。
n再次减小,变成了2。
第三个数字:
我们要取的是\(k/(n-1)! = 0/(2-1)! = 0\)(这里的0是下标为0的那个),也就是{1,4}中的1了。
第四个数字也就只剩下了4
这个过程想明白后,代码也就简单了:
这里参考了HuaHua酱的代码
// Author: Huahua
class Solution {
public:
string getPermutation(int n, int k) {
vector<int> num;
vector<int> fact(10, 1);
for (int i = 1; i <= 9; i++) {
num.push_back(i);
fact[i] = fact[i - 1] * i;
}
string s;
k--;
while (n--) {
int d = k / fact[n];
k %= fact[n];
s += ('0' + num[d]);
for (int i = d + 1; i <= 9; i++)
num[i - 1] = num[i];
}
return s;
}
};
Leetcode 全排列专题(更新ing)的更多相关文章
- LeetCode树专题
LeetCode树专题 98. 验证二叉搜索树 二叉搜索树,每个结点的值都有一个范围 /** * Definition for a binary tree node. * struct TreeNod ...
- 适合入门自学服装裁剪滴书(更新ing)
[♣]适合入门自学服装裁剪滴书(更新ing) [♣]适合入门自学服装裁剪滴书(更新ing) 适合入门自学服装裁剪滴书(更新ing) 来自: 裁缝阿普(不为良匠,便为良医.) 2014-04-06 23 ...
- LeetCode 字符串专题(一)
目录 LeetCode 字符串专题 <c++> \([5]\) Longest Palindromic Substring \([28]\) Implement strStr() [\(4 ...
- Coursera,Udacity,Edx 课程列表(更新ing)
Coursera,Udacity,Edx 课程列表(更新ing) Coursera有很多特别好的课程,平时没有机会听到国外大牛的课程,通过Coursera算是可以弥补一下吧,国外的课程普遍比国内的老师 ...
- storcli 命令(更新Ing)
help [root@centos7]# storcli -h Storage Command Line Tool Ver 007.0606.0000.0000 Mar , (c)Copyright ...
- LeetCode刷题(持续更新ing……)
准备刷题了!已经预见未来的日子是苦并快乐的了!虽然 N 年前刷过题,但现在感觉数据结构与算法的基本功快忘光了
- [LeetCode] “全排列”问题系列(二) - 基于全排列本身的问题,例题: Next Permutation , Permutation Sequence
一.开篇 既上一篇<交换法生成全排列及其应用> 后,这里讲的是基于全排列 (Permutation)本身的一些问题,包括:求下一个全排列(Next Permutation):求指定位置的全 ...
- [LeetCode] “全排列”问题系列(一) - 用交换元素法生成全排列及其应用,例题: Permutations I 和 II, N-Queens I 和 II,数独问题
一.开篇 Permutation,排列问题.这篇博文以几道LeetCode的题目和引用剑指offer上的一道例题入手,小谈一下这种类型题目的解法. 二.上手 最典型的permutation题目是这样的 ...
- SpringBoot布道系列 | 目录汇总 | 2019持续更新ing
SpringBoot 基础教程 | 三大推荐理由 1.文章内容均为原创,结合官方文档和实战经验编写. 2.文章结构经过细致整理,对新人学习更加友好. 3.精选常用技术,不求全面,但求精华!! Spri ...
随机推荐
- LinuX操作系统基础------> 用户管理,grep搜索
用户管理 用户组管理 字符管理和grep搜索 1.用户管理: LinuX是一个多用户,多任务的操作系统,可以让多个用户使用,这个账号可以帮助对用户进行追踪,必须在初始化的时候设置好自己的密码 1-1配 ...
- 《Python测试开发技术栈—巴哥职场进化记》—软件测试工程师“兵器库”
上文<Python测试开发技术栈-巴哥职场进化记>-初来乍到,请多关照 我们介绍了巴哥入职后见到了自己的导师华哥,第一次参加团队站会,认识了团队中的开发小哥哥和产品小姐姐以及吃到了公司的加 ...
- [深度学习] Pytorch学习(一)—— torch tensor
[深度学习] Pytorch学习(一)-- torch tensor 学习笔记 . 记录 分享 . 学习的代码环境:python3.6 torch1.3 vscode+jupyter扩展 #%% im ...
- C#LeetCode刷题-并查集
并查集篇 # 题名 刷题 通过率 难度 128 最长连续序列 39.3% 困难 130 被围绕的区域 30.5% 中等 200 岛屿的个数 38.4% 中等 547 朋友圈 45.1% ...
- Probabilistic PCA、Kernel PCA以及t-SNE
Probabilistic PCA 在之前的文章PCA与LDA介绍中介绍了PCA的基本原理,这一部分主要在此基础上进行扩展,在PCA中引入概率的元素,具体思路是对每个数据$\vec{x}_i$,假设$ ...
- STL函数库的应用第三弹——数据结构(栈)
Part 1:栈是什么 栈(stack)又名堆栈,它是一种运算受限的线性表.限定仅在表尾进行插入和删除操作的线性表. 这一端被称为栈顶,相对地,把另一端称为栈底. 向一个栈插入新元素又称作进栈.入栈或 ...
- 《Java从入门到失业》第二章:Java环境(二):JDK、JRE、JVM
2.2JDK.JRE.JVM 在JDK的安装目录中,我们发现有一个目录jre(其实如果是下一步下一步安装的,在和JDK安装目录同级目录下,还会有一个jre目录).初学Java的同学,有时候搞不清楚这3 ...
- 记写 android 微信登录的demo历程
前言 首先看一条链接: https://github.com/Tencent/WeDemo 腾讯给了一个wedemo,微信第三方登录的例子.里面是php和ios,ios是object写的,php还是原 ...
- 关于ACID,BASE和CAP定理的探究
前言 当我看到"根据CAP理论,由于分布式系统必须保证分区容错性,所以只能选择AP原则或者CP原则"这种结论时,我感到很疑惑: 什么是分区容错性? 为什么分布式系统必须保证分区容错 ...
- Trapdoors for Hard Lattices and New Cryptographic Constructions
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文. Abstract 我们展示了如何构造各种“trapdoor”密码工具,假设标准格问题的最 ...