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)



参考




posted @ 2020-05-17 01:07  bw98  阅读(236)  评论(0编辑  收藏  举报