leetcode高频题——双指针

一、盛最多水的容器

此题为leetcode第11题
思路:用两个指针指向数组的头尾,以上面的1、7为例,此时水的容量为min(1, 7) * 8=8。然后考虑移动指针,因为水的容量是由最小的那个数决定的,所以应该移动数小的那个指针,这样后面才有机会得到一个大数,使得水的容量变大。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        if len(height) < 2:
            return 0
        i, j = 0, len(height) - 1
        res = 0
        while i <= j:
            res = max(min(height[i], height[j]) * (j - i), res)
            if height[i] <= height[j]:
                i += 1
            else:
                j -= 1
                
        return res

二、删除链表倒数第n个节点

此题为leetcode第19题
在这里插入图片描述

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        res = ListNode(0)
        res.next = head
        
        p1 = p2 = res
        for i in range(n + 1):
            p1 = p1.next
        while p1:
            p1 = p1.next
            p2 = p2.next
        p2.next = p2.next.next
        return res.next

三、删除排序数组中的重复项

此题为leetcode第26题
思路:采用双指针slow和fast,fast向前移动,当nums[fast] != nums[slow]的时候slow才移动一个,然后将nums[fast]的值赋给nums[slow]。这样当nums[fast] == nums[slow]的时候说明是重复项,slow不动,fast继续移动,考察下个元素是否是重复项。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        i = 0
        for j in range(len(nums)):
            if nums[i] != nums[j]:
                i += 1
                nums[i] = nums[j]
        return i + 1

四、接雨水

此题为leetcode第42题
思路:此题与“盛最多水的容器”相似,但这个题要求只要是两个空隙间都有水,因此初始的res在后面可能要叠加。启发于那道题,同样使用双指针,指向首尾。当某个指针的数比较大时,盛水的量由较小的那个数决定,因此当右指针大于左指针时就遍历左指针,左指针大于右指针时就遍历右指针。然后在遍历的过程中,盛水的形状是凹的。以遍历左指针为例,我们需要记录一下当前左半边最高柱子left_max,然后每移动一步指针就累积一下指针所指列盛的水,即res += (left_max - height[left])。所指高度高于了left_max,那就将此高度作为left_max,此时不累积水。同理右边也是一样。由于一开始便规定了一个指针大于另一个指针时就遍历另一个指针,因此只要遍历的指针不大于最大值,累积上的水一定是有效的。此题也可以用栈来解决。

class Solution:
    def trap(self, height: List[int]) -> int:
        # 双指针
        left, right = 0, len(height) - 1
        left_max, right_max = 0, 0
        res = 0
        while left < right:
            if height[left] < height[right]:
                if height[left] >= left_max:
                    left_max = height[left]
                else:
                    res += (left_max - height[left])
                left += 1
            else:
                if height[right] >= right_max:
                    right_max = height[right]
                else:
                    res += (right_max - height[right])
                right -= 1
        return res

五、颜色分类

此题为leetcode第75题
思路:此问题又称荷兰国旗问题,其主要思想是给每个数字设定一种颜色,并按照荷兰国旗颜色的顺序进行调整。这里只有3种数字,0、1、2,我们可以想象有一个指针curr,其左边都小于等于它,右边都大于等于它,即遇到0的话就往左边移,遇到2就往右移,需要保证左边的0都是连续地挨在一起,右边的2也都是连续地挨在一起,剩下的就是1。我们需要一个指针p0记录左边连续0的右边界,和另一个指针p2记录右边连续2的左边界。如果curr指向0,那么nums[curr]和nums[p0]交换,然后curr和p2都右移一步;如果curr指向2,那么nums[curr]和nums[p2]交换,p2左移一步;如果curr指向1,那么只curr右移一步。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        p0 = curr = 0
        p2 = len(nums) - 1
        while curr <= p2:
            if nums[curr] == 0:
                nums[p0], nums[curr] = nums[curr], nums[p0]
                curr += 1
                p0 += 1
            elif nums[curr] == 2:
                nums[p2], nums[curr] = nums[curr], nums[p2]
                p2 -= 1
            else:
                curr += 1

六、环形链表

1、环形链表

此题为leetcode第141题
思路:使用快慢指针,两个指针初始指向头指针,然后快指针一次走2步,慢指针一次走1步,这样走下去,如果有环的话,快慢指针肯定会相遇;如果没有环,快指针会首先走到结尾

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        fast = slow = head
        while fast and slow and fast.next:
            fast = fast.next.next
            slow = slow.next
            if slow == fast:
                return True
        return False
2、环形链表II

此题为leetcode第142题
思路:和上题类似,我们也可以用快慢指针判断是否有环。但快慢指针相遇的地方不一定是入环的第一个节点,因此无法直接判断入环节点是哪个。我们将上题的过程抽象成如下图:
在这里插入图片描述
快慢指针相遇时,快指针走过了F、a、b、a这几步,而慢指针走过了F、a这几步。因为快指针每次走2步,慢指针每次走1步,快指针走过的距离是慢指针的2倍,所以可以列出一个等式: F + a + b + a = 2 ( F + a ) F+a+b+a=2(F+a) F+a+b+a=2(F+a),解得 F = b F=b F=b。那么当快慢指针相遇时,可以用一个新指针指向头结点,慢指针和新指针每次移动1步,相遇时即为入环节点。

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        p1 = p2 = head
        while p2 and p2.next:
            p1 = p1.next
            p2 = p2.next.next
            if p1 == p2:
                p = head
                while p != p1:
                    p = p.next
                    p1 = p1.next
                return p
        return None

七、相交链表

此题为leetcode第160题
思路:这个和环形链表有点相似,我们也把它想成追赶问题。如下图所示,假设a<b,每次两个节点各走一步。p1会先到none,但我们希望p1和p2相等,相等的节点即为相交节点(如果都为none说明没有相交节点)。所以在指针指到none后想办法让其再回到头结点,再继续走,直到两个指针相等,此时两个指针走过的步数应该是一样的。假设p1走到none后再走x步,p2走到none后再走y步两者会相遇,那么有 a + c + x = b + c + y a+c+x=b+c+y a+c+x=b+c+y,得 a + x = b + y a+x=b+y a+x=b+y。那么可以令 x = b , y = a x=b,y=a x=b,y=a,这个指针会相遇。这个的含义是,当p1指针指向none时,下一步指向p2的头结点,p2指向none时,下一步指向p1的头结点。
在这里插入图片描述

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        # 双指针
        p1, p2 = headA, headB
        while p1 != p2:
            p1 = p1.next if p1 is not None else headB
            p2 = p2.next if p2 is not None else headA
        return p1

八、移动零

此题为leetcode第283题
思路:此题和颜色分类有一点类似,我们也可以用两个指针p1和p2,p2不断向右移动,p1指向连续一串0的右边第一个非0数。p2移动的时候,如果nums[p2]不等于0,那么交换nums[p1]和nums[p2],两个指针都移动一步,这样p1的左边一直是0。否则的话,只有p2移动。

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        if len(nums) <= 1:
            return nums
        
        p1, p2 = 0, 0   # p1左边都是非0数,p2要寻找为0的数
        while p2 < len(nums):
            if nums[p2] != 0:
                nums[p1], nums[p2] = nums[p2], nums[p1]
                p1 += 1
            p2 += 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值