PHP滑动窗口算法需依问题类型选结构:子串类用双指针+哈希表;最大值用单调双端队列;限流用Redis ZSet;平均值用增量更新;Unicode场景需mb_*函数与编码归一化。

PHP中实现滑动窗口算法需依据具体问题类型选择对应结构与逻辑,常见于子串查找、限流控制、最大值计算与流量统计等场景。以下是多种典型实现方法:
一、字符串/数组子串类问题(双指针+哈希表)
适用于最长无重复子串、最小覆盖子串等,核心是用左右指针动态调整窗口范围,并借助关联数组记录字符频次或最新位置,确保O(n)时间复杂度。
1、初始化左指针$left = 0,空关联数组$map用于记录字符最后出现索引,$maxLen = 0用于保存结果。
2、遍历字符串,右指针$i从0开始,每次将$s[$i]存入$map并更新其值为$i。
3、若当前字符已存在且索引≥$left,则将$left设为$map[$s[$i]] + 1,跳过重复部分。
4、每次循环后计算当前窗口长度$i - $left + 1,更新$maxLen = max($maxLen, $i - $left + 1)。
5、返回$maxLen作为最长无重复子串长度。
二、滑动窗口最大值(单调双端队列模拟)
针对固定长度k的滑动窗口求最大值,避免暴力遍历每个窗口,使用PHP数组模拟单调递减双端队列,仅存有效索引,保证队首始终为当前窗口最大值对应下标。
1、初始化空数组$deque和结果数组$result。
2、遍历数组$nums,索引为$i,对每个元素执行:从$deque尾部弹出所有对应值≤$nums[$i]的索引。
3、将当前索引$i压入$deque尾部。
4、检查$deque头部索引是否小于$i - $k + 1,若是则array_shift($deque)移除过期索引。
5、当$i ≥ $k - 1时,将$nums[$deque[0]]追加至$result。
三、API接口限流(Redis ZSet实现)
利用Redis的ZSet按时间戳排序特性,精确统计指定时间窗口内请求数,解决固定窗口临界突增问题,适合高并发场景下的频率控制。
1、定义$key = 'rate_limit:' . $userId,$windowSeconds = 60,$maxRequests = 100。
2、获取当前时间戳$now = time(),计算窗口起始时间$windowStart = $now - $windowSeconds。
3、执行$redis->zRemRangeByScore($key, 0, $windowStart)清理过期请求记录。
4、调用$redis->zCount($key, $windowStart, $now)获取当前窗口内请求数。
5、若$count ≥ $maxRequests,抛出Too many requests异常;否则执行$redis->zAdd($key, $now, uniqid())并设置$redis->expire($key, $windowSeconds + 10)。
四、滑动平均值计算(增量更新优化)
适用于实时流量统计、性能指标平滑等场景,通过维护窗口内总和避免重复累加,将时间复杂度由O(n×w)降至O(n),提升大数据量处理效率。
1、校验输入:若$windowSize ≤ 0或数组为空,直接返回空数组。
2、计算前$windowSize个元素和$sum = array_sum(array_slice($arr, 0, $windowSize)),加入$result[] = $sum / $windowSize。
3、从索引$windowSize开始遍历,每步执行$sum = $sum - $arr[$i - $windowSize] + $arr[$i]。
4、将更新后的$sum / $windowSize追加至$result。
5、返回$result作为各窗口对应的平均值序列。
五、大小写与Unicode兼容处理
在字符串类滑动窗口中,若需忽略大小写或支持多字节字符,必须提前统一编码形态,否则会导致字符匹配失败或越界访问。
1、对输入字符串调用strtolower($s)实现大小写归一化。
2、若含中文、emoji等Unicode字符,禁用单字节下标访问$s[$i],改用mb_substr($s, $i, 1, 'UTF-8')提取字符。
3、窗口扩展与收缩逻辑中所有字符操作均基于mb_*函数重写,确保位置计算准确。
4、关联数组键名使用md5($char)或bin2hex($char)规避非ASCII键名潜在问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











