【刷题2025】单指针双指针+滑动窗口+二分法三分法+区间问题
1.数组理论基础
2.单指针双指针
双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 暴力解法时间复杂度:O(n^2)
- 双指针时间复杂度:O(n)
例题:移除元素
'''
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
'''
# (版本一)快慢指针法
class Solution:def removeElement(self, nums: List[int], val: int) -> int:# 快慢指针fast = 0 # 快指针slow = 0 # 慢指针size = len(nums)while fast < size: # 不加等于是因为,a = size 时,nums[a] 会越界# slow 用来收集不等于 val 的值,如果 fast 对应值不等于 val,则把它与 slow 替换if nums[fast] != val:nums[slow] = nums[fast]slow += 1fast += 1return slow
例题:有序数组的平方
'''
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
'''
class Solution:def sortedSquares(self, nums: List[int]) -> List[int]:l, r, i = 0, len(nums)-1, len(nums)-1res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果while l <= r:if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值res[i] = nums[r] ** 2r -= 1 # 右指针往左移动else:res[i] = nums[l] ** 2l += 1 # 左指针往右移动i -= 1 # 存放结果的指针需要往前平移一位return res
(1)跳房子II
题目描述
跳房子,也叫跳飞机,是一种世界性的儿童游戏。
游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格,然后获得一次选房子的机会,直到所有房子被选完,房子最多的人获胜。
跳房子的过程中,如果有踩线等违规行为,会结束当前回合,甚至可能倒退几步。假设房子的总格数是count,小红每回合可能连续跳的步数都放在数组steps中,请问数组中是否有一种步数的组合,可以让小红三个回合跳到最后一格?
如果有,请输出索引和最小的步数组合(数据保证索引和最小的步数组合是唯一的)。
注意:数组中的步数可以重复,但数组中的元素不能重复使用。
(在list中选三个数和为房子总格数?)
输入描述
第一行输入为房子总格数count,它是int整数类型
第二行输入为每回合可能连续跳的步数,它是int整数数组类型
输出描述
返回索引和最小的满足要求的步数组合 (顺序保持 steps中原有顺序)
备注
−count≤10000−3≤steps.length≤10000−−100000≤steps[i]≤100000
样例
输入
[1,4,5,2,0,2]
9
输出
[4,5,0]
输入
[1,5,2,0,2,4]
9
输出
[5,2,2]
输入
[-1,2,4,9]
12
输出
[-1,4,9]
# 超时
# 排序后遍历每个元素作为第一个节点,取l和r作为第三个节点
# 总和偏小时l右移,总和偏大时r左移
steps = list(map(int, input()[1:-1].split(",")))
n = int(input())def solve():ans = []newsteps = []for i in range(len(steps)):newsteps.append([steps[i], i])newsteps.sort()for i in range(len(newsteps)-2):# 剪枝if newsteps[i][0] > 0 and 0 < n < newsteps[i][0]:breakif i > 0 and newsteps[i][0] == newsteps[i-1][0]:continuel = i + 1r = len(newsteps) - 1while l < r:if newsteps[i][0] + newsteps[l][0] + newsteps[r][0] > n:r -= 1elif newsteps[i][0] + newsteps[l][0] + newsteps[r][0] == n:while l < r-1 and newsteps[r][0] == newsteps[r-1][0]:r -= 1a = [newsteps[i], newsteps[l], newsteps[r]]a.sort(key=lambda x:x[1])ans.append(a)r -= 1else:l += 1ans.sort(key=lambda x:(x[0][1]+x[1][1]+x[2][1], x[0][1], x[1][1], x[2][1]))return ans[0]s = solve()
a = [s[0][0], s[1][0], s[2][0]]
print("[" + ",".join(list(map(str, a))) + "]")
(2)事件推送
题目描述
同一个数轴X上有两个点的集合A={A1,A2,..,Am}和B={B1,B2,…,Bn},Ai和Bj均为正整数,A、B已经按照从小到大排好序,
A、B均不为空,给定一个距离R(正整数),列出同时满足如下条件的所有(Ai,Bj)数对:
- Ai<= Bj
- Ai,Bj之间的距离小于等于R
- 在满足1,2的情况下,每个Ai只需输出距离最近的Bj
- 输出结果按Ai从小到大的顺序排序
输入描述
第一行三个正整数m,n,R
第二行m个正整数,表示集合A
第三行n个正整数,表示集合B
输入限制:
1<=R<=100000,1<=n,m<=100000,1<=Ai,Bj<=1000000000
这个题目和ABR很像,但是条件差不就那么一点点。
输出描述
每组数对输出一行Ai和Bj,以空格隔开
注意
1.Ai<=Bj
2.Ai,Bj距离小于等于R,但如果Ai找不到R范围内的B,则列出距它最近的1个Bj,当然此种情况仍然要满足1,
但如果仍然找不到,就丢弃Ai。
用例
输入
4 5 5
1 5 5 10
1 3 8 8 20
输出
1 1
5 8
5 8
m, n, R = list(map(int,input().split()))
A = list(map(int,input().split()))
B = list(map(int,input().split()))def solve():aj = 0bj = 0while aj < m and bj < n:if A[aj] <= B[bj]:if B[bj] - A[aj] <= R:print(" ".join(list(map(str, [A[aj], B[bj]]))))aj += 1else:bj += 1returnsolve()
# 知识点总结
# 当出现TLE RTE时检查循环跳出是否正确
# 检查坐标是否超出界限
m, n, R = list(map(int,input().split()))
A = list(map(int,input().split()))
B = list(map(int,input().split()))def solve():aj = 0bj = 0while aj < m and bj < n:while bj < n and B[bj] < A[aj]:bj += 1if bj >= n:breakif B[bj] - A[aj] > R:aj += 1continueprint(" ".join(list(map(str, [A[aj], B[bj]]))))aj += 1returnsolve()
(3)最长连续子序列
题目描述
有N个正整数组成的一个序列。给定整数sum,求长度最长的连续子序列,使他们的和等于sum,返回此子序列的长度。如果没有满足要求的序列,返回-1。
输入描述
第一行输入是:N个正整数组成的一个序列
第二行输入是:给定整数sum
输出描述
最长的连续子序列的长度
备注
输入序列仅由数字和英文逗号构成,数字之间采用英文逗号分隔。序列长度:1<=N<= 200
输入序列不考虑异常情况
示例1:
输入
1,2,3,4,2
6
输出
3
说明
1,2,3和4,2两个序列均能满足要求,所以最长的连续序列为1,2,3,因此结果为3.
示例2:
输入
1,2,3,4,2
20
输出
-1
说明
没有满足要求的子序列,返回-1
# 当left>=right时不要轻易break再给他一次机会吧!
# 先把问题解决了再想剪枝吧!
num = list(map(int, input().split(",")))
n = int(input())def solve():result = 0if len(num) == 1 and num[0] == n:return 1left, right = 0, 1 # 左闭右开区间cursum = num[0]while left < len(num) and right <= len(num):if (left >= right and right <len(num)) or cursum < n:right += 1if right <= len(num):cursum += num[right-1]else:breakelif cursum == n:result = max(result, right-left)if right < len(num) - 1 and left < len(num)-2:cursum += num[right]cursum -= num[left]right += 1left += 1else:breakelif cursum > n:cursum -= num[left]left += 1return result if result != 0 else -1print(solve())
# 或者采用前缀和方法也是可以滴
# 输入获取
nums = list(map(int, input().split(",")))
target = int(input())# 算法入口
def getResult():n = len(nums)preSum = [0] * (n + 1)for i in range(1, n + 1):preSum[i] = preSum[i - 1] + nums[i - 1]maxLen = -1for l in range(1, n + 1):for r in range(l, n + 1):if preSum[r] - preSum[l - 1] == target:maxLen = max(maxLen, r - l + 1)return maxLen# 算法调用
print(getResult())
(4)分割数组的最大差值
题目描述
给定一个由若干整数组成的数组nums,可以在数组内的任意位置进行分割,将该数组分割成两个非空子数组(即左数组和右数组),分别对子数组求和得到两个值,计算这两个值的差值,请输出所有分割方案中,差值最大的值。
输入描述
第一行输入数组中元素个数n,1<n<= 100000
第二行输入数字序列,以空格进行分隔,数字取值为4字节整数
输出描述
输出差值的最大取值
示例1:
输入:
6
1 -2 3 4 -9 7
输出:
10
说明:
将数组nums划分为两个非空数组的可行方案有:
左数组=[1]且右数组=[-2,3,4,-9,7],和的差值=|1-3|=2
左数组=[1,-2]且右数组=[3,4,-9,7],和的差值=|1-5|=6
左数组=[1,-2,3]且右数组=[4,-9,7],和的差值=|2-2|=0
左数组=[1,-2,3,4]且右数组=[-9,7],和的差值=|6-(-2)|= 8,
左数组=[1,-2,3,4,-9]且右数组=[7],和的差值=|-3-7|=10最大的差值为10
n = int(input())
nums = list(map(int, input().split()))def solve():maxdiff = 0index = 0 # 左数组[0, index],右数组[index+1, -1]leftsum = 0rightsum = sum(nums)while index < n -1:leftsum += nums[index]rightsum -= nums[index]maxdiff = max(maxdiff, abs(leftsum-rightsum))index += 1return maxdiffprint(solve())
(5)最大花费金额
题目描述
双十一众多商品进行打折销售,小明想购买自己心仪的一些物品,但由于受购买资金限制,所以他决定从众多心仪商品中购买三件,而且想尽可能的花完资金。
现在请你设计一个程序帮助小明计算尽可能花费的最大资金数额
输入描述
输入第一行为一维整型数组M,数组长度小于100,数组元素记录单个商品的价格,单个商品价格小于1000.
输入第二行为购买资金的额度R,R小于100000
输入格式是正确的,无需考虑格式错误的情况.
输出描述
输出为满足上述条件的最大花费额度
如果不存在满足上述条件的商品,请返回-1。
用例
prices = list(map(int, input().split(",")))
n = len(prices)
target = int(input())def solve():prices.sort()if n < 3:return -1if n == 3:return sum(prices) if sum(prices) <= target else -1ans = 0for i in range(n-2):for j in range(i+1, n-1):if prices[i] + prices[j] >= target or prices[i] + prices[j]*2 > target:continuefor k in range(j+1, n):if prices[i] + prices[j] + prices[k] == target:return targetelif prices[i] + prices[j] + prices[k] < target:ans = max(ans, prices[i] + prices[j] + prices[k])else:breakreturn ans if ans != 0 else -1print(solve())
3.滑动窗口
根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
- 暴力解法时间复杂度:O(n^2)
- 滑动窗口时间复杂度:O(n)
例题:长度最小的子数组
'''
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
'''
class Solution:def minSubArrayLen(self, s: int, nums: List[int]) -> int:l = len(nums)left = 0right = 0min_len = float('inf')cur_sum = 0 #当前的累加值while right < l:cur_sum += nums[right]while cur_sum >= s: # 当前累加值大于目标值min_len = min(min_len, right - left + 1)cur_sum -= nums[left]left += 1right += 1return min_len if min_len != float('inf') else 0
(1)寻找符合要求的最长子串
题目描述:
给定一个字符串 s ,找出这样一个子串:
1)该子串中的任意一个字符最多出现2次;
2)该子串不包含指定某个字符;
请你找出满足该条件的最长子串的长度。
输入描述:
第一行为要求不包含的指定字符,为单个字符,取值范围[0-9a-zA-Z]
第二行为字符串s,每个字符范围[0-9a-zA-Z],长度范围[1,10000]
输出描述:
一个整数,满足条件的最长子串的长度;如果不存在满足条件的子串,则返回0
示例1
输入:
D
ABC123
输出:
6
示例2
输入:
D
ABACA123D
输出:
7
forbid = input()
s = input()def solve():count = [0] * 128max_length = 0left = 0right = 0 # 左闭右闭区间while right < len(s):# 不是指定字符,上一子串结束,记录结果if not (s[right].isdigit() or s[right].isalpha()) or s[right] == forbid:for c in s[left:right]:count[ord(c)] -= 1max_length = max(max_length, right - left)left, right = right + 1, right + 1else:# 有重复字符if count[ord(s[right])] == 2:max_length = max(max_length, right - left)