关于赋值有一点值得了解的是,当列表大小在 [100, 200] 范围内时,[0, 50] 范围内的每个数字都将很容易出现在至少一半的列表中。对于可能的最小列表,如果我要手动均匀地分配数字,我会简单地从 0 开始并写下每个连续的数字。这意味着每个数字将在每个列表中出现一次。让它随机,有一个数字永远不会成功,但它很低。所以所需的输出似乎毫无意义。
如果我们只关注所需的输出,链表是一种糟糕的方法。进一步分解:
- 在 [0, 50] 范围内生成“n”个随机数序列。
- 注意每个序列中出现的数字
- 跟踪数字在任意序列中出现的次数
- 输出出现在至少一半序列中的数字(基本上每个数字)
我将使用std::set,而不是链接列表。它是有序的,更重要的是,每个元素都是唯一的。使用std::set 消除了处理重复项的需要。
然后我将计数保存在 std::map<int, int> 中,其中键是 [0, 50] 范围内的数字,键值是出现在“n”个序列中的该数字的计数。
最后,打印很容易。我只是通过地图,看看计数是否合格。
#include <iomanip>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <string>
#include <vector>
int main() {
std::mt19937 prng(std::random_device{}());
std::uniform_int_distribution<int> list_size(100, 200);
std::uniform_int_distribution<int> element_range(0, 50);
int num;
std::cout << "Number: ";
std::cin >> num;
std::map<int, int> intCountPerList;
for (int i = 0; i < num; ++i) {
int size = list_size(prng);
std::set<int> s;
for (int j = 0; j < size; ++j) {
s.insert(element_range(prng));
}
for (auto num : s) {
++intCountPerList[num];
}
}
std::cout << "Numbers that appeared in at least half of the 'lists': \n";
int numLength = std::to_string(num).length();
for (auto count : intCountPerList) {
if (count.second >= num / 2) {
std::cout << std::setw(numLength) << count.first << ": " << count.second
<< " times.\n";
}
}
}
如果你需要使用链表,程序基本保持不变。您只需添加生成列表的额外工作,然后将列表中的值添加到std::set。如果您不想使用std::set,则需要手动记录每个列表中的每个数字。这只是增加了不必要的中间人工作,但它是可行的。
在我继续之前,我对太多人在 C++ 中学习链表的方式有一个小小的抱怨。这是不对的。如果 C++ 是您的语言,请使用它。创建一个类,添加迭代器,使其成为模板类。链表是 C++ 中许多原则和实践(如 RAII)的结晶,但由于我们将全局可见的struct 和一些免费函数称为“数据结构”,所以它都被抛弃了。这真的很烦人。吐槽一下。
我的列表是双重链接的,主要原因是它的两倍好:-P。说真的,使用双向链表编写许多操作要容易得多。并且管理额外的指针是一个很小的成本。它还有迭代器,允许预期的遍历。这意味着我可以使用基于范围的 for 循环。此列表不完整,但我保留了它,主要用于 SO 答案。
#ifndef MY_LIST_HPP
#define MY_LIST_HPP
#include <algorithm> // std::swap
#include <cstddef> // std::size_t
/*
* Pre-declare template class and friends
*/
template <typename T>
class List;
template <typename T>
void swap(List<T>& lhs, List<T>& rhs);
/*
* List Class Declaration
*/
template <typename T>
class List {
public:
List() = default;
List(T val);
List(const List& other);
List(List&& other);
~List();
void push_front(T val);
void push_back(T val);
class iterator;
iterator begin();
iterator end();
// iterator find(T val);
std::size_t size() const;
iterator erase(iterator toErase); // Implement
void clear();
bool operator=(List other);
friend void swap<T>(List& lhs, List& rhs);
private:
struct Node {
T data;
Node* prev = nullptr;
Node* next = nullptr;
Node(T val) : data(val) {}
};
Node* m_head = nullptr;
Node* m_tail = nullptr;
std::size_t m_size = 0;
// Helper functions
void make_first_node(T val);
Node* find_node(T val);
};
/*
* List Iterator Declaration
*/
template <typename T>
class List<T>::iterator {
public:
iterator() = default;
iterator(List<T>::Node* node); // minimum
T& operator*(); // minimum
iterator& operator++(); // minimum
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
bool operator==(const iterator& other); // minimum
bool operator!=(const iterator& other); // minimum
private:
Node* m_pos = nullptr;
};
/*
* List Implementation
*/
template <typename T>
List<T>::List(T val) : m_head(new Node(val)), m_tail(m_head), m_size(1) {}
template <typename T>
List<T>::List(const List<T>& other) {
m_head = new Node((other.m_head)->data);
m_tail = m_head;
m_size = 1;
Node* walker = (other.m_head)->next;
while (walker) {
push_back(walker->data);
++m_size;
walker = walker->next;
}
}
template <typename T>
List<T>::List(List&& other) : List() {
swap(*this, other);
}
template <typename T>
List<T>::~List() {
clear();
}
template <typename T>
void List<T>::push_front(T val)
{
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->next = m_head;
m_head->prev = tmp;
m_head = tmp;
++m_size;
}
template <typename T>
void List<T>::push_back(T val) {
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->prev = m_tail;
m_tail->next = tmp;
m_tail = tmp;
++m_size;
}
template <typename T>
typename List<T>::iterator List<T>::begin() {
return iterator(m_head);
}
template <typename T>
typename List<T>::iterator List<T>::end() {
return iterator(nullptr);
}
// template <typename T>
// typename List<T>::iterator List<T>::find(T val) {
// return iterator(find_node(val));
// }
template <typename T>
std::size_t List<T>::size() const {
return m_size;
}
template <typename T>
typename List<T>::iterator List<T>::erase(typename List<T>::iterator toErase)
{
Node* node = find_node(*toErase);
if (node->prev) {
node->prev->next = node->next;
} else {
m_head = node->next;
}
if (node->next) {
node->next->prev = node->prev;
} else {
m_tail = node->prev;
}
Node* toReturn = node->next;
delete node;
return toReturn;
}
template <typename T>
void List<T>::clear() {
Node* tmp = m_head;
while (m_head) {
m_head = m_head->next;
delete tmp;
tmp = m_head;
}
m_tail = nullptr;
m_size = 0;
}
template <typename T>
bool List<T>::operator=(List other) {
swap(*this, other);
return *this;
}
template <typename T>
void List<T>::make_first_node(T val) {
m_head = new Node(val);
m_tail = m_head;
m_size = 1;
}
template <typename T>
typename List<T>::Node* List<T>::find_node(T val) {
if (!m_head) {
return nullptr;
}
Node* walker = m_head;
while (walker != nullptr && walker->data != val) {
walker = walker->next;
}
return walker;
}
template <typename T>
void swap(List<T>& lhs, List<T>& rhs) {
using std::swap;
swap(lhs.m_head, rhs.m_head);
swap(lhs.m_tail, rhs.m_tail);
swap(lhs.m_size, rhs.m_size);
}
/*
* List Iterator Implementation
*/
template <typename T>
List<T>::iterator::iterator(Node* node) : m_pos(node) {}
template <typename T>
T& List<T>::iterator::operator*() {
return m_pos->data;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator++() {
m_pos = m_pos->next;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator++(int) {
iterator tmp(m_pos);
++(*this);
return tmp;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator--() {
m_pos = m_pos->prev;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator--(int) {
iterator tmp(m_pos);
--(*this);
return tmp;
}
template <typename T>
bool List<T>::iterator::operator==(const iterator& other) {
return m_pos == other.m_pos;
}
template <typename T>
bool List<T>::iterator::operator!=(const iterator& other) {
return !(*this == other);
}
#endif
是的。而且还不完整。平心而论,也不是那么遥远。
这是修改后的main() 函数:
#include "niceList.hpp"
#include <iomanip>
#include <iostream>
#include <map>
#include <random>
#include <string>
#include <vector>
int main() {
std::mt19937 prng(std::random_device{}());
std::uniform_int_distribution<int> list_size(100, 200);
std::uniform_int_distribution<int> element_range(0, 50);
int num;
std::cout << "Number: ";
std::cin >> num;
std::map<int, int> intCountPerList;
for (int i = 0; i < num; ++i) {
int size = list_size(prng);
List<int> list;
for (int j = 0; j < size; ++j) {
list.push_back(element_range(prng));
}
int occurences[51]{0};
for (auto num : list) {
++occurences[num];
}
for (int idx = 0; idx < 51; ++idx) {
if (occurences[idx] > 0) {
++intCountPerList[idx];
}
}
}
std::cout << "Numbers that appeared in at least half of the 'lists': \n";
int numLength = std::to_string(num).length();
for (auto count : intCountPerList) {
if (count.second >= num / 2) {
std::cout << std::setw(numLength) << count.first << ": " << count.second
<< " times.\n";
}
}
}
如果您只是并排比较 main() 函数,您会发现这个函数所做的额外工作毫无意义。再次,我很想跳过列表并直接进入计数数组。对于这项任务,链表绝对是浪费时间和精力。我们说的是 40 行程序,而如果您需要自己编写列表,则至少要 2-3 行。而且启动效率要低得多。