算法从入门到精通——滑动窗口

文章目录

◆ 博主名称:此生决int

大家好,欢迎来到我的博客~

⭐ 个人专栏:快速复习系列

⭐ 热门专栏:算法基础到精通系列


大家好,我是此生决int,欢迎来到今天的算法专题

今天我们正式学习算法道路上的第一个经典算法------滑动窗口。双指针为什么能把原本 O(n²) 的暴力解法优化到 O(n)?它又为什么会成为面试中的高频考点?我们接着往下看吧!

上期回顾

上期我们主要介绍了左右指针""快慢指针 等经典的双指针算法

文章概要

本文将系统讲解算法中的经典思想------滑动窗口算法。文章会通过 7 道 LeetCode 高频经典题,由浅入深地讲解滑动窗口的核心思想与常见套路。从最基础的最短子数组问题,到字符串哈希滑窗,再到固定步长窗口与分组滑窗,逐步带大家掌握滑动窗口的完整知识体系,无论你是算法初学者,还是正在准备 LeetCode、蓝桥杯、ICPC 等算法竞赛的同学,都能通过这篇文章快速建立对滑动窗口算法的理解与应用能力。

滑动窗口算法简介

什么是滑动窗口?

滑动窗口的本质其实就是双指针,即同向双指针!

如果让你暴力枚举所有区间,时间复杂度往往是 O(n²)。

但很多题目里,我们其实没必要"重新遍历"整个区间 。这时候,滑动窗口就登场了。

滑动窗口本质上是:
leftright 两个指针维护一个动态区间,让窗口像尺子一样在数组或字符串上滑动,从而在线性时间内解决问题。

它最大的魅力就在于:

很多看似需要两层循环的问题,都能被优化到 O(n)。


什么时候可以使用滑动窗口?

判断一个题目能不能用滑动窗口,关键看一个特征:

左右指针是否具有"单调不回退"性质。

也就是说:

  • right 指针不断向右扩展窗口
  • left 指针在需要时向右收缩窗口
  • 两个指针都只向前走,不会后退

因此,每个元素最多被访问两次,时间复杂度通常可以降到:O(n)

接下来,我们就从最经典的题型开始,彻底掌握滑动窗口的核心套路。

本章算法题的简单总结(建议最后看)

1,滑动窗口

    1. 长度最小的子数组
    1. 最大连续1的个数 III
    1. 将 x 减到 0 的最小操作数
      2,滑动窗口+哈希
    1. 无重复字符的最长子串
    1. 水果成篮
    1. 找到字符串中所有字母异位词
    1. 串联所有单词的子串

滑动窗口

1,长度最小的子数组⭐

题目链接:

长度最小的子数组

解题思路:

题目要求我们寻找"长度最小"的连续子数组,本质上就是在数组中维护一个动态区间,因此可以使用滑动窗口(双指针)来解决。

我们用 left right 两个指针维护当前窗口,并记录窗口内元素之和 sum

由于数组中的元素全部为正数,因此窗口具有单调性:

sum < target 时,说明当前窗口不满足条件,需要扩大窗口,让 right 右移。

sum >= target 时,说明当前窗口已经满足条件,此时可以尝试缩小窗口,让 left 右移,从而寻找更短的满足条件的子数组。

在整个过程中,left right 都只会向右移动一次,因此时间复杂度为 O(n)。

解题代码

