0
点赞
收藏
分享

微信扫一扫

数组刷题之双指针技巧

闲云困兽 2022-05-29 阅读 90

1. 双指针技巧

在处理数组和链表相关问题时,双指针技巧是经常用到的,双指针技巧主要分为两类:左右指针快慢指针

  • 左右指针:两个指针相向而行或者相背而行
  • 快慢指针:就是两个指针同向而行,一快一慢

1.1 删除有序数组重复项

26. 删除有序数组中的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成
示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

题解:

from typing import List

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0

slow, fast = 0, 0
print(nums, slow, fast)
while fast < len(nums):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
fast += 1
print(nums, slow, fast)
return slow + 1

if __name__ == '__main__':
s = Solution()
print(s.removeDuplicates([1, 1, 2, 3])) # 3
print(s.removeDuplicates([0, 0, 1, 1, 2, 3, 4, 5, 6])) # 7
[1, 1, 2, 3] 0 0
[1, 1, 2, 3] 0 1
[1, 1, 2, 3] 0 2
[1, 2, 2, 3] 1 3
[1, 2, 3, 3] 2 4

slow 慢指针走在后面,fast 快指针走在前面,遇到不重复的元素就赋值给 slow,并 slow 前进一步,这样 nums[0, slow] 就是不重复的元素

1.2 删除排序链表中的重复元素

83. 删除排序链表中的重复元素

给定一个已排序的链表的头 head删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

输入:head = [1,1,2]
输出:[1,2]

输入:head = [1,1,2,3,3]
输出:[1,2,3]

题解:

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if head == None:
return None
slow = fast = head
while fast != None:
if fast.val != slow.val:
slow.next = fast
slow = slow.next

fast = fast.next
slow.next = None # 断开与后面重复元素的连接
return head

1.3 移除数组中指定元素

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

题解:

from typing import List

class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
if not nums:
return 0

slow, fast = 0, 0
print(f"nums: {nums}\tslow: {slow}\tfast: {fast}\tval: {val}")
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1

fast += 1
print(f"nums: {nums}\tslow: {slow}\tfast: {fast}\tval: {val}")

return slow

if __name__ == '__main__':
s = Solution()
# print(s.removeElement([0, 1, 2, 2, 3, 0, 4, 2], 2))
print(s.removeElement([0, 1, 2], 0)) # 2
nums: [0, 1, 2] slow: 0 fast: 0 val: 0
nums: [0, 1, 2] slow: 0 fast: 1 val: 0
nums: [1, 1, 2] slow: 1 fast: 2 val: 0
nums: [1, 2, 2] slow: 2 fast: 3 val: 0
  • fast 所在元素与 val 相同则跳过该元素,fast + 1
  • fast 所在元素与 val 不同时,将当前元素赋值给 slowslow 前进 1

1.4 二分法

def binary_search(nums, val):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == val:
return mid
elif nums[mid] > val: # [1, 2, 3, 4, 5] 2 3>2 index=2
right = mid - 1
else:
left = mid + 1

return -1

if __name__ == '__main__':
print(binary_search([1, 2, 3, 4, 5], 2))
print(binary_search([1, 2, 3, 4, 5], 1))
print(binary_search([1, 2, 3, 4, 5], 5))

1.5 两数之和

167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

题解:

from typing import List

class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left, right = 0, len(numbers) - 1
while left < right:
if numbers[left] + numbers[right] > target:
right -= 1
elif numbers[left] + numbers[right] < target:
left += 1
else:
return [left + 1, right + 1] # 索引从 1 开始
return [False, False]

注意:该题的前提是数组有序

1.6 反转数组

from typing import List

class Solution:
def reverse_list(self, nums: List[str]) -> List[str]:
left, right = 0, len(nums) - 1
while left < right:
temp = nums[left]
nums[left] = nums[right] # 左右两个数进行交换
nums[right] = temp
right -= 1
left += 1

return nums

if __name__ == '__main__':
s = Solution()
print(s.reverse_list(["1", "2", "3"]))
print(s.reverse_list(["3", "2", "1"]))

分析:

nums = ["1", "2", "3"]
# 第一次
left = 0, right = 2
while 0 < 2:
temp = "1"
nums[0] = nums[2] => nums = ["3", "2", "3"]
nums[2] = "1" => nums = ["3", "2", "1"]
right -= 1
left += 1

# 第二次
left = 1, right = 1
while 1 < 1:

return nums

1.7 判断字符串回文

def solution(string):
left, right = 0, len(string) - 1
while left < right:
if string[left] != string[right]:
return False
left += 1
right -= 1
return True

if __name__ == '__main__':
print(solution("aba"))
print(solution("abac"))
print(solution("1221"))

1.8 最长回文子串

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2

输入:s = "cbbd"

题解:

技巧:从中心向两端扩散的双指针技巧

因为回文串长度有可能是奇数,也有可能是偶数;如果是奇数时,可以以中间字符作为分割点向两端扩散,若是偶数时,则可以中心两个字符往外扩散:

class Solution:
def longestPalindrome(self, s: str) -> str:
long_sub = ""
for i in range(len(s)):
s1 = is_palindrome(s, i, i) # 以 s[i] 为中心的最长回文子串
s2 = is_palindrome(s, i, i+1) # 以 s[i] 和 s[i+1] 为中心的最长回文子串
long_sub = long_sub if len(long_sub) > len(s1) else s1
long_sub = long_sub if len(long_sub) > len(s2) else s2

return long_sub

def is_palindrome(s, l, r):
"""
在 s 中寻找以 s[l] 和 s[r] 为中心的最长回文串
"
""
# 防止下标越界
while l >= 0 and r < len(s):
if s[l] != s[r]:
break

# 双指针,向两边展开
l -= 1
r += 1
return s[l+1: r] # 返回以 s[l] 和 s[r] 为中心的最长回文串
举报

相关推荐

0 条评论