【问题标题】:Sliding window using deque (runtime error)使用双端队列滑动窗口(运行时错误)
【发布时间】:2020-08-25 16:47:51
【问题描述】:

在下面的程序中,我试图在长度为 n 的数组中找到窗口大小的最大值 k。供参考,问题来自LeetCode

例如,如果数组是 [2 5 3 1 2] 并且窗口大小是 3,那么我有窗口为 [2 5 3]、[5 3 1]、[3 1 2] 并且对应的最大值是 5 , 5, 3.

我为此目的使用std::deque,但它给出了运行时错误,我认为没有发生任何指针失效,我找不到任何。请帮帮我,我在这个问题上卡了很长时间。

这是我的代码:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> d;
        for (int i = 0; i < k; ++i) {
            while (!d.empty() && d.front() < nums[i]) {
                d.pop_front();
            }
            d.push_front(nums[i]);
        }
        vector<int> ans;
        ans.push_back(d.back());

        int n = nums.size();
        for (int i = k; i < n; ++i) {
            while (1) {
                int elem = d.back();
                d.pop_back();
                if (elem == nums[i-k]) {
                    break;
                }
            }
            while (!d.empty() && d.front() < nums[i]) {
                d.pop_front();
            }
            d.push_front(nums[i]);
            ans.push_back(d.back());
        }
        return ans;
    }
};

显示的错误信息是这样的:

Line 157: Char 16: runtime error: reference binding to misaligned address 0xbebebebebebec0ba for type 'int', which requires 4 byte alignment (stl_deque.h)
0xbebebebebebec0ba: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_deque.h:162:16

【问题讨论】:

  • 请通过提供minimal reproducible example 证明错误不在您显示的代码之外。
  • @Yunnosch 实际上该网站没有提供其内部代码,仅显示用户可以编辑的类。但是过了一段时间我发现点击操场,我可以访问内部功能。所以这里是有问题的完整代码:pastebin.com/p0Fk3z2V 在此,向量在标准输入的括号中提供,如 [2,5,3,1,2],k 在下一行提供。跨度>

标签: c++ data-structures runtime-error deque


【解决方案1】:

观察你的这部分代码:

while (1) {
    int elem = d.back();         // get element
    d.pop_back();                // pop element

    if (elem == nums[i-k]) {     // compare element
        break;                   // and break on success
    }                            // What happens on failure???
}                                // Inifinite loop and UB!

while 循环未退出。

在每次迭代中,您都在从 deque 访问和弹出一个元素,直到它为空,因为 elem 不等于 nums[i-k]

while (1) 更改为:

while ( !d.empty() )

因为只有在双端队列不为空时才能从双端队列中获取和弹出元素!

并且,在空双端队列上调用 back()pop_back()Undefined Behavior

来自std::deque::back()的文档:

在空容器上调用 back 会导致未定义的行为。

std::deque::pop_back() 也是如此:

在空容器上调用 pop_back 是未定义的。

【讨论】:

  • 是的,这就是问题所在,除了算法也无法正常工作。感谢您的回答!
  • 谢天谢地我想出了如何更正它,这里是更正的代码。基本上,位于 i-k 的元素已从双端队列中删除。只有当 deque.back() 有该元素时,我们才需要删除它:pastebin.com/NgRrqWfM
  • @jeea:太好了!很高兴它有帮助,你终于让它工作了。继续!祝你好运!
【解决方案2】:

抱歉,我忍不住要另一种实现方式。

因此,我试图更接近滑动窗口的隐喻——即在现有向量上滑动开始和结束索引以确定子范围的最大值。

我仍然记得存在一个接受初始化列表的std::max() 风格。 (以某种方式使用它是我的初衷。)

然而,当我在谷歌上搜索(回忆细节并准备好链接)时,我偶然发现了更好的东西(也由<algorithm> 提供):

std::max_element

在 [first, last) 范围内查找最大的元素。

所以,我创建了一个MCVE 来看看使用std::max_element() 会是什么样子:

#include <iostream>
#include <algorithm>
#include <vector>

std::vector<int> maxSlidingWindow(
  const std::vector<int> &nums, // the sample data
  int k) // the length of sliding window
{
  if (k <= 0) return { }; // no maxima for insufficient range
  if ((size_t)k > nums.size()) k = nums.size(); // Ehem... window too large
  std::vector<int> ans;
  //ans.reserve(num.size() - k); // allocate final size at once (for more speed)
  for (size_t i = k; i <= nums.size(); ++i) {
    ans.push_back(
      *std::max_element(nums.begin() + i - k, nums.begin() + i));
  }
  return ans;
}      

std::ostream& operator<<(std::ostream &out, const std::vector<int> &vec)
{
  const char *sep = "";
  for (int value : vec) {
    out << sep << value;
    sep = " ";
  }
  return out;
}

int main()
{
  std::vector<int> sample = { 2, 5, 3, 1, 2 };
  std::cout << "Input: " << sample << '\n';
  std::cout << "Output: " << maxSlidingWindow(sample, 3) << '\n';
}

输出:

Input: 2 5 3 1 2
Output: 5 5 3

Live Demo on coliru

【讨论】:

  • 感谢您的回答!但是这种方法有一个问题,它在 O(n*k) 中计算,其中 n, k 可以是 1e5。所以我们需要一个 O(n) 解决方案,在调试和 Azem 的回答之后,我得出了这个结论:pastebin.com/NgRrqWfM
猜你喜欢
  • 2021-08-10
  • 1970-01-01
  • 1970-01-01
  • 2018-07-02
  • 2013-05-21
  • 1970-01-01
  • 2016-03-11
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多