问题描述
我的 CS 类(数据结构)中有一个作业,但我不断收到上述错误。 基本上我需要在 C++ 中编写代码,其中用户给出了一些链表,然后程序获得一个随机链表大小(从 100 到 200)以及一个随机元素(从 0 到 50)来添加到链接中列出自己。目标是创建一个名为 output 的最终数组,其中的链表元素至少出现在给定列表的一半或一半以上(每个列表必须对每个元素计数一次)。 我收到一个错误(进程以退出代码 134 结束(被信号 6 中断:SIGABRT) 为什么我收到这个错误? 谢谢。
#include <iostream>
#include <random>
#include <functional>
#include <stdlib.h>
using namespace std;
struct node{
int data;
node*next;
};
class linkedlistCreation{
private:node*head,*tail;
public:linkedlistCreation()
{
head=NULL;
tail=NULL;
}
void addnode(int random){
node*tmp=new node;
tmp->data=random;
tmp->next=NULL;
if(head==NULL){
head=tmp;
tail=tmp;
}else{
tail->next=tmp;
tail=tail->next;
}
}
};
int main() {
linkedlistCreation a;
int k;
int i;
int j;
int listsize;
int element;
default_random_engine generator;
uniform_int_distribution<int> list_size_distribution(100,200);
uniform_int_distribution<int> data_element_distribution(0,50);
auto random_list_size = bind(list_size_distribution,generator);
auto random_element = bind(data_element_distribution,generator);
cout << "Give number of lists:" ;
cin >> k;
int NumbersList[50]={0};
int CheckingList[50]={0};
for(i=0;i<k;i++){
listsize=random_list_size();
CheckingList[50]={0};//Initializing the checking List
for(j=0;j<=listsize;j++){
element=random_element();
cout<<element<<endl;
a.addnode(element);
if (CheckingList[element]==0){
NumbersList[element]=NumbersList[element]+1;
CheckingList[element]=CheckingList[element]+1;
}
}
// for (i=0;i<k;i++){
// if(NumbersList[i]>=k/2){
// output[i]=i;
// cout<<output[i]<<endl;
//
// }
//
//
// }
return 0;
}
解决方法
关于分配值得预先了解的是,当列表大小在 [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++ 是您的语言,请使用它。创建一个类,添加迭代器,使其成为模板类。链表是 RAII 等 C++ 中许多原则和实践的结晶,但它们都被抛弃了,因为我们将把全局可见的 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,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 行的程序。而且启动效率要低得多。
SIGABRT 被许多库用于在发生严重或致命错误(在运行时产生)时中止程序。但是,在您的情况下,问题出在“CheckingList”上。一个非常简单的解决方法是在块之外定义它。