跳表

时间:2022-07-23
本文章向大家介绍跳表,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

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

其的一次增删改查的平均时间复杂度均为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;
    }
}