1. LeetCode Hot 100与双指针算法概述
LeetCode Hot 100是算法练习中最经典的题目集合,它筛选了平台上最高频、最具代表性的100道题目。对于准备技术面试的开发者来说,掌握这100题就相当于拿到了算法能力的"基准认证"。而在这些题目中,双指针(Two Pointers)技术出现的频率尤其高——据统计,约30%的Hot 100题目都可以用双指针或其变种来高效解决。
双指针的核心思想是通过维护两个指针(索引)在数据结构中协同移动,从而将原本O(n²)的时间复杂度优化到O(n)。这种技术在C语言中实现尤为高效,因为指针是C的核心特性之一,开发者可以直接操作内存地址,避免高级语言中迭代器的开销。典型的双指针应用场景包括:
- 数组/链表中的元素查找(如两数之和)
- 滑动窗口问题(如最长无重复子串)
- 有序数据的合并/比较(如合并两个有序数组)
- 快慢指针检测(如链表环检测)
提示:虽然现代高级语言提供了丰富的集合操作方法,但在算法面试中,面试官通常期望看到显式的指针/索引操作,因为这能直接展示你对算法本质的理解。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 双指针的两种基础模式与C实现
2.1 快慢指针(Fast-Slow Pointer)
快慢指针模式通常用于解决链表相关问题,其核心是一个指针移动速度快(如每次两步),另一个移动慢(每次一步)。这种不对称移动可以高效解决以下问题:
c复制// 链表环检测示例
bool hasCycle(struct ListNode *head) {
if (head == NULL) return false;
struct ListNode *slow = head;
struct ListNode *fast = head->next;
while (fast != NULL && fast->next != NULL) {
if (slow == fast) return true;
slow = slow->next;
fast = fast->next->next;
}
return false;
}
实现细节:
- 初始时fast比slow提前一个节点,避免首次比较就触发相等
- 循环条件检查fast及其next是否为空,防止解引用NULL指针
- 移动速度比为2:1,确保如果有环必定相遇
2.2 左右指针(Left-Right Pointer)
左右指针通常用于数组问题,两个指针分别从首尾向中间移动,典型应用如"两数之和II":
c复制// 有序数组的两数之和
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
int left = 0;
int right = numbersSize - 1;
int* result = malloc(2 * sizeof(int));
*returnSize = 2;
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum == target) {
result[0] = left + 1;
result[1] = right + 1;
return result;
} else if (sum < target) {
left++;
} else {
right--;
}
}
return result;
}
边界处理要点:
- 题目要求索引从1开始,所以返回时需+1
- C语言中需要手动分配返回数组的内存
- 循环条件
left < right确保指针不会交叉
3. Hot 100中的双指针实战案例
3.1 滑动窗口最大值(LeetCode 239)
虽然这题最优解是单调队列,但双指针可以提供一种直观的解法:
c复制int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
if (numsSize == 0) {
*returnSize = 0;
return NULL;
}
*returnSize = numsSize - k + 1;
int* result = malloc(*returnSize * sizeof(int));
for (int i = 0; i <= numsSize - k; i++) {
int max = nums[i];
for (int j = 1; j < k; j++) {
if (nums[i + j] > max) {
max = nums[i + j];
}
}
result[i] = max;
}
return result;
}
优化方向:
- 上述解法时间复杂度为O(n*k),可以通过记录窗口内的最大值索引来优化
- 当窗口滑动时,检查移出的元素是否是当前最大值,决定是否需要重新扫描窗口
3.2 盛最多水的容器(LeetCode 11)
这是双指针的经典应用,通过移动较短的边来寻找更大面积:
c复制int maxArea(int* height, int heightSize) {
int max_area = 0;
int left = 0;
int right = heightSize - 1;
while (left < right) {
int h = height[left] < height[right] ? height[left] : height[right];
int w = right - left;
int area = h * w;
if (area > max_area) {
max_area = area;
}
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return max_area;
}
算法正确性证明:
移动较短边的策略不会错过最优解,因为:
- 容器的面积由较短的边决定
- 移动较长的边不可能增加面积,因为宽度在减少而高度不会超过原短边
- 因此只有移动短边才有可能找到更大的面积
4. 双指针的进阶应用与调试技巧
4.1 复杂边界条件处理
在实现"删除排序数组中的重复项II"(LeetCode 80)时,需要处理最多保留
