给出一个答案,一个被接受。我想给出一个额外的答案,显示此任务的标准方法。
通常需要先对事物进行计数,然后再取回它们的排名或某些最高值或其他信息。
最常见的解决方案之一是为此使用所谓的关联容器,特别是std::map,甚至更好的是std::unordered_map。这是因为我们需要一个键值,上面描述的方式是一个字母和一个关联的值,这里是这个字母的计数。钥匙是独一无二的。同一个字母不能超过一个。这当然没有任何意义。
关联容器通过键值访问元素非常有效。
好的,有 2 个。 std::map 和 std::unordered_map。一种使用树以排序方式存储键,另一种使用快速散列算法访问键值。由于我们稍后对排序的键不感兴趣,但对出现的排序计数感兴趣,我们可以选择std::unordred_map。另一个好处是,这将使用提到的哈希算法快速访问密钥。
地图还有一个巨大的优势。有一个索引运算符[],它对于键值看起来非常快。如果找到,它将返回对与键关联的值的引用。如果没有找到,它将创建一个键并使用默认值(在我们的例子中为 0)初始化它的值。然后计算任意键就像map[key]++ 一样简单。
但是,后来,我们在这里经常听到:但它必须按计数排序。这当然不起作用,因为我的计数有重复的值,并且地图只能包含唯一的键值。所以,不可能。
解决方案是使用第二个关联容器std::multiset,它可以有更多相同的键和自定义排序运算符,我们可以在其中根据值进行排序。在这里,我们不是将键和值存储为 2 个元素,而是将两个值存储为 std::pair。我们按对的第二部分排序。
首先我们不能使用std::multi:set,因为我们需要唯一的键(在本例中是字母)。
上述方法为我们提供了极大的灵活性和易用性。我们基本上可以用这个算法计算任何东西
例如可以看下面的简洁代码:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <cctype>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, 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 Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// --------------------------------------------------------------------------------------
// Compact function to calculate the frequency of charcters and then get their rank
Rank getRank(std::string& text) {
// Definition of our counter
Counter counter{};
// Iterate over all charcters in text and count their frequency
for (const char c : text) if (std::isalpha(c)) counter[char(std::tolower(c))]++;
// Return ranks,sorted by frequency and then sorted by character
return { counter.begin(), counter.end() };
}
// --------------------------------------------------------------------------------------
// Test, driver code
int main() {
// Get a string from the user
if (std::string text{}; std::getline(std::cin, text))
// Calculate rank and show result
for (const auto& [letter, count] : getRank(text))
std::cout << letter << " = " << count << '\n';
}
请查看使用的最少语句。非常优雅。
但我们经常看到数组被用作关联容器。它们还有一个索引(一个键)和一个值。缺点可能是未使用密钥的空间开销。此外,这只适用于已知量级的东西。例如 26 个字母。其他国家的字母可能有更多或更少的字母。那么这种解决方案就没有那么灵活了。反正也经常用也OK。
因此,您的解决方案可能稍微复杂一些,但当然仍然有效。
让我再举一个例子来获取任何容器的最高值。在这里您将看到,这样的解决方案是多么灵活。
对不起,它有点高级。 . .
#include <iostream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
#include <iterator>
#include <type_traits>
#include <string>
// 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>;
template <typename Container>
using UnderlyingContainer = std::vector<Pair<Container>>;
// Predicate Functor
template <class Container> struct LessForSecondOfPair {
bool operator () (const Pair<Container>& p1, const Pair<Container>& p2) { return p1.second < p2.second; }
};
template <typename Container>
using MaxHeap = std::priority_queue<Pair<Container>, UnderlyingContainer<Container>, LessForSecondOfPair<Container>>;
// Function to get most frequent used number in any Container ---------------------------------------------------------
template <class Container>
auto topFrequent(const Container& data) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// Build a Max-Heap
MaxHeap<Container> maxHeap(counter.begin(), counter.end());
// Return most frequent number
return maxHeap.top().first;
}
else
return data;
}
// Test
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 };
std::cout << "Most frequent is: " << topFrequent(testVector) << "\n";
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
std::cout << "Most frequent is: " << topFrequent(cStyleArray) << "\n";
std::string s{ "abbcccddddeeeeeffffffggggggg" };
std::cout << "Most frequent is: " << topFrequent(s) << "\n";
double value = 12.34;
std::cout << "Most frequent is: " << topFrequent(value) << "\n";
return 0;
}