数据结构与算法学习之路9--排序(下)

259 阅读2分钟

冒泡排序、插入排序和选择排序的时间复杂度都为O(n^2),比较适合小规模数据的排序,在大规模数据的排序中我们一般使用归并排序和快速排序两种,归并排序和快速排序都用到了分治思想进行处理。

归并排序

原理

归并排序的原理是要想排序一个数组我们可以将数组分为前后两个部分,对前后两个部分分别进行排序,最后将两个两个排序好的数组进行合并这样就完成了整个数组的排序。

代码实现php版

归并排序使用递归的方式来实现,递推公式:merge_sort(arr) = merge(merge_sort(left), merge_sort(right)),终止条件:数组长度为1 不用再继续分解

function mergeSort($arr)
{
    $len = count($arr);
    if ($len < 2) {
        return $arr;
    }
    $middle = floor($len / 2);
    $left = array_slice($arr, 0, $middle);
    $right = array_slice($arr, $middle);
    return merge(mergeSort($left), mergeSort($right));
}

function merge($left, $right)
{
    $result = [];

    while (count($left) > 0 && count($right) > 0) {
        if ($left[0] <= $right[0]) {
            $result[] = array_shift($left);
        } else {
            $result[] = array_shift($right);
        }
    }

    while (count($left))
        $result[] = array_shift($left);

    while (count($right))
        $result[] = array_shift($right);

    return $result;
}

算法分析

  • 在merge函数中若是出现相同的元素,我们先将左数组的元素放入这样就能保证合并之后顺序不变,所以归并排序是稳定的排序算法
  • 将递归式完全扩展后,形成了完整的递归树,一共是lgn+1层(如果n是8,则树有lg8+1=4层),每层的代价是cn,那么总代价cnlgn+cn,忽略低阶项和常量c,即有T(n) = O(nlgn)。所以归并排序的时间复杂度是 O(nlogn);
  • 递归代码尽管每次合并操作都需要申请额外的内存空间,但在合并完成之后,临时开辟的内存空间就被释放掉了。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)。

快速排序

原理

快速排序的原理是:首先我们选择一个数字为基准数(一般我们选择第一个或者最后一个),接着我们遍历数组将小于基准的元素移动到左边,将大于基准的元素放到右边,这样序列就被分为左右两个相对有序的分区,将两个分区在按照同样的方法处理,直到区间缩小为 1,就说明所有的数据都有序了

代码实现php版

快速排序也使用递归的方式来实现,递推公式:quick_sort(arr) = quick_sort(left) +quick_sort(right),终止条件:数组长度为1

function quickSort($arr)
{
    if (count($arr) < 2) {
        return $arr;
    }
   $left = $right = [];
    for($i = 1; $i < count($arr); $i++) {
        if ($arr[$i] < $arr[0]) {
            $left[] = $arr[$i]; 
        } else {
            $right[] = $arr[$i];
        }
    }
    return array_merge(quickSort($left),[$arr[0]], quickSort($right));
}

算法分析

  • 最好情况,递归的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。
  • 因为分区的过程涉及交换操作,如果数组中有两个相同的元素,比如序列 6,8,7,6,3,5,9,4,在经过第一次分区操作之后,两个 6 的相对先后顺序就会改变。所以,快速排序并不是一个稳定的排序算法。
  • 快速排序在大部分情况下的时间复杂度都可以做到 O(nlogn),只有在极端情况下,才会退化到 O(n2)

归并排序和快速排序对比

由上图可以看出归并排序的处理过程是由下到上的,先处理子问题,然后再合并。而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题。