【问题标题】:Find frequency of each array element查找每个数组元素的频率
【发布时间】:2019-06-17 05:54:15
【问题描述】:

我有这件骇人听闻的事情要做。 N 必须

输入格式:

9

42 42 34 26 42 35 34 47 47

输出:

42 3

34 2

47 2

26 1

35 1

#include <iostream>
#include <algorithm>
using namespace std;

struct ele
{
    int count, index, val;
};


bool mycomp(struct ele a, struct ele b) {
    return (a.val < b.val);
}

bool mycomp2(struct ele a, struct ele b) {
     if (a.count != b.count) return (a.count < b.count);
    else return a.index > b.index;
}

void sortByFrequency(int arr[], int n)
{
    struct ele element[n];
    for (int i = 0; i < n; i++)
    {
        element[i].index = i;
        element[i].count = 0;
        element[i].val = arr[i];

    }

    stable_sort(element, element + n, mycomp);

    element[0].count = 1;

    for (int i = 1; i < n; i++)
    {
        if (element[i].val == element[i - 1].val)
        {
            element[i].count += element[i - 1].count + 1;

            element[i - 1].count = -1;

            element[i].index = element[i - 1].index;
        }

        else element[i].count = 1;
   }
   stable_sort(element, element + n, mycomp2);
   for (int i = n - 1, index = 0; i >= 0; i--)
       if (element[i].count != -1)
           for (int j = 0; j < element[i].count; j++)
               arr[index++] = element[i].val;
   }

int main() {
    int n; cin >> n;
    int* arr = new int[n];
    int* seen = new int[n];

    for (int i = 0; i < n; i++){
        while(arr[i] < 20 || arr[i] > 50) 
        cin >> arr[i];
    }

    sortByFrequency(arr, n);

        for (int i = 0; i < n; i++) {
        if (seen[i] == 0) {
            int count = 0;
           for (int j = i; j < n; j++)
                if (arr[j] == arr[i]) {
                    count += 1;
                    seen[j] = 1;
                }
            cout << arr[i] << " " << count << endl;
        }
    }
}

【问题讨论】:

  • 但这不起作用。什么不起作用?出乎意料的输出?超过时间长度?输出看起来并没有那么糟糕。您可以只计算出现次数,而不是排序。要计算 [20, 50] 范围内的可能值,大小为 50 - 20 + 1 的数组就足够了。
  • 我没有通过所有的测试用例。它们都被隐藏了。
  • 不是很有帮助。你怎么知道应该改进什么?真烦人……
  • 你可以使用std::map
  • 当您不知道测试用例时,您必须自己发明。这是一个需要检查的案例:n = 1,000,000。 struct ele element[n]; 是一个可变长度数组。它们未包含在标准 C++ 中的原因之一是它们很容易导致堆栈溢出。 ele 可以是 24 字节长。您的n 可能为 1,000,000。那是 24,000,000 字节的自动存储(通常是堆栈)。获得超过 10 MB 的自动存储空间是不常见的。幸运的是@Scheff 已经指出你不需要n 频率。

标签: c++ c++14


【解决方案1】:

并不是说我认为竞争性编程有助于提高编程技能。 (至少,不是日常业务的编程技能。)但是,我被诱惑了,无法抗拒。 (现在是星期一早上,可能是一周的热身。)

编译cmets的思路我得到了这个:

#include <cassert>
#include <iostream>
#include <vector>
#include <set>

