【问题标题】:a runtime error on my leetcode submission :heap use after free on address我的 leetcode 提交时出现运行时错误:地址释放后堆使用
【发布时间】:2020-08-26 16:38:16
【问题描述】:
#include <bits/stdc++.h>
using namespace std;
#include <unordered_set>
#include <queue>
struct word {
    string s;
    int level;
    word(string a, int b)
        : s(a)
        , level(b)
    {
    }
};
bool isadj(string s1, string s2)
{
    int len = s1.length(), count = 0;
    for (int i = 0; i < len; i++) {
        if (s1[i] != s2[i])
            count++;
        if (count > 1)
            return false;
    }
    return count == 1 ? true : false;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
    unordered_set<string> st;
    for (string s : wordList)
        st.insert(s); // adding elements into a set
    if (st.find(endWord) == st.end())
        return 0;
    queue<word> q;
    q.push(word(beginWord, 0)); // initialising the queue

    while (!q.empty()) {
        word temp = q.front(); // pop the current string
        q.pop();
        if (temp.s == endWord)
            return temp.level;
        for (auto it = st.begin(); it != st.end(); it++) { // loop over the set to find strings at a distance of 1 and add them to the queue
            if (isadj(temp.s, *it)) // i have inserted code here to print the string *it
            {
                q.push(word(*it, temp.level + 1));
                st.erase(*it); // delete the element to avoid looping
            }
        }
    }
    return 0;
}

int main()
{
    // make dictionary
    vector<string> D;
    D.push_back("poon");
    D.push_back("plee");
    D.push_back("same");
    D.push_back("poie");
    D.push_back("plie");
    D.push_back("poin");
    D.push_back("plea");
    string start = "toon";
    string target = "plea";
    cout << "Length of shortest chain is: "
         << ladderLength(start, target, D);
    return 0;
}

我要解决的问题是https://leetcode.com/problems/word-ladder/ 我无法追踪我在哪里使用了在我的程序中再次释放的内存?

以下是我的调试尝试:

我试图在另一个在线 ide 上运行它,代码编译并成功运行,但给出了错误的答案。为了调试它,我在我的代码中插入了一些行,以便打印当前字符串距离为 1 的所有字符串。令人惊讶的是,集合中出现了一个空字符串。请帮助我了解我在哪里做错了。

【问题讨论】:

  • 您应该在本地配置一个 IDE 并在附加调试器的情况下运行它。仅使用在线工具进行开发是一种糟糕的方法。
  • 您似乎在使用include &lt;bits/stdc++.h&gt; 却不知道它的实际作用。所以请阅读thisWhy is “using namespace std;” considered bad practice?
  • 可能不是当前的问题,但在isadj 中你默默地假设s2 不长于s1,如果是你正在访问s2 越界跨度>
  • @idclev463035818 假设所有字符串的长度相同
  • 绝对不是当前的问题:在许多地方,您正在制作不必要的副本。传递参数和for (string s : wordList) st.insert(s);,如果我计算正确,字符串(例如"poon")在它们最终进入unordered_set 之前被复制3 次

标签: c++ breadth-first-search unordered-set


【解决方案1】:

unordered_set::erase返回一个值,这个返回值很重要。你不应该忽视它。

在你的情况下,一旦你从集合中删除某些东西,it 就无效了。试图增加它会导致未定义的行为。

正确的做法是用返回的迭代器替换当前迭代器,然后在循环期间不递增。

for (auto it = st.begin(); it != st.end(); )
    if (...) {
        // ...
        it = st.erase(*it);
    } else
        ++it;

【讨论】:

  • 旁注:在许多情况下,您会发现Erase-Remove Idiom 很有帮助。
  • 在上面建议的解决方案中它 = st.erase(*it);还是不行。当我只使用迭代器时它 = st.erase(it);问题解决了。
  • @venkat 您是否更改为for 循环以不增加迭代器?您的解决方案声明了一个新的 it 变量。
【解决方案2】:

行后:

st.erase(*it); // 删除元素以避免循环

it 迭代器无效,不应使用。

【讨论】:

  • 正确诊断,但好的答案提供或建议解决方案。
  • @BOFEE664 能否请您详细说明为什么在该行之后它无效?
  • @venkat 迭代器指的是集合中的一个项目。如果您从集合中移除此项目,则迭代器不再引用集合中的项目。由于迭代器不再引用集合中的一个项目,因此不能保证正确地迭代到集合中的下一个项目。关于迭代器失效的一些好的一般性阅读:Iterator invalidation rules
【解决方案3】:

您的问题似乎已经解决,但如果您有兴趣,也可以不使用std::queue,只使用std::unordered_set

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(NULL);
    std::cout.tie(NULL);
    return 0;
}();


// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>

using ValueType = std::int_fast16_t;

static const struct Solution {
    static const int ladderLength(
        const std::string start,
        const std::string end,
        const std::vector<std::string>& words
    ) {
        std::unordered_set<std::string> words_map(std::begin(words), std::end(words));
        std::unordered_set<std::string> head;
        std::unordered_set<std::string> tail;
        std::unordered_set<std::string>* curr_head;
        std::unordered_set<std::string>* curr_tail;

        if (words_map.find(end) == std::end(words_map)) {
            return 0;
        }

        head.insert(start);
        tail.insert(end);
        ValueType ladder = 2;

        while (!head.empty() && !tail.empty()) {
            if (head.size() < tail.size()) {
                curr_head = &head;
                curr_tail = &tail;

            } else {
                curr_head = &tail;
                curr_tail = &head;
            }

            std::unordered_set<std::string> temp_word;

            for (auto iter = curr_head->begin(); iter != curr_head->end(); iter++) {
                std::string word = *iter;

                for (ValueType index_i = 0; index_i < word.size(); index_i++) {
                    const char character = word[index_i];

                    for (ValueType index_j = 0; index_j < 26; index_j++) {
                        word[index_i] = 97 + index_j;

                        if (curr_tail->find(word) != curr_tail->end()) {
                            return ladder;
                        }

                        if (words_map.find(word) != std::end(words_map)) {
                            temp_word.insert(word);
                            words_map.erase(word);
                        }
                    }

                    word[index_i] = character;
                }
            }

            ladder++;
            curr_head->swap(temp_word);
        }

        return 0;
    }
};

你可能想把它分解成更多的方法,对于一个函数来说有点太长了。


参考文献

  • 有关更多详细信息,请参阅Discussion Board,您可以在其中找到大量解释清楚且公认的解决方案,其中包含各种languages,包括低复杂度算法和渐近runtime/memory 分析@ 987654325@, 2.

【讨论】:

    猜你喜欢
    • 2020-11-13
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2021-03-07
    • 2023-02-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    相关资源
    最近更新 更多