news 2026/6/10 17:35:14

Leetcode 80 统计一个数组中好对子的数目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Leetcode 80 统计一个数组中好对子的数目

1 题目

1814. 统计一个数组中好对子的数目

给你一个数组nums,数组中只包含非负整数。定义rev(x)的值为将整数x各个数字位反转得到的结果。比方说rev(123) = 321rev(120) = 21。我们称满足下面条件的下标对(i, j)好的

  • 0 <= i < j < nums.length
  • nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])

请你返回好下标对的数目。由于结果可能会很大,请将结果对109 + 7取余后返回。

示例 1:

输入:nums = [42,11,1,97]输出:2解释:两个坐标对为: - (0,3):42 + rev(97) = 42 + 79 = 121, 97 + rev(42) = 97 + 24 = 121 。 - (1,2):11 + rev(1) = 11 + 1 = 12, 1 + rev(11) = 1 + 11 = 12 。

示例 2:

输入:nums = [13,10,35,24,76]输出:4

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109

2 代码实现

class Solution { private: long long rev(long long x){ long long res = 0 ; for (; x > 0 ; x /= 10 ){ res = res * 10 + x % 10 ; } return res ; } public: int countNicePairs(vector<int>& nums) { const int mod = 1e9 + 7 ; int count = 0 ; unordered_map <long long , int > diffMap ; for (int i = 0 ; i < nums.size() ; i ++){ long long diff = (long long )nums[i] - rev((long long )nums[i] ); count = (count + diffMap[diff]) % mod ; diffMap[diff] ++; } return count ; } };

又这么乱写,写一半...

我大概的想法,就是nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])变形成nums[i] - rev(nums[i]) ,i 和 j 取到相等 。怎么比对呢,unordered_map吗,不知道怎么写。

class Solution { private: int rev (int x ){ int res = 0 ; for ( ; x > 0 ; x /= 10){ res = res * 10 + x % 10 ; } return res ; } public: int countNicePairs(vector<int>& nums) { int count = 0 ; for (int i = 0 ; i < nums.size() ; i++){ } } };

补全代码

你现在的核心需求是把思路(将条件变形为nums[i] - rev(nums[i]) == nums[j] - rev(nums[j]),并用哈希表统计该值的出现次数来计算好对子数目)转化为完整的 C++ 代码,补全你写了一半的逻辑。

解题思路

  1. 公式变形:原条件nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])可变形为nums[i] - rev(nums[i]) == nums[j] - rev(nums[j]),我们把这个差值记为diff
  2. 哈希表统计:遍历数组时,用哈希表(unordered_map)记录每个diff出现的次数。对于当前元素的diff,它能和之前所有相同diff的元素组成好对子,因此直接将哈希表中该diff的现有次数加到结果中,再更新哈希表。
  3. 取模处理:由于结果可能很大,每一步都要对10^9 + 7取余。

完整代码实现

#include <vector> #include <unordered_map> using namespace std; class Solution { private: // 反转数字x的各位(注意x是非负整数) long long rev(long long x) { // 用long long避免数值溢出 long long res = 0; for (; x > 0; x /= 10) { res = res * 10 + x % 10; } return res; } public: int countNicePairs(vector<int>& nums) { const int MOD = 1e9 + 7; int count = 0; unordered_map<long long, int> diffMap; // 存储diff值对应的出现次数 for (int num : nums) { // 计算当前数字的diff = num - rev(num) long long diff = (long long)num - rev(num); // 强转避免溢出 // 现有相同diff的数量就是能组成的新好对子数,加到结果中 count = (count + diffMap[diff]) % MOD; // 更新哈希表,当前diff的出现次数+1 diffMap[diff]++; } return count; } }; // 测试用例 // 示例1:输入[42,11,1,97],输出2 // 示例2:输入[13,10,35,24,76],输出4

代码关键部分解释

  1. rev 函数的优化:将参数和返回值改为long long,因为nums[i]最大为10^9,反转后也可能接近10^9,用long long可以避免整数溢出(比如num=10^9时,int可能存不下反转后的数)。
  2. diff 的计算:同样用long long存储diff,防止num - rev(num)的结果超出int范围。
  3. 哈希表的使用diffMap的键是diff值,值是该diff出现的次数。遍历数组时,先取diffMap[diff](即之前出现过的相同diff的元素数量)加到结果中,再将diffMap[diff]加 1。这一步的逻辑是:当前元素能和之前所有相同 diff 的元素组成好对子,比如diff已经出现了k次,当前元素加入后会新增k个好对子。
  4. 取模操作:每次更新count时都对MOD取余,确保结果不会溢出且符合题目要求。

测试用例验证

以示例 1nums = [42,11,1,97]为例:

  • 42 的 diff:42 - 24 = 18 → diffMap 中 18 的次数为 0,count=0,diffMap [18]=1
  • 11 的 diff:11 - 11 = 0 → diffMap 中 0 的次数为 0,count=0,diffMap [0]=1
  • 1 的 diff:1 - 1 = 0 → diffMap 中 0 的次数为 1,count=1,diffMap [0]=2
  • 97 的 diff:97 - 79 = 18 → diffMap 中 18 的次数为 1,count=2,diffMap [18]=2最终结果为 2,符合示例要求。

