【Leetcode】vector刷题

news/2024/5/20 3:34:44

Alt

🔥个人主页Quitecoder

🔥专栏Leetcode刷题

Alt

目录

  • 1.只出现一次的数字
  • 2.杨辉三角
  • 3.删除有序数组中的重复项
  • 4.只出现一次的数字II
  • 5.只出现一次的数字III
  • 6.电话号码的字母组合

1.只出现一次的数字

题目链接:136.只出现一次的数字
题目描述在这里插入图片描述

这道题很简单,我们只需要遍历一遍数组,利用异或操作的性质(一个数与自身异或结果为0,任何数与0异或还是其本身)

class Solution {
public:int singleNumber(vector<int>& nums) {int value =0;for(auto v:nums){value^=v;}return value;}
};

2.杨辉三角

题目链接:118.杨辉三角
题目描述在这里插入图片描述

这道题我们需要构造二维数组,典型的vector的嵌套使用

在这里插入图片描述
首先,我们先构建二维数组,开辟行数大小:

vector<vector<int>> v(numRows);

接着对每一行进行开辟空间,并将两端初始化为1

for(int i=0;i<numRows;i++)
{v[i].resize(i+1);v[i][0]=1;v[i][i]=1;
}

注意,resize是会进行初始化的,我们没有传值,默认为零

所以我们只需要遍历一遍,遍历到的位置为0,进行相加操作

完整代码如下:

class Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> v(numRows);for(int i=0;i<numRows;i++){v[i].resize(i+1);v[i][0]=1;v[i][i]=1;}for(int i=0;i<numRows;i++){for(int j=0;j<i;j++){if(v[i][j]==0){v[i][j]=v[i-1][j]+v[i-1][j-1];}}}return v;}
};

3.删除有序数组中的重复项

题目链接:26.删除有序数组中的重复项
题目描述在这里插入图片描述

这题是一道简单的双指针思路的题,由于已经排序好,我们只需要设置两个索引,一个向后遍历,若与前面的索引指向值不相同,则对前面的值进行修改

lass Solution {
public:int removeDuplicates(vector<int>& nums) {if (nums.size() == 0) {return 0;}int slow = 0;for (int fast = 1; fast < nums.size(); fast++) {if (nums[fast] != nums[slow]) {slow++;nums[slow] = nums[fast];}}return slow + 1;}
};

完成了值的覆盖过程

4.只出现一次的数字II

题目链接:137.只出现一次的数字II
题目描述在这里插入图片描述

这个问题的解决方案基于位操作和有限状态自动机的原理。我们要处理的数字是32位整数,因此,我们需要考虑每一位相加后的结果。由于除了一个数字以外,其它数字都出现了三次,我们可以构造一个数字的每一位相加后,模3的结果就是这个只出现一次的数字的相应位

思路如下:

使用两个整数变量onestwosones将会记录每个位只出现一次的情况,而twos将会记录每个位出现两次的情况

对于每个数字num及其每一位,我们更新onestwos

  1. 在第i个位置上,如果ones里的位是1,则表示num要么是第一次遇到i位为1,要么是第四次。如果是第四次,我们已经在twos里记录了两次,所以这次应该把ones里的该位清零,否则保持不变

  2. 同理,如果twos里的位是1,则是第二次遇到i位为1或者是第五次。如果是第五次,我们既要在ones里面加1,同时也要在twos里面清零该位,否则保持不变

  3. 由于我们只需要考虑每个位上1出现的次数,所以任何时候位上的1出现3次,我们都应该清零

最后,ones保留的就是每位上出现一次的结果,而twos将会是0。

class Solution {
public:int singleNumber(vector<int>& nums) {int ones = 0, twos = 0;       for (int num : nums) {ones = (ones ^ num) & ~twos;twos = (twos ^ num) & ~ones;}return ones;}
};

当我们讨论处理出现三次的数字和一个只出现一次的数字时,onestwos 的位操作确实是难以理解的 ,分解这两行代码:

