双指针
核心本质:使用双指针优化时间复杂度为On。
对撞双指针:快速排序、反转字符串
同向双指针:颠倒字符串中的单词
位于不同数组的双指针:归并排序
快慢双指针:环状链表
滑动窗口:连续子数组类题目
位运算
常用操作:
- 求num二进制第k位的数:(num>>k)&1
- 求num二进制最低1的位置对应的值:x&(-x)=x&(~x+1)负数为正数取反+1
二进制中1的个数
# 法一、每次直接减去最低位1的值,减的次数就等于1的个数
class Solution(object):
def hammingWeight(self, n):
ans = 0
while n:
n -= n&(~n+1)
ans += 1
return ans
# 法2、遍历每一位,统计1的个数
class Solution(object):
def hammingWeight(self, n):
ans = 0
while n:
ans += n&1
n >>= 1
return ans
区间合并
一般套路:将所有区间按照左节点或者右节点排序,然后遍历一遍按照一定规则统计结果。
合并区间(统计合并后区间的个数)
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
class Solution(object):
def merge(self, intervals):
# 按照先左节点后右节点排序
# 如果后一区间起始位置比前一区间终止位置小,则合并两区间,更新右节点为两区间右节点最大值,否则无法合并加入一个新区间。
ans = []
intervals.sort()
for interval in intervals:
if ans and interval[0] <= ans[-1][1]:
ans[-1][1] = max(ans[-1][1], interval[1])
else:
ans.append(interval)
return ans
会议室II(统计区间的最大重叠个数,上下车问题)
给你一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,返回 所需会议室的最小数量 。
法一:转换为上下车(差分问题),由于区间节点很稀疏,使用哈希并模拟差分
class Solution(object):
def minMeetingRooms(self, intervals):
# 哈希表模拟上下车过程,本质上这里的哈希表是个差分
# 在上车的地方+1 在下车的地方-1
# 使用哈希表而不是数组是因为数组太稀疏了
updown = {}
for start, end in intervals:
updown[start] = updown[start] + 1 if start in updown else 1
updown[end] = updown[end]-1 if end in updown else -1
ans = 0
count = 0
for i in sorted(updown):
count += updown[i]
ans = max(count,ans)
return ans
法二:转换为堆排序问题,建立一个小跟堆,存放会议结束时间,把区间进行排序,进行遍历,若开始时间大等于堆顶小根堆,就不需要新开会议室,更新会议室的结束时间即可,否则需要新开一个会议室,存入会议结束时间。
class Solution(object):
def minMeetingRooms(self, intervals):
# 构建小根堆
import heapq
meetings = []
heapq.heapify(meetings)
# 按照会议开始时间和结束时间排序
intervals.sort()
ans = 0
for interval in intervals:
# 如果堆内无会议或当前会议开始时间小于堆内会议最小结束时间
# 需要再开一个会议室
if not meetings or (meetings and interval[0] < meetings[0]):
ans += 1
heapq.heappush(meetings, interval[1])
else:
# 如果当前会议开始时间大于堆内最快结束时间,不需要再开会议
heapq.heappop(meetings)
heapq.heappush(meetings,interval[1])
return ans
用最少的箭引爆气球(最少的线穿过区间)
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
class Solution(object):
def findMinArrowShots(self, points):
# 把区间按照右节点升序排列,在右节点处射出能够击中尽可能多的气球
# 如果下一个区间没法被上一根箭击中,则射出一个新的,更新最后一个箭的位置
points.sort(key=lambda x:x[1])
ans = 0
preright = None
for start, end in points:
if not preright or preright < start:
ans += 1
preright = end
return ans