cpp 复制代码
/*
解题思路:
使用滑动窗口(双指针)维护一个连续区间[left, right]。

1. 当窗口内元素和 sum 小于 target 时:
   说明当前窗口不满足条件,需要扩大窗口,right 右移。

2. 当 sum >= target 时:
   说明当前窗口已经满足条件,此时尝试缩小窗口,
   更新最小长度后让 left 右移,寻找更短的满足条件的子数组。

由于 left 和 right 都只会向右移动一次,时间复杂度为 O(n)。
*/

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0, right = 0;

        // 当前窗口的元素和
        int sum = nums[0];

        // 记录最小长度
        int ret = 0x3f3f3f3f;

        // 滑动窗口
        while (right < nums.size() && left <= right)
        {
            // 当前窗口和不足 target,扩大窗口
            if (sum < target)
            {
                right++;

                // 防止越界
                if (right < nums.size())
                    sum += nums[right];
            }
            else
            {
                // 更新最小长度
                ret = min(ret, right - left + 1);

                // 缩小窗口
                sum -= nums[left];
                left++;
            }
        }

        // 如果没有满足条件的子数组,返回 0
        return ret == 0x3f3f3f3f ? 0 : ret;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    int minSubArrayLen(int target, vector<int>& nums)
    {
        int n = nums.size();
        int sum = 0;                // 窗口内元素和
        int ret = INT_MAX;          // 记录最小长度,初始为最大值
        // 滑动窗口:left为窗口左边界,right为窗口右边界
        for(int left = 0, right = 0; right < n; right++)
        {
            sum += nums[right];     // 右指针右移,元素加入窗口
            
            // 当窗口和≥target时,收缩左边界,更新最小长度
            while(sum >= target)
            {
                ret = min(ret, right - left + 1); // 更新当前窗口的长度
                sum -= nums[left++];              // 移除左边界元素,左指针右移
            }
        }
        // 若ret仍为INT_MAX,说明无有效子数组,返回0;否则返回ret
        return ret == INT_MAX ? 0 : ret;
    }
};

2,无重复字符的最长子串⭐⭐

题目链接:

无重复字符的最长子串

解题思路:

题目要求我们寻找"最长的不含重复字符的子串",本质上也是在字符串中维护一个动态区间,因此可以使用滑动窗口(双指针)来解决。

我们用 leftright 两个指针维护当前窗口,并使用哈希表记录窗口中每个字符出现的次数。

当某个字符出现重复时,说明当前窗口已经不合法,此时需要不断移动 left 缩小窗口,直到窗口中不存在重复字符为止。

在窗口始终合法的过程中,我们不断更新窗口长度的最大值即可。

由于 leftright 都只会向右移动一次,因此时间复杂度为 O(n)

解题代码

cpp 复制代码
/*
解题思路:

使用滑动窗口(双指针)维护一个连续区间[left, right]。

1. right 指针不断向右扩展窗口,
   并统计当前字符出现的次数。

2. 如果当前字符出现重复:
   说明窗口不合法,此时移动 left 缩小窗口,
   直到窗口重新变成无重复字符。

3. 在窗口合法的过程中,
   不断更新最长子串长度。

由于 left 和 right 都只会向右移动一次,
所以时间复杂度为 O(n)。
*/

class Solution
{
public:
    int lengthOfLongestSubstring(string s)
    {
        // 数组模拟哈希表,记录字符出现次数
        int hash[128] = {0};

        // 滑动窗口左右边界
        int left = 0, right = 0;

        int n = s.size();

        // 记录最长无重复子串长度
        int ret = 0;

        // 滑动窗口
        while(right < n)
        {
            // 当前字符进入窗口
            hash[s[right]]++;

            // 出现重复字符,缩小窗口
            while(hash[s[right]] > 1)
            {
                // 左边界字符移出窗口
                hash[s[left]]--;

                // 左边界右移
                left++;
            }

            // 更新最长长度
            ret = max(ret, right - left + 1);

            // 扩大窗口
            right++;
        }

        return ret;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    int lengthOfLongestSubstring(string s)
    {
        int hash[128] = {0}; // 记录窗口内字符出现次数
        int left = 0, right = 0;
        int ret = 0;

        while(right < s.size())
        {
            hash[s[right]]++; // 当前字符进入窗口

            // 当前字符重复,缩小窗口
            while(hash[s[right]] > 1)
            {
                hash[s[left]]--;
                left++;
            }

            // 更新最长长度
            ret = max(ret, right - left + 1);

            right++; // 扩大窗口
        }

        return ret;
    }
};

3,最大连续1的个数 III⭐⭐

题目链接:

最大连续1的个数 III

解题思路:

题目要求我们求最长的连续 1 的个数,并且最多可以将 k 个 0 翻转成 1。

本质上就是在数组中维护一个合法区间,因此可以使用滑动窗口(双指针)来解决。

我们用 leftright 两个指针维护当前窗口,并用变量 zero 记录窗口中 0 的个数。

由于题目允许最多翻转 k 个 0,因此:

  • 当窗口内 0 的个数 <= k 时:

    当前窗口合法,可以更新答案。

  • 当窗口内 0 的个数 > k 时:

    当前窗口不合法,需要移动 left 缩小窗口,直到窗口重新合法。

在整个过程中,leftright 都只会向右移动一次,因此时间复杂度为 O(n)

解题代码

cpp 复制代码
/*
解题思路:

使用滑动窗口(双指针)维护一个连续区间[left, right]。

1. right 指针不断向右扩展窗口,
   并统计窗口内 0 的个数。

2. 当窗口内 0 的个数大于 k 时:
   说明当前窗口不合法,
   需要移动 left 缩小窗口。

3. 当窗口重新合法后,
   更新窗口的最大长度。

由于 left 和 right 都只会向右移动一次,
所以时间复杂度为 O(n)。
*/

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {

        // left 和 right 为滑动窗口左右边界
        int left = 0;
        int right = 0;

        // 记录窗口内 0 的个数
        int zero = 0;

        // 记录最长合法窗口长度
        int retlen = 0;

        // 滑动窗口
        while(right < nums.size())
        {
            // 当前元素为 0
            if(nums[right] == 0)
            {
                zero++;
            }

            // 窗口不合法,缩小窗口
            while(zero > k)
            {
                // 左边界为 0,窗口内 0 的个数减少
                if(nums[left] == 0)
                {
                    zero--;
                }

                // 左边界右移
                left++;
            }

            // 更新最大长度
            retlen = max(retlen, right - left + 1);

            // 扩大窗口
            right++;
        }

        return retlen;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    int longestOnes(vector<int>& nums, int k)
    {
        int ret = 0; // 记录窗口最大长度

        // 滑动窗口:left为左边界,right为右边界
        // zero记录窗口内0的个数
        for(int left = 0, right = 0, zero = 0; right < nums.size(); right++)
        {
            // 当前元素进入窗口
            if(nums[right] == 0)
                zero++;

            // 窗口不合法,缩小窗口
            while(zero > k)
            {
                // 左边界元素移出窗口
                if(nums[left++] == 0)
                    zero--;
            }

            // 更新最大长度
            ret = max(ret, right - left + 1);
        }

        return ret;
    }
};

4,将 x 减到 0 的最小操作数⭐⭐

题目链接:

将 x 减到 0 的最小操作数

解题思路:

题目要求我们每次从数组的最左边或者最右边删除元素,并让这些被删除元素的和等于 x

直接模拟删除会比较复杂,因此我们可以换个角度思考:

  • 删除左右两边元素的和为 x
  • 等价于:中间保留一段连续子数组,其和为 sum - x

并且:

  • 删除元素个数最少
  • 等价于:保留的子数组长度最长

因此问题就转换成:

求和为 sum - x 的最长连续子数组长度。

由于数组中的元素都为正数,因此可以使用滑动窗口解决。

我们用 leftright 维护窗口,并记录窗口元素和:

  • 当窗口和大于 target 时:

    缩小窗口。

  • 当窗口和等于 target 时:

    更新最长子数组长度。

最后:

  • 操作次数 = 数组总长度 - 最长子数组长度。

由于 leftright 都只会向右移动一次,因此时间复杂度为 O(n)

解题代码

cpp 复制代码
/*
解题思路:

题目要求从数组左右两边删除元素,
使删除元素之和等于 x。

可以转换思路:

删除左右元素之和为 x,
等价于保留中间一段连续子数组,
其和为 sum - x。

因此问题转换成:

求和为 target 的最长连续子数组长度。

最后:
操作次数 = 数组总长度 - 最长子数组长度。

由于数组元素全为正数,
可以使用滑动窗口解决。
*/

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {

        // 计算数组总和
        int sum = 0;
        for(auto it : nums)
        {
            sum += it;
        }

        // 目标窗口和
        int target = sum - x;

        // 总和小于 x,无法完成操作
        if(target < 0)
            return -1;

        // 整个数组都需要删除
        if(target == 0)
            return nums.size();

        // 滑动窗口左右边界
        int left = 0, right = 0;

        // 当前窗口和
        int tmp = 0;

        // 记录最长合法窗口长度
        int len = 0;

        // 滑动窗口
        while(right < nums.size())
        {
            // 当前元素进入窗口
            tmp += nums[right];

            // 窗口和过大,缩小窗口
            while(tmp > target)
            {
                tmp -= nums[left];
                left++;
            }

            // 找到合法窗口
            if(tmp == target)
            {
                len = max(len, right - left + 1);
            }

            // 扩大窗口
            right++;
        }

        // 没有找到合法窗口
        if(len == 0)
            return -1;

        // 最少操作次数
        return nums.size() - len;
    }
};

