有一种或多或少的标准方法可以计算容器中的某物,然后获取并显示其排名。
对于计数部分,我们可以使用关联容器,例如 std::map 或 std::unordered_map。在这里,我们将一个“键”(在本例中为单词)与一个计数与一个值(在本例中为特定单词的计数)相关联。
幸运的是,选择这种方法的基本原因是地图有一个非常好的索引operator[]。这将查找给定的键,如果找到,则返回对该值的引用。如果没有找到,那么它将使用密钥创建一个新条目并返回对新条目的引用。因此,在这两种情况下,我们都会获得对用于计数的值的引用。然后我们可以简单地写:
std::unordered_map<char,int> counter{};
counter[word]++;
这看起来非常直观。
经过这个操作,你已经有了频率表。按键(单词)排序,使用std::map 或未排序,但使用std::unordered_map 可以更快地访问。
在您的情况下,如果您只对排序计数感兴趣,建议使用std::unordered_map,因为无需按其键对std::map 中的数据进行排序,以后不再使用此排序。
然后,您想根据频率/计数进行排序。不幸的是,这在地图上是不可能的。因为 map - 容器系列的主要属性是它们对 key 的引用,而不是值或计数。
因此,我们需要使用第二个容器,例如 std::vector 等,然后我们可以使用 std::sort 对任何给定的谓词进行排序,或者,我们可以将值复制到容器中,例如 @987654332 @ 隐含地对其元素进行排序。而且因为这只是一个班轮,所以这是推荐的解决方案。
此外,由于为 std 容器编写了所有这些长名称,我们使用 using 关键字创建别名。
在我们得到单词的排名之后,按照计数排序的单词列表,我们可以使用迭代器和循环来访问数据并输出它们。
我们需要注意带有排序值的容器包含足够的值,然后使用简单的 for 循环,或者像 std::copy_n 这样的标准算法。
热限制值?有 3 种可能的解决方案。
- 针对
if (elementsToPrint > container.size()) elementsToPrint = container.size()大小的手写比较功能
-
std::min,它基本上完成了上述工作。所以,elementsToPrint = std::min(elementsToPrint, container.size());
-
std::clamp。为此目的定义的专用函数。请阅读here 了解此函数。结果将是:````elementsToPrint = std::clamp(elementsToPrint,1, container.size());
很好。现在,我们要显示最低的 k 等级和最高的 k 等级。我们可以通过使用指向开头的迭代器来做到这一点,例如begin(),或者,谈论结尾,std::rbegin()
在这一切之后,我们现在编写超紧凑的代码,只需几行代码即可完成任务:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <iomanip>
#include <algorithm>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<std::string, 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>;
// ------------------------------------------------------------
int main() {
// Open file and check, if it can be opened.
if (std::ifstream inputFileStream{ "input.txt" }; inputFileStream) {
// Definition of our counter
Counter counter;
// Count -------------------------
for (std::string word{}; inputFileStream >> word; counter[word]++);
// Sort
Rank rank(counter.begin(), counter.end());
// Output ------------------------
size_t elementsToShow = 3;
// Show 3 biggest counts
auto fromTop = rank.begin();
for (size_t i{}; i < std::clamp(elementsToShow, 1u, rank.size()); ++i)
std::cout << fromTop->first << '\t' << fromTop++->second << '\n';
// Show 3 smallest counts
auto fromBottom = rank.rbegin();
for (size_t i{}; i < std::clamp(elementsToShow, 1u, rank.size()); ++i)
std::cout << fromBottom->first << '\t' << fromBottom++->second << '\n';
}
else std::cerr << "\n*** Error. Could not open input file\n\n";
}
通常情况下,您需要继续使用收集的 count 值。那么建议使用std::partial_sort_copy。基本上,我们此时会将所有计数放入结果向量中。即top和bottom。
排序标准也可以在复制函数的 lambda 中调整。
请参阅下面的一种可能的解决方案:
#include <iostream>
#include <fstream>
#include <string>
#include <utility>
#include <unordered_map>
#include <type_traits>
#include <algorithm>
#include <vector>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<std::string, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Resulting data
using Result = std::vector<Pair>;
// ------------------------------------------------------------
int main() {
// Open file and check, if it can be opened.
if (std::ifstream inputFileStream{ "input.txt" }; inputFileStream) {
// Definition of our counter
Counter counter;
// Count -------------------------
for (std::string word{}; inputFileStream >> word; counter[word]++);
// Build result ------------------------
unsigned int three = std::min(3u, counter.size());
Result top(three);
Result bottom(three);
// Get 3 biggest counts
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const Pair& p1, const Pair& p2) { return p1.second > p2.second; });
// get 3 smallest counts
std::partial_sort_copy(std::next(counter.begin(), counter.size() - three), counter.end(), bottom.begin(), bottom.end(),
[](const Pair& p1, const Pair& p2) { return p1.second > p2.second; });
// Output ------------------------
for (const auto& [word, count] : top) std::cout << word << '\t' << count << '\n';
for (const auto& [word, count] : bottom) std::cout << word << '\t' << count << '\n';
}
else std::cerr << "\n*** Error. Could not open input file\n\n";
}