leetcode算法题I提升篇
顺序整理leetcode热门算法题,通过实际的题目提升算法水平,加强思维能力
# 常见算法题
# 基础算法
# 求两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
- 可以直接暴力两个循环查找,时间复杂度为O(N2)
- 下面的代码主要是通过空间换时间的思想,利用hashmap来降低时间复杂度
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
tmp = dict()
for index, val in enumerate(nums):
other = target-val
if other in tmp:
return tmp[other], index
tmp[val] = index
return None
2
3
4
5
6
7
8
9
func twoSum(nums []int, target int) []int {
hashdict := map[int]int{}
for index, val := range nums {
if p, ok := hashdict[target-val]; ok {
return []int{p, index}
}
hashdict[val] = index
}
return nil
}
2
3
4
5
6
7
8
9
10
11
// Make sure to add code blocks to your code group
# 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/add-two-numbers 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
"""
由于题目告诉链表本来就是逆序存储的,所以不考虑反转
注意点
1. L1和L2 需要移动指针
2. 注意进位
3. 返回的值为头节点的next值
"""
val = tmp = 0
head= current = ListNode() #head为头节点 current为当前节点
while l1 or l2 or val:
if l1:
val += l1.val
l1 = l1.next
if l2:
val += l2.val
l2 = l2.next
tmp = val%10
val = val//10
current.next = ListNode(tmp)
# 移动当前指针到下一个节点
current = current.next
return head.next # head的指针没有移动过,head.next才是真正的结果
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
head := new(ListNode)
current :=head
tmp := 0 // tmp 表示进位数
for l1 != nil || l2 !=nil || tmp !=0 {
current.Next = new(ListNode)
current = current.Next
if l1 !=nil {
tmp = l1.Val + tmp
l1 = l1.Next
}
if l2 != nil{
tmp = l2.Val + tmp
l2 = l2.Next
}
current.Val = tmp%10
tmp = tmp/10
}
return head.Next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Make sure to add code blocks to your code group
# 无重复最长字串
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
1
2
3示例 2:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
1
2
3示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
1
2
3
4提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
左边界固定,滑动右边界
"""
right, ans = -1, 0
tmp_set = set()
for left in range(len(s)):
if left != 0:
# 说明已上一个元素开头的最长字串已经找到了,需要向左移动
# 结合下面的循环退出条件, 此处会删除元素,
# 直到将和s[right]相同的元素删除后才会进入下面的while循环
# 其实最终的目的就是控制左指针移动
tmp_set.remove(s[left-1])
while (right+1)<len(s) and s[right+1] not in tmp_set:
tmp_set.add(s[right +1 ])
right += 1
ans = max(ans, len(tmp_set))
return ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
"""
滑动窗口
右边界固定,滑动左边界
"""
if not s:return 0
if len(s) == 1: return 1
# max_len = cur_len = left = 0
# occ = set()
# for right in range(len(s)):
# cur_len += 1
# while s[right] in occ:
# occ.remove(s[left])
# left += 1
# cur_len -= 1
# max_len = max(max_len, cur_len)
# occ.add(s[right])
# return max_len
left, max_len = 0, 0
occ = set()
for right in range(len(s)):
# cur_len +=1
while s[right] in occ:
occ.remove(s[left])
left += 1
# cur_len -= 1
max_len = max(max_len, right-left+1)
occ.add(s[right])
return max_len
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func lengthOfLongestSubstring(s string) int {
right, ans := -1, 0
n := len(s)
hashmap := map[byte]int{}
for left:=0; left < n; left ++ {
if left != 0 {
delete(hashmap, s[left-1])
}
for right+1 <n && hashmap[s[right+1]] == 0{
// 这里注意,go的取hashmap时,如果值不存在获取该类型的默认值
// hashmap的作用时标记是否存在过,类似去重
// 所以只要不为0就表示出现过
// right ++
hashmap[s[right+1]] ++
right ++
}
ans = max(ans, right- left +1 )
}
return ans
}
func max(x int, y int)int{
if x < y{
return y
} else{
return x
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func lengthOfLongestSubstring(s string) int {
right, ans := -1, 0
n := len(s)
hashmap := map[byte]int{}
for left:=0; left < n; left ++ {
if left != 0 {
delete(hashmap, s[left-1])
}
for right+1 <n && hashmap[s[right+1]] == 0{
// 这里注意,go的取hashmap时,如果值不存在获取该类型的默认值
// hashmap的作用时标记是否存在过,类似去重
// 所以只要不为0就表示出现过
// right ++
hashmap[s[right+1]] ++
right ++
}
ans = max(ans, right- left +1 )
}
return ans
}
func max(x int, y int)int{
if x < y{
return y
} else{
return x
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Make sure to add code blocks to your code group
# 最长回文字串
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
dp[i][j]的定义
dp[i][j]的地推公式
d[i[j]]的初始化
遍历shunxu
打印dp数组
此题:
定义
dp[i][j]表示开始为i, 终点为j的字串是不是回文字串
---> 由定义可知i=[0, i<j](开始小于结尾),j [1, len(s-1)](由于0已经填满了),所以我们需要填充的是网格的对角线以下的部分
初始化:
当i=j时,也就是支取一个的时候,一定时回文子串,所以对角线为True
递推公式:
dp[i][j] = True ---> s[i] = s[j] (dp[i+1][j-1] = True or j-i<3)
{dp[i+1][j-1]这个字串的状态为True 或者j-i的长度<=3(此处如果长度为三,那么字串的左右两边相等的话,一定为回文串,同理,如果长度为1、2也一定为回文串,并且为1的情况已经初始化过了)}
"""
if len(s) <=1:
return s
n = len(s)
dp = [[False] * n for _ in range(n)]
start = 0
max_len = 1
for i in range(n):
dp[i][i] = True
for j in range(1, n):
for i in range(0, j):
if s[i] != s[j]:
continue
else:
if s[i] == s[j]:
if (j-i+1) <3 or dp[i+1][j-1] ==True:
dp[i][j] = True
if dp[i][j] and (j- i +1)> max_len:
start = i
max_len = j-i+1
return s[start: start+max_len]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func longestPalindrome(s string) string {
dp := make([][]bool, len(s))
max_len := 1
start :=0
if len(s) <2{
return s
}
for i:=0;i<len(s);i++{
dp[i] = make([]bool, len(s))
dp[i][i] = true
}
for j:=1;j<len(s);j++{
for i:=0;i<j;i++{
if s[i] != s[j]{
continue
}else{
if j-i+1<3 || dp[i+1][j-1] == true{
dp[i][j] = true
}
}
if dp[i][j] == true && j-i+1>max_len{
start = i
max_len = j-i+1
}
}
}
return s[start:start+max_len]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Make sure to add code blocks to your code group
# z字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N A P L S I I G Y I R 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
def solution(s, rows):
"""
模拟二维矩阵
矩阵如何初始化
如何模拟
向上取整通用做法 int((a+b -1) /b)
每个周期为 2r -2, 每个周期内的列数为r-1
"""
n, r = len(s), numRows
if r <2:
return s
t = 2* r-2
# 根据周期求列数 ,每个周期内的列数为r-1列
c = (r-1) * int((n +t -1)/t)
# 初始化数组
dp = [[""]*c for _ in range(r)]
x, y = 0, 0
for index_, ss in enumerate(s):
dp[x][y] = ss
if index_%t < r-1:
x+=1
else:
x -= 1
y += 1
return "".join([ch for list1 in dp for ch in list1 if ch !=""])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution(object):
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
在第0行或者地n-行换向换行后表示需要和第几行的元素相加
"""
if len(s) < numRows or numRows == 1:
return s
dp = [""] * numRows
tag = -1 # 正向
i = 0
for ch in s:
dp[i] += ch
if i == 0 or i == numRows-1:
tag = -tag
i += tag
return "".join(dp)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func convert(s string, numRows int) string {
n := numRows
if len(s) < n || n ==1 {
return s
}
sign := -1
i :=0
dp := make([]string, n)
for _, ch := range s {
dp[i] += string(ch)
if i ==0 || i == n-1{
sign = -sign
}
i += sign
}
return strings.Join(dp, "")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Make sure to add code blocks to your code group
# 数字反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−2<<31, 2<<31 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/reverse-integer 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def reverse(self, x: int) -> int:
"""
除10每次取证,直到被除数<10为止
边界溢出判断【(-2**30)//10, 2*30//10-1】
"""
# if x< -(2<<31) or x >(2<<31 - 1):
# return 0
y, res = abs(x), 0
while y !=0 :
if res < -(2<<30)/10 or res> (2<<30)/10:
return 0
res = res*10 + y%10
y //=10
return res if x>=0 else 0-res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func reverse(x int) (rev int) {
// rev := 0
for x != 0{
if rev <math.MinInt32/10 || rev > math.MaxInt32/10{
return 0
}
digit := x%10
rev = rev *10 + digit
x /= 10
}
return
}
2
3
4
5
6
7
8
9
10
11
12
// Make sure to add code blocks to your code group
# 字符串转换整数
请你来实现一个
myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的atoi
函数)。函数
myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。- 如果整数数超过 32 位有符号整数范围
[−231, 231 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−231
的整数应该被固定为−231
,大于231 − 1
的整数应该被固定为231 − 1
。- 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符
' '
。- 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例 1:
输入:s = "42" 输出:42 解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。 第 1 步:"42"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"42"(读入 "42") ^ 解析得到整数 42 。 由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。
1
2
3
4
5
6
7
8
9
10
11示例 2:
输入:s = " -42" 输出:-42 解释: 第 1 步:" -42"(读入前导空格,但忽视掉) ^ 第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数) ^ 第 3 步:" -42"(读入 "42") ^ 解析得到整数 -42 。 由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
1
2
3
4
5
6
7
8
9
10
11示例 3:
输入:s = "4193 with words" 输出:4193 解释: 第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止) ^ 解析得到整数 4193 。 由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
1
2
3
4
5
6
7
8
9
10
11提示:
0 <= s.length <= 200
s
由英文字母(大写和小写)、数字(0-9
)、' '
、'+'
、'-'
和'.'
组成
INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31
class Automaton:
def __init__(self):
self.state = 'start'
self.sign = 1
self.ans = 0
self.table = {
'start':['start', 'signed', 'in_number', 'end'],
'signed': ['end', 'end', 'in_number', 'end'],
'in_number':['end', 'end', 'in_number', 'end'],
'end': ['end', 'end', 'end', 'end']
}
def get_col(self, c):
if c.isspace():
return 0
if c== "+" or c=="-":
return 1
if c.isdigit():
return 2
return 3
def get(self, c):
self.state = self.table[self.state][self.get_col(c)]
if self.state == 'in_number':
self.ans = self.ans*10 + int(c)
self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
elif self.state == "signed":
self.sign=1 if c=="+" else -1
class Solution:
def myAtoi(self, s: str) -> int:
automaton = Automaton()
for c in s:
automaton.get(c)
return automaton.sign*automaton.ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Make sure to add code blocks to your code group
# 回文数
给你一个整数
x
,如果x
是一个回文整数,返回true
;否则,返回false
。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
- 例如,
121
是回文,而123
不是。示例 1:
输入:x = 121 输出:true
1
2示例 2:
输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
1
2
3示例 3:
输入:x = 10 输出:false 解释:从右向左读, 为 01 。因此它不是一个回文数。
1
2
3
class Solution:
def isPalindrome(self, x: int) -> bool:
if x<0:
return False
if x in [0, 9]:
return True
y = x
tmp = 0
while y != 0:
tmp = tmp*10 + y%10
y = y//10
if tmp == x:
return True
else:
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func isPalindrome(x int) bool {
if x<0 {
return false
}
y := x
tmp := 0
for x != 0 {
tmp = tmp *10 + x %10
x = x/10
}
if tmp == y {
return true
} else{
return false
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Make sure to add code blocks to your code group
# 盛最多水的容器
给定一个长度为
n
的整数数组height
。有n
条垂线,第i
条线的两个端点是(i, 0)
和(i, height[i])
。找出其中的两条线,使得它们与
x
轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
**说明:**你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
1
2
3示例 2:
输入:height = [1,1] 输出:1
1
2
解法思路
设两指针 iii , jjj ,指向的水槽板高度分别为 h[i]h[i]h[i] , h[j]h[j]h[j] ,此状态下水槽面积为 S(i,j)S(i, j)S(i,j) 。由于可容纳水的高度由两板中的 短板 决定,因此可得如下 面积公式 :
S(i,j)=min(h[i],h[j])×(j−i)
短板向中间收窄一格,都会导致水槽 底边宽度 −1-1−1 变短:
若向内 移动短板 ,水槽的短板 min(h[i],h[j])min(h[i], h[j])min(h[i],h[j]) 可能变大,因此下个水槽的面积 可能增大 。 若向内 移动长板 ,水槽的短板 min(h[i],h[j])min(h[i], h[j])min(h[i],h[j]) 不变或变小,因此下个水槽的面积 一定变小 。 因此,初始化双指针分列水槽左右两端,循环每轮将短板向内移动一格,并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积。
算法流程: 初始化: 双指针 iii , jjj 分列水槽左右两端; 循环收窄: 直至双指针相遇时跳出; 更新面积最大值 resresres ; 选定两板高度中的短板,向中间收窄一格; 返回值: 返回面积最大值 resresres 即可;
class Solution:
def maxArea(self, height: List[int]) -> int:
"""
双指针法
水容量:
v= (r-l+1)*min(il,i2)
盛水最多:max(v)
"""
max_w = 0
l = 0
r = len(height) -1
while l < r:
w = (r-l) * min(height[l], height[r])
if w>max_w:
max_w = w
if height[l] <height[r]: # 比较那一侧较小,小的一侧需要移动指针,
l += 1
else:
r -= 1
return max_w
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func maxArea(height []int) int {
max_w := 0
l := 0
r := len(height) -1
for ;l<r;{
v := (r-l) * min(height[l], height[r])
if max_w < v {
max_w = v
}
if height[l] < height[r] {
l += 1
} else{
r -= 1
}
}
return max_w
}
func min(x int, y int) int {
if x > y {
return y
} else {
return x
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
# 整数转罗马数组
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给你一个整数,将其转为罗马数字。
示例 1:
输入: num = 3 输出: "III" 示例 2:
输入: num = 4 输出: "IV"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/integer-to-roman 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def intToRoman(self, num: int) -> str:
list2 = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]
str1 = ""
for index_, (n, ch) in enumerate(list2):
while num >= n:
v = num // n
str1 += v*ch
num = num % n
return str1
2
3
4
5
6
7
8
9
10
11
12
func intToRoman(num int) string {
type data struct {
val int
char string
}
// 定义list1切片
list1 := []data{
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"},
}
ans := ""
for _, s := range list1{
for num >= s.val{
ans += s.char
num -= s.val
}
}
return ans
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Make sure to add code blocks to your code group
# 数字转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给定一个罗马数字,将其转换成整数。
class Solution:
def romanToInt(self, s: str) -> int:
list1 = [('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)]
num = 0
for ch, n in list1:
while s.startswith(ch):
num += n
s= s[len(ch):]
return num
2
3
4
5
6
7
8
9
10
func romanToInt(s string) int {
type data struct{
char string
vla int
}
list1 := []data{
{"M", 1000},
{"CM", 900},
{"D", 500 },
{"CD", 400},
{"C", 100 },
{"XC", 90 },
{"L", 50 },
{"XL", 40 },
{"X", 10 },
{"IX", 9 },
{"V", 5 },
{"IV", 4 },
{"I", 1 },
}
num := 0
for _, d := range list1{
for strings.HasPrefix(s, d.char){
num += d.vla
s=s[len(d.char):]
}
}
return num
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Make sure to add code blocks to your code group
# 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串
""
。示例 1:
输入:strs = ["flower","flow","flight"] 输出:"fl"
1
2示例 2:
输入:strs = ["dog","racecar","car"] 输出:"" 解释:输入不存在公共前缀。
1
2
3
class Solution(object):
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
if len(strs) == 1:
return strs[0]
max_len = len(strs[0])
for i in range(max_len):
c = strs[0][i]
for char in strs:
if len(char) <= i:
return char[0:i]
if char[i] != c:
return strs[0][0:i]
return strs[0][:]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
first_ch = strs[0]
j = 0
res = ""
while j < len(first_ch):
ch = first_ch[j]
tag = True
for s in strs:
if len(s)<= j or ch != s[j]:
tag = False
if tag:
res += ch
else:
break
j +=1
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
后续补充
// Make sure to add code blocks to your code group
# 三数之和
给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为
0
且不重复的三元组。**注意:**答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
1
2
3
4
5
6
7
8示例 2:
输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。
1
2
3示例 3:
输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。
1
2
3提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
排序+双指针+剪枝
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 排序, 防止重复
nums.sort()
n = len(nums)
tmp = list()
for i in range(n):
first = nums[i]
if i >0 and nums[i] == nums[i-1]:
continue
target = -first
# 前后双指针
left = i +1
right = n-1
while left < right:
# 如果有重复的则忽略
second = nums[left]
third = nums[right]
if second + third == target:
tmp.append([first, second, third])
#判断是否有相同的元素,
while left < right and nums[left] == nums[left +1]:
left +=1
while left < right and nums[right] == nums[right -1]:
right -= 1
if second +third > target:
right -= 1
continue
if second + third < target:
left += 1
continue
left += 1
right -= 1
return tmp
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func threeSum(nums []int) [][]int {
sort.Ints(nums)
n := len(nums)
tmp := make([][]int, 0)
for i:=0;i<n;i++{
// first元素去重
if i > 0 && nums[i] == nums[i-1]{
continue
}
first := nums[i]
target := -first
// 寻找第二个和第三个元素,和为target
left := i + 1
right := n -1
for left < right{
second := nums[left]
third := nums[right]
if second + third == target{
correct := []int{first, second, third}
tmp = append(tmp, correct)
// 如果复合条件时,检查后面的是否有重复的second,third元素
for left < right && nums[left] == nums[left+1]{
left += 1
}
for left < right && nums[right] == nums[right-1]{
right -= 1
}
}
if second + third < target{
left += 1
continue
}
if second + third > target{
right -= 1
continue
}
left += 1
right -= 1
}
}
return tmp
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Make sure to add code blocks to your code group
# 最接近的三数之和
给你一个长度为
n
的整数数组nums
和 一个目标值target
。请你从nums
中选出三个整数,使它们的和与target
最接近。返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1 输出:2 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
1
2
3示例 2:
输入:nums = [0,0,0], target = 1 输出:0
1
2提示:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
"""
由题目可知,只有一个解,不考虑重复,三数之和最接近
min([target-(first+second+third)])
"""
nums.sort()
n = len(nums)
min_num = 10001*5 # 只有一个解,最接近的三数之和
for i in range(n):
# if i > 0 and nums[i] == nums[i-1]:
# continue
first = nums[i]
left = i +1
right = n-1
while left < right:
second = nums[left]
third = nums[right]
tmp = first + second + third
if abs(target - tmp) < abs(target - min_num):
min_num = tmp
if target == tmp:
return tmp
if tmp < target:
left += 1
else:
right -= 1
return min_num
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func threeSumClosest(nums []int, target int) int {
min_total := int(math.Pow(10, 5))
n := len(nums)
sort.Ints(nums)
for i:=0;i<n;i++ {
//first = nums[i]
left, right := i+1, n-1
for left < right {
current_num := nums[i] + nums[left] + nums[right]
if current_num == target{
return current_num
}
if math.Abs(float64(target - current_num)) < math.Abs(float64(target - min_total)){
min_total = current_num
}
if current_num < target{
left += 1
} else{
right -= 1
}
}
}
return min_total
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
# 删除链表的倒数第n个节点
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:
输入:head = [1], n = 1 输出:[] 示例 3:
输入:head = [1,2], n = 1 输出:[1]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode()
dummy.next = head
# 删除倒数第n个节点,相当于删除顺数的第len-n+1个节点,解除节点就可以了
# 计算整个的长度
#@property
def nodelen(head):
length = 0
while head:
length += 1
head = head.next
return length
length = nodelen(head)
current = dummy
for i in range(length-n):
current = current.next
current.next = current.next.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
"""
先入栈,出栈的第n个就是要删除的
"""
dummy = ListNode()
dummy.next = head
current = dummy
stack = list()
current = dummy
while current:
stack.append(current)
current = current.next
for i in range(n):
ret = stack.pop()
prev = stack[-1]
prev.next = prev.next.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func nodeLength(head *ListNode) (length int) {
for ;head !=nil;head=head.Next{
length ++
}
return
}
func removeNthFromEnd(head *ListNode, n int) *ListNode {
length := nodeLength(head)
dummy := &ListNode{0, head}
cur := dummy
for i :=0; i<length-n;i++{
cur = cur.Next
}
cur.Next = cur.Next.Next
return dummy.Next
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
# 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"] 示例 2:
输入:n = 1 输出:["()"]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/generate-parentheses 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
"""
相当于组合问题
"""
ret = []
def backtracking(path,left, right):
if len(path) == 2*n :
ret.append("".join(path))
return
if left < n:
path.append("(")
backtracking(path, left +1, right)
path.pop()
if right < left:
path.append(")")
backtracking(path, left, right+1)
path.pop()
backtracking([], 0, 0)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func generateParenthesis(n int) []string {
// var path string
ret :=[]string{}
var backtracking func(string, int, int)
backtracking = func (path string, left , right int){
if len(path) == 2*n{
ret = append(ret, path)
return
}
if left < n{
// path = append(path, "(")
backtracking(path + "(", left+1, right) // 这里是隐式的回溯,由于传递的是path + "(", 所以其实path 这个变量的值是没有变的,所以省去了pop的操作
// path.pop()
}
if right < left{
backtracking(path + ")", left, right +1)
}
}
backtracking("", 0, 0)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()" 输出:true 示例 2:
输入:s = "()[]{}" 输出:true 示例 3:
输入:s = "(]" 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/valid-parentheses 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def isValid(self, s: str) -> bool:
dict1 = {
'(': ')',
'{': '}',
'[': ']'
}
stack = list()
for i in range(len(s)):
if s[i] in dict1:
# 左括号入栈
stack.append(s[i])
else:
# 右括号需要找对应的左括号
if len(stack) > 0 and dict1.get(stack.pop()) == s[i]:
continue
else:
return False
return not stack or False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
后续补充
// Make sure to add code blocks to your code group
# 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例 2:
输入:l1 = [], l2 = [] 输出:[] 示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/merge-two-sorted-lists 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode()
head = dummy
while list1 and list2:
if list1.val <= list2.val:
head.next = list1
list1 = list1.next
else:
head.next = list2
list2 = list2.next
head = head.next
if list1:
head.next = list1
print(list1, list2)
if list2:
head.next = list2
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
待补充
// Make sure to add code blocks to your code group
# 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入:head = [1,2,3,4] 输出:[2,1,4,3] 示例 2:
输入:head = [] 输出:[] 示例 3:
输入:head = [1] 输出:[1]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/swap-nodes-in-pairs 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0, head)
current = dummy
while current.next and current.next.next:
node1 = current.next
node2 = current.next.next
current.next= node2
node1.next = node2.next
node2.next = node1
current = node1
# current.next.next= node1
# node1.next = node2.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
后续补充
// Make sure to add code blocks to your code group
# 删除有序数组的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。 返回 k 。 判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组 int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length; for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i]; } 如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。 示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4] 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# [1, 1, 1, 2, 2, 3, 4, 5, 7, 7]
"""
快慢指针
"""
if len(nums) < 2:
return len(nums)
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
后续补充
// Make sure to add code blocks to your code group
# 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] 解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。 示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3] 解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-element 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
"""
快慢指针
"""
if len(nums) == 0:
return 0
if len(nums) == 1:
return 0 if nums[0] == val else 1
slow = 0
n = len(nums)
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
后续补充
// Make sure to add code blocks to your code group
# kmp算法
- 暴力法图解
- kmp算法图解
# 找出字符串中第一个匹配项的下标
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
示例 1:
输入:haystack = "sadbutsad", needle = "sad" 输出:0 解释:"sad" 在下标 0 和 6 处匹配。 第一个匹配项的下标是 0 ,所以返回 0 。 示例 2:
输入:haystack = "leetcode", needle = "leeto" 输出:-1 解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 获取next数组
def get_next(needle):
n = len(needle)
# 定义next数组
next = [0 for _ in range(n)]
# 初始化next数组
j = 0
next[0] = j
for i in range(1, n):
# 不匹配怎么版
while j > 0 and needle[i] != needle[j]:
j = next[j-1]
# 匹配怎么办
if needle[i] == needle[j]:
j += 1
next[i] = j
return next
next = get_next(needle)
n, m = len(haystack), len(needle)
j = 0
for i in range(n):
while j > 0 and haystack[i] != needle[j]:
j = next[j-1]
if haystack[i] == needle[j]:
j += 1
if j == m:
return i - j +1
return -1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func strStr(haystack string, needle string) int {
// var getnext func
getnext := func(next []int, needle string)([]int){
n := len(needle)
j := 0
for i:=1;i <n; i++{
for j>0 && needle[i] != needle[j]{
j = next[j-1]
}
if needle[i] == needle[j]{
j += 1
}
next[i] = j
}
return next
}
next := make([]int, len(needle))
getnext(next, needle)
j := 0
for i:=0;i<len(haystack);i++ {
for j>0 && haystack[i] != needle[j]{
j = next[j-1]
}
if haystack[i] == needle[j]{
j = j +1
if j==len(needle){
return i - j +1
}
}
}
return -1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Make sure to add code blocks to your code group
# 两数相除
定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
class Solution:
# 此方法会超时
# def divide(self, dividend: int, divisor: int) -> int:
# # 由于取值范围是[−2**31, 2**31 − 1] 所以同一使用负数来处理溢出问题
# flag = False # 是否需要加负号
# if (dividend >0 and divisor < 0) or (dividend <0 and divisor >0):
# flag = True
# dividend = abs(dividend)
# divisor = abs(divisor)
# num = 0
# while divisor <= dividend:
# dividend -= divisor
# num -=1
# if num == (-2)**31:
# return 2**31-1
# else:
# return num if flag else -num
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
flag = (divisor >0) == (dividend >0)
limit = 2**31-1
dividend = abs(dividend)
divisor = abs(divisor)
if dividend < divisor:
return 0
# if dividend == divisor:
# return 1 if flag else -1
n = 0
-
res, div ,tmp = 0, divisor, 1
while dividend >= divisor:
while dividend > (div<<1): # 在暴力的基础上加一层指数递进
tmp = tmp << 1
div = div << 1
# 结果就在div * 2**tmp 附近(商大于这个数),后续再暴力逼近
dividend -= div
div = divisor
res += tmp # div * 2**tmp次方,外层循环通过减法来做, 后续每减一次,tmp+1
tmp = 1
if not flag:
res = - res
if res < (-2) **31 or res > (2) **31 -1:
return 2 ** 31 -1 if res > (2) **31 -1 else (-2) **31
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
后续补充
// Make sure to add code blocks to your code group
# 搜索螺旋排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
每一次二分查找后,数组都被分为一个有序的和一个无序的, 两种情况分别按照二分查找的方式处理就可以解决此问题
class Solution:
def search(self, nums: List[int], target: int) -> int:
"""
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。此时有序部分用二分法查找。无 序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.
"""
left, right = 0, len(nums)-1
while left <=right:
mid = (left+right)//2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]: # 前半部分有序,
if nums[0] <= target <nums[mid]: # 判断target是否在上升序列中,如果在,需要移动右指针
right = mid -1
else:
left = mid + 1 # 如果不在上升序列中,则舍弃该序列
else: # 类无序的一部分
if nums[mid] < target <= nums[len(nums)-1]: #
left = mid +1
else:
right = mid -1
return -1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
后续补充
// Make sure to add code blocks to your code group
# 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/search-insert-position 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid -1
return left
2
3
4
5
6
7
8
9
10
11
12
13
14
15
后续补充
// Make sure to add code blocks to your code group
# 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
"""
二分查找寻找左右边界
"""
def search_left(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) //2
print(mid, "mid")
if nums[mid] == target:
right = right -1
elif nums[mid] > target:
right = mid-1
else:
left = mid +1
if nums[left] == target:
return left
else:
return -1
def search_right(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = (left +right) // 2
if nums[mid] == target:
left = mid +1
elif nums[mid] >target:
right = mid -1
else:
left = mid +1
if nums[right] == target:
return right
else:
return -1
if not nums or nums[0] > target or nums[-1] < target:
return [-1, -1]
l = search_left(nums, target)
r = search_right(nums, target)
if all([l != -1, r != -1]):
return [l, r]
else:
return [-1, -1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
后续补充
// Make sure to add code blocks to your code group
# 有效的数独
from collections import defaultdict
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
row, col , sqrt = defaultdict(set), defaultdict(set), defaultdict(set)
for i in range(0, 9):# 行
for j in range(0, 9): # 列
if board[i][j] == ".":
continue
if board[i][j] in row[i]:
return False
if board[i][j] in col[j]:
return False
## 推导i, j 是在哪个盒子
if board[i][j] in sqrt[i//3, j//3]:
return False
row[i].add(board[i][j])
col[j].add(board[i][j])
sqrt[i//3,j//3].add(board[i][j])
return True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
后续补充
// Make sure to add code blocks to your code group
# 组合总数
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。 示例 2:
输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]] 示例 3:
输入: candidates = [2], target = 1 输出: []
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combination-sum 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
"""
组合问题,用回溯法
"""
path = list()
ret = list()
n = len(candidates)
def backtracking(path, startindex, total_num):
if total_num > target:
return
if total_num == target:
ret.append(path[:])
return
for i in range(startindex, n):
path.append(candidates[i])
total_num += candidates[i]
backtracking(path, i, total_num)
path.pop()
total_num -= candidates[i]
backtracking(path, 0, 0)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
后续补充
// Make sure to add code blocks to your code group
# 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
示例 1:
输入: num1 = "2", num2 = "3" 输出: "6" 示例 2:
输入: num1 = "123", num2 = "456" 输出: "56088"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/multiply-strings 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def multiply(self, num1: str, num2: str) -> str:
"""
1. 如何将字符转换为整数
2. 可以使用乘法?,如果不能需要模拟乘法
"""
num1_int, num2_int = 0, 0
def str_to_int(num: str) -> int:
ret = 0
for ch in num:
print(ch, ord(ch) - ord("0"))
ret = ret*10 + (ord(ch) - ord("0"))
return ret
num1_int, num2_int = str_to_int(num1), str_to_int(num2)
return str(num1_int*num2_int)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def multiply(self, num1: str, num2: str) -> str:
"""
1. 如何将字符转换为整数
2. 可以使用乘法?,如果不能需要模拟乘法,模拟乘法
"""
if num1 == "0" or num2 == "0":
return "0"
n, m = len(num2), len(num1)
ret = "0"
for i in range(n-1, -1, -1):
tag = 0 # 上一位的进位
y = int(num2[i])
cur = ["0"] * (n-i -1)
for j in range(m-1, -1, -1): # 循环取第一个数
tmp = (int(num2[i]) * int(num1[j]) + tag)%10
tag = (int(num2[i]) * int(num1[j]) + tag)//10
cur.append(str(tmp))
if tag >0:
cur.append(str(tag))
cur = "".join(cur[::-1])
ret = self.add_str(ret, cur)
return ret
def add_str(self, num1, num2):
# 两个数相加
i, j = len(num1)-1, len(num2)-1
add = 0
ans = []
while i>=0 or j>= 0 or add >0:
x = int(num1[i]) if i >=0 else 0
y = int(num2[j]) if j >=0 else 0
result = x + y + add
ans.append(str(result%10))
add = result//10
i -= 1
j -= 1
return "".join(ans[::-1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution:
# 模拟乘法运算过程,最后统一处理进位, 注意每次相乘结果对象要加到列表的i+j+1处
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
m, n = len(num1), len(num2)
temp = [0]* (m + n)
for i in range(0, m):
for j in range(0, n):
num = (ord(num1[i]) - ord("0")) * ((ord(num2[j]) - ord("0")))
temp[i + j +1] = temp[i+j+1] + num
falg_num = 0
for n in range(-1, -len(temp) +1, -1):
this_num = (temp[n]) % 10
falg_num = temp[n]// 10
temp[n] = this_num
temp[n-1] += falg_num
index = 1 if temp[0] == 0 else 0
ans = "".join([str(i) for i in temp[index:]])
return ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
后续补充
// Make sure to add code blocks to your code group
# 跳跃游戏II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
- 贪心算法的典型应用
class Solution:
def jump(self, nums: List[int]) -> int:
"""
每次跳尽可能的远,通过更新最大覆盖范围来看是否达到终点,记录跳跃次数
"""
if len(nums) <=1:
return 0
end = 0 # 记录上次能跳到的最远位置
max_pos = 0
steps = 0
for i in range(len(nums)-1):
if i <= max_pos: # 如果在覆盖范围内,标记最大的覆盖范围
max_pos = max(i+nums[i], max_pos)
if i == end: # 需要跳一部, 更新,最远位置
steps += 1
end = max_pos
return steps
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func jump(nums []int) int {
// 维护当前能到达的边界,如果到达边界,steps +1, 同时更新边界
step := 0
n := len(nums)
max_pos := 0
end := 0
for i:=0;i <n-1; i++ { // 最后一个不用跳,看前面的是否能跳到这个位置
if i <= max_pos{ //记录下次最大的覆盖范围
if max_pos < i + nums[i]{
max_pos = i + nums[i]
}
if i == end { // 走到本次的边界
step += 1
end = max_pos
}
}
}
return step
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 全排列
给定一个不含重复数字的数组 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]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/permutations 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 典型的回溯算法-- 通过同层是否使用过来进行剪枝
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
path = list()
result = list()
n = len(nums)
def backtracking(path, used):
if len(path) == n:
result.append(path[:])
return
for i in range(0, n):
if used[i]:
continue
used[i] = True
path.append(nums[i])
backtracking(path, used)
path.pop()
used[i] = False
return result
result = backtracking(path, {i:False for i in range(n)})
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
后续补充
// Make sure to add code blocks to your code group
# 全排列II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]] 示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/permutations-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
"""
今典的回溯法
注意剪枝
"""
n = len(nums)
path = list()
result = list()
nums.sort()
def backtracking(path, used):
if len(path) == len(nums):
result.append(path[:])
return
for i in range(n):
if used[i]: # 利用是否使用过避免同层同一个index对应的元素多次使用
continue
if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
continue # 避免同层同样的值多次使用
path.append(nums[i])
used[i] = True
backtracking(path, used)
path.pop()
used[i] = False
return result
result = backtracking(path, {i:False for i in range(n)})
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
后续补充
// Make sure to add code blocks to your code group
# 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/rotate-image 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[[7,4,1],[8,5,2],[9,6,3]]
1
2
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
#解法一 旋转90, 可以发现,第一行旋转后是最后一列,第二行是倒数第二列,依次类推
# 解法二:每个位置对应旋转,找到相应的位置,相当于每个位置都转圈找, 基础公式A[col][n-row-1] = A[row][col]
n = len(matrix)
for row in range(n//2): # 控制行
for col in range((n+1)//2): # 控制列
matrix[row][col], matrix[n-col-1][row], matrix[n-row-1][n-col-1], matrix[col][n-row-1] = matrix[n-col-1][row], matrix[n-row-1][n-col-1], matrix[col][n-row-1], matrix[row][col]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
后续补充
// Make sure to add code blocks to your code group
# 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]] 示例 2:
输入: strs = [""] 输出: [[""]] 示例 3:
输入: strs = ["a"] 输出: [["a"]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/group-anagrams 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
path = list()
n = len(strs)
result = list()
from collections import defaultdict
dict1 = defaultdict(list)
for ch in strs:
key = "".join(sorted(list(ch)))
dict1[key].append(ch)
return list(dict1.values())
2
3
4
5
6
7
8
9
10
11
12
13
后续补充
// Make sure to add code blocks to your code group
# 无重复字符的最长子串
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度
class Solution:
"""
可以使用双指针,也可以使用贪心算法,这里使用贪心算法
贪心算法: 要想使字串最长,那么,每个重复字母之间的距离最大,最后的最大距离就是最长字串的长度
s = "abcabcbb"
"""
def lengthOfLongestSubstring(self, s: str) -> int:
max_len = 0
start = 0
char_index = dict()
for i in range(len(s)):
# 任何字串都不能有重复的,如果获取到的下标小于start 则不更新start, 如果大于等于,则更新start
if s[i] in char_index and char_index[s[i]]>=start:
max_len = max(i-start, max_len)
start = char_index[s[i]] +1 if char_index[s[i]] >= start else start
char_index[s[i]] = i
print(max_len, len(s)- start)
return max(max_len, len(s) - start)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Power(x, n)
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。
示例 1:
输入:x = 2.00000, n = 10 输出:1024.00000 示例 2:
输入:x = 2.10000, n = 3 输出:9.26100 示例 3:
输入:x = 2.00000, n = -2 输出:0.25000 解释:2-2 = 1/22 = 1/4 = 0.25
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/powx-n 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
"""
x ** 89 89//2 +1
x ** 88 88/2
"""
def myPow(self, x: float, n: int) -> float:
def quick_mul(N):
if N == 0:
return 1.0
y = quick_mul(N//2)
return y *y if N %2 ==0 else y*y *x
return quick_mul(n) if n>=0 else 1/quick_mul(-n)
2
3
4
5
6
7
8
9
10
11
12
后续补充
// Make sure to add code blocks to your code group
# 最大子数组合
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。 示例 2:
输入:nums = [1] 输出:1 示例 3:
输入:nums = [5,4,-1,7,8] 输出:23
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/maximum-subarray 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 动态规划问题
- 可以将该问题看做是求以i结尾的最大连续子数组的和
- 同时考虑在i的位置 如果前i-1的和为负数,那么此时+nums[i]一定小于numi
- 如果前i-1的结果为正数, 那么此时i位置的最大连续子数组和+nums[i]
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
dp[i] 表示i结尾的连续 字数 最大和
dp[i] = dp[i-1] + nums[i] # nums[i]是个正数或者0
= nums[i] # 是个负数
"""
dp = [0] * len(nums)
dp[0] = nums[0]
for i in range(1, len(nums)):
if dp[i-1] >0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i] = nums[i]
return max(dp)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
后续补充
// Make sure to add code blocks to your code group
# 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5] 示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 输出:[1,2,3,4,8,12,11,10,9,5,6,7]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/spiral-matrix 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
'''
对于每层,从左上方开始以顺时针的顺序遍历所有元素。假设当前层的左上角位于 (top,left),右下角位于 (bottom,right),按照如下顺序遍历当前层的元素。
从左到右遍历上侧元素,依次为 (top,left) 到(top,right)。
从上到下遍历右侧元素,依次为 (top+1,right) 到 (\textit{bottom}, (bottom,right)。
如果 left<right 且 top<bottom,则从右到左遍历下侧元素,依次为 (bottom,right−1) 到 )(bottom,left+1),以及从下到上遍历左侧元素,依次为(top+1,left)。
遍历完当前层的元素之后,将 left 和top 分别增加 1,将 right 和 bottom 分别减少 11,进入下一层继续遍历,直到遍历完所有元素为止。
'''
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if len(matrix)==0:
return []
ret = list()
top, bottom, left, right = 0, len(matrix)-1, 0, len(matrix[0]) -1
while left <= right and top <= bottom:
for column in range(left, right +1):
ret.append(matrix[top][column])
for row in range(top+1, bottom+1):
ret.append(matrix[row][right])
if left < right and top < bottom:
for column in range(right-1, left, -1):
ret.append(matrix[bottom][column])
for row in range(bottom, top, -1):
ret.append(matrix[row][left])
top +=1
bottom -= 1
left += 1
right -= 1
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func spiralOrder(matrix [][]int) []int {
if len(matrix) == 0 || len(matrix[0])==0{
return []int{}
}
left := 0
right:=len(matrix[0])-1
top :=0
bottom := len(matrix)-1
result := make([]int, (right+1)*(bottom+1))
index := 0
for left<=right&&top<=bottom{
for col:=left;col<=right;col++{
result[index]=matrix[top][col]
index ++
}
for row:=top+1;row<=bottom;row++{
result[index]=matrix[row][right]
index++
}
if left<right &&top<bottom{
for col:=right-1;col>=left;col--{
fmt.Println(result)
result[index]= matrix[bottom][col]
index++
}
for row:=bottom-1;row>top;row--{
result[index]=matrix[row][left]
index++
}
}
left += 1
right -= 1
top += 1
bottom -= 1
}
return result
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Make sure to add code blocks to your code group
# 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4] 输出:true 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。 示例 2:
输入:nums = [3,2,1,0,4] 输出:false 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/jump-game 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def canJump(self, nums: List[int]) -> bool:
"""
贪心算法
每跳一次,争取跳最远,也就是覆盖的范围最大
"""
if len(nums) ==1:
return True
max_pos = 0
end = 0
step = 0
n = len(nums)
for i in range(n):
max_pos = max(i + nums[i], max_pos)
if i == end:
end = max_pos
if end >= n -1:
return True
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func canJump(nums []int) bool {
max_pos := 0
end := 0
for i:=0; i<len(nums);i++{
if nums[i] + i > max_pos{
max_pos = nums[i] + i
}
if i == end{
end = max_pos
if end >= len(nums) - 1{
return true
}
}
}
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 输出:[[1,6],[8,10],[15,18]] 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 示例 2:
输入:intervals = [[1,4],[4,5]] 输出:[[1,5]] 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/merge-intervals 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
"""
如何确定是不是重复,一定是前一个的最后一个和后面的第一个有交叉重复的
"""
intervals = sorted(intervals, key = lambda x: x[0])
tmp = [intervals[0],]
for interval in intervals[1:]:
#cur_min, cur_max =intervals[0], intervals[1]
pre = tmp[-1]
if interval[0]<=pre[1]:
if interval[1] > pre[1]:
pre[1] = interval[1]
else:
tmp.append(interval)
return tmp
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
"""
双指针法
"""
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
ret = list()
intervals = sorted(intervals, key=lambda x: (x[0], x[1]))
for i in range(len(intervals)):
if i == 0:
ret.append(intervals[0])
continue
pre_start = ret[-1][0]
pre_end = ret[-1][1]
nex_start = intervals[i][0]
nex_end = intervals[i][1]
if nex_start <= pre_end:
ret[-1]=[pre_start, max(pre_end, nex_end)]
else:
ret.append(intervals[i])
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 插入区间
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5] 输出:[[1,5],[6,9]] 示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] 输出:[[1,2],[3,10],[12,16]] 解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。 示例 3:
输入:intervals = [], newInterval = [5,7] 输出:[[5,7]] 示例 4:
输入:intervals = [[1,5]], newInterval = [2,3] 输出:[[1,5]] 示例 5:
输入:intervals = [[1,5]], newInterval = [2,7] 输出:[[1,7]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/insert-interval 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
left, right = newInterval
placed = False
ans = list()
for li, ri in intervals:
if li > right:
# 在插入区间的右侧且无交集
if not placed:
ans.append([left, right])
placed = True
ans.append([li, ri])
elif ri < left:
# 在插入区间的左侧且无交集
ans.append([li, ri])
else:
# 与插入区间有交集,计算它们的并集
left = min(left, li)
right = max(right, ri)
if not placed:
ans.append([left, right])
return ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = "Hello World" 输出:5 解释:最后一个单词是“World”,长度为5。 示例 2:
输入:s = " fly me to the moon " 输出:4 解释:最后一个单词是“moon”,长度为4。 示例 3:
输入:s = "luffy is still joyboy" 输出:6 解释:最后一个单词是长度为6的“joyboy”。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/length-of-last-word 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def lengthOfLastWord(self, s: str) -> int:
# s = s.rstrip(" ")
# num = 0
# if len(s) == 1:
# return 1
# for i in range(-1, -len(s)-1, -1):
# if s[i] == " ":
# break
# num += 1
# return num
# 用内置方法
return len(s.rstrip(" ").split(" ")[-1].lstrip(" "))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 螺旋矩阵II
给你一个正整数
n
,生成一个包含1
到n2
所有元素,且元素按顺时针顺序螺旋排列的n x n
正方形矩阵matrix
。示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
1
2
class Solution:
"""
模拟方向, 按照顺序填入数字
[[1,2,3],
[9,10,4],
[8,7,6]]
"""
def generateMatrix(self, n: int) -> List[List[int]]:
left = 0
right = n -1
top = 0
bottom = n-1
board = [["-"]*n for _ in range(n)]
start = 0
while top <= bottom and left <= right:
for col in range(left, right+1):
start += 1
board[top][col] = start
for row in range(top+1, bottom +1):
start += 1
board[row][right] = start
if left < right and top < bottom:
for col in range(right-1, left, -1):
start += 1
board[bottom][col] = start
for row in range(bottom, top, -1):
start+= 1
board[row][left] = start
top += 1
bottom -= 1
left += 1
right -= 1
return board
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Make sure to add code blocks to your code group
# 排序序列
给出集合 [1,2,3,...,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123" "132" "213" "231" "312" "321" 给定 n 和 k,返回第 k 个排列
#会超时
class Solution:
"""
适用回溯法
"""
def getPermutation(self, n: int, k: int) -> str:
sums = [i for i in range(1, n+1)]
print(sums)
path = []
result = list()
def backtracing(startindex, path, depth, used):
if depth == n:
result.append(path[:])
return
for i in range(0, n):
if used[i]:
continue
path.append(str(sums[i]))
used[i] = True
backtracing(startindex +1 , path, depth + 1, used)
path.pop()
used[i] = False
backtracing(0, path, 0, {i: False for i in range(0, n)})
return "".join(result[k-1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 旋转链表
给你一个链表的头节点
head
,旋转链表,将链表每个节点向右移动k
个位置。
输入:head = [1,2,3,4,5], k = 2 输出:[4,5,1,2,3]
1
2
输入:head = [0,1,2], k = 4 输出:[2,0,1]
1
2
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
"""
1. 计算链表的长度
2. 新链表的尾部(n - 1) - n%k 起始位置是0开始,如果是1开始不用减1
3. 转为循环链表
4. 在新链表尾部断开
"""
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if k == 0 or not head or not head.next:
return head
# 获取链表的长度
length = 1
cur = head
while cur.next:
length += 1
cur = cur.next
cur.next = head # b闭合为环,此时cur位于首节点
# 在第k个位置需要解环,k又可能是大于length
# 当k 大于等于length时,我们只需要向右移动k mod length 个节点,因为每length个长度为一个循环
# 此时新链表的最后一个节点为(n -1)-(k mod length)(从0开始,从1开始不需要减)
end = length - k % length # 新链表的最后一个节点,这个节点需要断开
while end > 0:
cur = cur.next
end -= 1
# 此时cur 位于需要断开的节点上,下一个节点就是新链表的首节点,然后断开返回就可以了
ret = cur.next
cur.next = None
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Make sure to add code blocks to your code group
# 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/unique-paths 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例 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 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# dp[i][j] 在ij位置处的所有路径
# 递推方程 dp[i][j] =dp[i][j] + dp[i-1][j] + dp[i][j-1]
# 生成dp 初始化, 只有一行,或者只有一列 只有一种走法
dp = [[1] *n] + [[1]+ [0]* (n-1) for _ in range(1, m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j] + dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 不同路径II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/unique-paths-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例 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:
"""
1. dp[i][j]到达(i)(j)的路径数
2. 递推公式: dp[i][j] = dp[i-1][j] + dp[i][j-1]
dp[i][j] = 0 # (i, j) 处有障碍物体
3. 初始化
"""
dp = [[0] * len(obstacleGrid[0]) for _ in range(len(obstacleGrid))]
for i in range(len(obstacleGrid)):
for j in range(len(obstacleGrid[0])):
if i ==0 and j ==0 and obstacleGrid[i][j] !=1:
dp[i][j] = 1
else:
if obstacleGrid[i][j] == 0:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 最小路径和
给定一个包含非负整数的 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:
"""
dp[i][j] 在(i,j) 处最小数字总和
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
"""
for i in range(1, len(grid)):
grid[i][0] += grid[i-1][0]
for j in range(1, len(grid[0])):
grid[0][j] += grid[0][j-1]
print(grid)
for i in range(1, len(grid)):
for j in range(1, len(grid[0])):
grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j]
return grid[-1][-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Make sure to add code blocks to your code group
# 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/plus-one 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
if not digits:
return digits
digits = [0] + digits
digits[-1] += 1
flag = 0
for i in range(-1, -len(digits)-1, -1):
num = (digits[i] + flag) % 10
flag = (digits[i] + flag) // 10
digits[i] = num
return digits[1:] if digits[0] == 0 else digits
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 二进制求和
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。
示例 1:
输入:a = "11", b = "1" 输出:"100" 示例 2:
输入:a = "1010", b = "1011" 输出:"10101"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/add-binary 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def addBinary(self, a: str, b: str) -> str:
n = max(len(a), len(b))
a = "0" + (n - len(a)) * "0" + a
b = "0" + (n - len(b)) * "0" + b
flag = 0
ret = [0] * len(a)
for i in range(-1, -len(ret)-1, -1):
num = ord(a[i]) - ord("0") + ord(b[i]) - ord("0") + flag
cur = num %2
flag = num//2
ret[i] = str(cur)
if len(ret) == 1:
return "".join(ret)
ret = ret[1:] if ret[0] == "0" else ret
return "".join(ret)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Make sure to add code blocks to your code group
# 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:
left, right, ans = 0, x, 0
if x in {0, 1}:
return x
while left <=right:
mid = (left + right) // 2
if mid * mid <= x and (mid+1)*(mid+1) >x:
return mid
elif mid * mid <x:
left = mid+1
else:
right = mid-1
return x
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 爬楼梯
假设你正在爬楼梯。需要 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:
"""
动态规划解法
dp[i] 爬到第i台阶共有dp[i]种爬法
推到公式: dp[i] = dp[i-1] + dp[i-2] 可知边界条件为i =1、i=2
初始化dp[1] = 1 dp[2] = 2
"""
if n <= 2:
return n
dp = [1, 2]
for i in range(2, n):
dp.append(dp[i-1] + dp[i-2])
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def climbStairs(self, n: int) -> int:
"""
斐波那契数列
"""
if n <= 2:
return n
a, b = 1, 2
tag = 2
while tag <n:
a, b = b, a+b
tag += 1
return b
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 简化路径
给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。
请注意,返回的 规范路径 必须遵循下述格式:
始终以斜杠 '/' 开头。 两个目录名之间必须只有一个斜杠 '/' 。 最后一个目录名(如果存在)不能 以 '/' 结尾。 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.' 或 '..')。 返回简化后得到的 规范路径 。
示例 1:
输入:path = "/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。 示例 2:
输入:path = "/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。 示例 3:
输入:path = "/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 示例 4:
输入:path = "/a/./b/../../c/" 输出:"/c"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/simplify-path 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
"""
./ 当前
../ 上一级
/c 能返回
"""
def simplifyPath(self, path: str) -> str:
path = path.strip("/")
paths = path.split("/")
stack = []
for cha in paths:
if cha == "..":
if stack:
stack.pop()
elif cha and cha != ".":
stack.append(cha)
return '/' + '/'.join(stack)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func simplifyPath(path string) string {
pathList := strings.Split(path, "/")
fmt.Println(pathList)
fmt.Println(len(pathList))
stack := make([]string, 0)
for _, ch := range pathList {
if ch == "" || ch == "."{
continue
} else if ch == ".."{
if len(stack)>0{
stack = stack[:len(stack)-1]
}
} else {
stack = append(stack, ch)
}
}
return "/" + strings.Join(stack, "/")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
# 矩阵置零
给定一个
*m* x *n*
的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 (opens new window) 算法**。**示例 1:
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]] 示例 2:
输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/set-matrix-zeroes 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
cols = [1] * len(matrix[0])
rows = [1] * len(matrix)
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == 0:
rows[i] = 0
cols[j] = 0
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if rows[i] == 0 or cols[j] == 0:
matrix[i][j] = 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func setZeroes(matrix [][]int) {
rows := make([]bool, len(matrix))
cols := make([]bool, len(matrix[0]))
for i:=0;i<len(matrix);i++{
for j:=0;j<len(matrix[0]);j++{
if matrix[i][j] == 0{
rows[i] = true
cols[j] = true
}
}
}
for i:=0;i<len(matrix);i++{
for j:=0;j<len(matrix[0]);j++{
if rows[i] || cols[j] {
matrix[i][j] = 0
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 搜索二维矩阵
给你一个满足下述两条属性的 m x n 整数矩阵:
每行中的整数从左到右按非递减顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
示例 1:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 输出:true 示例 2:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/search-a-2d-matrix 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
left, right = 0, len(matrix)
while left <=right and left < len(matrix):
mid = (left + right) // 2
if matrix[mid][0] > target:
right = mid - 1
print(right, "right")
elif matrix[mid][-1] < target:
left = mid + 1
else:
# 二分查找matrix[mid]
l, r = 0, len(matrix[mid])
while l<=r:
mid_mid = (l + r) // 2
if matrix[mid][mid_mid] == target:
return True
elif matrix[mid][mid_mid] > target:
r = mid_mid -1
else:
l = mid_mid +1
break
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
"""
本质和解法一相同,只不过是写法不同
行列都可以用二分查找
"""
# 1. 先看看是落在哪一行中
m, n = len(matrix), len(matrix[0])
top, bottom = 0, m -1
in_row = -1
while top <= bottom:
mid = (top + bottom) // 2
if matrix[mid][-1] < target:
top = mid +1
elif matrix[mid][0] > target:
bottom = mid -1
else:
in_row = mid
break
left, right = 0, n-1
if in_row == -1:
return False
while left <= right:
mid = (left + right) // 2
if matrix[in_row][mid] == target:
return True
elif matrix[in_row][mid] < target:
left = mid + 1
else:
right = mid -1
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func searchMatrix(matrix [][]int, target int) bool {
var (
left int
right int
top int
buttom int
mid int
possible_row int
)
left, right = 0, len(matrix[0])-1
top, buttom = 0, len(matrix)-1
possible_row = -1
mid = 0
fmt.Println(mid, possible_row)
for ;top<=buttom;{
mid = (top+buttom)/2
if matrix[mid][0]<=target && target<=matrix[mid][right]{
possible_row = mid
break
} else if matrix[mid][right]<target{
top = mid+1
} else{
buttom = mid -1
}
}
if possible_row == -1 {
return false
}
for left<=right{
mid = (left + right)/2
if matrix[possible_row][mid] ==target{
return true
} else if matrix[possible_row][mid]> target{
right = mid -1
} else{
left = mid +1
}
}
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Make sure to add code blocks to your code group
# 颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
- 各种排序算法,这里用快排算法
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
def quick_sort(nums, left, right):
if left< right:
mid = partition(nums, left, right)
quick_sort(nums, left, mid-1)
quick_sort(nums, mid+1, right)
return nums
def partition(data, left, right):
tmp = data[left]
while left < right:
while left < right and data[right] >= tmp:
right -= 1
data[left] = data[right]
while left < right and data[left] <= tmp:
left += 1
data[right] = data[left]
data[left] = tmp
return left
return quick_sort(nums, 0, len(nums)-1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
p0 = p1 = 0
for i in range(n):
if nums[i] == 1:
nums[p1], nums[i] = nums[i], nums[p1]
p1 += 1
elif nums[i] == 0:
nums[p0], nums[i] = nums[i], nums[p0]
if p0 < p1:
nums[p1], nums[i] = nums[i], nums[p1]
p0 += 1
p1 += 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func sortColors(nums []int) {
n := len(nums)
p0 := 0
p1 := 0
for i:=0;i<n;i++{
if nums[i] == 1{
nums[p1], nums[i] = nums[i], nums[p1]
p1 += 1
} else if nums[i]== 0{
nums[p0], nums[i] = nums[i], nums[p0]
if p0 < p1{
nums[p1], nums[i] = nums[i], nums[p1]
}
p0 += 1
p1 += 1
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Make sure to add code blocks to your code group
# 组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 示例 2:
输入:n = 1, k = 1 输出:[[1]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combinations 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
result = list()
path = list()
def backtracing(start_index, path):
if len(path) == k:
result.append(path[:])
for i in range(start_index, n +1):
path.append(i)
backtracing(i +1, path)
path.pop()
backtracing(1, path)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func combine(n int, k int) [][]int {
res := [][]int{}
path := []int{}
var backtracking func(startindex int,path []int, res *[][]int)
backtracking = func(startindex int, path []int, res *[][]int){
if len(path) == k{
temp := make([]int, k)
copy(temp, path)
*res = append(*res, temp)
return
}
for i:=startindex;i<=n;i++{
path = append(path, i)
backtracking(i+1, path, res)
path = path[:len(path)-1]
}
}
backtracking(1,path, &res)
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] 示例 2:
输入:nums = [0] 输出:[[],[0]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/subsets 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = []
path = list()
def backtracing(start_index, path):
result.append(path[:])
if len(path) == len(nums):
return
for i in range(start_index, len(nums)):
path.append(nums[i])
backtracing(i +1, path)
path.pop()
backtracing(0, path)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func subsets(nums []int) [][]int {
res := [][]int{}
path := []int{}
var backtracking func(startindex int, path []int)
backtracking = func(startindex int, path []int){
temp := make([]int, len(path))
copy(temp, path)
res = append(res, temp)
if len(path) == len(nums){
return
}
for i:= startindex;i<len(nums);i++{
path = append(path, nums[i])
backtracking(i+1, path)
path = path[:len(path)-1]
}
}
backtracking(0, path)
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func subsets(nums []int) [][]int {
res := [][]int{}
path := []int{}
backtracking(0, len(nums), &path, &res, &nums)
return res
}
func backtracking(startindex int, n int, path *[]int, res *[][]int, nums *[]int){
temp := make([]int, len(*path))
copy(temp, *path)
*res = append(*res, temp)
if len(*path) == n{
return
}
for i:=startindex;i<n;i++{
*path = append(*path, (*nums)[i])
backtracking(i+1, n, path, res, nums)
*path = (*path)[:len(*path)-1]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 单词搜索(特殊)
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true 示例 2:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE" 输出:true 示例 3:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB" 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/word-search 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 对应左右上下四个方向
def backtracing(i, j, k):
if board[i][j] != word[k]:
return False
if k == len(word) - 1:
return True
# 如果board[i][j] = word[k]:
visited.add((i, j))
result = False
for di, dj in directions:
newi, newj = i+di, j + dj
if 0<=newi<len(board) and 0<=newj<len(board[0]):
if (newi, newj) not in visited:
if backtracing(newi, newj, k +1):
result = True
break
visited.remove((i, j))
return result
visited = set()
for i in range(len(board)):
for j in range(len(board[0])):
if backtracing(i, j, 0):
return True
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func exist(board [][]byte, word string) bool {
directions := [][]int{{0, 1},{0, -1},{1, 0}, {-1, 0}}
m, n := len(board), len(board[0])
visited := make([][]bool, m)
for i:= range visited{
visited[i] = make([]bool, n)
}
var backtracking func(i int, j int, k int) bool
backtracking = func(i int, j int, k int) bool{
if board[i][j] != word[k]{
return false
}
if k == len(word)-1{
return true
}
visited[i][j] = true
defer func() { visited[i][j] = false }()
for _, dir := range directions{
newi, newj := i+dir[0], j +dir[1]
if 0<=newi && newi<m && 0<=newj && newj<n && !visited[newi][newj]{
flag := backtracking(newi, newj, k+1)
if flag{
return true
}
}
}
// visited[i][j] = false
return false
}
for i:=0;i<m;i++{
for j:=0;j<n;j++{
f := backtracking(i, j, 0)
if f{
return true
}
}
}
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Make sure to add code blocks to your code group
# 删除有序数组中的重复项
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。 示例 2:
输入:nums = [0,0,1,1,1,1,2,3,3] 输出:7, nums = [0,0,1,1,2,3,3] 解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。 不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow = 0
if len(nums) <=2:
return len(nums)
for fast in range(len(nums)):
if slow <2 or nums[fast] != nums[slow-2]:
nums[slow] = nums[fast]
slow += 1
return slow
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
start, end = 0, 0
while end <len(nums):
while nums[start] == nums[end] and end - start >=2:
#if end - start >= 2:
del nums[start]
end -= 1
#start += 1
# 此时两者之间的距离必小于二, 如果不相同, 则说明有了新元素,需要移动起始指针
if nums[start] != nums[end]:
start = end
end += 1
# 如果相同, 起始不变,end + 1
else:
end += 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func removeDuplicates(nums []int) int {
if len(nums) <=2{
return len(nums)
}
slow := 0
for quick:=0;quick < len(nums); quick++{
if slow <2 ||(nums[slow-2] != nums[quick]){
nums[slow] = nums[quick]
slow += 1
}
}
return slow
}
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 搜索旋转排列数组II
已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
你必须尽可能减少整个操作步骤。
示例 1:
输入:nums = [2,5,6,0,0,1,2], target = 0 输出:true 示例 2:
输入:nums = [2,5,6,0,0,1,2], target = 3 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/search-in-rotated-sorted-array-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
"""
1. 将数组分为两边
2. 旋转之前末尾所在的一边,如果最后一个元素不是最大值, 则这一半部分有序,有序的分割点为最大的一个数
3. 旋转之前末尾不在的一边, 完全有序,可以按照二分查找
"""
def search(self, nums: List[int], target: int) -> bool:
# 将数组按照下标分为两边
left , right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return True
if nums[left] == nums[mid] and nums[right] == nums[mid]:
left += 1
right -= 1
elif nums[left] <= nums[mid]:
if nums[left] <= target< nums[mid]: # 这半部分有序 处于上升序列中
right = mid -1
else:
left = mid +1
else: # 一半有序,一半部分有序, 无序
if nums[mid] < target <= nums[len(nums)-1]:
left = mid + 1
else:
right = mid -1
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func search(nums []int, target int) bool {
left, right := 0, len(nums)-1
for left <= right{
mid := (left + right)/2
if nums[mid] == target{
return true
}
if nums[left] == nums[mid] && nums[mid] == nums[right]{
left += 1
right -= 1
}else if nums[left]<=nums[mid]{
if nums[left]<=target && target<nums[mid]{
right = mid -1
} else{
left = mid + 1
}
} else{
if nums[mid]<target && target<=nums[right]{
left = mid +1
} else{
right = mid -1
}
}
}
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Make sure to add code blocks to your code group
# 删除排序链表中的重复元素
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例 1:
输入:head = [1,1,2] 输出:[1,2] 示例 2:
输入:head = [1,1,2,3,3] 输出:[1,2,3]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head:
return head
cur = head
while cur.next:
if cur.val == cur.next.val:
cur.next = cur.next.next # 删除原本的cur.next节点
else:
cur = cur.next
return head
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil{
return nil
}
dummy := &ListNode{0, head}
cur := dummy
for cur.Next != nil && cur.Next.Next != nil{
if cur.Next.Val == cur.Next.Next.Val{
repeat := cur.Next.Val
for cur.Next != nil && cur.Next.Val == repeat{
cur.Next = cur.Next.Next
}
} else{
cur = cur.Next
}
}
return dummy.Next
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
# 删除排序链表中的重复元素II
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5] 示例 2:
输入:head = [1,1,1,2,3] 输出:[2,3]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
"""
删除所有重复的数字节点,表示头节点也可能删除
数字重复的条件cur.val = cur.next.val
由于已经处理过的是不重复的,并且重复的不能加入,所以,这里考虑为了不用在处理已经连接过的节点,将重复条件转化为cur.next.val = cur.next.next.val, 直到找到不相等的tmp节点,此时
cur.next = tmp
"""
dummy = ListNode(0, head)
cur = dummy
while cur.next and cur.next.next:
if cur.next.val == cur.next.next.val:
# 直到找到不重复数字的节点连接到cur.next节点上
repeat = cur.next.val
while cur.next and cur.next.val == repeat:
cur.next = cur.next.next
else:
cur = cur.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
// dummy := &ListNode(0, head)
if head == nil{
return nil
}
cur := head
for cur.Next != nil{
if cur.Val == cur.Next.Val{
cur.Next = cur.Next.Next
} else{
cur = cur.Next
}
}
return head
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 分隔链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5] 示例 2:
输入:head = [2,1], x = 2 输出:[1,2]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/partition-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
"""
注意理解,得所有 小于 x 的节点都出现在 大于或等于 x的节点之前这句话
只是移动比x的节点小的,大于等于的节点保持顺序不变,不是排序
"""
left = leftnode = ListNode(-101) # 放比x小的
right = rightnode = ListNode(-101) # 放置比x大的
while head:
if head.val < x:
leftnode.next=head
leftnode = leftnode.next
else:
rightnode.next =head
rightnode = rightnode.next
head = head.next
rightnode.next = None # 由于大的某个节点可能指向小的某个节点,导致循环,这里需要将这个节点解开
leftnode.next = right.next
return left.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func partition(head *ListNode, x int) *ListNode {
small := &ListNode{0, nil}
smallHead := small
large := &ListNode{0, nil}
largeHead := large
if head == nil {
return nil
}
for head != nil{
if head.Val <x{
small.Next = head
small = small.Next
} else{
large.Next = head
large = large.Next
}
head = head.Next
}
large.Next = nil
small.Next = largeHead.Next
return smallHead.Next
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Make sure to add code blocks to your code group
# 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。 示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。 示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1] 。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/merge-sorted-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
l = 0
r = 0
ret = list()
while l <m and r < n:
if nums1[l] <= nums2[r]:
ret.append(nums1[l])
l += 1
else:
ret.append(nums2[r])
r += 1
ret = ret + nums1[l:m]
ret = ret + nums2[r:]
nums1[:] = ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func merge(nums1 []int, m int, nums2 []int, n int) {
l, r := 0, 0
temp := make([]int, 0)
for l <m && r <n{
if nums1[l]<nums2[r]{
temp = append(temp, nums1[l])
l += 1
} else{
temp = append(temp, nums2[r])
r += 1
}
}
if l<m{
temp = append(temp, nums1[l:m]...)
}
if r <n{
temp = append(temp, nums2[r:n]...)
}
copy(nums1, temp)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 子集II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]] 示例 2:
输入:nums = [0] 输出:[[],[0]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/subsets-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result = list()
path = list()
nums.sort()
def backtracing(path, start_index):
result.append(path[:])
if len(path) >= len(nums):
return
for i in range(start_index, len(nums)):
if i > start_index and nums[i] == nums[i-1]:
continue
path.append(nums[i])
backtracing(path, i +1)
path.pop()
backtracing(path, 0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func subsetsWithDup(nums []int) [][]int {
result := &[][]int{}
sort.Ints(nums)
backtracking([]int{}, nums, 0, result)
return *result
}
func backtracking(temp, nums []int, startindex int, result *[][]int){
tmp := make([]int, len(temp))
copy(tmp,temp)
*result = append(*result, tmp)
if len(tmp) >= len(nums){
return
}
for i:=startindex; i<len(nums);i++{
if i>startindex && nums[i] == nums[i-1]{
continue
}
temp = append(temp, nums[i])
backtracking(temp, nums, i+1, result)
temp = temp[:len(temp)-1]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 解码方法
一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
'A' -> "1" 'B' -> "2" ... 'Z' -> "26" 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:
"AAJF" ,将消息分组为 (1 1 10 6) "KJF" ,将消息分组为 (11 10 6) 注意,消息不能分组为 (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6" 和 "06" 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
示例 1:
输入:s = "12" 输出:2 解释:它可以解码为 "AB"(1 2)或者 "L"(12)。 示例 2:
输入:s = "226" 输出:3 解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 示例 3:
输入:s = "06" 输出:0 解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/decode-ways 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def numDecodings(self, s: str) -> int:
"""
定义dp 数组:dp[i]在s中,从s[0]-s[i]的所有解码方式
状态转移方程:dp[i] = dp[i-1] + dp[i-2]解码方式, i>2
初始化:dp[0] = 1
注意,每次计算dp[i]时, s[i-1]和s[i-2]都不能以`0`开头
"""
n = len(s)
dp = [1] + [0] * n # 空字符串可以又一种解码方法
for i in range(1, n+1):
if s[i-1] != "0":
dp[i] = dp[i] + dp[i-1]
if i >1 and s[i-2] !="0" and int(s[i-2:i]) <= 26:
dp[i] = dp[i] + dp[i-2]
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func numDecodings(s string) int {
/* 动态规划
dp[i]: 从0-i处所有的解码方法
dp[i] = dp[i-1] + dp[i-2]
dp[0] = 1
*/
n := len(s)
dp := make([]int, n+1)
dp[0] = 1
for i:=1;i<n+1;i++{
if s[i-1] != '0'{
dp[i] = dp[i] + dp[i-1]
}
if i>1 && s[i-2] !='0' && ((s[i-2] - '0') * 10 + (s[i-1]-'0')<=26){
dp[i] = dp[i] + dp[i-2]
}
}
return dp[n]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 反转链表II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5] 示例 2:
输入:head = [5], left = 1, right = 1 输出:[5]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/reverse-linked-list-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
def deverse_node(head):
# 原地逆置法反转链表
# bag = head
# end = bag.next
# while end:
# bag.next = end.next
# end.next = head
# head = end
# end = bag.next
# print(head, 999)
# 头插法
pre = None
cur = head
while cur:
next = cur.next # 指针指向下一个节点
# 在pre的头部插入cur节点
cur.next = pre
pre = cur
cur = next
# 反转后头节点是right_node, 尾节点是left_node
dummy = ListNode(0)
dummy.next = head
pre = dummy
for _ in range(left -1): # left 的前一个节点
pre = pre.next
right_node = pre
for _ in range(right - left +1): # 来到right 节点
right_node = right_node.next
# 切断出一个子链表(截取链表)
left_node = pre.next
curr = right_node.next
# 切断连接
pre.next = None
right_node.next = None
# 逆置
deverse_node(left_node)
# 拼接
pre.next = right_node
left_node.next = curr
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseBetween(head *ListNode, left int, right int) *ListNode {
/*
迭代法, 定义三个指针,pre, cur, nxt指针
有cur指针改变指向域,使其指向pre, 然后三个指针依次向后移动,直到nxt=nil为止
想法:
切出left --- right的链表,使用链表反转的的代码进行反转
将反转后的链表拼接回来
*/
var reverseNode func(head *ListNode)
reverseNode = func(head *ListNode){
beg := head
end := head.Next
for end != nil {
beg.Next = end.Next
end.Next = head
head = end
end = beg.Next
}
}
// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
dummy := &ListNode{-1, head}
cur := dummy
// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
// 建议写在 for 循环里,语义清晰
for i:=0;i<left-1;i++{
cur = cur.Next
}
// 第 2 步:从 cur 再走 right - left + 1 步,来到 right 节点
rightNode := cur
for i:=left-1;i<right;i++{
rightNode = rightNode.Next
}
// 第 3 步:切断出一个子链表(截取链表)
leftNode := cur.Next
lastNode := rightNode.Next
// 注意:切断链接
cur.Next = nil
rightNode.Next = nil
// 第 4 步:同第 206 题,反转链表的子区间
reverseNode(leftNode)
// 第 5 步:接回到原来的链表中
cur.Next = rightNode
leftNode.Next = lastNode
return dummy.Next
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Make sure to add code blocks to your code group
# 复原ip地址 - leetcode93
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"] 示例 2:
输入:s = "0000" 输出:["0.0.0.0"] 示例 3:
输入:s = "101023" 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/restore-ip-addresses 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
result = list()
path = list()
def backtracing(path, startindx):
if len(path) == 4 and startindx == len(s):
result.append(".".join(path[:]))
return
if startindx == len(s):
return
if s[startindx] == "0":
path.append("0")
backtracing(path, startindx+1)
path.pop()
for i in range(startindx, len(s)):
end = i +1
temp = int(s[startindx:end])
if 0 < temp and temp <= 255:
path.append(str(temp))
backtracing(path, end)
path.pop()
else:
break
backtracing(path, 0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import "strconv"
func restoreIpAddresses(s string) []string {
/*
回溯法,如果是0开头的单独处理
*/
var backTracking func(startindex int, path []string)
result := []string{}
backTracking = func(startindex int, path []string){
if len(path) == 4 && startindex == len(s){
temp := make([]string, len(path))
copy(temp, path[:])
result= append(result, strings.Join(temp, "."))
return
}
if startindex == len(s){
return
}
if s[startindex] == '0'{
path = append(path, "0")
backTracking(startindex+1, path)
path = path[:len(path)-1]
}
for i:=startindex;i<len(s);i++{
end := i+1
seg,_ := strconv.Atoi(s[startindex:end])
if 0<seg && seg<=255{
path = append(path, strconv.Itoa(seg))
backTracking(i+1, path)
path = path[:len(path)-1]
} else{
break
}
}
}
path := []string{}
backTracking(0, path)
return result
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Make sure to add code blocks to your code group
# 二叉树的中序遍历 - leetcode 94
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2:
输入:root = [] 输出:[] 示例 3:
输入:root = [1] 输出:[1]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-tree-inorder-traversal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
stack = []
cur = root
# 中序,模板:先用指针找到每颗子树的最左下角,然后进行进出栈操作
while stack or cur:
while cur:
stack.append(cur)
cur = cur.left
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
def mid_order(root, ret=[]):
if root is None:
return []
mid_order(root.left, ret)
ret.append(root.val)
mid_order(root.right, ret)
return ret
return mid_order(root, [])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
def mid_order(root, ret=[]):
if root is None:
return []
mid_order(root.left, ret)
ret.append(root.val)
mid_order(root.right, ret)
return ret
return mid_order(root, [])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 不同的二叉搜索树
给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3 输出:5 示例 2:
输入:n = 1 输出:1
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/unique-binary-search-trees 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
# 会超时
# def numTrees(self, n: int) -> int:
# def trees(start, end):
# if start > end:
# return [None, ]
# all_tress = list()
# for i in range(start, end+1):
# left_trees = trees(start, i-1)
# right_trees = trees(i+1, end)
# for l in left_trees:
# for r in right_trees:
# all_tress.append("_")
# return all_tress
# return len(trees(1, n))
def numTrees(self, n: int) -> int:
"""
dp[i] 表示在i处互不相同的二叉树个数
G(n) 表示长度为N 的序列组成二叉树的总个数
F(i, n) 表示以第i个元素为为根, 长度为n的元素组成二叉树的个数
G(n) = F(1, N) + F(2, n) + .... F(n,n)
F(i, n) = G(i-1)G(n-i)
G(i) += G(i-1)G(n-i)
"""
G = [0] * (n+1)
G[0] = 1
G[1] = 1
for i in range(2, n+1):
for j in range(1, i+1): # 长度为i 以j 为根的个数的总个数
G[i] += G[j-1]*G[i-j]
return G[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Make sure to add code blocks to your code group
# 不同的二叉搜索树II
给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。
示例 1:
输入:n = 3 输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]] 示例 2:
输入:n = 1 输出:[[1]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/unique-binary-search-trees-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def generateTrees(self, n: int) -> List[Optional[TreeNode]]:
"""
二叉搜索树:
1. 左节点>根节点>右节点
考虑当i为根节点时,可能的左子树为[1,i-1] 可能的右子树为[i+1, n]
每一次如此反复算小子集,所以用回溯法
"""
return self.generateTree(1, n) if n else []
def generateTree(self, start, end):
if start > end:
return [None]
allTrees = list()
for i in range(start, end+1):
# 获取可能所有左子树
leftTree = self.generateTree(start,i-1)
rightTree = self.generateTree(i+1, end)
for l in leftTree:
for r in rightTree:
cur = TreeNode(val=i)
cur.left = l
cur.right = r
allTrees.append(cur)
return allTrees
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Make sure to add code blocks to your code group
# 交错字符串
给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。
两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:
s = s1 + s2 + ... + sn t = t1 + t2 + ... + tm |n - m| <= 1 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ... 注意:a + b 意味着字符串 a 和 b 连接。
示例 1:
输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" 输出:true 示例 2:
输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" 输出:false 示例 3:
输入:s1 = "", s2 = "", s3 = "" 输出:true
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/interleaving-string 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
"""
如果在i 位置是交错则
s1[0] + s2[0] + s1[1] + s2[1] + .... s1[i] + s2[i] = s[2i] or
s1[0] + s2[0] + s1[1] + s2[1] + .... s1[i] = s[2i-1] # 或者先s2再s1
动态规划:
1. dp[i][j] = 在s1[0:i] s2[0:j] 处交错组成的字符串是否存在s3的字串 s3[0:i+j]
2.推导公式 len(s1[i]) + len(s2[j]) = len(s[3])
判断是否在是s1[i] 处是相交需要看是dp[i-1][j]处是否相交且s[i-1] == s3[i+j-1]
s2[j] dp[i][j-1] 且s2[j-1] = s3[i+j-1]
dp[0][0] = True 表示取空 可以生成s3中的空
2. 递推公式:
"""
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
#dp = [[False] * (len(s2)+1) for _ in range(len(s1)+1)]
len1 = len(s1)
len2 = len(s2)
len3 = len(s3)
if len1 + len2 != len3:
return False
dp=[[False]*(len2+1) for _ in range(len1+1)]
dp[0][0] = True
# 初始化行列
# print(dp)
for i in range(1, len(s2)+1): # 第一行
if s2[i-1] == s3[i-1]:
dp[0][i] = dp[0][i-1]
# print(dp, "初始化第一行")
for i in range(1, len(s1)+1 ): # 第一列
if s1[i-1] == s3[i-1]:
dp[i][0] = dp[i-1][0]
for i in range(1, len(s1)+1):
for j in range(1, len(s2)+1):
if s1[i-1] == s3[i+j-1]:
dp[i][j] = dp[i-1][j]
if s2[j-1] == s3[i+j-1]:
dp[i][j] = dp[i][j] or dp[i][j-1]
return dp[-1][-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Make sure to add code blocks to your code group
# 验证二叉搜索数
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3] 输出:true 示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/validate-binary-search-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 借助中序遍历
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
stack, prevorder = list(), float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
cur = stack.pop()
if cur.val <= prevorder:
return False
prevorder = cur.val
root = cur.right
return True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 恢复二叉搜索树
给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
示例 1:
输入:root = [1,3,null,null,2] 输出:[3,1,null,null,2] 解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。 示例 2:
输入:root = [3,1,4,null,null,2] 输出:[2,1,4,null,null,3] 解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/recover-binary-search-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def recoverTree(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
利用二叉搜索树的特性:通过中序遍历得到的数组时有序的,
也就是时说,数组中不符合条件的两个数就是交换位置的两个数
找到这两个node, 交换node的值就可以实现
"""
stack = list()
pre = TreeNode(float('-inf'))
firstnode = None
secondnode= None
while root or stack:
while root:
stack.append(root)
root = root.left
cur = stack.pop()
if not firstnode and cur.val < pre.val:
firstnode = pre
if firstnode and cur.val < pre.val:
secondnode = cur
pre = cur
root = cur.right
firstnode.val, secondnode.val = secondnode.val, firstnode.val
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Make sure to add code blocks to your code group
# 相同的树
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入:p = [1,2,3], q = [1,2,3] 输出:true 示例 2:
输入:p = [1,2], q = [1,null,2] 输出:false 示例 3:
输入:p = [1,2,1], q = [1,1,2] 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/same-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if (not p) ^ (not q):
return False
if (not p) and not q:
return True
queue1 = collections.deque([p])
queue2 = collections.deque([q])
while queue1 and queue2:
if len(queue1) != len(queue2):
return False
for _ in range(len(queue1)):
node1 = queue1.popleft()
node2 = queue2.popleft()
if node1.val != node2.val:
return False
left1, right1 = node1.left, node1.right
left2, right2 = node2.left, node2.right
# 两值相同异或结果为0,两只不同异或结果为1
if (not left1) ^ (not left2):
return False
if (not right1) ^ (not right2):
return False
if left1:
queue1.append(left1)
if right1:
queue1.append(right1)
if left2:
queue2.append(left2)
if right2:
queue2.append(right2)
return True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Make sure to add code blocks to your code group
# 对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
示例 1:
输入:root = [1,2,2,3,4,4,3] 输出:true 示例 2:
输入:root = [1,2,2,null,3,null,3] 输出:false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/symmetric-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
if (not root.left) ^ (not root.right):
return False
queue = collections.deque([root.left, root.right])
while queue:
# 同一层对称比较
l = queue.popleft()
r = queue.popleft()
# 全为None 跳过继续
# print(l.val, r.val)
if not l and not r:
continue
# 有一个不为空, 直接返回Fasle
if (not l) ^ (not r):
return False
if l.val != r.val:
return False
# 注意对称如队列
queue.append(l.left)
queue.append(r.right)
queue.append(l.right)
queue.append(r.left)
return True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Make sure to add code blocks to your code group
# 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]] 示例 2:
输入:root = [1] 输出:[[1]] 示例 3:
输入:root = [] 输出:[]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-tree-level-order-traversal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
"""
102题
107题
199题
"""
if not root:
return []
ret = list()
queue = collections.deque([root])
while queue: # 当前层的数组
tmp = list()
for _ in range(len(queue)):
cur = queue.popleft()
tmp.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
ret.append(tmp[:])
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Make sure to add code blocks to your code group
# 二叉树的锯齿形层序遍历
给你二叉树的根节点
root
,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[20,9],[15,7]] 示例 2:
输入:root = [1] 输出:[[1]] 示例 3:
输入:root = [] 输出:[]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-tree-zigzag-level-order-traversal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue = collections.deque([root])
flag = 1
ret = list()
while queue:
vals = list()
for _ in range(len(queue)):
cur = queue.popleft()
vals.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
if flag == -1:
vals = vals[::-1]
flag = -flag
ret.append(vals[:])
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Make sure to add code blocks to your code group
# 树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例: 给定二叉树 [3,9,20,null,null,15,7],
3 / \ 9 20 / \ 15 7
1
2
3
4
5返回它的最大深度 3 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
"""
广度优先遍历,层数就是最大深度
"""
if not root:
return 0
ret = 0
queue = collections.deque([root])
while queue:
for _ in range(len(queue)):
cur = queue.popleft()
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
ret += 1
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
# 二叉树的层序遍历 II
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[15,7],[9,20],[3]] 示例 2:
输入:root = [1] 输出:[[1]] 示例 3:
输入:root = [] 输出:[]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-tree-level-order-traversal-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue = collections.deque([root])
ret = list()
while queue:
vals = list()
for _ in range(len(queue)):
cur = queue.popleft()
vals.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
ret.append(vals)
return ret[::-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
# 将有序数组转换为二叉搜索树
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
示例 1:
输入:nums = [-10,-3,0,5,9] 输出:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3] 输出:[3,1] 解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return
if len(nums) == 1:
return TreeNode(nums[0])
mid = len(nums)//2
node = TreeNode(nums[mid])
node.left = self.sortedArrayToBST(nums[:mid])
node.right = self.sortedArrayToBST(nums[mid+1:])
return node
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 有序链表转换二叉搜索树
给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。
示例 1:
输入: head = [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0,-3,9,-10,null,5],它表示所示的高度平衡的二叉搜索树。 示例 2:
输入: head = [] 输出: []来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/convert-sorted-list-to-binary-search-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
def get_midnode(left: ListNode, right: ListNode) -> ListNode:
fast = slow = left
while fast !=right and fast.next != right:
fast = fast.next.next
slow = slow.next
return slow
def buildTree(left, right):
if left == right:
return
mid = get_midnode(left, right)
curnode = TreeNode(mid.val)
curnode.left = buildTree(left, mid)
curnode.right = buildTree(mid.next, right)
return curnode
return buildTree(head, None)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Make sure to add code blocks to your code group
# 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true 示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false 示例 3:
输入:root = [] 输出:true
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/balanced-binary-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
输入:root = [3,9,20,null,null,15,7] 输出:true 示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false 示例 3:
输入:root = [] 输出:true
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/balanced-binary-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isBalanced(self, root: Optional[TreeNode]) -> bool:
"""
最大值和最小值之间的差为1
"""
if not root:
return True
return abs(self.depth(root.left) - self.depth(root.right)) <=1 \
and self.isBalanced(root.right) \
and self.isBalanced(root.left)
def depth(self, root):
if not root:
return 0
return max(self.depth(root.left), self.depth(root.right)) + 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:2 示例 2:
输入:root = [2,null,3,null,4,null,5,null,6] 输出:5
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
left = root.left
right = root.right
min_depth = float("inf")
if not left and not right:
return 1
if root.left:
min_depth = min(self.minDepth(root.left), min_depth)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth)
return min_depth + 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
# 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 输出:true 解释:等于目标和的根节点到叶节点路径如上图所示。 示例 2:
输入:root = [1,2,3], targetSum = 5 输出:false 解释:树中存在两条根节点到叶子节点的路径: (1 --> 2): 和为 3 (1 --> 3): 和为 4 不存在 sum = 5 的根节点到叶子节点的路径。 示例 3:
输入:root = [], targetSum = 0 输出:false 解释:由于树是空的,所以不存在根节点到叶子节点的路径。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/path-sum 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
if root.left is None and root.right is None and root.val == targetSum:
return True
return self.hasPathSum(root.left, targetSum-root.val) or self.hasPathSum(root.right, targetSum-root.val)
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6] 示例 2:
输入:root = [] 输出:[] 示例 3:
输入:root = [0] 输出:[0]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
"""
先序遍历
"""
# head = TreeNode()
# dummy = TreeNode(right=head)
stack, path, cur = [], [], root
while stack or cur:
while cur:
stack.append(cur)
path.append(cur)
cur = cur.left
cur = stack.pop()
cur = cur.right
# ret = [root.val]
for i in range(1, len(path)):
prev, cur = path[i-1], path[i]
prev.left = None
prev.right = cur
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Make sure to add code blocks to your code group
# 杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2:
输入: numRows = 1 输出: [[1]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/pascals-triangle 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
输入: numRows = 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2:
输入: numRows = 1 输出: [[1]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/pascals-triangle 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def generate(self, numRows: int) -> List[List[int]]:
if numRows == 1:
return [[1]]
ret = list()
for i in range(0, numRows):
tmp = list()
for j in range(0, i+1):
if j == 0 or j == i:
tmp.append(1)
else:
num = ret[i-1][j-1] + ret[i-1][j]
tmp.append(num)
ret.append(tmp)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Make sure to add code blocks to your code group
# 杨辉三角II
给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: rowIndex = 3 输出: [1,3,3,1] 示例 2:
输入: rowIndex = 0 输出: [1] 示例 3:
输入: rowIndex = 1 输出: [1,1]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/pascals-triangle-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def getRow(self, rowIndex: int) -> List[int]:
prev = list()
cur = list()
for i in range(rowIndex+1):
for j in range(i+1):
if j == 0 or j == i:
cur.append(1)
else:
cur.append(prev[j] + prev[j-1])
prev = cur
cur = []
return prev
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 示例 2:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
动态规划:
1. 定义dp[i]截止到第i天时获得的最大收益
2. 状态转移方程 dp[i] = max(dp[i-1], prices - minprice)
3.初始化dp[0] = 0
4. 遍历顺序 左--> 右
"""
if len(prices) == 1:
return 0
dp = [0] * len(prices)
minprice = prices[0]
for i in range(len(prices)):
dp[i] = max(dp[i-1], prices[i] - minprice)
minprice = min(prices[i], minprice)
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 买卖股票的最佳时机II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4] 输出:7 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。 总利润为 4 + 3 = 7 。 示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。 示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
dp[i][0] 第i天结束时,手上没有股票的最大收益
dp[i][1] 第i天结束时,手上有股票的最大收益
# 手上没有股票可能情况有两种,一种时当天将股票卖了,另一种是前一天就卖了,
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
# 手上有股票可能的情况有两种,1.前一天有股票,一直没有卖,2.前一天没有,今天买
dp[i][1] = max(dp][i-1][1], dp[i-1][0]-prices[i])
初始化: dp[0][0] = 0
dp[0][1] = -prices[0]
"""
dp = [[0, 0] for _ in range(len(prices))]
dp[0][1] = -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
return max(dp[-1][0], dp[-1][1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 买卖股票的最佳时机III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4] 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。 示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3:输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。 示例 4:
输入:prices = [1] 输出:0
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
在第i天有五中状态
1. 没有任何操作 # 收益为0
2. 买入一支
3. 卖出同时买入第二支
4. 只是买入第二支(第一支前面已经完场了买卖)
5. 卖出第二支
1. buy1 = max(buy1', - prices[i])v # buy' 表示不进行操作,和前一天保持一直
2. sell1 = max(sell1', buy1' + prices[i])
3. buy2 = max(buy2', sell1-prices[i])
4. sell2 = max(sell2’ buy2 + prices[i])
由于如果计算当天的买入卖出收益为0的话,那么buy1', sell1'
可以对应的替换为buy1, sell1
"""
buy1= buy2 = -prices[0]
sell1 = sell2 = 0
for i in range(1, len(prices)):
buy1 = max(buy1, -prices[i])
sell1 = max(sell1, buy1+prices[i])
buy2 = max(buy2, sell1-prices[i])
sell2 = max(sell2, buy2+prices[i])
return sell2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
动态规划
I. 定义dp
dp[i][0|1][0|1|2]
dp[i][0][0] 没有任何操作过
dp[i][0][1]
未持仓,卖出过一支股票
可能时今天卖的,也可能昨天就已经卖了
max(dp[i-1][0][1], dp[i-1][1][0] + prices[i])
dp[i][0][2]
未持仓,卖出过两支
可能是今天卖的,也可能是之前卖的
max(dp[i-1][0][2], dp[i][1][1]+prices[i])
dp[i][1][0]
持仓, 可能是之前持仓,可能是当日持仓
max(dp[i-1][1][0], dp[i-1][0][0] - prices[i])
dp[i][1][1]
持仓,可能是之前持仓,也可能是当日持仓
max(dp[i-1][1][1], dp[i][1][0] - prices[i])
dp[i][1][2] : 不可能,最多有两次交易
II.初始化
"""
#结束时的最高利润=[天数][是否持有股票][卖出次数]
dp = [[[0, 0, 0], [0, 0, 0]] for _ in range(len(prices))]
print(dp)
dp[0][0][0] = 0 #什么都没做,最大收益为0
# 买入
dp[0][1][0] = -prices[0]
# 卖出过
dp[0][1][1] = float("-inf")
dp[0][0][1] = float("-inf")
dp[0][0][2] = float("-inf")
for i in range(1, len(prices)):
dp[i][0][0] = 0
dp[i][0][1] = max(dp[i-1][0][1], dp[i-1][1][0] + prices[i])
dp[i][0][2] = max(dp[i-1][0][2], dp[i-1][1][1] + prices[i])
dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0] - prices[i])
dp[i][1][1] = max(dp[i-1][1][1], dp[i-1][0][1] - prices[i])
return max(dp[-1][0][1], dp[-1][0][2], 0)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Make sure to add code blocks to your code group
# 股票买入最佳时机IV
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格,和一个整型 k 。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。 示例 2:
输入:k = 2, prices = [3,2,6,5,0,3] 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。 随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
"""
解法一:
dp[i][j][0]
表示第 i 天交易完,j 次交易(卖出)股票后,手上 没有 股票时的累计最大利润;
dp[i][j][1]
表示第 i 天交易完,j 次交易(买入)股票后,手上 持有 股票时的累计最大利润。
dp[i][0][0]
无操作
dp[i][0][1] 第0次买入后,手上持股的最大利润
max(dp[i-1][0][1], dp[i-1][0][0] - prices[i])
dp[i][1][1] 第1次买入后, 手上持股的最大利润
可能是前一天的买入,或者是当天买入
max(dp[i-1][1][1], dp[i-1][1][0] - prices[i])
dp[i][2][1] 第2次买入后, 手上持股的最大利润
可能是前一天的买入,或者是当天买入
max(dp[i-1][2][1], dp[i-1][2][0] - prices[i])
dp[i][k][1] 第k次买入后,手上持股的最大利润
max(dp[i-1][k][1], dp[i-1][k][0] - prices[i])
dp[i][1][0] 第1次卖出后手上不持股
可能是前一天卖出,也可能是当天卖出
max(dp[i-1][1][0], dp[i-1][0][1] + prices[i])
dp[i][2][0] 第2次卖出后手上不持股
可能是前一天卖出,也可能是当天卖出
max(dp[i-1][2][0], dp[i-1][1][1] + prices[i])
dp[i][3][0] 第3次卖出后手上不持股
可能是前一天卖出, 也可能是当天卖出
max(dp[i-1][3][0], dp[i-1][2][1] + prices[i])
dp[i][k][0] 第k次卖出后手上不持股
max(dp[i-1][k][0], dp[i-1][k-1][1] + prices[i])
"""
def maxProfit(self, k: int, prices: List[int]) -> int:
n = len(prices)
if n<2 or k==0:
return 0
k = min(k, n//2) # n 天最多只能进行 n/2 笔交易
dp = [[[0]*2 for j in range(k+1)] for _ in range(n)]
# 将不合理的初始状态统统设为一个较大的负值
for j in range(k+1):
dp[0][j][1] = float('-inf')
dp[0][j][0] = float('-inf')
# 合理的初始化
dp[0][0][0] = 0
dp[0][1][1] = -prices[0]
# 状态转移
for i in range(1, n):
for j in range(1, k+1):
dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i])
# j次买卖股票后的最大值即为最大利润
return max(dp[n-1][j][0] for j in range(k+1))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
"""
buy = max(buy', sell' - prices)
sell = max(sell, buy' + prices)
其中buy’ sell' 代表前一天买入和卖出的状态,
由于当天如果买入又卖出,收益为0所以 可以得到
buy[i] = max(buy[i], sell(i-1) - prices[i])
sell[i] = max(sell[i], buy[i] + prices[i])
"""
k = min(len(prices)//2, k)
buy = [[0]*(k+1) for _ in range(len(prices))]
sell = [[0]*(k+1) for _ in range(len(prices))]
buy[0][0], sell[0][0] = -prices[0], 0
for i in range(1, k+1):
buy[0][i] = sell[0][i] = float("-inf")
for i in range(1, len(prices)):
buy[i][0] = max(buy[i - 1][0], sell[i - 1][0] - prices[i])
for j in range(1, k+1):
buy[i][j] = \
max(buy[i-1][j], sell[i-1][j] - prices[i])
sell[i][j]= \
max(sell[i - 1][j], buy[i - 1][j - 1] + prices[i])
return max(sell[-1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Make sure to add code blocks to your code group
# 最佳股票买卖时机含冷冻期
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [1,2,3,0,2] 输出: 3 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] 示例 2:
输入: prices = [1] 输出: 0
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
最大交易次数 len(prices)//2
dp[i][j]
dp[i][0] : 第i天结束,手上持有一支股票
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
dp[i][1]: 第i天结束,处于冷冻期, 也就是说当天卖出了股票
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
dp[i][2]: 第i天结束,手上没有股票
dp[i][2] = max(dp[i-1][2], dp[i-1][0] + prices[i])
"""
dp = [[0, 0, 0] for _ in range(len(prices))]
dp[0][0] = -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
dp[i][1] = dp[i-1][0] + prices[i]
dp[i][2] = max(dp[i-1][1], dp[i-1][2])
return max(dp[-1][1], dp[-1][2])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
最大交易次数 len(prices)//2
dp[i][j]
dp[i][0] : 第i天结束,手上持有一支股票
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
dp[i][1]: 第i天结束,处于冷冻期, 也就是说当天卖出了股票
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
dp[i][2]: 第i天结束,手上没有股票
dp[i][2] = max(dp[i-1][2], dp[i-1][0] + prices[i])
"""
# 解法一
# dp = [[0, 0, 0] for _ in range(len(prices))]
# dp[0][0] = -prices[0]
# for i in range(1, len(prices)):
# dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
# dp[i][1] = dp[i-1][0] + prices[i]
# dp[i][2] = max(dp[i-1][1], dp[i-1][2])
# return max(dp[-1][1], dp[-1][2])
# 优化解法一的空间
dp0, dp1, dp2 = -prices[0],0, 0
for i in range(1, len(prices)):
newdp0, newdp1, newdp2 = dp0, dp1, dp2
newdp0 = max(dp0, dp2 - prices[i])
newdp1 = dp0 + prices[i]
newdp2 = max(dp1, dp2)
dp0, dp1, dp2 = newdp0, newdp1, newdp2
return max(dp1, dp2)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Make sure to add code blocks to your code group
# 买卖股票的最佳时机含手续费
给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2 输出:8 解释:能够达到的最大利润:
在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8 示例 2:输入:prices = [1,3,7,5,10,3], fee = 3 输出:6
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
"""
动态规划
dp[i][0] 表示到第i天为止,手里没有股票的最大收益
dp[i][1] 表示到第i天为止,手里有股票的最大收益
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]-fee)
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
"""
dp = [[0, 0] for _ in range(len(prices))]
dp[0][0] = 0
dp[0][1] = -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee)
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
return dp[-1][0]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
"""
动态规划
dp[i][0] 表示到第i天为止,手里没有股票的最大收益
dp[i][1] 表示到第i天为止,手里有股票的最大收益
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]-fee)
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
"""
# # 解法一
# dp = [[0, 0] for _ in range(len(prices))]
# dp[0][0] = 0
# dp[0][1] = -prices[0]
# for i in range(1, len(prices)):
# dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee)
# dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
# return dp[-1][0]
# 解法二: 在解法二的基础上优化空间
# 由于上面dp[i][0]只和dp[i-1]的状态有关,所以使用变量来记录i-1时的最大收益
buy, sell = -prices[0], 0
# buy手里持有股票的最大收益,sell 表示手里没有股票的最大收益
for i in range(1, len(prices)):
newbuy = max(buy, sell - prices[i])
newsell = max(sell, buy+prices[i]- fee)
buy, sell = newbuy, newsell
return sell
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Make sure to add code blocks to your code group
# 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
示例 1:
输入: s = "A man, a plan, a canal: Panama" 输出:true 解释:"amanaplanacanalpanama" 是回文串。 示例 2:
输入:s = "race a car" 输出:false 解释:"raceacar" 不是回文串。 示例 3:
输入:s = " " 输出:true 解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。 由于空字符串正着反着读都一样,所以是回文串。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/valid-palindrome 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def isPalindrome(self, s: str) -> bool:
"""
双指针法
"""
s= "".join(ch for ch in s if ch.isalnum()).lower()
left, right = 0, len(s) -1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/longest-consecutive-sequence 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
set1 = set(nums)
res = 0
for num in nums:
if num -1 not in set1:
tmp = 1
while num +1 in set1:
tmp += 1
num += 1
res = max(res, tmp)
return res
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
hashdict = dict()
res = 0
for num in nums:
if num not in hashdict:
left = hashdict.get(num-1, 0)
right = hashdict.get(num+1, 0)
# 记录长度
hashdict[num] = right + left +1
hashdict[num - left] = right + left +1
hashdict[num + right] = right + left + 1
res = max(res, left + right +1)
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 求根节点到叶节点数字之和
给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字:
例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3] 输出:25 解释: 从根到叶子节点路径 1->2 代表数字 12 从根到叶子节点路径 1->3 代表数字 13 因此,数字总和 = 12 + 13 = 25 示例 2:
输入:root = [4,9,0,5,1] 输出:1026 解释: 从根到叶子节点路径 4->9->5 代表数字 495 从根到叶子节点路径 4->9->1 代表数字 491 从根到叶子节点路径 4->0 代表数字 40 因此,数字总和 = 495 + 491 + 40 = 1026
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/sum-root-to-leaf-numbers 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sumNumbers(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
res = 0
queue = collections.deque([[root, root.val]])
while queue:
node, num = queue.popleft()
if not node.left and not node.right:
res += num
if node.left:
queue.append([node.left, num*10 + node.left.val])
if node.right:
queue.append([node.left, num*10 + node.right.val])
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Make sure to add code blocks to your code group
# 分割回文串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = "aab" 输出:[["a","a","b"],["aa","b"]] 示例 2:
输入:s = "a" 输出:[["a"]]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/palindrome-partitioning 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def partition(self, s: str) -> List[List[str]]:
"""
回溯算法试试
"""
ret = list()
def backtracking(startindex, path):
if startindex >= len(s):
ret.append(path[:])
return
for i in range(startindex, len(s)):
tmp = s[startindex: i+1]
# 判断被截取的是否为回文字串
if tmp == tmp[::-1]:
path.append(tmp)
backtracking(i+1, path)
path.pop()
else:
continue
backtracking(0, [])
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make sure to add code blocks to your code group
# 加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2] 输出: 3 解释: 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。 因此,3 可为起始索引。 示例 2:
输入: gas = [2,3,4], cost = [3,4,3] 输出: -1 解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。 我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油 开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油 开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油 你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/gas-station 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
"""
贪心算法
先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,
说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。
每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,
说明[0, i]区间都不能作为起始位置,
(这里是因为前面能到每个节点都可能有余油,
一支累加到这都到不了下一个节点,
那么0-i 从任意区间开始累加的余油一定更小,所以更不可能到达)
因为这个区间选择任何一个位置作为起点,到i这里都会断油,
那么起始位置从i+1算起,再从0计算curSum。
"""
start = 0
curent_sum = 0
total_sum = 0
for i in range(len(gas)):
curent_sum += gas[i] - cost[i]
total_sum += gas[i] -cost[i]
if curent_sum < 0:
start = i+1
curent_sum = 0
if total_sum <0:
return -1
if curent_sum>=0:
return start
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Make sure to add code blocks to your code group
# 分发糖果
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
示例 1:
输入:ratings = [1,0,2] 输出:5 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。 示例 2:
输入:ratings = [1,2,2] 输出:4 解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/candy 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def candy(self, ratings: List[int]) -> int:
"""
给右孩子评分更高的分得更多的糖果,如果不是增加的分给一个
给左孩子评分更高的分得更多的糖果,比较+1结果和原结果的大小,如果大则更新,否则不变
"""
list1 = list()
for i in range(len(ratings)):
if i > 0 and ratings[i] > ratings[i-1]:
list1.append(list1[-1] + 1)
else:
list1.append(1)
for j in range(len(ratings)-1, -1, -1):
if j-1>=0 and ratings[j] < ratings[j-1]:
list1[j-1] = max(list1[j]+1, list1[j-1])
return sum(list1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Make sure to add code blocks to your code group
# 只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1] 输出:1 示例 2 :
输入:nums = [4,1,2,1,2] 输出:4 示例 3 :
输入:nums = [1] 输出:1
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/single-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
"""
使用集合或者hashdict,由于额外的空间复杂度是O(n),所以不符合题意
这里给出set的解法
遍历列表,如果不在set则加入,如果在set则删除,最后set中的数字就是出现一次的
"""
set1 = set()
for num in nums:
if num in set1:
set1.remove(num)
else:
set1.add(num)
return set1.pop()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def singleNumber(self, nums: List[int]) -> int:
"""
使用位运算, 异或的特质 a ^ 0 = a # 相同为0 ,不同为1
a^a = 0
"""
ret = 0
for num in nums:
ret = ret^num
return ret
2
3
4
5
6
7
8
9
10
// Make sure to add code blocks to your code group
# 只出现一次的数字II
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且不使用额外空间来解决此问题。
示例 1:
输入:nums = [2,2,3,2] 输出:3 示例 2:
输入:nums = [0,1,0,1,0,1,99] 输出:99
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/single-number-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
hashdict = collections.defaultdict(int)
for num in nums:
hashdict[num] +=1
print(hashdict)
for k,v in hashdict.items():
if v == 1:
return k
2
3
4
5
6
7
8
9
// Make sure to add code blocks to your code group
# 背包问题
[背包问题合集python] 你的背包,背到现在还没烂 - 单词拆分 - 力扣(LeetCode) (opens new window)
# 单词拆分
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。 示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"] 输出: true 解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。 注意,你可以重复使用字典中的单词。 示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出: false
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/word-break 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
@functools.cache
def dfs(s: str, start: int):
if start == len(s):
return True
for i in range(start, len(s)):
if s[start: i + 1] in wordDict and dfs(s, i + 1):
return True
return False
return dfs(s, 0)
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
"""
动态规划
dp[i] 表示在下表为i处可以由字典中的单词组成
dp[i] = 最近=True的下标+1:i在字典中
"""
# dp[0]表示""可以被表示
dp = [True] + [False] * len(s)
for i in range(len(s)):
for j in range(i+1, len(s)+1):
if dp[i] and s[i:j] in wordDict:
dp[j] = True
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 单词差分II
给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意:词典中的同一个单词可能在分段中被重复使用多次。
示例 1:
输入:s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"] 输出:["cats and dog","cat sand dog"] 示例 2:
输入:s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"] 输出:["pine apple pen apple","pineapple pen apple","pine applepen apple"] 解释: 注意你可以重复使用字典中的单词。 示例 3:
输入:s = "catsandog", wordDict = ["cats","dog","sand","and","cat"] 输出:[]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/word-break-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
"""
回溯法来解决
"""
ret = []
path = list()
wordSet = set(wordDict)
def backtracking(startindex, path):
if startindex >= len(s):
ret.append(" ".join(path[:]))
# ret.append(path[:])
for i in range(startindex, len(s)):
if s[startindex:i+1] in wordDict:
path.append(s[startindex:i+1])
backtracking(i+1, path)
path.pop()
else:
continue
backtracking(0, path)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2:
输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3:
输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/linked-list-cycle 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
"""
记录每个节点的val和next, 如果访问过则说明有环
"""
readSet = set() # (val,nextnode)
while head:
tmp = (head, head.next)
if tmp in readSet:
return True
else:
readSet.add((head, head.next))
head = head.next
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
"""
方法一:快慢指针,如果是环形, 总有一刻,快慢指针会相遇
"""
if not head or not head.next:
return False
slow = fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 环形链表II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2:
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/linked-list-cycle-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
"""
3 2 0 4 7 -1 3 2
| 长度为a | 环形长度为b |
设快指针走过的路程为f 慢指针走过的路程为s
可以知道:
f = 2s (快指针是慢指针的两倍)
f = s + nb (f -s = nb得到,由于相遇的话,一定是快指针比慢多走了n圈)
进一步两式相减得到:
s = nb
f = 2nb
在想,所有经过入口节点的步数为k = a + kb
(a为初次到环形的时, 此后每跑一圈经过一次入口)
由于s 已经走了nb步,因此只要再走a步就时入口处,此时让一个指针从头走a 步,慢指针同样走a步,两者重合时就是入口
"""
# 写法一
# if not head:
# return None
# slow = fast = head
# while fast.next and fast.next.next:
# slow = slow.next
# fast = fast.next.next
# if slow is fast:
# break
# if not fast.next or not fast.next.next:
# return None
# fast = head
# while fast:
# if fast is slow:
# return slow
# fast = fast.next
# slow = slow.next
# 写法二:
if not head:
return None
slow = fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
fast = head
while slow is not fast:
slow = slow.next
fast = fast.next
return slow
return None
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Make sure to add code blocks to your code group
# 重排链表
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4] 输出:[1,4,2,3] 示例 2:
输入:head = [1,2,3,4,5] 输出:[1,5,2,4,3]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/reorder-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
"""
Do not return anything, modify head in-place instead.
"""
if not head:
return []
stack = list()
cur = head
while cur:
stack.append(cur)
cur = cur.next
middle = (len(stack) -1)//2
cur = head
while middle:
tmp = stack.pop()
tmp.next = cur.next
cur.next = tmp
cur = cur.next.next
middle -= 1
stack.pop().next = None
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Make sure to add code blocks to your code group
# 二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3] 输出:[1,2,3] 示例 2:
输入:root = [] 输出:[] 示例 3:
输入:root = [1] 输出:[1] 示例 4:
输入:root = [1,2] 输出:[1,2] 示例 5:
输入:root = [1,null,2] 输出:[1,2]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-tree-preorder-traversal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
"""
迭代法
"""
ret, stack = [], []
cur = root
while cur or stack:
while cur:
ret.append(cur.val)
stack.append(cur)
cur = cur.left
cur = stack.pop()
cur = cur.right
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def pre_order(self, root, ret=[]):
"""
递归法
"""
if not root:
return ret
ret.append(root.val)
self.pre_order(root.left, ret)
self.pre_order(root.right, ret)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
# LRU缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入 ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 输出 [null, null, null, 1, null, -1, null, -1, 3, 4]
解释 LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // 缓存是 {1=1} lRUCache.put(2, 2); // 缓存是 {1=1, 2=2} lRUCache.get(1); // 返回 1 lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3} lRUCache.get(2); // 返回 -1 (未找到) lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} lRUCache.get(1); // 返回 -1 (未找到) lRUCache.get(3); // 返回 3 lRUCache.get(4); // 返回 4
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/lru-cache 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cache = dict()
self.head = DLinkedNode
self.tail = DLinkedNode
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key: int) -> int:
node = self.cache.get(key, None)
if not node:
return -1
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.cache:
# 创建新节点加入
node = DLinkedNode(key, value)
self.cache[key] = node
self.addToHead(node)
self.size += 1
# 判断容量是否超过,如果超了,从尾部弹出
if self.size > self.capacity:
removed = self.moveTail()
self.cache.pop(removed.key)
self.size -= 1
else:
node = self.cache.get(key)
node.value = value
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
def moveToHead(self, node):
"""
a.next--->b.next---> c.next ----> d
<----b.prev <---- c.prev <---- d.prev
"""
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
self.addToHead(node)
def moveTail(self):
node = self.tail.prev
self.removeNode(node)
return node
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Make sure to add code blocks to your code group
# 对链表进行插入排序
给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。 重复直到所有输入数据插入完为止。 下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
示例 1:
输入: head = [4,2,1,3] 输出: [1,2,3,4] 示例 2:
输入: head = [-1,5,3,4,0] 输出: [-1,0,3,4,5]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/insertion-sort-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
"""
以第一个为 有序的基准,取未排序的node.val 与有序的进行比较找到合适的位置
"""
if not head or not head.next:
return head
dummy = ListNode(next=head)
lastSorted = head
cur = head.next
while cur:
if lastSorted.val <= cur.val:
lastSorted = lastSorted.next
else:
prev = dummy
while prev.next.val <= cur.val:
prev = prev.next
lastSorted.next = cur.next
cur.next = prev.next
prev.next = cur
cur = lastSorted.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Make sure to add code blocks to your code group
# 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3] 输出:[1,2,3,4] 示例 2:
输入:head = [-1,5,3,4,0] 输出:[-1,0,3,4,5] 示例 3:
输入:head = [] 输出:[]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/sort-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
"""
冒泡、插入、O(n**2)
归并排序,快排,堆排序
"""
def sortedFunc(head, tail):
if not head:
return head
if head.next == tail:
head.next = None
return head
fast = slow = head
while fast != tail:
slow = slow.next
fast = fast.next
if fast != tail:
fast = fast.next
mid = slow
left = sortedFunc(head, mid)
right = sortedFunc(mid, tail)
# print(left, right)
return merage(left, right)
def merage(head1, head2):
# print(head1, head2)
dummyHead = ListNode(0)
temp, temp1, temp2 = dummyHead, head1, head2
while temp1 and temp2:
if temp1.val <= temp2.val:
temp.next = temp1
temp1 = temp1.next
else:
temp.next = temp2
temp2 = temp2.next
temp = temp.next
if temp1:
temp.next = temp1
elif temp2:
temp.next = temp2
return dummyHead.next
return sortedFunc(head, None)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Make sure to add code blocks to your code group
# 逆波兰表达式求值
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
有效的算符为 '+'、'-'、'*' 和 '/' 。 每个操作数(运算对象)都可以是一个整数或者另一个表达式。 两个整数之间的除法总是 向零截断 。 表达式中不含除零运算。 输入是一个根据逆波兰表示法表示的算术表达式。 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9 示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6 示例 3:
输入:tokens = ["10","6","9","3","+","-11","","/","","17","+","5","+"] 输出:22 解释:该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
set1 = {"+", "-", "*", "/"}
stack = list()
for ch in tokens:
if ch not in set1:
stack.append(int(ch))
else:
num1 = stack.pop()
num2 = stack.pop()
if ch == "/":
temp = int(num2/num1)
elif ch == "*":
temp = num1*num2
elif ch == "+":
temp = num1 + num2
else:
temp = num2 - num1
stack.append(temp)
return stack[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 反转字符串中的单词
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue" 输出:"blue is sky the" 示例 2:
输入:s = " hello world " 输出:"world hello" 解释:反转后的字符串中不能存在前导空格和尾随空格。 示例 3:
输入:s = "a good example" 输出:"example good a" 解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/reverse-words-in-a-string 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def reverseWords(self, s: str) -> str:
"""
双指针确定每个单词的边界
"""
s = s.strip(" ")
res = list()
start, end = len(s)-1, len(s) -1
while start >=0:
# 找到首个单词的尾部指针开始寻找起始指针
while start>=0 and s[start] != " ":
start -= 1
res.append(s[start+1:end+1])
# 跳过空格
while start >=0 and s[start] == " ": # 指针位于前一个单词的结尾
start -= 1
end = start # end指向向下个单词的尾字符
return " ".join(res)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
# 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。 示例 2:
输入: nums = [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/maximum-product-subarray 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def maxProduct(self, nums: List[int]) -> int:
"""
动态规划
和最大子树组的和类似
最大子数组和解法
dp[i]: 在i位置的最大和为dp[i]
但是在本题中,由于相乘会有负负得正的情况,所以 上述dp[i]并不是最优解
思路:
同时记录最大解和最小解,比较
最大解*nums[i], 最小解*nums[i], nums[i]的大小
可以得到状态转移方程:
dp_min[i] = min(dp_min[i-1]* nums[i], dp_max[i-1]*nums[i],nums[i])
dp_max[i] = max(dp_max[i-1]* nums[i], dp_min[i-1]*nums[i], nums[i])
ans = max(ans, dp_max[i])
"""
dp_min = [1] * (len(nums) + 1)
dp_max = [1] * (len(nums) + 1)
ans = float("-inf")
for i in range(1, len(nums)+1):
dp_max[i] = max(dp_max[i-1]* nums[i-1], dp_min[i-1]*nums[i-1], nums[i-1])
dp_min[i] = min(dp_max[i-1]* nums[i-1], dp_min[i-1]*nums[i-1], nums[i-1])
ans = max(dp_max[i], ans)
return ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
# 寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到: 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2] 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7] 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2] 输出:1 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。 示例 2:
输入:nums = [4,5,6,7,0,1,2] 输出:0 解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。 示例 3:
输入:nums = [11,13,15,17] 输出:11 解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def findMin(self, nums: List[int]) -> int:
"""
二分查找
"""
left, right = 0, len(nums)-1
while left < right:
mid = (left + right) //2
if nums[mid] > nums[right]:
left = mid + 1
else:
right = mid
return nums[left]
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 寻找旋转排序数组中的最小值II
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到: 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4] 若旋转 7 次,则可以得到 [0,1,4,4,5,6,7] 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须尽可能减少整个过程的操作步骤。
示例 1:
输入:nums = [1,3,5] 输出:1 示例 2:
输入:nums = [2,2,2,0,1] 输出:0
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def findMin(self, nums: List[int]) -> int:
"""
二分法
"""
n = len(nums)
left, right = 0, n-1
while left < right:
mid = (left + right) // 2
if nums[mid] < nums[right]:
right = mid
elif nums[mid] > nums[right]:
left = mid + 1
else:
right -= 1
return nums[right]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Make sure to add code blocks to your code group
# 相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0 listA - 第一个链表 listB - 第二个链表 skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数 skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数 评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 — 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。
示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Intersected at '2' 解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。 在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。 示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。 由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 这两个链表不相交,因此返回 null 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/intersection-of-two-linked-lists 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
curA, curB = headA, headB
while curA != curB:
curA = curA.next if curA else curB
curB = curB.next if curB else curA
return curA
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Make sure to add code blocks to your code group
# 寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
示例 1:
输入:nums = [1,2,3,1] 输出:2 解释:3 是峰值元素,你的函数应该返回其索引 2。 示例 2:
输入:nums = [1,2,1,3,5,6,4] 输出:1 或 5 解释:你的函数可以返回索引 1,其峰值元素为 2; 或者返回索引 5, 其峰值元素为 6。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/find-peak-element 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
if len(nums) == 1:
return 0
while left < right:
mid = (left + right)//2
if nums[mid] < nums[mid+1]:
left = mid +1
else:
right = mid
return left
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 比较版本号
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 除此之外返回 0。
示例 1:
输入:version1 = "1.01", version2 = "1.001" 输出:0 解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1" 示例 2:
输入:version1 = "1.0", version2 = "1.0.0" 输出:0 解释:version1 没有指定下标为 2 的修订号,即视为 "0" 示例 3:
输入:version1 = "0.1", version2 = "1.1" 输出:-1 解释:version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 。0 < 1,所以 version1 < version2
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/compare-version-numbers 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
listVsersion1 = version1.split(".")
listVsersion2 = version2.split(".")
m, n = len(listVsersion1)-1, len(listVsersion2)-1
m1 = n1 = 0
while m1 <= m or n1 <= n:
num1 = 0 if m1 >m else int(listVsersion1[m1])
num2 = 0 if n1 >n else int(listVsersion2[n1])
if num1 > num2:
return 1
elif num1 < num2:
return -1
else:
m1 += 1
n1 += 1
return 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 分数到小数
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。
如果小数部分为循环小数,则将循环的部分括在括号内。
如果存在多个答案,只需返回 任意一个 。
对于所有给定的输入,保证 答案字符串的长度小于 104 。
示例 1:
输入:numerator = 1, denominator = 2 输出:"0.5" 示例 2:
输入:numerator = 2, denominator = 1 输出:"2" 示例 3:
输入:numerator = 4, denominator = 333 输出:"0.(012)"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/fraction-to-recurring-decimal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def fractionToDecimal(self, numerator: int, denominator: int) -> str:
"""
如果时循环小数,那么某个余数一定在循环出现
"""
if numerator % denominator == 0:
return str(int(numerator*1.0 // denominator))
ret = list()
# 判断正负号
if (numerator ^ denominator)<0:
ret.append("-")
numerator = abs(numerator)
denominator = abs(denominator)
# 获取整数部分
ret.append(str(numerator // denominator))
ret.append(".")
remainder = numerator % denominator
# 小数部分
#是有限还是循环,模拟出发
index_dict = dict()
while remainder and remainder not in index_dict:
index_dict[remainder] = len(ret)
remainder *= 10
ret.append(str(remainder // denominator))
remainder %= denominator
if remainder:
insertIndex = index_dict[remainder]
ret.insert(insertIndex, "(")
ret.append(")")
return "".join(ret)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Make sure to add code blocks to your code group
# 两数之和II-输入有序数组
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9 输出:[1,2] 解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。 示例 2:
输入:numbers = [2,3,4], target = 6 输出:[1,3] 解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。 示例 3:
输入:numbers = [-1,0], target = -1 输出:[1,2] 解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
"""
二分法可以优化时间
双指针法也可以
"""
left, right = 0, len(numbers)-1
while left < right:
if numbers[left] + numbers[right] == target:
return [left+1, right+1]
elif numbers[left] + numbers[right] < target:
left += 1
else:
right -= 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Make sure to add code blocks to your code group
# Excel表列名称
给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。
例如:
A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ...
示例 1:
输入:columnNumber = 1 输出:"A" 示例 2:
输入:columnNumber = 28 输出:"AB" 示例 3:
输入:columnNumber = 701 输出:"ZY" 示例 4:
输入:columnNumber = 2147483647 输出:"FXSHRXW"
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/excel-sheet-column-title 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def convertToTitle(self, columnNumber: int) -> str:
"""
看成是26进制 (0-25)
"""
ret = list()
ret = list()
while columnNumber>0:
columnNumber -= 1
cur = columnNumber % 26
AS = cur + ord("A")
ret.append((chr(AS)))
columnNumber //= 26
return "".join(ret[::-1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Make sure to add code blocks to your code group
# 多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3] 输出:3 示例 2:
输入:nums = [2,2,1,1,1,2,2] 输出:2
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/majority-element 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
import collections
dict1 = collections.defaultdict(int)
for num in nums:
dict1[num] +=1
ans = (float("-inf"), 0)
for key, val in dict1.items():
if val > ans[1]:
ans = (key, val)
return ans[0]
2
3
4
5
6
7
8
9
10
11
// Make sure to add code blocks to your code group
# Excel表序列号
给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。
例如:
A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ...
示例 1:
输入: columnTitle = "A" 输出: 1 示例 2:
输入: columnTitle = "AB" 输出: 28 示例 3:
输入: columnTitle = "ZY" 输出: 701
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/excel-sheet-column-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def titleToNumber(self, columnTitle: str) -> int:
"满25进1 0- 25"
ans = 0
multiple = 1
for i in range(len(columnTitle)-1, -1, -1):
k = ord(columnTitle[i]) - ord("A") + 1
ans += k * multiple
multiple *= 26
return ans
2
3
4
5
6
7
8
9
10
11
// Make sure to add code blocks to your code group
# 阶乘后的零
给定一个整数 n ,返回 n! 结果中尾随零的数量。
提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
示例 1:
输入:n = 3 输出:0 解释:3! = 6 ,不含尾随 0 示例 2:
输入:n = 5 输出:1 解释:5! = 120 ,有一个尾随 0 示例 3:
输入:n = 0 输出:0
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/factorial-trailing-zeroes 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def trailingZeroes(self, n: int) -> int:
"""
尾随有零, 存在2*5=10会生成0
所以简化为求 2*5出现的次数
又因为每两次出现一个2的倍数
每五次出现一个5的倍数
所以需统计5出现的次数就行
"""
ans = 0
for i in range(5, n+1, 5):
while i % 5 == 0:
ans += 1
i /= 5
return ans
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Make sure to add code blocks to your code group
# 二叉搜索树迭代器
实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器: BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。 boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。 int next()将指针向右移动,然后返回指针处的数字。 注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
示例:
输入 ["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"] [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []] 输出 [null, 3, 7, true, 9, true, 15, true, 20, false]
解释 BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]); bSTIterator.next(); // 返回 3 bSTIterator.next(); // 返回 7 bSTIterator.hasNext(); // 返回 True bSTIterator.next(); // 返回 9 bSTIterator.hasNext(); // 返回 True bSTIterator.next(); // 返回 15 bSTIterator.hasNext(); // 返回 True bSTIterator.next(); // 返回 20 bSTIterator.hasNext(); // 返回 False
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/binary-search-tree-iterator 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class BSTIterator:
def __init__(self, root: Optional[TreeNode]):
self.stack = list()
self.cur = root
def next(self) -> int:
"""
二叉树的中序遍历
"""
while self.cur:
self.stack.append(self.cur)
self.cur = self.cur.left
self.cur = self.stack.pop()
ret = self.cur.val
self.cur = self.cur.right
return ret
def hasNext(self) -> bool:
return self.cur is not None or len(self.stack) > 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
# 重复的DNA序列
DNA序列 由一系列核苷酸组成,缩写为 'A', 'C', 'G' 和 'T'.。
例如,"ACGAATTCCG" 是一个 DNA序列 。 在研究 DNA 时,识别 DNA 中的重复序列非常有用。
给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。
示例 1:
输入:s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT" 输出:["AAAAACCCCC","CCCCCAAAAA"] 示例 2:
输入:s = "AAAAAAAAAAAAA" 输出:["AAAAAAAAAA"]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/repeated-dna-sequences 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def findRepeatedDnaSequences(self, s: str) -> List[str]:
"""
hashmap + 滑动窗口法
"""
import collections
dict1 = collections.defaultdict(int)
ret = []
for i in range(len(s)-9):
key = s[i:i+10]
dict1[key] +=1
if dict1[key] == 2:
ret.append(key)
return ret
2
3
4
5
6
7
8
9
10
11
12
13
14
// Make sure to add code blocks to your code group
# 轮转数组
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4] 示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/rotate-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
每轮转len(nums)是一个周期, 除以每个周期,看余数是多少
"""
if len(nums) <=1 or k ==0:
return nums
n = len(nums)
k = k%n
nums[:] = nums[n-k:n] + nums[0:n-k]
return nums
2
3
4
5
6
7
8
9
10
11
12
13
// Make sure to add code blocks to your code group
# 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
1
2
3
4示例 2:
输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
1
2
3
4
class Solution:
def rob(self, nums: List[int]) -> int:
"""
动态规划确定如何偷窃才能利益最大化
dp[i] 偷盗i房间的最大金额
# 要么偷这个房间,要么不偷这个房间
# 如果偷这个房间,那么前一个房间没有取过
# 如果不偷这个房间,取决于前一天
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
"""
if len(nums) == 0:
return 0
if len(nums) == 1:
return nums[0]
dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i-2] + nums[i], dp[i-1])
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 岛屿数量
给你一个由
'1'
(陆地)和'0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1
1
2
3
4
5
6
7示例 2:
输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3
1
2
3
4
5
6
7
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
self.m, self.n = len(grid), len(grid[0])
self.grid = grid
self.visited = [[False] * self.n for i in range(self.m)]
self.num = 0
self.directions = [0, -1], [0, 1], [-1, 0], [1, 0]
for i in range(self.m):
for j in range(self.n):
if not self.visited[i][j] \
and self.grid[i][j] == "1":
self.bfs(i, j)
self.num +=1
return self.num
def bfs(self, i, j):
from collections import deque
queue = deque()
queue.append((i, j))
self.visited[i][j] = True
while (len(queue) !=0):
a, b = queue.popleft()
for d in self.directions:
new_a = a + d[0]
new_b = b + d[1]
if 0 <=new_a < self.m \
and 0 <=new_b<self.n \
and not self.visited[new_a][new_b] \
and self.grid[new_a][new_b] == "1":
queue.append((new_a, new_b))
self.visited[new_a][new_b] = True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Make sure to add code blocks to your code group
###快乐数
编写一个算法来判断一个数
n
是不是快乐数。「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果
n
是 快乐数 就返回true
;不是,则返回false
。示例 1:
输入:n = 19 输出:true 解释: 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1
1
2
3
4
5
6
7示例 2:
输入:n = 2 输出:false
1
2
class Solution:
def isHappy(self, n: int) -> bool:
def nex(n):
return sum(int(c)**2 for c in str(n))
slow, fast = n, nex(n)
while slow != fast:
slow = nex(slow)
fast = nex(nex(fast))
return slow == 1
2
3
4
5
6
7
8
9
10
11
12
13
// Make sure to add code blocks to your code group
# 移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
1
2示例 2:
输入:head = [], val = 1 输出:[]
1
2示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
1
2
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
cur = head
prev = dummy
while cur:
if cur.val == val:
prev.next = cur.next
else:
prev = cur
cur = cur.next
return dummy.next
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
# 动态规划问题
dp 数组的定义以及下标的含义dp[i][j]
递推公式
dp数组如何初始化
遍历顺序
打印dp数组
# 动态规划解决斐波那契数
dp数组下标以及含义dp[i], 第i个斐波那契数,dp[i]表示第i个斐波那契数值
递推公式dp[i] = dp[i-1] + dp[i-1]
初始化:dp[0] = 1 dp[1] = 1
遍历顺序:从前向后
打印dp数组
def fibonc(n):
dp = [1, 1]
#dp[0], dp[1] = 1, 1
if n ==0 or n ==1:
return 1
i = 2
while i <=n:
dp.append(dp[i-1] + dp[i-2])
i+=1
return dp[i-1]
if __name__ == "__main__":
fibonc(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 动态规划解决爬楼梯问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1阶 1
2阶 2
3阶 3
4阶 5
动归五部曲
- dp[i] 表示上到第i阶的所有方法
- 递推公式: dp[i] = dp[i-1] + dp[i-2]
- 初始化:dp[0] = 1, dp[1]=1, dp[2] = 2
- 确定遍历顺序: 从头向后
- 打印dp数组
class Solution(object):
def numWays(self, n):
"""
:type n: int
:rtype: int
"""
# 解法一
# 暴力解法, 比较耗时,不推荐
# def dfs (x):
# if x ==0:
# return 1
# if x ==1:
# return 1
# if x == 2:
# return 2
# return dfs(x-1) + dfs(x-2)
# return dfs(n)
# 解法二 动态规划 (由递归,取消递的过程,只保留归的过程)
# 公式 dp[i] = dp[i-1] + dp[i-2]
def solution(n):
dp = [1, 2]
if n == 0:
return 1
if n ==1 or n ==2:
return 1 if n==1 else 2
i = 2
while i <= n:
dp.append(dp[i-1] + dp[i-2])
i+=1
return dp[n-1] % 1000000007
return solution(n)
# 解法三: 只保留前两次的数据, 通过前两次计算最后一次
def fibo(x):
# if x ==0:
# return 1
# if x ==1:
# return 1
if x < 2:
return 1
a, b, = 1, 1
i = 2
while i <= n:
a, b = a+b, a
i += 1
return a
return fibo(n) % 1000000007
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 解法一: 暴力解法
// func numWays(n int) int {
// return dfs(n) %1000000007
// }
// func dfs( x int)int{
// if x == 0{
// return 1
// }
// if x == 1{
// return 1
// }
// if x ==2{
// return 2
// }
// return dfs(x-1) + dfs(x-2)
// }
//解法二:动态规划
// func numWays(n int) int {
// if n < 2 {
// return 1
// }
// dp := make([]int, n+1)
// dp[0], dp[1] = 1, 1
// for i:=2; i<=n;i++{
// dp[i] = (dp[i-1] +dp[i-2])% 1000000007
// }
// return dp[n]
// }
//解法三:
func numWays(n int) int {
if n < 2{
return 1
}
const d = 1e9+7
a, b := 1, 1
for i :=2;i<=n;i++{
a, b = (a+b)%d, a
}
return a
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 爬楼梯变型一
n阶台阶,一次可以上1, ,2, 3 ,到达第n阶有几种方法
此时状态方程为dp[i]=dp[i-
1] + dp[i-2] + dp[i-3] , 进一步转换dp[i] = dp[i] + dp[j], j in [1, 2, 3]`
dp[i] = dp[i] + dp[j] 由来是因为初始化的时候dp[i] 都为0
def solution(n):
steps = [1, 2, 3]
dp = [0] * n
dp[0] = 1
dp[1] = 1
for i in range(1, n):
for j in range(len(steps)):
step = steps[i]
if i < step:
continue
dp[i] = dp[i] + dp[j]
return dp[n]
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def waysToStep(self, n: int) -> int:
if n <= 2:
return n
elif n == 3:
return 4
dp = [0] + [0] * n
dp[0] = 0
dp[1] = 1
dp[2] = 2
dp[3] = 4
for i in range(4, n+1):
dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
dp[i] = dp[i] % 1000000007
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func waysToStep(n int) int {
if n <=2{
return n
} else if n == 3 {
return 4
}
dp := make([]int, n+1)
// steps := []{1, 2, 3}
dp[0] = 0
dp[1] = 1
dp[2] = 2
dp[3] = 4
for i:= 4;i<n+1;i++ {
dp[i] = dp[i] + dp[i-1] + dp[i-2] + dp[i-3]
dp[i] %= 1000000007
}
return dp[n]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
# 爬楼梯变型二
n阶台阶,一次可以上1, 2, 3 ,并且相邻的步伐不能相同,到达第n阶有几种方法
class Solution:
def climbStairs(self, n: int) -> int:
"""
dp[i][j] 通过j步跳到i台阶处的方法
dp[i][1] 通过一步到达i台阶
dp[i][2] 通过两部到达i台阶
dp[i][3] 通过三步到达i台阶
"""
dp = [[0] * 3 for _ in range(n)]
dp[1][1] = 1
dp[1][2] = 0
dp[1][3] = 0
dp[2][1] = 1
dp[2][2] = 1
dp[2][3] = 0
dp[3][1] = 3
dp[3][2] = 1
dp[3][3] = 1
if n < 3:
return dp[n-1][1] + dp[n-1][2] + dp[n-1] +3
for i in range(4, 0):
dp[i][1] = dp[i][0] + dp[i-1][1] + dp[i-1][2]
dp[i][2] = dp[i][1] + dp[i-2][1] + dp[i-2][2]
dp[i][3] = dp[i][3] + dp[i-3][1] + dp[i-3][2]
return dp[n-1][1] + dp[n-2][2] L+ dp[n-3][3]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
# 爬楼梯变型三
n阶台阶,一次可以上1, 2 ,并且不能连续跳两步,到达第n阶有几种方法
class Solution:
def climbStairs(self, n: int) -> int:
"""
1.每次你可以爬 1 或 2 个台阶。 2.不能连续跳两个台阶
dp[i][j]: 通过跳j步到达台阶处的方法
dp[i][1] = dp[i-1][1] + dp[i-1][2]
dp[i][2] = dp[i-2][1] + dp[i-2][2]
dp[i][j] = dp[i][1] + dp[j][2]
"""
dp = [[0, 0, 0] for _ in range(n+1)]
dp[1][1] = 1
dp[1][2] = 0
if n <=1:
return dp[n][0] + dp[n][1]
dp[2][1] = 1
dp[2][2] = 1
for i in range(2, n):
dp[i][1] = dp[i-1][1] + dp[i-1][2]
dp[i][2] = dp[i-2][1]
return dp[n][1] + dp[n][2]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Make sure to add code blocks to your code group
# 爬楼梯变型四
n阶台阶,一次可以上1, 2 ,并且不能在第五层跳,到达第n阶有几种方法
// Make sure to add code blocks to your code group
# 零钱兑换II-leetcode 518
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
由于题目要求时求组合数,所以要先遍历物品,再遍历背包, 如果先遍历背包再遍历物品是排列问题
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
"""
dp 当第个时的组合数
dp[i] = dp[i] + dp[i-1]
类似于上n 阶台阶, 每次能上[1, 3, 4, 5,]阶,由此推导过来
"""
dp = [0] * (amount+1)
dp[0] = 1
for conin in coins:
for i in range(conin, amount+1):
dp[i] += dp[i-conin]
print(dp)
return dp[-1]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 零钱兑换-leetcode 322
后续补充
# 不同路径问题 - leetcode 62
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# dp[i][j] 在ij位置处的所有路径
# dp[i][j] = dp[i-1][j] + dp[i][j-1]
dp = [[1]* n ] + [[1] + [0]*(n-1) for _ in range(m-1)] # m行 n 列
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
2
3
4
5
6
7
8
9
10
11
12
13
# 动态规划解决 0-1背包问题
0-1背包, n种物品,每种物品只有一个
完全背包,n种物品,每种物品有无限个
多重背包,n种物品,每种物品个数各不相同
01背包
背包问题含义是N个物品,容量V背包,每件物品仅用一次
有 N件物品和一个容量是 V的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
动归五部曲:
定义dp数组以及含义dp[i][j] 0-i之间的物品任取, 放进容量为j的背包里
递推公式: 不放物品i dp[i-1][j] 放物品i dp[i-1][j-weigh(i)]+ value[i]
初始化dp数组,
重量 | 价值 | |
---|---|---|
物品0 | ||
物品1 | ||
物品2 |
def solution():
"""
设定dp数组: dp[i][j] 第i个物品放入容量为j的背包中的最大价值
推导公式: 当v[i]>j 不放 的dp[i][j] = dp[i-1][j]
当v[i]<j, 放入,max(dp[i][j] = dp[i-1][j-v[i]] + w[i], dp[i][j]=dp[i-1][j])
初始化dp数组:当容量为0时,不论物品,最大价值为0
当物品为0时,不论背包容量,最大价值为0
"""
c = 10
w = [3, 4, 5, 7]
v = [1, 5, 6, 9]
n = len(w)
# 初始化dp数组, n * c 的数组
dp = [[0]*(c+1) for _ in range(n+1)]
w.insert(0, 0)
v.insert(0, 0)
print(dp)
for i in range(1, n+1): # 物体编号
for j in range(1, c+1): # 背包容量
if w[i] <= j:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
else:
dp[i][j] = dp[i-1][j]
print(dp[n][c])
if __name__ == "__main__":
solution()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 动态规划解决矩阵最小路径之和
给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6,1,0就是最小路径和,返回12.
def solution(in_matrix):
"""
定义dp数组; dp[i][j] 表示从出发点(0,0) 到(i, j)的最短路径为dp[i][j]
推到公式: 向下走 dp[i][j] = min(dp[i-1][j] + , dp[i][j-11]) + p[i][j]
初始化dp数组: 当上面为边界时, 值只能从左边来, 当做为边界时,值只能从上边来
循环方向: 从小到大
"""
m = len(in_matrix) # 行数
n = len(in_matrix[0]) # 列数
for mm in range(1, m): # 上边界
in_matrix[mm][0] += in_matrix[mm-1][0]
for nn in range(1, n): # 右边界
in_matrix[0][nn] += in_matrix[0][nn-1]
for i in range(1, m):
for j in range(1, n):
in_matrix[i][j] = min(in_matrix[i-1][j], in_matrix[i][j-1]) + in_matrix[i][j]
return in_matrix[m-1][n-1]
if __name__ == "__main__":
p = [
[1,3,1],
[1,5,1],
[4,2,1]
]
print(solution(p))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 动态规划解决最小路径和经过的路径
后续补充
# 动态规划解决最长回文字串问题
给你一个字符串
s
,找到s
中最长的回文子串
- 回文串,去除头尾依旧是回文串
动归五步曲
- d[i][j] 表示下标为i, 和下标为j 是否为回文字串
- 递推公式 d[i][j] = True , 则d[i+1][j-1] =True & s[i] = s[j]
- 初始化对角线元素(即元素相同时,一定事回文串)
- 遍历顺序,由i+1 是下一行,j-1是上一列,所以i,j 的状态转移依赖做下,所以填表要按照列填
- 打印dp
def solution(s):
"""
推到公式:dp[i][j] = True <====> s[i] == s[j] and (dp[i+1][j-1]= True or j-i <3)
初始化dp数组: 全部初始化为false, 对角线初始化为true
循环方向:因为dp[i][j] 依赖于左下的元素,所以循环方向 从小到大, 从列到行
"""
if len(s) <2:
return len(s)
# 初始化数组,对角线为True, 其他都为False
begin = 0
max_len = 0
dp = [[False] *len(s) for _ in range(len(s))]
for i in range(len(s)):
dp[i][i] = True
print(dp)
# 循环
for j in range(1, len(s)):
for i in range(0, i):
if s[i] == s[j]:
if j-i<3:
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j-1]
else:
dp[i][j] = False
current = j-i+1
if dp[i][j] and current > max_len:
max_len = current
begin = i
return s[begin: begin+max_len]
if __name__ == "__main__":
p = "babab"
print(solution(p), "结果")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 动态规划解决分割数组问题
给你一个 只包含正整数 的 非空 数组
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
该题等价于, 给你len(nums) 物品,每次从nums中取一个数,是否恰好存在取到的数字和恰好等于数组和的一一半。0-1背包问题
动归五步曲:
- 定义dp数组 dp[i][j], 表示从数组的0-ii下标中选取若干个数,是否存在一种选取方案,使得被选取数之和恰好等于j
初始时,dp中的所有元素都为false
递推公式: 对于nums[i] 如果选取 dp[i, j] = dp[i-1][j], 如果不选去 dp[i][j] = dp[i-1][j-nums[i]]
初始化 寻找边界 考虑,如果不取任何数,则被选取的正整数=0, 所以对于所有的d[i][0] = True, 当i == 0时, 只有nums[0]被选取,dp[0][nums[0]] = true
确定打印顺序
待补充
1
# 剪枝技巧总结
总结不完整,后续重新总结
一般对数组有要求,必须是有序数组,这点需要保证
标志位 子集II是个例子, 使用标志位used[i]= True or Fasle 进行同层剪枝,使得用过的元素不会重复使用
规定起始点
- 可以重复选择同一个元素,只需要更新起始点就可以了,使得起始点> 上一次使用的索引就可以了。例如leetcode 39
- 不可以使用重复元素,需要不断更新起始点才可以完成剪枝,例如leetcode 子集
跨层剪枝: 全排列ii, 必须传递标志位
# 回溯法
回溯算法时一种纯暴力的算法,通常用来解决组合、切割、子集、排列、棋盘、N皇后问题等
回溯算法和递归是相辅相成的,回溯的步骤通常在递归下面
回溯三部曲:
- 确定递归的参数和返回值
- 终止条件
- 确定单词递归的逻辑
# 组合问题
# 组合问题-leetcode77
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合
改变起始点剪枝
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
result = list()
path = list()
def backtracking(path, depth, startindex):
if depth == k:
result.append(path[:])
return
for i in range(startindex, n+1):
# 同层之间为[1, n], 起始点每次加1 所以同层肯定不会重复
path.append(i)
backtracking(path, depth +1, i +1) # 数[1, n]为不重复的数字,所以 i+1 坑定不会重复
path.pop()
backtracking(path, 0, 1)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 组合总和I(无重复元素,同一元素可以多次选取)leetcode 39
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
改变起始点剪枝, 同一元素多次拿取, 剪枝时需要包含自身
class Solution:
"""
注意: 每次向后取,所以要记住上一次的起始位置,所以有startindex
backtracking(path, depath +1, i, total_num) , 应为可以重复放置元素,所以此时startindex为i, 不需要+1
"""
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
result = list()
path = list()
def backtracking(path, depath, startindex, total_num):
if total_num > target:
return
if total_num == target:
result.append(path[:])
return
for i in range(startindex, len(candidates)):
total_num += candidates[i]
path.append(candidates[i])
backtracking(path, depath +1, i, total_num)
path.pop()
total_num -= candidates[i]
backtracking(path, 0,0, 0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 组合总和II (有重复元素, 同一元素不可多次选取)-letcode 40
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
改变起始点, 同层重复元素剪枝
class Solution:
"""
注意: 1. 给定的candidates 有重复的元素, 题目要求解集不能包好重复的元素,此时需要对原来的数组排序,然后通过判断i-1 进行剪枝,防止重复
"""
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
result = list()
path = list()
candidates.sort()
def backtracking(startindex, total_num):
if target == total_num:
result.append(path[:])
return
if total_num > target:
return
for i in range(startindex, len(candidates)):
if i > startindex and candidates[i] == candidates[i-1]:
continue
path.append(candidates[i])
total_num += candidates[i]
backtracking(i+1, total_num)
path.pop()
total_num -= candidates[i]
backtracking(0, 0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 组合总和III(无重复, 每个元素不可多次选取)-leetcode 216
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
改变起始位置进行剪枝
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
result = list()
path = list()
def backtracking(startindex, total_num):
if total_num > n or len(path) > k:
return
elif total_num == n and len(path) == k:
result.append(path[:])
return
for i in range(startindex, 10):
path.append(i)
total_num += i
backtracking(i+1, total_num)
path.pop()
total_num -= i
backtracking(1, 0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 分割问题
# 回溯法解决分割回文串(for 循环里切分字符串)-leetcode 131
给你一个字符串
s
,请你将s
分割成一些子串,使每个子串都是 回文串 。返回s
所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
改变起始位置,依据回文串的特点剪枝,注意需要切割
class Solution:
def partition(self, s: str) -> List[List[str]]:
result = list()
path = list()
def backtracking(startindex, path):
if startindex == len(s):
result.append(path[:])
return
for i in range(startindex, len(s)):
ch = s[startindex: i+1]
if ch == ch[::-1]:
path.append(s[startindex: i+1])
backtracking(i+1, path)
path.pop()
else:
continue
backtracking(0, path)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 拆分字符串使唯一子字符串的数目最大 - leetcode 1593
给你一个字符串 s ,请你拆分该字符串,并返回拆分后唯一子字符串的最大数目。
字符串 s 拆分后可以得到若干 非空子字符串 ,这些子字符串连接后应当能够还原为原字符串。但是拆分出来的每个子字符串都必须是 唯一的 。
注意:子字符串 是字符串中的一个连续字符序列。
class Solution:
max_num = 0
def maxUniqueSplit(self, s: str) -> int:
path = list()
result = list()
def backtracking(startindex, path):
if startindex == len(s):
result.append(path[:])
if self.max_num < len(path):
self.max_num = len(path)
return
for i in range(startindex, len(s)):
ch = s[startindex: i+1]
if ch in path:
continue
# if i + 1 == len(s) -1 and len(path) + 1 < max_len:
# continue
path.append(ch)
backtracking(i+1, path)
path.pop()
backtracking(0, path)
return self.max_num
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 单词拆分(困难leetcode140)
后续补充
# 子集问题
# 子集-leetcode 78
给你一个整数数组
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = []
path = list()
def backtracing(startindex, path):
result.append(path[:])
for i in range(startindex, len(nums)):
path.append(nums[i])
backtracing(i +1, path)
path.pop()
backtracing(0, path)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 子集II-leetcode 90 (同层通过排序后和前一个对比进行剪枝)
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result = list()
tmp = list()
nums.sort()
def backtracking(startindex):
result.append(tmp[:])
for i in range(startindex, len(nums)):
if i>startindex and nums[i] == nums[i-1]:
continue
tmp.append(nums[i])
backtracking(i+1)
tmp.pop()
backtracking(0)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 递增子序列 (同层通过set剪枝)-leetcode 491
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况
class Solution:
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
result = list()
path = list()
def backtracing(startindex, path):
if len(path) >=2:
result.append(path[:])
repeat = set()
for i in range(startindex, len(nums)):
if len(path) > 0 and path[-1] > nums[i]:
continue
if nums[i] not in repeat:
path.append(nums[i])
repeat.add(nums[i])
backtracing(i+1, path)
path.pop()
# repeat.remove(nums[i])
backtracing(0, path)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 排列问题
# 全排列(回溯法吧标准模板)-leetcode 46
给定一个不含重复数字的数组
nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案
全排列问题,循环从0开始到len(nums)结束, 通过维护在每一层每个元素的使用情况来进行剪枝
不包含重复元素
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
result = list()
path = list()
nums.sort()
def backtracking(path, depth, used):
if depth == len(nums):
result.append(path[:])
return
for i in range(0, len(nums)):
if used[i]:
continue
used[i] = True
path.append(nums[i])
backtracking(path, depth +1, used)
used[i] = False
path.pop()
backtracking(path, 0, {index_: False for index_, _ in enumerate(nums)})
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 全排列II-leetcode 47
给定一个可包含重复数字的序列
nums
,按任意顺序 返回所有不重复的全排列。
通过维护每个元素使用情况,对同层元素进行剪枝,避免同层多次选取,由于给定的元素有可能有重复,需要通过排序和下标状态进行剪枝
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums.sort()
result = list()
path = list()
def backtracking(path, used):
if len(path) == len(nums):
result.append(path[:])
return
for i in range(0, len(nums)):
if used.get(i, False):
continue
if i > 0 and nums[i] == nums[i-1] and not used.get(i-1, False):
continue
path.append(nums[i])
used[i] = True
backtracking(path, used)
path.pop()
used[i] = False
backtracking(path, { k: False for k in range(0, len(nums))})
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 字符串的排列-剑指offer38
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
class Solution:
def permutation(self, s: str) -> List[str]:
result = list()
path = list()
tmp = [ss for ss in s]
tmp.sort()
s = "".join(tmp)
def backtracing(startindex, depth, path, used):
if len(path) == len(s):
result.append("".join(path))
return
for i in range(0, len(s)):
if i >0 and s[i] == s[i-1] and not used[i-1]:
continue
if used[i]:
continue
used[i] = True
path.append(s[i])
backtracing(i+1, depth+1, path, used)
path.pop()
used[i] = False
backtracing(0 , 0, path, {index_: False for index_, _ in enumerate(s)})
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 优美的排列-leetcode 526
假设有从 1 到 n 的 n 个整数。用这些整数构造一个数组 perm(下标从 1 开始),只要满足下述条件 之一 ,该数组就是一个 优美的排列 :
perm[i] 能够被 i 整除 i 能够被 perm[i] 整除 给你一个整数 n ,返回可以构造的 优美排列 的 数量 。
class Solution:
def countArrangement(self, n: int) -> int:
result = list()
path = list()
def backtracking(path, used):
if len(path) == n:
result.append(path[:])
return
for i in range(1, n +1):
if used.get(i, False):
continue
path.append(i)
if path[-1] % len(path)==0 or len(path) % path[-1] == 0:
used[i] = True
backtracking(path, used)
path.pop()
used[i] = False
backtracking(path, {})
print(result)
return len(result)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 棋盘问题
# N皇后I -leetcode 51
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
if not n: return []
board = [['.'] * n for _ in range(n)]
res = []
def isVaild(board,row, col):
#判断同一列是否冲突
for i in range(len(board)):
if board[i][col] == 'Q':
return False
# 判断左上角是否冲突
i = row -1
j = col -1
while i>=0 and j>=0:
if board[i][j] == 'Q':
return False
i -= 1
j -= 1
# 判断右上角是否冲突
i = row - 1
j = col + 1
while i>=0 and j < len(board):
if board[i][j] == 'Q':
return False
i -= 1
j += 1
return True
def backtracking(board, row, n):
# 如果走到最后一行,说明已经找到一个解
if row == n:
temp_res = []
for temp in board:
temp_str = "".join(temp)
temp_res.append(temp_str)
res.append(temp_res)
for col in range(n):
if not isVaild(board, row, col):
continue
board[row][col] = 'Q'
backtracking(board, row+1, n)
board[row][col] = '.'
backtracking(board, 0, n)
return res
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
if not n:
return []
board = [["."]*n for _ in range(n)] #初始化棋盘
res = []
# 判断该点是否可以放置Q
def isValid(board, row, col):
# 判断同一列是否满足
for i in range(len(board)):
if board[i][col] == "Q":
return False
# 判断左上角是否满足
i = row -1
j = col -1
while i >=0 and j >=0:
if board[i][j] == "Q":
return False
i -= 1
j -= 1
# 判断右上是否满足
i = row -1
j = col +1
while i >=0 and j< len(board):
if board[i][j] == "Q":
return False
i -= 1
j += 1
return True
def backtracing(board, row, n):
if row == n:
tmp_res = list()
for temp in board:
tmp_res.append("".join(temp))
res.append(tmp_res[:])
return
for col in range(n):
if not isValid(board, row, col):
continue
board[row][col] = "Q"
backtracing(board, row+1, n)
board[row][col] = "."
backtracing(board, 0, n)
return res
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# N皇后II-leetcode 52
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
本质和N皇后I一样,只不过这个时需要求个数
class Solution:
num = 0
def totalNQueens(self, n: int) -> int:
if not n:
return self.num
board = [["."] * n for _ in range(n)]
def isValid(board, row, col):
"""
判断该点是否可以放置
"""
# 判断每一列
for i in range(n):
if board[i][col] == "Q":
return False
# 判断左上
i, j = row-1, col-1
while i >=0 and j>=0:
if board[i][j] == "Q":
return False
i -= 1
j -= 1
# 判断右上
i, j = row -1, col + 1
while i >=0 and j <n:
if board[i][j] == "Q":
return False
i -= 1
j += 1
return True
def backtracing(board, row, n):
if row == n:
self.num += 1
for col in range(n):
if not isValid(board, row, col):
continue
board[row][col] = "Q"
backtracing(board, row+1, n)
board[row][col] = "."
backtracing(board, 0, n)
return self.num
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 贪心问题
# 分发饼干-leetcode 455
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
"""
给胃口值为g[i]的孩子分配大小为s[j]的饼干,如果饼干过小,那么寻找下一块饼干,直到找到为止
"""
g.sort()
s.sort()
num = 0
i = j = count = 0
while i < len(g) and j <len(s):
while j < len(s) and g[i] > s[j]:
j += 1
if j < len(s):
count += 1
j += 1
i += 1
return count
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 分发糖果-leetcode 135
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
class Solution:
def candy(self, ratings: List[int]) -> int:
"""
相邻分为左相邻和有相邻
左相邻规则, 如果i < i+1, 则grade[i+1] = grade[i] +1, 否则, 分配一颗糖果
右相邻规则:
"""
nums = [1] * len(ratings)
for i in range(len(ratings) -1):
if ratings[i] < ratings[i+1]:
nums[i+1] = nums[i] +1
for i in range(-1, -len(ratings)+1-1, -1):
if ratings[i] < ratings[i-1]:
nums[i-1] = max(nums[i]+1, nums[i-1])
return sum(nums)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 无重叠区间-leetcode 435
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
"""
在选择要保留区间时,区间的结尾十分重要:选择的区间结尾越小,余留给其他区间的空间就越大,就能保留更多的区间。因此,我们采取的贪心策略为,优先保留结尾小且不相交的区间
"""
intervals = sorted(intervals, key = lambda x: (x[1],x[0]))
print(intervals)
i = del_num = 0
temp = list()
while i < len(intervals)-1:
if i == 0:
temp.append(intervals[0])
pre_end = temp[-1][1]
nex_start=intervals[i+1][0]
if nex_start < pre_end:
del_num +=1
i + 1
else:
temp.append(intervals[i+1])
i += 1
return del_num
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 跳跃游戏-leetcode 55
class Solution:
def canJump(self, nums: List[int]) -> bool:
"""
由于每层最多可以跳A[i]步,也可以跳0步或1步,因此如果能到达最高层,则说明每一层都可以到达。有了这个条件,说明可以用贪心算法
正向,从0出发,一层一层往上跳,看到最后能不能超过最高层,能超过则说明能到达,否则不能到达
"""
"""
0 1 2 3 4
[ 2, 3, 1, 1, 4]
"""
# 维护最远下标
last_index = 0
for i in range(len(nums)):
if i <= last_index:
last_index = max(last_index, i + nums[i])
if last_index >= len(nums) -1:
return True
return False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 跳跃游戏II-leetcode 45
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
class Solution:
# def jump(self, nums: List[int]) -> int:
# """
# 定义dp数组 dp[i] 表示在i 位置处的最小跳跃次数
# 递推公式: dp[i]= min(dp[i], dp[j] +1)
# 动态规划会超时
# """
# size = len(nums)
# dp = [float("inf") for _ in range(size)]
# dp[0] = 0
# for i in range(1, size):
# for j in range(i):
# if j + nums[j] >= i:
# dp[i] = min(dp[i], dp[j] + 1)
# return dp[size - 1]
def jump(self, nums: List[int]) -> int:
"""
我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1
每次跳尽可能的远,通过更新最大覆盖范围来看是否达到终点,记录跳跃次数
"""
if len(nums) <=1:
return 0
end = 0 # 记录上次能跳到的最远位置
max_pos = 0
steps = 0
for i in range(len(nums)-1):
if i <= max_pos: # 如果在覆盖范围内,标记最大的覆盖范围
max_pos = max(i+nums[i], max_pos)
if i == end: # 需要跳一部, 更新,最远位置
steps += 1
end = max_pos
return steps
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
func jump(nums []int) int {
// 维护当前能到达的边界,如果到达边界,steps +1, 同时更新边界
step := 0
n := len(nums)
max_pos := 0
end := 0
for i:=0;i <n-1; i++ { // 最后一个不用跳,看前面的是否能跳到这个位置
if i <= max_pos{ //记录下次最大的覆盖范围
if max_pos < i + nums[i]{
max_pos = i + nums[i]
}
if i == end { // 走到本次的边界
step += 1
end = max_pos
}
}
}
return step
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make sure to add code blocks to your code group
# 股票买卖的最佳时机-leetcode 121
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
假设当天为最高点卖出,只要直到前面的最低点,就可以算法当前的最大利润
"""
min_p = 0
max_val = 0
for i in range(len(prices)):
if i == 0:
min_p = prices[i]
continue
max_val = max(max_val, prices[i] - min_p)
min_p = min(min_p, prices[i])
return max_val
2
3
4
5
6
7
8
9
10
11
12
13
14
# 股票买卖的最佳时机II-leetcode 122
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
"""
找到每一个上坡,相加就是最终的结果
"""
ans = 0
for i in range(len(prices)-1):
if prices[i+1] >prices[i]:
ans += prices[i+1] - prices[i]
return ans
2
3
4
5
6
7
8
9
10