5,水果成篮⭐⭐⭐

题目链接:

水果成篮

解题思路:

题目要求我们寻找一个最长的连续子数组,并且这个子数组中最多只能包含两种水果。

本质上就是:

求"至多包含两种元素"的最长连续区间。

因此可以使用滑动窗口(双指针)解决。

我们用 leftright 维护当前窗口,并使用哈希表记录窗口中每种水果出现的次数。

由于窗口中最多只能有两种水果:

  • 当窗口内水果种类数 <= 2 时:

    当前窗口合法,可以更新答案。

  • 当窗口内水果种类数 > 2 时:

    当前窗口不合法,需要移动 left 缩小窗口。

在整个过程中,leftright 都只会向右移动一次,因此时间复杂度为 O(n)

解题代码

cpp 复制代码
/*
解题思路:

使用滑动窗口(双指针)维护一个连续区间[left, right]。

1. right 指针不断向右扩展窗口,
   并统计窗口内水果出现次数。

2. 当窗口内水果种类超过 2 种时:
   说明当前窗口不合法,
   需要移动 left 缩小窗口。

3. 当窗口重新合法后,
   更新最长窗口长度。

由于 left 和 right 都只会向右移动一次,
所以时间复杂度为 O(n)。
*/

class Solution {
public:
    int totalFruit(vector<int>& fruits) {

        // 滑动窗口左右边界
        int left = 0, right = 0;

        // 哈希表记录水果出现次数
        unordered_map<int, int> mp;

        // 记录最长合法窗口长度
        int len = 0;

        // 滑动窗口
        while(right < fruits.size())
        {
            // 当前水果进入窗口
            mp[fruits[right]]++;

            // 水果种类超过 2 种,缩小窗口
            while(mp.size() > 2)
            {
                // 左边界水果移出窗口
                mp[fruits[left]]--;

                // 数量为 0,移除该水果
                if(mp[fruits[left]] == 0)
                {
                    mp.erase(fruits[left]);
                }

                // 左边界右移
                left++;
            }

            // 更新最长长度
            len = max(len, right - left + 1);

            // 扩大窗口
            right++;
        }

        return len;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    int totalFruit(vector<int>& f)
    {
        unordered_map<int, int> hash; // 统计窗口内水果数量

        int ret = 0; // 记录最长合法窗口长度

        // 滑动窗口:
        // left 为左边界
        // right 为右边界
        for(int left = 0, right = 0; right < f.size(); right++)
        {
            // 当前水果进入窗口
            hash[f[right]]++;

            // 水果种类超过 2 种,缩小窗口
            while(hash.size() > 2)
            {
                hash[f[left]]--;

                // 当前水果数量为 0,移除
                if(hash[f[left]] == 0)
                {
                    hash.erase(f[left]);
                }

                left++;
            }

            // 更新最长长度
            ret = max(ret, right - left + 1);
        }

        return ret;
    }
};

6,找到字符串中所有字母异位词⭐⭐⭐

题目链接:

找到字符串中所有字母异位词

解题思路:

题目要求我们找到字符串 s 中所有与字符串 p 的字母异位词。

所谓字母异位词,就是:

两个字符串包含的字符种类和数量完全相同,只是顺序不同。

由于我们需要寻找的是:

长度固定为 p.size() 的连续子串。

因此可以使用固定长度的滑动窗口来解决。

我们使用两个哈希数组:

  • hash1:记录字符串 p 中每个字符出现的次数。
  • hash2:记录当前窗口中每个字符出现的次数。

同时使用 count 记录当前窗口中"有效字符"的数量。

滑动窗口过程:

  • 右边界进入窗口时:

    更新字符出现次数。

  • 当窗口长度超过 p.size() 时:

