【优选算法篇】编织算法的流动诗篇:滑动窗口的轻盈之美

文章目录

  • C++ 滑动窗口详解:基础题解与思维分析
    • 前言
    • 第一章:热身练习
      • 1.1 长度最小的子数组
        • 解法一(暴力求解)
        • 解法二(滑动窗口)
        • 滑动窗口的核心思想
        • 图解分析
        • 滑动窗口的有效性
        • 时间复杂度分析
        • 易错点提示
      • 1.2 无重复字符的最长子串
        • 解法一(暴力求解)
        • 解法二(滑动窗口)
        • 图解分析
          • 详细说明:
      • 1.3 最大连续 1 的个数 III
        • 解法(滑动窗口)
        • 滑动窗口的核心思想
        • 图解分析
          • 详细说明:
        • 关键点:
      • 1.4 将 x 减到 0 的最小操作数
        • 解法(滑动窗口):
        • 算法流程:
        • 代码实现:
        • 图解分析:
          • 详细说明:
    • 写在最后

C++ 滑动窗口详解:基础题解与思维分析

💬 欢迎讨论:如有疑问或见解,欢迎在评论区留言互动。

👍 点赞、收藏与分享:如觉得这篇文章对您有帮助,请点赞、收藏并分享!
🚀 分享给更多人:欢迎分享给更多对 C++ 感兴趣的朋友,一起学习滑动窗口的基础与进阶!


前言

滑动窗口是一种常用的算法技巧,主要用于处理子数组、子串等具有“窗口”特性的题目。在本篇博客中,我们将通过具体的例题讲解,深入剖析滑动窗口的思想和它的应用场景。滑动窗口法能够在保持高效计算的同时,减少重复的工作,因而在处理某些连续区间问题时,常常是最优解法。


第一章:热身练习

1.1 长度最小的子数组

题目链接:209. 长度最小的子数组
题目描述
给定一个含有 n 个正整数的数组 nums 和一个正整数 target
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0

示例 1

  • 输入:target = 7, nums = [2, 3, 1, 2, 4, 3]
  • 输出:2
  • 解释:子数组 [4, 3] 是该条件下的长度最小的子数组。

示例 2

  • 输入:target = 4, nums = [1, 4, 4]
  • 输出:1

示例 3

  • 输入:target = 11, nums = [1, 1, 1, 1, 1, 1, 1, 1]
  • 输出:0

解法一(暴力求解)

算法思路

通过枚举数组中的所有子数组,计算它们的和,并检查是否大于等于 target,从中找出符合条件的最小子数组。暴力解法虽然简单直接,但在处理大规模数据时效率较低,容易超时。

具体步骤

  1. 枚举数组中的所有子数组。
  2. 计算每个子数组的和。
  3. 如果子数组的和大于等于 target,记录其长度,并在所有子数组中找出最小的长度。

代码实现

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int n = nums.size();int result = INT_MAX;for (int start = 0; start < n; ++start) {int sum = 0;for (int end = start; end < n; ++end) {sum += nums[end];if (sum >= target) {result = min(result, end - start + 1);break;}}}return result == INT_MAX ? 0 : result;}
};

复杂度分析

  • 时间复杂度O(n^2),需要枚举所有可能的子数组。
  • 空间复杂度O(1),仅使用常量级的额外空间。

暴力求解的缺点

  • 对于大数据集(如 10^5 级别),该方法会导致时间超限。我们需要一种更高效的算法来解决这个问题。

解法二(滑动窗口)

算法思路

滑动窗口是一种高效的解决方法,它通过两个指针 leftright,动态调整窗口的大小来找到满足条件的最短子数组。具体过程如下:

  1. 初始化 leftright,从数组左端开始。
  2. right 向右移动,扩大窗口,并计算窗口内元素的和。
  3. 如果窗口内的和 ≥ target,尝试收缩窗口,即将 left 向右移动,尽可能缩小窗口,并更新最小子数组的长度。
  4. 重复上述过程,直到 right 遍历完整个数组。