对于每一个新的数字 num,我们用 onestwos 来跟踪彼此独立的状态:

  1. ones = (ones ^ num) & ~twos;

这里,我们正更新 ones 以包含出现一次的位。让我们分解这行代码:

  • ones ^ num:这个按位异或操作背后的思想是:当前的 ones 表示上一步迭代中已经出现一次的位。当我们再次看到这些位时(即 num 中的对应位也是1),我们希望重置 ones 中的那些位(因为出现一次变成了两次)。对于 num 中新出现的1,ones 中还没有记录,这将被加进 ones

  • & ~twos:接下来的按位与操作~twos 结合表示:我们删除 twos 中已经出现两次的位。~twos 是对 twos 取反,意味着取出 twos 中为0的位。只有那些在 twos 中没有记录(即还没达到两次)的1才应该加入 ones。即使刚才 ones ^ num 把某些位变成了1,若那些位在 twos 中已经出现过两次,我们必须确保它们在 ones 中不变成1

结合二者,ones 在每次迭代结束时仅保留那些恰好出现一次的位。如果某位在 ones 中变成了1但已经在 twos 中出现过,我们需要重置 ones 中的那位为0

  1. twos = (twos ^ num) & ~ones;

接着我们更新 twos 来反映那些已经看到两次的位:

  • twos ^ num:与更新 ones 类似,我们对于每个新来的 num,我们都会用按位异或更新 twos。如果在 twos 中的位是1,且对应的 num 中的位也是1,那么它们会重置为0,因为现在这个位出现了第三次,而我们的目标是找到出现了一次和两次的位。如果出现的是一个新的1(即 num 中的1,而 twos 中并没有记录),twos 就会记录它。这会出现加到三的情况,我们随后会处理。

  • & ~ones:这个按位与操作保证如果在 ones 中有1(意味着这个位已经出现了一次),我们不会在 twos 中加入该位。如果某个位同时在 onestwos 中出现,这意味着这个位出现了3次,并且最终会被忽略。

通过 & ~ones,我们确保了一个位仅仅当它在 num 中为1且在 ones 中尚未出现(即 ones 中为0)时,才会被加入 twos

总结来说,这两步操作是相互独立并且排他的:它们保证一个位在 onestwos 中出现,但不会同时出现。我们在整体数组中使用循环来考虑每个数字的影响。最终,由于所有出现三次的数字在这两个变量中都被消去,ones 会留下那个出现一次的唯一位

5.只出现一次的数字III

题目链接:260.只出现一次的数字III
题目描述在这里插入图片描述

此类问题可以通过位运算(异或操作)来解决。首先,我们可以通过对所有数组元素执行异或操作来找出两个只出现一次的元素的异或结果。因为异或操作具有交换律和结合律,同时一个数字和自己进行异或会变成0,所以最终剩下的结果就是那两个只出现一次的数字的异或结果

这个结果中至少有一个位是1(否则这两个数相同),我们可以找到这个数中的任何一个为1的位,用它来把原数组分成两组,一组在该位上是1,一组在该位上是0。这样每组就包含了一个只出现一次的数字和一些成对出现的数字。然后再对这两个组分别进行异或操作,即可得到这两个只出现一次的数字。

下面是这个算法实现:

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {// 第一步,对所有元素进行异或,最终的结果就是两个只出现一次数的异或结果int diff = 0;for (int num : nums) {diff ^= num;}// 找到diff中任何为1的位,可以使用diff & -diff快速找到// 这个操作可以隔离出diff最右端的1unsigned int diff_unsigned = diff;diff_unsigned &= -diff_unsigned;// 使用找到的这一位将数组中的数字分成两组vector<int> results(2, 0); // 最终结果for (int num : nums) {if ((num & diff_unsigned) == 0) {// 第一组,与diff_unsigned对应位为0results[0] ^= num;} else {// 第二组,与diff_unsigned对应位为1results[1] ^= num;}}return results;}
};