    左边界出窗口,维持窗口长度固定。

  • 当有效字符数量等于 p.size() 时:

    说明当前窗口就是一个字母异位词。

由于每个字符最多进出窗口一次,因此时间复杂度为 O(n)

解题代码

cpp 复制代码
/*
解题思路:

使用固定长度滑动窗口维护长度为 p.size() 的区间。

1. hash1 记录字符串 p 中字符出现次数。

2. hash2 记录当前窗口中字符出现次数。

3. count 记录窗口内有效字符数量。

4. 当窗口长度超过 p.size() 时:
   移动 left 缩小窗口。

5. 当 count == p.size() 时:
   说明当前窗口为字母异位词。

时间复杂度为 O(n)。
*/

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {

        // 记录 p 中字符出现次数
        int hash1[26] = {0};

        for(auto it : p)
        {
            hash1[it - 'a']++;
        }

        // 保存答案
        vector<int> ret;

        // 记录窗口中字符出现次数
        int hash2[26] = {0};

        // count 记录有效字符数量
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            // 当前字符进入窗口
            hash2[s[right] - 'a']++;

            // 当前字符属于有效字符
            if(hash2[s[right] - 'a'] <= hash1[s[right] - 'a'])
            {
                count++;
            }

            // 窗口长度超过 p.size()
            if(right - left + 1 > p.size())
            {
                // 左边界字符属于有效字符
                if(hash2[s[left] - 'a'] <= hash1[s[left] - 'a'])
                {
                    count--;
                }

                // 左边界字符移出窗口
                hash2[s[left] - 'a']--;

                // 缩小窗口
                left++;
            }

            // 当前窗口是字母异位词
            if(count == p.size())
            {
                ret.push_back(left);
            }
        }

        return ret;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    vector<int> findAnagrams(string s, string p)
    {
        // cnt1 记录 p 中字符出现次数
        int cnt1[26] = {0};

        // cnt2 记录窗口中字符出现次数
        int cnt2[26] = {0};

        // 统计 p 中字符频率
        for(char ch : p)
        {
            cnt1[ch - 'a']++;
        }

        vector<int> ret;

        // 固定长度滑动窗口
        for(int left = 0, right = 0; right < s.size(); right++)
        {
            // 当前字符进入窗口
            cnt2[s[right] - 'a']++;

            // 窗口长度超过 p.size()
            if(right - left + 1 > p.size())
            {
                // 左边界字符移出窗口
                cnt2[s[left] - 'a']--;

                left++;
            }

            // 判断当前窗口是否为字母异位词
            if(cnt1 == cnt2)
            {
                ret.push_back(left);
            }
        }

        return ret;
    }
};

7,串联所有单词的子串⭐⭐⭐⭐

题目链接:

串联所有单词的子串

解题思路:

题目要求我们找到:

words 中所有单词按任意顺序拼接形成的子串。

并且:

  • 每个单词长度相同
  • 每个单词必须全部使用一次

因此我们可以使用:在这里插入代码片

"固定步长滑动窗口"

来解决。

假设:

  • 每个单词长度为 n
  • 单词总数为 m

那么合法窗口长度一定为:

m * n

cpp 复制代码
/*
解题思路:

由于所有单词长度相同,
因此可以使用固定步长滑动窗口。

1. 每次窗口移动一个单词长度。

2. mp1 记录 words 中单词出现次数。

3. mp2 记录当前窗口中单词出现次数。

4. count 记录当前窗口中有效单词数量。

5. 当窗口长度超过总长度时:
   移动 left 缩小窗口。

6. 当 count == words.size() 时:
   当前窗口为合法答案。

由于需要处理不同起点,
因此需要枚举 0 ~ n-1 的所有起点。
*/

class Solution {

