每日一题 力扣 3761. 镜像对之间最小绝对距离 哈希表 数组 C++ 题解

文章目录

题目描述

3761. 镜像对之间最小绝对距离

示例 1:

输入: nums = [12,21,45,33,54]

输出: 1

解释:

镜像对为:

(0, 1),因为 reverse(nums[0]) = reverse(12) = 21 = nums[1],绝对距离为 abs(0 - 1) = 1。

(2, 4),因为 reverse(nums[2]) = reverse(45) = 54 = nums[4],绝对距离为 abs(2 - 4) = 2。

所有镜像对中的最小绝对距离是 1。
示例 2:

输入: nums = [120,21]

输出: 1

解释:

只有一个镜像对 (0, 1),因为 reverse(nums[0]) = reverse(120) = 21 = nums[1]。

最小绝对距离是 1。
示例 3:

输入: nums = [21,120]

输出: -1

解释:

数组中不存在镜像对。
提示:

1 <= nums.length <= 105

1 <= nums[i] <= 109

思路简述

在初次尝试解决这道题时,我的第一反应是利用哈希表记录数组中元素的下标,遍历数组时在哈希表中查找对应的镜像数,进而计算距离。但很快发现,这种方法在处理数组中存在重复元素的情况时,无法保证计算出的距离是最小的,因此需要转换思路。

我们可以设计这样一个哈希表:键(Key)是某个数镜像反转后的值,值(Value)是该镜像数在原数组中对应的下标 。换句话说,如果 hash[v] = j,就意味着在下标 j 的位置,存在一个数 nums[j],其反转后的值恰好等于 v

由于我们是从左到右遍历数组的,哈希表中存储的下标一定是距离当前位置最近的(因为哈希表的键是唯一的,后续更近的下标会覆盖之前较远的下标)。

基于这个设计,遍历过程中会出现两种情况:

  1. 当我们遍历到当前数 nums[i] 时,如果它存在于哈希表中,说明在之前的位置中,有一个数的镜像值正好等于当前数,且那个数的下标就是 hash[nums[i]]。此时,我们直接计算这两个下标之间的绝对距离 abs(hash[nums[i]] - i),并尝试更新最小距离。

  2. 无论当前数是否在哈希表中,我们都需要将当前数的镜像值及其下标 i 存入哈希表。这一步正是保证我们能获取最小距离的关键:如果哈希表中之前没有这个镜像值,那么这次存入是第一次记录;如果之前已经有了,那么用当前更近的下标 i 覆盖之前的下标,这样后续遇到匹配的数时,计算出的距离一定是更小的。

这个思路虽然在设计和对哈希表的理解上稍稍绕一点,但是巧妙地解决了最棘手的重复元素问题和最小距离的保证问题。

至于如何实现求整数镜像值的函数,有很多种实现方式,我用的是通过模运算(%)取最后一位,再通过除法(/)去掉最后一位,逐步构建出反转后的数。

代码实现

cpp 复制代码
class Solution {
public:
    // 函数功能:将整数 x 的数字反转,忽略前导零
    // 例如:rev(120) -> 21,rev(12) -> 21
    int rev(int x)
    {
        int y = 0; // 用于存储反转后的结果
        while (x > 0) 
        {
            y = y * 10 + x % 10; // 取出 x 的最后一位,加到 y 的末尾
            x /= 10; // 去掉 x 的最后一位
        }
        return y;
    }

    int minMirrorPairDistance(vector<int>& nums) 
    {
        unordered_map<int,int> hash; // 哈希表:键是镜像后的值,值是原数的下标

        int ret = INT_MAX; // 初始化最小距离为最大整数,表示尚未找到任何镜像对
        for(int i = 0; i < nums.size(); i++)
        {
            int x = nums[i];
            // 情况1:如果当前数 x 存在于哈希表中,说明前面有它的镜像数
            if(hash.count(x))
            {
                // 计算当前下标与哈希表中存储的下标的距离,并更新最小距离
                ret = min(ret, abs(hash[x] - i));
            }
            // 情况2:无论是否找到镜像对,都将当前数的镜像值和下标存入哈希表
            // 这样可以保证后续遇到匹配的数时,拿到的是最近的下标
            hash[rev(x)] = i;
        }
        // 如果 ret 仍然是初始值,说明没有找到镜像对,返回 -1;否则返回最小距离
        return ret == INT_MAX ? -1 : ret;
    }
};

复杂度分析

时间复杂度 : O ( n ) O(n) O(n),其中 n n n 为数组长度。

我们仅需对数组进行一次遍历,遍历过程中哈希表的查询、插入操作均为 O ( 1 ) O(1) O(1) 平均时间复杂度;数字反转函数的执行次数与数字位数成正比,属于常数级操作,因此整体时间复杂度为线性,完全适配 10 5 10^5 105 的数据规模。

空间复杂度 : O ( n ) O(n) O(n)。

最坏情况下数组中所有元素的反转值均不重复,哈希表需要存储 n n n 个键值对,因此空间复杂度为 O ( n ) O(n) O(n)。

踩坑记录

  1. 一定要明确哈希表的键值含义:一定要弄明白哈希表中键和值各自代表的是什么------键是"镜像后的值",值是"原数的下标"。如果在这一点上混淆了,很容易在后续的查找和更新操作中出错,导致逻辑混乱。
  2. 注意最小距离变量的初始化 :在初始化用于记录最小距离的变量 ret 时,要将其设置为一个足够大的值(例如 INT_MAX),这样在第一次找到有效距离时,才能正确地将其更新为第一个候选值。如果初始值过小,可能会导致后续无法正确更新。

如果这篇内容帮你理清了哈希表的设计逻辑,避开了我踩过的坑,麻烦大家动动小手点个赞,也可以把文章收藏起来,不管是后续刷题复盘、还是面试前回顾,都能随时翻出来看。

想要跟着刷更多 LeetCode 每日一题、解锁更多算法解题技巧的小伙伴,一定要关注我呀!后续会持续更新超详细的题解、算法思路和避坑指南,陪你一起刷穿题库,稳稳拿下算法关~

相关推荐
John.Lewis2 小时前
C++加餐课-哈希:扩展学习(2)布隆过滤器
c++·算法·哈希算法
网域小星球2 小时前
C++ 从 0 入门(三)|类与对象基础(封装、构造 / 析构函数,面试必考)
开发语言·c++·面试·构造函数·析构函数
j_xxx404_2 小时前
Linux:缓冲区
linux·运维·c++·后端
我真不是小鱼2 小时前
cpp刷题打卡记录29——矩阵置零 & 旋转图像 & 除了自身以外数组的乘积
数据结构·c++·算法·leetcode·矩阵
网域小星球2 小时前
C++ 从 0 入门(二)|引用与指针区别、函数重载、内联函数(面试高频)
开发语言·c++·面试·函数重载·内联函数·引用与指针区别
代码中介商2 小时前
C++ 多态与虚函数入门:从概念到规则
开发语言·c++
澈2072 小时前
快速排序与希尔排序实战解析
数据结构·算法·排序算法
kyle~2 小时前
工业以太网协议---EtherCAT
开发语言·c++·网络协议·机器人·ros2
say_fall2 小时前
深入理解AVL树:平衡调整机制与性能优化实战
开发语言·数据结构·c++·学习