所以我一直在为学校做一个项目,而且我遇到了困难。我的add_node函数无法正常工作,我知道原因。我要做的是接收一个包含多个随机生成的字母的文件,然后从中创建树,然后进行确认。
问题在于它覆盖了同一个节点,而不是创建多个节点。我使用Visual studio调试器来解决这个问题,但我不知道要实现什么来修复它。会发生的是,不是让多个节点创建一个树(如gagtttca),而是创建一个节点并覆盖它。节点变为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] = '\0';
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] = '\0';
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;
}
就像我之前说的那样,这是一个项目。我不是在寻找一个简单的出路。我只想学习如何修复我的代码。
答案 0 :(得分:3)
代码中最基本的问题是使用指针不方便的简单明显。从它的外观来看,你可能来自其他语言:
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 NOT\n";
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!!
<强>摘要强>
我强烈建议您在调试器中运行它,并牢牢抓住监视窗口。按照指针轨迹查看程序进展时的设置方式。有很多我没有谈到的事情:正确的构造,初始化,Rule of Three合规性等等。我也可以使用智能指针(如std::unique_ptr<>
或std::shared_ptr<>
。我真诚地希望你能从中得到的东西。它只会从这里变得更糟。
祝你好运
答案 1 :(得分:0)
我不知道为什么但是 这个:
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
看起来不适合我,还没有完成c ++一段时间和节点,但我不认为你是怎么做到的?将检查并编辑此内容。