用取模运算实现循环列表,核心是将线性索引通过 i % n 映射为环形访问,需用安全取模函数处理负数;适用于环形缓冲区、轮询资源等场景,注意空数组、浮点索引等边界问题。
用数组实现循环列表,核心是用取模运算(
)把线性索引“弯成环”,让下标超出边界时自动绕回开头。不需要额外链表结构,轻量、高效,特别适合缓冲区、滑动窗口、环形队列等场景。
理解循环索引:用取模把数组变“首尾相接”
假设数组长度为
,要让索引
在访问时自动循环,只需写成
。但注意负数索引问题:JavaScript/Python 中
结果不同(Python 得 4,JS 得 -1),需统一处理。
安全取模函数(推荐):
,确保结果始终在
正向遍历(如“下一个位置”):
反向遍历(如“上一个位置”):
,不能直接用
实战:实现一个可读写的环形缓冲区(Ring Buffer)
环形缓冲区常用于日志缓存、音频流、实时数据暂存。它有头(读位置)、尾(写位置),满/空判断靠“多预留一个空位”或“额外计数器”来区分。
用两个指针
(读起点)、
(写起点),初始都为 0
写入逻辑:
读取逻辑:
判空:
;判满:
(预留法)
变量环形访问:在固定集合中轮询使用资源
比如后端有 3 台机器,请求按轮询方式分发;或前端动画帧中循环切换 4 种颜色——本质都是对有限变量集做环形索引。
定义变量池:
每次获取下一个:
支持“回退一步”:
支持跳步(如每轮用第 2 个):
避坑提醒:边界与类型细节
看似简单,但几个细节常导致 bug:
不要直接用
期望得到末尾元素——数组不支持负索引自动循环,必须显式计算
当数组为空(
)时,
运算会报错,使用前加保护:
浮点索引(如
)不能直接取模,先
或四舍五入,视业务定
性能敏感场景中,取模在现代 JS 引擎中已优化,无需刻意用位运算替代(除非
是 2 的幂且确定)
%nii % n-1 % 5const mod = (a, n) => ((a % n) + n) % n[0, n-1]next = (i + 1) % nprev = mod(i - 1, n)(i - 1) % nheadtailbuffer[tail] = item; tail = (tail + 1) % capacity;item = buffer[head]; head = (head + 1) % capacity;head === tail(tail + 1) % capacity === headconst servers = ['srv-a', 'srv-b', 'srv-c']; let idx = 0;const server = servers[idx]; idx = (idx + 1) % servers.length;idx = mod(idx - 1, servers.length);idx = (idx + 2) % servers.length;arr[-1]mod(-1, n)n === 0%if (!arr.length) return;i = 2.7Math.floor(i)n