数据结构整体框架:数据结构https://blog.csdn.net/qq_39328436/article/details/114337675
说明:本篇文章是笔者为准备研究生复试以及校招而写,因此主要针对问答,注重理解,而不在于细节计算。涵盖考研范围内的所有知识内容,读者可以先查看上面的链接走一遍数据结构的整体框架结构。其中,重点难点以及易混淆的知识点,笔者用其他近30篇文章进行了详述,在知识点处贴好了链接,有需要的可以查阅。
开始吧 (*^▽^*)
目录
栈的应用★
哈夫曼树★
图的遍历★
图的应用★
内部排序★
散列表:★
线性表
-
数组和链表的区别
-
从结构上:数组和链表都是线性结构,但是,数组是一块连续的内存空间,链表可以是零散的内存空间,用指针连接
-
从操作上:数组能够实现按下标访问和修改,查找效率高,链表的查找效率低;数组的增加和删除需要移动元素,效率低,但是链表的增加和删除只需要修改指针,在o(1)时间内就能完成
-
从存储上:静态数组从栈上开辟空间,链表从堆上开辟空间
-
-
链表种类
-
单链表,双链表,循环链表,静态链表
-
-
栈的应用
-
括号匹配:https://blog.csdn.net/qq_39328436/article/details/108006112
-
表达式求值:https://blog.csdn.net/qq_39328436/article/details/106623999
-
第一步:中缀表达式转后缀表达式,操作符栈中存储未确定顺序的操作符
-
第二步:后缀表达式求值,操作数栈中存储未确定运算顺序的操作数和中间结果
-
-
递归栈
-
非递归形式的树的先序,后序,中序遍历
-
拓扑排序:栈用来存放入度为0的节点
-
-
队列种类
-
顺序结构:用取模运算将静态数组在逻辑上变为环状
-
链式结构:带头结点的单链表,front指针指向收元素,rear指针指向尾元素的下一个位置
-
双端队列
-
-
顺序结构的队列如何区分对空和堆满
-
方法一:牺牲一个存储单元
-
方法二:增加成员size
-
方法三:增加成员tag
-
-
队列的应用
-
树的层次遍历
-
图的广度搜索
-
缓冲区:解决主机和外部设备之间速度不匹配问题
-
-
特殊矩阵的压缩存储
-
含义:对于三角矩阵,对角矩阵,三对角矩阵等稀疏矩阵,如果按照二维数组的模样正常存储会带来空间上的大量浪费,于是利用这些特殊矩阵的形状特征,将有数据的部分压缩存储在一位数组中,减少内存浪费。
-
关键点:找到二维矩阵的下标ij与一维矩阵下标k之间的关系
-
树
-
度为m树与m叉树的区别和联系
-
相同点:任意结点的度不得超过m
-
不同点:
-
度为m的树:至少有一个结点的度为m,整棵树最少有m+1个结点
-
m叉树:允许所有的结点的度都少于m,可以是空树
-
-
-
特殊二叉树有哪些
-
满二叉树:顾名思义
-
完全二叉树:结点顺序和满二叉树一样,但是不一定满
-
二叉排序树:左(右)子树上的所有节点的关键字均小于(大于)根节点
-
平衡二叉树:在二叉排序树的基础上,左右子树深度之差不超过1
-
-
二叉树的性质
-
度为0的结点数=度为2的节点数+1即n0=n2+1
-
第i层有2的i-1次方个结点
-
高度为h的二叉树最多有2的h次方-1个结点(这两点和二进制编码一样呀)
-
-
完全二叉树的性质
-
完全二叉树最多只会有一个度为1的结点
-
n1=1或0;因此知道完全二叉树的节点总数,可以分别推出n0,n1,n2
-
-
判断完全二叉树的某个结点i是否有左右孩子
-
2i<=n? 2i+1<=n?
-
-
完全二叉树的最后一个分支节点序号:n/2向下取整
-
-
二叉树的遍历方式
-
先序遍历:先序遍历对应入栈,中序遍历对应出栈
-
中序遍历:https://blog.csdn.net/qq_39328436/article/details/106762908
-
后序遍历:https://blog.csdn.net/qq_39328436/article/details/106762908
-
三种遍历方式的非递归模板 while ( !s.empty()||p != NULL ) { if (p) { .... } else { ..... } }
-
-
由遍历序列构造二叉树
-
由遍历序列可以确定唯一二叉树的情况:前序+中序;后序+中序;层次+中序(一定要有中序)
-
前序序列和后序序列可以确定任意两个结点直接的父子关系
-
-
线索二叉树
-
为什么要引入线索二叉树?
-
树的前中后遍历序列是线性的,如果想要访问遍历序列中某个结点的前序或者后继,只能从头开始访问,非常不方便。并且链式存储的二叉树上有n+1个空指针,可以利用来实现线索二叉树
-
-
线索二叉树的数据结构
-
增加tag来标记左右指针是指向前驱后继还是孩子
-
-
线索二叉树的线索化
-
线索化指的是在遍历的过程中不断初始化左右孩子指针和左右标记的过程
-
前序遍历会有爱的魔力转圈圈问题
-
-
线索二叉树的遍历:https://blog.csdn.net/qq_39328436/article/details/106746927
-
先序线索二叉树找前驱节点困难,找后继节点简单(除非用三叉链表,找到父指针)
-
后序线索二叉树找后继节点困难,找前驱节点简单(除非用三叉链表,找到父指针)
-
中序线索二叉树找前驱节点,后继节点都很简单
-
遍历中序和先序线索二叉树,不需要栈,遍历后序线索二叉树要用栈
-
-
-
二叉排序树
-
计算题:查找成功和查找失败的ASL
-
二叉排序查找和二分查找的区别
-
二叉排序树查找序列不唯一,二分查找序列唯一:二叉排序将待排序数列构成一棵二叉树,同一批数据按照不同的顺序插入,能构成不同的二叉排序树,从而有不同的搜索序列。二分查找的处理对象是有序数组,因此二分查找的查找序列是唯一的。
-
-
二叉排序树的插入
-
递归实现
-
新插入的结点一定是叶子结点
-
-
二叉排序树的删除
-
删除叶子结点:直接删除
-
删除结点只有左子树或只有右子树:用左子或者右子代替自己
-
删除结点左右子树都有:与中序遍历的直接后继交换位置,并删除自己
-
-
-
平衡二叉树
-
调整不平衡
-
LL:在左孩子的左子树中插入:向右直接转动一次不平衡节点
-
RR:在右孩子的右子树中插入:向左直接转动一次不平衡节点
-
RL:在右孩子的左子树中插入:先向右转一次不平衡结点的孩子,再向左转一次不平衡节点
-
LR:在左孩子的右子树中插入:先向左转一次不平衡结点的孩子,再向右转一次不平衡节点
-
-
平衡二叉树与二叉排序数的区别和联系
-
平衡二叉树是在二叉排序树的基础上,再增加平衡的条件
-
二叉排序树插入新节点,该节点一定会成为叶子结点
-
平衡二叉树插入新节点,该节点不一定是叶子结点(因为导致不平衡之后还会调整)
-
-
-
哈夫曼树
-
定义
-
含n个节点的二叉树中,带权路径最小的那棵二叉树
-
树的带权路径长度:所有叶子结点的带权路径长度之和
-
带权路径长度WPL:从根结点到某结点的路径长度*结点的权值(计算题)
-
-
性质
-
哈夫曼树不唯一,(因为左右孩子的顺序任意),但是WPL唯一
-
初始结点最终都成为了叶子结点,wpl=所有非叶子结点的权值之和
-
不存在度为1的结点
-
-
哈夫曼编码
-
利用哈夫曼树能够设计出长度最短的二进制编码:以使用频率作为编码单元的权值构造一棵哈夫曼树(使用次数最多的符号编码应该最短)。
-
在该哈夫曼树中,向左走为0,向右走为1,完成编码
-
-
哈夫曼树与归并排序
-
在课本中讨论的内部排序中归并组的元素个数总是相等的,所以哪些组先归并哪些组后归并对时间效率并没有影响。然而,外部排序有时将序列分成很多长度不等的组(选择置换排序),这个时候排序次序就会严重影响时间效率,此时就可以按照哈夫曼的思想构造最佳归并树,这棵树的wpl就是最坏情况下比较的总次数。
-
-
图
-
图中的概念辨析
- 有向图 VS 无向图:有向图入度等于出度;无向图度之和=2*边数
- 连通图 VS 强连通图:分别表示有向图/无向图中任意两个结点都互相连通
- 连通分量 VS 强连通分量:分别表示有向图/无向图 中的极大连通子图
- 连通分量 VS 生成树 :连通分量是极大联通子图【记忆法:大连】,生成树是包含全部顶点的极小连通子图
-
图的存储结构
- 1.邻接矩阵:https://blog.csdn.net/qq_39328436/article/details/106947015
- 第i行非0元素个数之和表示i结点的出度,第i列非0元素个数之和表示i结点的入度
- 某个图的邻接矩阵为A(仅限于01邻接矩阵),对A求n次方得到矩阵B,B[i][j】表示从i结点到j结点路径长度为n的路径条数。
- 2.邻接表:https://blog.csdn.net/qq_39328436/article/details/106886278
- 求出度方便(单链表元素个数),求入度很不方便
- 3.十字链表:只针对有向图。解决了邻接表求入度困难的问题。
- 4.邻接多重表:只针对无向图。解决了邻接表存储冗余信息,插入删除慢的问题
- 1.邻接矩阵:https://blog.csdn.net/qq_39328436/article/details/106947015
-
图的遍历
-
图的应用
-
1.最小生成树
- Prim算法:每加入一条边就看成一个统一的整体,每次选取与整体连通的整体之外的最小边
- Kruskal算法:在所有离散的边中选最小的边,一直到所有节点都连通
-
2.最短路径问题
- 单源最短路径:
- BFS实现:https://blog.csdn.net/qq_39328436/article/details/106976629
- Dijkstra算法:(不能求带负权值的,不能求带回路的)
- 初始化:从源点到其他所有节点的直达路径(不直达的话,路径为无穷大)
- 选择:从直达路径中选择一条最短的加入
- 更新:加入的新节点是否使得源点到达其他结点的距离变短,变短了就更新
- Dijkstra与Prim的区别和联系
- Prim只能求最小生成树,Dijkstra只能求最短路径
- 都是从备选的边中挑选一个权值最短的边加入已选集合
- 两者对"权值最短"定义不一样
- Prim是选取距离整个已选边集合最近的结点
- Dijkstra是选取某个单源点的距离
- 各节点之间的最短路径
- Floyd算法:(可以求带负权值的,但是不能去带回路的)
- 用一个矩阵存储路径,每次在直接路径中插入中间结点,如果插入的中间结点使得原路径变短,则更新。
- n次Dijkstra:时间复杂度过高
- Floyd算法:(可以求带负权值的,但是不能去带回路的)
- 单源最短路径:
-
3.拓扑排序
- 用栈实现拓扑排序:https://blog.csdn.net/qq_39328436/article/details/106925057
- 用dfs实现拓扑排序:https://blog.csdn.net/qq_39328436/article/details/107450388
- 用dfs实现逆拓扑排序:https://blog.csdn.net/qq_39328436/article/details/108375625
- 性质:
- 若有向图存在拓扑排序,则邻接矩阵一定是三角矩阵
- 拓扑排序可能不唯一
-
4.关键路径
- 定义:AOE网中,以顶点表示事件,以有向边表示活动,以边上的权值表示活动的开销。从源点到汇点的路径可能有多条,所有路径中最长的那一条是关键路径,关键路径上的活动是关键活动
- 关键路径要解决的问题:
- 缩短哪一个活动的时间能缩短整个工期的时间呢?
- 想要在规定时间内结束工程,最晚什么时候开始这个工程呢?
- 做题步骤:画出两个表格
- 关键路径改变有什么影响
- 关键活动耗时增加,整个工程的工期将增加
- 关键活动耗时减少,整个工程的工期不一定减少
- 关键路径 VS 拓扑排序
- 关键路径以拓扑排序为基础,但是拓扑排序要求的是完整地遍历完所有的结点,关键路径只要求从源点走到汇点就好了。
- 记忆法:
-
排序
-
内部排序
-
https://blog.csdn.net/qq_39328436/article/details/107089615
-
插入排序
-
交换排序
-
冒泡排序n2:https://blog.csdn.net/qq_39328436/article/details/107044774
-
快速排序nlogn:https://blog.csdn.net/qq_39328436/article/details/107048587
-
//快速排序代码是重点 int partition(vector<int> &num,int low,int high){ int pivot=num[low]; while(low<high){ while(low<high&&num[high]>=pivot)--high; num[low]=num[high]; while(low<high&&num[low]<=pivot)++low; num[high]=num[low]; } num[low]=pivot; return low; } void quickSort(vector<int> &num,int low,int high){ if(low<high){ int pivotpos=partirion(num,low,high); quickSort(num,low,pivotpos-1); quickSort(num,pivotpos+1,high); } }
-
快速排序改进:https://blog.csdn.net/qq_39328436/article/details/107134719
-
-
选择排序
-
简单选择排序n2:https://blog.csdn.net/qq_39328436/article/details/107063037
-
堆排序nlogn(建堆n,一次调整logn):https://blog.csdn.net/qq_39328436/article/details/107065737
-
-
归并排序nlogn:https://blog.csdn.net/qq_39328436/article/details/107085258
-
基数排序:https://blog.csdn.net/qq_39328436/article/details/107089241
-
奇怪的记忆
-
时间性能与初始序列无关:一堆乌龟选基友(堆排序,归并排序,简单选择,基数排序)
-
不稳定:尔快选(希尔排序,快速排序,选择排序(简单选择+对堆排))
-
全局有序:快排,冒泡,堆排,简单选择排序
-
-
-
外部排序
-
https://blog.csdn.net/qq_39328436/article/details/107127598
-
外部排序的时间开销:
-
1.读写外存的时间:最主要,受归并趟数影响,有几趟就要读写几次
-
2.内部排序的时间:初始归并段要有序,内部排序很快,基本可以忽略
-
3.内部归并的时间:从k路归并段中每次挑选最小值的过程
-
-
s=logkr(s是归并趟数,k是归并路数,r是初始归并段个数)
-
优化
-
增加归并路数k,从而减少归并趟数s,从而减少io次数
-
减少初始归并段个数(增加单个归并段的长度),从而减少归并趟数s,从而减少io次数
-
-
败者树:从k个归并路中挑选最小值的方法,减少增加归并路数带来的时间开销
-
选择置换排序:构造更长的初始归并段(长度各不相同),减少归并段的数量。
-
最佳归并树:在选择置换排序下,各个归并段的长度不相同,每次挑选哪两个段进行归并,能够使得读写磁盘的次数更少呢?
-
查找
-
顺序查找:从头到尾或者从尾到头逐个比较
-
折半查找:
-
分块查找:
-
先在索引表中索引分块(可顺序,可折半,索引分块可以是分块中的最大关键字),然后在块内顺序查找。块内无序,块间有序
-
-
B树与B+树:
-
散列表:
-
哈希表处理冲突的方法
-
拉链法:所有的同义词存储在一个单链表中
-
开放地址法
-
线性探测法:容易造成同义词之间的聚集现象
-
平方探测法
-
再散列法:准备多个哈希函数
-
伪随机序列法:冲突时地址加上一个伪随机数构造新地址
-
-
-
常见散列函数
-
除留余数法
-
直接定址法
-
数字分析法
-
平方取中法
-
-
串
完结撒花 ✿✿ヽ(°▽°)ノ✿