代码实现

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int left = 0, sum = 0, result = INT_MAX;for (int right = 0; right < nums.size(); ++right) {sum += nums[right];while (sum >= target) {result = min(result, right - left + 1);sum -= nums[left++];}}return result == INT_MAX ? 0 : result;}
};

复杂度分析

  • 时间复杂度O(n),每个元素最多被访问两次(一次是加入窗口,一次是移出窗口)。
  • 空间复杂度O(1),只使用了固定的额外空间。
滑动窗口的核心思想

滑动窗口法通过调整窗口的大小来找到满足条件的最优解。当窗口内元素的和大于等于 target 时,我们尝试缩小窗口,这样可以找到满足条件的最短子数组。

图解分析

假设 target = 7, nums = [2, 3, 1, 2, 4, 3]

  1. 初始状态left = 0, right = 0, sum = 2,窗口未达到 target
  2. 扩大窗口:将 right 向右移动,直到窗口和 sum = 9,满足条件。
  3. 缩小窗口:移动 left,将子数组 [4, 3] 缩小到长度 2

步骤图解

IterationLeftRightSumSubarrayResult
1038[2, 3, 1, 2]4
2136[3, 1, 2]4
31410[3, 1, 2, 4]4
4247[1, 2, 4]3
5346[2, 4]3
6359[2, 4, 3]3
7457[4, 3]2

图解说明

  1. Iteration 1:从左端 left = 0,右端扩展到 right = 3,子数组 [2, 3, 1, 2] 的和为 8,大于 target,记录最小长度为 4
  2. Iteration 2:缩小窗口,将 left 右移到 1,新窗口和为 6,小于 target,不更新结果。
  3. Iteration 3:右端扩展到 4,子数组和为 10,更新最小长度为 4
  4. Iteration 4:继续缩小窗口,将 left 右移到 2,子数组 [1, 2, 4] 的和为 7,更新最小长度为 3
  5. Iteration 5left 再次右移,和降到 6,小于 target,不更新结果。
  6. Iteration 6right 扩展到 5,子数组 [2, 4, 3] 的和为 9,不更新最小长度。
  7. Iteration 7:最后,left 移到 4,子数组 [4, 3] 的和为 7,最终更新最小长度为 2
滑动窗口的有效性

滑动窗口是一种高效的算法思想,特别适用于处理子数组和子串等问题。下面详细解释其原理以及为何时间复杂度较低:

  1. 滑动窗口的核心思想
    滑动窗口寻找的是:以当前窗口最左侧元素(记为 left1)为基准,找出从 left1 开始满足条件的区间,也就是使得子数组的和 sum 大于等于 target 时的最右侧(记为 right1)。在这道题中,当 sum >= target 时,说明从 left1right1 的子数组满足条件,并且我们可以记录这个窗口的长度。

  2. 避免重复计算
    当我们已经找到从 left1 开始的最优区间后,left1 就可以被舍弃。此时如果继续像暴力解法那样重新从 left2 开始计算后续的子数组和,会导致大量重复的计算。因为从 left1right1 的区间中,许多元素的和已经被计算过了,我们可以充分利用这个已有的信息。

  3. 优化窗口的移动
    此时,right1 的作用就显现出来了。通过滑动窗口,我们不需要重新计算新的区间,而是将 left1 从窗口内移除(即从 sum 中减去 left1 对应的值)。然后,我们直接从 right1 开始,继续向右扩展 right 来寻找下一个满足条件的区间(即 left2 开始的最短区间)。这样,当 sum >= target 的条件不再满足时,我们再次缩小窗口,从而达到寻找最优解的目的。

  4. 高效性
    滑动窗口法使得我们可以避免重新从头计算每个子数组的和。每当一个区间满足条件时,我们就可以通过缩小窗口来检查是否有更短的区间可以满足条件。通过这种滑动的方式,我们不仅能解决问题,还大幅减少了重复计算,从而提高了算法效率。

