尾递归:实际中为什么快排会比堆排快?

尾递归

  • 尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去.
  • 递归解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。
  • 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数,深层函数所面对的不是越来越简单的问题,而是越来越复杂的问题,因为参数里带有前面若干步的运算路径。
  • 以上参考1 递归与尾递归总结 2 尾递归-百科

//quick sort code
QUICK-SORT(A[],p,r)
    if(p < r)
        q = PARTITION(A,p,r)
        QUICK-SORT(A,p,q-1)
        QUICK-SORT(A,q+1,r)

由上面可看,quick-sort在 在编译时,能够进行 尾递归 优化,减少运行栈深度,提升效率和速度。

堆排和快排的运行比较问题参考大牛的数学之美番外篇

里面强调,其主要问题是堆排在每次 将首元素A[1] 与 A[n] 交换之后,再将新的A[1]下移至合适的位置(即保持堆的最大化性质)。问题就出现在这里,因为每次与A[1]交换的A[n]元素一定十分的小,它在交换后能够保持呆在A[1]的概率非常的低,必须要经过很多次的比较和向下挪动才能到达合适位置。而最优的方式是,每次对调的位置,有1/2的概率使得A[n]能够保持呆下去。堆排虽然和快排一样复杂度都是O(NlogN)但堆排复杂度的常系数更大。

里面的引出问题是一个称球的智力题,从这个问题去分析快排和堆排还有基数排序的性能分析。很有意思。
引用大牛的数学之美番外篇里边的一段话

另外,这几天我重新把TAOCP 第三卷(第二版)翻出来看了看 Knuth 怎么说这个问题的, 发现真是牛大了:

先说性能:

pp148, section 5.2.3 说:

When N = 1000, the approximate average runiing time on MIX are 160000u for heapsort 130000u for shellsort 80000u  for quicksort

这里,  Knuth 同学发现一般情况下 heapsort 表现很不好. 于是,在下文他就说,习题18 (pp156, 难度21)

(R.W.Floyd) During the selection phase of heapsort, the key K tends to be quite small, so that nearly all the comparisons in step H6 find Kto modify the algorithm so that K is not compared with K_j in the main loop of the computation, thereby nearly cutting the average number of comparisons in half.

你可能感兴趣的:(尾递归:实际中为什么快排会比堆排快?)