十大经典排序算法

十大经典排序算法

最近在工作中偶然间涉及到数据库的存储和访问,数据库里存放着员工的指纹、年龄以及姓名等信息,当然指纹是通过md5加密存储的。目前需要对员工的年龄、学历、工作年限等进行排序,如果只有几十个上百个样本,应该不会那么麻烦;关键这是几万名员工的数据,这个量很大,马虎不得。悄悄的告诉你,别惹我,我懂得删库跑路哦。

脑海中对排序的记忆有点模糊,只对「归并排序」印象较为深刻,为了加深理解,重拾「数据结构与算法」,并总结了一下常用的十大经典排序算法,由于平台为linux,因此代码全部用C++实现,全部源码均在linux下编译通过并测试成功,可以作为参考。

排序算法在程序猿的编程生涯中虽然用的不多,但是作为基本功,还是要掌握一下。排序算法是「数据结构与算法」中最基本的算法,它分为「内部排序」和「外部排序」;「内部排序」一般在内存中实现;当数据量很大时,内存有限,不能将所有的数据都放到内存中来,这个时候必须使用「外部排序」。

先看一张图,对常用算法的时间复杂度做个比较:


这里的「稳定」是指当排序后两个相等键值的顺序和排序之前的顺序相同;


一 冒泡排序

冒泡排序是排序算法中较为简单的一种,英文称为Bubble Sort。它遍历所有的数据,每次对相邻元素进行两两比较,如果顺序和预先规定的顺序不一致,则进行位置交换;这样一次遍历会将最大或最小的数据上浮到顶端,之后再重复同样的操作,直到所有的数据有序。

如果有

个数据,那么需要

的比较次数,所以当数据量很大时,冒泡算法的效率并不高。
当输入的数据是反序时,花的时间最长,当输入的数据是正序时,时间最短。

平均时间复杂度:


空间复杂度:


动态演示:

代码:

新建代码文件bubble_sort.cpp,将以上代码写入,linux下编译:

测试:

输出结果:

以下的编译方法和测试方法和这里一样,所以下面不再重复编译和测试的说明。

二 选择排序

选择排序简单直观,英文称为Selection Sort,先在数据中找出最大或最小的元素,放到序列的起始;然后再从余下的数据中继续寻找最大或最小的元素,依次放到排序序列中,直到所有数据样本排序完成。很显然,选择排序也是一个费时的排序算法,无论什么数据,都需要

的时间复杂度,不适宜大量数据的排序。

平均时间复杂度::


空间复杂度::


动态演示:

代码:

三 插入排序

插入排序英文称为Insertion Sort,它通过构建有序序列,对于未排序的数据序列,在已排序序列中从后向前扫描,找到相应的位置并插入,类似打扑克牌时的码牌。插入排序有一种优化的算法,可以进行拆半插入。

基本思路是先将待排序序列的第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;然后从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置,直到所有数据都完成排序;如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。

平均时间复杂度::


空间复杂度::


动态演示:

代码:

四 希尔排序

希尔排序也称递减增量排序,是插入排序的一种改进版本,英文称为Shell Sort,效率虽高,但它是一种不稳定的排序算法。

插入排序在对几乎已经排好序的数据操作时,效果是非常好的;但是插入排序每次只能移动一位数据,因此插入排序效率比较低。

希尔排序在插入排序的基础上进行了改进,它的基本思路是先将整个数据序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全部数据进行依次直接插入排序。

平均时间复杂度::


空间复杂度::


