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

文章目录

题目描述

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

示例 1:

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

输出: 1

解释:

镜像对为:

(0, 1),因为 reverse(nums0) = reverse(12) = 21 = nums1,绝对距离为 abs(0 - 1) = 1。

(2, 4),因为 reverse(nums2) = reverse(45) = 54 = nums4,绝对距离为 abs(2 - 4) = 2。

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

输入: nums = 120,21

输出: 1

解释:

只有一个镜像对 (0, 1),因为 reverse(nums0) = reverse(120) = 21 = nums1

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

输入: nums = 21,120

输出: -1

解释:

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

1 <= nums.length <= 105

1 <= numsi <= 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 每日一题、解锁更多算法解题技巧的小伙伴,一定要关注我呀!后续会持续更新超详细的题解、算法思路和避坑指南,陪你一起刷穿题库,稳稳拿下算法关~

相关推荐
吃好睡好便好1 天前
提取矩阵某一行或某一列元素
开发语言·人工智能·线性代数·算法·matlab·矩阵
圣保罗的大教堂1 天前
leetcode 2540. 最小公共值 简单
leetcode
wljy11 天前
二、进制状态转换
linux·运维·服务器·c语言·c++
云泽8081 天前
笔试算法 -位运算篇(二):从唯一字符到消失数字
c++·算法·位运算
ʚ希希ɞ ྀ1 天前
不同路径|| -- dp
算法
繁华落尽,倾城殇?1 天前
[C++11] : atomic,nullptr,default/delete,enum class
开发语言·c++·c++11·nullptr·atomic·enum class·default/delete
代码村新手1 天前
C++-二叉搜索树
开发语言·c++
IT 行者1 天前
SimHash 与 MinHash:相似性计算的双子星算法
算法·hash·比对
智者知已应修善业1 天前
【51单片机8位数码管动态显示日期小数点风格】2023-11-13
c++·经验分享·笔记·算法·51单片机