在这个代码中:

  • diff_unsigned 最终会被设置为两个目标数字的异或结果。
  • diff_unsigned &= -diff_unsigned; 的结果是取出 diff_unsigned 最右边的1位,也就是两个只出现一次的数在这一位上不同的地方。
  • 然后我们通过判断这一位是否为1来将全部数字分为两组,并再次分别对它们进行异或操作,以此找到两个只出现一次的数。

这条语句 diff_unsigned &= -diff_unsigned; 是一种计算机用来找到一个数字中最右边的1的位,并且保持所有其他位为0的技巧。为了更好地理解这个技巧,我们需要先了解计算机中的数字表示——特别是补码表示法,因为这个技巧与负数的二进制表示相关

在补码表示中,一个负数是通过取其正值的二进制表示的反码(每个位取反)然后加1得到的。例如,假设我们有一个4位的系统:

正数 2 的二进制表示:  0010
反码 (invert):      1101
加1得到负数 -2:      1110

观察发现,从正数2的二进制表示到负数-2的表示,最右边的1以及之前的所有0都保持不变,而最右边的1之后的所有位都翻转了。这给了我们一种找到最右边的1的方法。现在,如果我们对2和-2执行按位与操作:

正数 2:                0010
负数 -2:               1110
按位与:                0010

按位与操作的结果就是只有最右边的1保留了下来,其它所有位都变成了0。换句话说,diff_unsigned &= -diff_unsigned; 将结果的所有位都置为0,除了最右边的1所在的位。

在解决问题时,我们首先会通过对所有数字进行异或得到 diff,这代表了两个只出现一次的数字的差异。
diff 变量首先被转换成一个无符号整数 diff_unsigned,然后对它进行取负和按位与操作,以避免未定义行为。这样就保证了即使 diff 的最高有效位是1,我们也不会超出无符号整型的范围

然后使用 diff_unsigned &= -diff_unsigned; 来保留最右边的1,这是两个独特数字在二进制表示中第一个不同的位。

通过这个位的差异,我们可以将所有的数字分成两组来进一步操作,每组包含一个只出现一次的数字以及成对出现的数字。这个1所在的位将用于分辨哪些数字在该位为0或1 —— 这正是对数组进行划分的依据

6.电话号码的字母组合

题目链接:17.电话号码的字母组合
题目描述在这里插入图片描述

这个问题可以通过回溯法解决,这是一种通过穷举所有可能的解来找到全部解的算法。基本思想是从左到右遍历数字字符串,对于每个数字,向当前的字母组合中添加对应的每个字母,然后对剩余的字符串重复这个过程。

下面是递归解决实现:

class Solution {
public:vector<string> letterCombinations(string digits) {if (digits.empty()) return {}; // 如果输入为空,直接返回空数组vector<string> mappings = {  // 数字到字母的映射"", "", "abc", "def",   // '0','1','2',..."ghi", "jkl", "mno","pqrs", "tuv", "wxyz"};vector<string> result;string current;backtrack(result, digits, 0, current, mappings);return result;}private:void backtrack(vector<string>& result, const string& digits, int index, string& current, const vector<string>& mappings) {if (index == digits.length()) { // 如果到达了数字字符串的末尾,就添加当前的字母组合到结果中result.push_back(current);return;}string letters = mappings[digits[index] - '0']; // 获取当前数字对应的所有字母for (char letter : letters) { // 遍历这些字母current.push_back(letter);   // 添加当前的字母backtrack(result, digits, index + 1, current, mappings);  // 继续处理下一个数字current.pop_back();  // 回溯,移除当前字母,以便尝试下一个字母}}
};

这段代码定义了一个辅助函数 backtrack,用来递归寻找所有可能的字母组合。我们维护一个 current 字符串,它保存当前的部分组合。函数的工作流程是这样的:

