【哈希表与字符串的算法之路:思路与实现】—— LeetCode

news2025/3/15 9:36:12

文章目录

  • 两数之和
  • 面试题01.02.判定是否为字符重排
  • 存在重复元素
  • 存在重复元素||
  • 字母异位词分组
  • 最长公共前缀和
  • 最长回文子串
  • 二进制求和
  • 字符串相乘

两数之和

在这里插入图片描述
这题的思路很简单,在读完题目之后,便可以想到暴力枚举,直接遍历整个数组两遍即可,但是时间复杂度高,下面是运行之后的结果
在这里插入图片描述

很简单快速的将这个题目写完了,但是有没有更高效,时间复杂度更低的方法呢?
当然!
思路:

  • 利用哈希表进行优化已经知道了target,那么遍历让x = target-nums[i]如果x在哈希表中存在,那么就存在这组元素——即结果。
class Solution 
{
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
       unordered_map<int, int> hash;
       for(int i = 0; i < nums.size(); i++)
       {
            int x = target - nums[i];
            if(hash.count(x)) return {hash[x], i};
            hash[nums[i]] = i;
       }
       return {};
    }
};

面试题01.02.判定是否为字符重排

在这里插入图片描述
这个题目也是一个很经典的题目,也是一个一眼题

  • 方法一:直接将两个字符串进行排序,相同就是true,不同就是false。时间复杂度也是最优的
class Solution 
{
public:
    bool CheckPermutation(string s1, string s2)
    {
        sort(s1.begin(),s1.end());
        sort(s2.begin(),s2.end());

        if(s1 == s2) return true;
        else return false;
    }
};
  • 方法二:可以用一个数组替代哈希表,将遍历一组字符串进入哈希表,然后再遍历另外一组字符串——从哈希表中依次减少另外一组字符串的个数,最后结果为0便是true,否则剩余数字就是false。
class Solution 
{
public:
    bool CheckPermutation(string s1, string s2) 
    {
        if(s1.size() != s2.size()) return false;

        int hash[26] = { 0 };
        //先统计第一个字符串的信息
        for(auto ch : s1)
            hash[ch - 'a']++;

        //扫描第二个字符串,看看能不能重排
        for(auto ch : s2)
        {
            hash[ch - 'a']--;
            if(hash[ch - 'a'] < 0) return false;
        }        
        return true;
    }
};

存在重复元素

在这里插入图片描述

  • 思路:
    将数组一遍遍历,一边插入到哈希表当中,如果遍历到每个元素的时候已经存在该元素,直接返回true即可,如果遍历完之后没有元素了返回false。
class Solution 
{
public:
    bool containsDuplicate(vector<int>& nums) 
    {
        // 创建一个无序集合,用于存储nums向量中的唯一元素
        unordered_set <int> hash;
        
        // 遍历nums向量中的每个元素
        for(auto x : nums)
        {
            // 如果元素已经在集合中,说明存在重复元素
            if(hash.count(x)) 
                return true;  // 如果发现重复元素,返回true
            else 
                hash.insert(x);  // 如果没有重复元素,将该元素插入集合中
        }

        // 如果遍历完后没有发现任何重复元素,返回false
        return false;
    }
};

存在重复元素||

在这里插入图片描述

  • 思路: 这题跟上面一题基本一样只不过增加了一个| i - j | <= k的条件。
class Solution 
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    {
        // 创建一个哈希表,用于存储每个元素及其最后出现的位置
        unordered_map<int, int> hash;
        
        // 遍历nums向量中的每个元素
        for(int i = 0; i < nums.size(); i++)
        {
            // 如果当前元素在哈希表中已经存在
            if(hash.count(nums[i]))
            {
                // 判断当前索引与该元素上次出现索引的差是否小于等于k
                if(i - hash[nums[i]] <= k) 
                    return true;  // 如果差值小于等于k,返回true,表示找到满足条件的重复元素
            }
            // 更新当前元素在哈希表中的位置
            hash[nums[i]] = i;
        }
        
        // 如果遍历完所有元素都没有找到满足条件的重复元素,返回false
        return false;
    }
};

字母异位词分组

在这里插入图片描述

  • 思路:
  1. 排序法:
    对于每个字符串,将其按字母排序。排序后的字符串可以作为它们的“标识符”,即所有字母异位词排序后得到的字符串是相同的。
    例如,“eat”和“tea”排序后都会变成“aet”,这使得我们能够将它们归为一组。

  2. 使用哈希表分组:
    使用一个哈希表 unordered_map<string, vector>,其中键是排序后的字符串,值是具有相同排序后的字母异位词的字符串集合。
    对于每个字符串,排序并将它放入对应的组中。如果该组已经存在,就将其添加到对应组里;如果该组不存在,就创建新组。

  3. 提取结果:
    最后,从哈希表中提取出所有的字母异位词组并返回。

