数据结构
- 常见数据结构与算法整理总结(上)
- 线性表(数组,链表),栈与队列,树,二叉树,二叉查找/搜索/排序树,平衡二叉树,红黑树,图。
- 数组和链表
类型 分配内存方式 内存连续性 位置 查找复杂度 插入/删除 数组 静态 连续 - 优点:可以通过下标来访问元素
- 缺点:若插入/删除元素,要将其后所有元素移动位置
栈 O(1) O(n) 链表 动态 不连续 堆 O(n) O(1) - 堆和栈。队列、堆、栈、堆栈的区别?
队列 有一个入口和一个出口,先进先出 栈 就像一个箱子,后进先出 堆 请求操作系统分配内存,效率低
- 数据结构中常用的树
遍历 都是相对于根节点来说的,左节点总是在右节点前面 - 先序:根-左-右
- 中序:左-根-右
- 后序:左-右-根
阶 表示一个节点最多能有多少个子节点,如二叉树的阶数就是2。 度 指父结点下面有几个孩子结点。 满二叉树 一棵深度为k且有2k-1(2的k次幂减1)个结点的二叉树称为满二叉树。 完全二叉树 每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应。 二叉树 每个结点至多只有2棵子树,二叉树的子树有左右之分,次序不能颠倒。 - 类型:满二叉树,完全二叉树。
- 二叉树第 i 层上最多有2i-1个节点
- 深度为 k 的二叉树,最多有2k - 1个节点
- 对于任意一颗二叉树T,如果其终端节点数为n1度数为2的节点数为n2 则 n1 = n2 + 1
- 具有n个节点的完全二叉树深度为[ log2n] + 1;
树和二叉树 - 二叉树每个节点最多有2个子节点,树无限制。
- 二叉树有序。子树分为左子树和右子树,即使某节点只有一棵子树,也要指明是左子树还是右子树。
- 二叉树可以是空。树不能为空,至少有一个节点。
- BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树。AVL 红黑树 B(+)树 跳表 字典树 应用场景及分析。
类型 特点 缺点 应用 二叉查找树 - 节点有序,利于二分查找。
- 左子树小于它的根结点,右子树大于它的根结点,没有值相等的结点。
- 因为不一定平衡,有可能退化成线性表,搜索性能不佳
- 用于搜索
平衡二叉树 - 是二叉查找树
- 严格要求平衡。左子树和右子树的深度之差的绝对值不超过1。
- 查询性能好,维护成本高
- 适合用于插入删除次数比较少,但查找多的情况。
红黑树 - 通过任何一条从根到叶子的简单路径上各个节点的颜色进行约束,确保没有一条路径会比其他路径长2倍,因而是近似平衡的。
- 相对于AVL树来说,旋转保持平衡次数较少。用于搜索时,插入删除次数多的情况下我们使用红黑树来取代AVL。
- 更新数据时,红黑树有个平衡的过程,牵涉到大量的节点,争锁的代价相对高。性能不如跳跃表。
- epoll 在内核中的实现,用红黑树来管理事件块
- nginx中,用红黑树来管理timer等
- Java中的TreeMap实现
B树/B+树 - 多路查找树,分支多层数少,可以有效减少磁盘IO次数。
- 相当于AVL更适合于文件系统。
null - 磁盘IO是非常耗时的,为文件系统、数据库系统而生。
跳跃表 - 更新数据时,跳跃表需要更新的部分少,锁的东西少,性能佳
null - redis sorted set
- 什么是B-Tree。什么是B+Tree。
数据库索引 - 索引存储在磁盘上,数据量比较大时,索引大小也跟着增长,达到几个G。
- 利用索引进行查询时,不可能把索引全部加载到内存,只能逐一加载每个磁盘页,这里的磁盘页就对应索引树的节点。
b树 - 关键字集合分布在整颗树中;
- 任何一个关键字出现且只出现在一个结点中;
- 搜索有可能在非叶子结点结束;
- 其搜索性能等价于在关键字全集内做一次二分查找;
- 自动层次控制
b+树 - 单节点可以存储更多的元素。使得查询磁盘IO次数更少。
- 所有查询都要查找到叶子节点,查询性能稳定。
- 所有叶子节点形成有序链表,便于范围查询。
- 数据结构之哈希表。大多数分布式存储系统要么实现一个分布式Hash表,要么实现分布式B+树。
- Redis为什么用跳表而不用平衡树?
- 跳跃表特点
- 在有序链表的基础上发展而来的多层链表。
- 间隔一定元素增加指针,原链表足够长时,能跳过很多下层节点,大大加快查询速度。
- 上层链表节点个数,是下曾节点个数一半,非常类似一个二分查找。时间复杂度O(log n)。
- 然后对于删除和插入,采用随机生成层数,保证插入或者删除只需要修改结点前后指针,性能优于平衡树。
- 层随机数,不超过一个最大值。
- 对比
类型 元素是否有序 平均复杂度/ 单key查找 范围查找 内存占用 算法实现难度 优点 平衡树 是 O(log n) 复杂 复杂 平衡 哈希表 否。所以不支持范围查询 O(1) 不支持 大(需要事先分配足够的内存存储散列表) 取决于哈希函数 查找快 跳跃表 是 O(log n) 简单 灵活 简单 更新数据时,跳跃表需要更新的部分少,锁的东西少 - sorted set数据结构实现
- 数据较少时,由ziplist实现。
- 数据较多时,由dict + skiplist实现。dict维护数据到分数的对应,skiplist用来根据分数范围查找数据。
- 分数相同时,根据数据内容进行字典排序。
- 第一层量表不是单向链表,而是一个双向链表,为了方便以倒序方式获取一个范围内元素。只有第一层链表时双向链表。
- 跳跃表特点
算法
- 常见数据结构与算法整理总结(下)
- 常见排序算法。八大排序算法。
大类 小类 算法 选择 选择 - 每个数(当前数)和之后的每个数比较,每次记下小数的位置,然后交换当前数和最小数的位置
堆排序 特点: - 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
- 或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
- 若升序,构建大顶堆,
- 堆顶元素和末尾元素交换,末尾表示排好的序列。
- 对前面元素重复前2步,
- 直到整个序列有序。
插入 直接插入 - 从第一个数开始,之后每个数插入前面假定排好序的位置。根据如何确定插入位置,分为直接插入(遍历)和二分查找插入
希尔排序 改进的插入排序。按步长进行分组,每组内的元素进行插入排序。逐步减小步长,直到为1,即为普通的插入排序的情况。前面的步骤可以将所有元素变成基本是排好序的,较少要移动的元素。 交换 冒泡 - 每个数与下一个相邻的数比较,大的往后放。每轮下来,产生一个最大数放在最后位置(前插在后面最大数序列中)
快速 - 选择基准数(第一个也可以),比它大的放右边容器,小的放左边,左边和右边数做同样处理(递归),然后合并左、基准数、右边数即得
归并排序 - 分而治之
基数排序 按个位大小排序、按十位大小排序、百位...... 总结 平均时间复杂度:nlogn:快速、堆、归并。关键字随机分布时,快速排序平均时间最短 - 查找
顺序查找 二分查找 - 两个名字(二分查找、折半查找)
- 优点三个(比较次数少、查找速度快、平均性能好)
- 缺点两个(待查找表为有序表、插入删除困难),用数字表示就是232。8
- 初始传入待查找数组的左右边界分别为0和数组长度-1。
- 边界检查:如果左边界>右边界,则返回“找不到该数”,退出,
- 将待查找数与中间位置(向下取整,只要能定出界限,无所谓多1还是少1)数比较,
- 如果比中间位置数大,则继续在中间位置右边的数中查找(执行递归,左边界变成中间位置+1),
- 如果比中间位置数小,则继续在中间位置左边的数中查找(执行递归,右边界变成中间位置-1),
- 否则相等的话,表示找到待查找的数,退出。
- 代码实现:递归与非递归实现
分块查找 是前两者结合,分块有序,块内无序,插入和删除无需大量移动记录 散列表 - 代码:常用排序和两个查找。
- 实战:
- 剑指offer中的算法题。数据结构和算法面试题。
- 百钱买鸡。满足一定条件,数据在一定范围。制定for循环,剩下的事交给计算机。制定等式,接下来思路,让变量变化,所以制定for循环。
- 约瑟夫环。递归算法和公式法。
- 斐波那契数列。递归法和迭代法。
- TopK。bitmap计数,求TopK最快的方法?
常规 - 全局排序,再取前k个。复杂度:n*lg(n)(快速排序)。
- 局部排序,只对最大k个排序。使用冒泡排出k个最大数。复杂度:n*k(冒泡:n*n)。
- 堆排序。只找到TopK,不排序TopK。复杂度:n*lg(k)(堆排序:n*lg(n))。
- 分治,每个分支都要递归,例如快速排序。不懂。
- 减治,只要递归一个分支,例如二分查找。不懂。
- 随机选择+partition。不懂。
bitmap 元素没有重复 - 相应位的元素存在则存1,否则存0。
- 扫描一次所有n个元素,生成bitmap,时间复杂度O(n)。
- 生成后,去TopK只需要找到最高位的k个bit即可。
- 每个元素的一个bit变成一个计数,
- 找TopK的过程:从高位往低位扫描,得到count之和等于k,对应的bit就是TopK所求。
- 全排列算法。
- 天平称球。
- 二进制和三进制的妙用。
- PHP实现的算法合集。