总结

  1. 核心变形:将原条件转化为nums[i] - rev(nums[i]) == nums[j] - rev(nums[j]),把问题转化为统计相同差值的元素对数。
  2. 哈希表效率:使用unordered_map统计差值出现次数,遍历数组的时间复杂度为O(n),能处理n=10^5的规模(避免了双重循环的O(n^2)超时问题)。
  3. 溢出处理:使用long long存储反转后的数和差值,防止整数溢出导致错误。

diffMap[diff]的含义

你现在的核心疑问是代码中diffMap[diff]的含义,以及它在统计好对子数目时的作用。我会先解释这个表达式的意思,再结合代码逻辑说明它的具体作用。

diffMap[diff]的含义

在 C++ 的unordered_map<long long, int> diffMap中:

  • 键(key):是long long类型的diff值(即nums[i] - rev(nums[i]))。
  • 值(value):是int类型的数字,表示这个diff在当前遍历位置之前已经出现过的次数
  • diffMap[diff]就是获取键为diff对应的 value 值
    1. 如果diff这个键已经存在于哈希表中,返回的是它对应的出现次数。
    2. 如果diff这个键不存在于哈希表中,C++ 会自动将这个键插入到哈希表中,并将其对应的 value 初始化为0,然后返回 0。

结合代码逻辑理解diffMap[diff]的作用

我们的核心逻辑是:两个元素的diff相等,就可以组成一个好对子。遍历数组时,diffMap[diff]的作用是统计 “当前元素能和之前多少个元素组成好对子”。

代码执行过程拆解(以示例 1nums = [42,11,1,97]为例)
  1. 遍历第一个元素 42
    • 计算diff = 42 - rev(42) = 42 - 24 = 18
    • diffMap[18]:此时哈希表中没有 18 这个键,返回 0 →count = 0 + 0 = 0
    • 执行diffMap[18]++→ 哈希表中 18 的次数变为 1。
  2. 遍历第二个元素 11
    • 计算diff = 11 - rev(11) = 11 - 11 = 0
    • diffMap[0]:哈希表中没有 0 这个键,返回 0 →count = 0 + 0 = 0
    • 执行diffMap[0]++→ 哈希表中 0 的次数变为 1。
  3. 遍历第三个元素 1
    • 计算diff = 1 - rev(1) = 1 - 1 = 0
    • diffMap[0]:哈希表中已有 0 这个键,返回 1 →count = 0 + 1 = 1
    • 执行diffMap[0]++→ 哈希表中 0 的次数变为 2。
  4. 遍历第四个元素 97
    • 计算diff = 97 - rev(97) = 97 - 79 = 18
    • diffMap[18]:哈希表中已有 18 这个键,返回 1 →count = 1 + 1 = 2
    • 执行diffMap[18]++→ 哈希表中 18 的次数变为 2。最终count为 2,和示例 1 的结果一致。

补充:代码中的一个隐藏问题

你的rev函数中,long long res ;没有初始化,这会导致未定义行为(res 的初始值是随机的垃圾值,反转数字的结果会出错)。需要将其初始化为 0:

(之前写错的...)

