双指针 & 贪心算法六题通关:从回文串到跳跃游戏(Python + C++)

双指针 & 贪心算法六题通关:从回文串到跳跃游戏(Python + C++)

双指针和贪心算法是笔试面试中常见的两类技巧。双指针常用于数组、字符串的线性扫描;贪心则用于最优子结构问题。本文整理6道经典题目,每道题包含:题目描述、解题思路、图解(文本示意)、Python代码、C++代码、复杂度分析


📌 题目清单

题号 题目 核心考点
125 验证回文串 双指针 + 字符判断
680 验证回文串 II 双指针 + 一次删除机会
455 分发饼干 贪心(最小胃口分配最小饼干)
122 买卖股票的最佳时机 II 贪心(累加正利润)
55 跳跃游戏 贪心(维护最远可达)
45 跳跃游戏 II BFS / 贪心(最少步数)

1. 验证回文串(LeetCode 125)

题目描述

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,忽略字母的大小写。

示例

输入:"A man, a plan, a canal: Panama" → 输出:true

输入:"race a car" → 输出:false

解题思路

  • 使用双指针 leftright 分别指向字符串开头和结尾。
  • 跳过非字母数字字符,然后比较两个字符(统一转为小写)。
  • 如果遇到不相等,返回 false;否则移动指针继续。

图解

复制代码
s = "A man, a plan, a canal: Panama"
left=0 'A', right=30 'a' → 都转小写 'a' == 'a' → 移动
left=1 ' ', 跳过 → left=2 'm', right=29 'm' → 相等
... 直到 left>=right → true

Python代码

python 复制代码
def isPalindrome(s: str) -> bool:
    left, right = 0, len(s) - 1
    while left < right:
        # 跳过非字母数字字符
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1
        if s[left].lower() != s[right].lower():
            return False
        left += 1
        right -= 1
    return True

C++代码

cpp 复制代码
class Solution {
public:
    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;
    }
};

复杂度分析

  • 时间复杂度:O(n),每个字符最多被访问一次。
  • 空间复杂度:O(1),仅使用常数个变量。

2. 验证回文串 II(LeetCode 680)

题目描述

给定一个非空字符串,判断最多删除一个字符后,能否成为回文串。

示例

输入:"aba" → 输出:true

输入:"abca" → 输出:true(删除 'b' 或 'c' 后得到 "aca" 或 "aba")

解题思路

  • 使用双指针从两端向中间比较。
  • 当遇到字符不相等时,分别尝试删除左边字符 (即判断 s[left+1:right+1])或删除右边字符 (即判断 s[left:right])是否为回文。
  • 如果任一为真,则返回 true;否则 false。

图解

复制代码
s = "abca"
left=0 'a', right=3 'a' 相等 → left=1, right=2
left=1 'b', right=2 'c' 不相等
尝试删除b: 检查 s[2:3] = "c" 是回文 → true
或删除c: 检查 s[1:2] = "b" 是回文 → true

Python代码

python 复制代码
def validPalindrome(s: str) -> bool:
    def is_palindrome(l, r):
        while l < r:
            if s[l] != s[r]:
                return False
            l += 1
            r -= 1
        return True

    left, right = 0, len(s) - 1
    while left < right:
        if s[left] != s[right]:
            return is_palindrome(left + 1, right) or is_palindrome(left, right - 1)
        left += 1
        right -= 1
    return True

C++代码

cpp 复制代码
class Solution {
public:
    bool validPalindrome(string s) {
        auto isPal = [&](int l, int r) {
            while (l < r) {
                if (s[l] != s[r]) return false;
                ++l; --r;
            }
            return true;
        };
        int left = 0, right = s.size() - 1;
        while (left < right) {
            if (s[left] != s[right]) {
                return isPal(left + 1, right) || isPal(left, right - 1);
            }
            ++left; --right;
        }
        return true;
    }
};

复杂度分析

  • 时间复杂度:O(n),每个子串检查最多 O(n),总 O(n)。
  • 空间复杂度:O(1)。

3. 分发饼干(LeetCode 455)

题目描述

假设你是一位家长,想要给你的孩子们一些小饼干。每个孩子 i 有一个胃口值 g[i],每块饼干 j 有一个尺寸 s[j]。如果 s[j] >= g[i],可以将饼干分配给孩子,孩子得到满足。每个孩子最多一块饼干。求最多能满足多少个孩子。

示例

输入:g = [1,2,3], s = [1,1] → 输出:1

输入:g = [1,2], s = [1,2,3] → 输出:2

解题思路(贪心)

  • 将孩子胃口和饼干尺寸都排序
  • 使用双指针(或两个索引),尽可能用最小的满足胃口的饼干去满足胃口最小的孩子。
  • 如果当前饼干满足当前孩子,则计数加1,移动两个指针;否则,只移动饼干指针找更大的饼干。

图解

复制代码
g = [1,2,3], s = [1,1] 排序后相同
i=0(胃口1), j=0(饼干1): 满足 → count=1, i=1, j=1
i=1(胃口2), j=1(饼干1): 不满足 → j=2 越界结束 → count=1

Python代码

python 复制代码
def findContentChildren(g, s):
    g.sort()
    s.sort()
    i = j = 0
    while i < len(g) and j < len(s):
        if s[j] >= g[i]:
            i += 1
        j += 1
    return i

C++代码

cpp 复制代码
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int i = 0, j = 0;
        while (i < g.size() && j < s.size()) {
            if (s[j] >= g[i]) ++i;
            ++j;
        }
        return i;
    }
};

复杂度分析

  • 时间复杂度:O(m log m + n log n),排序开销。
  • 空间复杂度:O(log m + log n) 或 O(1),取决于排序实现。

