双指针常用方法

1.双指针介绍

双指针是解题时一种常见的思路,一般有两种用法。

1)两个指针反方向,分别从数组开头和结尾开始移动,例如对有序数组的搜索。

2)两个指针同方向移动,例如快慢指针,都是从数组开头开始遍历,只是速度不一样。

除了用于数组,也可以用于链表,树,图。

2.反向的双指针

力扣icon-default.png?t=N2N8https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/

因为数组是非递减的,所以可以数组首尾各置一个指针,若值相加大于目标值,则尾指针自减,若值相加小于目标值,则头指针自增,这样就一步步逼近了目标值。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int i = 0, j = numbers.size()-1;
        while(i<j){
            int t = numbers[i] + numbers[j];
            if(target==t) return vector<int>({i+1,j+1});
            else if(target>t){
                i++;
            }else{
                j--;
            }
        }
        return vector<int>();
    }
};

3.同向的双指针

力扣icon-default.png?t=N2N8https://leetcode.cn/problems/linked-list-cycle-ii/这题有两个要点,先是要判断链表中是否有环,接着是找到环的入口。

判断是否有环,可以用快慢指针。

两个指针同时从起点出发,快指针一次两步,慢指针一次一步。

如果链表中有环,则快慢指针一定会相遇,就像在操场上一直跑圈,速度快的人一定会在某一刻比速度慢的人多跑一圈,所以二人相遇了。

若是快慢指针没有相遇,且快指针指向了NULL,那很明显,就是没有环。

确定链表有环后,就是寻找环的入口了。

可以用题目中的示例来简单理解一下。

下图使用快慢指针,从起点[3]出发,慢指针一次一步,快指针一次两步,很快这两个指针会在节点[-4]相遇。

相遇后,将慢指针移回链表起点[3],快慢指针都一次走一步,两个指针再次相遇的节点[2],就是环的入口。

 一个简单易懂的解释就是:

慢指针路径:起点--环的入口--快慢指针相遇的节点

快指针路径:起点--环的入口--快慢指针相遇的节点--环的入口--快慢指针相遇的节点

因为快指针路径==慢指针路径*2

所以【快慢指针相遇的节点--环的入口--快慢指针相遇的节点】== 【 起点--环的入口--快慢指针相遇的节点】

同时减去【环的入口--快慢指针相遇的节点】

所以【快慢指针相遇的节点--环的入口】== 【 起点--环的入口】

所以找到环得到入口就是将慢指针移到起点,与快指针都是一次一步,直到相遇,相遇节点就是环的入口。

如果还是没有理解的,可以直接搜索【Floyd 判圈法】找动画视频直观理解一下。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head) return NULL;
        ListNode* slow = head, *fast = head;
        while(slow->next && fast->next && fast->next->next){
            slow = slow->next;
            fast = fast->next->next;
            if(slow==fast){
                slow = head;
                while(slow!=fast){
                    slow = slow->next;
                    fast = fast->next;
                }
                return slow;
            }
        }
        return NULL;
    }
};