class Solution 
{
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) 
    {
        // 创建一个哈希表,key是排序后的字符串,value是与该排序字符串对应的字母异位词列表
        unordered_map <string, vector<string>> hash;

        // 1. 将所有字符串排序并分组
        for(auto& s : strs)
        {
            // 将字符串复制到tmp中,然后排序
            string tmp = s;
            sort(tmp.begin(), tmp.end());
            // 根据排序后的字符串,将原始字符串加入哈希表的对应组中
            hash[tmp].push_back(s);
        }

        // 2. 从哈希表中提取出结果
        vector<vector<string>> ret;
        for(auto&[x, y] : hash)
        {
            // 将每一组字母异位词加入到结果列表中
            ret.push_back(y);
        }
        
        // 返回结果
        return ret;
    }
};

最长公共前缀和

在这里插入图片描述

  • 思路:
    这道题的思路是通过逐个字符地检查所有字符串的字符来找出最长公共前缀。从第一个字符串的第一个字符开始,逐个比较该位置上其他字符串的字符是否相同。如果遇到不同的字符或某个字符串的长度不足,就返回当前找到的公共前缀。如果没有遇到不匹配的字符,说明第一个字符串本身就是所有字符串的公共前缀,直接返回它——中心扩展算法
class Solution 
{
public:
    string longestCommonPrefix(vector<string>& strs) 
    {
        // 遍历第一个字符串的每一个字符
        for(int i = 0; i < strs[0].size(); i++)
        {
            // 获取当前字符
            char tmp = strs[0][i];
            
            // 遍历后续的字符串,检查是否在当前字符位置上与第一个字符串相同
            for(int j = 1; j < strs.size(); j++)
            {
                // 如果某个字符串的长度小于等于i,或者字符不匹配,返回从0到i的子串作为公共前缀
                if(i == strs[j].size() || tmp != strs[j][i])
                    return strs[0].substr(0, i);
            }
        }
        
        // 如果没有遇到不匹配的情况,说明第一个字符串就是公共前缀
        return strs[0];
    }
};

最长回文子串

  • 思路:
    这道题要求找到一个字符串中最长的回文子串。回文串是指从前往后和从后往前读都相同的字符串。我们可以通过 中心扩展法 来解决这个问题。每个回文子串都有一个中心,中心可以是一个字符(对于奇数长度回文串)或者两个字符之间(对于偶数长度回文串)。从每个字符(或字符间隙)向外扩展,检查左右两边的字符是否相等,直到不再相等为止。
class Solution 
{
public:
    string longestPalindrome(string s) 
    {
        // 获取字符串长度n
        int n = s.size(), begin = 0, len = 0;

        // 遍历每个字符,尝试找到以该字符为中心的回文串
        for(int i = 0; i < n; i++)
        {
            // 处理奇数长度回文串,以字符s[i]为中心
            int right = i, left = i;
            // 扩展左右两边,直到不满足回文条件
            while(left >= 0 && right < n && s[left] == s[right])
            {
                left--;
                right++;
            }
            // 更新最长回文串的起始位置和长度
            if(right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }

            // 处理偶数长度回文串,以字符s[i]和s[i+1]为中心
            right = i + 1, left = i;
            while(left >= 0 && right < n && s[left] == s[right])
            {
                left--;
                right++;
            }
            // 更新最长回文串的起始位置和长度
            if(right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }
        }

        // 返回最长回文子串
        return s.substr(begin, len);
    }
};

二进制求和

在这里插入图片描述

  • 思路:
    我们从 a 和 b 的末尾(即最低位)开始逐位进行加法运算,类似于手动加法的过程。每一位相加时,判断是否需要进位。进位的处理通过变量 t 来存储,当两位数字加起来大于或等于2时,t 就会向下一位传递进位。最终,逐位的结果被累积到字符串 ret 中,并且需要反转,因为我们从低位到高位计算。整个过程确保处理了两者的不同长度以及最终的进位。
class Solution 
{
public:
    string addBinary(string a, string b) 
    {
        string ret; // 用于存储结果的字符串
        int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0; // cur1, cur2分别是a和b的当前字符索引,t是进位

        // 遍历两个字符串直到都遍历完并且没有进位
        while(cur1 >= 0 || cur2 >= 0 || t)
        {
            // 如果a还有剩余位,取出a对应位置的数字并加到t中
            if(cur1 >= 0) t += a[cur1--] - '0';  
            // 如果b还有剩余位,取出b对应位置的数字并加到t中
            if(cur2 >= 0) t += b[cur2--] - '0';  

            // 将当前位的结果加到结果字符串中,t % 2是当前位(0或1),进位需要除以2
            ret += t % 2 + '0';
            t /= 2; // 更新进位,t // 2
        }

        // 由于我们是从低位到高位处理的,最后需要反转结果字符串
        reverse(ret.begin(), ret.end());

        return ret; // 返回加法结果的二进制字符串
    }
};

