双指针C++[算法详解+Leetcode例题练习]

目录

什么是双指针? 

对撞指针

快慢指针

LeetCode27 移除元素(快慢)

LeetCode26 删除有序数组中的重复项(快慢)

LeetCode283 移除零(快慢)

LeetCode125 验证回文串(对撞)

LeetCode 11 盛最多水的容器(对撞)

LeetCode 844 比较含退格的字符串(对撞) 


什么是双指针? 

双指针是一种在遍历数组/字符串时使用两个索引变量的方法,常用于优化暴力遍历
双指针常见的两种形式:
1. 对撞指针 
含义:从两端往中间靠
应用场景:排序数组中找两数、判断回文
2. 快慢指针
含义:两个指针同向走,一个快一个慢
应用场景:去重、链表找环、移动元素

对撞指针

对撞指针通过设置两个指针从数组两端向中间移动,根据条件调整指针位置以解决问题。

基本写法(伪代码):

int left = 0, right = n - 1;
while(left < right) {
	if(条件满足) {
	// 做一些处理
} else if(条件不满足) {
	// 移动指针
	left++; // 或right--
}
}

快慢指针

快慢指针设置移动速度不同的两个指针,快指针每次移动步数多于慢指针,通过相对位置关系解决问题。

基本写法(伪代码):

int slow = 0;
for(int fast = 0; fast < n; fast++) {
    if(满足条件) {
	nums[slow++] = nums[fast];
    }
}

下面通过题目练习双指针

LeetCode27 移除元素(快慢)

int removeElement(vector& nums, int val) {
    int slow = 0; 
    for (int fast = 0; fast < nums.size(); fast++) {
        if (nums[fast] != val) {
            nums[slow++] = nums[fast];
        }
    }
    return slow; 
}

通过快慢指针遍历数组并将所有不等于 val 的元素移到数组前面,最终返回有效元素的数量。


LeetCode26 删除有序数组中的重复项(快慢)

int removeDuplicates(vector& nums) {
    int slow = 0;
    for (int fast = 1; fast < nums.size(); fast++) {
        if (nums[fast] != nums[slow]) {
            nums[++slow] = nums[fast];
        }
    }
    return slow + 1; // slow 是索引,需要+1返回长度
}

通过快慢指针去重,遍历数组并将不同的元素移动到数组前面,最终返回去重后数组的长度。

疑问点:为什么最后返回slow + 1

因为:
LeetCode 27 中 slow 是新数组的长度
LeetCode 26 中 slow 是最后一个新值的索引,所以长度要 +1

换句话说前者把slow当作索引,后者把++slow当作索引
 

LeetCode283 移除零(快慢)

这里给出两种解法

void moveZeroes(vector& nums) {
    int slow = 0;
    for(int fast = 1; fast < nums.size(); fast++) {
        if(nums[slow] == 0 && nums[fast] != 0) {
            swap(nums[slow],nums[fast]);
        }     
        if(nums[slow] != 0) slow++;         
    } 
}

通过快慢指针将非零元素交换到数组前面,最终将所有零元素移动到数组末尾。

void moveZeroes(vector& nums) {
    int slow = 0; 
    for (int fast = 0; fast < nums.size(); fast++) {
        if (nums[fast] != 0) {
            if (slow != fast) { 
                swap(nums[slow], nums[fast]);
            }
            slow++; 
        }
    }
}

这个解法关键在于slow++,也就是说当fast位置元素为0时,slow索引不能再加1,方便下一次交换


LeetCode125 验证回文串(对撞)

bool isPalindrome(string s) {
    int left = 0, right = s.size() - 1;
    while(left < right) {
        while(left < right && !isalnum(s[left])) left++; 
        while(left < right && !isalnum(s[right])) right--;
        if(tolower(s[left]) != tolower(s[right])) return false;
        left++;
        right--;
    }   
    return true;
}

通过对撞指针忽略非字母数字字符并逐个比较字符串两端的字符,判断是否为回文字符串。

需要留意的点:

!isalnum(s[left]) 表示当前字符不是字母或数字。只有当字符是非字母数字时,指针 left 才会右移。一旦遇到字母或数字,循环就会停止。
 

LeetCode 11 盛最多水的容器(对撞)

int maxArea(vector& height) {
    int left = 0, right = height.size() - 1;
    int max_area = 0;

    while (left < right) {
        int width = right - left;
        int h = min(height[left], height[right]);
        max_area = max(max_area, width * h);

        if (height[left] < height[right]) left++;
        else right--;
    }

    return max_area;
}

通过对撞指针计算每对边界的容器面积,动态更新最大面积,最终返回最大水容量。

LeetCode 844 比较含退格的字符串(对撞) 

bool backspaceCompare(string S, string T) {
    int i = S.length() - 1, j = T.length() - 1;
    int skipS = 0, skipT = 0;

    while (i >= 0 || j >= 0) {
        while (i >= 0) {
            if (S[i] == '#') {
                skipS++, i--;
            } else if (skipS > 0) {
                skipS--, i--;
            } else {
                break;
            }
        }
        while (j >= 0) {
            if (T[j] == '#') {
                skipT++, j--;
            } else if (skipT > 0) {
                skipT--, j--;
            } else {
                break;
            }
        }
        if (i >= 0 && j >= 0) {
            // 都有有效字符,直接比较
            if (S[i] != T[j]) {
                return false;
            }
        } else {
            // 一个有有效字符,一个没有,直接不相等
            if (i >= 0 || j >= 0) {
                return false;
            }
        }
        i--, j--;
    }
    return true;
}

代码符合对撞指针思想
        虽然指针是从后向前移动,但它们的目标是逐步靠近字符串的起始位置,即向中间 “对撞”。当两个指针都到达字符串的起始位置时,循环结束。
        每次循环中,指针 i 和 j 都会处理当前字符并向左移动,直到无法继续移动或字符不匹配。这本质上是在缩小需要比较的子串范围,与对撞指针的 “缩小搜索空间” 逻辑相同。

你可能感兴趣的:(算法,c++,leetcode,双指针)