算法拾忆
参考课程:
https://mooc.study.163.com/course/1000002012?_trace_c_p_k2_=71acf89eff5548528d3ffa68b0912f8f#/info
https://mooc.study.163.com/course/1000005000?_trace_c_p_k2_=60f72128fe6348b7ae19623592d44785#/info
1、算法概论
1.1、复杂性
复杂性假设每个机器指令的执行时间是常量,然后考虑由于N变大而导致的增长速度。
时间复杂性
空间复杂性
最坏复杂性
最小复杂性
平均复杂性
计算递归函数的复杂度有递归方程和定理。
1.2、算法的设计模式
暴力搜索
分治法
图搜索和枚举
分支界限、回溯
随机化方法
1.3、算法实现方法
递归与迭代
顺序、并行、分布式
确定性与非确定性
近似求解与精确求解
量子算法
1.4、最优化算法设计方法
线性规划
动态规划
贪心法
启发式方法
2、分治算法
步骤: 原始问题->划分子问题->求解子问题->合并结果。
2.1、求第m大的数,或者中位数
思想:
0、入参:数据总量为T,求第N大的数
1、把数据分成k等份,每份T/k(此值要较小,便于排序)
2、对每份进行排序
3、每份取第N/k个数,作为一个集合,对这个集合取第N/k个数,此数记为M
4、把原本的T中,按照各组的第N/k个数,取出比M大的数和比M小的数,分别记为T1,T2
5、如果T1的元素数量大于N,则说明T1里的数据太多了,那么需要对T1中找第K个数;
如果T1的元素数量小于N,则说明T1里的数据太少了,那么需要在T2中找第N-K个数
递归执行。
2.2、快速排序进阶
快速排序实际也是个分治算法,每次分成两部分。如果看成一棵二叉树,每次选一个根节点,小于的在左节点,大于的在右节点,然后对左右进行递归。
快速排序的最坏情况发生在逆序时,也就是二叉树退化成了链表的时候。时间复杂度为O(n*n)
避免最坏情况的方法: 每次选数的时候用随机数,这样可以防止输入原始数组时恶意传递逆序数组。
3、树
3.1、二叉树遍历非递归算法
中序:遇到节点压栈,遍历左树,左树遍历结束,弹出栈顶元素并打印,遍历右树;
先序:
后序:
3.2、堆
完全二叉树,用数组存放,访问用下标,父节点位置为1/2子节点位置。左儿子位置=当前位置*2,右儿子位置=当前位置*2+1
最大堆只保证父节点比子节点大,左右节点无关。
增加:加入最尾部,然后调整
删除:堆的删除用途都是删除最大/小值,也就是堆顶元素。删除后,用最后的补充,然后调整
最大堆的建立:顺序建立,从最后一个有子节点的节点开始调整,依次向上全部调整为最大堆。复杂度O(N)
3.3、哈夫曼树与哈夫曼编码
哈夫曼树 就是一个判定树(决策树),但他要保证判断路径最优。又叫最优二叉树
https://mooc.study.163.com/learn/1000033001?tid=2402970002#/learn/content?type=detail&id=2403307452
建树过程,从所有树中挑两个权值最小的树,组成一个新树,用新树的权值代替之前的两个权值,再建下一个新树。
哈夫曼编码:不等长编码。把频率高的数据用短的编码,频率低的字符串用长的编码。
但要解决的问题是:二义性问题。解决二义性问题的方案是任意一个编码不能是其他编码的前缀。
保证这个的办法就是构造一个二叉树,每个节点代表0/1,然后只有叶子节点可以存放真正的编码。
而保证最优的办法,就是这个二叉树不平衡,最频繁出现的编码的深度为最小。
而这个不平衡的二叉树,就是哈夫曼树。
他的创建过程:编码的出现频率就是其权重,然后按这个权重组成哈夫曼树。
4、堆栈
4.1、堆栈的应用
函数调用及递归实现
深度优先搜索
回溯算法
4.2、算数表达式计算
计算机最容易计算后缀表达式,日常见到的是中缀表达式,所以第一步需要把中缀改成后缀
5、图
5.1、图的存放
邻接矩阵
邻接表 -- 一定要够稀疏才合适使用
其他方法,比如非量子的坐标系。
5.2、图的遍历
深度优先 DFS Depth First Search :
一直走,走到头,返回,返回的过程中,发现没走过的路,继续一直走。。。 原路返回的数据结构就是堆栈。
广度优先 BFS Breadth First Search :
取初始点,压栈,栈中取节点,获取与该节点连着的所有节点,压栈。。。
复杂地取决于数据结构,对于邻接矩阵,需要O(N*N);对于邻接表,需要O(N+E)