字符串相乘

在这里插入图片描述

  • 思路:
    这道题目要求实现两个大整数的乘法。我们不能直接将两个大整数转换为整数进行计算,因为它们的长度可能超出整数类型的表示范围。我们通过模拟竖式乘法的方法来手动计算乘积。首先,我们逆序遍历两个字符串,将每一位的数字相乘并加到临时结果数组中,处理乘法的每一位。然后,我们处理进位,最后将结果反转并处理前导零,得到最终的乘积结果。
    在这里插入图片描述
    在这里插入图片描述
class Solution 
{
public:
    string multiply(string num1, string num2)
    {
        // 获取两个字符串的长度
        int m = num1.size(), n = num2.size();

        // 反转字符串,以便从低位开始计算
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());

        // 临时数组用于存储每一位的乘积结果
        vector<int> tmp(m + n - 1);

        // 进行无进位的相乘
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                tmp[i + j] += (num1[i] - '0') * (num2[j] - '0');
            
        // 处理进位
        int cur = 0, t = 0;
        string ret;
        while(cur < m + n - 1 || t != 0)
        {
            // 将当前的乘积加到结果中
            if(cur < m + n - 1) t += tmp[cur++];
            
            // 取当前位的数字,并更新进位
            ret += t % 10 + '0';
            t /= 10;
        }

        // 处理前导零
        while(ret.size() > 1 && ret.back() == '0') ret.pop_back();

        // 反转最终的结果字符串
        reverse(ret.begin(), ret.end());

