• 双指针问题的算法


    双指针主要分两类: 快慢指针和左右指针

    快慢指针

    对于链表问题, 我们一般可以使用快慢指针解决
    所谓的快慢指针是指, 使用两个指针按照不同的速度前进, 有两个指针我们可以确定:

    1. 链表是否有环
    2. 链表的倒数第k个节点

    一些题目

    比如: 141题环形链表

    注意: 两个指针相遇则有环 (类比在操场跑步, 速度不相等总能相遇)

    代码

    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            if not head:
                return False
            fast_node = head.next
            slow_node = head
            while fast_node and fast_node.next:
                if slow_node == fast_node:
                    return True
                fast_node = fast_node.next.next
                slow_node = slow_node.next
            return False
    

    又比如:

    左右指针

    左右指针一般的形式就是我们常说的二分查找(二分搜索)

    一般的使用方式:

    def binary_search(nums: List[int], target: int):
        left = 0
        right = ...
        while ...:
            mid = left + (right - left) / 2
            if nums[mid] == target:
                ...
            elif nums[mid] < target:
                left = ...
            elif nums[mid] > target:
                right = ...
                
        return ...
    

    我们需要的注意点为...部分:

    1. 前两个...有由搜索区间决定, 即问题为[left : right][left : right)
      [left: right]: right = len(nums) - 1while left <= right
      [left: right): right = len(nums)while left < right
      前者的结束条件为: [right + 1 : right](如: [3 : 2])
      后者的结束条件为: [left : right](如: [2 : 2])
    2. 后面的...由题目决定
      nums[mid] == target: 返回某个值的问题, 直接返回; 返回边界的问题, 缩小边界(right = mid或left = mid)
      nums[mid] > target: 太大了, 一般为缩小右边界:right = mid - 1
      nums[mid] < target: 太小了, 一般为缩小左边界:left = mid + 1

    对于一些问题, 我们可以将问题的区间统一为[left : right], 即right = len(nums) - 1while left <= right

    比如:
    返回某个值的问题

    def binary_search(nums: List[int], target: int):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
            elif nums[mid] == target:
                # 直接返回
                return mid
        return -1
    
    

    在leetcode上对应的题目:704.二分查找(简单) 题解

    返回左边界的问题

    def left_bound(nums: List[int], target: int):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
            elif nums[mid] == target:
                # 别返回,锁定左侧边界
                right = mid - 1
    
        # 最后要检查 left 越界的情况
        # left可能超出范围 即 >= len(nums)
        if left >= len(nums) or nums[left] != target:
            return -1
        return left
    
    

    结束条件为: [right + 1 : right](如: [3 : 2])
    返回左边界left

    返回右边界的问题

    def right_bound(nums: List[int], target: int):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
            elif nums[mid] == target:
                # 别返回,锁定右侧边界
                left = mid + 1
    
        # 最后要检查 right 越界的情况
        if right < 0 or nums[right] != target:
            return -1
    
        return right
    
    

    结束条件为: [right + 1 : right](如: [3 : 2])
    返回右边界right

    在leetcode上对应的题目:

    滑动窗口

    滑动窗口是双指针的一种, 也可以说是左右指针的一种
    其主要解决的是子串问题
    所谓的子串问题是指: 一个长的字符串是否全部包含 一个短的字符串 (包括数量)
    滑动窗口的思想:
    ① 我们可以设两个指针, 分别对应窗口的左右边界
    ② 右边界向右扩大窗口, 直到找到符合条件的子串
    ③ 左边界向右缩小窗口, 更新数据, 根据题目是否返回
    ④ 假如还未返回, 则重复②和③
    ⑤ 根据题目是否返回

    模板:

    def sliding_window(s: str, t: str):
        """
        :param s 大的字符串
        :param t 小的字符串
        :return 根据题目返回
        
        """
        need = {}  # 存放需要的字符数
        windows = {}  # 存放当前窗口中的字符数
        # 记录需要的字符数
        for char in t:
            # 需要字符的数量自增1
            need[char] = need.setdefault(char, 0) + 1  # setdefault作用字典中不存在字符时, 返回0
        # 左右边界的指针
        left = right = 0
        # 用于记录已经通过检验的字符数
        valid = 0
        """
        假如有其他变量可以在这里添加
        """
        while right < len(s):
            # right_char为将要移入的窗口字符
            right_char = s[right]
            # 右移窗口
            right += 1
            # 假如当前 右边界 的字符,为需要的字符
            if right_char in need:
                """
                更新数据
                一般是
                1. 当前窗口的数据
                2. 通过的 字符数
                """
                # 记录当前窗口符合要求的字符数量
                windows[right_char] = windows.setdefault(right_char, 0) + 1
                # 假如当前字符已经符合要求
                # 通过检验的字符数 + 1
                if windows[right_char] == need[right_char]:
                    valid += 1
            
            # 判断左边界是否可以收缩
            # !!!! 这个条件根据题目而变
            while condition:
                """
                根据题目是否在这里返回
                """
                
                # left_char为将要移出的窗口字符
                left_char = s[left]
                left += 1
                if left_char in need:
                    """"
                    更新数据
                    一般是
                    1. 当前窗口的数据
                    2. 通过的 字符数
                    
                    自定义的其他变量也在这里更新
                    """
                    # 假如当前字符已经符合要求
                    # 通过检验的字符数 - 1
                    if windows[left_char] == need[left_char]:
                        valid -= 1
                    # 记录当前窗口符合要求的字符数量 - 1
                    windows[left_char] = windows.setdefault(left_char, 0) - 1
        """
        根据题目是否在这里返回
        """
    
    

    这个模板的主要注意点是

    1. 是否需要一些其他的变量, 如当前子串的长度
    2. 边界缩小的条件
    3. 什么时候返回

    一些题目

  • 相关阅读:
    文件操作:根据现有类生成所需要的类
    Microsoft JScript 运行时错误: “”未定义
    未完成
    WPF模拟雷达界面效果图
    WebClient模拟网页提交表单
    201319
    Delphi中的InStrRev函数(倒找文本)
    利用IDhttp实现图片下载
    白话解释哈希表
    整理的Delphi常用字符串函数
  • 原文地址:https://www.cnblogs.com/lczmx/p/15859259.html
Copyright © 2020-2023  润新知