数组与链表区别
数组和链表是两种常见的数据结构,它们有几个关键区别:
-
存储方式:
- 数组:在内存中连续存储一组相同类型的元素,这使得数组支持随机访问,即可以通过索引直接访问任何位置的元素。
- 链表:由节点组成,每个节点包含数据和指向下一个节点的指针(单向链表)或同时包含指向前一个节点和下一个节点的指针(双向链表)。链表的节点可以在内存中是不连续的,这使得插入和删除操作更加高效,但是访问节点时需要按照顺序一个接一个地遍历。
-
插入和删除操作的效率:
- 数组:在数组中插入或删除元素时,需要移动后续元素来保持连续性,这可能需要 O(n) 的时间复杂度,其中 n 是数组的长度。
- 链表:链表的插入和删除操作通常只涉及节点之间的指针调整,因此插入和删除操作的时间复杂度通常是 O(1),即常量时间。
-
随机访问的效率:
- 数组:由于数组的元素在内存中是连续存储的,因此可以通过索引在常量时间内进行随机访问。
- 链表:由于链表的节点不一定是连续存储的,因此不能直接通过索引进行随机访问,必须按顺序遍历链表来访问特定位置的元素,这使得随机访问的效率较低。
-
空间复杂度:
- 数组:通常情况下,数组的空间分配是静态的,一旦创建后大小固定。如果需要更多的空间,可能需要重新分配内存并复制数据,这可能会导致额外的开销。
- 链表:链表的空间分配是动态的,每个节点可以在运行时动态分配。这意味着链表可以灵活地扩展或收缩,只要内存可用。
综上所述,数组适合需要频繁随机访问元素的情况,而链表适合频繁插入和删除操作的场景。
栈
堆
树
前序遍历
中序遍历
二叉树
搜索二叉树(二叉查找树)
二叉查找树(BST:Binary Search Tree)是一种特殊的二叉树,它改善了二叉树节点查找的效率。二叉查找树有以下性质:
对于任意一个节点 n,
- 其左子树(left subtree)下的每个后代节点(descendant node)的值都小于节点 n 的值;
- 其右子树(right subtree)下的每个后代节点的值都大于节点 n 的值。
红黑树
树的遍历
四种主要的遍历思想为:
-
前序遍历:根结点 —> 左子树 —> 右子树
-
中序遍历:左子树—> 根结点 —> 右子树
-
后序遍历:左子树 —> 右子树 —> 根结点
-
层次遍历:只需按层次遍历即可
二分查找
https://mp.weixin.qq.com/s/0Ni9i78uRWjZj4hrhTH03g
二分思想
本质上是折半查找思想,每一轮查找的范围是上一轮的一半,每次查找比较中间元素的值和目标值的大小,比较结论决定取哪一半边作为下一轮的查找范围。当查找范围缩小到空时停止。
局限性:有序、数组
- 有序性:保证每次取半的意义
- 数组:数组寻址的复杂度是 O(1)
如果是用链表存储的一串数,二分查找是无意义的。链表的寻址是 O(n)。
跳表
为什么需要跳表
- 链表查询太慢 → O(n)
- 什么算法查询快?二分!→ O(logn)
- 怎么把二分思想在链表中实践?→ 跳表的诞生 → O(?)
二分查找
- 有序
- 数组元素可以随机访问 → 寻址 O(1)
改造链表
- 有序,不在改造范围内
- 加快寻址速度
- 空间换时间 → 上索引
- 构建索引层 → 每 2 个节点提取 1 个到上一级,逐层构建
时间复杂度
- 跳表高度:n, n/2, n/4, n/8, …, n/(2^k) → log2n
- 每层遍历 m 个节点 → O(m*logn)
- 每层索引最多只需要遍历 3 个节点 → O(logn)
空间复杂度 - n/2 + n/4 + n/8 + … + 8 + 4 + 2 → n-2 → O(n)
- 实际操作中,链表中存储的对象元素很大,索引节点只会存- - 关键信息(比如 id)和指针,索引占用的额外空间可以忽略不计
跳表 索引的更新实现(随机 + 概率)
随机建立索引
- 原链表,随机抽 n/2 个元素建立一级索引
- 一级索引,随机抽 n/4 个元素建立二级索引
- 以此类推,元素越多,索引分布越均匀
概率(随机)更新索引
- 设计一个特别的函数,按照概率返回 [1, MAX_LEVEL] 之间的整数
- 1/2 的概率返回 1,不需要更新索引,直接在在原链表中插入元素
- 1/4 的概率返回 2,需要为新元素建立一级索引
- 以此类推,无论插入多少个元素,各级索引的节点数依然是 n/2, n/4, ….
Redis 源码
跳表相关问题
-
是否了解跳表?
-
Redis 为什么用跳表?
-
Redis 为什么用跳表?为什么不用红黑树?
-
实现跳表(变态!)
-
二分思想在链表的应用,提升查找效率,O(n) → O(logn)
-
空间换时间,构建索引层,空间复杂度 O(n)
-
增删改查都很高效 -> O(logn)
加分点
- 索引层的更新,看过 Redis 跳表的源码,一些侃侃而谈
- 跳表和红黑树的对比
- 跳表能按照指定的区间查数据
- 红黑树实现起来很复杂,跳表实现起来不容易出错(不给自己挖坑)
- 高效查找、动态插入删除,都能做到 O(logn)
- 共同点
- 区别
延伸点
- 对比红黑树
- 对比散列表
- Redis 的其他特性,除了跳表,我还知道 xxxx