    // 获取从 j 开始长度为 n 的字符串
    string gets(int j, int n, string s)
    {
        string tmp = "";

        while(n--)
        {
            tmp += s[j++];
        }

        return tmp;
    }

public:
    vector<int> findSubstring(string s, vector<string>& words) {

        // 单词长度
        int n = words[0].size();

        // 统计 words 中单词出现次数
        unordered_map<string, int> mp1;

        for(auto it : words)
        {
            mp1[it]++;
        }

        vector<int> ret;

        // 枚举不同起点
        for(int m = 0; m < n; m++)
        {
            // 当前窗口单词统计
            unordered_map<string, int> mp2;

            // 滑动窗口
            for(int left = m, right = m, count = 0;
                right + n <= s.size();
                right += n)
            {
                // 当前单词进入窗口
                string tmp = s.substr(right, n);

                mp2[tmp]++;

                // 当前单词有效
                if(mp2[tmp] <= mp1[tmp])
                {
                    count++;
                }

                // 窗口长度超过限制
                if((right - left + n) > (words.size() * n))
                {
                    // 左边界单词移出窗口
                    string tmp2 = gets(left, n, s);

                    // 当前单词属于有效单词
                    if(mp2[tmp2] <= mp1[tmp2])
                    {
                        count--;
                    }

                    mp2[tmp2]--;

                    // 数量为 0 时移除
                    if(mp2[tmp2] == 0)
                    {
                        mp2.erase(tmp2);
                    }

                    // 缩小窗口
                    left += n;
                }

                // 找到合法窗口
                if(count == words.size())
                {
                    ret.push_back(left);
                }
            }
        }

        return ret;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution
{
public:
    vector<int> findSubstring(string s, vector<string>& words)
    {
        int n = words[0].size(); // 每个单词长度
        int m = words.size();    // 单词数量

        // 统计 words 中每个单词出现次数
        unordered_map<string, int> target;

        for(auto& word : words)
        {
            target[word]++;
        }

        vector<int> ret;

        // 枚举不同起点
        for(int i = 0; i < n; i++)
        {
            unordered_map<string, int> window;

            // 滑动窗口:
            // left 为左边界
            // right 为右边界
            // count 为有效单词数量
            for(int left = i, right = i, count = 0;
                right + n <= s.size();
                right += n)
            {
                // 当前单词进入窗口
                string word = s.substr(right, n);

                window[word]++;

                // 当前单词有效
                if(window[word] <= target[word])
                {
                    count++;
                }

                // 窗口长度超过要求
                while(right - left + n > m * n)
                {
                    // 左边界单词
                    string del = s.substr(left, n);

                    // 当前单词有效
                    if(window[del] <= target[del])
                    {
                        count--;
                    }

                    // 左边界单词移出窗口
                    window[del]--;

                    if(window[del] == 0)
                    {
                        window.erase(del);
                    }

                    left += n;
                }

                // 找到合法窗口
                if(count == m)
                {
                    ret.push_back(left);
                }
            }
        }

        return ret;
    }
};

下期预告

今天的分享就到这里,下一期我们将一起学习二分算法!!!

结语

本期内容就到这里啦,欢迎大家在评论区一起交流讨论

如果你也在为蓝桥杯/ACM备赛头疼,或是准备算法面试找不到系统学习路径,欢迎订阅我的「算法从入门到精通」专栏

这里没有枯燥的理论堆砌,只有完整的算法学习路线

搭配精选梯度习题+清晰思路解析,帮你把每个算法学透、练熟。包教包会的!

我们一起在算法路上稳步进阶!

相关推荐
兩尛2 小时前
std::shared_mutex、std::mutex和std::recursive_mutex是什么锁
开发语言·c++·算法
WL_Aurora2 小时前
备战蓝桥杯国赛【Day 16】
python·蓝桥杯
kyle~2 小时前
查找---插值查找(二分查找的改进版本)
开发语言·c++
木井巳2 小时前
【递归算法】不同路径Ⅲ
java·算法·leetcode·深度优先
Hunter_pcx3 小时前
ubuntu:内存假泄漏
linux·运维·服务器·开发语言·c++·人工智能·ubuntu
想带你从多云到转晴3 小时前
07、数据结构与算法---优先级队列(堆)与排序
java·数据结构·算法
吃好睡好便好3 小时前
在Matlab中绘制非默认峰值图
开发语言·学习·算法·matlab
Soley3 小时前
自动驾驶C++实时中间件:PuppetMaster 重构记录,阶段三:通信层抽象
c++·自动驾驶
不吃土豆的马铃薯3 小时前
5.SGI STL 二级空间配置器 _S_chunk_alloc核心函数解析
开发语言·c++·vscode·c·内存池