int main()
{
  // input and count frequency
  int n; std::cin >> n;
  assert(n >= 0 && n < 1000000);
  const int valueMin = 20, valueMax = 50;
  int freq[valueMax - valueMin + 1] {};
  for (int i = 0; i < n; ++i) {
    int value; std::cin >> value;
    ++freq[value - valueMin];
  }
  // sort frequency
  std::set<std::pair<int, int>, bool(*)(const std::pair<int, int>&, const std::pair<int, int>&)>
    freqSorted([](const std::pair<int, int> &pair1, const std::pair<int, int> &pair2) {
      if (pair1.first != pair2.first) return pair1.first > pair2.first;
      return pair1.second < pair2.second;
    });
  for (int i = valueMin; i <= valueMax; ++i) {
    if (const int freqI = freq[i - valueMin]) {
      freqSorted.insert(std::make_pair(freqI, i));
    }
  }
  // output
  for (std::pair<int, int> entry : freqSorted) {
    std::cout << entry.second << ' ' << entry.first << '\n';
  }
}

注意事项:

  1. 我用

    const int valueMin = 20, valueMax = 50;
    int freq[valueMax - valueMin + 1] {};
    

    以最小的内存占用存储出现的次数。 (输入值范围的明确限制鼓励了我。)

  2. user4581301 让我对内存消耗很敏感。 (在阅读之前,我并没有意识到输入值的存储实际上是没有必要的。)

  3. 使用std::map(就像 Vishnu Dasu 推荐的那样)也是我的第一个想法。在测试时,我想知道丢失的结果,直到我意识到以出现次数为键的map 将仅存储具有相同频率的多个值中的一个。 因此,我将其更改为 std::set,并以两个值作为键。

输出:

42 3
34 2
47 2
26 1
35 1

Live Demo on coliru

【讨论】:

  • 太好了,我唯一没有提到的是,如果有 2 个元素具有相同的频率,你先打印较小的数字。
  • @TedA。如果这是违反要求的,则必须调整 set 中的 lambda ......这与您的问题完全相同。 ;-)
  • 它是必需的,因此所有测试用例都可以通过。你的通过 5/6
  • @TedA。对于两个相等的频率,是否需要先打印较小的数字?这正是我所做的。看看 lambda。首先是频率 -> 与&gt; 比较。其次是价值->与&lt;相比。
  • 是的。没有什么我没有提到的。我检查了它。它只是在最后一个测试用例上说“错误答案”。
【解决方案2】:

有时我想知道,可以为简单的任务编写多少行代码。无论如何。

有一个或多或少的标准方法来计算一般或容器中的东西。

我们可以使用std::mapstd::unordered_map 之类的关联容器。在这里,我们将“键”(在本例中为数字)与一个值(在本例中为特定数字的计数)相关联。

幸运的是,地图有一个非常好的索引运算符[]。这将查找给定的键,如果找到,则返回对该值的引用。如果未找到,则它将使用密钥创建一个新条目并返回对新条目的引用。因此,在这两种情况下,我们都会获得对用于计数的值的引用。然后我们可以简单地写:

std::unordered_map<int,unsigned int> counter{};
counter[value]++;

这看起来非常直观。

经过这个操作,你已经有了频率表。按键(值)排序,使用std::map 或未排序,但使用std::unordered_map 可以更快地访问。由于std::unordered_map 使用哈希,所以通常非常快。

现在您想根据频率/计数进行排序。不幸的是,这在地图上是不可能的。

因此我们需要使用第二个容器,例如 std::vector,然后我们可以对任何给定的谓词排序 unsing std::sort,或者,我们可以将值复制到容器中,例如隐含排序的 std::multiset它的元素。

这就是我们将要做什么以及会产生一些非常紧凑的代码:

#include <iostream>
#include <utility>
#include <unordered_map>
#include <type_traits>
#include <set>

// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<int, unsigned int>;

// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;

// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Sorter = std::multiset<Pair, Comp>;
// ------------------------------------------------------------

int main() {

    // Read number of elements to check
    if (int numberOfElements{}; std::cin >> numberOfElements) {

        // Read and count
        Counter counter{};
        for (int i{}, value{}; (i < numberOfElements) && (std::cin >> value); counter[value]++, ++i);

        // Sort
        Sorter sorter(counter.begin(), counter.end());

        // Show result
        for (const auto& [value, count] : sorter) std::cout << value << ' ' << count << '\n';
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多