【ACWING】【3】双指针+位运算+合并区间

本文深入探讨了双指针在各种算法问题中的应用,包括快速排序、字符串反转、区间合并、会议室调度和最少箭引爆气球等。通过实例解析,展示了如何利用双指针优化时间复杂度,以及如何转换问题来解决区间问题,如利用哈希表或堆排序进行高效求解。此外,还介绍了二进制操作在计算中的一些常见技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

双指针

核心本质:使用双指针优化时间复杂度为On。
对撞双指针:快速排序、反转字符串
同向双指针:颠倒字符串中的单词
位于不同数组的双指针:归并排序
快慢双指针:环状链表
滑动窗口:连续子数组类题目

位运算

常用操作:

  1. 求num二进制第k位的数:(num>>k)&1
  2. 求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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值