将多个节点添加到树C++中

Adding multiple nodes to a tree C++

本文关键字:C++ 添加 节点      更新时间:2023-10-16

所以我为学校做了一段时间的项目,遇到了一堵墙。我的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_wordhas_word(后者用于验证确实存在的东西)。

该代码的工作原理是如何在添加和检查函数add_wordhas_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++和节点了,但我认为你不应该这样做?将检查并编辑此。