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

184 阅读3分钟

几种最经典、最常用的排序方法有:冒泡排序、插入排序、选择排序、快速排序、归并排序、计数排序、基数排序、桶排序。

排序算法分析

对于排序算法我们除了学习它的算法原理、代码实现之外还要学会如何分析一个排序算法。分析一个排序算法一般我们从执行效率、内存消耗以及稳定性

执行效率

执行效率一般从如下三个方面进行衡量。

  • 最好情况、最坏情况、平均情况时间复杂度。
  • 时间复杂度的系数、常数 、低阶。在排序的数据量比较小时考虑。
  • 比较次数和交换(或移动)次数。

内存消耗

算法的内存消耗可以使用空间复杂度来表示,排序时引入原地排序这个概念。原地排序算法指的是空间复杂度为O(1)的排序算法。

稳定性

排序算法的稳定性指的是在待排序的序列中存在值相同的元素,经过排序之后相等的元素之间的前后顺序不变。顺序不变的称为稳定的排序算法,顺序改变的称为不稳定的排序算法。

冒泡排序

原理

冒泡排序的原理:

  • 只操作相邻的两个元素
  • 每次冒泡都比较相邻的两个元素看是否满足大小关系,不满足时则两者互换
  • 一次冒泡至少能让一个元素移动到它应该在的位置,经过n次之后就完成了n个元素的排序

当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作。

代码实现php版

//冒泡排序,$arr表示数组,$n表示数组大小
function bubbleSort($arr, $n)
{
    if ($n <= 1) {
        return $arr;
    }
    for ($i = 0; $i < $n ; $i++) { 
        $flag = false;
        for ($j = 0; $j < $n-$i-1 ; $j++) { 
            if ($arr[$j] > $arr[$j+1]) {
                $tmp = $arr[$j];
                $arr[$j] = $arr[$j+1];
                $arr[$j+1] = $tmp;
                $flag = true;
            }
        }
        if (!$flag) {
            break;
        }
    }
    return $arr;
}

算法分析

  • 冒泡排序只牵扯相邻数据的交换,因此只需要常数级的临时空间,对应的空间复杂度为O(1),所以是原地排序。
  • 冒泡排序只有两个大小不一样时且不满足条件时我们才进行数据转换,因此冒泡排序是一个稳定的排序算法。
  • 当序列正好是有序的我们只需要一次冒泡就行,因此最好情况下时间复杂度为O(n);当序列为倒序时,我们需要进行n次冒泡,所以最坏情况下时间复杂度为O(n^2);平均时间复杂度为O(n^2)

插入算法

原理

将要排序的序列分为已排序区和未排序区,已排序区开始只有一个元素,即序列的第一个元素。插入排序时在外排序区取出元素,然后在已排序区找到合适的位置插入并保证已排序区间一直有序,当未排序区中没数据时表示排序完成。

代码实现php版

function insertionSort($arr, $n)
{
    if ($n <= 1) {
        return $arr;
    }
    for ($i = 1; $i < $n ; $i++) { 
        $value = $arr[$i];
        $j = $i - 1;
        for ($j; $j >= 0 ; $j--) { 
            if ($arr[$j] > $value) {
                $arr[$j+1] = $arr[$j];
            } else {
                break;
            }
        }
        $arr[$j+1] = $value;
        
    }
    return $arr;
}

算法分析

  • 插入排序算法的运行并不需要额外的存储空间,所以空间复杂度是O(1),是一个原地排序算法。
  • 在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现元素的后面,这样就可以保持原有的前后顺序不变,所以插入排序是稳定的排序算法。
  • 如果要排序的数据已经是有序的,我们并不需要搬移任何数据,因此最好的时间复杂度为O(n);如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以最坏情况时间复杂度为 O(n^2);对于插入排序来说,每次插入操作都相当于在数组中插入一个数据,循环执行 n 次插入操作,所以平均时间复杂度为 O(n^2)

选择排序

原理

选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

代码实现php版

function selectionSort($arr, $n) {
    if ($n <= 1) {
        return $arr;
    }
    for($i=0;$i<$n;$i++){
        $min = $i;
        for($j=$i+1;$j<$n;$j++){
            if($arr[$j] < $arr[$min]){
                $min = $j;
            }
        }
        if($min != $i){
            $temps = $arr[$i];
            $arr[$i] = $arr[$min];
            $arr[$min] = $temps;
        }
    }
    return $arr;
}

算法分析

  • 选择排序的最好、最坏、平均情况时间复杂度都是O(n^2)。为什么?因为无论是否有序,每个循环都会完整执行,没得商量。
  • 选择排序算法空间复杂度是O(1),是一种原地排序算法。
  • 选择排序算法不是一种稳定排序算法,比如[5,8,5,2,9]这个数组,使用选择排序算法第一次找到的最小元素就是2,与第一个位置的元素5交换位置,那第一个5和中间的5的顺序就变量,所以就不稳定了。

总结