青训营 滑动窗口一般解法及具体题目详解 | 豆包MarsCode AI刷题

148 阅读5分钟

青训营 滑动窗口一般解法及具体题目详解| 豆包MarsCode AI刷题

案例引入

今日份AI刷题过程中,遇见一道需要使用滑动窗口解决的题目。因此决定以此题为跳板,归纳一般滑动窗口解法。本题采用go语言进行解题。

题目描述

image-20241110131249066.png

题目初步分析:

在本题中,小U需要将红包分为三个部分,并且要求第一部分和第三部分相当,以此求得第一部分的最大值。考虑三个部分的长度以及内容互相影响,并且每个部分均为整个数组的连续子数组,因此这道题考虑用滑动窗口来进行解决。

题目详解

  1. 首先考虑所需维护窗口边界:

    本道题目所需要维护的为一排红包的三个部分,但是因为三个部分互不相交并且补集为所有红包,同时需要满足第一部分和第二部分相同。因此优化为只需要考虑中间部分为窗口。从左窗口滑出的即为第一部分红包,从右红包滑出即为第三部分红包。

  2. 初始化窗口:

    经过第一步分析,本题主要考虑从原有窗口滑出的元素之和是否相等。因此初始化窗口应该是包含整个红包数组。即左边界为0,右边界为len(redpacks)-1。同时因为此时还没有元素滑出,因此第一部分第三部分红包总额均为0。

    left, right := 0, len(redpacks)-1//窗口初始化为整个数组
    sumA, sumB := 0, 0//A为第一部分总额,B为第三部分窗口
    
    
  3. 更新并维护窗口:

    1. 收缩左窗口

      因为本题初始化为整体数组,因此先进行收缩窗口。在本题解中,优先收缩左窗口,每次更新左窗口后将滑出元素加入第一部分中,再由第一部分金额来决定右窗口如何移动。

      for left <= right {
      		sumA += redpacks[left]//将左窗口元素加入第一部分中
      		left++//更新左窗口边界
      	}
      
    2. 收缩右窗口:

      初始化时第三部分红包数额为零,因此左窗口滑出元素(即第一部分添加金额)时,第三部分金额会小于第一部分金额。因此为了满足第一部分与第三部分金额相等,则在保持左窗口不更新的同时不断滑出右窗口元素使第三部分金额尽可能与第一部分相等,若第三部分大于第一部分则停止滑出,同时更新左窗口。

      //只要第一部分大于第三部分则不断从右窗口滑出元素
      for sumA > sumB && left <= right {
      			sumB += redpacks[right]//将右窗口元素加入第三部分中
      			right--//更新右窗口
      			if sumA == sumB {
      				mx = sumB
      			}//如果相等则更新最大值
      		}
      

不断重复以上过程直到窗口内元素为零, 即可遍历所有情况同时得出结果。完整代码如下:

func solution(redpacks []int) int {
	// Please write your code here
	mx := 0
	left, right := 0, len(redpacks)-1
	sumA, sumB := 0, 0

	for left <= right {
		sumA += redpacks[left]
		left++

		if sumA == sumB {
			mx = sumB
		}
		for sumA > sumB && left <= right {
			sumB += redpacks[right]
			right--
			if sumA == sumB {
				mx = sumB
			}
		}

	}

	return mx
}

MarsCode AI转化语言

因为此时判题不支持go语言,因此借助豆包AI来将代码转化为java语言。

 public static int solution(List<Integer> redpacks) {
        int mx = 0;
        int left = 0;
        int right = redpacks.size() - 1;
        int sumA = 0;
        int sumB = 0;

        while (left <= right) {
            sumA += redpacks.get(left);
            left++;

            if (sumA == sumB) {
                mx = sumB;
            }
            while (sumA > sumB && left <= right) {
                sumB += redpacks.get(right);
                right--;
                if (sumA == sumB) {
                    mx = sumB;
                }
            }
        }

        return mx;
    }

整体逻辑未有太大变化,且运行结果如下:

image-20241110133714515.png

一般解法归纳

通过本题归纳以及豆包AI总结,将滑动窗口一般解法总结如下:

滑动窗口定义:

滑动窗口(Sliding Window)是一种常用的算法技巧,通常用于解决数组或字符串中的子数组或子字符串问题。滑动窗口算法的核心思想是通过维护一个窗口来减少重复计算,从而提高算法的效率。

滑动窗口适用场景:

  • 找到满足某个条件的最大或最小子数组或子字符串。
  • 计算子数组或子字符串的和、平均值等。
  • 解决与连续子数组或子字符串相关的问题。

滑动窗口一般解法步骤:

  1. 初始化窗口
    • 定义两个指针,通常称为leftright,它们分别表示窗口的左边界和右边界。
    • 初始化这两个指针,使其指向数组的起始位置。
  2. 扩展窗口
    • 通过移动right指针来扩展窗口,直到满足某个条件为止。
    • 在扩展窗口的过程中,更新相关的中间结果(如窗口内的元素和、最大值、最小值等)。
  3. 收缩窗口
    • 当窗口满足某个条件后,通过移动left指针来收缩窗口,直到窗口不再满足条件为止。
    • 在收缩窗口的过程中,更新相关的中间结果。
  4. 更新结果
    • 在每次扩展或收缩窗口后,根据当前窗口的状态更新最终结果。
  5. 重复步骤2-4
    • 继续扩展和收缩窗口,直到遍历完整个数组或字符串。

总结与思考

本题相较一般滑动窗口题目略有不同,一般滑动窗口初始为空不断加入元素,同时根据限定条件来更新;本题初始化为整个数组,不断收缩窗口边界来进行解题。两者本质相同但思路略有差异。同时在解答本题时借助豆包AI来进行语言的转变极为方便,并且归纳的解法也较为正确。