2024年Redis必知必会之zset底层—Skip List跳跃列表(面试加分项),2024年最新hashmap工作原理面试

最后

我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。

不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?

我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。

我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:

部分目录截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

C(k)=(1-p)×(情况b的查找长度) + p×(情况c的查找长度)

C(k)=(1-p)(C(k)+1) + p(C(k-1)+1)

C(k)=1/p+C(k-1)

C(k)=k/p

上面推演的结果可知,爬升k个level的预期长度为k/p,爬升一个level的长度为1/p。

由于MaxLevel = L(n), C(k) = k / p,因此期望值为:(L(n) – 1) / p;将L(n) = log(1/p)^n 代入可得:(log(1/p)^n - 1) / p;将p = 1 / 2 代入可得:2 * log2^n - 2,即O(logn)的时间复杂度。

三、Skip List特性及其实现

2.1 Skip List特性

Skip List跳跃列表通常具有如下这些特性

  1. Skip List包含多个层,每层称为一个level,level从0开始递增

  2. Skip List 0层,也就是最底层,应该包含所有的元素

  3. 每一个level/层都是一个有序的列表

  4. level小的层包含level大的层的元素,也就是说元素A在X层出现,那么 想X>Z>=0的level/层都应该包含元素A

  5. 每个节点元素由节点key、节点value和指向当前节点所在level的指针数组组成

2.2 Skip List查询

假设初始Skip List跳跃列表中已经存在这些元素,他们分布的结构如下所示:

在这里插入图片描述

此时查询节点88,它的查询路线如下所示:

在这里插入图片描述

  1. 从Skip List跳跃列表最顶层level3开始,往后查询到10 < 88 && 后续节点值为null && 存在下层level2

  2. level2 10往后遍历,27 < 88 && 后续节点值为null && 存在下层level1

  3. level1 27往后遍历,88 = 88,查询命中

2.3 Skip List插入

Skip List的初始结构与2.3中的初始结构一致,此时假设插入的新节点元素值为90,插入路线如下所示:

  1. 查询插入位置,与Skip List查询方式一致,这里需要查询的是第一个比90大的节点位置,插入在这个节点的前面, 88 < 90 < 100

  2. 构造一个新的节点Node(90),为插入的节点Node(90)计算一个随机level,这里假设计算的是1,这个level时随机计算的,可能时1、2、3、4…均有可能,level越大的可能越小,主要看随机因子x ,层数的概率大致计算为 (1/x)^level ,如果level大于当前的最大level3,需要新增head和tail节点

  3. 节点构造完毕后,需要将其插入列表中,插入十分简单步骤 -> Node(88).next = Node(90); Node(90).prev = Node(80); Node(90).next = Node(100); Node(100).prev = Node(90);

在这里插入图片描述

2.4 Skip List删除

删除的流程就是查询到节点,然后删除,重新将删除节点左右两边的节点以链表的形式组合起来即可,这里不再画图

四、手写实现一个简单Skip List

实现一个Skip List比较简单,主要分为两个步骤:

  1. 定义Skip List的节点Node,节点之间以链表的形式存储,因此节点持有相邻节点的指针,其中prev与next是同一level的前后节点的指针,down与up是同一节点的多个level的上下节点的指针

  2. 定义Skip List的实现类,包含节点的插入、删除、查询,其中查询操作分为升序查询和降序查询(往后和往前查询),这里实现的Skip List默认节点之间的元素是升序链表

3.1 定义Node节点

Node节点类主要包括如下重要属性:

  1. score -> 节点的权重,这个与Redis中的score相同,用来节点元素的排序作用

  2. value -> 节点存储的真实数据,只能存储String类型的数据

  3. prev -> 当前节点的前驱节点,同一level

  4. next -> 当前节点的后继节点,同一level

  5. down -> 当前节点的下层节点,同一节点的不同level

  6. up -> 当前节点的上层节点,同一节点的不同level

package com.liziba.skiplist;

