加入收藏 | 设为首页 | 会员中心 | 我要投稿 保山站长网 (https://www.0875zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

太刺激了,面试官让我手写跳表,而我用两种实现措施吊打了TA!

发布时间:2021-06-04 17:29:34 所属栏目:大数据 来源:互联网
导读:通用实现 通用实现主要参考JDK中的ConcurrentSkipListMap,在其基础上,简化,并优化一些东西,学好通用实现也有助于理解JDK中的ConcurrentSkipListMap的源码。 数据结构 首先,我们要定义好实现跳表的数据结构,在通用实现中,将跳表的数据结构分成三种:
通用实现
通用实现主要参考JDK中的ConcurrentSkipListMap,在其基础上,简化,并优化一些东西,学好通用实现也有助于理解JDK中的ConcurrentSkipListMap的源码。
数据结构
首先,我们要定义好实现跳表的数据结构,在通用实现中,将跳表的数据结构分成三种:
普通节点,处于0层的节点,存储数据,典型的单链表结构,包括h0
索引节点,包含着对普通节点的引用,同时增加向右、向下的指针
头索引节点,继承自索引节点,同时,增加所在的层级
类图大概是这样:
 
OK,给出代码如下:
友情提示:代码太长,请横屏观赏~~
/** 
  * 头节点:标记层 
  * @param <T> 
  */ 
private static class HeadIndex<T> extends Index<T> { 
    // 层级 
    int level; 
 
    public HeadIndex(Node<T> node, Index<T> down, Index<T> right, int level) { 
        super(node, down, right); 
        this.level = level; 
    } 
 
/** 
  * 索引节点:引用着真实节点 
  * @param <T> 
  */ 
private static class Index<T> { 
    // 真实节点 
    Node<T> node; 
    // 下指针(第一层的索引实际上是没有下指针的) 
    Index<T> down; 
    // 右指针 
    Index<T> right; 
 
    public Index(Node<T> node, Index<T> down, Index<T> right) { 
        this.node = node; 
        this.down = down; 
        this.right = right; 
    } 
 
/** 
  * 链表中的节点:真正存数据的节点 
  * @param <T> 
  */ 
static class Node<T> { 
    // 节点元素值 
    T value; 
    // 下一个节点 
    Node<T> next; 
 
    public Node(T value, Node<T> next) { 
        this.value = value; 
        this.next = next; 
    } 
 
    @Override 
    public String toString() { 
        return (value==null?"h0":value.toString()) +"->" + (next==null?"null":next.toString()); 
    } 
查找元素
查找元素,是通过头节点,先尽最大努力往右,再往下,再往右,每一层都要尽最大努力往右,直到右边的索引比目标值大为止,到达0层的时候再按照链表的方式来遍历,用图来表示如下:
 
所以,整个过程分成两大步:
寻找目标节点前面最接近的索引对应的节点;
按链表的方式往后遍历;
请注意这里的指针,在索引中叫作right,在链表中叫作next,是不一样的。
这样一分析代码实现起来就比较清晰了:
/** 
  * 查找元素 
  * 先找到前置索引节点,再往后查找 
  * @param value 
  * @return 
  */ 
public T get(T value) { 
    System.out.println("查询元素:u6b22u8fceu5173u6ce8u516cu4f17u53f7u5f64u54e5u8bfbu6e90u7801uff0cu83b7u53d6u66f4u591au67b6u6784u3001u57fau7840u3001u6e90u7801u597du6587uff01"); 
    if (value == null) { 
        throw new NullPointerException(); 
    } 
    Comparator<T> cmp = this.comparator; 
    // 第一大步:先找到前置的索引节点 
    Node<T> preIndexNode = findPreIndexNode(value, true); 
    // 如果要查找的值正好是索引节点 
    if (preIndexNode.value != null && cmp.compare(preIndexNode.value, value) == 0) { 
        return value; 
    } 
    // 第二大步:再按链表的方式查找 
    Node<T> q; 
    Node<T> n; 
    int c; 
    for (q = preIndexNode;;) { 
        n = q.next; 
        c = cmp.compare(n.value, value); 
        // 找到了 
        if (c == 0) { 
            return value; 
        } 
        // 没找到 
        if (c > 0) { 
            return null; 
        } 
        // 看看下一个 
        q = n; 
    } 
 
/** 
  * 
  * @param value 要查找的值 
  * @param contain 是否包含value的索引 
  * @return 
  */ 
private Node<T> findPreIndexNode(T value, boolean contain) { 
    /* 
         * q---->r---->r 
         * |     | 
         * |     | 
         * v     v 
         * d     d 
         * q = query 
         * r = right 
         * d = down 
         */ 
    // 从头节点开始查找,规律是先往右再往下,再往右再往下 
    Index<T> q = this.head; 
    Index<T> r, d; 
    Comparator<T> cmp = this.comparator; 
    for(;;) { 
        r = q.right; 
        if (r != null) { 
            // 包含value的索引,正好有 
            if (contain && cmp.compare(r.node.value, value) == 0) { 
                return r.node; 
            } 
            // 如果右边的节点比value小,则右移 
            if (cmp.compare(r.node.value, value) < 0) { 
                q = r; 
                continue; 
            } 
        } 
        d = q.down; 
        // 如果下面的索引为空了,则返回该节点 
        if (d == null) { 
            return q.node; 
        } 
        // 否则,下移 
        q = d; 
    } 

(编辑:保山站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读