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

时间:2016-03-31 02:58:00

标签: c++ binary-search-tree

所以我一直在为学校做一个项目,而且我遇到了困难。我的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;    

}

就像我之前说的那样,这是一个项目。我不是在寻找一个简单的出路。我只想学习如何修复我的代码。

2 个答案:

答案 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_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 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 ++一段时间和节点,但我不认为你是怎么做到的?将检查并编辑此内容。