SortedSet SkipList插入时如何更新指针长度以实现O(logn)的get(index)
为SortedSet跳表实现O(logn)时间复杂度的get(index)的插入逻辑改造
要让get(index)达到O(logn),核心是给跳表的每个节点增加**length数组**,与next数组一一对应。length[r]的定义是:在第r层中,当前节点的next[r]节点与自身的底层索引差值(即从当前节点沿next[r]跳转,会跨越多少个元素)。这样在查询索引时,就能通过累加各层的length值快速定位目标位置。
以下是针对你现有插入逻辑的具体修改步骤:
1. 补充遍历过程中的索引计算与路径记录
首先需要在遍历过程中累加索引,同时记录每一层前驱节点的索引(用于后续更新length)。修改原遍历循环:
// 新增数组记录各层前驱节点的底层索引 int[] indexStack = new int[stack.length]; // 假设stack已初始化足够长度 Node<T> u = sentinel; int r = h; int comp = 0; int j = -1; // sentinel的底层索引为-1 Node<T> w = new Node<>(x, pickHeight()); while (r >= 0) { while (u.next[r] != null && (comp = c.compare(u.next[r].x, x)) < 0) { // 累加当前层的length,得到下一个节点的索引偏移 j += u.length[r]; u = u.next[r]; } // 记录当前层的前驱节点及其索引 stack[r] = u; indexStack[r] = j; r--; // 遇到重复元素直接返回 if (r+1 >=0 && u.next[r+1] != null && comp == 0) { return false; } } int wIndex = j + 1; // 新节点的底层索引
2. 处理跳表高度扩展
当新节点高度超过当前跳表高度时,需要初始化新层的哨兵节点length:
while (h < w.height()) { stack[++h] = sentinel; indexStack[h] = -1; // 哨兵的索引始终为-1 // 新层哨兵的length为总元素数(插入后),因为哨兵到末尾共n+1个元素 sentinel.length[h] = n + 1; }
3. 插入节点并更新length数组
插入新节点时,需要先保存前驱节点原有的length值,再拆分更新前驱和新节点的length:
for (int i = 0; i < w.next.length; i++) { Node<T> prev = stack[i]; Node<T> nextNode = prev.next[i]; int prevIndex = indexStack[i]; // 保存前驱节点原有的length值 int oldPrevLength = prev.length[i]; // 插入新节点的next指针 w.next[i] = nextNode; prev.next[i] = w; // 更新前驱节点的length:现在指向新节点,差值为新节点索引 - 前驱索引 prev.length[i] = wIndex - prevIndex; // 更新新节点的length:拆分原前驱的length,剩余部分指向后继节点 if (nextNode != null) { w.length[i] = oldPrevLength - (wIndex - prevIndex); } else { // 无后继节点时,length为总元素数 - 新节点索引 w.length[i] = (n + 1) - wIndex; } } n++; // 最后更新节点总数 return true;
关键注意事项
- 节点结构要求:你的
Node<T>类必须新增int[] length成员,初始化时与next数组长度一致。 - length定义一致性:所有层的
length都基于底层索引计算,确保跨层跳转时的索引累加逻辑正确。 - 重复元素判断:必须在遍历过程中提前检测重复元素,避免后续无效的length更新。
内容的提问来源于stack exchange,提问作者32_Quốc Vinh_11A13
相关产品推荐
相关产品推荐

