八大排序算法

0. 前言

众所周知,对于顺序表,以二分法查找一个数,算法时间复杂度为O(nlongn)。因而,一次排好序,便可以节约很多查找的时间。

由此可见,排序算法尤为重要。以下介绍八大排序算法,都是从小到大排序。

1. 插入排序

i从1n-1,每次将第i位的数插入到前面已排好序的序列中。

插入的方法为:将i与i-1位置的数比较,i小则交换并i--,i大则停止插入。

// 插入排序
void InsertSort(int *num, int n, int start, int grep)
{
    // 对第i个数往前面已排好序的数组插入
    for (int i=start; i<n; i+=grep)
    {
        // 当j位置数小于j-1位置数时,交换;若大于则不交换
        for (int j=i; j>=0+grep && num[j]<num[j-grep]; j-=grep)
        {
            int temp = num[j];
            num[j] = num[j-grep];
            num[j-grep] = temp;
        }
    }
}

2. 希尔排序

将以5 3 1为增量的序列依次进行插入排序。例如,增量为5时,对(a0,a5...)、(a1,a6...)、(a2,a7...)、(a3,a8...)、(a4,a9...)序列依次进行插入排序。

// 希尔排序
void ShellSort(int *num, int n)
{
    // 对每隔5个数的序列排序,一共有5个这样序列 ,起始点分别为0,1,2,3,4
    for (int i=0; i<5; i++) InsertSort(num, n, i, 5);
    // 对每隔3个数的序
    for (int i=0; i<3; i++) InsertSort(num, n, i, 3);
    InsertSort(num, n, 0, 1);
}

3. 冒泡排序

每次从0到i序列的数中挑取最大的数放在i位置,i从n-10

挑取最大数的过程为:将j位置数与j+1位置比较,j大则交换,小不交换,j++。

// 冒泡排序
void BubbleSort(int *num, int n)
{
    for (int i=0; i<n; i++)
    {
        bool allSort = true;
        for (int j=1; j<n-i; j++)
        {
            if (num[j] < num[j-1])
            {
                int temp = num[j];
                num[j] = num[j-1];
                num[j-1] = temp;
                allSort = false;
            }
        }
        // 如果遍历一次所有已经排好序,则不需要再排序了
        if (allSort) break;
    }
}

4. 快速排序

遍历一次数组,以第一个数为标准,分成三部分,实现:第一个数在中间,小于它的数在它前面,大于它的数在它后面。然后对前部分和后部分递归该操作,递归终点为数组中个数为1。此处要注意的是,中间部分的数不需要再递归操作。

快排核心

遍历一遍数组,空间复杂度为O(1),将数组分为三部分的方法为:

  • 1)取出第一个数暂存,设定ij分别指向数组头和尾,pos指向空位置;
  • 2)j--,直到比第一个数小,取出j位置数放到pos位置,pos=j
  • 3)i++,直到比第一个数大,取出i位置数放到pos位置,pos=i
  • 其中,要时刻保持i<j

代码:

// 快速排序
void QuickSort(int *num, int n)
{
    // 递归终止条件
    if (n <= 1) return;

    int i = 0, j = n-1;
    int temp = num[i];  // 将i位置的数先存起来,i位置现在相当于“空”
    while (i<j)
    {
        while (num[j] >= temp && j>i) j--;
        // 此处直接将j位置不合格的数放在i位置就可以了,因为i位置本来就是“空的”
        num[i] = num[j];
        while (num[i] <= temp && i<j) i++;
        num[j] = num[i];
    }
    num[i] = temp;

    QuickSort(num, i);
    QuickSort(num+i+1, n-i-1);  // 此处不用再把pos位置的数传进去排序了!!
}

5. 选择排序

每次从i到n-1个数中选最小的数放在i位置,i从0到n-1。

// 选择排序
void SelectSort(int *num, int n)
{
    for (int i=0; i<n; i++)
    {
        int min = num[i], pos;
        for (int j=i+1; j<n; j++)
        {
            if (num[j] < min)
            {
                min = num[j];
                pos = j;
            }
        }
        num[pos] = num[i];
        num[i] = min;
    }
}

6. 堆排序

