当前位置: 首页 > news >正文

C++ vectorOJ练习题

目录

136. 只出现一次的数字

118. 杨辉三角

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

137. 只出现一次的数字ll

260. 只出现一次的数字 III

17. 电话号码的字母组合

JZ39 数组中出现次数超过一半的数字


136. 只出现一次的数字

采用异或运算的思路

  • 异或运算的特性是,相同的数异或结果为0,而任何数与0异或结果仍为原数。
  • 通过在循环中不断进行异或运算,最终只出现一次的元素将会被保留在变量v中,而其他出现多次的元素则会被抵消掉。
class Solution {
public:int singleNumber(vector<int>& nums) {int v=0;for(auto a:nums){v^=a;}return v;}
};

118. 杨辉三角

思路:找出杨辉三角的规律,发现每一行头尾都是1,中间第[j]个数等于上一行[j-1]+[j]。

class Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> v;v.resize(numRows, vector<int>());for (size_t i = 0; i < numRows; i++) {v[i].resize(i + 1, 0);v[i][0] = v[i][v[i].size() - 1] = 1;}for (size_t i = 0; i < v.size(); i++) {for (size_t j = 0; j < v[i].size(); j++) {if (v[i][j] == 0) {v[i][j] = v[i - 1][j] + v[i - 1][j - 1];}}}return v;}
};
  • 首先,函数定义了一个二维整型数组v,用于存储生成的杨辉三角形。通过调用resize函数,将数组的行数设置为numRows,并将每一行的列数初始化为0。
  • 接下来,使用两个嵌套的for循环来遍历数组v中的每个元素。外层循环控制行数,内层循环控制列数。
  • 在内层循环中,首先通过v[i].resize(i+1,0)将当前行的列数设置为i+1,并将所有元素初始化为0。然后,将当前行的第一个元素和最后一个元素设置为1,即v[i][0]=v[i][v[i].size()-1]=1
  • 接下来,通过判断当前元素是否为0,来确定是否需要计算该元素的值。如果当前元素为0,则表示该元素是由上方两个元素相加得到的。通过v[i-1][j-1]+v[i-1][j]计算出当前元素的值,并将结果赋值给v[i][j]
  • 通过这样的循环嵌套,依次计算出杨辉三角形中每个位置的值。
  • 最后,函数返回生成的杨辉三角形数组v

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

方法一 

使用双指针方法,一个指针用于遍历数组并查找唯一的元素,另一个指针用于跟踪下一个不重复元素应该插入的位置。时间复杂度为O(N).

class Solution {
public:int removeDuplicates(vector<int>& nums) {if (nums.empty())return 0;int a = 0, b = 1;while (b < nums.size()) {if (nums[a] != nums[b]){nums[++a] = nums[b];}++b;}return a + 1;}
};
  • 如果nums为空,则立即返回0。这是因为空数组没有元素可以移除。函数使用两个指针ab(或者可以认为是索引)来遍历数组。a指向要插入下一个唯一元素的位置,而b遍历数组寻找不重复的元素。
  • b小于nums.size()时,继续遍历数组。
  • 检查nums[a]nums[b]。如果它们不相等,这意味着nums[b]是一个新的唯一元素,它应该被移到a指向的下一个位置。
  • 如果nums[a]nums[b]相等,这意味着nums[b]是重复元素,因此不应移动a
  • 当找到新的唯一元素时(即nums[a]nums[b]不相等),将nums[b]的值复制到nums[a+1]的位置。这样做是为了在数组的前部保留所有唯一的元素。
  • 指针a递增以指向下一个可能的唯一元素的位置。
  • 无论nums[a]nums[b]是否相等,b都会递增,以便继续检查数组的下一个元素。
  • 函数返回a+1,这是因为a是最后一个唯一元素的索引,而数组长度是最后一个元素的索引加1。

方法二

 思路: erase删除重复数据,不过会导致数据挪动,时间复杂度为O(N)

  1. 由于数据是排序好的,通过查询每一个数据的重复区间
  2. 将重复区间进行删除,只保留一个数据即可
class Solution {
public:int removeDuplicates(vector<int>& nums) {if (nums.size() == 1)return 1;vector<int>::iterator it = nums.begin();vector<int>::iterator it1 = it + 1;while (it != nums.end()) {while (it1 != nums.end() && *it1 == *it){it1++;}it = nums.erase(++it, it1);it1 = it;it1++;}return nums.size();}
};

137. 只出现一次的数字ll

方法一:我们可以使用哈希映射统计数组中每个元素的出现次数。对于哈希映射中的每个键值对,键表示一个元素,值表示其出现的次数。在统计完成后,我们遍历哈希映射即可找出只出现一次的元素。

