菜鸡刷题,是记录自己的写题过程,不具备什么参考价值,有空的话会找优秀的解法,暂时以先做出来为准
目录
- 分治算法
- 双指针
- 无重复的最长子串
- 三数之和
- 接雨水
- 盛最多水的容器
- 最接近的三数之和
- 删除排序数组中的重复项
- 四数之和
- 合并两个有序数组
- 分隔链表
- 删除链表的倒数第N个节点
- 两个数组的交集
- 两数之和 II - 输入有序数组
- 串联所有单词的字串
- 寻找重复数
- 实现Strstr()
- 颜色分类
- 移除元素
- 反转字符串
- 环形链表
- 回文链表
- 移动零
- 长度最小的子数组
- 旋转链表
- 反转字符串中的元音字母
- 环形链表 II
- 验证回文串
- 两个数组的交集 II
- 乘积小于K的子数组
- 通过删除字母匹配到字典里最长单词
- 有序转化数组
- 数组中的K-diff数对
- 字符串的排列
- 至多包含两个不同字符的最长子串
- 较小的三数之和
- 最大连续1的个数 II
- 划分字母区间
- 安排工作以达到最大收益
- 独特字符串
- 比较含退格的字符串
- 数组中的最长山脉
- 救生艇
- 水果成篮
- 三数之和的多种可能
- 长按键入
- 有序数组的平方
- 三个有序数组的交集
- 最大连续1的个数 III
- 大样本统计
- 区间列表的交集
- 统计「优美子数组」
- 和相同的二元子数组
- 未完成的part
分治算法
寻找两个有序数组的中位数
4
我觉得似乎没有用分治法
难点在于题目要求时间复杂度为O(log(m+n))
这类题可以转化为求有序数组第k大/小的问题
…
通过比较两个数组k//2处的数字,可以一下排除一半的查询范围
感谢这位的解法三 参考解法
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
def findKnum(nums1,begin1,m,nums2,begin2,n,k):
if begin1>=m: return nums2[begin2+k-1]
if begin2>=n: return nums1[begin1+k-1]
if k==1:
return min(nums1[begin1],nums2[begin2])
p1 = min(begin1 + k//2 -1,m-1)
p2 = min(begin2 + k//2 -1,n-1)
if nums1[p1]<nums2[p2]:
return findKnum(nums1,p1+1,m,nums2,begin2,n,k-(p1-begin1+1))
else:
return findKnum(nums1,begin1,m,nums2,p2+1,n,k-(p2-begin2+1))
m = len(nums1)
n = len(nums2)
if (m+n)%2==1: return findKnum(nums1,0,m,nums2,0,n,(m+n)//2+1)
else: return (findKnum(nums1,0,m,nums2,0,n,(m+n)//2+1)+findKnum(nums1,0,m,nums2,0,n,(m+n-1)//2+1))/2
合并k个链表
这里分治解法的思想是两两链表合并,时间复杂度为O(Nlogk)
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
def merge2List(l1,l2):
l = ListNode(-1)
p = l
while l1 and l2:
if l1.val<=l2.val:
p.next = l1
l1 = l1.next
else:
p.next = l2
l2 = l2.next
p = p.next
if l1: p.next = l1
if l2: p.next = l2
return l.next
if len(lists)==0:return None
while len(lists)!=1:
lists.append(merge2List(lists.pop(0),lists.pop(0)))
return lists[0]
最大子序和
53
这里分别使用了两种方法:动态规划法和分治法(感谢大神)
分治法的主要思想是最大子序和分为以下三种情况:在左半段里,在右半段里,或者是跨过左右半端,并依次这么分下去,取最大值即可
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# 这里用的动态规划,在返回值的时候请动脑子应该返回啥
n = len(nums)
dp = [0 for i in range(n)]
dp[0] = nums[0]
for i in range(1,n):
dp[i] = max(dp[i-1]+nums[i],nums[i])
return max(dp)
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
size = len(nums)
if size == 0:
return 0
return self.__max_sub_array(nums, 0, size - 1)
def __max_sub_array(self, nums, left, right):
if left == right:
return nums[left]
mid = (left + right) >> 1
return max(self.__max_sub_array(nums, left, mid),
self.__max_sub_array(nums, mid + 1, right),
self.__max_cross_array(nums, left, mid, right))
def __max_cross_array(self, nums, left, mid, right):
# 一定包含 nums[mid] 元素的最大连续子数组的和,
# 思路是看看左边"扩散到底",得到一个最大数,右边"扩散到底"得到一个最大数
# 然后再加上中间数
left_sum_max = 0
start_left = mid - 1
s1 = 0
while start_left >= left:
s1 += nums[start_left]
left_sum_max = max(left_sum_max, s1)
start_left -= 1
right_sum_max = 0
start_right = mid + 1
s2 = 0
while start_right <= right:
s2 += nums[start_right]
right_sum_max = max(right_sum_max, s2)
start_right += 1
return left_sum_max + nums[mid] + right_sum_max
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/maximum-subarray/solution/dong-tai-gui-hua-fen-zhi-fa-python-dai-ma-java-dai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
多数元素
这里的思想是把数组看成左右两部分,如果左右两部分众数一样则整个数组的众数就是该数字,如果不一样,分别计算左右众数在整个数组中的个数,选出最大的
class Solution:
def majorityElement(self, nums: List[int]) -> int:
def findmost(left,right):
if left==right:
return nums[left]
mid = (left+right)>>1
lmost = findmost(left,mid)
rmost = findmost(mid+1,right)
if lmost==rmost: return lmost
else:
ln,rn = 0,0
for i in range(left,right+1):
if nums[i]==lmost: ln += 1
elif nums[i]==rmost: rn += 1
return lmost if ln>rn else rmost
n = len(nums)
return findmost(0,n-1)
数组中的第K个最大元素
215
这里分别使用了两种方法,分治法和堆
分治法用了快排中的思想,先找一个数排到适当的位置,根据它是第几个数判断我们要找的数会在它的左右范围内查找
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def quick_sort(l,r):
pivot = nums[l]
i = l
while l!=r:
while nums[r]<=pivot and l<r:
r -= 1
while nums[l]>=pivot and l<r:
l += 1
if l<r:
nums[l],nums[r] = nums[r],nums[l]
nums[l],nums[i] = pivot,nums[l]
return l
n = len(nums)
l = 0
r = n-1
m = quick_sort(l, r)
while m!=k-1:
if m>k-1:
r = m-1
m = quick_sort(l,r)
else:
l = m+1
m = quick_sort(l,r)
return nums[m]
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def build_heap(i):
smallest = i
l = 2*i + 1
r = 2*i + 2
if l<k and nums[i]>nums[l]:
smallest = l
if r<k and nums[smallest]>nums[r]:
smallest = r
if i!=smallest:
nums[i],nums[smallest] = nums[smallest],nums[i]
build_heap(smallest)
n = len(nums)
for i in range(k-1,n):
for j in range(k-1,-1,-1):
if nums[i]>nums[0]:
nums[0],nums[i] = nums[i],nums[0]
build_heap(j)
return nums[0]
搜索二维矩阵 II
这里的思想是把矩阵分为四个部分,然后如果target小于某个矩阵左上角的值,就排除该矩阵,这里要注意边界条件
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
def search_target(left,right,up,down):
if left>right or up>down or matrix[up][left]>target: return False
if left==right and up==down: return matrix[up][left]==target
if matrix[up][left]==target:return True
cmid = (left+right)>>1
rmid = (up+down)>>1
return search_target(left,cmid,up,rmid) or search_target(cmid+1,right,up,rmid) or search_target(left,cmid,rmid+1,down) or search_target(cmid+1,right,rmid+1,down)
m = len(matrix)
if m==0: return False
n = len(matrix[0])
return search_target(0,n-1,0,m-1)
为运算表达式设计优先级
这里的思想是看到一个运算符,就分别计算它的左右两个部分
class Solution:
def diffWaysToCompute(self, input: str) -> List[int]:
def cal(x,y,op):
if op=='+':
return x+y
elif op=='-':
return x-y
elif op=='*':
return x*y
def multi_calculate(s):
if s.isdigit(): return [int(s)]
res = []
for c in range(len(s)):
if s[c] in ['+','-','*']:
for l in multi_calculate(s[:c]):
for r in multi_calculate(s[c+1:]):
res.append(cal(l,r,s[c]))
return res
return multi_calculate(input)
最接近原点的K个点
分治的思想用于类似快速排序那样,和数组中的第K个最大元素那题类似
class Solution:
def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
def distance(p):
return p[0]**2+p[1]**2
def quick_sort(left,right):
i = left
pivot = points[left]
while left<right:
while distance(pivot)<=distance(points[right]) and left<right:
right -= 1
while distance(pivot)>=distance(points[left]) and left<right:
left += 1
if left!=right:
points[left],points[right] = points[right],points[left]
points[left],points[i] = pivot,points[left]
return left
n = len(points)
if K>=n: return points
if K<=0: return []
l,r = 0,n-1
k = quick_sort(l,r)
while k!=K:
if k>K:
r = k-1
k = quick_sort(l,r)
elif k<K:
l = k+1
k = quick_sort(l, r)
return points[:k]
矩形内船只的数目
这里分治法的思想是把完整的海域分成四个部分,分别判断,如果有船则细分,没有则忽略
# """
# This is Sea's API interface.
# You should not implement it, or speculate about its implementation
# """
# class Sea(object):
# def hasShips(self, topRight: 'Point', bottomLeft: 'Point') -> bool:
# class Point(object):
# def __init__(self, x: int, y: int):
# self.x = x
# self.y = y
class Solution(object):
def countShips(self, sea: 'Sea', topRight: 'Point', bottomLeft: 'Point') -> int:
def find_ship(topRight,bottomLeft):
left,right,up,down = bottomLeft.x,topRight.x,topRight.y,bottomLeft.y
if left>right or up<down or not sea.hasShips(topRight,bottomLeft): return 0
if left==right and up==down: return 1
cmid = (left+right)>>1
rmid = (up+down)>>1
return find_ship(Point(cmid,up),Point(left,rmid+1))+find_ship(topRight,Point(cmid+1,rmid+1))+find_ship(Point(cmid,rmid),bottomLeft)+find_ship(Point(right,rmid),Point(cmid+1,down))
return find_ship(topRight,bottomLeft)
双指针
子序列!=子串 (子序列可以是由里面的字母按顺序但可以不连续的组成的部分)
无重复的最长子串
主要的思想就是用两个指针,分别表示子串的起始和结束位置
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
w = {}
l,r = 0,0
n = len(s)
count = 0
while r<n:
if s[r] not in w.keys() or w[s[r]]<l:
w[s[r]] = r
else:
l = w[s[r]]+1
w[s[r]] = r
count = max(count,r-l+1)
r += 1
return count
三数之和
用排序,用两个指针分别指向数组的头和尾,然后根据结果的大小进行移动
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 先排序,O(NlogN),再用两个指针分别指向左右,根据和的大小进行移动,这里需要注意优化,比如输入的列表长度小于3且最大的一项都小于0则无需向下排,另外如果选的最小项>0也无需排,相等的值可以跳过
nums.sort()
n = len(nums)
res = []
if n<3 or nums[-1]<0: return res
for i in range(n-2):
l,r = i+1,n-1
if nums[i]>0:
return res
if i>0 and nums[i]==nums[i-1]:
continue
while l<r:
s = nums[i]+nums[l]+nums[r]
if s>0:
r -= 1
elif s<0:
l += 1
else:
res.append([nums[i],nums[l],nums[r]])
while l<r and nums[l]==nums[l+1]:
l += 1
while l<r and nums[r]==nums[r-1]:
r -= 1
l += 1
r -= 1
return res
接雨水
分别用了动态规划法和双指针法
其中双指针法主要思路就是在首尾各有一个指针,然后按照条件进行移动
class Solution:
def trap(self, height: List[int]) -> int:
# 这里用了动态规划的思想,如果按常规的方法,应该是循环找每一列的左边最高列和右边最高列,这样会有很多重复的数据,所以只需把它们保存起来即可
# n = len(height)
# if n<3: return 0
# max_left = [0 for _ in range(n)]
# max_right = [0 for _ in range(n)]
# for i in range(n):
# if i==0:
# max_left[i] = height[i]
# else:
# max_left[i] = max(max_left[i-1],height[i])
# # print(max_left)
# for i in range(n-1,-1,-1):
# if i==n-1:
# max_right[i] = height[i]
# else:
# max_right[i] = max(max_right[i+1],height[i])
# # print(max_right)
# ans = 0
# for i in range(n):
# if height[i]<max_left[i] and height[i]<max_right[i]:
# ans += min(max_left[i],max_right[i])-height[i]
# return ans
# 这个节约了一点空间,可以不用数组表示左边最大值
# n = len(height)
# if n<3: return 0
# max_left = 0
# max_right = [0 for _ in range(n)]
# for i in range(n-1,-1,-1):
# if i==n-1:
# max_right[i] = height[i]
# else:
# max_right[i] = max(max_right[i+1],height[i])
# # print(max_right)
# ans = 0
# for i in range(n):
# max_left = max(max_left,height[i])
# if height[i]<max_left and height[i]<max_right[i]:
# ans += min(max_left,max_right[i])-height[i]
# return ans
class Solution:
def trap(self, height: List[int]) -> int:
# 这是用了双指针,但是对于它的逻辑我还不是那么熟悉,还需要反复看,但是空间复杂度下降为常数了
n = len(height)
if n<3:return 0
left,right = 1,n-2
max_left,max_right = 0,0
ans = 0
for i in range(1,n-1):
if height[left-1]<height[right+1]:
max_left = max(max_left,height[left-1])
if height[left]<max_left:
ans += max_left-height[left]
left += 1
else:
max_right = max(max_right,height[right+1])
if height[right]<max_right:
ans += max_right-height[right]
right -= 1
return ans
盛最多水的容器
有指向首尾的两个指针,移动较短的那根才有可能盛水量变多
class Solution:
def maxArea(self, height: List[int]) -> int:
# 这个就是使用两个指针,分别指向头和尾,由于大小取决于两者中最短的线,移动较短的线才有可能得到更大的面积
n = len(height)
l,r = 0,n-1
maxarea = 0
while l<r:
maxarea = max(maxarea,min(height[l],height[r])*(r-l))
if height[l]<height[r]:
l += 1
else:
r -= 1
return maxarea
最接近的三数之和
和三数之和那题类似,先排序,再用两个指针找
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
# 主要还是和三数之和类似的方法,先排序,再设置两个指针找
n = len(nums)
nums.sort()
if n<3: return
minsum = nums[0]+nums[1]+nums[2]
for i in range(n-2):
l,r = i+1,n-1
while l<r:
s = nums[i]+nums[l]+nums[r]
minsum = s if abs(target-s)<abs(target-minsum) else minsum
if s<target:
l += 1
elif s>target:
r -= 1
else:
return target
return minsum
删除排序数组中的重复项
设置快慢两个指针,不相等时就覆盖
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# 还是双指针的思想,一个快一个慢然后比较并赋值,应该是O(N)
n = len(nums)
if n<2:return n
p,q=0,1
while q<n:
if nums[p]!=nums[q]:
p += 1
nums[p] = nums[q]
q += 1
return p+1
四数之和
和三数之和类似
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
# O(N^3) 基本思路同两数和三数相加一样
nums.sort()
n = len(nums)
res = []
if n < 4: return res
for i in range(n-3):
if i>0 and nums[i]==nums[i-1]: continue
for j in range(i+1,n-2):
if j>i+1 and nums[j]==nums[j-1]: continue
l,r = j+1,n-1
while l<r:
s = nums[i]+nums[j]+nums[l]+nums[r]
if s==target:
res.append([nums[i],nums[j],nums[l],nums[r]])
while l<r and nums[l]==nums[l+1]:
l += 1
while l<r and nums[r]==nums[r-1]:
r -=1
l += 1
r -= 1
elif s>target:
r -= 1
else:
l += 1
return res
合并两个有序数组
两个数组分别从后往前比较,然后从后向前放置合并元素
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.
"""
cur = m+n-1
i,j = m-1,n-1
while i>=0 and j>=0 and cur>=0:
if nums1[i]>nums2[j]:
nums1[cur] = nums1[i]
i -= 1
else:
nums1[cur] = nums2[j]
j -= 1
cur -= 1
while j>=0:
nums1[cur] = nums2[j]
j -= 1
cur -= 1
分隔链表
似乎可以省掉一个变量,不用cur直接用head,但是基本思想就是找到要插入的位置和要插入的元素
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
pre = ListNode(-1)
pre.next = head
cur = head
h = pre # 插入到它后面
prehead = pre
while cur:
while h and h.next and (h.next).val<x:
h = h.next
pre = h
cur = h.next
while cur and cur.val>=x:
cur = cur.next
pre = pre.next
if h and cur:
pre.next = cur.next
cur.next = h.next
h.next = cur
return prehead.next
删除链表的倒数第N个节点
是用一个指针先走N步,然后另一个指针从头一起走,这样就可以指向倒数第N个节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
prehead = ListNode(-1)
prehead.next = head
pre = prehead
f = head
while n>0 and f:
f = f.next
n -= 1
while f and pre and head:
f = f.next
pre = pre.next
head = head.next
pre.next = head.next
return prehead.next
两个数组的交集
按双指针写,用了排序,所以时间复杂度比较搞
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
res = []
nums1.sort()
nums2.sort()
i,j = 0,0
m,n = len(nums1),len(nums2)
while i<m and j<n:
if nums1[i]>nums2[j]:
j += 1
elif nums1[i]<nums2[j]:
i += 1
else:
res.append(nums1[i])
i += 1
while i<m and nums1[i]==nums1[i-1]:
i += 1
j += 1
while j<n and nums2[j]==nums2[j-1]:
j += 1
return res
两数之和 II - 输入有序数组
非常简单的一题,和两数之和一样,对于排序数组是首尾两个指针,根据和大小分别移动两个指针
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
n = len(numbers)
l,r = 0,n-1
while l<r:
numsum = numbers[l]+numbers[r]
if numsum==target:
return [l+1,r+1]
elif numsum>target:
r -= 1
else:
l += 1
return []
串联所有单词的字串
有点滑动窗口的样子
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
m = len(words) # 单词个数
if m==0: return []
n = len(words[0]) # 每个单词的长度
ns = len(s)
l,r = 0,m*n
words.sort()
while r<=ns:
m_words = []
for i in range(m):
m_words.append(s[l+i*n:l+(i+1)*n])
if sorted(m_words)==words: res.append(l)
l += 1
r += 1
return res
寻找重复数
思想是类似二分法那样,计算小于等于中间数的个数,如果大于一定的数目,就说明重复数在那个范围里
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
n =len(nums)
left,right = 1,n-1
while left<right:
mid = (left+right)>>1
cnt = 0
for num in nums:
if num<=mid:
cnt += 1
if cnt>mid:
right = mid
else:
left = mid+1
return left
实现Strstr()
很简单的一题,就是类似滑动窗口的思想
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
m = len(haystack)
n = len(needle)
l,r = 0,n
while r <= m:
if haystack[l:r]==needle:
return l
l += 1
r += 1
return -1
颜色分类
我觉得这题能这么写是因为只有三类,它的普适写法应该是先扫一边计算每个元素个数再重新产生该列表,如果用双指针的话就分别指向0和2的位置,然后扫描的元素符合条件,就和相应位置的元素交换
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
zero,two = 0,n-1
cur = 0
while cur<=two:
if nums[cur]==0:
nums[cur],nums[zero] = nums[zero],nums[cur]
zero += 1
cur += 1
elif nums[cur]==2:
nums[cur],nums[two] = nums[two],nums[cur]
two -= 1
else:
cur += 1
移除元素
首尾两个指针,然后用尾部的非target替换首部target
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 首尾两个指针,找到想要的值就替换
n = len(nums)
if n<1: return n
p,q = 0,n-1
while p<q:
while p<q and nums[p]!=val:
p += 1
while p<q and nums[q]==val:
q -=1
if p<q:
nums[p] = nums[q]
p += 1
q -= 1
return p+1 if p==q and nums[p]!=val else p
反转字符串
首尾指针交换
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
n = len(s)
l,r = 0,n-1
while l<r:
s[l],s[r] = s[r],s[l]
l += 1
r -= 1
环形链表
快慢两个指针,相遇即有环
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
slow = head
if head and head.next and (head.next).next:
fast = (head.next).next
else:
return False
while slow and slow.next and fast and fast.next:
if slow==fast:
return True
slow = slow.next
fast = (fast.next).next
return False
回文链表
先找中间位置,然后将后半段逆序,然后分别比较前后半段
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
cnt = 0
p = head
while p:
cnt += 1
p = p.next
if cnt<=1: return True
p = head
half_cnt = (cnt-1)//2
while half_cnt>0:
half_cnt -= 1
p = p.next
pre = p.next
cur = pre.next
while cur:
pre.next = cur.next
cur.next = p.next
p.next = cur
cur = pre.next
half_cnt = cnt//2-1
p1 = head
p2 = p.next
while half_cnt>=0:
if p1.val!=p2.val:
return False
half_cnt -= 1
p1 = p1.next
p2 = p2.next
return True
移动零
也是很简单的一题,两个指针,把非零的元素按位置顺序放入数组中
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
i,j = 0,0
while j<n:
if nums[j]!=0:
nums[i] = nums[j]
i += 1
j += 1
while i<n:
nums[i] = 0
i += 1
长度最小的子数组
类似滑动窗口的思想
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
n = len(nums)
if sum(nums)<s: return 0
l,r = 0,0
minlen = n
valsum = nums[0]
if valsum>s:
return 1
while r<n:
if valsum<s:
r += 1
if r<n:
valsum += nums[r]
else:
minlen = min(minlen,r-l+1)
valsum -= nums[l]
l += 1
return minlen
旋转链表
主要是找到旋转位置,然后放到头部,有点像找到链表倒数第k个元素
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
cnt = 0
p = head
while p:
cnt += 1
p = p.next
if cnt<=1: return head
prehead = ListNode(-1)
prehead.next = head
k = k%cnt
if k==0: return head
q = head
tail = prehead
while k>=0:
q = q.next
tail = tail.next
k -= 1
while q:
head = head.next
q = q.next
tail = tail.next
tail.next = prehead.next
prehead.next = head.next
head.next = None
return prehead.next
反转字符串中的元音字母
分别从前后查找需要交换的元素
class Solution:
def reverseVowels(self, s: str) -> str:
n = len(s)
l,r = 0,n-1
vowel = ['a','e','i','o','u']
s_list = list(s)
while l<r:
while l<r and s_list[l].lower() not in vowel:
l += 1
while l<r and s_list[r].lower() not in vowel:
r -= 1
if l<r:
s_list[l],s_list[r] = s_list[r],s_list[l]
l += 1
r -= 1
return ''.join(s_list)
环形链表 II
先像之前的一样判断有无环,然后一个从head开始,一个从相遇点开始,再一次相遇时就为环的起点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow,fast = head,head
flag = False
while slow and slow.next and fast and fast.next and (fast.next).next:
slow = slow.next
fast = (fast.next).next
if slow== fast:
flag = True
break
if not flag:
return None
p = head
while p!=slow:
p = p.next
slow = slow.next
return p
验证回文串
首尾指针比较
class Solution:
def isPalindrome(self, s: str) -> bool:
n = len(s)
l,r = 0,n-1
while l<r:
while l<r and not s[l].isalnum():
l += 1
while l<r and not s[r].isalnum():
r -= 1
if l<r:
if s[l].lower()!=s[r].lower():
return False
l += 1
r -= 1
return True
两个数组的交集 II
很简单第一题,排序后比较
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
m = len(nums1)
n = len(nums2)
p1,p2 = 0,0
res = []
while p1<m and p2<n:
if nums1[p1]==nums2[p2]:
res.append(nums1[p1])
p1 += 1
p2 += 1
elif nums1[p1]>nums2[p2]:
p2 += 1
else:
p1 += 1
return res
乘积小于K的子数组
这个有参考别人的方式,我自己写的不知道为什么超时了,注意这里res的算法,有点巧妙
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
n = len(nums)
if n==0: return 0
res = 0
l,r = 0,0
multi = 1
while l<n:
while r<n and multi*nums[r]<k:
multi *= nums[r]
r += 1
if l<r:
res += r-l
multi //= nums[l]
l += 1
else:
l += 1
r = l
multi = 1
return res
通过删除字母匹配到字典里最长单词
这里我先给字典按长度和字典序排列,然后分别在字符串中查找字典中的对应字符串
class Solution:
def findLongestWord(self, s: str, d: List[str]) -> str:
d.sort(key=lambda x: [-len(x), x])
# print(d)
for ds in d:
i,j = 0,0
while i<len(s) and j<len(ds):
if s[i]==ds[j]:
j += 1
i += 1
if j==len(ds): return ds
return ''
有序转化数组
先找出曲线最低或最高点,然后用两个指针从此点分别向左向右移动
class Solution:
def sortTransformedArray(self, nums: List[int], a: int, b: int, c: int) -> List[int]:
n = len(nums)
if n==0: return []
res = []
mindex = 0
if a!=0:
mid = -b/(2*a)
d = abs(nums[0]-mid)
for i in range(1,n):
if abs(nums[i]-mid)<d:
d = abs(nums[i]-mid)
mindex = i
l,r = mindex-1,mindex
while l>=0 and r<n:
if abs(nums[l]-mid)<abs(nums[r]-mid):
res.append(a*(nums[l])**2+b*nums[l]+c)
l -= 1
else:
res.append(a*(nums[r])**2+b*nums[r]+c)
r += 1
while l>=0:
res.append(a*(nums[l])**2+b*nums[l]+c)
l -= 1
while r<n:
res.append(a*(nums[r])**2+b*nums[r]+c)
r += 1
return res if a>0 or (a==0 and b>0) else reversed(res)
数组中的K-diff数对
先排序再用双指针找符合条件的元素
class Solution:
def findPairs(self, nums: List[int], k: int) -> int:
nums.sort()
n = len(nums)
if n<2: return 0
l,r = 0,1
res = 0
while r<n:
m = nums[r]-nums[l]
if m==k:
res += 1
l += 1
while l<n and nums[l]==nums[l-1]:
l += 1
while r<n and nums[r]==nums[r-1]:
r += 1
if l==r:
r = l+1
elif m<k:
r += 1
else:
l += 1
if l==r:
r = l+1
return res
字符串的排列
滑动窗和字典计数的方式
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
m = len(s1)
n = len(s2)
if m>n: return False
s1_dict = {}
for c in s1:
s1_dict[c] = s1_dict.get(c,0)+1
l,r = 0,m-1
s2_dict = {}
for i in range(m):
s2_dict[s2[i]] = s2_dict.get(s2[i],0)+1
while r<n:
if s1_dict==s2_dict:
return True
s2_dict[s2[l]] -= 1
if s2_dict[s2[l]]==0: s2_dict.pop(s2[l])
l += 1
r += 1
if r<n:
s2_dict[s2[r]] = s2_dict.get(s2[r],0)+1
return False
至多包含两个不同字符的最长子串
滑动窗口的思想
class Solution:
def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
pos = {}
n = len(s)
l,r = 0,0
maxlen = 0
while r<n:
# print('l:',l)
# print('r:',r)
if s[r] not in pos.keys() and len(pos)==2:
p1,p2 = pos.items()
maxlen = max(maxlen,r-l)
l = min(p1[1],p2[1])+1
pos.pop(p1[0]) if p1[1]<p2[1] else pos.pop(p2[0])
pos[s[r]] = r
r += 1
maxlen = max(maxlen,r-l)
return maxlen
较小的三数之和
先排序,然后首尾指针,按照条件进行移动,注意这里符合条件的处理方式
class Solution:
def threeSumSmaller(self, nums: List[int], target: int) -> int:
nums.sort()
n = len(nums)
if n<3: return 0
res = 0
for i in range(n-2):
t = target-nums[i]
l,r = i+1,n-1
while l<r:
m = nums[l]+nums[r]
if m<t:
res += r-l
l += 1
else:
r -= 1
return res
最大连续1的个数 II
从头开始的两个指针
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
n = len(nums)
l,r = 0,0
is_change = False # 判断是否已经转化过一次
c_index = 0
res = 0
while r<n:
if nums[r]==0:
if not is_change:
is_change = True
c_index = r
else:
res = max(res,r-l)
is_change = False
l = c_index+1
r -= 1
r += 1
res = max(res,r-l)
return res
划分字母区间
应该算是合并子区间的问题,好像没有用到双指针
class Solution:
def partitionLabels(self, S: str) -> List[int]:
def merge_intervals(a):
n = len(a)
i,j = 0,1
while i<len(a) and j<len(a):
if a[i][0]<a[j][0]<a[i][1]:
a[i][1] = max(a[i][1],a[j][1])
a.pop(j)
else:
i += 1
j = i+1
pos = {}
res = []
n = len(S)
for i in range(n):
if S[i] not in pos:
pos[S[i]] = [i,i]
else:
pos[S[i]][1] = i
# print(pos)
pos_list = list(pos.values())
# print(pos_list)
merge_intervals(pos_list)
for i in range(len(pos_list)):
res.append(pos_list[i][1]-pos_list[i][0]+1)
# print(pos_list)
return res
安排工作以达到最大收益
这里是参考别人的,写得比我原先的简洁很多,这里有几个坑,一个是可能难度小的收益大,所以可以把最好收益存下来
class Solution:
def maxProfitAssignment(self, difficulty: List[int], profit: List[int], worker: List[int]) -> int:
jobs = list(zip(difficulty, profit))
jobs.sort()
ans = i = best = 0
for skill in sorted(worker):
while i < len(jobs) and skill >= jobs[i][0]:
best = max(best, jobs[i][1])
i += 1
ans += best
return ans
独特字符串
这也是参考别人的,写的很精妙,思想就是每个字母分别看在哪些子串中可以作为独特字符
class Solution:
def uniqueLetterString(self, S: str) -> int:
index = collections.defaultdict(list)
for i, c in enumerate(S):
index[c].append(i)
print(index)
n = len(S)
res = 0
for A in index.values():
A = [-1] + A + [n]
for i in range(1,len(A)-1):
res += (A[i] - A[i-1]) * (A[i+1] - A[i])
return res%(10**9+7)
比较含退格的字符串
这里用栈写的,没有用双指针
class Solution:
def backspaceCompare(self, S: str, T: str) -> bool:
def get_str(s):
stack = []
n = len(s)
for c in s:
if c=='#':
if stack:
stack.pop()
else:
stack.append(c)
return ''.join(stack)
return get_str(S)==get_str(T)
数组中的最长山脉
左右指针,顺着规律找山脉
class Solution:
def longestMountain(self, A: List[int]) -> int:
n = len(A)
l,r=0,0
res = 0
while r<n:
for i in range(l,n-1):
if A[i+1]>A[i]:
l = i
break
elif i==n-2:
l = n-1
r = l+1
is_down = False
while r<n and A[r]>A[r-1]:
r += 1
if r<n and A[r]==A[r-1]:
l = r
continue
while r<n and A[r]<A[r-1]:
is_down = True
r += 1
if is_down:
res = max(res,r-l)
l = r-1
return res
救生艇
这个也是参考别人的答案,一个重的搭配目前最轻的,不行的话就一个人搭船,我在这题走了很多弯路,我希望找到一个体重和符合要求且是最大的,完全没必要
class Solution:
def numRescueBoats(self, people: List[int], limit: int) -> int:
people.sort()
n = len(people)
res = 0
l,r = 0,n-1
while l<=r:
res += 1
if people[l]+people[r]<=limit:
l += 1
r -= 1
return res
水果成篮
这个和159题-至多包含两个不同字符的最长子串 非常相似
class Solution:
def totalFruit(self, tree: List[int]) -> int:
fruit = {}
n = len(tree)
l,r = 0,0
maxlen = 0
while r<n:
if len(fruit)==2 and tree[r] not in fruit.keys():
p1,p2 = fruit.items()
maxlen = max(maxlen,r-l)
l = min(p1[1],p2[1])+1
fruit.pop(p1[0]) if p1[1]<p2[1] else fruit.pop(p2[0])
fruit[tree[r]] = r
r += 1
maxlen = max(maxlen,r-l)
return maxlen
三数之和的多种可能
这里除了和之前三数之和那题类似,还多了一个计数步骤
class Solution:
def threeSumMulti(self, A: List[int], target: int) -> int:
def get_choice(n,x): # 这里是计算从n个数取x个数有多少种方式
if n<x: return 0
if n==x: return 1
if x==1: return n
a,b = 1,1
for i in range(x):
a *= (n-i)
b *= (i+1)
return a//b
cnt = {}
A_list = []
for num in A:
if num not in cnt.keys():
A_list.append(num)
cnt[num] = cnt.get(num,0)+1
A_list.sort()
n = len(A_list)
ans = 0
for i in range(n):
l,r = i,n-1
while l<=r:
# print('i,l,r:',i,l,r)
m = A_list[i]+A_list[l]+A_list[r]
if m==target:
c = {}
c[A_list[i]] = c.get(A_list[i],0)+1
c[A_list[l]] = c.get(A_list[l],0)+1
c[A_list[r]] = c.get(A_list[r],0)+1
res = 1
for j in c.keys():
res *= get_choice(cnt[j],c[j])
ans += res
l += 1
r -= 1
elif m<target:
l += 1
else:
r -= 1
return ans%(10**9+7)
长按键入
很简单的一题,主要是在typed中找有无name匹配即可
class Solution:
def isLongPressedName(self, name: str, typed: str) -> bool:
m = len(name)
n = len(typed)
i,j = 0,0
while i<m and j<n:
if name[i]==typed[j]:
i += 1
j += 1
return False if i<m else True
有序数组的平方
很简单的一题
class Solution:
def sortedSquares(self, A: List[int]) -> List[int]:
ans = [x*x for x in A]
ans.sort()
# 或者将A直接平方,然后通过首尾两个指针放到新列表的相应位置
return ans
三个有序数组的交集
这题也比较简单,主要是移动指针
class Solution:
def arraysIntersection(self, arr1: List[int], arr2: List[int], arr3: List[int]) -> List[int]:
n1,n2,n3 = len(arr1),len(arr2),len(arr3)
i,j,k = 0,0,0
ans = []
while i<n1 and j<n2 and k<n3:
if arr1[i]==arr2[j]==arr3[k]:
ans.append(arr1[i])
i += 1
j += 1
k += 1
elif arr1[i]<arr2[j] or arr1[i]<arr3[k]:
i += 1
elif arr2[j]<arr1[i] or arr2[j]<arr3[k]:
j += 1
elif arr3[k]<arr1[i] or arr3[k]<arr2[j]:
k += 1
return ans
最大连续1的个数 III
和之前最大连续1的个数类似,别忘了考虑K为0的情况
class Solution:
def longestOnes(self, A: List[int], K: int) -> int:
n = len(A)
l,r = 0,0
res = 0
zero_list = []
while r<n:
if A[r]==0:
if len(zero_list)==K:
res = max(res,r-l)
if zero_list:
l = zero_list.pop(0)+1
else:
l = r+1
if K!=0:
zero_list.append(r)
r += 1
res = max(res,r-l)
return res
大样本统计
没用到双指针思想
class Solution:
def sampleStats(self, count: List[int]) -> List[float]:
n = len(count)
minval,maxval = n-1,0
cnt = 0
numsum = 0
mostcnt,mostval = 0,0
for i in range(n):
if count[i]>0:
minval = min(minval,i)
maxval = max(maxval,i)
cnt += count[i]
numsum += i*count[i]
if count[i]>mostcnt:
mostcnt = count[i]
mostval = i
mid = cnt/2
midval1,midval2 = n,n
m = 0
for i in range(n):
m += count[i]
if m>=mid:
midval1 = min(midval1,i)
if m>=mid+1:
midval2 = min(midval2,i)
break
midval = midval1 if cnt%2==1 else (midval1+midval2)/2
return [minval,maxval,numsum/cnt,midval,mostval]
区间列表的交集
按照条件进行移动指针
class Solution:
def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
m = len(A)
n = len(B)
i,j = 0,0
ans = []
while i<m and j<n:
if B[j][0]>A[i][1]:
i += 1
elif A[i][0]>B[j][1]:
j += 1
else:
if A[i][0]<=B[j][0]<=A[i][1]:
ans.append([B[j][0],min(A[i][1],B[j][1])])
elif B[j][0]<=A[i][0]<=B[j][1]:
ans.append([A[i][0],min(A[i][1],B[j][1])])
if A[i][1]<B[j][1]:
i += 1
else:
j += 1
return ans
统计「优美子数组」
把奇数的位置存起来,需要奇数外的偶数不会影响结果,所以只需要乘积计算包含偶数的各个情况
class Solution:
def numberOfSubarrays(self, nums: List[int], k: int) -> int:
n = len(nums)
odd = []
for i in range(n):
if nums[i]%2==1:
odd.append(i)
odd = [-1] + odd + [n]
# print(odd)
m = len(odd)
ans = 0
for i in range(k,m-1):
ans += (odd[i-k+1]-odd[i-k])*(odd[i+1]-odd[i])
return ans
和相同的二元子数组
这是把需要的数记录下来,计算包含需要数的范围数目,只要前后无影响的数目个数相乘即可,和前面的思想很像,这里就是把1都记录下来
class Solution:
def numSubarraysWithSum(self, A: List[int], S: int) -> int:
def get_num(x):
ans = 0
for i in range(x):
ans += x-i
return ans
n = len(A)
ans = 0
if sum(A)<S or n<1: return 0
pos = []
for i in range(n):
if A[i]==1:
pos.append(i)
pos = [-1] + pos + [n]
m = len(pos)
if S!=0:
for i in range(S,m-1):
ans += (pos[i-S+1]-pos[i-S])*(pos[i+1]-pos[i])
else:
for i in range(1,m):
ans += get_num(pos[i]-pos[i-1]-1)
return ans
未完成的part