LeetCode 56,57,60,连刷三题不费劲
本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是LeetCode专题的第34篇文章,刚好接下来的题目比较简单,很多和之前的做法类似。所以我们今天出一个合集,一口气做完接下来的57、59和60这三题。
再次申明一下,为了节约篇幅,保证文章的质量,我跳过了LeetCode当中所有的Easy以及少量没什么营养的Medium和Hard的问题。Easy的问题都不是很难,即使是新手一般来说也不需要看题解,仔细想想也应该能搞定。所以就不占用篇幅了,如果大家有比较感兴趣的Easy问题,可以在下方的小程序处给我留言。
LeetCode 57 插入区间
第一题是57题Insert Interval,插入区间。题意会给定一组区间和一个单独的区间,要求将这个单独的区间插入区间集合,如果有两个区间存在交叉的情况,需要将它们合并,要求合并之后的最终结果。从题意上来看,基本上和我们上一篇文章讲的区间合并问题完全一样。唯一不同的是,在这题当中给定的这一组区间都是天然有序的,我们不需要对它进行排序。
区间已经有序了,剩下的就很简单了,我们只需要进行插入即可。区间插入的判断条件还是和之前一样,如果A区间的左端点在B区间左端点左侧,那么只要A区间的右侧端点在B区间左端点的右侧即可。所以这题基本上没有难度,就是一道裸题,我也不明白为什么官方给定的难度是Hard。
我们直接放出代码:
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
ret = []
# l, r记录待插入区间
l, r = newInterval
# 记录待插入区间是否完成插入
flag = False
for x, y in intervals:
# x, y记录当前区间
# 如果当前区间在待插入区间左侧,那么将当前区间插入答案
if y < l:
ret.append([x, y])
# 如果当前区间在待插入区间右侧,那么将两个区间都插入答案
elif r < x:
if not flag:
flag = True
ret.append([l, r])
ret.append([x, y])
# 否则,说明当前区间与待插入区间可以合并
# 更新待插入区间的范围
else:
l, r = min(l, x), max(r, y)
# 如果最后还没有完成插入,说明待插入区间大于所有区间
# 手动插入,防止遗漏
if not flag:
ret.append([l, r])
return ret
只要理解了区间合并的条件,这题真的没有难度。
LeetCode 59 螺旋矩阵II
前不久我们刚出过螺旋矩阵I的题解,在螺旋矩阵I当中,我们给定了一个矩阵让我们螺旋形去遍历它。这题则相反,给定我们矩阵的长和宽,让我们生成一个这样的螺旋矩阵。
我们来看下样例:
在这题当中,我们使用54题的思路也完全可以解出来,但是这题更加简单了一些。由于是让我们构造一个矩阵,那么我们其实没有必要维护每个方向的边界了。只要出现出界或者是遇到了已经填好的数字那么就说明应该转向了。某种程度上来说,这题应该是I,之前的54题应该是II,因为这题更简单一些。
如果对54题解法不熟悉的同学,可以点击下方的传送门,学习一下方向数组的使用方法。
传送门
由于我们不需要维护每个方向的边界,并且移动的步数是固定的,更重要的是,转向每次最多只会发生一次,所以这个流程非常简单,我们直接来看代码即可。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
# 初始化答案
ret = [[0 for _ in range(n)] for _ in range(n)]
# 初始化方向数组
fx = [[0, 1], [1, 0], [0, -1], [-1, 0]]
# 初始化方向以及起始点
dt = 0
x, y = 0, 0
# n*n的方阵,一共需要填入n*n个数字
for i in range(n*n):
ret[x][y] = i+1
# 移动的下一个位置
x_, y_ = x+fx[dt][0], y+fx[dt][1]
# 如果发生超界或者是遇到的数字大于0,说明需要转向
if x_ < 0 or x_ >= n or y_ < 0 or y_ >= n or ret[x_][y_] > 0:
dt = (dt + 1) % 4
# 转向之后的位置
x, y = x+fx[dt][0], y+fx[dt][1]
return ret
LeetCode 60 第K个排列
这题是一个排列组合问题,给定一个整数n,它代表[1,2,3,...,n]这n个元素的序列。然后还有一个整数K,要求这n个元素从小到大第K个排列。
这题其实蛮有意思,我觉得可以上hard,但遗憾的是它有一个讨巧的办法,大概也许是这样,所以才被降级成Medium的吧。这个讨巧的办法应该蛮容易想到的,很明显,由于n个数字是确定的,所以最小的排列一定是[1,2,3,...,n]。而我们之前做过一道LeetCode31题,它求的是给定一个排列,然后生成字典序比它大刚好一位的下一个排列。
既然如此,我们可以重复使用这个算法K-1次,就得到了答案了。对于做过31题的同学而言,这题毫无难度。如果对于31题解法不熟悉的同学可以点击下方传送门,回去复习一下。
传送门
但其实可以不用这么麻烦,因为Python当中有自带的排列组合的工具库,我们可以直接调用,只用5行代码就可以搞定。
class Solution:
# 引入包
from itertools import permutations
def getPermutation(self, n: int, k: int) -> str:
# 由于最后返回的结果要是string
# 生成['1','2','3',...,'n']的序列用来计算下一个排列
base = [str(i) for i in range(1, n+1)]
# 调用permutations会得到一个按顺序计算排列的生成器
perms = permutations(base, n)
for i in range(k):
# 我们调用k-1次next就能获得答案了
ret = next(perms)
return ''.join(ret)
这个方法虽然写起来短平快,但是也有一个重大的问题,就是耗时很长。这个其实很容易算,根据当前排列生成下一个排列的复杂度是\(O(n)\)。我们一共需要计算k-1次,所以整体的复杂度就是\(O(nk)\),极端情况下k=n!,所以最差复杂度是\(n \cdot n!\)。要知道n个物体的排列一共有n!种,如果k很大,这个复杂度是爆表的。不过这题没有过多为难人,这样的复杂度也能AC。我个人觉得这是一个很遗憾的事情,因为简单的算法也可以AC会导致很多人没有斗志再去研究复杂的算法了。
最后,我们来看正解。
正解其实不涉及任何新的算法和数据结构,甚至说穿了一文不值,但是很多人就是很难想到。还是老话题,它需要我们对问题进行深入的思考。
既然我们一个一个地求下一个排列非常慢,那么我们能不能有快速一点的办法呢?加快速度大概有两种办法,第一种增加步幅,比如之前的方法是每次获取字典序+1的排列, 我们能不能生成字典序+k的排列?第二种想法是我们能不能直接求解答案,直接生成出这个排列?
简单分析一下会发现第一种是不可行的,或者说是伪命题。因为如果说我们可以想出一个求解字典序+k的算法,那么我们令这个k等于题目中要求的k不就是直接求解答案了?所以说,如果可能存在更好的办法,一定只能是第二种,也就是直接构造答案的方法。那么问题就剩下了,我们怎么直接构造这个答案呢?这就需要我们对排列组合的理解了。
如果你亲手写过某个集合的全排列,你会发现一些规律。比如说123的全排列好了。我们都知道,它的全排列一共是123,132,213,231,31和321。这个很简单,我们观察一下会发现,123一共三个数字,6种排列。每个数字打头的都是2种,如果换成1234的排列呢?列一下就会知道是6种。如果你找规律你会发现,每个数字开头的种数是(n-1)!。
如果要推导也很简单,因为每个数字都是公平的,所以每个数字开头的种数都是一样的。而全排列一共有n!种,所以分摊到每个数字头上剩下(n-1)!种。那如果我们已经知道了第一个数字是1,请问第二个数字是2的种类数有多少种?同样的方法可以算出是(n-2)!种。到这里有没有火花闪过的感觉?
我们来举个例子吧,假设我们现在n=5,我们算一下会知道,一共有120种排列。假设我们要求第100个排列,由于从0开始,所以也就是第99大的排列。那么根据我们刚才说的,我们已经知道每个数字开头的情况都是4!也就是24种,那么我们用99/24得到4,所以开头的数字是第4大的数(第0大的是1),也就是5。答案一下子缩小了很多,那接下来呢?接下来我们用99减去5之前的所有情况,也就是96种,得到3。也就说答案是5开头的第3个排列,那么我再问,第二个数字是多少?
同样的办法,除去5之后还剩下4个数字,每个数字排第二都有3!也就是6种,我们用3/6=0,应该是第0大的数,也就是1。我们继续,除去5和1之后,还剩3个数字。每个数字排第三的情况有2种,我们用3/2=1,我们应该选择第1大的数,这里剩下的数是2,3,4,所以第三位应该是3。以此类推,我们可以得到每一位的数字。整体的复杂度是O(n),和上面的方法相比,有了质的突破。
我把整个计算过程做成了图,有看不懂的小伙伴可以结合一下下图理解:
最后,我们把这个方法实现即可:
class Solution:
def getPermutation(self, n: int, k: int) -> str:
frac = [1 for _ in range(n+1)]
# 生成一个序列存放所有的元素
nums = [i for i in range(1, n+1)]
# 计算每一位的种类数
for i in range(1, n):
frac[i] = i * frac[i-1]
ret = ''
k -= 1
for i in range(n-1, -1, -1):
# 计算第i位的数字是当前第几大的数
cur = k // frac[i]
# 放完之后取模,表示去除掉之前的所有情况数
k %= frac[i]
# 求出当前元素之后,需要从序列当中移除
ret += str(nums[cur])
nums.remove(nums[cur])
return ret
结尾
到这里三题就算是讲完了,今天的这三道题目或多或少的都和之前的问题有关,这也是我把这三题放在一篇文章当中阐述的原因。
这三题当中我个人最喜欢第三题,尤其是完美解法。它的整个思路和代码都不复杂,也没有什么特殊的技巧或者是方法,但是如果没有对题目有足够深入的了解是很难想到这个算法的。这其实也是算法题的精髓所在,比赛当中多的是知道解法也做不出来的题目。所以我们要提升算法水平,光学算法是不够的,也需要对题目有深入理解才行。
今天的文章就到这里,原创不易,需要你的一个关注,你的举手之劳对我来说很重要。
LeetCode 56,57,60,连刷三题不费劲的更多相关文章
- leetcode第三题
leetcode第三题: 题目: 给定一个字符串,找出不含有重复字符的最长子串的长度. 源码(使用java语言): class Solution { public int lengthOfLonges ...
- NOIP2005-普及组复赛-第三题-采药
题目描述 Description 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到一个到处都是草药的山 ...
- 看雪.TSRC 2017CTF秋季赛第三题
看雪.TSRC 2017CTF秋季赛第三题 wp 这是一道很简单的题,反调试的坑略多.这道题采用了很多常用的反调试手段,比如调用IsDebuggerPresent.进程名检查等等.另外也有利用SEH的 ...
- test20181018 B君的第三题
题意 B 君的第三题(shenyang) 题目描述 客似云来,万里无云 B 君得到了一个数组\(\{a_1,a_2,\dots,a_n\}\). B 君想通过修改让数组中个每对数都互质. 每次使一个数 ...
- LeetCode(57) Insert Interval
题目 Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if nece ...
- 【noip】跟着洛谷刷noip题2
noip好难呀. 上一个感觉有点长了,重开一个. 36.Vigenère 密码 粘个Openjudge上的代码 #include<cstdio> #include<iostream& ...
- shanquan2的两年三题系列
好像只有2个月就退役啦 不管了,先说一下哪三题:多点求值.lcm.替罪羊树(bzoj3065) [upd0]2016.3.29 多点求值A掉啦,myy卡常数sxbk(不是说好的是shanquan2出的 ...
- Java-集合-第三题 有如下Student 对象, private String name; private int age; private int score; private String classNum; 其中,classNum 表示学生的班号,例如“class05”。 有如下List List list = new ArrayList(); l
第三题 有如下Student 对象, private String name; private int age; private int score; private String classNum; ...
- NOIP 2008提高组第三题题解by rLq
啊啊啊啊啊啊今天已经星期三了吗 那么,来一波题解吧 本题地址http://www.luogu.org/problem/show?pid=1006 传纸条 题目描述 小渊和小轩是好朋友也是同班同学,他们 ...
随机推荐
- PHP中嵌入正则表达式常用的函数
PHP中嵌入正则表达式常用的函数有四个: 1.preg_match() :preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 . 语法:int preg_match( ...
- 2020-3-15 20175110王礼博 Exp2后门原理与实践
目录 1.使用netcat获取主机操作Shell,cron启动 2.使用socat获取主机操作Shell, 任务计划启动 3.使用MSF meterpreter(或其他软件)生成可执行文件,利用nca ...
- 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
结构体和方法 一.值,指针和引用 我们现在有一段程序: package main import "fmt" func main() { // a,b 是一个值 a := 5 b : ...
- threejs使用各种坑实验过程
第一次使用threejs到实际项目中,开始的时候心情有点小激动,毕竟是第一次嘛,然而做着做着就感受到这玩意水好深,满满的都是坑,填都填不过来.经过老板20天惨无人道的摧残,终于小有成就. 因为第一次搞 ...
- Python openpyxl使用操作和openpyxl操作
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取htt ...
- stand up meeting 12/25/2015 & weekend 12/26/2015~12/27/2015
part 组员 工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 在pdf阅读页面添加生词本显示:UI美化 6 完善显示 ...
- 国产操作系统深度deepin V20体验
1. 安装系统 国产操作系统deepin V20 bata版本已经发布.本人第一时间安装和体验.在犹豫很久之后,因为受到最新内核,高版本的bash和Python的诱惑,字体更加和谐等因素,选择升级系统 ...
- Suctf知识记录&&PHP代码审计,无字母数字webshell&&open_basedir绕过&&waf+idna+pythonssrf+nginx
Checkin .user.ini构成php后门利用,设置auto_prepend_file=01.jpg,自动在文件前包含了01.jpg,利用.user.ini和图片马实现文件包含+图片马的利用. ...
- [PHP] 获取IP 和JS获取IP和地址
通过js获取 服务器 ip 服务器端口 服务器地址 var address=window.location.href; thisDLoc = document.location; var hostpo ...
- 数值计算方法实验之Newton 多项式插值(MATLAB代码)
一.实验目的 在己知f(x),x∈[a,b]的表达式,但函数值不便计算或不知f(x),x∈[a,b]而又需要给出其在[a,b]上的值时,按插值原则f(xi)=yi (i=0,1,……, n)求出简单函 ...