数据结构
红黑树
红黑树的一种特殊的AVL(平衡二叉树),平衡二叉树插入删除节点时要维持特性而进行大量的旋转操作,因此会降低性能,而红黑树正是牺牲了严格的平衡条件,提升了性能。红黑树插入不平衡时,最多进行两次旋,删除不平衡,最多进行三次旋转。
具有的特性:
-
不是黑色,就是红色(非黑即红)
-
根节点为黑色
-
叶子节点为黑色
-
如果一个节点为红色,则其两个子节点必须是黑色的(根到叶子的所有路径,不可能存在两个连续的红色节点)
-
根节点到叶子节点的所有路径,都包含相同数目的黑色节点(相同的黑色高度)
进程的调度方式
内存碎片管理(内存与红黑树有关系)
网卡的数据
B树与B+树
他们两的区别?
B树又叫做多路平衡查找树,它具有的最大子节点数叫做阶。通常用m表示,满足以下条件:
- 树中节点至多有m棵子树,最多m-1个关键字
- 根节点如果不是终端节点的话,至少有两棵子树
- 除根节点外的所有非子节点至少有M/2个子树,最多有M个子树,关键字的范围为M/2-1到M-1。
- 节点的关键字之间通常含有指向子节点的指针
- 叶子节点出现在同一层,且不带信息
- 所有节点平衡因子均为0
B树的查找过程分为两步,第一步查找节点,这是在磁盘进行的,然后将节点的关键字读入内存,通过折半查找的方法,进行查找,如果查找不到,再到对应的指针节点去查找,如果找到叶子节点,说明不存在。
B+树
- 树中节点至多有M棵子树,M个关键字
- 关键字 (子树) 个数的范围为M/2-M, 关键字也是这个范围
- 根节点还是至少有两个子树
区别:
- B+树的非叶子节点只是起一个索引的作用,非叶子节点每个索引项只包含有对应的子树的最大关键字,以及指向该子树的指针,不包含关键字的存储地址。所以叶子节点中包含了关键字,即非叶子节点中的关键字也会出现在叶子节点中;而B树中,叶子节点包含的关键字和其他节点包含的关键字
最小生成树
克鲁斯卡尔:基于贪心思想,具体的一个算法步骤,
将顶点之间的边即权值用一个数组来升序排列,依次从数组中取出变,来构成树,如果取出的边形成了环,就舍去该边,如果未形成环,就加入,直到具有n-1条边,生成树就完成了。
这种算法适合于边比较少的稀疏图。
prim法则:也是基于贪心思想,具体的算法思想如下:
随机选取一个点,选择与该点距离最近的点,加入到集合中,然后再寻找与该集合最近的点,加入到集合中,不断寻找,直到集合中的点的个数为n时,生成树完成了。
这种算法适合于边比较多的稠密图。
二叉排序树与平衡二叉树
二叉排序树是一种方便查找的数据结构,每一个节点的左孩子都小于该节点,右孩子大于该节点。通过中序遍历可以得到一个有序的排列。插入,查找,删除的速度都为Log2N.
平衡二叉树,是指节点的左右孩子的高度差的绝对值不能超过1。平衡二叉树的创建就是为了维护树形结构,提升查找速度。防止排序树退化成链表。
AVL调整的过程:
删除节点,如果是叶子节点就直接删除,如果只有一个子树,就直接用子树替换,如果既有左孩子又有右孩子,就选择高的子树的叶子节点与被删节点交换后删除
都是先自上而下判断平衡因子,找到不平衡子树,然后从根节点起选3个节点进行调整
二叉排序树的几种调整方法:
左旋LL,,在右孩子的右子树上插入节点,导致的不平衡。
- 右旋RR,在左孩子的左子树上插入节点,导致的不平衡。
- 左右LR,在左孩子的右子树上插入节点,导致的不平衡。
- 右左RL,在右孩子的左子树上插入节点,导致的不平衡。
8大排序算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mMuhTmim-1661000957154)(%E7%BA%A2%E9%BB%91%E6%A0%91.assets/image-20220722110258118.png)]
插入排序:
- 直接插入排序
平均时间复杂度为O(N2),思想是将一个待排序的元素插入到前面已经排好序的序列中。当元素本身有序时,时间复杂度最低为O(N)
- 希尔排序:
其实是一种特殊的插入排序,它通过设置增量,将元素分为多个组,每组进行排序,然后再将增量减半,再分组排序,当增量减为1时,就退化成直接插入排序了。
交换排序:
-
冒泡排序
顾名思义,将元素依次比较,不断往后移动,当不满足冒泡条件时,然后确定自己的最终位置,平均时间复杂度也是O(N2), 最低时间复杂度为O(N)。
-
快速 排序
这是一种不稳定的排序方法,平均时间复杂度为(NlogN),基于分治的思想。选择任意元素为枢纽,通过一趟排序将排序列表划分为独立的两个部分,每次排序会确定一个元素的最终位置,使得该元素前面的元素都小于它,后面的元素都大于它。再递归的对子表进行快速排序。
选择排序
-
简单选择排序
也是顾名思义,每次选择最小的数,放到对应的位置,使得数列逐渐变得有序。
-
堆排序
小根堆与,大根堆。小根堆满足所有所有子元素都大于根节点,大根堆与之相反。性质包括堆顶元素为最大值或最小值。用数组存储的堆,满足,左孩子的下标等于2n+1,右孩子的节点下标为2n+2。
那么排序的过程就是将每个子节点与自己的父节点做比较,如果大于父节点就进行交换。并且递归的进行交换操作,直到小于父节点为止。这个过程叫做下坠。
插入元素的过程是将元素放入末尾,再进行下坠。
删除堆顶元素,是将堆顶元素与末尾元素交换,删除完末尾元素后再进行下坠。
归并排序
归并排序的思想对有序表进行合并得到更大的有序表,合并时只需要比较两个或多个有序表中的首个元素,选择最小的元素放入新的有序表。归并排序一开始将每个元素看做一个有序表,然后两两合并,通过递归操作最终合并成一个大的有序表。所谓的分治思想就是将一个大的问题递归划分为无数个小的问题进行解决。
桶排序
是基数排序的扩展,是利用分治的思想,将元素放入对应区间的桶里面,然后对每个桶里的元素进行排序,然后再将元素取出。
基数排序
简述二分查找的过程
二分查找又叫做折半查找,适用于有序表,思想是先与中间元素进行比较,如果小于该元素则在前半部分进行查找,如果大于该元素则在后半部分查找。这样不断缩小查找范围,平均查找时间复杂度为log2N
栈和队列有什么区别
栈是拥有一个头顶指针的线性结构(有序数据项的集合)。特点是在一头插入元素,一头删除元素,先进后出的。
队列是拥有一个队头指针和队尾指针的线性结构。特点是一头插入元素,另一头删除元素,先进先出。
通过什么数据结构,通过学号查找姓名速度最快
通过hash表,时间复杂度为O(1),因为hash表的存储原理,是将key通过散列算法得到一个Hash Code,也就是存储的下标值,然后再将value放入对应的位置进行存储。hash表本质是一个数组。
对很多数据进行排序,使用
使用折半查找时若对数据进行删除和插入,需要移动位置,有什么更高效率的方式么?
折半查找实际上是构建了一颗二叉排序树,因此我们可以使用二叉排序树来存储该序列,这样增加删除的时候就会方便很多。
对百万考生的成绩进行排序
使用桶排序,缺点就是额外的空间消耗量大,但是时间复杂度低,O(N+M),n为元素个数,M为桶的个数,时间复杂度为O(k*N),基数排序的时间复杂度也是这个
如果是使用一个性能很高的计算机从平均时间复杂度来看的话,还是堆排序和桶排序以及归并排序的时间复杂度要低一些。
如果是自己设计的话?
简述KMP算法
满二叉树:顾名思义,除了叶子节点没有子节点外,每一层都有两个子节点
对于有序的快速排序,会退化成一棵单枝的树,时间复杂度为O(N2)。
什么是数据结构,数据结构与算法的关系(不熟)
程序=数据结构+算法。
数据结构与算法的关系是:数据结构是底层,算法是高层,数据结构为算法服务,算法围绕数据结构进行操作。算法在特定的应用场景选择合适的数据结构,比如在对数据删除与插入操作较多的场景,适合用链表这一数据结构,当对数据读取次数较多的情况,适合用数组这一数据结构。
数据结构:数据不是独立存在的,数据之间存在关系,这种关系被称为结构。数据结构包括三方面内容:逻辑结构,存储结构、数据的运算。算法的设计取决于所选定的逻辑结构,算法的实现依赖于所采用的存储结构。
数据结构在计算机网络和操作系统中有什么应用?
- 操作系统:进程调度应用到了栈和队列、文件管理应用到了树、图和排序、信息存储应用到了线性表和串。
- 计算机网络:分组与转发用到了队列与图。DNS解析应用到了树,信息表示用到了线性表
链表与数组有什么异同
相同点:两者都可以表示线性结构,链表是通过上一个节点指向下一个节点,而数组是在内存中顺序存储。
不同点:数组中存储的数据的物理地址是连续的,而链表中的存储的数据的物理位置并不是连续的,而是通过每个节点的指针指向下一个节点。链表适合于对数据进行插入和删除较多的情况,数组适合对数据进行查找较多的情况。
什么是结构体?
通过基本的数据结构组合而成的一种自定义的数据结构
二叉树和结点只有两个孩子的树有什么区别?
二叉树中每个元素的子树都是有序的,也就是说,可以用左、右子树来区别。树的两个孩子没有左右之分。
递归求阶乘
int fact(int n){
if(n==1){
return 1;
}else{
return n*fact(n-1);
}
}
对树进行层次遍历
可以借助一个队列进行遍历,即广度优先搜索遍历,将根节点放入队列中,然后出队列,每次出队列后需要将孩子孩子节点依次放入队列中,这样直到所有节点都出队列,并且没有节点入队列后,完成了层次遍历
数字数组排序使用什么方法
- 若 n 比较小,采用直接插入排序或者简单选择排序。
- 若数组基本有序,采用直接插入或者冒泡排序。
- 若 n 比较大,采用快速排序、堆排序和归并排序。
- 若 n 很大,采用基数排序。
什么是哈夫曼树?有什么作用
哈夫曼树:由n个带权叶子节点构成的所有的二叉树中带权路径长度最短的二叉树
哈夫曼编码:对一棵具有n个叶子节点的哈夫曼树,若对树中的每个左分支赋予0,右分支赋予1,则从根到每个叶子的通路上,分别构成了一个二进制串,该二进制串就被称为哈夫曼编码,哈夫曼编码是最优前缀码(每一个编码都不可能称为其他编码的前缀),常用于做数据压缩处理。
堆的存储结构
物理存储结构是数组,逻辑存储结构是树
Hash冲突以及如何解决
hash冲突是指不同的值映射到相同的值的情况
解决冲突的方法有拉链法与开放地址法
- 开放地址:
- 线性探测:将冲突值放于后面的存储单元
- 平方探测:以冲突值的单元为中心,向两边按平方数的方式查找合适的存储单元
- 再散列法:建立第二个散列函数进行处理
- 拉链法,用指针将冲突值给连接起来
满二叉树的高度:有n个节点,高度为log2(n+1)
单链表寻找中间节点
通过快慢指针,慢指针一次走一个节点,快指针一次走两个节点,当快指针到达末尾的时候,慢指针就到中间位置了
二叉查找树与数组实现的二分查找的区别
二叉查找树是一种动态查找可以便于对数据进行插入和删除,二分查找是静态查找,不便于删除数据
最短路径?
迪杰斯特拉(基于贪心的思想)
需要:visited[]、D[]、path[]、arcs[] [];
分别指访问过的节点,然后剩余的节点,到对应节点的最短路径,用于记录,然后最后用一个二维数组表示节点之间的关系就是路径长度,
通过一轮一轮的比较,每次罗列出能够到达的节点的距离,然后把最短距离所能到达的节点,加入Vist集合中,然后再罗列出与剩余在D中节点的距离,因为每次有新的节点加入,所以路径会更新,但是每次都是选择最短路径的节点加入,直到剩余节点集合为空,而path就保留者最短的路径。
弗洛伊德算法
思想要简单一些,是利用了动态规划的思想,时间复杂度为O(N3)。
通过三层循环来更新一个记录距离的二维数组
转移方程是 a[i] [j]=min(a[i] [j],a[i] [k]+a[k] [i]);
最小生成树:
prim,加点法,先随机选择一个点,放入已访问的节点的集合,再从剩余节点中挑选一个与该节点距离最近也就是权值最小的节点加入,以此类推,直到所有节点都被访问。
克鲁斯卡尔算法(加边法):就是将深林不断合并成树的过程,将图中边由小到大进行排序,选择最小的边,并将其对应的节点加入访问过的节点集合中。直到所有的节点都加入到集合中,但这个过程中需要判断加入的边是否构成环,这就需要判断,通过并查集的方法,及判断节点是否已经在同一颗树中,及他们的祖宗节点是一致的,最初每个节点的祖宗都是自己。
KMP算法
这个算法是一种字符串匹配算法,在一个主串中寻找其包含的字串,
传统暴力求解的方法是给每个串都配置一个指针,从头开始,然后不断移动一一对比,一旦不相同,两个串的指针就返回头部,并且主串前进一步,这个算法的时间复杂度为O(N*M),
KMP, 算法的时间负责度为O(M*N),主要利用的是在匹配的过程中字串的前缀是有重复的地方,利用失败的信息,避免我们每次不用都重复开始匹配,匹配的关键是建立一个数组,每个数组记录,我们匹配失败后就将从哪个位置开始匹配。
而next数组的求解就是动态规划
寻找最长公共先后缀,判断字串的字串,的公共前缀的方法,就是依次比较,如果一样
第 k 个元素的最长前后缀的关键代码:
S表示字符串数组,next表示记录的数组。
next(k) = S[k]==S[0+next[k-1] ] ? next(k-1)+1 : 0