【比较类排序算法】冒泡排序、选择排序、快速排序、插入排序、希尔排序(PHP)

常见的经典比较类排序算法有冒泡排序选择排序快速排序插入排序希尔排序。这几种排序中快速排序和希尔排序的平均时间复杂度都突破了O(n^2),主要得益于这两种排序每轮排序都对下一轮产生影响,而其余的排序如选择排序每轮排序只是单纯从数组中找出最值,而没有对下一次的数组排序做出贡献。
<输入的最好方式就是输出>,本着学习的态度表达一下自己浅显的理解。下面主要介绍每种算法的中心思想,具体的代码实现(PHP),并分析对应的时间复杂度和空间复杂度。

1.冒泡排序

以升序为例(后面同理),冒泡排序的思路是:相邻两个元素进行比较实现左小右大,交换完一轮得到最大值位于最后一位,按这个方式交换n-1轮(n为元素总数,后面同理)即完成排序。

代码如下:

function bubbleSort(array $arr)
{
    $len = count($arr);
    for ($i = 0; $i < $len - 1; $i++) {
        for ($k = 0; $k < $len - 1 - $i; $k++) {
            //交换位置条件
            if ($arr[$k] > $arr[$k + 1]) {
                $tmp = $arr[$k];
                $arr[$k] = $arr[$k + 1];
                $arr[$k + 1] = $tmp;
            }
        }
    }
    return $arr;
}

记忆关键词: 左右交换、每轮最值、n-1轮
性能分析:时间复杂度O(n^2),空间复杂度O(1)

2.选择排序

选择排序的思路是:从左边第1个元素开始,与右边剩下的数组元素进行比较,每一轮得到最小值位于左侧,总共比较n-1轮完成排序。

代码如下:

function selectSort(array $arr)
{
    $len = count($arr);
    for ($i = 0; $i < $len - 1; $i++) {
        $min = $arr[$i];//最小值
        $minIndex = $i;//最小值下标
        for ($k = $i + 1; $k < $len; $k++) {
            if ($min > $arr[$k]) {
                $min = $arr[$k];
                $minIndex = $k;
            }
        }
        //比较完一轮后,交换左侧位置的最小值
        $tmp = $arr[$i];
        $arr[$i] = $arr[$minIndex];
        $arr[$minIndex] = $tmp;
    }
    return $arr;
}

记忆关键词: 左元素、右数组、左侧累加最值
性能分析:时间复杂度O(n^2),空间复杂度O(1)

3.快速排序

快速排序的核心思想是递归和二分,思路是:先将待排数组分为左右分区,并获得中间基准,使得左分区元素均小于中间基准,右分区元素均大于中间基准。同样地,左右两分区按照相同的方式,递归划分各自的左右分区,最后完成排序。
快速排序的关键在于中间基准的确定,方法是:取数组左边第一位作为初始基准,把它与右边剩下的所有元素比较,依次将小于该基准的元素进行替换并记录位置,最后把获取到的位置元素与初始基准元素交换,完成左右分区,返回该中间基准位置。

代码如下:

function quickSort(array $arr, $left = null, $right = null)
{
    $len = count($arr);
    //初始化左右下标位置
    $left = $left ?? 0;
    $right = $right ?? $len - 1;

    //递归条件
    if ($left < $right) {
        //获取中心基准
        $partitionIndex = getPartitionIndex($arr, $left, $right);
        //左分区递归
        $arr = quickSort($arr, $left, $partitionIndex - 1);
        //右分区递归
        $arr = quickSort($arr, $partitionIndex + 1, $right);
    }

    return $arr;
}

/**
 * 将数组进行左右分区,并返回中心基准位置
 * @param array $arr
 * @param int $left
 * @param int $right
 * @return int
 */
function getPartitionIndex(array &$arr, int $left, int $right)
{
    $pivot = $left;//初始基准
    $index = $left + 1;//初始化游标位置,中心基准交换位置的下一位
    for ($i = $index; $i <= $right; $i++) {
        //将元素与初始基准比较
        if ($arr[$i] < $arr[$pivot]) {
            //小于基准,与游标元素交换
            $tmp = $arr[$i];
            $arr[$i] = $arr[$index];
            $arr[$index++] = $tmp;//移动游标
        }
    }

    //比较结束获得中心基准位置,与初始基准交换
    $resultIndex = $index - 1;
    $tmp = $arr[$pivot];
    $arr[$pivot] = $arr[$resultIndex];
    $arr[$resultIndex] = $tmp;
    return $resultIndex;
}

记忆关键词: 二分、递归、左右分区,中间基准
性能分析:时间复杂度O(nlog2n),因为利用二分递归进行排序,时间复杂度突破n^2;空间复杂度O(log2n),与递归次数相关。

4.插入排序

插入排序的思路是:左边先构建有序数组(初始为左边第一个元素),右边元素从其下一位开始与其比较,若大于其末位元素则直接在末尾插入,否则继续往左遍历比较,确认插入位置,最后交换插入,构建新的有序数组。右边元素依次按照该方式插入有序数组,完成排序。

代码如下:

function insertSort(array $arr)
{
    $len = count($arr);
    for ($i = 1; $i < $len; $i++) {
        $index = $i - 1;//左边数组末位元素
        $current = $arr[$i];//右边第一个元素
        //当前值遍历左边数组,进行比较
        while ($index >= 0 && $current < $arr[$index]) {
            $arr[$index + 1] = $arr[$index];//左边数组扩大,最大值右移
            $index--;//左边数组比较位往前移
        }
        //插入左边数组结果位置
        $arr[$index + 1] = $current;
    }
    return $arr;
}

记忆关键词: 左有序数组、右元素遍历、右移插入
性能分析:时间复杂度O(n^2),由代码可看出右移次数越少,右元素扫描遍历的次数就越少(由此引出插入排序的改良版希尔排序),最好的时间复杂度是O(n); 空间复杂度O(1)。

5.希尔排序

希尔排序是插入排序的改良版,也称缩小增量排序。思路是:将待排数组,按照指定的增量进行分组,每组分别进行插入排序,由于每个分组都经过排序,从而导致整个数组宏观上相对有序。最后将整个数组再进行一次插入排序。因为前面分组排序对最后一次排序产生了积极影响,所以整体的排序速度得到提升。

代码如下:

function shellSort(array $arr)
{
    $len = count($arr);
    //增量确定,这里取数组长度一半;
    //增量gap即为分组数,每趟下来分组数减半,直到分组数为1进行最后一次插入排序
    for ($gap = intval($len / 2); $gap > 0; $gap = intval($gap / 2)) {
        //对gap个数组分别进行插入排序
        for ($i = $gap; $i < $len; $i++) {
            $current = $arr[$i];//当前比较值
            $index = $i;//游标位置
            //当前值与上一个值比较(因为按照gap分组,所以分组内上一个值为index-gap)
            while ($index - $gap >= 0 && $current < $arr[$index - $gap]) {
                $arr[$index] = $arr[$index - $gap];
                $index = $index - $gap;
            }
            $arr[$index] = $current;
        }
    }
    return $arr;
}

记忆关键词: 确定增量、分组插入排序、缩小增量
性能分析:时间复杂度:O(nlogn)~O(n^2), 空间复杂度O(1)

非比较类排序算法传送门://www.greatytc.com/p/8de11b38ff18

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容