long long rev(long long x){ long long res = 0; // 必须初始化! for (; x > 0 ; x /= 10 ){ res = res * 10 + x % 10 ; } return res ; }

总结

  1. diffMap[diff]的核心含义:获取当前diff值在遍历到当前元素之前出现过的次数,这个次数就是当前元素能组成的新好对子数量。
  2. 哈希表的工作流程:先通过diffMap[diff]累加对子数,再将diff的出现次数加 1,确保后续元素能统计到当前元素。
  3. 关键修复点rev函数中的res必须初始化为 0,否则会因垃圾值导致反转结果错误。

这个map里面的key 和 value 可以这么直接定义进去吗?

unordered_map <long long , int > diffMap ; for (int i = 0 ; i < nums.size() ; i ++){ long long diff = (long long )nums[i] - rev((long long )nums[i] ); count = (count + diffMap[diff]) % mod ; diffMap[diff] ++; }

你现在的核心疑问是:在代码中直接使用diffMap[diff]这种方式,能否自动将diff作为 key、对应的值作为 value 存入unordered_map中,以及这种用法的底层逻辑是什么。

直接结论

可以的。C++ 的unordered_map(包括map)支持通过[]运算符直接访问 / 插入键值对,这是它的一个核心特性,你的代码中这种用法是完全合法且符合常规写法的。

底层逻辑与具体行为

当你执行diffMap[diff]时,unordered_map会做以下两件事:

  1. 检查diff这个 key 是否已经存在
    • 如果存在:直接返回该 key 对应的 value 的引用(可以读取或修改这个值)。
    • 如果不存在自动将这个 key 插入到哈希表中,并将其对应的 value 初始化为该类型的默认值(对于int类型,默认值是0),然后返回这个默认值的引用。

结合你的代码理解具体执行过程

你的代码中这两行是核心:

count = (count + diffMap[diff]) % mod ; // 步骤1:访问diffMap[diff] diffMap[diff] ++; // 步骤2:修改diffMap[diff]

我们以示例中diff=0(第一次出现)为例:

  1. 步骤 1diff=0不存在于diffMap中,unordered_map自动插入key=0value=0,然后返回0,所以count加上的是0
  2. 步骤 2:对diffMap[0]的引用执行++操作,此时value0变成1,哈希表中就存储了0:1的键值对。

diff=0第二次出现时:

  1. 步骤 1diff=0已经存在,直接返回对应的value=1count加上1
  2. 步骤 2value1变成2,哈希表中存储0:2

补充:这种用法与find/insert的对比

如果你担心自动插入的行为(比如只想读取不想插入),可以用find方法先判断 key 是否存在,比如:

// 不自动插入的写法(仅读取) auto it = diffMap.find(diff); int current = 0; if (it != diffMap.end()) { current = it->second; // 存在则取对应的值 } count = (count + current) % mod; diffMap[diff]++; // 这里还是会插入(如果不存在)

但在你的代码场景中,我们本来就需要将diff插入到哈希表中并统计次数,所以直接用[]运算符是最简洁、最高效的写法,完全不需要额外的find判断。

验证示例

我们用一段简单的代码演示这个特性:

#include <iostream> #include <unordered_map> using namespace std; int main() { unordered_map<long long, int> diffMap; // 第一次访问key=18(不存在) cout << diffMap[18] << endl; // 输出:0(自动插入18:0) diffMap[18]++; cout << diffMap[18] << endl; // 输出:1(value被修改) // 第二次访问key=18(存在) cout << diffMap[18] << endl; // 输出:1 diffMap[18]++; cout << diffMap[18] << endl; // 输出:2 return 0; }

运行这段代码,你会看到unordered_map自动处理了 key 的插入和 value 的初始化。


总结

  1. 可以直接用[]定义键值对unordered_map[]运算符会自动检查 key 是否存在,不存在则插入并初始化 value 为类型默认值(int是 0),存在则返回对应 value 的引用。
  2. 你的代码用法是最优的:在统计次数的场景中,直接使用diffMap[diff]是最简洁的写法,无需额外的findinsert操作。
  3. 区别于find:如果只是想读取 key 的值而不想插入,可用find方法;如果需要插入并修改,[]运算符更高效。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 20:35:21

39、Linux Shell 高级特性与技巧解析

Linux Shell 高级特性与技巧解析 1. 数组相关操作 在处理数组时,有时需要对数组元素的输出进行排序。由于数组元素扩展后的结果通常是无序的,所以可以将循环的整个输出通过管道传递给 sort 命令来实现排序。另外,使用 ${!array[@]} 扩展可以得到数组索引列表,而不是数…

作者头像 李华
网站建设 2026/6/8 12:51:17

Java设计模式系列 - 基本概念

基本思想设计模式是软件开发中经过反复验证的、解决特定问题的优秀解决方案模板。在Java开发中&#xff0c;设计模式不仅是一种编程技巧&#xff0c;更是提高代码质量、可维护性和可扩展性的重要工具。使用设计模式的主要目的是为了重用代码、让代码更容易被他人理解、保证代码…

作者头像 李华
网站建设 2026/6/10 9:08:06

非专业也能看懂的AI大模型工作原理!(非常详细)从零基础到精通,一篇就够了!

本文全面介绍AI大语言模型工作流程&#xff0c;从文本输入预处理到输出生成全过程。涵盖分词嵌入、Transformer架构、自注意力机制、位置编码及长文本外推等技术&#xff0c;并结合DeepSeek V3实例解析模型如何将语言转换为数学矩阵并生成回答。同时提供丰富的工程实践指导&…

作者头像 李华
网站建设 2026/6/10 10:34:48

前端锚点定位,零基础入门到精通,收藏这篇就够了

一 背景知识: 1.1 #号的作用 #代表网页中的一个位置。其右面的字符&#xff0c;就是该位置的标识符。比如&#xff0c;http://www.example.com/index.html#print 就代表网页index.html的print位置。浏览器读取这个URL后&#xff0c;会自动将print位置滚动至可视区域。 为网页…

作者头像 李华
网站建设 2026/6/9 15:01:09

AI应用架构师避坑指南:虚拟协作架构中的依赖问题

AI应用架构师避坑指南:虚拟协作架构中的依赖问题——从“木桶效应”到“弹性网络”的进化之路 关键词 虚拟协作架构、依赖管理、服务耦合、故障隔离、弹性设计、AI应用架构、断路器模式 摘要 在AI应用规模化落地的今天,虚拟协作架构(由多个分布式服务/模块协同完成复杂任…

作者头像 李华
网站建设 2026/6/9 16:43:20

公司局域网访问外网的原理

公司局域网访问外网的原理 在现代企业环境中&#xff0c;如何让内部员工既能访问公司内部资源&#xff0c;又能安全地访问互联网是一个重要的网络架构问题。本文将以通俗易懂的方式介绍公司局域网访问外网的基本原理和常见配置方式。 基本网络架构 大多数公司的网络架构通常…

作者头像 李华