You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LeetCode滑动窗口最大值问题中AddressSanitizer堆释放后使用错误排查

关于LeetCode 239题滑动窗口最大值的堆释放后使用错误问题

我目前正在学习C++,尝试解决LeetCode上的239. Sliding Window Maximum问题。尽管代码中从未调用过free(),却持续遇到ERROR: AddressSanitizer: heap-use-after-free错误,请问可能的原因是什么?

我的代码:

#include <vector>
#include <deque>
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if (k == 1) {
            return nums;
        }

        deque<int> q = {nums[k-1]};
        deque<int> qi = {k-1};
        for (int i = k-2; i >= 0; i--) {
            if (nums[i] > q.front()) {
                q.push_front(nums[i]);
                qi.push_front(i);
            }
        }
        vector<int> output = {q.front()};

        for (int i = k; i < nums.size(); i++) {
            if (qi.front() < i - k + 1) {
                q.pop_front();
                qi.pop_front();
            }
            if (q.size() > 0) {
                while (q.back() <= nums[i]) {
                    q.pop_back();
                    qi.pop_back();
                }
            }
            q.push_back(nums[i]);
            qi.push_back(i);

            output.push_back(q.front());
        }
        return output;
    }
};

完整错误信息:

==================================================================
==23==ERROR: AddressSanitizer: heap-use-after-free on address 0x6150000009fc at pc 0x000000356a92 bp 0x7ffd9138ecb0 sp 0x7ffd9138eca8
READ of size 4 at 0x6150000009fc thread T0
    #2 0x7f1796504082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
0x6150000009fc is located 508 bytes inside of 512-byte region [0x615000000800,0x615000000a00)
freed by thread T0 here:
    #4 0x7f1796504082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
previously allocated by thread T0 here:
    #4 0x7f1796504082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
Shadow bytes around the buggy address:
  0x0c2a7fff80e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff80f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8100: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2a7fff8110: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2a7fff8120: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c2a7fff8130: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]
  0x0c2a7fff8140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8150: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2a7fff8160: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2a7fff8170: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2a7fff8180: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==23==ABORTING

问题原因及修复方案

核心问题:双端队列同步逻辑错误+空队列未检查

你的代码中q(存数值)和qi(存索引)需要严格同步,但初始化和滑动窗口处理的逻辑破坏了这种同步性,且未在访问队列元素前检查是否为空,导致访问已释放的队列内存,触发heap-use-after-free错误。

具体问题点:

  1. 初始化逻辑错误
    原初始化代码只在nums[i] > q.front()时才添加元素,无法构建正确的单调递减队列,导致队列元素数量不足,后续滑动窗口处理时容易出现队列空了还访问的情况。
  2. 未检查队列是否为空就访问元素
    • 滑动窗口循环中,直接调用qi.front()而未检查qi是否为空,若队列已空,会访问已释放的内存。
    • while (q.back() <= nums[i])循环中,虽然有if (q.size() > 0)判断,但循环过程中q可能被清空,此时q.back()仍会访问空队列。
  3. 窗口外元素移除不彻底
    原代码用if判断移除窗口外元素,可能存在多个元素在窗口外的情况,未全部移除会导致队列状态异常。

修复步骤:

  1. 修正初始化逻辑
    遍历第一个窗口的所有元素,维护单调递减队列:移除队列尾部所有小于等于当前元素的元素,再加入当前元素和索引,确保队首是窗口最大值。
  2. 访问队列前强制检查是否为空
    所有调用front()或back()的操作前,都要加!empty()判断。
  3. 彻底移除窗口外元素
    用while循环替换if,确保所有不在当前窗口内的元素都被移除。

修复后的完整代码:

#include <vector>
#include <deque>
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if (k == 1) {
            return nums;
        }

        deque<int> q;
        deque<int> qi;
        // 初始化第一个窗口,构建单调递减队列
        for (int i = 0; i < k; ++i) {
            while (!q.empty() && nums[i] >= q.back()) {
                q.pop_back();
                qi.pop_back();
            }
            q.push_back(nums[i]);
            qi.push_back(i);
        }
        vector<int> output = {q.front()};

        for (int i = k; i < nums.size(); ++i) {
            // 移除所有不在当前窗口内的元素
            while (!qi.empty() && qi.front() < i - k + 1) {
                q.pop_front();
                qi.pop_front();
            }
            // 维护单调递减队列
            while (!q.empty() && nums[i] >= q.back()) {
                q.pop_back();
                qi.pop_back();
            }
            q.push_back(nums[i]);
            qi.push_back(i);

            output.push_back(q.front());
        }
        return output;
    }
};

为什么会触发heap-use-after-free?

C++标准库的deque在元素被弹出后,内部内存可能被释放。当队列为空时,调用front()或back()属于未定义行为,AddressSanitizer会检测到这种对已释放内存的访问,抛出该错误。你的代码中未检查队列是否为空,加上队列同步逻辑错误,最终导致了这个问题。


内容的提问来源于stack exchange,提问作者haoyu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:14:52