跳表
增加了向前指针的链表叫作跳表。跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。(来自百度百科)
其的一次增删改查的平均时间复杂度均为O(logN)。
跳表结点的数据结构如下:
class Skiplist {
public static class Node {
int val;
List<Node> next;
public Node(int val){
this.val = val;
next = new ArrayList<>();
}
}
int height;
// 增高因子
double eps;
// head 不存数据
Node head;
public Skiplist() {
height = 1;
eps = 0.5;
head = new Node(-1);
head.next.add(null);
}
}
插入操作:
首先通过随机产生当前结点的高度,然后从头结点的当前高度开始,依次找到每一层中不小于当前结点的最后一个,然后将待插入结点插入到其后面,如此直到完成第一层的插入。不过需要注意的是,下一层待插入的位置选择不需要从头结点重新开始,只需要从当前层待插入位置下移即可。
以上图为例,若待插入的结点为4, 其生成的高度为3。(对于高度,例如头结点从下往上一次为第一层, 第二层,第三层,第四层)
首先从头结点的第三层开始,发现其下一个位置是5大于4了,因此知道在第三层,应该插入到头结点后面。
对于第二层首先发现一个结点为3,小于4,继续后移,发现下一个结点为5,因此第二层插入到3后面。
对于第一层,发现3的下一个结点为5,大于4,插入3后面即可。
注:插入操作与链表的插入操作相同,cur.next = pre.next,pre.netx = cur。
注:代码中为了方便期间头结点不存元素,认为任何元素都比其大
代码如下:
public void add(int num) {
int curHeight = 1;
while(Math.random() < eps){
curHeight++;
}
// 待插入的结点
Node target = new Node(num);
for(int i = 0; i < curHeight; i++){
target.next.add(null);
}
for(int i = height; i < curHeight; i++){
head.next.add(null);
}
height = Math.max(height, curHeight);
// 依次从上往下 从左往右寻找
Node cur = head;
for(int i = curHeight - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val <= num){
cur = cur.next.get(i);
}
target.next.set(i, cur.next.get(i));
cur.next.set(i, target);
}
}
查找操作:
与插入操作类似,还是从头结点的最高层开始,依次往右移动,直到其下一个元素大于给定值,然后下移,重复该步骤。不过需要注意的当某一层中找到当前元素时直接返回即可,不需要继续往下寻找。
代码如下:
public boolean search(int target) {
Node cur = head;
for(int i = height - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val <= target){
cur = cur.next.get(i);
}
if(cur == head){
continue;
}
if(cur.val == target){
return true;
}
}
return false;
}
删除操作:
依次从头结点的最高层开始,找到小于给定值的最大结点,判断其下一个结点是否为给定值,若不是当前结点下移,若是是删除下一个结点再下移。重复该过程直到到达第一层。
还是以该跳表为例,若我们待删除的元素为3。
第四层,当前结点的下一个位置是5,不为3,下移
第三层,当前结点的下一个位置是5,不为3,下移
第二层,当前结点的下一个位置是3,删除其下一个位置,下移
第一层,当前结点的下一个位置为2小于三,右移,当前位置的下一个元素为3,删除其下一个元素。
代码如下:
public boolean erase(int num) {
boolean exist = false;
Node cur = head;
for(int i = height - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val < num){
cur = cur.next.get(i);
}
if(cur.next.get(i) != null && cur.next.get(i).val == num){
cur.next.set(i, cur.next.get(i).next.get(i));
exist = true;
}
}
return exist;
}
完整代码如下:
class Skiplist {
public static class Node {
int val;
List<Node> next;
public Node(int val){
this.val = val;
next = new ArrayList<>();
}
}
int height;
//增高因子
double eps;
// head 不存数据
Node head;
public Skiplist() {
height = 1;
eps = 0.5;
head = new Node(-1);
head.next.add(null);
}
public boolean search(int target) {
Node cur = head;
for(int i = height - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val <= target){
cur = cur.next.get(i);
}
if(cur == head){
continue;
}
if(cur.val == target){
return true;
}
}
return false;
}
public void add(int num) {
int curHeight = 1;
while(Math.random() < eps){
curHeight++;
}
// 待插入的结点
Node target = new Node(num);
for(int i = 0; i < curHeight; i++){
target.next.add(null);
}
for(int i = height; i < curHeight; i++){
head.next.add(null);
}
height = Math.max(height, curHeight);
// 依次从上往下 从左往右寻找
Node cur = head;
for(int i = curHeight - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val <= num){
cur = cur.next.get(i);
}
target.next.set(i, cur.next.get(i));
cur.next.set(i, target);
}
}
public boolean erase(int num) {
boolean exist = false;
Node cur = head;
for(int i = height - 1; i >= 0; i--){
while(cur.next.get(i) != null && cur.next.get(i).val < num){
cur = cur.next.get(i);
}
if(cur.next.get(i) != null && cur.next.get(i).val == num){
cur.next.set(i, cur.next.get(i).next.get(i));
exist = true;
}
}
return exist;
}
}
- android SurfaceView绘制实现原理解析
- AbstractQueuedSynchronizer实现示例
- PyQt的一个UI单元测试框架思路
- Spring Cloud Zuul实现动态路由
- android应用资源预编译,编译和打包全解析
- Python优雅地dumps非标准类型
- html5标签基础
- 彻底搞懂Gradle、Gradle Wrapper与Android Plugin for Gradle的区别和联系
- 社会化分享
- Android真机安装sqlite3的方法
- 二次元世界的Linux—东方Project之B站掠影
- 带三方登录(qq,微信,微博)
- Android Studio移动鼠标显示悬浮提示的设置方法
- Spring Cloud构建微服务架构:服务注册与发现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法