数据结构
线性表
单向链表,循环链表,双向链表
编程题
- 合并两个有序链表
- 反转链表
- 判断单链表是否是循环链表(双指针,1步,2步)
数组
和链表区别
- 数组元素在内存中连续存放,链表非顺序存储
- 数组随机访问性强,链表无法随机访问
- 数组查找快,链表查找慢(需要从头遍历)
- 数组插入删除效率低
- 数组必须有足够连续内存空间,需要预留空间
- 数组大小固定
栈 队列
编程题
- 用两个栈实现一个队列
- 用两个队列实现一个栈
哈希
冲突
两个关键字被映射到同一个slot(或者桶bucket)
冲突解决方法
开放寻址法
装载因子<1
发生冲突,以当前地址为基准,根据再寻址方法寻找下一个地址,若冲突则继续。
- 线性探查
- 二次探查:
- 双重散列: 加另一个哈希函数
链表法(拉链法)
冲突位置为链表
公共溢出区
冲突的放入溢出区
负载因子和 rehash
负载因子
对于C++,因子==1时,将长度扩展为两倍
C++中的hash_map和map
hash_map:哈希表实现,查找平均O(1)极端O(N),无序,占用内存大
map:红黑树实现,查找O(lgN),有序,占用内存小
树
编程题
堆
大(小)根堆
- 完全二叉树(除最后一层均满,最后一层均靠左排列)
- 每个节点都大于等于(小于等于)子节点
一些性质
- 适合用数组存储,只需下标/2即可找到其父节点
堆操作
插入
O(log n)
自下而上堆化
- 插入最后一层最左边的空位
- 和父节点(n/2)对比,不满足大小关系就交换,一直到满足
删除堆顶元素
O(log n)
自上而下堆化
- 删除,将末尾元素放置顶
- 和子节点(2n,2n+1)中较小的(小根堆)交换,一直下去
建堆
等于依次插入
每个元素最多比较次数和高度成正比
堆排序
- 删除堆顶,堆化
- 重复
时间复杂度,空间复杂度
题目
- 100万个数中找到最大的前K个数(内存放不下这么多数)
建立K个元素的小根堆,然后剩下的依次和堆顶元素比较,进行删除替换
- 堆排序
- 求中位数
用一个最大堆存放比中位数小(或等于)的元素,用一个最小堆存放比中位数大(或等于)的元素
插入元素时,维持两个堆数量差小于等于1,且大根堆堆顶元素<小根堆堆顶元素
平衡搜索树
平衡二叉(AVL)树
所有节点的左右子树高度之差的绝对值不超过1
红黑树
弱平衡二叉树,最长路径不超过最短路径的2倍
高度 O(lgN)
性质
- 节点是红色或者黑色
- 根节点是黑色
- 每个叶子的节点都是黑色的空节点(NULL)
- 每个红色节点的两个子节点都是黑色的
- 从任意节点到其每个叶子的所有路径都包含相同的黑色节点
自平衡操作
- 左旋
- 右子节点变为父节点,右子节点的左节点变为右子节点 只影响右子树
- 右旋
- 左子节点变为父节点,左子节点的右节点变为左子节点 只影响左子树
- 变色
插入
插入 自平衡
空树
插入,设为黑色
插入节点的key已存在
更新节点值
插入节点的父节点为黑色
直接插入并为红色
插入节点的父节点为红色
叔叔节点存在且为红色
父节点,叔叔节点设置为黑色
祖父节点设置为红色
祖父节点作为当前插入节点
- 若PP的父节点为红色,将PP作为插入节点继续自平衡
- 若PP为根节点,需要变成黑色,此时是==唯一一种增加黑色层数的场景==
叔叔节点不存在(若黑色则必为叶子Nil,否则黑色数量不平衡)
删除
太复杂
面试题
应用
c++ STL中的 map和set
数据结构怎么定义
key,data,color,*left,*right,*parent
性质
5点
相对于二叉搜索,AVL的优点
不是完全平衡,但搜索也能O(lgN)
插入删除比AVL简单
和哈希表对比
hash查找快O(1)
hash无序,红黑有序
红黑占用内存小,hash需要事先分配足够内存
B树 B+树
B树
磁盘操作,访问一棵树的任意节点都要一次磁盘访问,B树为了减小磁盘访问
高度
节点可以有很多子女
特性
对于一棵m-阶B树,若非空
- 根节点至少有2个孩子
- 根节点外,所有内部节点至少有个孩子,至多m个孩子(m阶)
- 所有叶子在同一层
数据结构定义
keyNum,*parent,*ptr,*key
B+树
特性
- 所有数据都保存在叶子
- 叶子节点之间形成有序链表
- 内部节点保存所有子节点的最大(小)值
和B树区别
- 内部节点只存储索引,而B树存储数据,所以可以容纳更多索引,高度更矮
- 叶子间形成链表,便于范围查找(如查找[3,11],找到3即可通过链表在叶子之间查找)
- 有m个孩子的节点存储m个索引,而B树为m-1
优势
- 单一节点存储更多元素,查找次数更少
- 所有查询都要查找到叶子,性能稳定
- 叶子形成有序链表,便于范围查询