class Solution {
public:int singleNumber(vector<int>& nums) {unordered_map<int, int> count;for (auto a : nums) {++count[a];}for (auto& pair : count) {if (pair.second == 1) {return pair.first;}}return -1;}
};class Solution {
public:int singleNumber(vector<int>& nums) {unordered_map<int, int> map;for (auto n : nums) {++map[n];}int a = 0;for (auto [n, o] : map) {if (o == 1) {a = n;break;}}return a;}
};
  1. 创建一个无序映射(unordered_map)count,用于记录每个元素出现的次数。

  2. 遍历数组nums,对于数组中的每个元素a,将其作为键,将其出现的次数加1。

  3. 遍历映射count中的每个键值对,对于每个键值对pair,如果其值等于1,说明该键对应的元素只出现了一次,返回该键。

  4. 如果遍历完映射count后仍未找到只出现一次的元素,则返回-1。

方法二:只有一个数字出现一次,其余数字均出现3次,假设数组为{3,5,3,3} 通过分析可知: 3的二进制:0 0 0 0 0 0 1 1 5的二进制:0 0 0 0 0 1 0 1 3的二进制:0 0 0 0 0 0 1 1 3的二进制:0 0 0 0 0 0 1 1 0 0 0 0 0 1 3 4 二进制1的总数 对于出现3次的数字,各位出现的次数都是3的倍数,因此对统计的为1的比特总数%3 0 0 0 0 0 1 0 1 = 5 

class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;for (int i = 0; i < 32; i++) {int sum = 0;for (auto n : nums) {if (((n >> i) & 1) == 1)sum++;}sum %= 3;if (sum == 1)ret |= 1 << i;}return ret;}
};

260. 只出现一次的数字 III

 方法一:哈希表

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {unordered_map<int, int> map;for (auto n : nums) {map[n]++;}vector<int> rec;for (size_t i = 0; i < nums.size(); ++i) {if (map[nums[i]] == 1) {rec.push_back(nums[i]);}}return rec;}
};

这种方法虽然在代码中实现了,但是它不符合题目要求的线性时间和常数空间的要求。

  • 使用哈希表来记录每个元素出现的次数。
  • 遍历数组,将出现一次的元素添加到结果列表中返回。

这种方法的时间复杂度为O(n),空间复杂度也为O(n),因为需要额外的空间来存储哈希表。

 方法二:位操作

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {int sum = 0;for (int num : nums) {sum ^= num;}int l = (sum == INT_MIN ? sum : sum & (-sum));int num1 = 0, num2 = 0;for (int num : nums) {if (num & l)num1 ^= num;elsenum2 ^= num;}return {num1, num2};}
};

这是符合题目要求的方法,利用了XOR运算的性质来解决问题:

  • 异或运算特性:相同为0,相异为1,无进位相加

    • 任何数和0做异或运算,结果仍然是原来的数,即 a⊕0=aa⊕0=a。
    • 任何数和其自身做异或运算,结果是0,即 a⊕a=0a⊕a=0。
    • 异或运算满足交换律和结合律,即 a⊕b=b⊕aa⊕b=b⊕a 和 (a⊕b)⊕c=a⊕(b⊕c)(a⊕b)⊕c=a⊕(b⊕c)。
  • 第一次异或

    • 我们遍历整个数组,将所有元素进行异或运算。由于数组中的大部分元素都是成对出现的,所以这些成对的元素相互抵消,最后得到的结果就是那两个只出现一次的数的异或结果。设这两个数为 xx 和 yy,那么我们得到的异或结果 sum=x⊕ysum=x⊕y。
  • 寻找差异位

    • 因为 xx 和 yy 是不同的数,所以 sumsum 至少有一个比特位是1。我们需要找到这个比特位,这可以通过将 sumsum 和它的补码 sum⊕(−sum)sum⊕(−sum) 进行与运算得到。这个比特位就是 sumsum 中从最低位开始的第一个1。
    • 另一种方法是直接使用 sumsum 本身,因为如果 sumsum 不是最小负数 INT_MIN,那么 sumsum 和它的补码的与运算结果就是 sumsum 的最低位1。如果 sumsum 是 INT_MIN,那么 sumsum 就已经是这个比特位了。
  • 第二次异或

    • 利用找到的差异位,我们可以将数组中的所有元素分为两类:那些在这个比特位上为1的数,以及那些在这个比特位上为0的数。
    • 对这两类数分别进行异或运算。因为每一对重复的数在这个比特位上的值是一样的,它们会在各自的类别中相互抵消,剩下的就是那个类别中只出现一次的那个数。
    • 于是我们得到了 xx 和 yy。

这种方法的时间复杂度为O(n),并且只需要常数级别的额外空间,满足题目的要求。

方法三:排序后比较

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {sort(nums.begin(), nums.end());vector<int> res;int i = 0;for (; i < nums.size() - 1;) {if (nums[i] == nums[i + 1]) {i += 2;} else {res.push_back(nums[i]);i += 1;}}if (i < nums.size()) {res.push_back(nums[i]);}return res;}
};

这种方法也不完全符合题目要求的线性时间和常数空间的要求,但可以理解它的逻辑:

  • 先对数组进行排序。
  • 遍历排序后的数组,如果遇到相邻元素不相等的情况,则当前元素一定是只出现一次的元素。
  • 最后处理数组尾部的元素。

