LeetCode 题解:一顿操作猛如虎,一看击败百分五
大家好,我是吴师兄,今天分享一道和数学有关的算法题,题目不难,但不优化的话会出现 一顿操作猛如虎,一看击败百分五 的情况:)
题目描述
给出集合 [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"
来源:https://leetcode-cn.com/problems/permutation-sequence
题目解析
一道比较偏数学的题目。
题意是给定 1 ~ n 的数字(n <= 9),因为数字所在的位置不同,这些数字可以组成的整数也不同。最后让你求按照组成的整数大小排序,排在第 k 的整数是多少。
要求全排列的话,时间和空间复杂度都是 O(n!)
。
先看最直接的解法,通过暴力搜索,找出所有的全排列的情况,然后排序,直接就可以得出答案。
这么做的时间复杂度是 O(n!log(n!))
。
很显然的是,这么做时间上面不符合要求,需要进一步优化算法。
首先考虑的一个问题是,我们需不需要找出所有的组合情况?
如果要找出所有的情况,那么时间上肯定是没法提高的。这其实就涉及到一个暴力搜索的剪支的问题,就拿 n = 3 为例,按照元素大小,全排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
这里,如果说 k = 5,那么是不是可以说以 “1” 和 “2” 开始的情况我们就不需要考虑了?
我们只需要从 “3” 开始的序列开始找就可以了,这样子可以很大程度上节省搜索的成本。
那么问题来了,如何确定我们需要找的结果在不在以某个数字开头的区间内?
其实,每个数字开头的序列总个数都是一样的,比如上面的以 “1”,“2”,“3” 开头的序列个数都是 2 个,我们只需要逐个排除就行了。
如果 k = 5,因为 5 > 2,说明结果不会在以 “1” 开头的区间中。又因为 5 > 4,说明结果不会在以 “2” 开头的区间中。
但是 5 < 6 的,所以我们需要在开头是 “3” 的序列中继续找。确定了开头数字是 3 之后,我们可以把 3 排除,然后继续去用同样的方法确定第二个数字,第三个数字。。。
这样做下来,时间上面是可以大大提高的。至于具体的时间复杂度是多少,这和具体的输入有关。但是空间方面,我们并不需要 O(n!)
的空间,n 的空间(函数栈空间)就足够了。
参考代码
func getPermutation(n int, k int) string {
nums := []int{}
for i := 1; i <= n; i++ {
nums = append(nums, i)
}
return helper(nums, k)
}
func helper(nums []int, k int) string {
totalNums := 1
// 计算以每个数字开头的序列个数
for i := 1; i < len(nums); i++ {
totalNums *= i
}
for i := range nums {
// 以当前数字开头的话,前面的序列的总个数
sum := (i + 1) * totalNums
sub := k - sum
// 说明结果是以当前数字开头
// 去掉当前选中的数字,递归去确定接下来的数字
if sub <= 0 {
arr := []int{}
arr = append(arr, nums[:i]...)
arr = append(arr, nums[i+1:]...)
return strconv.Itoa(nums[i]) + helper(arr, k - i * totalNums)
}
}
return ""
}
- Spring Boot 2.0正式发布,升还是不升呢?
- Spring Cloud构建微服务架构:分布式服务跟踪(入门)
- Spring Cloud构建微服务架构:分布式服务跟踪(跟踪原理)
- Spring Cloud Gateway真的有那么差吗?
- Spring Cloud构建微服务架构:消息驱动的微服务(核心概念)【Dalston版】
- Golang语言社区--【基础知识】循环
- Spring Cloud构建微服务架构:消息驱动的微服务(消费组)【Dalston版】
- Spring Cloud构建微服务架构:消息驱动的微服务(消费分区)【Dalston版】
- Spring Boot中使用LDAP来统一管理用户信息
- 使用Swagger2Markup实现API文档的静态部署(一):AsciiDoc
- 使用Swagger2Markup实现API文档的静态部署(二):Markdown和Confluence
- Dubbo官方的Starter发布1.0.0测试版,与Spring Boot的结合将更加自然
- spring-boot-starter-swagger 1.2.0.RELEASE:新增分组配置功能
- 领域驱动设计
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python代码需要缩进吗
- 解决Python paramiko 模块远程执行ssh 命令 nohup 不生效的问题
- Python计算信息熵实例
- 详解python logging日志传输
- 将tf.batch_matmul替换成tf.matmul的实现
- Python正则表达式高级使用方法汇总
- CentOS7.0下安装PHP5.6.30服务的教程详解
- Laravel Validator自定义错误返回提示消息并在前端展示
- 完美解决keras 读取多个hdf5文件进行训练的问题
- keras:model.compile损失函数的用法
- PHP获取当前系统时间的方法小结
- PHP结合jquery ajax实现上传多张图片,并限制图片大小操作示例
- php微信公众号开发之微信企业付款给个人
- PHP中抽象类,接口功能、定义方法示例
- YII分模块加载路由的实现方法