说明
这里的双指针 针对于数组的形式展开
其他形式的双指针如滑动窗口如【算法-面试】子串专题
【算法-面试】二分搜索专题
# coding = "utf-8"
'''
数组
常见思路:
快慢指针
滑动窗口
'''
def reverse_string(s):
'''
翻转数组
思路:
双指针
'''
l, r = 0, len(s) - 1
while l < r:
temp = s[l]
s[l] = s[r]
s[r] = temp
l += 1
r -= 1
return s
def remove_duplicates(nums):
'''
给你⼀个有序数组 nums,请你原地删除重复出现的元素,使每个元素只出现⼀次,返回删除后数组的新⻓度。
leetcode: 26
input: nums = [1,1,2]
output: 2, nums = [1,2]
思路:
1. 使用快慢指针
2. 快指针先走,当快慢指针指向的值不相等时,更新慢指针
'''
if len(nums) == 0:
return 0
slow, fast = 0, 0
while fast <= len(nums) - 1:
if nums[fast] != nums[slow]:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow + 1
def remove_element(nums, val):
'''
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
leetcode: 27
input: nums = [3,2,2,3], val = 3
output:2, nums = [2,2]
思路:
1. 快慢指针
'''
if len(nums) == 0:
return 0
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
print(nums[:slow])
return slow
def remove_zero(nums):
'''
给定⼀个数组 nums,编写⼀个函数将所有 0 移动到数组的末尾,必须在原数组上操作,同时保持⾮零元素的相对顺序。
leetcode: 283
input: [0,1,0,3,12]
output:[1,3,12,0,0]
思路:
1. 快慢指针
2. 当快指针不等于0时,慢指针指向的值为快指针值
3. 终止条件时快指针到结尾
4. 根据慢指针指向的值,将末尾的部分全部补充为0
'''
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
fast += 1
while slow < len(nums):
nums[slow] = 0
slow += 1
return nums
def water_trap(height):
'''
给定 n 个⾮负整数表示每个宽度为 1 的柱⼦的⾼度图,计算按此排列的柱⼦,下⾬之后能接多少⾬⽔。
leetcode: 42
input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
output:6
思路:
1. 左右指针,向中间靠拢
2. 靠拢的时候,分别记录两边的最大值
3. 根据左右两遍的最大值相比大小,选取其中小的值,与当前指针对应的height进行做差值,即得到当前指针对应的接到雨水的值
4. while终止条件为左指针大于右指针
'''
n = len(height)
l, r = 0, n - 1 # 左右指针
l_max, r_max, res = height[0], height[n - 1], 0 # 左侧最大值 右侧最大值 结果保存
while l <= r:
l_max = max(l_max, height[l])
r_max = max(r_max, height[r])
if l_max < r_max:
res += l_max - height[l]
l += 1
else:
res += r_max - height[r]
r -= 1
return res
def intervalIntersection(firstList, secondList):
'''
给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。
每个区间列表都是成对 不相交 的,并且 已经排序 。返回这两个区间列表的交集 。
leetcode: 986
input: firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
思路:
1. 针对一段区间firstList的[a1, a2], secondList的[b1, b2], 核心思路是c1=max(a1, b1), c2=min(a2, b2)
2. 设置变量双指针i,j,分表代表firstList 和 secondList
3. 前进条件:当 b2<a2 的时候,j++; 反之i++
4. 终止条件:i>=len(firstList) && j>=len(secondList)
'''
i, j, res = 0, 0, []
while i < len(firstList) and j < len(secondList):
a1, a2 = firstList[i][0], firstList[i][1]
b1, b2 = secondList[j][0], secondList[j][1]
if b2 >= a1 and a2 >= b1: # 保证是有交集的
res.append([max(a1, b1), min(a2, b2)])
if b2 < a2:
j += 1
else:
i += 1
return res
def advantageCount(nums1, nums2):
'''
给定两个大小相等的数组 A 和 B,A 相对于 B 的优势可以用满足 A[i] > B[i] 的索引 i 的数目来描述。
返回 A 的任意排列,使其相对于 B 的优势最大化。
leetcode: 870
input: A = [2,7,11,15], B = [1,10,4,11]
output: [2,11,7,15]
思路:
1. 将不改变原来的结构的降序后的[i, nums2]组放入队列中max_q,将nums1升序排序
2. 定义r指针,代表nums1的最右端(最大值)的指针; l指针,代表nums1最左端(最小值)的指针
3. 比较nums1[r] 和 取出的max_q[0]进行逐一比较
4. 如果 nums1[r]>max_q[0],res[i]中保存nums1[r], r--;反之,res[i]保存nums1[l],l++
5. 终止条件:max_q为空
'''
from queue import PriorityQueue
class PQ(object):
def __init__(self, idx, val):
self.idx = idx
self.val = val
def __lt__(self, other):
return self.val > other.val
n = len(nums2)
l, r, res, max_q = 0, n - 1, [], PriorityQueue()
for i, num in enumerate(nums2):
max_q.put(PQ(i, num))
res.append(0)
# max_q = sorted(max_q, key=lambda x: x[1], reverse=True)
nums1 = sorted(nums1)
print(max_q, nums1)
while not max_q.empty():
m = max_q.get()
i, max_value = m.idx, m.val
if nums1[r] > max_value:
res[i] = nums1[r]
r -= 1
else:
res[i] = nums1[l]
l += 1
return res
def threeSum(nums):
'''
给你⼀个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c,使得 a + b + c = 0? 请你找出所有和为 0 且不重复的三元组。
leetcode: 15
input: nums = [-1,0,1,2,-1,-4]
output: [[-1,-1,2],[-1,0,1]]
思路:
1. 先排序,后双指针
2. res中是一个三元组,[[x,x,x],[x,x,x]]
3. 转换为twosum问题,使用双指针向中间靠拢,当sum值于target相等时,则保存该双元组
4. 优化:while循环避免重复元素
'''
def towSum(arr, start, target):
l, r = start, len(arr) - 1
tups = []
while l < r:
left, right = arr[l], arr[r]
s = left + right
if s < target:
while l < r and nums[l] == left: # 避免重复元素
l += 1
elif s > target:
while l < r and nums[r] == right:
r -= 1
else:
tups.append([left, right])
while l < r and nums[l] == left:
l += 1
while l < r and nums[r] == right:
r -= 1
return tups
nums = sorted(nums) # 排序
i, n, res = 0, len(nums), []
while i < n:
tups = towSum(nums, start=i, target=-nums[i])
for t in tups:
t.append(nums[i])
res.append(t)
while i < n - 1 and nums[i] == nums[i + 1]:
i += 1
i += 1
return res
if __name__ == "__main__":
# print(reverse_string(["h", "e", "l", "l", "o"]))
# print(remove_duplicates([1, 1, 2]))
# print(remove_element([3, 2, 2, 3], 3))
# print(remove_zero([0, 1, 0, 3, 12]))
# print(water_trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]))
# print(intervalIntersection([[0, 2], [5, 10], [13, 23], [24, 25]], [[1, 5], [8, 12], [15, 24], [25, 26]]))
# print(advantageCount([2, 7, 11, 15], [1, 10, 4, 11]))
print(threeSum([-1, 0, 1, 2, -1, -4]))