说明
本文整合自王道和里昂学长的PPT(无盈利侵权必删),是完整的408代码大题解题方案,整合好的pdf文档可以联系我获取
快速排序
代码实现

快速排序算法以huafen()函数为核心
如何划分?
第一趟
首先将左指针L对应的数组元素设为mid值,我们之后要将mid值和其他值对比然后找到mid值该在的位置
先从R向前寻找第一个小于mid的值(如果没有R- -一直找),如果有,就把他和目前存着mid值的A[L]互换位置
再从L向后寻找第一个大于mid的值(如果没有L++一直找),如果有,就把上一步的A[R]换过来
第二趟
上一趟完成了第一次交换,把小于mid的换到了右边,而把大于mid的换到了左边
第二趟又在新的R找到了小于mid的元素,放在之前发现的左边覆盖,以此类推
最后一趟
当最后一趟开始时,若是L++导致L==R则
A[L]换上了右边更小一点的元素。
而下一步L++,已不满足L<R的条件。而这一步的L就是上一步的R(L==R),A[R]的值已经迁移到了其左边。
因此设A[L]=mid,就把两者很好的拆开了
当最后一趟开始时,若是R- -导致L==R则
也就是上一趟将**大于mid的A[L]**的值放到右边时,将较大的值换到了A[R]中。
此时R- -到R==L,因此此时A[L]中的存储的是已经是到了还没有**R–**时的右边。
因此A[L]=mid,即可拆开
快速排序算法
因此在快排中调用这段代码
先将A中的LR成以mid为中间的两段,左边小于mid,右边大于mid
我们的划分传来的是mid的位置,因此要想把整个数组排序好,只需要排好左右一直调用。
将数组的最左边L和mid的前一个位置进行划分,穷举下去即可解决
完整代码
1 | void Qsort(int A[], int L, int R) { |
划分函数的妙用
求数组中第k小元素的方法

按理说我们只需要快速排序好,再直接利用数组的随机查找特性找到A[k-1]就可以
但是huafen函数可以直接找到某个元素的具体位置,我们可以直接用M接收huafen函数传来的此时的位置
若这个位置比已知位置小,则k-1在M前面,则设L=M+1继续试
1 | int func(int A[], int n, int k) { |
归并排序
归并排序用于两个数组排到一个数组里的排序中

1 | int Merge(int A[], int N, int B[], int M, int C[]) { |
链表
查找+删除

1 | //删除值为 x 的结点 |
查找+插入

1 | //在递增有序链表中插入值为 x 的信息 |
头插法(原地逆转链表)

在勾画的绿色部分正是逆置的核心出装,将新插入的结点(在原链表中更靠后)不断放前,而实现逆置
1 | void ListReserve(LinkList L){ |
尾插法保持原序

以下是我自己写的代码:
1 | void ListPart(LinkList C){ |
二叉树
前/中/后序遍历

1 | //二叉树的结点定义(链式存储) |
层序遍历
在层序遍历中需要使用到队列,也就需要队列的操作
1 | //初始化队列 |
1 | //层序遍历 |
求树高
(方法一)先序遍历
1 | /*求树的高度(方法一)*/ |
调用时用**PreOrder(T, 1**)
原理是每过一层则+1并和目前高度比较
(方法二)后序遍历
1 | int PostOrder(BiTree T){ |

以该图为例,g和h没有左右结点了,故直接返回0+1给e作为e的left和right,e再进行比较以大的深度+1作为自己的深度交给b,b也这么做并和c一起交给a
后序遍历以孩子出发从孩子开始数并返给根节点最长的版本
求树宽

此处先用先序遍历统计每一层的结点数,也就是使用先序遍历先根后孩子的特点来统计
1 | int width[MAX]; //用于统计各层的结点总数 |
再找到宽度数组中最大的一项,将其作为树宽
1 | void treeWidth(BiTree T) { |
求树WPL

求树的WPL还是要使用树的先序遍历
使用了求树高和树宽的思想
先找到叶子节点(也就是左右孩子为空)并且将此处的带权路径长度加入WPL
如果不是叶子结点则向下找到其他叶子节点高度
1 | void PreOrder (BiTree T, int level){ |
特殊树型判断
二叉排序树判定

中序遍历出来的序列是升序序列,所以二叉排序树序列中下一个结点必然大于前面所有的结点(也就是MAX{前面结点})这里用temp表示
所以只要后面遍历到的小于temp,则直接判定失败
若不小于则继续向右子树遍历,直到为NULL退回
1 | int temp=MIN_INT; //记录当前遍历到的最小值 |
平衡二叉树判定

此树判定和方法二找树深一样
通过后序遍历的特点:先访问孩子再进行操作,由最后的孩子也就是结点A的左右子树都退回来自己的高度之后,再进行左右子树高度判断
1 | bool isBalance=true; //二叉树是否平衡 |
完全二叉树判定
这里先借用一下层序遍历的图

层序遍历,因为完全二叉树是按顺序排放,所以要看每一层的结点的逐个讨论、
四种情况分别在访问这个结点时讨论一次

1 | // 特殊树形判定:完全二叉树? |
图

图的数据结构定义
邻接矩阵
这是邻接矩阵的数据结构

这是邻接矩阵的初始化操作

邻接表
这是邻接表的数据结构,在图ALGraph中存储一个顶点表数组和顶点数和边数
在顶点表中存储这个结点的数据和第一个依附该点的边
边表则是指出这条边指向的点和下一条边的指针以及该边权值

这个表依旧要执行初始化操作
先将顶点表数组中的第一条边全部置为NULL
再将其顶点数和边数先更新
用封装好的AddEdge(图,出点,入点,权值)函数添加边

AddEdge函数是这么写的
首先先创建一个边结点,将其的权值和指向的结点j配置好
再将他连接到i结点第一条边之前,让这条新边指向老的第一条边(类似头插法将靠后的插在头边)
最后让i的点表连接到这条边作为first

最后可以看到他的逻辑结构长这样

图的遍历
深度优先遍历

1 | //邻接矩阵实现深度优先搜索 |
首先先把第一个访问的结点打上true,对第一结点比如A,找他向别人发射过的边,直到j<G.vexnum(穷尽了所有点)
当遇到没有遍历到过的且存在的邻接点时,将其投入DPS
然后一直DPS递归下去,直到再没有邻接的点,逐层返回没有访问完邻接点的结点中继续访问他们邻接点
1 | //邻接表实现深度优先搜索 |
前面步骤一样,依旧从i点开始遍历。先找到i点的第一个边,将其位置存放到p中
j就是此时p指向的点,若此点没有被访问过则开始深度遍历
直到遍历到最后一个能遍历到的点,就往上返回,继续访问之前没有访问完其他边的结点。
广度优先遍历

1 | //邻接矩阵实现广度优先搜索 |
广度优先遍历类似于图的层序遍历
从v出发,先将v存入队列中
当队列不为空时就出队一个结点,并且从第0个结点开始遍历,寻找v指向的且没有被访问过的结点,找到一个入队一个直到找不到为止
然后从i结点找到的第一个结点开始继续重复这个步骤,直到所有结点都找完使队列为空
1 | //邻接表实现广度优先搜索 |
套路和上面一样,入队之后先找到第一个边指向的结点看是否被访问过,若没有则全部入队直到v结点没有邻接的点为止。
然后又从v第一条边邻接点继续广度优先遍历
拓扑排序
先按一下思路分析
拓扑排序的步骤:
- 选择入度为0的顶点并输出
- 从图中删除该顶点与所有以它为弧的边
- 重复步骤1、2,直到不存在入度为0的顶点
| 拓扑序列 | 条件 |
|---|---|
| 存在且唯一 | 每次选择结点都只有一个结点可选 |
| 存在不唯一 | 选择结点时不止一个结点可选 |
| 不存在 | 图中有环,环中顶点未被访问 |
先拿王道的流程图说一下思想

首先我们需要循环找到入度为0的点并且全部入队。
此处如果队列里元素个数超过1,
- 入度为0的点肯定超过2个也就是拓扑排序不唯一。
- 若是有多个连通分量,一个连通分量多个入度为0,另一个连通分量是有环图,则很明显不存在拓扑排序
然后当我们出队时,我们将计数器+1,来记录已经出队的数
出队之后将所有出队的点指向的点,把他们的入度-1,然后继续寻找入度为0的点循环这个过程
当队空之后我们根据count数进行判断
- 如果count等于顶点数,那么说明从顶点到最后一个点只有一条路径。没错,拓扑排序唯一
- 若count数不等于也就是小于顶点数,说明存在环路,始终有点入度不为0互相堵着
1 | //邻接表实现 |
1 | //邻接矩阵实现 |
