堆排序,又称完全二叉树排序。是将数组看作一颗完全二叉树,数组i位置的左右孩子分别为2*i+12*i+2位置。

排序过程为:

  • 1)建堆:从最后一个往第一个,逐个进行“筛选”,即建成最大顶堆;
  • 2)排序:将堆顶取出,与最后一个进行交换,然后再对第一个点进行“筛选”,此次“筛选”数组长度减一;
  • 筛选:对某个结点筛选,即当该结点两个孩子中存在值比该结点大的,就交换位置。然后重复操作,直到结点的两个孩子的值都比该结点小,或孩子为空。

代码:

// 堆排序中的筛选算法
void HeapAdjust(int *num, int n, int pos)
{
    int lchild = 2*pos+1, rchild = 2*pos+2; // 左孩子和右孩子的位置
    int maxPos;
    while (pos<n && lchild<n)
    {
        // 找到孩子中最大那个孩子
        if (num[lchild] >= num[rchild]) maxPos = lchild;
        else if (rchild < n) maxPos = rchild;

        // 如果筛选点小于孩子中最大的,则筛下去继续操作
        if (num[pos] < num[maxPos])
        {
            int temp = num[maxPos];
            num[maxPos] = num[pos];
            num[pos] = temp;

            pos = maxPos;
            lchild = 2*pos+1, rchild = 2*pos+2;
        }
        else break;
    }
}

// 堆排序(完全二叉树排序)
void HeapSort(int *num, int n)
{
    // 从底往顶逐个筛选,建成小顶堆
    for (int i=n-1; i>=0; i--)
    {
        HeapAdjust(num, n, i);
    }

    // 交换第一个和最后一个,再对第一个筛选
    for (int i=n-1; i>0; i--)
    {
        int temp = num[0];
        num[0] = num[i];
        num[i] = temp;
        HeapAdjust(num, i, 0);
    }
}

7. 归并排序

对于一段数组先分两半,各自进行归并排序,再将两半部分内容有序地合并起来,此时只需各遍历一次即可,采用递归实现。递归的终结点是数组中只有一个数时则不需要进行上述操作。

// 归并排序
void MergerSort(int *num, int n)
{
    // 递归终止条件
    if (n == 1) return;

    int mid = n/2;
    // 对前半部分归并排序
    MergerSort(num, mid);
    // 对后半部分归并排序
    MergerSort(num+mid, n-mid);

    // 将排好序两部分合并到temp数组
    int *temp = (int*)malloc(sizeof(int) * n);
    int i = 0, j = mid, k = 0;
    while (i<mid && j<n)
    {
        if (num[i] <= num[j])
        {
            temp[k] = num[i];
            i++;
        } else
        {
            temp[k] = num[j];
            j++;
        }
        k++;
    }
    // i指针未到中间
    while (i<mid)
    {
        temp[k] = num[i];
        k++;
        i++;
    }
    // j指针未到结尾
    while (j<n)
    {
        temp[k] = num[j];
        k++;
        j++;
    }

    // 将排序好的temp数组赋给num
    for (int i=0; i<n; i++) num[i] = temp[i];
    free(temp);
}

8. 基数排序

在所有要排序的数据中找到最大的数,求出它的位数n。

建立编号从0到9的十个队列。

i从最高位n到最低位1。

遍历数组将第i位为0的依次放入编号为0的队列中,第i位为1的放入编号为1的队列中。。。

所有数入相应队列后,再依次将0到9编号队列中数取出放回数组中。

然后对低一位重复该操作。

// 基数排序
void RadixSort(int *num, int n, int limit)
{
    queue<int> radix[10];

    // i是余数
    for (int i=1; i<limit; i*=10)
    {
        for (int j=0; j<n; j++)
        {
            radix[num[j]/i%10].push(num[j]);
        }
        int j=0;
        for (int k=0; k<10; k++)
        {
            while (!radix[k].empty())
            {
                num[j] = radix[k].front();
                radix[k].pop();
                j++;
            }
        }
    }
}

9. 八大排序时间复杂度比较

sortCmp.png

10.

更多详细代码和时间复杂度比较,见github

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

推荐阅读更多精彩内容