数据结构面试知识点汇总

剑指Offer题目解析请看这里
LeetCode经典题目解析请看这里
操作系统面试知识点汇总,请看这里
计算机网络面试知识点汇总,请看这里

1. 数组和链表

数组和链表的区别

数组的特点:

数组是将元素在内存中连续存放, 由于每个元素占用内存相同, 可以通过下标迅速访问数组中任何元素。

数组的插入数据和删除数据效率低, 插入数据时, 这个位置后面的数据在内存中都要向后移。 删除数据时, 这个数据后面的数据都要往前移动。

数组需要预留空间, 在使用前要先申请占内存的大小,可能会浪费内存空间。 并且数组不利于扩展, 数组定义的空间不够时要重新定义数组。

链表的特点:

链表中的元素在内存中不是顺序存储的, 而是通过存在元素中的指针联系到一起。

如果要访问链表中一个元素,需要从第一个元素开始, 一直找到需要的元素位置。

增加和删除一个元素对于链表数据结构就非常简单了, 只要修改元素中的指针就可以了。链表不用指定大小, 扩展方便, 数据随意增删。

2. 树

2.1 相关定义

:树是由n个节点组成的有限集合,其中:(1)空树n=0;(2)n>0,一个根节点,m个互不相交的有限子集,每个子集是一个子树。

二叉树:二叉树是有限的节点集合:这个集合或者是空,或者由一个根节点和两棵互不相交的二叉树(左右子树)组成。

满二叉树:所有分支节点都有双分节点;叶节点都集中在二叉树的最下一层。

完全二叉树:最多只有下面两层的节点度数小于2;最下面一层的叶节点都依次排列在该层最左边的位置上。

二叉树的存储结构
1.顺序存储结构
对一般二叉树,先用空节点补全成为完全二叉树,然后对节点编号。按照一定的次序顺序,存储到一片连续的存储单元中。
特点:对完全二叉树适合,对一般二叉树浪费空间;容易找节点的双亲和孩子。

2.链式存储结构
每个节点含有3个域:一个数据元素域,一个左孩子指针域,一个右孩子指针域。
特点:节省存储空间,与树形无关,只与节点数有关;找一个节点的孩子容易,但找双亲不方便。

2.2 二叉排序树

二叉排序树或者是空树,或者是满足如下性质(BST性质)的二叉树:
(1)若它的左子树非空,则左子树所有节点值(指关键字值)均小于根节点值;
(2)若它的右子树非空,则右子树上所有节点值均大于根节点值;
(3)左、右子树本身各又是一棵二叉排序树。
注意:二叉排序树中没有相同关键字的节点。其中序序列是一个递增的有序序列。

常用操作:插入节点,创建二叉排序树,删除节点。

2.2.1 平衡二叉树

平衡二叉树是一种特殊的二叉排序树,其任意节点的左右子树高度差的绝对值不超过1。将节点左子树的高度减去右子树的高度称为平衡因子。

在平衡二叉树中插入新节点后,会破坏平衡性,解决方法为调整,包括:
(1)LL平衡旋转(右单旋转):在节点A的左孩子(L)的左子树(L)上插入了新节点;
(2)RR平衡旋转(左单旋转):在节点A的右孩子(R)的右子树(R)上插入了新节点;
(3)LR平衡旋转(先左后右双旋转):在A的左孩子(L)的右子树(R)上插入新节点;
(4)RL平衡旋转(先右后左双旋转):在A的右孩子(R)的左子树(L)上插入新节点。

在最坏的情况下,普通二叉排序树的查找长度为O(n),平衡二叉树为O(logn)。

2.2.2 红黑树

红黑树是一种二叉查找树, 但在每个节点增加一个存储位表示节点的颜色, 而且满足:
(1)每个节点非红即黑
(2)根节点和所有叶节点(叶节点即树尾端 NULL 节点)都是黑的;
(3)如果一个节点是红色的, 则它的子节点必须是黑色的。
(4)对于任意节点而言, 其到叶子点的每条路径都包含相同数目的黑节点;

通过对任何一条从根到叶子的路径上各个节点着色的方式的限制, 红黑树确保没有一条路径会比其它路径长出两倍, 因此, 红黑树是一种弱平衡二叉树, 相对于要求严格的 AVL 树来说, 它的旋转次数少, 所以对于搜索, 插入, 删除操作较多的情况下, 通常使用红黑树。

红黑树较平衡二叉树的优点:
平衡二叉树树是高度平衡的, 频繁的插入和删除, 会引起频繁的 rebalance, 导致效率下降; 红黑树不是高度平衡的, 算是一种折中, 插入最多两次旋转, 删除最多三次旋转。所以红黑树在查找、 插入和删除的性能都是 O(logn), 且性能稳定, 所以 STL 里面很多结构包括 map 底层实现都是使用的红黑树。

红黑树旋转
红黑树的旋转是一种能保持二叉搜索树性质的搜索树局部操作。 有左旋和右旋两种旋转, 通过改变树中某些结点的颜色以及指针结构来保持对红黑树进行插入和删除操作后的红黑性质。

map 和 unordered_map 优点和缺点
对于 map, 其底层是基于红黑树实现的, 优点如下:

1)有序性, 这是 map 结构最大的优点, 其元素的有序性在很多应用中都会简化很多的操作
2)map 的查找、 删除、 增加等一系列操作时间复杂度稳定, 都为 logn

缺点如下:
查找、 删除、 增加等操作平均时间复杂度较慢, 与 n 相关

对于 unordered_map 来说, 其底层是一个哈希表, 优点如下:

查找、 删除、 添加的速度快, 时间复杂度为常数级 O©

缺点如下:
因为 unordered_map 内部基于哈希表, 以(key,value) 对的形式存储, 因此空间占用率高
Unordered_map 的查找、 删除、 添加的时间复杂度不稳定, 平均为 O©, 取决于哈希函数。极端情况下可能为 O(n)

2.3 B树与B+树

B树
B树又称多路平衡查找树,B树中所有节点的孩子个数的最大值称为B树的阶。B树是一种组织和维护外存文件系统非常有效的数据结构。

一棵m阶B树或者是空树,或者是满足如下要求的m叉树:
(1)树中每个节点至多有m个孩子节点,即至多含有m-1个关键字;
(2)若根节点不是叶子节点,则根节点至少有两个孩子节点;
(3)除根节点外,其他非叶子节点至少有⌈m/2⌉个孩子节点;
(4)每个节点中按关键字大小顺序排列;
(5)所有外部节点都在同一层。(B树是所有节点的平衡因子均等于0的多路查找树)

B+树
B+是一种多路搜索树, 主要为磁盘或其他直接存取辅助设备而设计的一种平衡查找树, 在B+树中, 每个节点的可以有多个孩子, 并且按照关键字大小有序排列。 所有记录节点都是按照键值的大小顺序存放在同一层的叶节点中。

B+树的定义基本与B-树相同,除了:

非叶子结点的子树指针与关键字个数相同;为所有叶子结点增加一个链指针;所有关键字都在叶子结点出现;

3. 哈希

哈希表
Hash表也称散列表,Hash表是一种根据关键字值(key - value)而直接进行访问的数据结构。它基于数组,通过把关键字映射到数组的某个下标来加快查找速度,在哈希表中查找某个关键字,时间级为O(1)。

3.1 哈希函数

哈希函数就是将关键字转换为数组的下标的转换函数,转换的过程称为哈希化。哈希函数把一个大范围的数字转化成一个小范围的数字,这个小范围的数对应着数组的下标。使用哈希函数向数组插入数据后,这个数组就是哈希表。

1.除余法
选择一个适当的正整数 p ,令 h(k ) = k mod p ,这里, p 如果选取的是比较大的素数,效果比较好。

2.平方散列法
求index是非常频繁的操作,而乘法的运算要比除法省时,所以我们考虑把除法换成乘法和一个位移操作。公式:index = (value * value) >> 28 ( 右移,除以2^28。记法:左移变大,是乘。右移变小,是除)

3.数字选择法
如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。

3.2 解决哈希冲突的方法

哈希冲突
当哈希表关键字集合很大时, 关键字值不同的元素可能会映象到哈希表的同一地址上, 这样的现象称为哈希冲突。

1.开放定址法
当发生地址冲突时, 按照某种方法继续探测哈希表中的其他存储单元, 直到找到空位置为止。常用探查序列的方法有:
线性探测:顺序查看表中下一单元, 直到找出一个空单元或查遍全表(x+1,x+2,x+3…);
二次探测:在表的左右进行跳跃式探测, 比较灵活(x+1,x-1,x+4,x-4,x+9,x-9…);
伪随机探测:建立一个伪随机数发生器, 生成一个位随机序列, 并给定一个随机数做起点, 每次去加上这个伪随机数。
二次探测可以防止聚集的产生,但是也会导致二次聚集的产生。

2.再哈希法
当发生哈希冲突时使用另一个哈希函数计算地址值, 直到冲突不再发生。 这种方法不易产生聚集, 但是增加计算时间, 同时需要准备许多哈希函数。

3.链地址法
将所有哈希值相同的 Key 通过链表存储。 key 按顺序插入到链表中。哈希数组的每个元素都是一个单链表的头节点, 链表是用来解决冲突的, 如果不同的 key 映射到了数组的同一位置处, 就将其放入单链表中, 即链接在桶后。

4.建立公共溢出区
建立一个公共溢出区域, 把 hash 冲突的元素都放在该溢出区里。 查找时, 如果发现 hash表中对应桶里存在其他元素, 还需要在公共溢出区里再次进行查找。

3.3 rehash

C++的 hash 表中有一个负载因子 loadFactor, 当 loadFactor<=1 时, hash 表查找的期望复杂度为 O(1). 因此, 每次往 hash 表中添加元素时, 我们必须保证是在 loadFactor <1 的情况下, 才能够添加。

因此, 当 Hash 表中 loadFactor==1 时,Hash 就需要进行 rehash。 rehash 过程中,会模仿C++的 vector 扩容方式,开辟一个原来桶数组的两倍空间,然后把原来的桶数组中元素全部重新哈希到新的桶数组中。

参考文献
牛客校招面试题
面试高频-哈希表

猜你喜欢

转载自blog.csdn.net/qq_42820853/article/details/108117888