核心就是:leftright指针不会回退!!!也称为同向指针


时间复杂度分析

滑动窗口虽然看起来有两层循环(外层控制 right 的扩展,内层控制 left 的缩小),但实际上每个指针(leftright)最多只会遍历数组 n 次。

  • right 指针:从左向右遍历整个数组,每个元素最多只被访问一次。
  • left 指针:每当 sum >= target 时,left 会右移以缩小窗口,每个元素也最多只会被访问一次。

因此,leftright 指针都是单调递增的,不会回退,二者在整个过程中最多各自移动 n 次,最终的时间复杂度为 O(n)

易错点提示
  1. 窗口的收缩条件:当窗口内的和 ≥ target 时,我们才能缩小窗口。
  2. 窗口最小长度的更新:每次缩小窗口时,都要更新最小长度,否则可能遗漏最优解。

1.2 无重复字符的最长子串

题目链接:3. 无重复字符的最长子串
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。

示例 1

  • 输入:s = "abcabcbb"
  • 输出:3
  • 解释:因为无重复字符的最长子串是 "abc",所以其长度为 3

示例 2

  • 输入:s = "bbbbb"
  • 输出:1
  • 解释:因为无重复字符的最长子串是 "b",所以其长度为 1

示例 3

  • 输入:s = "pwwkew"
  • 输出:3
  • 解释:因为无重复字符的最长子串是 "wke",所以其长度为 3
    请注意,答案必须是 子串 的长度,"pwke" 是一个 子序列,不是子串。

提示

  • 0 <= s.length <= 5 * 10^4
  • s 由英文字母、数字、符号和空格组成

解法一(暴力求解)

算法思路

通过枚举从每个位置开始,向后寻找无重复字符的子串能到达的位置,记录其中最长的子串长度即可。在寻找无重复子串时,可以使用哈希表统计字符出现的频次,遇到重复字符时停止。

具体步骤

  1. 枚举每个起始位置 i,从该位置开始寻找最长的无重复子串。
  2. 使用哈希表统计字符出现的频次,一旦出现重复字符,终止当前枚举。
  3. 更新最长无重复子串的长度。
  4. 返回结果。

代码实现

class Solution {
public:int lengthOfLongestSubstring(string s) {int ret = 0;  // 记录结果int n = s.length();// 枚举从不同位置开始的无重复子串for (int i = 0; i < n; i++) {int hash[128] = { 0 };  // 记录字符频次for (int j = i; j < n; j++) {hash[s[j]]++;  // 统计字符频次if (hash[s[j]] > 1)  // 出现重复,终止break;ret = max(ret, j - i + 1);  // 更新结果}}return ret;}
};

复杂度分析

  • 时间复杂度O(n^2),枚举每个起点,并从该起点向后查找无重复字符的子串。
  • 空间复杂度O(1),只需常量空间存储字符频次。

缺点

  • 对于大数据集(如 10^5 级别),该算法会超时,因此需要一种更高效的解法。

解法二(滑动窗口)

算法思路

滑动窗口是一种高效解决子串问题的方式。使用滑动窗口法时,维持一个窗口,使得窗口内的所有字符都是不重复的。当窗口右端进入新字符时,更新哈希表记录字符频次:

  • 如果字符频次大于 1,则窗口内出现了重复字符,开始从左侧缩小窗口,直到频次恢复为 1
  • 如果没有重复字符,直接更新最长无重复子串的长度。

代码实现

class Solution {
public:int lengthOfLongestSubstring(string s) {int hash[128] = { 0 };  // 使用数组模拟哈希表int left = 0, right = 0, n = s.size();int ret = 0;  // 记录结果while (right < n) {hash[s[right]]++;  // 将右端字符加入窗口// 如果窗口内出现重复字符,移动左端,直到没有重复while (hash[s[right]] > 1) {hash[s[left++]]--;}// 更新最长子串的长度ret = max(ret, right - left + 1);right++;  // 移动右端}return ret;}
};

复杂度分析

