当前位置: 首页 > article >正文

双指针算法详解:原理、应用场景及代码示例

文章目录

      • 双指针算法概述
      • 主要应用场景及实现思路
        • 1. 数组排序后的两数之和
        • 2. 链表中环的检测
        • 3. 滑动窗口
        • 4. 合并两个有序数组
        • 5. 移除数组中的重复元素
      • 总结

双指针算法概述

原理
双指针算法的核心在于使用两个指针遍历数据结构,而不是单个指针。这种方法在某些情况下可以显著减少时间复杂度,特别是在处理数组和链表时。

主要应用场景及实现思路

1. 数组排序后的两数之和

应用场景

  • 在已排序的数组中查找两个数,使它们的和等于特定的目标值。
  • 适用于需要高效查找特定组合的情况。

实现思路

  • 初始化两个指针,一个指向数组的起始位置(左指针),另一个指向数组的末尾位置(右指针)。
  • 计算两个指针所指向元素的和。
  • 根据和与目标值的比较结果,调整指针位置:
    • 如果和小于目标值,移动左指针向右。
    • 如果和大于目标值,移动右指针向左。
    • 如果和等于目标值,返回指针的位置。
  • 继续调整指针位置,直到找到答案或两个指针相遇。

代码示例

def two_sum_sorted(arr, target):
    left, right = 0, len(arr) - 1
    
    while left < right:
        current_sum = arr[left] + arr[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1
    
    return []

# 测试
arr = [1, 2, 3, 4, 6]
target = 10
print(two_sum_sorted(arr, target))  # 输出: [3, 4]
2. 链表中环的检测

应用场景

  • 判断链表中是否存在环。
  • 适用于防止无限循环和确保链表结构的正确性。

实现思路

  • 使用两个指针,一个慢指针每次前进一步,一个快指针每次前进两步。
  • 如果链表中存在环,快指针最终会追上慢指针。
  • 如果快指针到达链表末尾(即 fastfast.nextNone),则说明链表中没有环。

代码示例

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

def has_cycle(head):
    if not head or not head.next:
        return False
    
    slow = head
    fast = head.next
    
    while fast and fast.next:
        if slow == fast:
            return True
        slow = slow.next
        fast = fast.next.next
    
    return False

# 测试
# 创建一个带有环的链表
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node2  # 创建环

print(has_cycle(node1))  # 输出: True
3. 滑动窗口

应用场景

  • 解决字符串或数组中子串/子数组的问题。
  • 特别适用于需要找到满足某些条件的最小子数组长度或最大和等情况。

实现思路

  • 使用两个指针作为窗口的两端,根据条件调整窗口大小。
  • 当窗口满足条件时,尝试缩小窗口以寻找更优解。
  • 通过不断调整窗口的位置和大小,直到找到满足条件的最佳解。

代码示例

from collections import Counter

def min_window(s, t):
    need = Counter(t)
    missing = len(t)
    left, start, end = 0, 0, 0
    
    for right, char in enumerate(s):
        if need[char] > 0:
            missing -= 1
        need[char] -= 1
        
        if missing == 0:
            while left < right and need[s[left]] < 0:
                need[s[left]] += 1
                left += 1
            
            if end == 0 or right - left < end - start:
                start, end = left, right + 1
            
            need[s[left]] += 1
            missing += 1
            left += 1
    
    return s[start:end]

# 测试
s = "ADOBECODEBANC"
t = "ABC"
print(min_window(s, t))  # 输出: "BANC"
4. 合并两个有序数组

应用场景

  • 将两个已排序的数组合并成一个新的有序数组。
  • 适用于需要高效合并多个有序数据集的情况。

实现思路

  • 分别为两个数组设置指针,从头开始比较两个数组的当前元素。
  • 将较小的元素放入结果数组中,并移动对应的指针。
  • 继续这一过程,直到其中一个数组的所有元素都被处理完毕。
  • 最后,如果还有剩余未处理的数组,则将其余部分直接复制到结果数组的末端。

代码示例

def merge_sorted_arrays(arr1, arr2):
    merged = []
    i, j = 0, 0
    
    while i < len(arr1) and j < len(arr2):
        if arr1[i] < arr2[j]:
            merged.append(arr1[i])
            i += 1
        else:
            merged.append(arr2[j])
            j += 1
    
    # 添加剩余的元素
    while i < len(arr1):
        merged.append(arr1[i])
        i += 1
    
    while j < len(arr2):
        merged.append(arr2[j])
        j += 1
    
    return merged

# 测试
arr1 = [1, 3, 5]
arr2 = [2, 4, 6]
print(merge_sorted_arrays(arr1, arr2))  # 输出: [1, 2, 3, 4, 5, 6]
5. 移除数组中的重复元素

应用场景

  • 在非递减数组中去除重复出现的元素,使每个元素只出现一次。
  • 适用于需要高效去重的情况。

实现思路

  • 使用两个指针,一个用于追踪当前处理到的非重复元素的位置,另一个用于遍历整个数组。
  • 遍历过程中,如果发现当前元素与前一个元素不同,则将其放置在追踪指针的位置,并向前移动追踪指针。
  • 遍历结束后,追踪指针之前的数组部分就是去重后的数组。

代码示例

def remove_duplicates(arr):
    if not arr:
        return 0
    
    write_pointer = 1  # 写指针,记录下一个非重复元素的位置
    
    for read_pointer in range(1, len(arr)):
        if arr[read_pointer] != arr[read_pointer - 1]:
            arr[write_pointer] = arr[read_pointer]
            write_pointer += 1
    
    return write_pointer

# 测试
arr = [1, 1, 2, 2, 3, 4, 4, 5]
length = remove_duplicates(arr)
print(arr[:length])  # 输出: [1, 2, 3, 4, 5]

总结

双指针算法通过巧妙地利用两个指针来遍历数据结构,能够在多种场景下提供高效的解决方案。以下是双指针算法的一些关键点:

  • 时间复杂度:通常可以将时间复杂度从 O(n^2) 降低到 O(n),显著提高效率。
  • 空间复杂度:大多数情况下,双指针算法的空间复杂度为 O(1),因为不需要额外的存储空间。
  • 灵活性:适用于多种数据结构和问题类型,包括数组、链表、字符串等。

希望这些详细的讲解和代码示例能帮助你更好地理解和应用双指针算法。如果你有任何进一步的问题或需要更多的示例,请随时告诉我。


http://www.kler.cn/a/405598.html

相关文章:

  • 基于 ESP-AT (v3.x)固件通过 AT+SYSMFG 指令更新证书设置
  • 深述C++模板类
  • 每天五分钟深度学习:神经网络模型的直观理解
  • 前端常用内容
  • 汽车免拆诊断案例 | 2012款路虎揽胜运动版柴油车加速无力
  • 《Qt Creator:人工智能时代的跨平台开发利器》
  • 特殊 ABAP SQL 表达式 NULL
  • 【论文复现】语言模型中的多模态链式推理
  • springboot实战(15)(注解@JsonFormat(pattern=“?“)、@JsonIgnore)
  • Vm桥接模式下的网卡选择
  • 【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
  • 16. 【.NET 8 实战--孢子记账--从单体到微服务】--汇率获取定时器
  • 如何在Linux上安装Canal同步工具
  • JDBC编程---Java
  • 完全竞争市场
  • 理解折半查找法
  • go的依赖注入究竟是毒药还是解药
  • Windows系统运行库软件游戏修复工具
  • 【MySQL】阶段性总结
  • iOS构建版本以及Hbuilder打iOS的ipa包全流程