将多个节点添加到树C++中
Adding multiple nodes to a tree C++
所以我为学校做了一段时间的项目,遇到了一堵墙。我的add_node函数工作不正常,我知道原因。我想做的是接收一个包含多个随机生成的字母的文件,并用它们创建树,然后进行确认。
问题是它覆盖了同一个节点,而不是创建多个节点。我使用Visual Studio调试器解决了这个问题,但我不知道该实现什么来修复它。结果是,它不是让多个节点创建一个树(如gatttca),而是创建一个节点并覆盖它。节点变成g,然后变成a,等等。我该如何在不覆盖它的情况下向树中添加更多节点?add_node函数是最后一个。
#include "stdafx.h"
#include <iostream>
#include <stack>
#include <fstream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
class myTreeNode
{
public:
char Data;
myTreeNode *childA; //A's always go in child1
myTreeNode *childT; //T's always go in child2
myTreeNode *childC; //c's always go in child3
myTreeNode *childG; //G's always go in child4
};
class Tree
{
public:
myTreeNode * Root;
Tree()
{
Root = new myTreeNode;
Root->Data = '-';
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
}
bool add_a_word(string word);
bool is_this_word_in_the_tree(string word);
bool add_node(myTreeNode * parent, char letter);
bool add_words(vector<string> w);
};
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words);
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads);
bool write_out_the_vector_to_screen(vector<string> my_vector);
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name);
ofstream out;
int main()
{
out.open("my_results.txt");
vector<string> words_in_genome;
char * genome_file_name = "my_genome.txt";//make certain to place this file in the correct folder. Do not change path.
if (!get_words_from_the_file(genome_file_name, words_in_genome))
return 1;
Tree * trees = new Tree();
trees->add_words(words_in_genome);
char * reads_file_name = "reads.txt"; //make certain to place this file in the correct folder. Do not change path.
if (!get_the_reads_from_file(reads_file_name, reads_to_be_tested))
return 1;
for (int i = 0; i < reads_to_be_tested.size(); i++)
{
out <<reads_to_be_tested[i] << " " << trees->is_this_word_in_the_tree(reads_to_be_tested[i]);
}
cout << "All done" << endl;
//Write out a file named "myResults.txt".
//For each read, list its sequence and either "Yes" or "No".
//This will indicate if it does or doesn't map to the genome.
/** Used for debugging
cout << "words" << endl;
write_vector_to_screen(words);
write_vector_to_file(words,"testing.txt");
cout << "reads" << endl;
write_vector_to_screen(reads);
***/
out.close();
}
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words)
{
int i, j;
int len = 0;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "I could not find " << my_file_name << endl;
cout << "Check the location.n";
return false;
}
char * my_word = new char[11];
while (in.peek() != EOF) { in >> my_word[0]; len++; }
in.clear(); in.close(); in.open(my_file_name);
for (i = 0; i<10; i++)
{
in >> my_word[i];
if (my_word[i]<97) my_word[i] += 32; //makes it lowercase
}
my_word[10] = ' ';
vector_of_words.push_back(my_word);
for (i = 1; i<(len - 10 - 1); i++) //read until the end of the file
{
//shift
for (j = 0; j<9; j++) my_word[j] = my_word[j + 1];
in >> my_word[9];
if (my_word[9]<97) my_word[9] += 32; //makes it lowercase
my_word[10] = ' ';
cout << i << "t" << my_word << endl; cout.flush();
vector_of_words.push_back(my_word);
}
in.clear(); in.close();
return true;
}
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads)
{
int i;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "The read file " << my_file_name << " could not be opened.nCheck the location.n";
return false;
}
char * word = new char[20]; //this is a default, we'll be looking at words of size 10
while (in.peek() != EOF)
{
in.getline(word, 20, 'n');
for (i = 0; i<10; i++) { if (word[i]<97) word[i] += 32; } //makes it lowercase
reads.push_back(word);
}
in.clear(); in.close();
delete word;
return true;
}
bool write_out_the_vector_to_screen(vector<string> my_vector)
{
int i;
for (i = 0; i < my_vector.size(); i++)
{
cout << my_vector[i].c_str() << endl;
}
return true;
}
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name)
{
ofstream out;
out.open(my_file_name);
int i;
for (i = 0; i<my_vector.size(); i++)
out << my_vector[i].c_str()<< endl;
out.clear();
out.close();
return true;
}
bool Tree::add_words(vector<string> w)
{
for (int i = 0; i < w.size(); i++)
add_a_word(w[i]);
return true;
}
bool Tree::add_a_word(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
tempNode = tempNode->childA;
else
{
add_node(tempNode, word[i]);//add a node: what letter, who's my parent
tempNode = tempNode->childA;
}
}
else if (word[i]== 'g')
{
if (tempNode->childG != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childG;
}
}
else if (word[i] == 'c')
{
if (tempNode->childC != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childC;
}
}
else if (word[i] == 't')
{
if (tempNode->childT != NULL)
tempNode = tempNode->childT;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childT;
}
}
else
{
cout << "The tree is full, or can't find data" << endl;
return NULL;
break;
}
}
}
}
}
bool Tree::is_this_word_in_the_tree(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
char com1, com2, com3, com4;
if (tempNode == NULL)
{
cout << "The tree is empty. Sorry" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
{
if (tempNode->childA)
{
tempNode = tempNode->childA;
com1 = 'y';
}
}
else
{
com1 = 'n';
}
}
if (word[i] == 'g')
{
if (tempNode->childG != NULL)
{
if (tempNode->childG)
{
tempNode = tempNode->childG;
com2 = 'y';
}
}
else
{
com2 = 'n';
}
}
if (word[i] == 't')
{
if (tempNode->childT != NULL)
{
if (tempNode->childT)
{
tempNode = tempNode->childG;
com3 = 'y';
}
}
else
{
com3 = 'n';
}
}
if (word[i] == 'c')
{
if (tempNode->childC != NULL)
{
if (tempNode->childC)
{
tempNode = tempNode->childC;
com4 = 'y';
}
}
else
{
com4 = 'n';
}
}
}
out << com1 << com2 << com3 << com4 << endl;
if (com1 == com2 == com3 == com4)
{
out << "The test passed" << endl;
}
else
{
out << "The test failed" << endl;
return false;
}
}
}
return true;
}
bool Tree::add_node(myTreeNode * parent, char letter)
{
//Can't figure out how to fix error. Run-Time error is that it overwrites the node instead of adding it.
//How would i make it so it's a new node every time?//
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
正如我之前所说,这是一个项目。我不是来找个简单的出路的。我只想学习如何修复我的代码。
代码中最基本的问题是显而易见的,即您不愿意使用指针。从外观上看,你可能来自其他语言,其中的方言是:
Type *p = new Type;
p = Something;
是常见的。它在C++中一点也不常见。与C中一样,动态分配由返回的地址管理,该地址将被保存、处理,如果一切顺利,最终将被处理。这些地址保存在指针变量中。C++中的指针不包含对象;它们持有地址。
也就是说,我不会毁掉你写的所有东西。我不会粉饰这件事;这将是枪杆子里的鱼。我将描述应该在add_node
中做什么,向您展示哪里出了问题,最后给出一个简单的例子,消除了现有代码中的大部分漏洞(文件io等),重点关注眼前的真正问题:树节点管理和完成它所需的指针竞争。
任务
您应该从根节点开始,对于字符串中的每个连续字母,向下移动树。当您遇到想要走的路径,但因为还没有节点挂起而无法时,就是当您分配一个新节点,挂起它,移动到它,然后继续该过程,直到输入字符串中没有更多字符为止。
您的代码
也就是说,请查看以下中的评论
bool Tree::add_node(myTreeNode * parent, char letter)
{
myTreeNode * tempNode = new myTreeNode;
// this is outright wrong. you just leaked the memory
// you allocated above. this has no place here and
// should be removed.
//
// Note: the remainder of this analysis will assume you
// have, in fact, removed this line.
tempNode = Root;
// all of this belongs in your myTreeNode constructor.
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
// this is flat-out impossible. Assuming you fixed your incorrect
// Root assignment mentioned above, you just allocated a new node
// therefore this can NEVER be NULL (an exception would have thrown
// on a failure to allocate).
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
// This NEVER changes. Nowhere in the code below this is
// tempNode ever assigned a different value. this loop
// should not even be here. A simple if-else-if stack or
// a switch on letter is all that is needed.
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
样本代码
以下内容删除了所有的io文件,以及关于管理树的大部分疯狂内容。只有两个成员函数,add_word
和has_word
(后者用于验证确实存在的东西)。
该代码的工作原理是如何在添加和检查函数add_word
和has_word
中使用指针对指针。此外,我们从根节点指针开始,使用输入字符串中的每个连续字符,向下移动树。当一个子指针为NULL时,我们分配一个新节点,挂起它,然后继续前进。检查函数has_word
做的事情完全相同,只是有一个区别:它不挂起新节点。当它在不应该有NULL的地方遇到NULL时,这意味着出现了错误,并且输入单词不在树中。
#include <iostream>
#include <random>
#include <string>
struct myTreeNode
{
char data;
myTreeNode *childA;
myTreeNode *childT;
myTreeNode *childC;
myTreeNode *childG;
myTreeNode( char c )
: data(c), childA(), childT(), childC(), childG()
{
}
~myTreeNode()
{
delete childA;
delete childT;
delete childC;
delete childG;
}
// squelch these
myTreeNode(const myTreeNode&) = delete;
myTreeNode& operator=(const myTreeNode&) = delete;
};
class Tree
{
private:
myTreeNode *Root;
public:
Tree() : Root( new myTreeNode('-')) { }
~Tree() { delete Root; }
// squelch these
Tree(const Tree&) = delete;
Tree& operator =(const Tree&) = delete;
// adds a given string into the tree if it isn't already there.
void add_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default:
std::cerr << "skipping unsupported char '" << c << "'n";
}
if (!*pp)
*pp = new myTreeNode(c);
}
}
// returns true if the given string is in the tree
bool has_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default: // should never happen with proper input
return false;
}
if (!*pp)
return false;
}
return true;
}
};
////////////////////////////////////////////////////////////////////////////////
int main()
{
// setup a random device and some uniform distributions
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<> dchar(0,3);
std::uniform_int_distribution<> dlen(3,8);
// our restricted alphabet. random indexes for creating our
// strings will be coming by indexing with dchar(rng)
char s[] = {'a', 't', 'c', 'g' };
// build set of random strings
std::vector<std::string> strs;
for (int i=0; i<20; ++i)
{
std::string str;
int len = dlen(rng);
for (int j=0; j<len; ++j)
str.push_back(s[dchar(rng)]); // push random char
strs.emplace_back(str);
}
// drop list of strins into tree
Tree tree;
for (auto const& str : strs)
{
std::cout << str << 'n';
tree.add_word(str);
}
// now verify every string we just inserted is in the tree
for (auto const& str : strs)
{
if (!tree.has_word(str))
{
std::cerr << "Word "" << str << "" should be in tree, but was NOTn";
std::exit(EXIT_FAILURE);
}
}
std::cout << "All test words found!!n";
return EXIT_SUCCESS;
}
输出(因随机发生器而异)
gctccgga
agtccatt
gagcg
gtggg
tca
aga
cacaggg
cga
tgga
ttatta
cagg
aac
tatttg
gccttat
acctcca
tgagac
aagacg
tgc
aaccgg
tca
All test words found!!
摘要
我强烈建议您在调试器中运行此程序,并在观察窗口中牢牢把握它。跟随指针轨迹,查看程序进行过程中的设置。有很多事情我没有谈到:正确的构造、初始化、遵守三条规则等。我也可以(如果不是学术案例的话)使用智能指针,如std::unique_ptr<>
或std::shared_ptr<>
。我真诚地希望你能从中得到一些东西。从现在开始,情况只会变得更糟。
祝好运
我不知道为什么,但是这个:
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
对我来说不太合适,有一段时间没有做c++和节点了,但我认为你不应该这样做?将检查并编辑此。
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 如何在C++中从两个单独的for循环中添加两个数组
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 如何仅为一个函数添加延迟
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 使用std::transform将一个范围的元素添加到另一个范围中
- 如何将更多文件夹添加到c++include路径
- 如何将元素添加到数组的线程安全函数?
- QT通过C++添加映射QML项目
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 只能向C++添加一定数量的字符
- Qt和C++:将QLineEdit添加到QTabWidget中
- 将QIcon添加到QTableView单元格
- 为什么我必须在C++中添加一个赋值符号来声明一个数组
- 为什么除非添加括号,否则构造函数上的模板替换会失败?
- 将图像添加到资源文件夹UWP C++
- 当我尝试添加 2 个大字符串时,我无法弄清楚出了什么问题