滑动窗口
最近参加有个大公司(非BAT)的线上笔试,发现这个公司以往的面经以及笔经,滑动窗口他们似乎非常重视,既然我参加了他们的笔试,对他们公司更加的了解,也是对自己遗漏欠缺的知识进行一个补充。
其实但从力扣上看一下关于滑动窗口的题的出现率应该就能懂些什么了吧
滑动窗口的知识点非常重要!!!这也是我认识到之后特地写的这一篇文章
首先我们先了解一下滑动窗口的概念吧,毕竟我只是大概知道滑动窗口什么,详细的内容却一无所知。
沉下心一起看看吧,概念有点复杂,
概念
摘自 百度百科
滑动窗口(Sliding window)是一种流量控制技术。早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口机制来解决此问题。参见滑动窗口如何根据网络拥塞发送数据仿真视频。图片是一个滑动窗口的实例:
滑动窗口协议是用来改善吞吐量的一种技术,即容许发送方在接收任何应答之前传送附加的包。接收方告诉发送方在某一时刻能送多少包(称窗口尺寸)。
TCP中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为0时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个1字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。
滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。
工作原理
通过限制在任何给定时间可以发送或接收的数据包的数量,滑动窗口协议允许使用固定大小的序列号传送无限数量的数据包。发送方侧的术语“窗口”表示接收方尚未确认的分组总数的逻辑边界。接收方在每个确认包中通知发送器当前的最大接收缓冲区大小(窗口边界)。 TCP报头使用16位字段向发送方报告接收窗口大小。因此,可以使用的最大窗口是2^16 = 64千字节。在慢启动模式下,发送器以低分组计数器开始,并且在从接收方接收到确认分组之后增加每个传输中的分组数量。对于接收的每个ACK分组,该窗口通过一个分组(逻辑地)滑动以传送一个新分组。当达到窗口阈值时,发送器发送一个包,用于接收到的一个ACK分组(确认分组)。如果窗口限制为10个数据包,则在慢启动模式下,发送器可以开始发送一个数据包,后跟两个数据包(发送两个数据包之前必须接收一个数据包),其次是三个数据包等等,直到10个数据包。但是在达到10个分组之后,进一步的传输被限制为一个接收到的一个分组发送的分组。在仿真中,看起来好像窗口对于接收到的每个ACK分组移动一个分组距离。在接收方侧,窗口也会为接收到的每个数据包移动一个数据包。滑动窗口方法可以确保网络上的交通拥堵得以避免。应用层仍将提供传输到TCP的数据,而不用担心网络流量拥塞问题,因为发送方和接收方的TCP实现分组缓冲区的滑动窗口。窗口大小可能根据网络流量而动态变化。
相信你大概和我一样,看不下去了吧。。。T_T
说到底,其核心就是进行流量控制。
示例一
下面我们就以力扣的这道题为例,来演示下简单滑动窗口的实现吧
题目来源: 力扣-数据流中的移动平均值
题目:给定一个整数数据流和一个窗口大小,根据该滑动窗口的大小,计算其所有整数的移动平均值。
示例:
MovingAverage m = new MovingAverage(3);
m.next(1) = 1
m.next(10) = (1 + 10) / 2
m.next(3) = (1 + 10 + 3) / 3
m.next(5) = (10 + 3 + 5) / 3
列表实现
步骤图解
当列表中元素长度不大于窗口大小的时候,就将列表中数字的总和除以列表长度。
当列表中元素长度大于窗口长度的时候,只计算最后添加三项数据的总和然后除以窗口大小。
起始位置判定:
数据长度-窗口长度<0,即数据个数还没有窗口大,就从0开始
数据长度-窗口长度>0,数据个数大于窗口大小,从 (数据长度-窗口长度)开始,起始位置+窗口长度=数据长度。
这就是一个简单的滑动窗口的实现
代码应用
public class moveWindow{
/**滑动窗口的大小*/
private int size;
/**列表存储数据*/
List<Integer> list = new ArrayList<>();
//构造器初始化窗口大小
public moveWindow(int size){
this.size = size;
}
/*
* 添加一个数字道列表中,窗口移动,返回窗口内数字平均值
* @param num 添加的数字
* @return double 双精度浮点数
*/
public double next(int num){
int sum = 0;
for(int i = Math.max(0,list.size-size);i<list.size;i++){
sun += (int) list.get(i);
}
return sum*0.1/Math.min(list.size,size);
}
}
双端队列实现