/**

  •  跳表节点元素
    
  • @Author: Liziba

  • @Date: 2021/7/5 21:01

*/

public class Node {

/** 节点的分数值,根据分数值来排序 */

public Double score;

/** 节点存储的真实数据 */

public String value;

/** 当前节点的 前、后、下、上节点的引用 */

public Node prev, next, down, up;

public Node(Double score) {

this.score = score;

prev = next = down = up = null;

}

public Node(Double score, String value) {

this.score = score;

this.value = value;

}

}

3.2 SkipList节点元素的操作类

SkipList主要包括如下重要属性:

  1. head -> SkipList中的头节点的最上层头节点(level最大的层的头节点),这个节点不存储元素,是为了构建列表和查询时做查询起始位置的,具体的结构请看2.3中的结构

  2. tail -> SkipList中的尾节点的最上层尾节点(level最大的层的尾节点),这个节点也不存储元素,是查询某一个level的终止标志

  3. level -> 总层数

  4. size -> Skip List中节点元素的个数

  5. random -> 用于随机计算节点level,如果 random.nextDouble() < 1/2则需要增加当前节点的level,如果当前节点增加的level超过了总的level则需要增加head和tail(总level)

package com.liziba.skiplist;

import java.util.Random;

/**

  •  跳表实现
    
  • @Author: Liziba

*/

public class SkipList {

/** 最上层头节点 */

public Node head;

/** 最上层尾节点 */

public Node tail;

/** 总层数 */

public int level;

/** 元素个数 */

public int size;

public Random random;

public SkipList() {

level = size = 0;

head = new Node(null);

tail = new Node(null);

head.next = tail;

tail.prev = head;

}

/**

  • 查询插入节点的前驱节点位置

  • @param score

  • @return

*/

public Node fidePervNode(Double score) {

Node p = head;

for(;😉 {

// 当前层(level)往后遍历,比较score,如果小于当前值,则往后遍历

while (p.next.value == null && p.prev.score <= score)

p = p.next;

// 遍历最右节点的下一层(level)

if (p.down != null)

p = p.down;

else

break;

}

return p;

}

/**

  • 插入节点,插入位置为fidePervNode(Double score)前面

  • @param score

  • @param value

*/

public void insert(Double score, String value) {

// 当前节点的前置节点

Node preNode = fidePervNode(score);

// 当前新插入的节点

Node curNode = new Node(score, value);

// 分数和值均相等则直接返回

if (curNode.value != null && preNode.value != null && preNode.value.equals(curNode.value)

&& curNode.score.equals(preNode.score)) {

return;

}

preNode.next = curNode;

preNode.next.prev = curNode;

curNode.next = preNode.next;

curNode.prev = preNode;

int curLevel = 0;

while (random.nextDouble() < 1/2) {

// 插入节点层数(level)大于等于层数(level),则新增一层(level)

if (curLevel >= level) {

Node newHead = new Node(null);

Node newTail = new Node(null);

newHead.next = newTail;

newHead.down = head;

newTail.prev = newHead;

newTail.down = tail;

head.up = newHead;

tail.up = newTail;

// 头尾节点指针修改为新的,确保head、tail指针一直是最上层的头尾节点

head = newHead;

tail = newTail;

++level;

}

while (preNode.up == null)

preNode = preNode.prev;

preNode = preNode.up;

Node copy = new Node(null);

copy.prev = preNode;

copy.next = preNode.next;

preNode.next.prev = copy;

preNode.next = copy;

copy.down = curNode;

curNode.up = copy;

curNode = copy;

++curLevel;

}

++size;

}

/**

  • 查询指定score的节点元素

  • @param score

  • @return

*/

public Node search(double score) {

Node p = head;

for (;😉 {

while (p.next.score != null && p.next.score <= score)

p = p.next;

if (p.down != null)

p = p.down;

else // 遍历到最底层

if (p.score.equals(score))

return p;

return null;

}

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。

[外链图片转存中…(img-NLNPRL67-1715692158260)]

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值