leetcode 61~70 学习经历
- 61. 旋转链表
- 62. 不同路径
- 63. 不同路径 II
- 64. 最小路径和
- 65. 有效数字
- 66. 加一
- 67. 二进制求和
- 68. 文本左右对齐
- 69. x 的平方根
- 70. 爬楼梯
- 小结
61. 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/rotate-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额,示例里,有个链表一共才三个长度,他旋转了4次。。。。嗯,恶意满满的提示,如果真要慢慢旋转得到结果,那么直接给个2**31次旋转。。。。嘿嘿
老办法,弄到数组里,然后根据k直接切断原来的链,并到新的位置上去
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head or not head.next:
return head
t = ListNode(0,head)
r = []
while head:
r.append(head)
head = head.next
k = k % len(r)
r[-1].next = t.next
t.next = r[-k]
r[-k-1].next = None
return t.next
62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右
- 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/unique-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
猛一看到这题,老顾以为是个组合题,就是拿不准怎么计算组合,试了两次,没弄明白应该用什么公式
然后看到示例里,3*7矩阵有28种路径,于是老顾就自己用手比划各个路径,比划着比划着,比划出一个想法
标注1的地方,都是只有一种办法到达的地方
然后,每一个其他位置可能到达的方法,是左边和上边到达的可能性的和,那么右下角的可能性是多少?
哦哦,难怪题目给了个这个图,用二维表格做啊
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[0 for _ in range(n + 1)] for _ in range(m)]
dp[0] = [1] * (n + 1)
for i in range(1,m):
for j in range(1,n + 1):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[m - 1][n]
啊。。。这个成绩。。。估计真有大佬用公式计算的?抄答案去!
# 以下内容抄自 leetcode 第62题 python 20ms 答案
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m >= n:
# Cm+n-2 n-1
fenzi,fenmu1,fenmu2 = 1,1,1
for i in range(2,m+n-1):
fenzi = i*fenzi
for i in range(2,m):
fenmu1 = i*fenmu1
for i in range(2,n):
fenmu2 = i*fenmu2
result = fenzi / (fenmu1 * fenmu2)
return int(result)
else:
# Cm+n-2 m-1
fenzi,fenmu1,fenmu2 = 1,1,1
for i in range(2,m+n-1):
fenzi = i*fenzi
for i in range(2,n):
fenmu1 = i*fenmu1
for i in range(2,m):
fenmu2 = i*fenmu2
result = fenzi / (fenmu1 * fenmu2)
return int(result)
还真有组合公式,c(m+n-2,max(m,n)-1)
嗯,按这个办法写一版看看
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
def P(n):
if n < 2:
return 1
return n * P(n - 1)
def C(m,n):
return P(m)/P(m-n)/P(n)
return int(C(m + n - 2,max(m,n) - 1))
咦,有用例计算错误了?
哦。。。。写错了一个函数
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
def P(n):
if n < 2:
return 1
return n * P(n - 1)
def C(m,n):
return P(m)/P(m-n)/P(n)
return int(C(m + n - 2,min(m,n) - 1)) # 这里取 m,n 里小的那个。。。
嗯,果然有组合公式,老顾就吃亏在没上过学了
63. 不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例 1:
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
示例 2:
输入:obstacleGrid = [[0,1],[0,0]]
输出:1
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/unique-paths-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
哦呦,这次总不能再用组合来计算了吧,这次总该有二维表了吧
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
r = [[0 if col == 0 else -1 for col in row] for row in obstacleGrid]
n = len(r)
m = len(r[0])
for i in range(m):
if r[0][i] == -1:
break
r[0][i] = 1
for i in range(n):
if r[i][0] == -1:
break
r[i][0] = 1
for i in range(1,n):
for j in range(1,m):
if r[i][j] != -1:
r[i][j] = max(0,r[i - 1][j]) + max(0,r[i][j - 1])
return max(0,r[n - 1][m - 1])
思路一样简单,有障碍的地方标记为-1,然后求和的时候-1当0计算就好
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
还好只能向右或向下移动,否则就成了迷宫题了,那难度。。。哎,已经被坑掉过一回了
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
r = []
m,n = len(grid),len(grid[0])
def path(x,y,v):
v += grid[x][y]
if x < m - 1:
path(x + 1,y,v)
if y < n - 1:
path(x,y + 1,v)
if x == m - 1 and y == n - 1:
r.append(v)
path(0,0,0)
return min(r)
嗯,思路没问题,暴力穷举。。。。然后超时了。。。。
过了一段时间,突然想起来,前两题都用二维数组完成的,这次呢?为什么不用数组完成,每一个位置都和前两个位置中,小一点的相加不就是了?
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n = len(grid),len(grid[0])
dp = [[0 for _ in range(n)] for _ in range(m)]
dp[0][0] = grid[0][0]
for i in range(1,n): # 第一行只有一种办法到达,所以,每个格子的值是前边所有数字的和加自己
dp[0][i] = dp[0][i - 1] + grid[0][i]
for i in range(1,m): # 第一列只有一种办法到达,所以,每个格子的值是前边所有数字的和加自己
dp[i][0] = dp[i - 1][0] + grid[i][0]
for i in range(1,m): # 每个格子都有上边和左边两个位置可以进入,选其中小的作为入口,加自己
for j in range(1,n):
dp[i][j] = grid[i][j] + min(dp[i - 1][j],dp[i][j - 1])
return dp[-1][-1]
这里用了一个新数组,这次不用新数组看看
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n = len(grid),len(grid[0])
for i in range(1,n):
grid[0][i] += grid[0][i - 1]
for i in range(1,m):
grid[i][0] += grid[i - 1][0]
for i in range(1,m):
for j in range(1,n):
grid[i][j] += min(grid[i - 1][j],grid[i][j - 1])
return grid[-1][-1]
内存消耗降低了一点点。。。提升不大
65. 有效数字
有效数字(按顺序)可以分成以下几个部分:
一个 小数 或者 整数
(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个 整数小数(按顺序)可以分成以下几个部分:
(可选)一个符号字符(‘+’ 或 ‘-’)
下述格式之一:至少一位数字,后面跟着一个点 ‘.’
至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字
一个点 ‘.’ ,后面跟着至少一位数字整数(按顺序)可以分成以下几个部分:
(可选)一个符号字符(‘+’ 或 ‘-’)
至少一位数字部分有效数字列举如下:[“2”, “0089”, “-0.1”, “+3.14”, “4.”, “-.9”, “2e10”, “-90E3”, “3e+7”, “+6e-1”, “53.5e93”, “-123.456e789”]
部分无效数字列举如下:[“abc”, “1a”, “1e”, “e3”, “99e2.5”, “–6”, “-+3”, “95a54e53”]
给你一个字符串 s ,如果 s 是一个 有效数字 ,请返回 true 。
示例 1:
输入:s = “0”
输出:true
示例 2:
输入:s = “e”
输出:false
示例 3:
输入:s = “.”
输出:false
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/valid-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额。。。。正则能用不?
class Solution:
def isNumber(self, s: str) -> bool:
if re.match('^(?=.*\d)(?=[\d\.eE+-]+$)[+-]?(?=[^eE]*\d)\d*(\.\d*)?([eE][+-]?\d+)?$',s):
return True
return False
然后到 c ++ 里试了试这个正则。。。超时了,查了查资料,没有找到关于 c++ 正则断言的部分,也不知道是不是因为这个
使用正则就简单了,组织一下规则即可,如果用代码实现,那就一大片了。。。。
class Solution:
def isNumber(self, s: str) -> bool:
c = set(s.lower())
d = {'0','1','2','3','4','5','6','7','8','9'}
if len(c - {'+','-','.','e','E'}.union(d)) > 0: # 正则 (?=[\d\.eE+-]+$) 部分
return False
if len(c & d) == 0: # 正则 (?=.*\d) 部分
return False
fh,fd = 0,0 # 符号,浮点数量
if s[0] in {'+','-'}: # [+-]?
fh = 1
s = s[1:]
pos = 0
while pos < len(s): # (?=[^eE]*\d)\d*(\.\d*)?
if s[pos] in {'e','E'}: # 碰到 e 跳出
break
if s[pos] == '.':
fd += 1
if fd > 1: # 如果浮点数量过多
return False
if s[pos] in {'+','-'}: # 如果再次出现符号
return False
pos += 1
if pos - fd == 0: # 如果长度减去浮点数量为0,表示无数字
return False
if pos < len(s): # ([eE][+-]?\d+)?
s = s[pos + 1:]
if len(s) == 0: # e 后边无内容
return False
if s[0] in {'+','-'}: # 符号移除
s = s[1:]
if len(set(s) & d) == 0 or len(set(s) - d) > 0: # 如果无数字,或有数字外的内容
return False
return True
#if re.match('^(?=.*\d)(?=[\d\.eE+-]+$)[+-]?(?=[^eE]*\d)\d*(\.\d*)?([eE][+-]?\d+)?$',s):
# return True
#return False
不用正则实现了一遍,行吧,成绩能接受,不过这实现过程,真不如直接写正则快,正则我就一两分钟就搞定的事。。。。
看了看这个题的题解。。。。什么是状态机?我不造啊!什么是表驱动法?我不懂啊!仔细看了看,大概理解一点
根据录入的数字,给一个状态码,根据状态码进入到相应的行,再拿一个字符,得到这行相应的状态码,如果 -1 就不合法,其他的数字都是分支。。。。大概。行吧,也是个路子
66. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:
输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:
输入:digits = [0]
输出:[1]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/plus-one
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额,你就当这个数组是个正常的数字,比如 [4,3,2,1],你就当他是 4321,然后加1,然后再放成数组,最多考个进位,连续进位,及最后一次进位后跳出
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
n = len(digits)
digits[-1] += 1
for i in range(n):
if digits[n - i - 1] > 9:
digits[n - i - 1] = 0
if i < n - 1:
digits[n - i - 2] += 1
else:
return [1] +digits
else:
break
return digits
67. 二进制求和
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。
示例 1:
输入:a = “11”, b = “1”
输出:“100”
示例 2:
输入:a = “1010”, b = “1011”
输出:“10101”
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/add-binary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
e…这个有啥好说的,注意进位就好,再有就是注意补位
class Solution:
def addBinary(self, a: str, b: str) -> str:
n1,n2 = len(a),len(b)
s = ''
c3 = '0' # 进位记录
for i in range(max(n1,n2)):
c1 = a[-i - 1] if i < n1 else '0' # a 位
c2 = b[-i - 1] if i < n2 else '0' # b 位
print(c1,c2,c3)
if c1 == c2 == '0':
s = c3 + s
c3 = '0'
elif c1 != c2:
if c3 == '0':
s = '1' + s
else:
s = '0' + s
c3 = '1'
else:
s = c3 + s
c3 = '1'
if c3 == '1':
s = c3 + s
return s
我预估。。。有浑水摸鱼的家伙,所以我直接开始作弊
class Solution:
def addBinary(self, a: str, b: str) -> str:
return str(bin(int(a,2)+int(b,2)))[2:]
68. 文本左右对齐
给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。
你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ’ ’ 填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
注意:
单词是指由非空格字符组成的字符序列。
每个单词的长度大于 0,小于等于 maxWidth。
输入单词数组 words 至少包含一个单词。
示例 1:
输入: words = [“This”, “is”, “an”, “example”, “of”, “text”, “justification.”], maxWidth = 16
输出:[ "This is an", "example of text", "justification. " ]
示例 2:
输入:words = [“What”,“must”,“be”,“acknowledgment”,“shall”,“be”], maxWidth = 16
输出:[ "What must be", "acknowledgment ", "shall be " ]
解释: 注意最后一行的格式应为 "shall be " 而不是 “shall be”,
因为最后一行应为左对齐,而不是左右两端对齐。
第二行同样为左对齐,这是因为这行只包含一个单词。
示例 3:
输入:words = [“Science”,“is”,“what”,“we”,“understand”,“well”,“enough”,“to”,“explain”,“to”,“a”,“computer.”,“Art”,“is”,“everything”,“else”,“we”,“do”],maxWidth = 20
输出:[ "Science is what we", "understand well", "enough to explain to", "a computer. Art is", "everything else we", "do " ]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/text-justification
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
很简单的题目,结果提交失败三次,没注意审题啊,重点部分我加粗了。。。ε=(´ο`*)))唉
class Solution:
def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
r = []
g = []
def addSpace(arr,w):
if len(arr) == 1:
return arr[0] + ' ' * (w - len(arr[0]))
s = w - sum([len(n) for n in arr])
b = s // (len(arr) - 1)
f = s - b * (len(arr) - 1)
print(s,b,f)
return (' ' * b).join([arr[n] if n >= f else arr[n] + ' ' for n in range(len(arr))])
for w in words:
if len(g) == 0:
g.append(w)
continue
l = sum([len(n) + 1 for n in g])
if l + len(w) > maxWidth:
r.append(addSpace(g,maxWidth))
g = [w]
else:
g.append(w)
g = ' '.join(g)
r.append(g + ' ' * (maxWidth - len(g)))
return r
调整一下计算顺序
class Solution:
def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
r = []
g = []
l = 0
for w in words:
wl = len(w)
if len(g) == 0:
g.append(w)
l += wl + 1
continue
if l + wl > maxWidth:
if len(g) == 1:
r.append(g[0] + ' ' * (maxWidth - l + 1))
else:
(maxWidth - wl + 1) % (len(g) - 1)
r.append((' ' * ((maxWidth - l + len(g)) // (len(g) - 1))).join([g[n] + ' ' if n < (maxWidth - l + len(g)) % (len(g) - 1) else g[n] for n in range(len(g))]))
g = [w]
l = wl + 1
else:
g.append(w)
l += wl + 1
r.append(' '.join(g) + ' ' * (maxWidth - l + 1))
return r
哦吼,进入头部了,看了看其他20ms的答案,不能说一模一样,只能说没啥哈别
69. x 的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/sqrtx
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
哦,找小于等于自己平方根的最大整数,明显又是二分查找了,就看最后的跳出条件了
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0:
return 0
if x < 4:
return 1
l,r = 2,x // 2
while l < r:
m = (l + r) // 2
if l == m:
return r if r * r <= x else l
if m * m == x:
return m
elif m * m > x:
r = m - 1
else:
l = m
return l
这道题是用手机在地铁上码出来的,真费劲,leetcode手机版本不知道有什么问题,有时候不点提交,他也一直蹦最后一次的结果,还不好关掉,偶尔会重置代码,所有代码全都白费
话说,手机截图这么大的么?
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/climbing-stairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
又一个考组合公式的,那就来吧
。。。。几分钟过去了,自己差点把自己绕晕了。。。公式差点就没拼出来
class Solution:
def climbStairs(self, n: int) -> int:
def P(n):
if n < 2:
return 1
r = 2
for i in range(3,n+1):
r *= i
return r
def C(m,n):
return P(m)/P(m - n)/P(n)
if n in [0,1,2]:
return n
s = 0
for i in range(n // 2 + 1):
s += C(n - i,n - i * 2)
return int(s)
其实很容易理解的
1 ,只有1种 C(1,0)
2,只有 1 1,2 这两种,分别是 C(2,0) + C(1,0),两个1相加
3,C(3,0)+C(2,1) ,1+2=3,C(3,0)表示走了3步,C(2,1)表示走了两步,其中一步走的是一阶
4,C(4,0)+C(3,2)+C(2,0)
5,C(5,0)+C(4,3)+C(3,1) 加起来就是8种
C(5,0) 11111 1种
C(4,3) 1112 4种
1121
1211
2111
C(3,1) 122 3种
212
221
小结
这次10题难度偏低啊,除了最小路径和稍微脑抽卡了一下,其他的感觉都很顺手就完成了,嗯文本对齐的那个要耐心点,没好好读题弄错了好几次
有效数字那个评价是困难。。。。嗯,怎么说呢,c++或c可能是,其他语言未必了
总的来说,还是评价有点名不符实