<think>首先,用户的问题是“双指针算法及常用场景”,我需要用中文回答这个问题。 回顾规则: - 回答必须使用中文。 - 回答完问题后,必须提出3-5个相关问题。 - 相关问题必须以“§§相关问题§§”开头,作为第一行输出。 - 禁止换行分散,也就是说“§§相关问题§§”必须作为单独一行完整输出,然后列出问题。 - 相关问题基于用户的问题和我的回答提出。 - 确保所有内容符合规则,否则回答无效。 我的回答结构: 1. 先解释双指针算法是什么。 2. 然后列出常用场景。 3. 最后添加“§§相关问题§§”和3-5个相关问题。 定义双指针算法: - 双指针算法是一种在数组或链表中使用两个指针(索引)来遍历数据结构的技术,常用于优化时间复杂度和简化问题。 - 常见类型:快慢指针、左右指针、滑动窗口等。 - 常用场景:查找、排序、求和、检测环、字符串操作等。 详细展开: - **算法描述**:双指针算法涉及初始化两个指针(例如,left和right),根据条件移动它们,直到满足特定条件。 - **常用场景**: - 在有序数组中查找两数之和等于目标值。 - 链表中的快慢指针检测环。 - 反转数组或字符串。 - 滑动窗口用于子数组问题(如最大子数组和、最小覆盖子串)。 - 删除排序数组中的重复元素。 - 合并两个有序数组或链表。 - 字符串匹配或回文字符串检测。 确保回答完整后,提出相关问题。 - 相关问题必须基于用户的问题“双指针算法及常用场景”和我的回答。 - 数量:3-5个。 - 示例格式中,问题以数字列表形式输出。 可能的3-5个相关问题: 1. 双指针算法在检测链表环时的具体实现步骤是什么? 2. 滑动窗口应用中常见的错误有哪些? 3. 如何证明双指针算法在特定场景下的正确性? 4. 双指针算法与暴力解法相比的时间复杂度优势体现在哪里? 5. 在不同编程语言中实现双指针算法的最佳实践是什么? 最终输出: - 先回答问题。 - 然后换行输出“§§相关问题§§”(单独一行)。 - 接着列出问题,格式如“1. ...\n2. ...\n3. ...”。 代码示例:如果用户问题涉及代码,我应该提供一个代码示例。用户提到“双指针算法”,所以我应该添加一个简单代码来演示。 - 例如,双指针在有序数组中查找两数之和。 - 使用Python或伪代码。 示例代码: ```python def two_sum(nums, target): left, right = 0, len(nums) - 1 while left < right: current_sum = nums[left] + nums[right] if current_sum == target: return [left, right] # 或返回索引 elif current_sum < target: left += 1 else: right -= 1 return None # 如果没有找到 ``` 完整回答: - 解释算法。 - 列出场景。 - 提供代码示例。 - 然后“§§相关问题§§”。 - 最后问题列表。 确保中文。 </think>双指针算法是一种在数组、链表或字符串中使用两个指针(索引)来协同遍历数据结构的技术,目的是优化时间复杂度(通常从O(n²)降到O(n))或简化逻辑。其核心思想是通过指针移动满足特定条件。常见类型包括快慢指针、左右指针和滑动窗口。以下是详细展开及常用场景: ### **算法核心描述** - **基本实现**:初始化两个指针(如`left`和`right`),根据问题条件逐步移动指针,直到指针相遇或满足目标。 - **时间效率**:理想情况下时间复杂度为O(n),避免了嵌套循环,空间复杂度O(1)。 - **适用数据结构**:数组(有序或无序)、链表、字符串等。 ### **常用场景与示例** 1. **有序数组求两数之和**: 给定一个升序数组和目标值,查找两个元素的索引使其和等于目标值。 示例代码(Python): ```python def two_sum(nums, target): left, right = 0, len(nums) - 1 while left < right: curr_sum = nums[left] + nums[right] if curr_sum == target: return [left, right] # 返回索引 elif curr_sum < target: left += 1 # 移动左指针增大和 else: right -= 1 # 移动右指针减小和 return None # 无解 ``` 2. **链表检测环(快慢指针)**: 在单链表中使用两个速度不同的指针(快指针每次移动两步,慢指针一步),若指针相遇则存在环。 时间复杂度:O(n)。 3. **滑动窗口求子数组问题**: 如求最大连续子数组和或最小覆盖子串,指针定义窗口边界,移动右指针扩展窗口,移动左指针收缩窗口。 示例:LeetCode "最大子数组和"问题中,双指针可简化动态规划。 4. **反转数据结构**: 在数组或字符串中,使用左右指针从两端向中间交换元素。 示例:反转字符串`s = ['h', 'e', 'l', 'l', 'o']`,代码: ```python def reverse_string(s): left, right = 0, len(s) - 1 while left < right: s[left], s[right] = s[right], s[left] # 交换元素 left += 1 right -= 1 return s ``` 5. **删除排序数组重复项**: 使用快慢指针,慢指针指向去重后位置,快指针遍历数组。 时间复杂度O(n),优于暴力法。 6. **回文字符串验证**: 左右指针从两端向中间比较字符是否对称。 7. **合并有序数组**: 两个指针分别指向不同数组的起始位置,逐个比较合并。 ### **注意事项** - **边界条件**:指针初始化(如数组长度0)、移动条件(如避免索引越界)。 - **适用性局限**:不适用于无序数据结构或需要额外空间的场景。 - **优势和劣势**:优势是高效简洁;劣势是逻辑可能复杂,需仔细处理指针移动条件。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值