从狭义上讲,很多演算法补救的路子是完全相同的。因而,为的是方便快捷,通常依照演算法选用的形式和路子来给它们展开分类。这种给演算法展开分类的两个原因是:如果他们理解了它选用的一般路子他们常常就能够对该演算法赢得许多深入细致的了解。在解决许多没有整套演算法解,但与原有难题类似的难题时,他们由此可以得到许多启迪和意念。当然,有些演算法有违展开分类原则,而另许多则是多种形式并重的乙醛。这四节将介绍许多常见的形式。
1 乱数法
乱数法依赖于乱数数的统计优点。两个应用乱数法的范例是加速次序。
加速次序按如下表所示形式组织工作:构想要对一大堆补发的帐单次序,具体来说将失序的一整堆分为两部份。其中一大堆里使大部份的帐单电话号码都小于等同于所预设的两个中间值,另一大堆里保证大部份的帐单电话号码都小于那个中间值。除非有了这种的陶土帐单后,就再以反之亦然的形式对这陶土帐单多次重复刚才的分割过程,直至每一大堆里都多于两张帐单年末。那个时候大部份的帐单就都排好序了。
为的是赢得较低的操控性,加速次序倚赖每一次要如何分割帐单,他们须要让分割出的陶土帐单数量几乎完全相同。为的是实现这一步棋,平庸的形式是在分割帐单之前具体来说找到帐单电话号码的中间值。可是为的是确定那个中间值须要结点大部份的帐单,因而他们并不打算这么做。做为代替的形式,乱数选择两个帐单电话号码做为分割的依照。加速次序的平均操控性很不错,因为乱数数的概率密度函数使得分割的结论是相对平衡的。
2 共管法
共管法包涵3个关键步骤:降解、解与分拆。在降解期,将数据降解为更小、更容易管理的部份。在解期,对每个降解出的部份展开处置。在分拆期,将每部份处置的结论展开分拆。两个共管法的范例是福兰县次序。
福兰县次序依照如下表所示形式组织工作。与此相反,反之亦然假定要次序一大堆补发的帐单。首先将失序的一整堆分为两片,下一步棋分别将陶土帐单再各自分为两片,始终持续那个关键步骤直至每一大堆中都多于两张帐单年末。除非大部份堆中都多于两张帐单时,就将其对角分拆且确保每两个分拆后的新堆都是科学规范的。始终做对角分拆直至重新得到一大堆,此时大部份的帐单就都已经排好序了。
在大部份的共管演算法中都有完全相同的三个关键步骤。福兰县次序能以以下形式来描述,具体来说,在降解期将数据分割为两份。接下来,依照递归的形式对降解出的两部份应用归并次序。最后,在分拆期将两部份分拆成两个排好序的集合。
3 动态规划
动态规划同共管法类似,都是将较大的难题降解为子难题最后再将结论分拆。然而,它们处置难题的形式与子难题之间的关系有关。在共管法中,每一个子难题都是独立的。为此,他们以递归(见第3章)的形式解决每两个子难题,然后将结论与其他子难题的结论分拆。在动态规划中,子难题之间并不是独立的。换句话说,子难题之间可能有关联。这类难题选用动态规划法比共管法更合适。因为若用共管法来解决这类难题会多做很多不必要的组织工作,有些子难题会多次重复计算多次。尽管动态规划法是一种重要的思想且很多演算法都利用了这种思想,但本书介绍的演算法中都没有使用到它。
4 贪心法
贪心法在解难题时总能够做出在当前的最佳选择。换句话说,不是从整体最优上考虑,而仅仅是在某种意义上的局部最优解。遗憾的是,当前的最优解长远来看却未必是最优的。因而,贪心法并不会始终产生最优结论。然而,在某些方面来说,贪心法确是最佳选择。两个选用贪心法的范例是霍夫曼编码(见第14章),这是两个数据压缩演算法。
霍夫曼编码中最重要的部份是构建一棵霍夫曼树(又称最优二叉树)。为的是构建一棵霍夫曼树,从它的叶子节点自底向上处置。将每个要压缩的符号和它们在数据中出现的次数(频率)一起做为二叉树的根节点保存。接下来,选择两棵拥有最小频率值的根节点做为左右子树节点,构造一棵新的二叉树,且确保新的二叉树根节点的频率值为左右子树节点的频率值之和。然后多次重复那个关键步骤,直至得到唯一的一棵树,这就是最终的霍夫曼树。霍夫曼树的根节点包涵数据中符号的总数,叶子节点包涵原始的符号以及它们出现的频率。霍夫曼编码是一种贪心演算法,因为每次它都会挑选出当前最合适的两棵树来分拆。
5 近似法
并不计算出最优解,相反,它只计算出“足够好”的解。通常利用近似法解决那些计算成本很高又因为其本身十分有价值而不愿放弃的难题。推销员难题(见第16章)是一个通常会用近似法去解决的难题。
构想一位推销员须要设计一条去往好几个城市组织工作的路线。推销员难题的目的是找到最短的可能路径,以便推销员能够在回到出发点前恰好每座城市都只去过一次。由于推销员难题可能存在一种最优的策略,但计算的代价很高,因而可以选用启迪式的形式得到两个近似解。当最优策略行不通时,启迪式的形式是他们能够接受的一种比最优策略稍逊许多的策略。
推销员难题可以用图表的形式来描绘。把推销员必须前往的城市在格子中用点标记出来。然后依照如下表所示启迪式的形式来寻找这些点之间的最短路径。从推销员出发的位置开始多于两个点,将那个点涂黑。大部份其他的点在加入路线图之前都是白色的,当它们加入时也反之亦然涂黑。接下来,对于每两个还没有加入到路线图中的点v,计算最后两个加入到路线图中的点u和v之间的距离。通过这种形式,选择离u最近的那个点,将其涂黑并加入路线图中。多次重复那个过程直至大部份的点都已经涂黑。最后,再次将出发点加入路线图使其闭合,这种就完成了整个路线图。