数据结构 Chapter 10 - Algorithm design technique
这一章名为算法设计技巧,但是一开始还是以算法介绍和算法分析为主,介绍了五个大的常用算法
贪婪算法(Greedy algorithm)
贪婪算法的概念是按照阶段来进行操作,每个阶段里都进行在当前阶段里来看最好的选择,即阶段最优,寄希望于这样可以达到全局最优。在有些问题里阶段最优就可以达成全局最优,比如找钱问题。另一些问题中达不成全局最优,但是若结果接近也可以接受,还有一些时候需要精确地最优结果,则必须对贪婪算法进行修改,使其能够在每个阶段反悔之前的选择。
应用1:进程安排问题
对于一个单线程的处理器来说,如果要安排一系列的事务来处理,希望每件事务的完成时间平均起来最短,则应用贪婪算法,按用时从短到长来处理即可。若是多线程,则按用时长短在多个线程里依次排放事务即可。
应用2:文件压缩
对于一个文件中的字符表示来说,理论上有x个字符需要表示,则所表示一个字符所需的bit为logx个。用树来表示的话,只将字符放在树叶上,从root开始,表示字符的方法为向左记为0,向右记为1,每个字符表示所需bit数即为其深度。这样的话,可以得出,只要可以降低树叶的depth即可减少编码量,也即压缩文件。而可以看出,我们并不需要每个字符都用同样长度的bit来表示,只要短长度编码与长长度编码不发生重码即可,所以只要保证所有字符都在树叶上,即可保证编码的有效性,于是可以得出如optimal prefix code这样的特殊的树,而接下来需要考虑的就是如何分配低深度的字符和高深度的字符。
optimal prefix code树的构建
树的最终理想构建结果是出现频率越高,其深度越低。假设共有C个需编码字符,每个字符的出现次数即为其权重,那么算法即为,一开始各个字符单为一个树,每次选择权重最小的两个字符树组成一个树的两个树叶,合并之后的新树其权重为原来两个树之和。这样一直合并C-1次,得到的即为所需树。这是一个由深至浅构筑的算法,也是一个贪婪算法,因为每个阶段都取得是当前权重最小的两个树来合并。如果将所有树用堆来存储,则运行时间为O(ClogC)。如果堆是用链表来实现的,则所需时间为O(C^2)。
这是一个两阶段的算法,第一阶段需要将编码字符的权重传入,第二阶段进行树的构建并进行编码。
应用3:装箱问题
假设要对一系列的物品装箱,每个箱子的容量固定,则如何装箱可以达到所用箱子最少,即为装箱问题。相关的算法分为在线解决(边读入边装箱)和离线解决(全部读入后再装箱)
在线算法
由于在线算法不知道何时输入会结束,所以只有在当前阶段才能保证其决策为最佳,因此其无法总是给出最优结果。
- Next Fit:对每个新物品,检测是否可以与上一个物品放在一个箱内,如果可以就放,不能就再放一个新箱子里。最坏结果会给出最优结果两倍的箱子数。
- First Fit:对每个新物品,从头开始检测是否有放的进去的箱子,如果没有则放新箱子里。最坏结果为最优结果的1.7倍。
- Best Fit:对每个新物品,从头检测放进能放的空余最小的箱子里,如果没有则放新箱子里。最坏结果为最优结果的1.7倍。在随机输入时结果要比First Fit好。
离线算法
由于离线算法比在线算法多出了对全局输入的了解,所以多出了额外的信息,相应的给出的结果也应更好。在线算法最难解决的就是后续大件物品的处理,而最自然的解决办法就是先装大件,再装小件,所以可以对所有物品进行降序排序,之后再进行First Fit或者Best Fit算法。这样的话最坏结果为最优结果的4/3倍。
分析关键点在两个:所有大小超过1/3的物品都被放在前M个箱子中,M个以后的箱子中最多有M-1个物品。(证明没看懂)
分而治之(Divide and Conquer)
分而治之是应用到递归的算法。分是指将问题使用递归分解问题,治是指原问题的解由分问题合并而成。
分而治之问题的运行时间
若T(N) = aT(N/b) + theta(N^k)
T(N) = O(N^logb(a)) if a > b^k;
T(N) = O((N^k) * logN) if a = b^k;
T(N) = O(N^k) a > b^k;
应用1:最近点问题
在平面上有N个点,求距离最近的两个点,如果用最基本的brutal force algorithm,需要O(N^2)运行时间。我们的目标是达到O(NlogN)
如果我们对所有的点都进行按X坐标排序,排序本身需要O(NlogN)的时间。在平面中取一条垂线使之中分所有点(分而治之的分),那么所有的点不是在左面PL就是在右面PR,则距离最近的两个点不是都在左面dL,就是都在右面dR,或者一左一右dC。假设theta为dL和dR的最小值,那么我们不需要计算所有点的dC,只需要计算距离在中垂线两侧各theta距离内的点即可,这个区域称为strip。由于在这个区域内的点数目很少,直接brutal force算出各个之间的距离即可,用时为O(N)(因为所涉及的点数量很少)。为了进一步提高效率,我们既然已经对x坐标进行了约束,同样的对y坐标排序并进行约束即可,在实际中保留两个list,一个存有x坐标排序的点,一个存有y坐标排序的点。这样的话在进行dC的计算时,最多只需要算7个点即可,其用时为O(1),这样总其用时即为O(NlogN)
应用2:选择问题
如果有N个点,求第k大的点。
在这里我们使用取五个为一组的分组,求出每五个一组的median,然后对所有的median求median,以此为中分点,则可将整个set分为0.7N和0.2N,因此整个的算法用时最后为O(N)。但是这个算法的其他需要考虑情况太复杂,一般不会使用。
应用3:数学模型的运算优化(都没看懂)
- 整数相乘
- 矩阵相乘
动态编程(dynamic programing)
其实就是在进行运算的过程中,将之后的运算时需要用到的过程值顺势保存下来,以防止重复计算导致算法的低效,常用于改进递归算法。
浙公网安备 33010602011771号