LeetCode 滑动窗口 (周分析)

滑动窗口 (周分析)

Date Created: Feb 4, 2021 9:50 AM
Status: 要学习的

公平的糖果棒交换

爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。

因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)

返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。

如果有多个答案,你可以返回其中任何一个。保证答案存在。

//设定x,y 进行求第一个解
class Solution{
		public int[] fairCandySwap(int[] A,int[] B){
				int sumA = 0;
				int sumB = 0;
				for(int one:A){
						sumA+=one;
				}
				for(int one:B){
						sumB+=one;
				}
				for(int i = 0;i<A.length;i++){
						for(int j = 0;j<B.length;j++){
								if(sumA-A[i]+B[j] == sumB-B[j]+A[i]){
										return new int[]{A[i],B[j]};
								}
						}
				}
				return new int[];
		}
}

替换后的最长重复字符

给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。

注意:字符串长度 和 k 不会超过 10 的4次方。

//首先分析题目要求
// 1. 仅由大写英文字母组成,可以想到哈希表 用来存储和分析组成成分
// 2. 最多可以替换k次 
// 3. 最长字串 说明了是连续的内容,可以使用滑动窗口的方式 解决这个问题。因为控制滑动窗口的移动,
// 可以保证连续性

// 来分析解题思路,首先要确认的是 如何在使用最小的转换次数内保证重复的字母最多呢?
// 应该是 修改除了出现次数最多的 哪个字母来进行。
// 但是题目要求必须是连续的 应该如何控制呢?所以只需要保证同时满足 两个条件就可以 保证
// 当前滑动窗口内的字母出现次数最多 

// 滑动窗口的编写注意事项有
// 1. 确定滑动窗口 在哪个 字符串(可是数组也可以是其他的 链表之类的)上进行左右滑动
// 2. 确定起始位置 这里面的起始位置 包括 left(一般是0 和被动运动) 和 right(一般为最小长度
// ,主动向单一方向移动)。
// 3. 确定left 是如何移动,这点很重要。
class Solution{
		public int characterReplacement(String s,int k){
				if(s.length()<2){
						return s.length();
				}
				int length = s.length();
				int[] record = new int[26];
				char[] charsLength = s.CharArray();
				int lengthMax = 0;
				int right = 0;
				int left = 0;
				while(right <length){
					record(charsLength[right]-'A')++;
					lengthMax = Math.max(lengthMax,recrd(charsLength[right]-'A'));
					right++;
					if(right-left>lengthMax+k){
							record[charsLength[left] - 'A']--;
							left++;
					}
				}
				return right- left;
		}
}


滑动窗口中位数

中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。

例如:

