携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
设计跳表
不使用任何库函数,设计一个 跳表 。
跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
例如,一个跳表包含 [30, 40, 50, 60, 70, 90] ,然后增加 80、45 到跳表中,以下图的方式操作:
Artyom Kalinin [CC BY-SA 3.0], via Wikimedia Commons
跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。
了解更多 : en.wikipedia.org/wiki/Skip_l…
在本题中,你的设计应该要包含这些函数:
bool search(int target): 返回target是否存在于跳表中。void add(int num): 插入一个元素到跳表。bool erase(int num): 在跳表中删除一个值,如果num不存在,直接返回false. 如果存在多个num,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。
示例 1:
输入
["Skiplist", "add", "add", "add", "search", "add", "search", "erase", "erase", "search"]
[[], [1], [2], [3], [0], [4], [1], [0], [1], [1]]
输出
[null, null, null, null, false, null, true, false, true, false]
解释
Skiplist skiplist = new Skiplist();
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
skiplist.search(0); // 返回 false
skiplist.add(4);
skiplist.search(1); // 返回 true
skiplist.erase(0); // 返回 false,0 不在跳表中
skiplist.erase(1); // 返回 true
skiplist.search(1); // 返回 false,1 已被擦除
提示:
0 <= num, target <= 2 * 10^4- 调用
search,add,erase操作次数不大于5 * 10^4
解题
const MAX_LEVEL = 16;
const LEVEL_P = 0.25;
function randomLevel() {
let level = 0;
while (Math.random() < LEVEL_P && level < MAX_LEVEL) {
level++;
}
return level;
}
class SkipNode {
constructor(val) {
this.value = val;
this.forward = [];
}
}
class Skiplist {
constructor() {
this.head = new SkipNode(-1);
this.level = 0;
}
/**
* @param {number} target
* @return {boolean}
*/
search(target) {
let nodes = this.searchLeftNodes(target);
let node = nodes[0].forward[0];
return !!node && node.value === target;
}
/**
* @param {number} num
* @return {void}
*/
add(num) {
const node = new SkipNode(num);
let level = randomLevel();
let nodes = this.searchLeftNodes(num);
for (let i = 0; i <= level; i++) {
let currNode = nodes[i] || this.head;
node.forward[i] = currNode.forward[i];
currNode.forward[i] = node;
}
return this;
}
/**
* @param {number} num
* @return {boolean}
*/
erase(num) {
let nodes = this.searchLeftNodes(num);
let i = 0
for (; i < nodes.length; i++) {
let node = nodes[i].forward[i];
if (node && node.value === num) {
nodes[i].forward[i] = node.forward[i];
} else {
break;
}
}
return i!==0
}
searchLeftNodes(target) {
let currNode = this.head;
let nodes = new Array(currNode.forward.length).fill(null);
let i = currNode.forward.length - 1;
while (i >= 0) {
let node = currNode.forward[i];
if (!node || node.value >= target) {
nodes[i] = currNode;
i--;
} else {
currNode = node;
}
}
return nodes;
}
}
/**
* Your Skiplist object will be instantiated and called as such:
* var obj = new Skiplist()
* var param_1 = obj.search(target)
* obj.add(num)
* var param_3 = obj.erase(num)
*/