  • 时间复杂度O(n),每个字符最多被左右指针访问两次,因此时间复杂度为线性。
  • 空间复杂度O(1),只需常量空间存储字符频次。
图解分析

假设 s = "abcabcbb",滑动窗口的执行过程如下:

IterationLeftRightHash TableSubstringLength (Result)
100a:1“a”1
201a:1, b:1“ab”2
302a:1, b:1, c:1“abc”3
403a:2, b:1, c:1 → 左移 left=1“bca”3
514b:2, c:1, a:1 → 左移 left=2“cab”3
625b:1, c:2, a:1 → 左移 left=3“abc”3
736b:2, c:1 → 左移 left=5“cb”3
857b:2 → 左移 left=6“b”3
详细说明:
  1. Iteration 1

    • Left=0Right=0,加入字符 a,哈希表为 a:1,当前子串为 "a",长度为 1
  2. Iteration 2

    • Right=1,加入字符 b,哈希表为 a:1, b:1,当前子串为 "ab",长度为 2
  3. Iteration 3

    • Right=2,加入字符 c,哈希表为 a:1, b:1, c:1,当前子串为 "abc",长度为 3
  4. Iteration 4

    • Right=3,加入字符 a,哈希表更新为 a:2, b:1, c:1。由于字符 a 出现两次,移动 Left 指针到 1,此时子串变为 "bca",长度仍然是 3
  5. Iteration 5

    • Right=4,加入字符 b,哈希表更新为 b:2, c:1, a:1。由于字符 b 出现两次,移动 Left2,子串变为 "cab",长度保持为 3
  6. Iteration 6

    • Right=5,加入字符 c,哈希表更新为 b:1, c:2, a:1。此时字符 c 出现两次,需要再次移动 Left,将 Left 移到 3,此时子串变为 "abc",长度为 3
  7. Iteration 7

    • Right=6,加入字符 b,哈希表更新为 b:2, c:1。由于 b 出现两次,移动 Leftleft=5,此时子串应为 "cb",长度为 2
  8. Iteration 8

    • Right=7,继续加入字符 b,哈希表更新为 b:2,再次出现重复字符,移动 Left6,子串为 "b",长度为 2

1.3 最大连续 1 的个数 III

题目链接:1004. 最大连续 1 的个数 III
题目描述
给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0,则返回数组中 连续 1 的最大个数。

示例 1

  • 输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2

  • 输出:6

  • 解释:翻转红色标记的 0 变为 1,最长的子数组长度为 6

    [1,1,1,0,0,1,1,1,1,1]

示例 2

  • 输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3

  • 输出:10

  • 解释:翻转红色标记的 0 变为 1,最长的子数组长度为 10

    [0,0,1,1,1,1,1,1,1,1,1,1]


解法(滑动窗口)

算法思路
这道题可以简化为求解一段 连续的 1 中包含最多 k 个 0 的最长子数组。由于这个子数组是 连续区间,因此可以使用滑动窗口来解决。

具体步骤

