跳表

跳表

1.定义

跳表(SkipList):增加了向前指针的链表叫做指针。跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质是一种可以进行二分查找的有序链表。跳表在原有的有序链表上增加了多级索引,通过索引来实现快速查询。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能

2.详解

对于一个单链表来讲,即便链表中存储的数据是有序的,如果要查询需要O(n)的时间复杂度

比如有一条原始链表 1->5->7->9->11->13

利用二分的思想来加快链表查询,我们可以每个几个链表节点建立一个索引,比如每两个建立一个索引

索引层1——>7——>11

​ ↓ ↓ ↓

数据层1->5->7->9->11->13

我们发现要查询9,只用先查询索引层的1、7以及数据层的7与9,上面的例子知识建立了一个索引层,建立多个索引层也是可以的

索引层1——–——>11

​ ↓ ↓ ↓

​ 1——>7——>11

​ ↓ ↓ ↓

数据层1->5->7->9->11->13

2.1时间复杂度空间复杂度分析

我们首先考虑这样一个问题,如果链表里有n个结点,那么会有多少级索引呢?按照上面讲的,每两个结点都会抽出一个结点作为上一级索引的结点。那么第一级索引的个数大约就是n/2,第二级的索引大约就是n/4,第三级的索引就是n/8,依次类推,也就是说,第k级索引的结点个数是第k-1级索引的结点个数的1/2,那么第k级的索引结点个数为:。

假设索引有h级,最高级的索引有2个结点,通过上面的公式,我们可以得到,从而可得:h = 。如果包含原始链表这一层,整个跳表的高度就是。我们在跳表中查找某个数据的时候,如果每一层都要遍历m个结点,那么在跳表中查询一个数据的时间复杂度就为:O(m*logn)。m为每个该层相邻节点之间在下层的间距。

空间复杂度,对于2分链表 这几级索引结点总和为:n/2 + n/4 + n/8 + … + 8 + 4 + 2 = n - 2。所以跳表的空间复杂度为O(n)

2.2实现

Node的next为该节点存储在哪一层的节点,如Node.next[2]表示,Node在第三层的节点

// 跳表中存储的是正整数,并且存储的数据是不重复的
public class SkipList {
	
	private static final int MAX_LEVEL = 16;    // 结点的个数
	
	private int levelCount = 1;   // 索引的层级数
	
	private Node head = new Node();    // 头结点
	
	private Random random = new Random();
 
	// 查找操作
	public Node find(int value){
		Node p = head;
		for(int i = levelCount - 1; i >= 0; --i){
			while(p.next[i] != null && p.next[i].data < value){
				p = p.next[i];
			}
		}
		
		if(p.next[0] != null && p.next[0].data == value){
			return p.next[0];    // 找到,则返回原始链表中的结点
		}else{
			return null;
		}
	}
	
	// 插入操作
	public void insert(int value){
		int level = randomLevel();
		Node newNode = new Node();
		newNode.data = value;
		newNode.maxLevel = level;   // 通过随机函数改变索引层的结点布置
		Node update[] = new Node[level];
		for(int i = 0; i < level; ++i){
			update[i] = head;
		}
		
        Node p = head;
        for(int i = level - 1; i >= 0; --i){
        	while(p.next[i] != null && p.next[i].data < value){
        		p = p.next[i];
        	}
        	update[i] = p;
        }
        
        for(int i = 0; i < level; ++i){
        	newNode.next[i] = update[i].next[i];
        	update[i].next[i] = newNode;
        }
        if(levelCount < level){
        	levelCount = level;
        }
	}
	
	// 删除操作
	public void delete(int value){
		Node[] update = new Node[levelCount];
		Node p = head;
		for(int i = levelCount - 1; i >= 0; --i){
			while(p.next[i] != null && p.next[i].data < value){
				p = p.next[i];
			}
			update[i] = p;
		}
		
		if(p.next[0] != null && p.next[0].data == value){
			for(int i = levelCount - 1; i >= 0; --i){
				if(update[i].next[i] != null && update[i].next[i].data == value){
					update[i].next[i] = update[i].next[i].next[i];
				}
			}
		}
	}
	
	// 随机函数
	private int randomLevel(){
		int level = 1;
		for(int i = 1; i < MAX_LEVEL; ++i){
			if(random.nextInt() % 2 == 1){
				level++;
			}
		}
		
		return level;
	}
	
	// Node内部类
	public class Node{
		private int data = -1;
		private Node next[] = new Node[MAX_LEVEL];
		private int maxLevel = 0;
		
		// 重写toString方法
		@Override
		public String toString(){
			StringBuilder builder = new StringBuilder();
			builder.append("{data:");
			builder.append(data);
			builder.append("; leves: ");
			builder.append(maxLevel);
			builder.append(" }");
			return builder.toString();
		}
	}
	
	// 显示跳表中的结点
	public void display(){
		Node p = head;
		while(p.next[0] != null){
			System.out.println(p.next[0] + " ");
			p = p.next[0];
		}
		System.out.println();
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 PostgreSQL 数据库中,并没有内置的跳表(Skip List)索引实现。PostgreSQL 提供了多种索引类型,如B树索引、哈希索引、GiST索引和GIN索引等,但没有直接支持跳表的索引类型。 B树索引是 PostgreSQL 中最常用的索引类型之一。它适用于范围查询和等值查询,并且可以保持数据有序性。B树索引在处理数据块的平衡性和查询效率方面具有很好的性能。 除了B树索引之外,PostgreSQL 还提供了其他类型的索引用于特定的场景。例如,哈希索引适用于等值查询,可以提供快速的哈希查找;GiST 索引(通用搜索树)和 GIN 索引(通用倒排索引)适用于全文搜索和复杂的匹配查询。 虽然 PostgreSQL 不提供内置的跳表索引实现,但是你可以使用扩展或自定义索引实现跳表的功能。通过编写自定义插件或使用第三方扩展,你可以在 PostgreSQL 中实现跳表索引。这需要一定的开发工作,并且需要充分测试和评估性能。 需要注意的是,自定义实现的跳表索引可能会受到 PostgreSQL 内核版本更新的影响,并且可能无法享受到 PostgreSQL 内置索引的一些优化和支持。 总之,PostgreSQL 并没有内置的跳表索引实现,但提供了其他类型的索引,如B树索引、哈希索引、GiST索引和GIN索引等,用于满足不同的查询需求。如果需要使用跳表索引,你可以考虑自定义实现或使用第三方扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值