Redis底层数据结构:跳表的JAVA实现(gitbuh项目:AlgorithmPractice)

项目介绍

  • 本项目通过分解各大厂的常见笔面试题,追本溯源至数据结构和算法的底层实现原理,知其然知其所以然;
  • 建立知识结构体系,方便查找,欢迎更多志同道合的朋友加入项目AlgorithmPractice,(欢迎提issue和pull request)。

一、题目描述

  • 跳表是什么:跳表是一种可以进行对半查找的链表,其通过在源链表上插入标记指针,再将标记指针也串成链表,这样通过查找标记指针达到快速查找数值的目的。同理,标记指针过长,可以同样对标记指针再次进行标记,类似多级缓存。

  • 跳表及其实现:(方法主要包括:add()delete()find()initial()print())。

  • 代码实现SkipList

二、解题思路
首先准备一个Node结点的数据结构,我们假设这个快表的每一个链表数值都是从小到大排序,所有起点的值设置为 Integer.MIN_VALUE。

class SkipListNode {
    
    

    int value;
    SkipListNode next;
    SkipListNode down;

    public SkipListNode() {
    
    

        this.value = Integer.MIN_VALUE;
        next = null;
        down = null;
    }

    public SkipListNode(int value) {
    
    

        this.value = value;
        next = null;
        down = null;
    }
}

1、initial()方法

每条链表初始化的时候,都需要头尾两个结点,注意到头结点和尾结点的值设置为(从小到大):
public static final int HEAD_KEY = Integer.MIN_VALUE;
public static final int TAIL_KEY = Integer.MAX_VALUE;

每次初始化的时候都需要交换新老头结点和尾结点的链接。

public SkipListNode initial() {
    
    
        SkipListNode phead = new SkipListNode();
        SkipListNode ptail = new SkipListNode();
        phead.value = HEAD_KEY;
        ptail.value = TAIL_KEY;
        phead.next = ptail;
        return phead;
    }

2、add()方法

add方法首先判断节点时都存在,存在的话直接return,不存在的话,首先判断是否需要在源链表的基础上新建一层标记指针。
public void add(int Value) {
    
    

        if (find(Value)) {
    
    
            return;
        }
        SkipListNode insertKey;
        SkipListNode fathersln = null;
        /* 经过实验证明:查找 耗时 比 插入 更加耗时。
        层数越高,整个数据备份越多,越臃肿,但是查找快、插入和删除慢
		层数越低,整个数据备份越少,      但是查找慢、插入和删除快
		综合考虑,建议层数高
		*/

        //无需新建层数
        if (SkipListnum <= (2 << SkipListlevel - 1)) {
    
    
            int k = randomLevel();
            insertKey = head;
            for (int i = 0; i < k; i++) {
    
    
                insertKey = insertKey.down;
            }
        } else {
    
    //需要新建层数
            SkipListNode phead = initial();
            phead.down = head;
            phead.next.down = tail;
            head = phead;
            tail = phead.next;
            insertKey = head;
            SkipListlevel++;
        }
        //向下补全所有的插入节点
        while (insertKey != null) {
    
    
            while (insertKey.next.value < Value) {
    
    
                insertKey = insertKey.next;
            }
            SkipListNode sln = new SkipListNode(Value);
            if (fathersln != null) {
    
    
                fathersln.down = sln;
            }
            sln.next = insertKey.next;
            insertKey.next = sln;
            insertKey = insertKey.down;
            fathersln = sln;
        }
        SkipListnum++;
    }

3、delete()方法

删除方法需要注意三点:1、表层数降级,2、横向查找数值,3、纵向查找数值。
public boolean delete(int x) {
    
    

        //快表层高降级
        if (head.next == tail && head.down != null) {
    
    
            head = head.down;
            tail = tail.down;
            this.SkipListlevel--;
        }
        //注意顺序:先降级、再判断不然会出现最后一个元素删除了,但是表级仍然不为1
		if (!find(x)) {
    
    
			return false;
		}
        SkipListNode point = head;
        SkipListNode prepoint = point;


        //横向查找
        while (point.value != x) {
    
    
            while (point.next.value <= x) {
    
    
                prepoint = point;
                point = point.next;
            }
            if (point.value == x) {
    
    
                break;
            }
            point = point.down;
            prepoint = prepoint.down;
            if (point.equals(prepoint)) {
    
    
                continue;
            }
            while (prepoint.next != point) {
    
    
                prepoint = prepoint.next;
            }
        }
        //纵向删除
        while (prepoint != null) {
    
    

            point = point.down;
            prepoint.next = prepoint.next.next;
            prepoint = prepoint.down;
            //循环至最低点结束
            while (prepoint != null && prepoint.next != point) {
    
    
                prepoint = prepoint.next;
            }
        }
        this.SkipListnum--;
        return true;
    }

4、find()方法

public boolean find(int x) {
    
    

        SkipListNode point = head;
        while (true) {
    
    
            while (point.next.value <= x) {
    
    
                point = point.next;
            }
            if (point.value == x) {
    
    
                return true;
            }
            if (point.down == null) {
    
    //到达最底层
                return false;
            }
            point = point.down;
        }
    }

5、print()方法

输出的是链表的从上到下整体结构。
public void print() {
    
    //输出链表的整体结构
        SkipListNode point;
        SkipListNode headpoint = head;
        while (headpoint != null) {
    
    
            point = headpoint;
            while (point.value != TAIL_KEY) {
    
    
                if (point.value != HEAD_KEY) {
    
    
                    System.out.print(point.value + "-");
                }
                point = point.next;
            }
            System.out.println();
            headpoint = headpoint.down;
        }
    }

猜你喜欢

转载自blog.csdn.net/ljfirst/article/details/104504763