  1. 初始化左右指针 leftright,以及记录窗口内 0 的个数的变量 zero
  2. right 指针向右扩展时:
    • 如果当前元素是 0,增加 zero 计数。
    • 如果 zero 超过了 k,需要通过移动 left 指针来移出窗口内的 0,直到 zero 恢复到不超过 k 的状态。
  3. 在窗口合法时,更新最大长度 ret
  4. 循环结束后,ret 保存的即为最大连续 1 的长度。

代码实现

class Solution {
public:int longestOnes(vector<int>& nums, int k) {int ret = 0;for (int left = 0, right = 0, zero = 0; right < nums.size(); right++) {if (nums[right] == 0) zero++;  // 窗口内增加一个 0// 如果 0 的数量超过 k,移动 left 指针while (zero > k) {if (nums[left++] == 0) zero--;  // 窗口左边界收缩,减少一个 0}// 更新最大长度ret = max(ret, right - left + 1);}return ret;}
};

复杂度分析

  • 时间复杂度O(n),每个元素最多被左右指针访问两次(一次进入窗口,一次被移出窗口)。
  • 空间复杂度O(1),只使用了几个固定的额外变量存储当前状态。
滑动窗口的核心思想

滑动窗口方法通过不断扩展和收缩窗口来保证窗口内的 0 不超过 k。当窗口内的 0 超过 k 时,移动左边界 left,保持窗口内的 0 不超过 k。在每次移动时,记录下窗口的最大长度。

图解分析

假设 nums = [1,1,1,0,0,0,1,1,1,1,0]K=2,以下是滑动窗口的执行过程:


步骤图解

IterationLeftRightZero CountSubarrayLength (Result)
1000[1]1
2010[1, 1]2
3020[1, 1, 1]3
4031[1, 1, 1, 0]4
5042[1, 1, 1, 0, 0]5
6053[1, 1, 1, 0, 0, 0]5
7452[0, 0]5
8462[0, 0, 1]5
9472[0, 0, 1, 1]5
10482[0, 0, 1, 1, 1]5
11492[0, 0, 1, 1, 1, 1]6
124103[0, 0, 1, 1, 1, 1, 0]6
135102[0, 1, 1, 1, 1, 0]6

详细说明:
  1. Iteration 1-3:从 Right=0Right=2,我们持续遇到 1,所以窗口扩展,Zero Count 仍为 0,子数组 [1,1,1] 长度逐渐增加到 3

  2. Iteration 4Right=3,遇到一个 0Zero Count=1,但还在 k=2 的允许范围内,子数组 [1,1,1,0] 长度为 4

  3. Iteration 5Right=4,再遇到一个 0Zero Count=2,此时子数组 [1,1,1,0,0] 满足条件,长度增加到 5

  4. Iteration 6Right=5,再遇到一个 0Zero Count=3,超出 k=2 的限制。因此需要缩小窗口,Left 开始向右移动。最终 Left 移动到 4,窗口变为 [0, 0]Zero Count 恢复到 2,子数组长度保持为 5

  5. Iteration 7-10Right 不断扩展,子数组逐渐变为 [0,0,1,1,1],虽然 Zero Count 始终为 2,但最大长度仍为 5

  6. Iteration 11Right=9,加入一个 1,窗口变为 [0,0,1,1,1,1],满足 Zero Count=2,子数组长度增加到 6

  7. Iteration 12-13Right=10,遇到一个 0Zero Count=3,再次超过限制,因此移动 Left,直到 Left=5,窗口变为 [0, 1, 1, 1, 1, 0],最大长度仍为 6


关键点:
  • 每次当 0 的数量超过 k 时,我们通过移动 left 指针来缩小窗口,直到窗口内的 0 的数量不超过 k。当窗口合法时,不断更新最长子数组的长度。

1.4 将 x 减到 0 的最小操作数

题目链接:1658. 将 x 减到 0 的最小操作数

题目描述
给你一个整数数组 nums 和一个整数 x。每次操作时,你可以移除数组 nums 的最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,你需要修改数组以供接下来的操作使用。如果可以将 x 恰好减到 0,返回最少的操作数;否则,返回 -1

示例 1

  • 输入:nums = [1,1,4,2,3], x = 5
  • 输出:2
  • 解释:最佳解决方案是移除数组末尾的两个元素 [2,3],将 x 减为 0

示例 2

  • 输入:nums = [5,6,7,8,9], x = 4
  • 输出:-1
  • 解释:无法将 x 减到 0

示例 3

  • 输入:nums = [3,2,20,1,1,3], x = 10
  • 输出:5
  • 解释:最佳解决方案是移除前三个元素 [3,2,20] 和后两个元素 [1,3],共计 5 次操作。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104
  • 1 <= x <= 109

解法(滑动窗口):

算法思路

  • 题目要求找到移除的最少操作数,使得 x被减为0。实际上,这可以转换为在数组中找到和为 sum(nums) - x 的最长子数组,剩下的部分就是需要移除的最小操作数。
  • 问题本质是找到和为 target = sum(nums) - x 的最长连续子数组,然后通过滑动窗口进行求解。
算法流程:
  1. 计算目标和:先计算数组的总和 sum,然后求出 target = sum - x。如果 target < 0,直接返回 -1
  2. 滑动窗口初始化:使用两个指针 leftright 来控制窗口,并通过维护一个窗口的和 sum 来查找目标值。
  3. 滑动窗口操作
    • 扩展窗口时,右移 right 指针,增加窗口的和。
    • 收缩窗口时,左移 left 指针,减小窗口的和。
    • 当窗口和等于 target 时,更新最长子数组的长度。
  4. 返回结果:如果找到满足条件的最长子数组,返回 nums.size() - maxLen;否则返回 -1

代码实现:
class Solution {
public:int minOperations(vector<int>& nums, int x) {// 1. 计算数组总和和目标和int sum = 0;for (int a : nums) sum += a;int target = sum - x;if (target < 0) return -1;// 2. 滑动窗口查找和为 target 的最长子数组int ret = -1;for (int left = 0, right = 0, tmp = 0; right < nums.size(); right++) {tmp += nums[right]; // 扩展窗口while (tmp > target) tmp -= nums[left++]; // 收缩窗口if (tmp == target) ret = max(ret, right - left + 1); // 更新最大长度}// 3. 返回结果:数组总长度减去最长子数组长度return ret == -1 ? -1 : nums.size() - ret;}
};

图解分析:

假设 nums = [1,1,4,2,3]x = 5,即目标是找到和为 sum(nums) - x = 11 - 5 = 6 的最长子数组。

步骤图解

IterationLeftRightWindow SumSubarrayMax Length (Result)
1001[1]-1
2012[1, 1]-1
3026[1, 1, 4]3
4038[1, 1, 4, 2]3
5137[1, 4, 2]3
6236[4, 2]3
7249[4, 2, 3]3
8345[2, 3]3

详细说明:
  1. Iteration 1Right=0,加入元素 1,窗口和为 1,还没达到目标和 6,最大长度保持 -1
  2. Iteration 2Right=1,加入元素 1,窗口和为 2,还没达到目标和,最大长度保持 -1
  3. Iteration 3Right=2,加入元素 4,窗口和为 6,正好达到了目标和 6,最大子数组长度更新为 3
  4. Iteration 4Right=3,加入元素 2,窗口和为 8,超出目标和 6,开始移动 left 指针缩小窗口。
  5. Iteration 5Left=1,去掉左侧元素 1,窗口和为 7,仍然超出目标和,继续移动 left
  6. Iteration 6Left=2,去掉左侧元素 1,窗口和为 6,再次达到了目标和,但最大子数组长度仍然为 3
  7. Iteration 7Right=4,加入元素 3,窗口和为 9,超过目标和,继续移动 left
  8. Iteration 8Left=3,去掉左侧元素 4,窗口和为 5,窗口不再满足条件,结束循环。

写在最后

在这篇文章中,我们详细介绍了滑动窗口这一高效的算法技巧,并通过四道经典题目逐步剖析了它的核心思想和实际应用。在长度最小的子数组、无重复字符的最长子串、最大连续 1 的个数 III 以及将 x 减到 0 的最小操作数等问题中,滑动窗口均展现了其强大的解决区间问题的能力。

通过这篇文章,读者不仅可以掌握滑动窗口的基础原理,还能够通过具体的题目理解如何灵活运用滑动窗口解决实际的算法问题。从优化时间复杂度到减少重复计算,滑动窗口无疑是处理连续区间问题的强力工具。希望大家在理解并掌握这些基础应用后,能够在更复杂的场景中灵活应用这一技巧。

我们将在下一篇文章中深入探讨滑动窗口的进阶应用,包括处理更复杂的数据结构、动态窗口调整等内容,进一步提升大家在算法优化中的实战能力。


以上就是关于【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/450081.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于webrtc实现音视频通信

与传统通信方式不同&#xff0c;p2p通信的实现过程不依赖于中间服务器的信息收发&#xff0c;直接通过信令等完成通信过程的建立&#xff1b; 通过websocket实现信令服务器的建立&#xff0c;而通过信令来确定通信双方&#xff1b; webrtc通过 sdp协议来完善通信双方间协议的…

塞班和诺基亚(中古手机图,你见过哪个?)

诺基亚的塞班系统&#xff0c;是比较早和强大的移动操作系统了。当时还有Palm&#xff0c;微软的平台&#xff0c;但市占率都很低。 安卓从被谷歌收购那天&#xff0c;每个特性都预示着&#xff0c;未来一定会超越塞班。而塞班后来取消了生态&#xff0c;自己来使用&#xff0c…

mac上docker desktop 服务指南

容器化技术是指将软件代码与运行此代码所需的操作系统 (OS) 库和依赖项进行集体打包&#xff0c;以便创建可在任意基础设施上一致运行的单个轻量级可执行文件&#xff08;称为容器&#xff09;&#xff0c;比物理机部署具备更好的可移植性和维护性&#xff0c;比虚拟机具有更高…

基于Spring Boot+Vue的医疗健康的便民服务平台系统的设计与实现(协同过滤算法、实时聊天)

&#x1f388;系统亮点&#xff1a;协同过滤算法、实时聊天&#xff1b; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框架…

atcoder abc375

A seats 代码&#xff1a; #include <bits/stdc.h> using namespace std;int main() {int n;cin >> n;vector<char> a(n 1);for(int i 1; i < n; i ) cin >> a[i];int cnt 0;for(int i 1; i < n - 2; i ) {if(a[i] # && a[i 1…

【spring ai】java 实现RAG检索增强,超快速入门

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;一个常见的问题是模型会产生幻觉&#xff08;即生成的内容与事实不符&#xff09;&#xff0c;同时由于缺乏企业内部数据的支持&#xff0c;导致其回答往往不够精准和具体&#xff0c;偏向于泛泛而谈。这些问题…

STM32 实现 TCP 服务器与多个设备通信

目录 一、引言 二、硬件准备 三、软件准备 四、LWIP 协议栈的配置与初始化 五、创建 TCP 服务器 1.创建 TCP 控制块 2.绑定端口 3. 进入监听状态 4.设置接收回调函数 六、处理多个客户端连接 七、数据处理与通信管理 八、错误处理与资源管理 九、总结 一、引…

【C++】:工厂模式

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 简单工厂模什么是简单工厂模式&#xff1f;如何实现简单工厂模式&#xff1f; 工厂方法抽象工厂模式总结简单工厂模式工厂方法抽象工厂「Abstract Factory」 简单工厂模 什么是简单工厂模式&#xf…

ps提示不能使用移动工具,因为目标通道被隐藏的解决办法

解决&#xff1a;按F7&#xff0c;或者从窗口把图层打开 按图示找到快速蒙版图层。它可能被隐藏或以特殊图标显示。右键删除或者拖到右下角垃圾桶里

小猿口算炸鱼脚本

目录 写在前面&#xff1a; 一、关于小猿口算&#xff1a; 二、代码逻辑 1.数字识别 2.答题部分 三、代码分享&#xff1a; 补充&#xff1a;软件包下载 写在前面&#xff1a; 最近小猿口算已经被不少大学生攻占&#xff0c;小学生直呼有挂。原本是以为大学生都打着本…

从融资烧钱到商业落地:中国AI大模型步入「实战期」

在AI还尚且未达到生产力工具的时候&#xff0c;没人能知道怎样的基础模型会是尽头&#xff0c;以及对付费客户而言&#xff0c;他们如何才能将这笔投入转化为真实营收。 而对于大模型究竟什么能盈利&#xff0c;目前国内的任何一家都未表过态。或者说&#xff0c;这不是一个当…

从0开始深度学习(11)——多层感知机

前面介绍了线性神经网络&#xff0c;但是线性模型是有可能出错的&#xff0c;因为线性模型意味着是单调假设&#xff0c;但是现实中往往很复杂。例如&#xff0c;我们想要根据体温预测死亡率。 对体温高于37摄氏度的人来说&#xff0c;温度越高风险越大。 然而&#xff0c;对体…

MySQL插入优化-性能对比

插入优化主要包括&#xff1a; 批量插入条数据&#xff0c;而不是单个记录逐条插入。手动提交事务&#xff0c;避免自动提交事务带来的额外开销。使用load命令从本地文件导入。 性能对比 创建数据库表 CREATE TABLE if not exists tb_sku ( id int(20) …

facefusion,使用CPU实现一键图片、视频换脸,无需显卡,无限时长(附下载即用的整合包)

FaceFusion 是一种利用人工智能技术将两张或多张人脸融合在一起的图像处理技术。这种技术通过面部特征的识别和重构&#xff0c;将不同的人脸混合成一张新的脸&#xff0c;生成的图像看起来像是这些人脸的融合体。 FaceFusion使用深度学习算法&#xff0c;来捕捉人脸的细节和特…

OpenCV答题卡识别

文章目录 一、基本流程二、代码实现1.定义函数2.图像预处理&#xff08;1&#xff09;高斯模糊、边缘检测&#xff08;2&#xff09;轮廓检测&#xff08;3&#xff09;透视变换&#xff08;4&#xff09;阈值处理和轮廓检测 3.筛选和排序选项轮廓4.判断答案5.显示结果 三、总结…

YOLOv11改进有效系列目录 - 包含卷积、主干、检测头、注意力机制、Neck上百种创新机制 - 针对多尺度、小目标、遮挡、恶劣天气等问题

目标检测作为计算机视觉领域的一项核心任务&#xff0c;极大地推动了整个领域的发展。它不仅是其他许多视觉任务的基础工具&#xff0c;还在学术研究和实际应用之间架起了一座桥梁。目标检测的主要任务是识别和定位图像或视频中的特定对象&#xff0c;通常需要模型能同时处理多…

C++ | Leetcode C++题解之第476题数字的补数

题目&#xff1a; 题解&#xff1a; class Solution { public:int findComplement(int num) {int highbit 0;for (int i 1; i < 30; i) {if (num > (1 << i)) {highbit i;}else {break;} }int mask (highbit 30 ? 0x7fffffff : (1 << (hig…

【DBA Part01】国产Linux上安装Oracle进行数据迁移

内容如下&#xff1a; 1.1.生产环境RHEL/OEL Linux8Oracle11gR2安装配置 1.2.国产麒麟操作系统Oracle11gR2安装配置 1.3.国产麒麟操作系统Oracle11gR2 RAC集群安装配置 1.4.Oracle11gR2迁移到国产麒麟操作系统&#xff08;单机/RAC&#xff09; 本阶段课程项目需求说明&am…

零一万物 Yi-Lightning:超越 GPT-4o 冲击全球榜单;阿里国际 Marco 翻译大模型发布丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。 我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 …

CEEMDAN +组合预测模型(Transformer - BiLSTM + ARIMA)

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享&#xff01; EMD、EEMD、FEEMD、CEEMD、CEEMDAN的区别、原理和Python实现&#xff08;一&#xff09;EMD-CSDN博客 EMD、EEM…