算法百练,直击OFFER -- day5

前言

🔥个人主页:不会c嘎嘎
📚专栏传送门:【数据结构】【C++】【Linux】【算法】【MySQL】
🐶学习方向:C++方向学习爱好者
⭐人生格言:谨言慎行,戒骄戒躁

每日一鸡汤:

"你现在的努力,不是为了感动谁,也不是为了证明给谁看,而是为了将来有一天,你能有底气去选择自己想要的生活,而不是被生活选择。别怕路长梦远,总有人山高水长为你而来。"

目录

开篇

1.验证回文串II

题目描述

核心算法

解题思路

流程图

具体代码

2.最长回文串

题目描述

核心算法

解题思路

​编辑

具体代码

3.寻找重复数

题目描述

核心算法

解题思路

流程图

具体代码

结语


开篇

今天给大家带来三道面试题分别是:

1.【验证回文串II】https://leetcode.cn/problems/RQku0D/description/ 字节跳动-2024-开发
2.【最长回文串】https://leetcode.cn/problems/longest-palindrome/description/ "谷歌-2024-开发 字节跳动-2023-开发
3.【寻找重复数】https://leetcode.cn/problems/find-the-duplicate-number/description/ 彭博-2024-开发

同学们可以先自己看看能不能独立的做出来,然后再来看看我的解法思路哦!

1.验证回文串II

题目描述

给定一个非空字符串 s,请判断如果 最多从字符串中删除一个字符能否得到一个回文字符串。

核心算法

碰撞双指针

解题思路

相信大家都做过回文串1,回文串就是两边对称的字符串。验证一个字符串是否为回文串只需要从两边向中间一直比较字符,直到两个指针相遇"或直到 left > right,如果全都相同说明是回文串。

1.题目给出了可以删除一个字符,那我们就按照回文串的方式验证当前字符串。

2.当字符相同的时候两边指针向中间逼近,继续比较后续的两个字符。

3.当遇到不相同的字符时,说明我们需要做出一次'删除'选择。此时我们有两种选择:要么删除左指针字符(验证 [left + 1, right] 区间),要么删除右指针字符(验证 [left, right - 1] 区间)。只要其中任意一个区间是回文串,原字符串即符合要求。

流程图

示例:woldbaabdow

1.当left = 2 right = 8的时候,两个字符不相同,此时需要跳过字符

2.跳过下标2的字符,再验证剩下的字符串是否回文。明显这里dbaabd是回文串

3.当两个指针相遇的时候循环结束

具体代码

