• LeetCode #532. K-diff Pairs in an Array 数组 哈希 双指针


    Description


    Given an array of integers and an integer k, you need to find the number of unique k-diff pairs in the array. Here a k-diff pair is defined as an integer pair (i, j), where i and j are both numbers in the array and their absolute difference is k.

    Example 1:

    Input: [3, 1, 4, 1, 5], k = 2
    Output: 2
    Explanation: There are two 2-diff pairs in the array, (1, 3) and (3, 5).
    Although we have two 1s in the input, we should only return the number of unique pairs.
    

    Example 2:

    Input:[1, 2, 3, 4, 5], k = 1
    Output: 4
    Explanation: There are four 1-diff pairs in the array, (1, 2), (2, 3), (3, 4) and (4, 5).
    

    Example 3:

    Input: [1, 3, 1, 5, 4], k = 0
    Output: 1
    Explanation: There is one 0-diff pair in the array, (1, 1).
    

    Note:

    • The pairs (i, j) and (j, i) count as the same pair.
    • The length of the array won't exceed 10,000.
    • All the integers in the given input belong to the range: [-1e7, 1e7].



    思路


    解法一

    这道题暴力解法很简单,将元素两两组合变成一个pair,并用一个数组存储所有的 pair 以便查重。当然,在暴力解法 O(n^3) 的基础上,我们可以利用 HashMap 把第三层的数组给优化成 Hash 查询,从而将时间复杂度优化为 O(n^2)。

    Hash 的策略是这样:如果我们假设 pair 中的第一个元素不大于第二个元素,那么我们使用第一个元素就能唯一标识一个 pair。

    当然,该 Hash 策略说明了我们可以只使用 HashSet,能比 HashMap 少用一些空间,因为 HashMap 中键值对的值并没有什么用。

    特别需要注意的是,这道题有个很恶心的坑点,即 K 可以小于 0,而题目中说了 k 表示两数的绝对值,很容易误导人(我就是这么被误导了....)。

    时间复杂度:O(n^2)
    空间复杂度:O(n)

    耗时 1696 ms, faster than 5%, Memory 17.7 MB

    class Solution {
    public:
        int findPairs(const vector<int> &nums, int k) {
            if (nums.size() < 2) return 0;
            if (k < 0) return 0;  // absolute difference won't be negative
    
            int pair_num = 0;
            // a number can represent a pair
            // if the "first" of pair is always no more than the "second", like {2, 3}
            unordered_set<int> visited;
    
            for (int i = 0; i < nums.size() - 1; ++i) {
                for (int j = i + 1; j < nums.size(); ++j) {
                    // Attention! k can be negative
                    if (abs(nums[i] - nums[j]) == k) {
                        if (nums[i] <= nums[j]) {
                            // num[i] is the "first" in pair
                            if (visited.find(nums[i]) == visited.end()) {
                                visited.insert(nums[i]);
                                ++pair_num;
                            }
                        } else {
                            // num[j] is the "first" in pair
                            if (visited.find(nums[j]) == visited.end()) {
                                visited.insert(nums[j]);
                                ++pair_num;
                            }
                        }
                    }
                }
            }
    
            return pair_num;
        }
    };
    



    解法二

    更改一下 Hash 策略,key 还是各个数字,但是 value 变成了各个数字出现的次数。遍历一遍 nums 得到各个数字以及出现的次数后,可以遍历 map 中的所有键值对,使用 HashMap O(1) 的查找操作查找与当前间隔为 k 的数。

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

    耗时 72 ms, faster than 30.7 %, Memory 18.4 MB

    class Solution {
    public:
        int findPairs(const vector<int> &nums, int k) {
            if (nums.size() < 2) return 0;
            if (k < 0) return 0;  // absolute difference won't be negative
    
            int pair_num = 0;
            // key is a number, value is frequency that the number appears
            unordered_map<int, int> m;
    
            for (int x : nums) {
                auto iter = m.find(x);
                if (iter != m.end()) {
                    ++iter->second;
                } else {
                    m[x] = 1;
                }
            }
    
            for (const auto &item : m) {
                if (k > 0) {
                    if (m.count(item.first + k)) ++pair_num;
                } else if (k == 0) {
                    if (item.second > 1) ++pair_num;
                }
            }
    
            return pair_num;
        }
    };
    



    解法三

    参考博客中还提到了一种使用双指针的解法,在本题里它是一个时间换空间的策略,因为必须先进行 O(nlgn) 的排序保证序列的有序性,然后再求绝对值,否则就变成了暴力解法了。

    我们知道解法一中,对于每一个序列 {a, b} 且 a <= b,我们可以使用 a 来唯一表示 {a, b}。那么排序做法的另一个好处是我们不再需要用于 pair 查重时所需的 O(n) 容器,在每一轮中只需要一个变量存储 pair 即可。

    具体解法可以参考一下文章末尾贴出的博客。

    时间复杂度:O(n^2) = 排序O(nlgn) + 双重扫描O(n^2)
    空间复杂度:O(n)



    参考




  • 相关阅读:
    关于GitHub推送时发生Permission denied (publickey)的问题
    线性模型——机器学习(西瓜书)读书笔记
    梯度下降算法的简单理解
    PRML学习笔记第一章
    python函数学习之装饰器
    机器学习 概论
    Mybatis
    Nginx 常用配置清单
    接口,抽象类
    IntelliJ IDEA打war包
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/12903196.html
Copyright © 2020-2023  润新知