day1
1. 合并两个有序链表(中等)
1.1 题目描述
将两个升序链表合并为一个新的 **升序** 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
1.2 算法思路
- Input: list1,list2
- output list
-
要求:list 为 list1 和 list2 合并 后的有序数组
-
思路:
-
存在空链表,返回另一个非空链表
-
不存在空链表,执行下列操作
-
创建节点指针 h1,h2 分别指向链表list1,list2的表头,以及空节点prehead和指向该空节点的指针pre,prehead 为合并链表的头节点。
-
比较 节点h1 和 h2 的大小,pre与值较小的节点相连,然后被选中的节点指针后移
-
继续上一步,直至 某一链表的元素全部被加入到新链表中,即被合并。
-
返回prehead.next
-
-
1.3 代码
#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]:
#保证链表都不为空
if not list1:
return list2
if not list2:
return list1
#
h1 = list1
h2 = list2
prehead = ListNode()
pre = prehead
while h1 and h2:
if h1.val<=h2.val:
pre.next = h1
pre = pre.next
h1=h1.next
else:
pre.next = h2
pre = pre.next
h2 = h2.next
if not h1:
pre.next = h2
else:
pre.next = h1
return prehead.next
2.括号生成
2.1 题目描述
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且** 有效的 **括号组合。
2.2 算法思路
- Input : n:int
- Output: list[]
- 该问题可抽象为深度优先遍历一颗以‘(’为根,且‘(’与‘)’的数目一致其等于n的树
2.3 代码
from typing import List
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
cur_str = ''
def dfs(cur_str, left, right):
"""
:param cur_str: 从根结点到叶子结点的路径字符串
:param left: 左括号还可以使用的个数
:param right: 右括号还可以使用的个数
:return:
"""
if left == 0 and right == 0:
res.append(cur_str)
return
if right < left: # 防止")("错误
return
if left > 0:
dfs(cur_str + '(', left - 1, right)
if right > 0:
dfs(cur_str + ')', left, right - 1)
dfs(cur_str, n, n)
return res
3.合并k个升序链表
3.1 题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。)
3.2 算法思路
- 运用了归并的思想
3.3 代码
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists: return None
n = len(lists) #记录子链表数量
return self.mergeSort(lists, 0, n - 1) #调用归并排序函数
def mergeSort(self, lists: List[ListNode], l: int, r: int) -> ListNode:
if l == r:
return lists[l]
m = (l + r) // 2
L = self.mergeSort(lists, l, m) #循环的递归部分
R = self.mergeSort(lists, m + 1, r)
return self.mergeTwoLists(L, R) #调用两链表合并函数
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1:return l2
if not l2:return l1
dummy = ListNode(0) #构造虚节点
move = dummy #设置移动节点等于虚节点
while l1 and l2: #都不空时
if l1.val < l2.val:
move.next = l1 #移动节点指向数小的链表
l1 = l1.next
else:
move.next = l2
l2 = l2.next
move = move.next
move.next = l1 if l1 else l2 #连接后续非空链表
return dummy.next #虚节点仍在开头
4.两两交换链表中的节点
4.1 题目描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
![在这里插入图片描述](https://img-blog.csdnimg.cn/a1dcadaab0cd4f97bf2f60a70c0fa6f4.png#pic_center)
4.2 算法思路
- Input:list
- Output:list
- 链表元素数量小于2,放回该链表的头节点head
- 链表元素数量大于2
- 创建两个分别指向第一个和第二个元素的指针p1,p2和一个连接头结点的空节点p。
- 置换p1 ,p2所指向的相邻节点
- 更新p1,p2的位置
- 若p1,p2不为空则继续执行置换和更新
- 循环结束,表示链表已经操作完成,放回链表的非空头节点
- 如图所示:
4.3 代码
# 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]:
p = ListNode(0,head)
if head and head.next:
p1 = head
p2 = p1.next
head = head.next
while p1 and p2:
# 置换
p.next = p2
p1.next = p2.next
p2.next = p1
# 更新位置
p = p1
p1 = p1.next
if p1:
p2 = p1.next
return head
day2
1.K个一组翻转链表
1.1 题目描述
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将
最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
1.2算法思路
- 链表分区为已翻转部分+待翻转部分+未翻转部分
- 每次翻转前,要确定翻转链表的范围,这个必须通过 k 此循环来确定
- 需记录翻转链表前驱和后继,方便翻转完成后把已翻转部分和未翻转部分连接起来
- 初始需要两个变量 pre 和 end,pre 代表待翻转链表的前驱,end 代表待翻转链表的末尾
- 经过k此循环,end 到达末尾,记录待翻转链表的后继 next = end.next
- 翻转链表,然后将三部分链表连接起来,然后重置 pre 和 end 指针,然后进入下一次循环
- 特殊情况,当翻转部分长度不足 k 时,在定位 end 完成后,end==null,已经到达末尾,说明题目已完成,直接返回即可
- 时间复杂度为 O(n∗K) 最好的情况为 O(n) 最差的情况为 O(n**2)
- 空间复杂度为 O(1) 除了几个必须的节点指针外,我们并没有占用其他空间
1.3 代码
class Solution:
# 翻转一个子链表,并且返回新的头与尾
def reverse(self, head: ListNode, tail: ListNode):
prev = tail.next
p = head
while prev != tail:
nex = p.next
p.next = prev
prev = p
p = nex
return tail, head
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
hair = ListNode(0)
hair.next = head
pre = hair
while head:
tail = pre
# 查看剩余部分长度是否大于等于 k
for i in range(k):
tail = tail.next
if not tail:
return hair.next
nex = tail.next
head, tail = self.reverse(head, tail)
# 把子链表重新接回原链表
pre.next = head
tail.next = nex
pre = tail
head = tail.next
return hair.next
2.删除有序数组中的重复项
2.1 题目描述
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,
使每个元素 只出现一次 ,返回删除后数组的新长度。
元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
2.2 算法思路
- 题目中所给的数组有序,故若有复数个相同元素,则它们必相邻。
- 从左至右扫描数组
- 每次扫描都判断当前元素是否扫描过
- 若没有扫描过,将其赋值到数组相应的位置
- 否则,跳过
2.3 代码
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
dic = {}
i = 0
for num in nums:
if num not in dic:
dic.setdefault(num)
nums[i]=num
i+=1
return i
3.移除元素
3.1 题目详情
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
3.2 算法思路
-
将所有等于val的元素移动到首位
:{1,3,5,7,9,7},taget = 5 ->{1,3,7,9,7,5},return 5
步骤:
1.从左到右扫描列表nums
2.发现与目标值相同的元素时,找到该元素后面首个不等于val的元素,与其交换位置
3.循环执行步骤2,直至列表遍历完或者发现某位置之后的元素的值全都等于val时,停止循环
4.放回最后一个非val元素的位置
3.3 代码
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
n = len(nums)
k = 0
for i in range(n):
if nums[i]==val:
for j in range(i,n):
if nums[j]!=val:
break
if nums[j] == val:
return i
else:
t = nums[j]
nums[j] = nums[i]
nums[i] = t
return n
4.找出字符串中第一个匹配项的下标
4.1 题目详情
给你两个字符串 haystack 和 needle ,
请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。
如果 needle 不是 haystack 的一部分,则返回 -1 。
4.2 算法思路
- 设n1,n2 为 haystack 和 needle 的大小
- 易知,n1<n2 时,needle 不可能 在 haystack中
- 当n1>=n2时:
- 找出 haystack 中与needle首字符相同的字符的位置(即,找出以needle[0]开头的子串)
- 逐个匹配子串和needle
- 若出现匹配,返回该索引
- 否则,返回 -1
4.3 代码
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
n1,n2 = len(haystack),len(needle)
if n1<n2:
return -1
ss = []
c1 = needle[0]#首字符
# 找到haystack中所有的以c1为开头的,且长度为n2的字符串
for i in range(n1-n2+1):
if haystack[i]==c1:
ss.append(i)
for j in ss:
if haystack[j:j+n2]==needle:
return j
return -1
day3
1.两数相除
1.1 题目详情
给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。
返回被除数 dividend 除以除数 divisor 得到的 商 。
注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231, 231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。
1.2 算法思路
- 被除数为最小值时
- 除数为1,return 最小值
- 除数为-1,return 最大值
- 除数为最小值时
- 若被除数为最小值,return 1
- 否则,return 0
- 除数为0,return 0
- 设立一个flag,正负数转换
- 正数:取反 ,flag = True
- 负数:flag = False
1.3 代码
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
INT_MIN, INT_MAX = -2**31, 2**31 - 1
# 考虑被除数为最小值的情况
if dividend == INT_MIN:
if divisor == 1:
return INT_MIN
if divisor == -1:
return INT_MAX
# 考虑除数为最小值的情况
if divisor == INT_MIN:
return 1 if dividend == INT_MIN else 0
# 考虑被除数为 0 的情况
if dividend == 0:
return 0
# 一般情况,使用类二分查找
# 将所有的正数取相反数,这样就只需要考虑一种情况
rev = False
if dividend > 0:
dividend = -dividend
rev = not rev
if divisor > 0:
divisor = -divisor
rev = not rev
candidates = [divisor]
# 注意溢出
while candidates[-1] >= dividend - candidates[-1]:
candidates.append(candidates[-1] + candidates[-1])
ans = 0
for i in range(len(candidates) - 1, -1, -1):
if candidates[i] >= dividend:
ans += (1 << i)
dividend -= candidates[i]
return -ans if rev else ans
2.下一个排列
2.1 题目详情
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
2.2 算法思路
具体地,我们这样描述该算法,对于长度为 n 的排列 a:
1. 首先从后向前查找第一个顺序对 (i,i+1),满足 a[i]<a[i+1]。这样「较小数」即为
a[i]。此时 [i+1,n) 必然是下降序列。
2. 如果找到了顺序对,那么在区间 [i+1,n) 中从后向前查找第一个元素 j
满足 a[i]<a[j]。这样「较大数」即为 a[j]。
3. 交换 a[i] 与 a[j],此时可以证明区间 [i+1,n) 必为降序。
我们可以直接使用双指针反转区间 [i+1,n) 使其变为升序,而无需对该区间进行排序。
2.3 代码
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
i = len(nums) - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i >= 0:
j = len(nums) - 1
while j >= 0 and nums[i] >= nums[j]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
left, right = i + 1, len(nums) - 1
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
3.搜索旋转排序数组
3.1 题目详情
整数数组 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) 的算法解决此问题。
3.2 算法思路
二分查找
3.3 代码
class Solution:
def search(self, nums: List[int], target: int) -> int:
def Bin_search(nums,l,r,target):
if l==r and nums[l]!=target:
return -1
mid = (l+r)//2
if nums[mid]==target :
return mid
elif nums[mid]<target:
return Bin_search(nums,mid+1,r,target)
elif nums[mid]>target:
return Bin_search(nums,l,mid,target)
n=len(nums)
if n==1 and nums[0]!=target:
return -1
elif n==1 and nums[0]==target:
return 0
left,right = 0,1
while right<n:
if nums[left]<nums[right]:
left+=1
right+=1
else:
break
if right==n:
return Bin_search(nums,0,n-1,target)
else:
if target== nums[0]:
return 0
elif target>nums[0]:
return Bin_search(nums,0,left,target)
elif target<nums[0]:
return Bin_search(nums,right,len(nums)-1,target)
4.在排序数组中查找元素的第一个和最后一个位置
4.1 题目详情
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
4.2 算法思路
二分查找
4.3 代码
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if len(nums)==0 or target not in nums:
return [-1,-1]
def fistposition():
left = 0
right = len(nums)-1
while(left<right):
mid = (left+right)//2
if target>nums[mid]:
left = mid+1
if target==nums[mid]:
right = mid #注意这里,是right = mid ,因为找初始位置,后面的不管了,用二分法继续找前面的,直到left==right
if target<nums[mid]:
right = mid-1
return left
def lastposition():
left = 0
right = len(nums)-1
while(left<right):
mid = (left+right+1)//2
if target>nums[mid]:
left = mid+1
if target==nums[mid]:
left = mid #这里则相反,只找到后面的,不找前面的,但mid = (left+right+1)//2,这里要+1,因为要往后推1位,如果不加1,实际上就没法推了
if target<nums[mid]:
right = mid-1
return left
leftt = fistposition()
lastt = lastposition()
return [leftt,lastt]
day4
1.搜索插入位置
1.1 题目详情
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
1.2 算法思路
二分查找该元素
1.3 代码
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums)-1
if r<0:
return 0
while(l<r):
mid = (l+r)//2
if nums[mid]==target:
return mid
elif target>nums[mid]:
l = mid+1
else:
r = mid
if nums[l]<target:
return l+1
return l
# if nums[i]==target:
# return i
# elif nums[i]>target and nums[i-1]<target:
# return i
# elif nums[i]<target and nums[i+1]>target:
# return i
2.有效的数独
2.1 题目详情
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
2.2 算法思路
(1)行判断
- 逐行扫描
(2)列判断
- 逐列扫描
(3)九宫格判断
- 对每个九宫格做判断
2.3 代码
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
for i in range(9): #对每一行进行判断
storage = []
for j in range(9):
if board[i][j] == '.':
continue
if board[i][j] in storage:
return False
else:
storage.append(board[i][j])
for i in range(9): #对每一列进行判断
storage = []
for j in range(9):
if board[j][i] == '.':
continue
if board[j][i] in storage:
return False
else:
storage.append(board[j][i])
for i in range(0, 9, 3): #对九宫格是否重复进行判断
for j in range(0, 9, 3):
storage = []
for x in range(0, 3):
for y in range(0, 3):
if board[i + x][j + y] == '.':
continue
if board[i + x][j + y] in storage:
return False
else:
storage.append(board[i + x][j + y])
return True
3.组合总和
3.1 题目详情
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
3.2 算法思路
3.3 代码
from typing import List
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(candidates, begin, size, path, res, target):
if target < 0:
return
if target == 0:
res.append(path)
return
for index in range(begin, size):
dfs(candidates, index, size, path + [candidates[index]], res, target - candidates[index])
size = len(candidates)
if size == 0:
return []
path = []
res = []
dfs(candidates, 0, size, path, res, target)
return res
4.组合总和II
4.1 题目详情
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
4.2 算法思路
”回溯法“
由于我们需要求出所有和为 target的组合,并且每个数只能使用一次,因此我们可以使用递归 + 回溯的方法来解决这个问题:
- 我们用dfs(pos,rest) 表示递归的函数,其中 pos 表示我们当前递归到了数组candidates 中的第 pos 个数,而 rest 表示我们还需要选择和为 rest 的数放入列表作为一个组合;
- 对于当前的第 pos 个数,我们有两种方法:选或者不选。如果我们选了这个数,那么我们调用 dfs(pos+1,rest−candidates[pos]) 进行递归,注意这里必须满足 rest≥candidates[pos]。如果我们不选这个数,那么我们调用 dfs(pos+1,rest) 进行递归;
- 在某次递归开始前,如果 rest 的值为 0,说明我们找到了一个和为 target 的组合,将其放入答案中。每次调用递归函数前,如果我们选了那个数,就需要将其放入列表的末尾,该列表中存储了我们选的所有数。在回溯时,如果我们选了那个数,就要将其从列表的末尾删除。
4.3 代码
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(pos: int, rest: int):
nonlocal sequence
if rest == 0:
ans.append(sequence[:])
return
if pos == len(freq) or rest < freq[pos][0]:
return
dfs(pos + 1, rest)
most = min(rest // freq[pos][0], freq[pos][1])
for i in range(1, most + 1):
sequence.append(freq[pos][0])
dfs(pos + 1, rest - i * freq[pos][0])
sequence = sequence[:-most]
freq = sorted(collections.Counter(candidates).items())
ans = list()
sequence = list()
dfs(0, target)
return ans
day5
1.字符串相乘
1.1 题目详情
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
1.2 算法思路
根据字符的ascall值,计算他的整数形式大小
1.3 代码
class Solution:
def multiply(self, num1: str, num2: str) -> str:
n1 = 0
n2 = 0
for i,c1 in enumerate(num1):
n1+=(ord(c1)-ord('0'))*10**(len(num1)-i-1)
for j,c2 in enumerate(num2):
n2+=(ord(c2)-ord('0'))*10**(len(num2)-j-1)
return "{}".format(n1*n2)
3.跳跃游戏II
3.1 题目详情
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
3.2 算法思路
- 如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
- 例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。
- 从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。
- 在具体的实现中,我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。
- 在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。
3.3 代码
class Solution:
def jump(self, nums: List[int]) -> int:
n = len(nums)
maxPos, end, step = 0, 0, 0
for i in range(n - 1):
if maxPos >= i:
maxPos = max(maxPos, i + nums[i])
if i == end:
end = maxPos
step += 1
return step
4.全排列
4.1 题目详情
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
4.2 算法思路
“回溯”
这个问题可以看作有 n 个排列成一行的空格,我们需要从左往右依此填入题目给定的
n 个数,每个数只能使用一次。那么很直接的可以想到一种穷举的算法,即从左往右每一个位置都依此尝试填入一个数,看能不能填完这
n 个空格,在程序中我们可以用「回溯法」来模拟这个过程。
我们定义递归函数 backtrack(first,output) 表示从左往右填到第 first 个位置,当前排列为 output。 那么整个递归函数分为两个情况:
(1)如果 first=n,说明我们已经填完了 n 个位置(注意下标从 0 开始),找到了一个可行的解,我们将 output 放入答案数组中,递归结束。
(2)如果 first<n,我们要考虑这第 first 个位置我们要填哪个数。根据题目要求我们肯定不能填已经填过的数,因此很容易想到的一个处理手段是我们定义一个标记数组 vis 来标记已经填过的数,那么在填第 first 个数的时候我们遍
n 个数,如果这个数没有被标记过,我们就尝试填入,并将其标记,继续尝试填下一个位置,即调用函数 backtrack(first+1,output)。回溯的时候要撤销这一个位置填的数以及标记,并继续尝试其他没被标记过的数。
4.3 代码
class Solution:
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def backtrack(first = 0):
# 所有数都填完了
if first == n:
res.append(nums[:])
for i in range(first, n):
# 动态维护数组
nums[first], nums[i] = nums[i], nums[first]
# 继续递归填下一个数
backtrack(first + 1)
# 撤销操作
nums[first], nums[i] = nums[i], nums[first]
n = len(nums)
res = []
backtrack()
return res
day6
2.旋转图像
2.1 题目详情
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
2.2 算法思路
由上图中的变化可以看出,旋转前后,坐标之间的映射关系。
为 (i,j) ->(j,n-1-i)
2.3 代码
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
res = [[0]*n for _ in range(n)]
for i in range(n):
for j in range(n):
res[j][n-1-i] = matrix[i][j]
matrix[:] = res
3. 字母异位词分组
3.1 题目详情
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
3.2 算法思路
编码:将一个字符串转换为一个二十四位的元组(每位的数值表示,该字母在字符串中的数量)
比较编码,判断是否是字母异位词
3.3 代码
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
mp = collections.defaultdict(list)
for st in strs:
counts = [0] * 26#"add"->"(1002 0000 0000 0000 0000 000000)"
for ch in st:
counts[ord(ch) - ord("a")] += 1
# 需要将 list 转换成 tuple 才能进行哈希
mp[tuple(counts)].append(st)
return list(mp.values())
4.pow(x,n)
4.1 题目详情
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,x**n )。
4.2 算法思路
思路同归并
4.3 代码
class Solution:
def myPow(self, x: float, n: int) -> float:
# 采用归并的方法,两两相乘,逐层如此
if x==0:
return 0
flag = 1
if n<0 :
flag*=-1
n*=-1
if flag==-1:
return 1/self.mergeSort(n,x)
return self.mergeSort(n,x)
def mergeSort(self,n:int ,x:float) -> float:
#递归出口
if n==0:
return 1
t = self.mergeSort(n//2,x)
if (n%2==1):
return t*t*x
return t*t
day7
1.最后一个单词的长度
1.1 题目详情
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
1.2 算法思路
eg: “hello world”
一个句子的最后一个单词有以下特征:
- 位于句子的最后
- 被空格分割
解决:
1.去句子左右空格,设n为最后一个单词的长度,初始化为0
2.从句子末尾向开头遍历,每次循环n+=1,碰到空格,或者越界就停止。
3.return n
1.3 代码
class Solution:
def lengthOfLastWord(self, s: str) -> int:
s = s.lstrip()
s = s.rstrip()
n = 0
for i in range(1,len(s)+1):
if s[-i] != ' ':
n+=1
else:
break
return n
2.加一
2.1 题目详情
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
2.2 算法思路
数组->数字->数组
2.3 代码
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
res = 0
for i,n in enumerate(digits):
res = res*10+n
res+=1
a = []
while res:
digit = res-res//10*10
res//=10
a.append(digit)
return a[::-1]
3.二进制求和I
3.1 题目详情
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。
3.2 算法思路
二进制字符串->十进制数字->求和->和(十进制)->和(二进制)
3.3 代码
class Solution:
def addBinary(self, a: str, b: str) -> str:
num1 =0
num2 = 0
for i in a:
num1=num1*2+int(i)
for i in b:
num2=num2*2+int(i)
res = num1+num2
if res>0:
ans = ''
while res:
t = res-res//2*2
res//=2
ans+='{}'.format(t)
return ans[::-1]
elif res==0:
return '0'
4.对称二叉树
4.1 题目详情
给你一个二叉树的根节点 root , 检查它是否轴对称。
4.2 算法思路
递归思想:
一颗二叉数是不是镜像
- 两颗子树都是镜像,且左右孩子的值相等 ,True
- 两颗子树不都是镜像 或 左右孩子的值不相等,False
递归出口
- 左右孩子都为空,即为叶节点,True
4.3 代码
# 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 check(self,p:TreeNode,q:TreeNode)->bool:
if not p and not q:
return True
if not p or not q:
return False
return (p.val == q.val) and self.check(p.left,q.right) and self.check(p.right,q.left)
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
return self.check(root,root)
5.k间物品的最大和
5.1 题目详情
袋子中装有一些物品,每个物品上都标记着数字 1 、0 或 -1 。
给你四个非负整数 numOnes 、numZeros 、numNegOnes 和 k 。
袋子最初包含:
numOnes 件标记为 1 的物品。
numZeroes 件标记为 0 的物品。
numNegOnes 件标记为 -1 的物品。
现计划从这些物品中恰好选出 k 件物品。返回所有可行方案中,物品上所标记数字之和的最大值。
5.2 算法思路
按价值高低选择物品。
5.3 代码
class Solution:
def kItemsWithMaximumSum(self, numOnes: int, numZeros: int, numNegOnes: int, k: int) -> int:
sum = 0
for i in range(k):
if numOnes>=1:
sum+=1
numOnes = numOnes-1
continue
if numZeros>=1:
sum+=0
numZeros = numZeros-1
continue
if numNegOnes>=1:
sum-=1
numNegOnes = numNegOnes-1
continue
return sum