cpp 复制代码
// 辅助函数:验证子串是否为回文
// 注意:这里使用 const string& s 避免字符串拷贝,提高性能
bool check(int left, int right, const string& s) {
    while (left < right) {
        if (s[left] != s[right]) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

bool validPalindrome(string s) {
    if (s.size() <= 2) return true;
    
    int left = 0, right = s.size() - 1;
    while (left < right) {
        if (s[left] != s[right]) {
            // 遇到不匹配时,尝试删除左边字符 OR 删除右边字符
            // 只要有一边成功,结果就为 true
            return check(left + 1, right, s) || check(left, right - 1, s);
        }
        left++;
        right--;
    }
    return true;
}

2.最长回文串

题目描述

给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的 回文串 的长度。在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串。

核心算法

哈希表 + 贪心算法

解题思路

1.分析回文特性 :我们实际上并不用真的构造回文串,只需要计算长度,构成回文串的核心在于:除了最中间的字符外,其他字符必须成对出现 。例如 aabbb 可以组成 abbba,其中 ab 各有一对,剩下的一个 b 放中间。

2.统计词频:既然要看字符能否凑成对,首先利用哈希表(或数组)统计字符串中每个字符出现的次数。

3.计算偶数部分 :遍历哈希表,对于每个字符的数量 count,其能组成回文串的长度贡献为 count / 2 * 2(即取最大的偶数)。

4.处理中心位 :如果在统计过程中,发现有字符的数量是奇数(说明凑完对子后有剩余),或者最终长度小于原字符串长度,说明我们可以选一个字符放在回文串的最中间,此时最终长度 +1

具体代码

cpp 复制代码
class Solution {
public:
    int longestPalindrome(string s) {
        // 使用数组模拟哈希表(ASCII字符范围)
        // 也可以使用 unordered_map<char, int> map;
        int map[128] = {0}; 
        
        // 1. 统计每个字符出现的次数
        for(char c : s) {
            map[c]++;
        }
        
        int len = 0;
        // 2. 遍历统计结果
        for(int i = 0; i < 128; i++) {
            // 加上该字符数量的偶数部分
            // 比如 5/2 = 2, 2*2 = 4,说明有4个可以组成回文
            len += map[i] / 2 * 2;
        }
        
        // 3. 判断是否有中心点
        // 如果当前计算的长度小于原字符串长度,说明肯定有剩下的字符(奇数个的字符)
        // 我们可以在中间放一个,所以长度 +1
        if (len < s.size()) {
            len++;
        }
        
        return len;
    }
};

3.寻找重复数

题目描述

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。假设 nums 只有 一个重复的整数 ,返回 这个重复的数

核心算法

快慢指针(Floyd 判圈算法) & 数组抽象化

解题思路

这道题的难点在于如何在不修改数组且空间复杂度为 O(1)的情况下解决。

1.抽象为链表 : 由于数组中数值范围是 [1, n],而数组下标范围是 [0, n],我们可以建立一种映射关系:将下标 i 看作当前节点,nums[i] 看作指向下一个节点的指针(即 next)。

  • 例如 [1,3,4,2,2]

    • 0 -> 1

    • 1 -> 3

    • 3 -> 2

    • 2 -> 4

    • 4 -> 2 (这里指向了已经访问过的节点2,形成了环)

  • 因为存在重复的数字(多个下标指向同一个值),所以这个抽象链表中一定存在 ,且环的入口就是那个重复的数

2.快慢指针找相遇点 : 定义 slowfast 指针,slow 每次走一步,fast 每次走两步。如果有环,它们必然会在环内相遇。

3.数学推导找入口: 假设:

  • 起点到环入口的距离为 a。

  • 环入口到相遇点的距离为 b。

  • 相遇点再走 c步回到环入口(即环周长 L = b + c)。

当两者相遇时:

  • slow 走的距离:a + b

  • fast 走的距离:a + b + kL (k 为在环里绕的圈数)

  • 因为 fast 速度是 slow 的两倍:2(a + b) = a + b + kL

  • 化简得:a = kL - b

  • 进一步转化为:a = (k-1)L + (L - b) = (k-1)L + c

结论 :这就意味着,一个指针从起点出发(走 a 步),另一个指针从相遇点出发(走 c 步,或者多绕几圈后的 c 步),它们最终会在环的入口点相遇。

流程图

数组模拟链表操作

链表抽象图

具体代码

cpp 复制代码
int findDuplicate(vector<int>& nums) {
    int slow = 0;
    int fast = 0;
    
    // 1. 第一阶段:快慢指针寻找环内的相遇点
    // 由于必定有重复值,所以必定有环,循环不会死锁
    while (true) {
        slow = nums[slow];           // 慢指针走一步
        fast = nums[nums[fast]];     // 快指针走两步
        
        if (slow == fast) {
            break; // 找到相遇点
        }
    }
    
    // 2. 第二阶段:寻找环的入口(即重复的数)
    // 将 slow 重置回起点,fast 停留在相遇点
    // 此时两个指针每次都只走一步
    slow = 0;
    while (true) {
        slow = nums[slow];
        fast = nums[fast];
        // 当它们再次相遇时,所在的节点就是环的入口
        if (slow == fast) {
            break;
        }
    }
    
    return slow;
}

结语

本文分享了三个算法面试题的解法:1. 验证回文串II(字节跳动)使用碰撞双指针,当字符不匹配时尝试删除左/右字符验证剩余部分是否为回文;2. 最长回文串(谷歌/字节跳动)通过哈希表统计字符频率,利用贪心算法计算偶数对并处理中心位;3. 寻找重复数(彭博)将数组抽象为链表,应用快慢指针的Floyd判圈算法找到环入口即重复数。

以上就是本期博客的全部内容,感谢各位的阅读以及观看。如果内容有误请大佬们多多指教,一定积极改进,加以学习。

相关推荐
序属秋秋秋2 小时前
《Linux系统编程之进程环境》【环境变量】
linux·运维·服务器·c语言·c++·操作系统·系统编程
Aileen_0v02 小时前
【Gemini3.0的国内use教程】
android·人工智能·算法·开源·mariadb
CoderYanger2 小时前
C.滑动窗口——1423. 可获得的最大点数
java·开发语言·算法·leetcode·1024程序员节
乌萨奇也要立志学C++2 小时前
【洛谷】二分查找专题 告别二分死循环!模板 + 细节 + 实战
c++·算法
Rock_yzh2 小时前
LeetCode算法刷题——128. 最长连续序列
数据结构·c++·算法·哈希算法
WolfGang0073212 小时前
代码随想录算法训练营Day32 | 518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶)
算法
wheeldown2 小时前
【Rokid+CXR-M】基于Rokid CXR-M SDK的博物馆AR导览系统开发全解析
c++·人工智能·ar
利刃大大3 小时前
【c++中间件】语音识别SDK && 二次封装
开发语言·c++·中间件·语音识别
晨非辰3 小时前
C++ 波澜壮阔 40 年:从基础I/O到函数重载与引用的完整构建
运维·c++·人工智能·后端·python·深度学习·c++40周年