        return ret;  // 返回最终的乘积结果
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2315369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hello Mr. My Yesterday日文歌词附假名注音,祭奠逝去的青春

hello mr. my yesterday Hundred Percent Free Hello Mr. my yesterday云っておくれよ “夢叶うその瞬間にまた逢える”と 前方の幾多前途多難の未知 後方の道後悔も知った 経験と価値 夢なかば 一本の道結果だが ひとつだけ知りたいよ 神様がいるのなら “幸せの定義っ…

ubuntu ollama+dify实践

安装ollama 官网的指令太慢了&#xff0c;使用以下指令加速&#xff1a; export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download" curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/dow…

S7-1200 G2移植旧版本S7-1200程序的具体方法示例

S7-1200 G2移植旧版本S7-1200程序的具体方法示例 前期概要: S7-1200 G2必须基于TIA博途V20,之前的程序可通过移植的方式在新硬件上使用。 该移植工具可自动将TIA Portal 项目从 S7-1200 移植到更新的S7-1200 G2。 注意: 该插件支持在同一TIA Portal项目实例内将软件和/或硬…

新办公室哪款空气净化器除甲醛效果好?高效除甲醛,提升效率

现代办公环境中&#xff0c;空气质量对员工的健康与工作效率产生着不可忽视的影响。尤其是新装修的办公室&#xff0c;往往因为空气中的甲醛浓度超标而导致一系列健康问题。因此&#xff0c;选择一款性能优越的除甲醛空气净化器就显得尤为重要。合适的空气净化器不仅可以有效过…

大视频背景暗黑风格的wordpress企业主题免费下载

整体风格是黑色的&#xff0c;首页首屏大视频背景&#xff0c;动态效果非常好。向下滚动时&#xff0c;滚动的特效也不错。 原文 https://www.bixugao.com/wp/26.html

C语言基础知识04

指针 指针概念 指针保存地址&#xff0c;地址是字节的编号 指针类型和保存的地址类型要一直 使用时注意&#xff0c;把地址转换为&变量的格式来看 int a[3]; a转为&a[0] 指针的大小 64bit 固定8字节&#xff0c; 32bit 固定4字节 指针…

VUE的脚手架搭建引入类库

VUE的小白脚手架搭建 真的好久好久自己没有发布自己博客了,对于一直在做后端开发的我 ,由于社会卷啊卷只好学习下怎么搭建前端,一起学习成长吧~哈哈哈(最终目的,能够懂并简易开发) 文章目录 VUE的小白脚手架搭建1.下载node.js2.安装vue脚手架3.创建一个项目4.代码规范约束配置(…

android lmkd.rc 介绍

service service lmkd /system/bin/lmkdclass coreuser lmkdgroup lmkd system readproccapabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCEcriticalsocket lmkd seqpacketpasscred 0660 system systemtask_profiles ServiceCapacityLow属于核心服务组&#xff0…

Matlab 双线性插值(二维)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 双线性插值是一种 二维插值方法,用于计算 栅格(Grid) 或 像素点 之间的插值值。它主要用于 图像缩放、旋转、变换 等操作,以在新像素位置估算灰度值或颜色值。 如上图所示,假设存在一个二维离散函数(如图像)…

TCP 三次握手四次挥手过程详解

注&#xff1a;本文为 “TCP 的三次握手与四次挥手” 相关文章合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;未整理去重。 英文引文第二篇&#xff0c;实为国内《稀土掘金技术社区》文章&#xff0c;没检索到原文&#xff0c;此处 “出口转内销” 。 如有内…

程序编译生成的文件

目录 .i 文件 .s 文件 .o文件 总结 在 C 编程中&#xff0c;.i、.s和 .o 文件是编译过程中生成的不同阶段的文件&#xff0c;它们代表不同的含义&#xff1a; .i 文件 全称 &#xff1a;预处理后的文件&#xff08;Intermediate File&#xff09;。 含义&#xff1a;.i文件…

C++类的基础题(4)

练习1&#xff1a;&#xff08;简单&#xff09; 基于如下程序&#xff0c;按要求修改和完善。 #include <iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {numn;scores;} void displ…

MindGYM:一个用于增强视觉-语言模型推理能力的合成数据集框架,通过生成自挑战问题来提升模型的多跳推理能力。

2025-03-13&#xff0c;由中山大学和阿里巴巴集团的研究团队提出了MindGYM框架&#xff0c;通过合成自挑战问题来增强视觉-语言模型&#xff08;VLMs&#xff09;的推理能力。MindGYM框架通过生成多跳推理问题和结构化课程训练&#xff0c;显著提升了模型在推理深度和广度上的表…

WPS 搭配 Zotero 插件使用

安装Zotero后&#xff0c;Word自动引入了插件&#xff0c;但WPS却没有&#xff0c;做为WPS的重度用户&#xff0c;这是不行的。 解决方案&#xff1a; 1.找到 Zotero.dotm 一般在安装目录下&#xff0c; 2.然后复制到WPS的startup下 我的目录是&#xff1a;C:\Users\lianq…

汽车NVH诊断案例 | 纯电车急加速过大弯底盘异响

引言 失去发动机的掩蔽效应后&#xff0c;新能源电车的NVH问题&#xff0c;成为了困扰维修技师新难点。风噪、胎噪、电机高频啸叫等问题更容易车主识别&#xff0c;根源却难以被有效分辨。如何更精准且高效地识别电车NVH问题根源&#xff1f;今天分享的这个案例&#xff0c;内…

万字长文详解嵌入式电机软件开发

第一章&#xff1a;嵌入式电机概述 1.1 电机类型&#xff1a;选对 “主角” 有多重要&#xff1f; 在嵌入式电机控制系统里&#xff0c;电机就如同故事中的主角&#xff0c;选对了方能使整个剧情顺利推进。不同应用场景对精度、速度、功率以及成本的需求各异&#xff0c;因而了…

电机控制常见面试问题(十二)

文章目录 一.电机锁相环1.理解锁相环2.电机控制中的锁相环应用3.数字锁相环&#xff08;DPLL&#xff09; vs 模拟锁相环&#xff08;APLL&#xff09;4.锁相环设计的关键技术挑战5.总结 二、磁链观测1.什么是磁链&#xff1f;2.为什么要观测磁链&#xff1f;3.怎么观测磁链&am…

卡尔曼滤波算法从理论到实践:在STM32中的嵌入式实现

摘要&#xff1a;卡尔曼滤波&#xff08;Kalman Filter&#xff09;是传感器数据融合领域的经典算法&#xff0c;在姿态解算、导航定位等嵌入式场景中广泛应用。本文将从公式推导、代码实现、参数调试三个维度深入解析卡尔曼滤波&#xff0c;并给出基于STM32硬件的完整工程案例…

韦伯望远镜的拉格朗日点计算推导过程,包含MATLAB和python运动轨迹仿真代码

研究过程 起源与提出&#xff1a;1687 年牛顿提出 “三体问题”&#xff0c;旨在研究三个可视为质点的天体在相互之间万有引力作用下的运动规律&#xff0c;但因运动方程过于复杂&#xff0c;难以得到完全解。欧拉的贡献1&#xff1a;1767 年&#xff0c;瑞士数学家莱昂哈德・…

STM32 RS232通信开发全解析 | 零基础入门STM32第五十九步

主题内容教学目的/扩展视频RS232串口电路原理&#xff0c;跳线设置&#xff0c;驱动程序。与超级终端通信。了解电路原理和RS232协议。 师从洋桃电子&#xff0c;杜洋老师 &#x1f4d1;文章目录 一、RS232通信系统架构二、RS232核心原理与硬件设计2.1 电气特性对比2.2 典型电路…