【问题标题】:C++ changing object in vector doesn't workC++ 更改向量中的对象不起作用
【发布时间】:2020-04-28 00:40:02
【问题描述】:

我想使用一个向量来实现一个 trie 来存储节点,但不知何故我的插入方法不起作用。我已经设法使用不同的实现来构建 trie 数据结构,但我想了解为什么我当前的实现不起作用。

作品(不是基于索引的子/引用存储):

struct Trie {
   struct Trie *references[26];
   bool end; //It is true if node represents end of word.
};

不起作用(基于索引的子/引用存储):

struct node {
    int references[26] = {0};
    bool end; 
};

由于插入功能错误,它不起作用。

void insert_word(string s){
    node *current_node = &trie[0];
    // current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
    for(int i=0;i<s.size();i++){
        print_trie();
        int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
        int next_index = current_node->references[letter_num];
        cout << "letter num: " << letter_num << " next index: " << next_index << endl;
        if(next_index == 0){
            node new_node;
            trie.push_back(new_node);
            current_node->references[letter_num] = trie.size()-1; // DOESN'T WORK! Node in Trie is NOT UPDATED
            cout << "new value: ";
            for(auto c:current_node->references)
                cout << c << " ";
            cout << endl;
            cout << "in for" << endl;
            print_trie();
            current_node = &trie.back();
        } else{
            current_node = &trie[next_index];
        }
    }
    current_node->end = true;
}

问题在于,当我访问current_node 作为对对象 ob 的引用时,我会更改它的值。 trie 向量中的对象/节点并不总是更新。它在第二行工作,但更进一步它以某种方式停止工作。我想知道为什么。

这是我为简化问题而编写的一个简短的调试程序。这里似乎一切正常。

    n1.references[0] = 1;
    n2.references[0] = 2;
    n3.references[0] = 3;

    trie.push_back(n1);
    trie.push_back(n2);
    trie.push_back(n3);

    node *n = &trie[0];
    n->references[0] = 10; // Tree is updated properly
    n = &trie[1];
    n->references[0] = 11; // Tree is updated properly

你能帮我理解为什么插入功能不能正常工作吗?

编辑:最小的工作示例

#include <vector>
#include <string>
#include <iostream>

using namespace std;

struct node
{
    int num_words;
    int references [26] = {0};
    bool end; 
};

vector<node> trie;
int n;


void print_trie(){
    cout << "#### NEW PRINT TRIE ##### " << endl;
    for(int i=0;i<trie.size();i++){
        cout << "node " << i << ": ";
        for(int j=0;j<26;j++)
            cout << trie[i].references[j] << " ";
        cout << endl;
    }
}

void insert_word(string s){
    node *current_node = &trie[0];
    // current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
    for(int i=0;i<s.size();i++){
        print_trie();
        int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
        int next_index = current_node->references[letter_num];
        cout << "letter num: " << letter_num << " next index: " << next_index << endl;
        if(next_index == 0){
            node new_node;
            trie.push_back(new_node);
            current_node->references[letter_num] = trie.size()-1; // DOESN'T WORK! Node in Trie is NOT UPDATED
            cout << "new reference value of node: ";
            for(auto c:current_node->references)
                cout << c << " ";
            cout << endl;
            current_node = &(trie[trie.size()-1]);
        } else{
            current_node = &trie[next_index];
        }
    }
    current_node->end = true;
}



int main()
{
    node root;
    trie.push_back(root);
    insert_word("hallohallo");
    return 0;
}

【问题讨论】:

  • 这些:node *current_node = &amp;trie[0];current_node = &amp;trie.back();current_node = &amp;trie[next_index] 是引入悬空指针的秘诀。看循环操作。当/如果向量由于push_back 操作(显然在您的循环中)而调整大小时,这些指针存储可能/将变得无效。口头禅:不要将指向对象的指针存储在经历可变大小操作的向量中;使用 index 代替。

标签: c++ pointers reference trie


【解决方案1】:

每当std::vector&lt;T&gt; 进行大小调整操作时,所有迭代器和指向元素的指针都会失效。以你的 mcve 为例说明它在哪里出轨,请考虑标记线:

void insert_word(string s){
    node *current_node = &trie[0];  // **HERE
    for(int i=0;i<s.size();i++){
        print_trie();
        int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
        int next_index = current_node->references[letter_num];
        cout << "letter num: " << letter_num << " next index: " << next_index << endl;
        if(next_index == 0){
            node new_node;
            trie.push_back(new_node); //** RESIZE
            current_node->references[letter_num] = trie.size()-1;
            cout << "new reference value of node: ";
            for(auto c:current_node->references)
                cout << c << " ";
            cout << endl;
            current_node = &(trie[trie.size()-1]); // **HERE
        } else{
            current_node = &trie[next_index]; // **HERE
        }
    }
    current_node->end = true;
}

在每个标有// **HERE 的位置中,您都在存储指向向量中托管的对象的指针。但是一旦达到容量,标有// **RESIZE 的行可以(并且将)通过复制/移动/等调整整个向量的大小。这意味着current_node 不再指向一个有效对象,它是一个悬空指针,但您的代码并不明智,并且会进入未定义的行为

有几种方法可以解决这个问题。如果您提前知道它,您可以reserve 从一开始就知道它的容量,但是对于更强大的解决方案,不要一开始就使用指针。如果您通过 index 而不是指针进行枚举,则您的解决方案如下:

void insert_word(std::string s)
{
    size_t idx = 0;

    for(int i=0;i<s.size();i++){
        print_trie();
        int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
        size_t next_index = trie[idx].references[letter_num];
        std::cout << "letter num: " << letter_num << " next index: " << next_index << std::endl;
        if(next_index == 0){
            trie.emplace_back();
            trie[idx].references[letter_num] = trie.size()-1;
            std::cout << "new reference value of node: ";
            for(auto c : trie[idx].references)
                std::cout << c << ' ';
            std::cout << std::endl;
            idx = trie.size()-1;
        } else{
            idx = next_index;
        }
    }
    trie[idx].end = true;
}

注意current_node 的所有实例是如何被trie[idx] 替换的。而更改“当前节点”现在只需更改 idx 的值即可,即使基础向量调整大小也是如此。

【讨论】:

    【解决方案2】:

    这可能是由类型不匹配导致的int 被分配size_t
    试试...= (int)trie.size()-1

    #include <vector>
    #include <iostream>
    using namespace std;
    
    struct node{
        int num_words;
        int references [26] = {};   //........... int
        bool end; 
    };
    
    vector<node> trie;
    int n;
    
    void print_trie(){
        cout << "#### NEW PRINT TRIE ##### " << endl;
        for(int i=0;i<trie.size();i++){
            cout << "node " << i << ": ";
            for(int j=0;j<26;j++)
                    cout << trie[i].references[j] << " ";
            cout << endl;
        }
    }
    
    void insert_word(const string& s){
        node *current_node = &trie[0];
        // current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
        for(int i=0;i<s.size();i++){
            print_trie();
            int letter_num = int(tolower(s[i]) - 'a');
            int next_index = current_node->references[letter_num];
            cout << "letter num: " << letter_num << " next index: " << next_index << endl;
            if(next_index == 0){
                    node new_node;
                    trie.push_back(new_node);
                    current_node->references[letter_num] = (int)trie.size()-1; //....size_t  DOESN'T WORK! Node in Trie is NOT UPDATED
                    cout << "new reference value of node: ";
                    for(auto c:current_node->references)
                        cout << c << " ";
                    cout << endl;
                    current_node = &(trie[trie.size()-1]);
            } else{
                    current_node = &trie[next_index];
            }
        }
        current_node->end = true;
    }
    
    
    
    int main()
    {
        node root;
        trie.push_back(root);
        insert_word("hallohallo");
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2017-10-31
      • 2020-03-13
      相关资源
      最近更新 更多