用取模运算实现循环列表,核心是将线性索引通过 i % n 映射为环形访问,需用安全取模函数处理负数;适用于环形缓冲区、轮询资源等场景,注意空数组、浮点索引等边界问题。

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










