双指针算法
·
文章目录
双指针算法是算法设计中非常实用且高效的技巧,特别适合处理线性数据结构(数组、链表、字符串)。我将从原理、分类、应用场景到具体代码实现,为你全面解析这个算法思想。
一、双指针算法核心思想
双指针通过使用两个指针变量在数据结构中有规律地移动,以 O (n) 的时间复杂度解决问题。两个指针的移动方式主要有三种模式:
- 同向移动(快慢指针)
- 相向移动(左右指针)
- 滑动窗口(特殊双指针变种)
二、双指针算法分类详解
1. 快慢指针(同向指针)
应用场景:链表操作、原地修改数组、检测循环
典型示例:删除排序数组中的重复项
int removeDuplicates(vector<int>& nums) {
if(nums.empty()) return 0;
int slow = 0;
for(int fast = 1; fast < nums.size(); ++fast) {
if(nums[fast] != nums[slow]) {
nums[++slow] = nums[fast];
}
}
return slow + 1;
}
代码解析:
slow指针标记有效序列末尾fast指针扫描所有元素- 时间复杂度:O (n),空间复杂度:O (1)
2. 左右指针(相向指针)
应用场景:有序数组、两数之和、回文判断
典型示例:两数之和 II
vector<int> twoSum(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left < right) {
int sum = nums[left] + nums[right];
if(sum == target) {
return {left+1, right+1};
} else if(sum < target) {
++left;
} else {
--right;
}
}
return {};
}
代码解析:
- 利用数组有序特性缩小搜索范围
- 每次排除不可能的情况
- 时间复杂度:O (n),空间复杂度:O (1)
3. 滑动窗口(特殊双指针)
应用场景:子串/子数组问题
典型示例:无重复字符的最长子串
int lengthOfLongestSubstring(string s) {
unordered_set<char> window;
int max_len = 0, left = 0;
for(int right = 0; right < s.size(); ++right) {
while(window.count(s[right])) {
window.erase(s[left++]);
}
window.insert(s[right]);
max_len = max(max_len, right - left + 1);
}
return max_len;
}
代码解析:
left和right维护当前窗口- 哈希集合快速判断重复
- 时间复杂度:O (n),空间复杂度:O (字符集大小)
三、双指针的四大经典应用场景
1. 链表操作
// 检测链表环
bool hasCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while(fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast) return true;
}
return false;
}
2. 数组去重
// 移除指定元素
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.size(); ++fast) {
if(nums[fast] != val) {
nums[slow++] = nums[fast];
}
}
return slow;
}
3. 字符串处理
// 反转字符串
void reverseString(vector<char>& s) {
int left = 0, right = s.size()-1;
while(left < right) {
swap(s[left++], s[right--]);
}
}
4. 多维数组扫描
// 搜索二维矩阵 II
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int row = 0, col = matrix[0].size()-1;
while(row < matrix.size() && col >= 0) {
if(matrix[row][col] == target) return true;
matrix[row][col] > target ? --col : ++row;
}
return false;
}
四、双指针算法设计要点
- 指针移动条件:明确每个指针何时需要移动
- 终止条件:正确处理边界条件防止越界
- 初始位置:合理设置指针初始位置(头尾、同起点等)
- 移动步长:确定每次移动的步长(固定 1 步或动态调整)
五、常见错误及调试技巧
- 死循环:检查指针移动条件和终止条件
- 数组越界:确保访问前检查边界
- 遗漏情况:使用测试用例验证边界条件
- 指针同步:注意多指针间的联动关系
六、复杂度分析对比
| 问题类型 | 暴力解法 | 双指针解法 |
|---|---|---|
| 两数之和 | O (n²) | O (n) |
| 数组去重 | O (n²) | O (n) |
| 最长无重复子串 | O (n³) | O (n) |
| 链表环检测 | O (n) 哈希法 | O (n) |
七、LeetCode 进阶练习题
-
中等难度:
-
- 三数之和
-
- 盛最多水的容器
-
- 接雨水
-
-
困难难度:
-
- 最小覆盖子串
-
- 串联所有单词的子串
-
- 最小区间
-
八、总结与提升建议
双指针算法的核心在于 通过合理组织指针运动,将暴力解法中的冗余计算消除。掌握这个算法需要:
- 理解指针移动的内在逻辑
- 积累常见问题模式
- 进行系统性题目训练
- 学会分析指针移动的数学原理
建议从简单题开始,逐步体会不同场景下指针的移动策略,最终达到灵活运用的水平。记住:双指针不是死板的模板,而是一种通过组织计算顺序来优化效率的思想。
更多推荐


所有评论(0)