这种方法的时间复杂度为O(n log n),因为排序通常需要O(n log n)的时间复杂度。空间复杂度取决于使用的排序算法,如果是原地排序则可以达到O(1),但大多数情况下会大于O(1)。

17. 电话号码的字母组合

回溯算法

class Solution {
public:string num_str[10] = {"",    "",    "abc",  "def", "ghi","jkl", "mno", "pqrs", "tuv", "wxyz"};void combination(const string& digits, size_t di, string combStr,vector<string>& strv) {if (di == digits.size()) {strv.push_back(combStr);return;}int num = digits[di] - '0';string str = num_str[num];for (auto ch : str) {combination(digits, di + 1, combStr + ch, strv);}}vector<string> letterCombinations(string digits) {vector<string> str;if (digits.size() == 0)return str;combination(digits, 0, "", str);return str;}
};
  1. 初始化映射表

    • 创建了一个静态数组 num_str,用于存储数字到字母的映射关系。例如,"2" 映射到 "abc""3" 映射到 "def" 等等。注意索引 0 和 1 处的字符串为空,因为我们不考虑这两个数字对应的字母。
  2. 递归函数 combination

    • 这个函数接受四个参数:digits(输入的数字字符串)、di(当前处理的数字索引)、combStr(当前构建的字符串组合)以及strv(用于存储所有可能组合的向量)。
    • 如果当前处理的索引 di 已经等于 digits 的长度,说明已经处理完所有的数字,此时将当前构建的字符串 combStr 加入结果向量 strv
    • 否则,获取当前数字对应的字母字符串 str,然后遍历这个字符串中的每一个字母,并递归调用 combination 函数,同时增加索引 di 并将当前字母加入到 combStr
  3. 主函数 letterCombinations

    • 检查输入的 digits 字符串是否为空,如果为空则直接返回空的字符串向量。
    • 调用 combination 函数开始递归生成所有可能的组合。
    • 最终返回所有可能的字母组合。

示例步骤

假设 digits = "23",则有以下步骤:

  • 开始时,di=0combStr=""num=2,对应的字母是 "abc"
  • 对于每个字母 abc,执行 di+1,即 di=1
  • 当 di=1 时,num=3,对应的字母是 "def"
  • 对于每个字母 def,执行 di+1,即 di=2
  • 当 di=2 时,已经到达字符串末尾,将当前的 combStr 添加到结果向量中。

结果

对于 "23",结果向量将会包含 "ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"

这种方法的时间复杂度是 O(3^N * 4^M),其中 N 是数字2、3、4、5、6、8出现的次数,M 是数字7、9出现的次数,因为数字7和9对应4个字符,其它数字对应3个字符。空间复杂度主要取决于递归调用栈的深度,最坏情况下为 O(N)。

JZ39 数组中出现次数超过一半的数字

哈希表 

class Solution {public:int MoreThanHalfNum_Solution(vector<int>& numbers) {unordered_map<int, int> count;int n=numbers.size();for (auto a : numbers) {++count[a];if (count[a] > n / 2) {return a;}}return -1;}
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【C/C++】“秒懂”学C/C++不可错过的“经典编程题” — 日期类的经典运用 (含题链接)
  • Git-下载的zip包项目重新指向github项目地址
  • Request Response
  • VSCode学习笔记
  • 802.11 中 scrambler的matlab仿真
  • 云计算之大数据(下)
  • 陕西农信银行合规知识竞赛活动方案
  • STM32 HAL freertos零基础(一)-任务创建
  • 算法-双指针技巧
  • 搭建Kafka+zookeeper集群调度
  • 运营如何判断账号是否起号失败?
  • Bev pool 加速(1): torch.autograd.Function的使用
  • 从C到C++
  • 微信小程序-文件下载
  • 体系结构权衡分析方法(ATAM)
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • go语言学习初探(一)
  • Java多线程(4):使用线程池执行定时任务
  • js 实现textarea输入字数提示
  • quasar-framework cnodejs社区
  • React-redux的原理以及使用
  • SpiderData 2019年2月13日 DApp数据排行榜
  • SpiderData 2019年2月16日 DApp数据排行榜
  • SQLServer插入数据
  • Terraform入门 - 1. 安装Terraform
  • Web Storage相关
  • 从0实现一个tiny react(三)生命周期
  • 从PHP迁移至Golang - 基础篇
  • 二维平面内的碰撞检测【一】
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 回流、重绘及其优化
  • 计算机常识 - 收藏集 - 掘金
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 在Unity中实现一个简单的消息管理器
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • (06)Hive——正则表达式
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (C#)获取字符编码的类
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .jks文件(JAVA KeyStore)
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .Net IOC框架入门之一 Unity
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET编程——利用C#调用海康机器人工业相机SDK实现回调取图与软触发取图【含免费源码】
  • .Net插件开发开源框架
  • .NET建议使用的大小写命名原则
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .NET命名规范和开发约定
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解