4. 买卖股票的最佳时机 II(LeetCode 122)

题目描述

给定一个数组 prices,其中 prices[i] 表示第 i 天的股票价格。你可以多次买卖股票(必须先卖出才能再次买入),求最大利润。

示例

输入:[7,1,5,3,6,4] → 输出:7

解释:在第2天买入(1),第3天卖出(5)利润4;第4天买入(3),第5天卖出(6)利润3;总利润7。

解题思路(贪心)

  • 因为可以多次交易,只需累加所有上升段的差价。
  • 遍历数组,如果 prices[i] > prices[i-1],则加上差值。

图解

复制代码
prices: 7,1,5,3,6,4
差: -6, +4, -2, +3, -2
累加所有正数: 4+3=7

Python代码

python 复制代码
def maxProfit(prices):
    profit = 0
    for i in range(1, len(prices)):
        if prices[i] > prices[i-1]:
            profit += prices[i] - prices[i-1]
    return profit

C++代码

cpp 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int profit = 0;
        for (int i = 1; i < prices.size(); ++i) {
            if (prices[i] > prices[i-1]) {
                profit += prices[i] - prices[i-1];
            }
        }
        return profit;
    }
};

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

5. 跳跃游戏(LeetCode 55)

题目描述

给定一个非负整数数组 nums,你最初位于数组的第一个下标。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。

示例

输入:[2,3,1,1,4] → 输出:true

输入:[3,2,1,0,4] → 输出:false

解题思路(贪心)

  • 维护最远可达位置 maxReach
  • 遍历数组,如果当前位置 i 超过 maxReach,则无法到达。
  • 否则更新 maxReach = max(maxReach, i + nums[i])
  • 如果 maxReach >= n-1,返回 true。

图解

复制代码
nums = [2,3,1,1,4]
i=0: maxReach=2
i=1: maxReach=max(2,1+3)=4 >= 4 → true

Python代码

python 复制代码
def canJump(nums):
    max_reach = 0
    n = len(nums)
    for i in range(n):
        if i > max_reach:
            return False
        max_reach = max(max_reach, i + nums[i])
        if max_reach >= n - 1:
            return True
    return True

C++代码

cpp 复制代码
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int maxReach = 0;
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            if (i > maxReach) return false;
            maxReach = max(maxReach, i + nums[i]);
            if (maxReach >= n - 1) return true;
        }
        return true;
    }
};

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

6. 跳跃游戏 II(LeetCode 45)

题目描述

给定一个非负整数数组,你最初位于第一个位置。每个元素代表在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达最后一个位置。假设你总是可以到达最后一个位置。

示例

输入:[2,3,1,1,4] → 输出:2(跳第一步到下标1,再跳3步到末尾)

解题思路(贪心 / BFS)

  • 维护当前跳跃能到达的最远边界 end,以及下一步能到达的最远位置 maxReach
  • 遍历数组(不包括最后一个元素),每步更新 maxReach
  • i == end 时,说明需要再跳一次,跳跃次数加1,并更新 end = maxReach

图解

复制代码
nums = [2,3,1,1,4]
i=0: maxReach=2, i==end(0) → jumps=1, end=2
i=1: maxReach=max(2,1+3)=4
i=2: i==end(2) → jumps=2, end=4 → 结束
结果 jumps=2

Python代码

python 复制代码
def jump(nums):
    n = len(nums)
    if n == 1:
        return 0
    jumps = 0
    end = 0
    max_reach = 0
    for i in range(n - 1):
        max_reach = max(max_reach, i + nums[i])
        if i == end:
            jumps += 1
            end = max_reach
            if end >= n - 1:
                break
    return jumps

C++代码

cpp 复制代码
class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return 0;
        int jumps = 0, end = 0, maxReach = 0;
        for (int i = 0; i < n - 1; ++i) {
            maxReach = max(maxReach, i + nums[i]);
            if (i == end) {
                ++jumps;
                end = maxReach;
                if (end >= n - 1) break;
            }
        }
        return jumps;
    }
};

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

🎯 总结

题目 核心技巧 时间复杂度 空间复杂度
125. 验证回文串 双指针跳过非字母数字 O(n) O(1)
680. 验证回文串 II 双指针 + 一次删除尝试 O(n) O(1)
455. 分发饼干 排序 + 双指针贪心 O(m log m + n log n) O(1)
122. 买卖股票最佳时机 II 累加所有上升差 O(n) O(1)
55. 跳跃游戏 维护最远可达位置 O(n) O(1)
45. 跳跃游戏 II 贪心 + 记录边界 O(n) O(1)

双指针常用于有序序列的线性比较;贪心则要求每一步局部最优能推出全局最优。这两类题目多画图、手动模拟,很容易掌握规律。

相关推荐
WL_Aurora1 小时前
Python 算法基础篇之元组与列表
python·算法
颜安青1 小时前
【python】运算符号(后续不断补充)
开发语言·python
于先生吖1 小时前
家政派单小程序源头厂家
python
于先生吖1 小时前
口碑好的家政派单小程序
python
深度学习lover1 小时前
<数据集>yolo 货车识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·货车识别
geovindu1 小时前
Python: Condition Variable Pattern
开发语言·python·设计模式·条件变量模式
yuanpan1 小时前
Python + Pillow 实战:开发一个图片批量格式转换工具
python·microsoft·pillow
YJlio1 小时前
2023-09-25:ChatGPT 开始支持“看、听、说”,从纯文本正式迈向多模态交互
人工智能·python·科技·chatgpt·django·交互·pygame
技术工小李1 小时前
陆家嘴国泰人寿荣誉墙|iPad人脸识别人像大屏展示案例
python