假如有这样一组数据,[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果以步长5进行分割,每一列为一组,那么这组数据应该首先分成这样

之后对每列进行插入排序:

将上述四行数据依序拼接在一起,得到[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ],此时10已经移到正确的顺序了,之后以步长3进行插入排序:

排序之后变为:

最后以步长1进行排序。
步长的选择是希尔排序的关键,只要最终步长为1,任何步长序列都可以。建议最初步长选择为数据长度的一半,直到最终的步长为1

图解:

代码:

五 归并排序

归并排序英文称为Merge Sort,归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。它首先将数据样本拆分为两个子数据样本, 并分别对它们排序, 最后再将两个子数据样本合并在一起; 拆分后的两个子数据样本序列, 再继续递归的拆分为更小的子数据样本序列, 再分别进行排序, 直到最后数据序列为1,而不再拆分,此时即完成对数据样本的最终排序。

归并排序严格遵循从左到右或从右到左的顺序合并子数据序列, 它不会改变相同数据之间的相对顺序, 因此归并排序是一种稳定的排序算法.

作为一种典型的分而治之思想的算法应用,归并排序的实现分为两种方法:

平均时间复杂度::


空间复杂度::


动态演示:

代码:

 1#include <iostream>
 2#include <algorithm>
 3
 4using namespace std;
 5
 6template<typename T> 
 7void merge_sort_iteration(T arr[], int len) {//迭代法
 8    T* a = arr;
 9    T* b = new T[len];
10    for (int seg = 1; seg < len; seg += seg) {
11        for (int start = 0; start < len; start += seg + seg) {
12            int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
13            int k = low;
14            int start1 = low, end1 = mid;
15            int start2 = mid, end2 = high;
16            while (start1 < end1 && start2 < end2)
17                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
18            while (start1 < end1)
19                b[k++] = a[start1++];
20            while (start2 < end2)
21                b[k++] = a[start2++];
22        }
23        T* temp = a;
24        a = b;
25        b = temp;
26    }
27    if (a != arr) {
28        for (int i = 0; i < len; i++)
29            b[i] = a[i];
30        b = a;
31    }
32    delete[] b;
33}
34template<typename T> 
35void merge_sort_recursive_t(T arr[], T reg[], int start, int end) {//递归法
36    if (start >= end)
37        return;
38    int len = end - start, mid = (len >> 1) + start;
39    int start1 = start, end1 = mid;
40    int start2 = mid + 1, end2 = end;
41    merge_sort_recursive_t(arr, reg, start1, end1);
42    merge_sort_recursive_t(arr, reg, start2, end2);
43    int k = start;
44    while (start1 <= end1 && start2 <= end2)
45        reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
46    while (start1 <= end1)
47        reg[k++] = arr[start1++];
48    while (start2 <= end2)
49        reg[k++] = arr[start2++];
50    for (k = start; k <= end; k++)
51        arr[k] = reg[k];
52}
53template<typename T> 
54void merge_sort_recursive(T arr[], const int len) {
55    T *reg = new T[len];
56    merge_sort_recursive_t(arr, reg, 0, len - 1);
57    delete[] reg;
58}
59
60int main() {
61
62    float arrf[] = { 17.5, 19.1, 0.6, 1.9, 10.5, 12.4, 3.8, 19.7, 1.5, 25.4, 28.6, 4.4, 23.8, 5.4 };
63    int len = (int) sizeof(arrf) / sizeof(*arrf);   
64    merge_sort_recursive(arrf,len);
65    for (int i = 0; i < len; i++)
66        cout << arrf[i] << ' ';
67
68    merge_sort_iteration(arrf,len);
69    for (int i = 0; i < len; i++)
70        cout << arrf[i] << ' ';
71    return 0;
72}

六 快速排序

快速排序,英文称为Quicksort,又称划分交换排序 partition-exchange sort 简称快排。

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。首先从数列中挑出一个元素,并将这个元素称为「基准」,英文pivot。重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。之后,在子序列中继续重复这个方法,直到最后整个数据序列排序完成。
在平均状况下,排序n个项目要

次比较。在最坏状况下则需要

次比较,但这种状况并不常见。事实上,快速排序通常明显比其他算法更快,因为它的内部循环可以在大部分的架构上很有效率地达成。

平均时间复杂度::


空间复杂度: :


动态演示:

更直观一些的动图演示:

代码分两种方式实现,分别为迭代法和递归法。
迭代法:

递归法:

七 堆排序

堆排序,英文称Heapsort,是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序实现分为两种方法:

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

算法步骤:

  1. 创建一个堆 H[0……n-1];
  2. 把堆首(最大值)和堆尾互换;
  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  4. 重复步骤 2,直到堆的尺寸为 1

平均时间复杂度: :


空间复杂度: :


动图演示:

来一个更直观一些的:

代码:

八 计数排序

计数排序英文称Counting sort,是一种稳定的线性时间排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于 i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。基本的步骤如下:

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  3. 对所有的计数累加,从C中的第一个元素开始,每一项和前一项相加
  4. 反向填充目标数组,将每个元素i放在新数组的第C[i]项,每放一个元素就将C[i]减去1

平均时间复杂度::


空间复杂度: :


动图演示:

代码:

九 桶排序

桶排序也称为箱排序,英文称为 Bucket Sort。它是将数组划分到一定数量的有序的桶里,然后再对每个桶中的数据进行排序,最后再将各个桶里的数据有序的合并到一起。

平均时间复杂度::


空间复杂度::


动态演示:

代码:

 1#include <iostream>
 2#include <vector>
 3#include <stdio.h>
 4#include <stdlib.h>
 5#include <string.h>
 6using namespace std;
 7
 8struct tNode
 9{
10    int tValue; 
11    tNode *next; 
12
13    tNode(int val) 
14    {
15        this->tValue = val;
16        this->next = NULL;
17    }
18};
19
20
21
22bool bucket_sort(int *arrf, const int SIZE)
23{
24    tNode **pNode = (tNode **)malloc(sizeof(tNode *) * 512);
25    if (NULL == pNode)
26        return false;
27
28    memset(pNode, 0, sizeof(tNode *) * 512);
29
30    int shiftNum = 0;
31    tNode *p = NULL;
32    tNode *pLast = NULL;
33    tNode *pNewNode = NULL;
34
35    for (int i = 0; i < SIZE; ++i)
36    {
37        shiftNum = arrf[i] >> 24;
38        p = pNode[shiftNum];
39
40        pNewNode = new tNode(arrf[i]);
41        if (NULL == pNewNode)
42            return false;
43
44        if (NULL == p)
45        {
46            pNode[shiftNum] = pNewNode;
47        }
48        else if (arrf[i] <= p->tValue) 
49        {
50            pNode[shiftNum] = pNewNode;
51            pNewNode->next = p;
52        }
53        else 
54        {
55            while (NULL != p->next)
56            {
57                if (arrf[i] > p->next->tValue)
58                    p = p->next;
59                else
60                    break;
61            }
62            pNewNode->next = p->next;
63            p->next = pNewNode;
64        }
65    }
66
67    for (int i = 0, k = 0; i < 512; i++)
68    {
69        p = pNode[i];
70
71        while (NULL != p)
72        {
73            arrf[k++] = p->tValue;
74            p = p->next;
75        }
76    }
77
78    return true;
79}
80int main(int argc, char **argv)
81{
82    int arr[] = { 5,558,772,935,344,487,96,665,302,735,954,308,718,147,185,371,166,849,202,478,874,169,980,125,44,15,279,882,466,974 };
83
84
85    bucket_sort(arr,30);
86    for (int i = 0; i < 30; ++i)
87    {
88        cout<<arr[i]<<" ";
89    }
90
91    cout<<endl;
92
93    return 0;
94}

十 基数排序

基数排序英文称Radix sort,是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串和特定格式的浮点数,所以基数排序也仅限于整数。它首先将所有待比较数值,统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

平均时间复杂度: :


空间复杂度: :


动态演示:

代码:

参考

wiki
github.com/hustcc/JS-So
「数据结构与算法」
「算法导论」

深圳SEO优化公司咸宁网站定制多少钱四平网页制作宁德网站搜索优化推荐湛江外贸网站建设吕梁设计网站哪家好常州网络广告推广推荐常德网站关键词优化价格东莞高端网站设计公司南充SEO按天收费推荐茂名百度竞价报价镇江企业网站改版海南网站推广工具报价日照seo网站优化潮州优化哪家好民治网站优化按天扣费报价双龙至尊标王哪家好新余外贸网站制作报价丽江外贸网站设计公司广元网站改版爱联网站制作推荐昭通百度竞价多少钱深圳关键词按天扣费长春外贸网站建设哪家好商丘SEO按天计费报价包头网站优化推广公司韶关建设网站报价衢州企业网站建设推荐大理SEO按天计费哪家好昌都网站推广工具天门seo哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化