精华内容
下载资源
问答
  • 对我自己而言,可能统计的算法是比较关心的,都知道sentinel的统计算法是滑动时间窗口算法,这个算法一这么说就不接地气了,下面谈谈我们农村的说法。 探索 说这个算法主要要解决什么事呢?就是说在某个时间间隔...

    简介

        其实sentinel核心原理并不难理解,就是在访问被保护资源时,根据实时的统计信息和预先定义的规则,检查是否可以访问。对我自己而言,可能统计的算法是比较关心的,都知道sentinel的统计算法是滑动时间窗口算法,这个算法一这么说就不接地气了,下面谈谈我们农村的说法。

    探索

        说这个算法主要要解决什么事呢?就是说在某个时间间隔要做点统计,比如说现在时间是16:06:21,320,我现在想知道16:06:21,000-16:06:21,320我的应用app通过了多少个请求,即当前的320ms通过了多少请求?哎这个简单啊,等我数数。。。,直接说说sentinel怎么搞的,想想文字说起来苍白无力,来点图片看看。

    sentinel时间窗口

     

        就如图片展示的,想统计一段时间内的数据,就定义一个WindowWrap(时间窗口)来实现,在时间轴上随着时间的流逝,会有无限多连续的时间窗口,时间窗口通过MetricBucket来统计数据,都统计什么数据呢,MetricEvent定义了相关的统计数据类型,不同的统计数据类型由一个LongAdder来计数,这样在请求到来的时候先根据当前时间戳定位时间窗口,由当前的时间窗口来记录需要统计的信息,至于LeapArray是怎么回事,代码逻辑比较容易理解,但是设计意图在这里还不是特别清晰,可以看看其sampleCount这个属性,应该能有所猜测,再多的可以到下文再仔细品味下。

        咋就好像说完了呢,还不到10块钱的,再继续聊聊,上代码,不详细说为什么代码执行流程了,就看看关键的代码。

    public class{
        private final LeapArray<MetricBucket> data;
        public void addPass(int count) {
           WindowWrap<MetricBucket> wrap = data.currentWindow();
           wrap.value().addPass(count);
        }
    }
    public abstract class LeapArray{
        public WindowWrap<T> currentWindow(long timeMillis) {
            if (timeMillis < 0) {
                return null;
            }
            //计算当前时间在数组中的位置
            int idx = calculateTimeIdx(timeMillis);
            //计算当前时间窗口的开始时间
            long windowStart = calculateWindowStart(timeMillis);
            while (true) {
                //在数组中获取时间窗口,注意这里的old是说时间窗口已经创建过了
                WindowWrap<T> old = array.get(idx);
                //如果没有就搞一个新的时间窗口
                if (old == null) {
                    WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
                    if (array.compareAndSet(idx, null, window)) {
                        return window;
                    } else {
                        Thread.yield();
                    }
                //如果开始时间等于old的开始时间就把old返回
                } else if (windowStart == old.windowStart()) {
                    return old;
                //如果大于old开始时间,就重置
                } else if (windowStart > old.windowStart()) {
                    if (updateLock.tryLock()) {
                        try {
                            return resetWindowTo(old, windowStart);
                        } finally {
                            updateLock.unlock();
                        }
                    } else {
                        Thread.yield();
                    }
                } else if (windowStart < old.windowStart()) {
                    return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
                }
            }
        }
    }
    public class MetricBucket {
        public MetricBucket add(MetricEvent event, long n) {
            //找到对应的事件LongAdder递增
            counters[event.ordinal()].add(n);
            return this;
        }
    }

        结合代码又进一步了解了sentinel的滑动时间窗口算法,一段时间(事件窗口)+统计对象,即时间在时间窗口中滑动并作数据统计。说到这里其实算法这块差不多结束了,不行我这暴脾气上来了,就说咋统计了,那统计完咋用的啊?那就再看看。

    public class StatisticNode implements Node {
        public long totalPass() {
            return rollingCounterInMinute.pass();
        }
    }
    public class ArrayMetric implements Metric {
        public long pass() {
            data.currentWindow();
            long pass = 0;
            List<MetricBucket> list = data.values();
    
            for (MetricBucket window : list) {
                pass += window.pass();
            }
            return pass;
        }
    }
    public class MetricBucket {
        public long pass() {
            return get(MetricEvent.PASS);
        }
        public long get(MetricEvent event) {
            return counters[event.ordinal()].sum();
        }
    }

        这就明白了,不就是找在固定时间间隔内所有时间窗口样本的统计数据嘛,还记得sampleCount(LeapArray)吗?好像有点内味了,再看看qps。

    public class StatisticNode implements Node {
        public double passQps() {
            return rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec();
        }
    }

        qps=所有样本请求通过数/固定时间间隔,呦,这么一看我可以把时间间隔搞得小一点比如(500ms,默认1000ms),那样本数我也不搞那么多,我让窗口长度就是500,那统计qps的时候就是1个500ms的qps,这样好像避免了统计学的误差哈,那要是。。。,剩下的自己动手吧。

    总结

        有兴趣可以自己实现下滑动时间窗口算法,有了sentinel的启发,相信实现起来难度不会太大,那再多说一下啊如果去掉时间二字,就变成了滑动窗口算法,这个算法的定义百度一下都能找得到,那我们可以再思索下sentinel的滑动时间窗口算法在算法角度,到底带来了多大的好处。

    展开全文
  • 本文实现了一种基于java的滑动时间窗口计数器算法 滑动时间窗口计数器算法思想:针对固定时间算法会在临界点存在瞬间大流量冲击的场景,滑动时间窗口计数器算法应运而生。它将时间窗口划分为更小的时间片段,每过一...

    本文实现了一种基于java的滑动时间窗口计数器算法

    滑动时间窗口计数器算法思想:针对固定时间算法会在临界点存在瞬间大流量冲击的场景,滑动时间窗口计数器算法应运而生。它将时间窗口划分为更小的时间片段,每过一个时间片段,我们的时间窗口就会往右滑动一格,每个时间片段都有独立的计数器。我们在计算整个时间窗口内的请求总数时会累加所有的时间片段内的计数器。时间窗口划分的越细,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

    本文代码逻辑:新建一个本地缓存,每5s为一个时间窗口,每1s为一个时间片段,时间片段作为缓存的key,原子类计数器作为缓存的value。每秒发送随机数量的请求,计算每个时间片段的前5秒内的累加请求数量,超出阈值则限流。

    @Slf4j
    public class WindowLimiter {
    
        private LoadingCache<Long, AtomicLong> counter =
                CacheBuilder.newBuilder()
                        .expireAfterWrite(10, TimeUnit.SECONDS)
                        .build(new CacheLoader<Long, AtomicLong>() {
                            @Override
                            public AtomicLong load(Long seconds) throws Exception {
                                return new AtomicLong(0);
                            }
                        });
        private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        //限流阈值
        private long limit = 15;
    
        /**
         * 滑动时间窗口
         * 每隔1s累加前5s内每1s的请求数量,判断是否超出限流阈值
         */
        public void slideWindow() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
                try {
                    long time = System.currentTimeMillis() / 1000;
                    //每秒发送随机数量的请求
                    int reqs = (int) (Math.random() * 5) + 1;
                    counter.get(time).addAndGet(reqs);
                    long nums = 0;
                    // time windows 5 s
                    for (int i = 0; i < 5; i++) {
                        nums += counter.get(time - i).get();
                    }
                    log.info("time=" + time + ",nums=" + nums);
                    if (nums > limit) {
                        log.info("限流了,nums=" + nums);
                    }
                } catch (Exception e) {
                    log.error("slideWindow error", e);
                } finally {
                }
            }, 5000, 1000, TimeUnit.MILLISECONDS);
        }
    }

    输出结果:

    20:14:50.083 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079290,nums=15
    20:14:51.114 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079291,nums=13
    20:14:52.119 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079292,nums=14
    20:14:53.121 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079293,nums=15
    20:14:54.126 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079294,nums=17
    20:14:54.127 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17
    20:14:55.128 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079295,nums=14
    20:14:56.131 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079296,nums=17
    20:14:56.132 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17

    展开全文
  • 限流算法:滑动时间窗口算法

    千次阅读 2019-12-27 14:38:51
    滑动时间窗口计数器算法思想:针对固定时间算法会在临界点存在瞬间大流量冲击的场景,滑动时间窗口计数器算法应运而生。它将时间窗口划分为更小的时间片段,每过一个时间片段,我们的时间窗口就会往右滑动一格,每个...

    本文实现了一种基于java的滑动时间窗口计数器算法

    滑动时间窗口计数器算法思想:针对固定时间算法会在临界点存在瞬间大流量冲击的场景,滑动时间窗口计数器算法应运而生。它将时间窗口划分为更小的时间片段,每过一个时间片段,我们的时间窗口就会往右滑动一格,每个时间片段都有独立的计数器。我们在计算整个时间窗口内的请求总数时会累加所有的时间片段内的计数器。时间窗口划分的越细,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

    本文代码逻辑:新建一个本地缓存,每5s为一个时间窗口,每1s为一个时间片段,时间片段作为缓存的key,原子类计数器作为缓存的value。每秒发送随机数量的请求,计算每个时间片段的前5秒内的累加请求数量,超出阈值则限流。

    @Slf4j
    public class WindowLimiter {
     
        private LoadingCache<Long, AtomicLong> counter =
                CacheBuilder.newBuilder()
                        .expireAfterWrite(10, TimeUnit.SECONDS)
                        .build(new CacheLoader<Long, AtomicLong>() {
                            @Override
                            public AtomicLong load(Long seconds) throws Exception {
                                return new AtomicLong(0);
                            }
                        });
        private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        //限流阈值
        private long limit = 15;
     
        /**
         * 滑动时间窗口
         * 每隔1s累加前5s内每1s的请求数量,判断是否超出限流阈值
         */
        public void slideWindow() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
                try {
                    long time = System.currentTimeMillis() / 1000;
                    //每秒发送随机数量的请求
                    int reqs = (int) (Math.random() * 5) + 1;
                    counter.get(time).addAndGet(reqs);
                    long nums = 0;
                    // time windows 5 s
                    for (int i = 0; i < 5; i++) {
                        nums += counter.get(time - i).get();
                    }
                    log.info("time=" + time + ",nums=" + nums);
                    if (nums > limit) {
                        log.info("限流了,nums=" + nums);
                    }
                } catch (Exception e) {
                    log.error("slideWindow error", e);
                } finally {
                }
            }, 5000, 1000, TimeUnit.MILLISECONDS);
        }
    }

    输出结果:

    20:14:50.083 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079290,nums=15
    20:14:51.114 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079291,nums=13
    20:14:52.119 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079292,nums=14
    20:14:53.121 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079293,nums=15
    20:14:54.126 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079294,nums=17
    20:14:54.127 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17
    20:14:55.128 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079295,nums=14
    20:14:56.131 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079296,nums=17
    20:14:56.132 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17

    展开全文
  • ​相关历史文章(阅读本文之前,您可能需要先看下之前的系列 )为数据可视化赋能Spring Boot... 值得收藏 - 第299篇「世界上最好的学习法:费曼学习法」高并发,不怕不怕「限流算法第一把法器:计数器法」 - 第300篇...

    3a517f6a3467d9e97f6ee3f144227524.png

    相关历史文章(阅读本文之前,您可能需要先看下之前的系列 )

    为数据可视化赋能Spring Boot Admin - 第297篇

    超实用的康奈尔笔记法

    「定制Spring Boot Admin UI的页面」- 第298篇

    国内最全的Spring Boot系列之三

    版本号命名的前世今生- 值得收藏 - 第299篇

    「世界上最好的学习法:费曼学习法」

    高并发,不怕不怕「限流算法第一把法器:计数器法」 - 第300篇

    一、回顾:计算器算法存在问题

    对于秒级以上的时间周期来说,会存在一个非常严重的问题,那就是临界问题。

    c34696c8ea09dd02667c30310049f6ce.png

    从上图中我们可以看到,假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

    师傅:不错,那么今天就说说如何解决这个由于问题。

    计算器的问题其实是因为我们统计的精度太低(统计的1秒内的流量情况,只有1个值进行记录)。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法。

    二、滑动窗口算法

    滑动窗口,又称rolling window。为了解决计数器法统计精度太低的问题,引入了滑动窗口算法。下面这张图,很好 地解释了滑动窗口算法:

    ffb5ab711616f13d2f4ad0ec5a2c5606.png

    在上图中,整个红色的矩形框表示一个时间窗口,在我们的例子中,一个时间窗口就是一分钟。然后我们将时间窗口进行划分,比如图中,我们就将滑动窗口划成了6格,所以每格代表的是10秒钟。每过10秒钟,我们的时间窗口就会往右滑动一格。每一个格子都有自己独立的计数器counter,比如当一个请求 在0:35秒的时候到达,那么0:30~0:39对应的counter就会加1。

    那么滑动窗口怎么解决刚才的临界问题的呢?在上图中,0:59到达的100个请求会落在灰色的格子中,而1:00到达的请求会落在橘×××的格子中。当时间到达1:00时,我们的窗口会往右移动一格,那么此时时间窗口内的总请求数量一共是200个,超过了限定的100个,所以此时能够检测出来触发了限流。

    回顾一下上面的计数器算法,我们可以发现,计数器算法其实就是滑动窗口算法。只是它没有对时间窗口做进一步地划分,所以只有1格。

    由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

    BTW:

    (1)滑动窗口算法是以当前这个时间点为基准,往前推移1秒进行计算当前1秒内的请求量情况。
    (2)滑动窗口限流统计的精准度是由划分的格子多少决定的,这个怎么理解呐,就是把1秒中进行划分成多个时间段,比如2个格子的话,那么就是2段,0-500ms和501-1000ms。那么就会两个值进行存储统计请求量,比如数组 [0,1] 各存储一个段的请求值。
    (3)计算器算法是滑动窗口算法将时间段划分为1的特殊情况。

    综上我们对滑动窗口算法下个定义:滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。

    三、滑动窗口算法实现

    我们看一段代码:

    /**
    * 滑动时间窗口限流实现
    * 假设某个服务最多只能每秒钟处理100个请求,我们可以设置一个1秒钟的滑动时间窗口,
    * 窗口中有10个格子,每个格子100毫秒,每100毫秒移动一次,每次移动都需要记录当前服务请求的次数 
    */
    public class SlidingTimeWindow {
    	// 时间窗口内最大请求数
    	public final int limit = 100; 
    	// 服务访问次数
    	Long counter = 0L;
    	// 使用LinkedList来记录滑动窗口的10个格子。
    	LinkedList<Long> slots = new LinkedList<Long>();
    	// 时间划分多少段落
    	int split = 10;
    	// 是否限流了,true:限流了,false:允许正常访问。
    	boolean isLimit = false;
    
    	private void doCheck() throws InterruptedException {
    		while (true) {
    			slots.addLast(counter);
    			if (slots.size() > split) {
    				slots.removeFirst();// 超出了,就把第一个移出。
    			}
    
    			// 比较最后一个和第一个,两者相差100以上就限流
    			if ((slots.peekLast() - slots.peekFirst()) > limit) {
    				System.out.println("限流了。。");
    				// 修改限流标记为true
    				isLimit = true;
    			} else {
    				// 修改限流标记为false
    				isLimit = false;
    			}
    			Thread.sleep(1000/split);
    		}
    	}
    
    	/**
    	 * 测试
    	 * @param args
    	 * @throws InterruptedException 
    	 */
    	public static void main(String[] args) throws InterruptedException {
    		SlidingTimeWindow timeWindow = new SlidingTimeWindow();
    		//开启一个线程判断当前的限流情况.
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				try {
    					timeWindow.doCheck();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}).start();
    		
    		
    		//模拟请求.
    		while(true) {
    			//判断是否被限流了.
    			if(!timeWindow.isLimit) {
    				timeWindow.counter++;
    				//未被限流执行相应的业务方法.
    				//  executeBusinessCode();
    				//模拟业务执行方法时间.
    				Thread.sleep(new Random().nextInt(15));
    				System.out.println("业务方法执行完了...");
    			}else {
    				System.out.println("被限流了,直接返回给用户");
    			}
    		}
    	}
    }
    

    说明:

    (1)doCheck方法,就是改变是否限流标志位的方法:这里通过LinkedList(链表)来进行模拟,通过LinkedList的大小来控制1秒分隔多少段。peekLast()、slots.peekFirst() 就是这个时间窗口的一个流量值。

    (2)在main方法中,我们通过开启一个线程来执行我们的doCheck()方法,由于doCheck()方法是while(true),所以会一直执行,sleep的时间刚好是每个时间间隔值,那么就会不断刷新LinkedList中的值。

    (3)最后while(true)代码是模拟业务执行调用代码,也就是controller发起调用的代码了。

    四、悟纤小结

    师傅:徒儿,师傅这代码都敲的腰酸背痛了。

    悟纤:那师傅赶紧早点回去休息吧。

    师傅:(内心世界:徒儿这是没听明白我的意思,有句话说重要的事情要说3遍)徒儿,师傅腰酸背痛~ 腰酸背痛~ 腰酸背痛~。

    悟纤:师傅,我懂了,您这是要让我给您敲敲背是吧,这您不找人给你按摩去,找我这个不专业的,按着也不舒服。

    师傅:就徒儿顽皮,不按就不按,看看你认真听没有,小结下这节的内容。

    (1)滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。

    (2)计算器算法也属于滑动窗口算法,是滑动窗口算法的一种情况(划分周期为1)。

    (3)滑动窗口算法实现方式:数组(多少周期,数组大小就是多大,将每个时间通过一定的算法落到每个数组下标即可);链表(通过链表中的大小来判断划分周期个数,如果超过了,可以使用链表的removeFirst()移除第一个,从而实现往后移动窗口)

    (4)数组实现的实例:这个可以看Sentinel的源码,就是定义了一个数组来实现的,时间划分是2。

    五、小思考

    当在系统刚刚启动的时候,系统很多资源都未分配完成,瞬间来了n个请求,如果使用滑动窗口算法的话,那么只有达到了QPS控制阈值,才会触发限流机制,那么这样针对系统刚启动资源未分配的情况,就无法防止系统瘫痪了。

    针对上面的这种情况,那么你有什么对应的限流算法可以应对呐?

    展开全文
  • 相关历史文章(阅读本文之前,您可能需要先看下之前的系列????) 为数据可视化赋能SpringBootAdmin-第297篇 超实用的康奈尔笔记法 「定制SpringBootAdminUI的页面」-第298...高并发,不怕不怕「限流算法第一把法...
  • ​相关历史文章(阅读本文之前,您可能需要先看下之前的系列 )一、回顾:计算器算法存在问题对于秒级以上的时间周期来说,会存在一个非常严重的问题,那就是临界问题。​从上图中我们可以看到,假设有一个恶意用户,...
  • 一、回顾:计算器算法存在问题对于秒级以上的时间周期来说,会存在一个非常严重的...我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们...
  • 在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码:package cn.dijia478.util;import java.time.LocalTime;import java.util.LinkedList;import java.util.List;import java.util.Map;...
  • 滑动窗口算法

    2020-12-12 19:30:38
    滑动窗口算法可以用以解决数组/字符串的子元素问题,它可以将嵌套的循环问题,转换为单循环问题,降低时间复杂度。 给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。 输入: s = “abcabcbb” 输出: 3 ...
  • 在前面搞清楚了Sentinel的使用后,大致理了一下...这一次找了一些空闲时间,捋了一下它的滑动窗口算法,在这里做一个记录。后面会继续去梳理它的令牌算法和漏桶算法。关于滑动窗口的原理,Sentinel为什么要使...
  • 滑动窗口算法学习

    千次阅读 2019-05-14 12:05:36
    最近做了几道有关滑动窗口算法,在此总结一下。 滑动窗口 就像描述的那样,可以理解成是一个会滑动窗口,每次记录下窗口的状态,再找出符合条件的适合的窗口 可以使用滑动窗口来减少时间复杂度 经典滑动窗口...
  • 滑动窗口算法总结

    2021-03-11 14:40:01
    我个人觉得滑动窗口算法思想与双指针相似,模拟一个窗口,创建的窗口逐渐滑动,根据题目给定的限制条件,来更新窗口边界,从而不断的更新题目所需值,当窗口滑动至边界时,这时也意味着,算法结束,即可
  • import java.time.LocalTime; import java.util.LinkedList; import java.util.List; import java.util.Map;...滑动时间窗口限流工具 本限流工具只适用于单机版,如果想要做全局限流,可以按本程序的思想,用red
  • 在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码: package cn.dijia478.util; import java.time.LocalTime; import java.util.LinkedList; import java.util.List; import java.util...
  • 因为size=0,小于5,都没有到限制的次数,完全不用考虑时间窗口,直接把这次事件的时间戳放到0的位置:图片3.第2.8秒的时候,第二个事件来了。因为此时size=1,还是小于5,把这次事件的时间戳放到0的位置,原来第1秒...
  • 滑动窗口算法问题

    2019-10-09 09:34:03
    相应的滑动窗口问题在 leetcode上的 第3题 (median),第76题(hard),第438题(median),这几道都是可以用滑动窗口在O(n)O(n)O(n)的时间复杂度内解决,无需使用暴力的解法,虽然有些题目暴力的解法也是可以通过...
  • 文章目录前言算法题1....滑动窗口算法可以将嵌套的循环问题,转换为单循环问题,降低时间复杂度。 算法题 1. LeetCode 1423. 可获得的最大点数 LeetCode 1423 几张卡牌 排成一行,每张卡牌都有一个对
  • 在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码:package cn.dijia478.util;import java.time.LocalTime;import java.util.LinkedList;import java.util.List;import java.util.Map;...
  • 在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码:import java.time.LocalTime;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Random;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 600
精华内容 240
关键字:

滑动时间窗口算法