  1. 确定终止条件:如果 current 的长度与输入数字字符串的长度相同,说明当前递归路径已经走到头,我们找到了一个完整的字母组合,将其添加到结果中。

  2. 确定递归逻辑:从 mappings 数组中获取当前处理的数字对应的所有可能字母,然后逐一向 current 添加每个字母,并递归地调用自己处理下一个数字。

  3. 回溯处理:每次递归调用完成后,需要将之前添加的字母移除,以便对当前位置尝试不同的字母。


http://www.mrgr.cn/p/50558222

相关文章

使用 NVM 动态切node版本

一、安装nvm 官网链接&#xff1a; Release 1.1.9 coreybutler/nvm-windows GitHub 无脑安装直接下一步 安装完之后验证一下&#xff1a; #打开命令行输入命令 nvm 这样就是安装好了&#xff0c;然后我们开始安装node。 二、使用nvm安装node 1、去node官网获取版本号 …

【Linux】帮助类命令

在Linux中&#xff0c;man用于查看系统手册页&#xff08;manual pages&#xff09;。它用于查阅关于特定命令、函数、工具或文件格式的详细信息。要使用man命令&#xff0c;只需在终端中输入man&#xff0c;后跟您要查看的命令或主题的名称。 例如&#xff0c;如果查看ls命令…

JEECG/SpringBoot集成flowable流程框架

IDEA安装Flowable BPMN visualizer插件 pom.xml中引入flowable相关依赖 <dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version></dependency><depe…

CSS基础:table的4个标签的样式详解(6000字长文!附案例)

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

JVM知识点总结二

参考文章&#xff1a;【Java面试题汇总】JVM篇&#xff08;2023版&#xff09;_jvm面试题2023-CSDN博客 1、说说你了解的JVM内存模型&#xff1a; JVM由三部分组成&#xff1a;类加载子系统、运行时数据区、执行引擎 JVM内存模型&#xff1a; 内存模型里的运行时数据区&#…

汽车组装3D电子说明书更通俗易懂

激光打印机由于造价高、技术更先进&#xff0c;因此在使用和维护上需要更专业的手法&#xff0c;而对于普通客户来说并不具备专业操作激光打印机的技能&#xff0c;为了通俗易懂地让客户理解激光打印机&#xff0c;我们为企业定制了激光打印机3D产品说明书&#xff0c;将为您带…

计算机(电脑)硬件组成基本介绍4

详细介绍的计算机(电脑)硬件组成.电源插座为主板提供供电的电源接口目前,主板电源接口插座主要采用ATX电源接口, ATX电源接口一般为24针电源插座、8 针电源插座、4针电源插座等,主要为主板提供5V、 12V、3.3V 电压等. ATX 电源都支持软件关机功能。目前,双核CPU 主板上的…

Git学习路线

1.看书 把这本书看懂就可以了&#xff1b;这个是比较专业的一本书&#xff1b;比较系统&#xff1b;没有书的可以私信我 2.理解Git多个分区和多个分支 多个分区包括&#xff1a;工作区、暂存区、本地仓、本地的远端仓信息、远端仓 多个分区的状态 分支及其变化 3.记住常用命令…

计算机(电脑)硬件组成基本介绍3

详细介绍的计算机(电脑)硬件组成.重要接口SATA连接大容量存储设备的SATA接口SATA (Serial ATA)接口即串行ATA,它是目前硬盘采用的一种新型的接口类型。SATA接口主要采用连续串行的方式传输数据,这样在同一时间点内只会有1位数据传输,此做法能减小接口的针脚数目,用4个针…

机器学习-保险花销预测笔记+代码

读取数据 import numpy as np import pandas as pddatapd.read_csv(rD:\人工智能\python视频\机器学习\5--机器学习-线性回归\5--Lasso回归_Ridge回归_多项式回归\insurance.csv,sep,) data.head(n6) EDA 数据探索 import matplotlib.pyplot as plt %matplotlib inlineplt.hi…

计算机(电脑)硬件组成基本介绍1

详细介绍的计算机(电脑)硬件组成。目录目录操作系统与硬件及应用程序软件的关系电脑各个设备之间关系 如何评价一台电脑? 通过CPU型号看性能 通过 CPU 主频评价 通过内存容量评价 通过显卡芯片及显存容量评价 通过显示器评价 中央处理器 存储器 输入设备 输出设备 接口…

20.Nacos集群搭建

模拟Nacos三个节点&#xff0c;同一个ip,启动三个不同的端口&#xff1a; 节点 nacos1, 端口&#xff1a;8845 节点 nacos2, 端口&#xff1a;8846 节点 nacos3, 端口&#xff1a;8847 1.搭建数据库&#xff0c;初始化数据库表结构 这里我们以单点的数据库为例 首先新建一…

DC学习笔记

视频 数字逻辑综合工具实践 DC 01_哔哩哔哩_bilibili 一、DC工作模式&#xff08;此小节为搬运内容&#xff09; 原链接&#xff1a;Design_Compiler User Guide 随手笔记&#xff08;9&#xff09;Using Floorplan Information - 知乎 DC拥有四种工作模式&#xff1a; 工…

Ubuntu24.04系统Docker安装nextcloud+onlyoffice

1.Ubuntu系统下载 Ubuntu镜像站大全 我用的是山东大学的镜像站 我下的是desktop版本就是有GUI图形界面,如果不需要可以下载server版本2.开启SSH启用root用户远程登陆 由于我使用远程工具MobaXterm进行连接,所以安装完系统后需要开启SSH,如果你不需要使用远程工具远程可以跳过…

18种WEB常见漏洞:揭秘网络安全的薄弱点

输入验证漏洞: 认证和会话管理漏洞: 安全配置错误: 其他漏洞: 防范措施: Web 应用程序是现代互联网的核心&#xff0c;但它们也容易受到各种安全漏洞的影响。了解常见的 Web 漏洞类型&#xff0c;对于开发人员、安全测试人员和普通用户都至关重要。以下将介绍 18 种常见的 …

C语言--基础面试真题

1、局部变量和静态变量的区别 普通局部变量和静态局部变量区别 存储位置&#xff1a; 普通局部变量存储在栈上 静态局部变量存储在静态存储区 生命周期&#xff1a; 当函数执行完毕时&#xff0c;普通局部变量会被销毁 静态局部变量的生命周期则是整个程序运行期间&#…

学习Rust第14天:HashMaps

今天我们来看看Rust中的hashmaps&#xff0c;在 std::collections crate中可用&#xff0c;是存储键值对的有效数据结构。本文介绍了创建、插入、访问、更新和迭代散列表等基本操作。通过一个计算单词出现次数的实际例子&#xff0c;我们展示了它们在现实世界中的实用性。Hashm…

基于harris角点和RANSAC算法的图像拼接matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ....................................................................... I1_harris fu…

对EKS(AWS云k8s)启用AMP(AWS云Prometheus)监控+AMG(AWS云 grafana)

问题 需要在针对已有的EKS k8s集群启用Prometheus指标监控。而且&#xff0c;这里使用AMP即AWS云的Prometheus托管服务。好像这个服务&#xff0c;只有AWS国际云才有&#xff0c;AWS中国云没得这个托管服务。下面&#xff0c;我们就来尝试在已有的EKS集群上面启用AMP监控。 步…

IP地址定位:揭秘精准定位的技术与应用

在数字化时代&#xff0c;IP地址已成为连接互联网世界的关键标识之一。但是&#xff0c;很多人对于IP地址的精准定位能力存在疑虑。本文将深入探讨IP地址定位的技术原理以及其在实际应用中的精确度。 IP地址查询&#xff1a;IP数据云 - 免费IP地址查询 - 全球IP地址定位平台 …