[2,3,4],中位数是 3
[2,3],中位数是 (2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。

// 从题目上理解就是简单的滑动窗口的方式解决 问题
// 只不过在每一次滑动窗口 中需要进行一次中位数的计算并且放入指定数组中
// 由于是指定数组 需要提前预估数组的大小 大小应该为滑动窗口长度-k+1 (长度的判断可以使用极限值的
// 方式)
// 还需要注意的是保留小数的处理
class Solution{
		public double[] medianSlidingWindow(int[] nums, int k){
				double[] result = new double[nums.length-k+1];
				int right = k-1;
				int left = 0;
				int length = nums.length;
				while(right < length){
						ArrayList<Integer> list = new ArrayList<>();
						for(int i = left;i <=right){
							list.add(nums[i]);
						}
						list.sort(Comparator.comparingInt(a->a));
						if(list.size()%2 == 0){
							result[left] = (Double.valueOf(list.get(list.size()/2-1) + Double.valueOf(list.
get(list.size()/2))))/2d;
						}else{
							result[left] = list.get(list.size()/2);
						}
						right++;
						left++;
				}
				return result;
		}
}

// 此方法会效率不高
// 建议使用第二种方式 就是使用大根堆和小根堆 以及延迟删除的方式解决这个问题
// 我们思考中位数的性质:如果一个数是中位数,那么在这个数组中,大于中位数的数目和小于中位数的
// 数目,要么相等,要么就相差一。
// 因此,我们采用对顶堆的做法,控制所有小于等于中位数的数组放到一个堆中,控制所有比中位数大的数字
// 放到另一个堆中,并且保证两个堆的数目相差小于等于1,这样就可以保证每一次查询中位数的时候,答案
// 一定出于两个堆的堆顶元素之一。
// 因此选定数据结构:优先队列。因为优先队列采用的是堆结构。正好符合我们的需求。我们将所有小于等于
// 中位数的元素放到small堆中(是一个大顶堆),将所有大于中位数的元素都放到big堆中(是一个小顶堆)。

子数组最大平均数I

给定n个整数,找出平均数最大且长度为k的连续子数组,并输出该最大平均数。

// 先说下解题思路,首先看到连续子数组 第一个反应就是滑动窗口,
// 接下来在看平均数 这里联想到滑动窗口的移动轨迹,可以初步想出 当到临界点时可以使用添加right
// 和删除left 时,直接拿avg + (A[right]-A[left] )/k 计算平均值。 减少重复循环的时间浪费。
class Solution{
		boolean flagOne = true;
		double sumOne = 0d;
		public double findMaxAverage(int[] nums,int k){
				double result = 0d;
				int right = k;
				boolean flag = true;
				int length = nums.length;
				int left = 0;
				while(right<=length){
					if(flag){
							result = getAVG(nums,right,left);
							flag = false;
					}else{
							result = Math.max(result,getAVG(nums,right,left));
					}
					right++;
					left++;
				}
				return result;
		}
		private double getAVG(int[] nums,int right,int left){
				if(flagOne){
					for(int i=left;i<right;i++){
						sumOne += nums[i];
					}
					flagOne = false;
				}else{
					sumOne +=nums[right-1];
					sumOne -= nums[left-1];
				}
				return (sumOne/(double)(right - left));
		}
}

// 但是 这个解题思路有些麻烦了,
// 第二个思路,由于规定了子数组的长度为k,因此可以通过寻找子数组的最大元素和的方式寻找子数组的最大
// 平均数,元素和最大的子数组对应的平均数也是最大的。
class Solution{
			public double findMaxAverage(int[] nums,int k){
					int sum = 0;
					int n = nums.length;
					for(int i = 0;i<k;i++){
						sum +=nums[i];
					}
					int maxSum = sum;
					for(int i = k;i<length;i++){
							sum = sum - nums[i-k] + nums[i];
							maxSum = Math.max(maxSum,sum);
					}
					return 1.0 * maxSum/k;
		}
}

尽可能使字符串相等

给你两个长度相同的字符串,s和t。

将s中的第i个字符变到t中的第i个字符需要|s[i]-t[i]|的开销(开销可能为0),也就是两个字符的ASCII码值的差的绝对值。

用于变更字符串的最大预算是maxCost,在转化字符串的时候,总开销应当小于等于预算,这也意味着字符串的转化可能是不完全的。

如果你可以将s的字符串转化为它在t中对应的字符串,则返回可以转化的最大长度。

如果s中没有字符串可以转化为t中的字符串,则返回0;

/**
	* 将差值作为一个目标对象,使用滑动窗口进行移动获取最大的值
**/

class Solution{
		public int equalSubstring(String s,String t,int maxCost){
				ArrayList<Integer> nums = new ArrayList<>();
				for(int i = 0;i<s.length();i++){
						nums.add(Math.abs(s.charAt(i) - t.charAt(i)));
				}
				int left = 0;
				int right =0;
				int length = 0;
				int code  = maxCost;
				while(right<nums.size()&& left<=right){
						code -= nums.get(right);
						if(code >= 0){
								
						}else if (right == left){
								left++;
								code = maxCost;
						}else{
								length = Math.max(length,right-left);
								code += nums.get(left);//还回来
								left++;
						}
						right++;
				}
				return Math.max(length,right-left);
		}
}

可获得的最大点数

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

// 将目标对象进行重新构建为 k-0 0-k 的一个数组,滑动窗口从左面移动到右面进行计算。
class Solution{
			private int sum = 0;
			private boolean start = true;
			public int maxSore(int[] cardPoints,int k){
					int[] card = new int[k];
					for(int i =0;i<k;i++){
							card[i] = cardPonits(cardPoints.length -i-1);
					}
					int cardStart = k;
					int pointStart = 0;
					int max = 0;
					while(pointStart<=k){
							max = Math.max(max,whereToGo(cardStart,pointStart,card,cardPoints));
							cardStart--;
							pointStart++;
					}
					return max;
			}

			private int whereToGo(int card,int point,int[] cardArray,int[] cardPonint){
					if(start){
							for(int i =0 ;i<card;i++){
								sum+=cardArray[i];
							}
							start = false;
					}else{
							sum -=cardArray[card];
							sum += cardPoint[point-1];
					}
					return sum;
			}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值