【问题标题】:Finding the top K frequent Elements寻找前K个频繁元素
【发布时间】:2021-05-16 00:05:59
【问题描述】:

我正在尝试解决有关 leetcode 的问题,该问题是查找前 k 个频繁元素。我认为我的代码是正确的,但是测试用例的输出失败了。

输入:[ 4,1,-1,2,-1,2,3]

K=2

我的答案是 {1,-1},但预期是 {-1,2}。我不知道我哪里错了。

    struct myComp{
    constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
        const noexcept
        {
            if(a.second==b.second)
            {
                return a.first<b.first;
            }
           return a.second<b.second;
        }
};
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> mp;
        for(int i=0;i<nums.size();i++)
        {
            mp[nums[i]]++;
        }
        priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
        for(auto x:mp)
        {
            minheap.push(make_pair(x.second,x.first));
            if(minheap.size()>k)
            {
                minheap.pop();
            }
        }
        vector<int> x;
        while(minheap.size()>0)
        {
            x.push_back(minheap.top().second);
            minheap.pop();
        }
        return x;

链接:https://leetcode.com/problems/top-k-frequent-elements

【问题讨论】:

  • 这似乎是学习如何调试代码的好时机。对于这么多元素,您可以在调试器中单步执行,看看哪里出了问题。

标签: c++ hash hashmap priority-queue


【解决方案1】:

您的代码有几个问题。

很明显,您的代码中有using namespace std;。应该避免这种情况。你会在这里找到很多关于 SO 的帖子,解释为什么不应该这样做。

然后我们需要使用std:: 来限定std 库中的所有元素,这使得范围非常明确。

下一步:您不需要自己的排序功能。由于您以交换顺序将对中的元素插入std::priority_queue,因此排序标准对计数器部分有效,而不是键值。因此,您的排序功能无论如何都是错误的,因为它是根据“第二”而不是“第一”进行排序。但是如果我们有一个标准的排序,我们就不需要一个特殊的排序算法。 std::pair 有一个小于运算符。所以,定义可以很简单:

std::priority_queue<std::pair<int, int>> minheap;

那么,你的if 声明

            if(minheap.size()>k)
            {
                minheap.pop();
            }

错了。您将只允许插入 k 个值。这不一定是最大的。因此,您需要插入来自std::unordered map 的所有值。然后对它们进行排序。

进行一些外观更改后,代码将如下所示:

#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>

std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
    std::unordered_map<int, int> mp;
    for (size_t i = 0; i < nums.size(); i++)
    {
        mp[nums[i]]++;
    }
    std::priority_queue<std::pair<int, int>> minheap;
    for (auto x : mp)
    {
        minheap.push(std::make_pair(x.second, x.first));
    }
    std::vector<int> x;
    for (size_t i{}; i< k; ++i)
    {
        x.push_back(minheap.top().second);
        minheap.pop();
    }
    return x;
}

int main() {
    std::vector data{ 4,1,-1,2,-1,2,3 };
    std::vector result = topKFrequent(data, 2);
    for (const int i : result) std::cout << i << ' '; std::cout << '\n';
    return 0;
}

额外的解决方案

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>

auto topKFrequent(std::vector<int>& nums, size_t k) {

    // Count occurences
    std::unordered_map<int, size_t> counter{};
    for (const int& i : nums) counter[i]++;

    // For storing the top k
    std::vector<std::pair<int, size_t>> top(k);

    // Get top k
    std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
        [](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });

    return top;
}
// Test code
int main() {
    std::vector data{ 4,1,-1,2,-1,2,3 };

    for (const auto& p : topKFrequent(data, 2))
        std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
    return 0;
}

当然,我们也为任何类型的可迭代容器提供通用解决方案。包括使用 SFINAE 定义类型特征并检查正确的模板参数。

#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>

// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
    std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()),     // begin/end and operator !=
    ++std::declval<decltype(std::begin(std::declval<Container&>()))&>(),                // operator ++
    void(*std::begin(std::declval<Container&>())),                                      // operator*
    void(),                                                                             // Handle potential operator ,
    std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);

// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));

// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;

// Function to get the k most frequent elements used  in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {

    if constexpr (is_iterable<Container>::value) {

        // Count all occurences of data
        Counter<Container> counter{};
        for (const auto& d : data) counter[d]++;

        // For storing the top k
        std::vector<Pair<Container>> top(k);

        // Get top k
        std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
            [](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });

        return top;
    }
    else
        return data;
}
int main() {
    std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
    for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n'; 
    std::cout << '\n';
    
    double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
    for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n'; 
    std::cout << '\n';

    std::string s{"abbcccddddeeeeeffffffggggggg"};
    for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n'; 
    std::cout << '\n';

    double value = 12.34;
    std::cout << topKFrequent(value,2) << "\n";

    return 0;
}

使用 Microsoft Visual Studio Community 2019 版本 16.8.2 开发和测试。

使用 clang11.0 和 gcc10.2 额外编译和测试

语言:C++17

【讨论】:

    【解决方案2】:

    minheap 中,一对&lt;frequency, element&gt; 被推送。由于我们要根据frequency对这些对进行排序,所以我们只需要根据frequency进行比较。

    假设有两对 ab。然后对于正常排序,比较将是:

     a.first < b.first;
    

    对于反向排序,比较是:

     a.first > b.first;
    

    在最小堆的情况下,我们需要反向排序。因此,以下比较器使您的代码通过所有测试用例:

    struct myComp
    {
        constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
            const noexcept
            {
               return a.first > b.first;
            }
    };
    

    【讨论】:

      猜你喜欢
      • 2021-07-08
      • 2022-11-28
      • 2019-03-02
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 2016-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多