八大排序算法

部分参考 八大排序算法

概述


排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

我们这里说说八大排序就是内部排序。

这里写图片描述

当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。

快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短。

1. 插入排序-直接插入排序

部分参考 经典排序算法 – 插入排序Insertion sort白话经典算法系列之二 直接插入排序的三种实现


插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。
下图演示了对4个元素进行直接插入排序的过程,共需要(a),(b),(c)三次插入。

图1

设数组为a[0…n-1]。

  1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1
  2. 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
  3. i++并重复第二步直到i==n-1。排序完成。

下面给出C++实现插入排序算法的代码

void insert_sort(int unsorted[], int n)
{
    // 以第0个元素为有序数组,把第一个元素作为插入元素
    for (int i = 1; i < n; i++) {
        // 如果前面的元素大于此时的插入元素
        if (unsorted[i-1] > unsorted[i]) {
            // 将前面的元素向后移动,直至小于此时的插入元素
            int temp = unsorted[i];
            int j = i;
            while (j>0 && unsorted[j-1] > temp) {
                unsorted[j] = unsorted[j-1];
                j--;
            }
            // 将插入元素插入到这个位置
            unsorted[j] = temp;
        }
    }
}

如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

效率:时间复杂度:O(n^2).

2. 插入排序—希尔排序(Shell`s Sort)


部分参考 常见排序算法 - 希尔排序 (Shell Sort)

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序。

基本思想

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

实例分析

假设有数组 array = [80, 93, 60, 12, 42, 30, 68, 85, 10],首先取 d1 = 4,将数组分为 4 组,如下图中相同颜色代表一组:

这里写图片描述

然后分别对 4 个小组进行插入排序,排序后的结果为:

这里写图片描述

然后,取 d2 = 2,将原数组分为 2 小组,如下图:

这里写图片描述

然后分别对 2 个小组进行插入排序,排序后的结果为:

这里写图片描述

最后,取 d3 = 1,进行插入排序后得到最终结果:

这里写图片描述

算法实现

先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。

C++代码实现希尔排序


/**
 *  直接插入的一般形式
 *
 *  @param a  <#a description#>
 *  @param n  <#n description#>
 *  @param dk 缩小增量,如果是直接插入排序,dk=1
 */
void ShellInsertSort(int a[],int n, int dk)
{
    for (int i = dk; i < n; i++) {
        if (a[i] < a[i-dk]) {
            int temp = a[i];
            int j = i;
            while (j > 0 && a[j-dk] > temp) {
                a[j] = a[j-dk];
                j -= dk;
            }
            a[j] = temp;
        }
    }
}

void shellSort(int a[], int n)
{
    int dk = n/2;
    while (dk >= 1) {
        ShellInsertSort(a, n, dk);
        dk/= 2;
    }
}

希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。

3. 选择排序—简单选择排序(Simple Selection Sort)


基本思想

在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。

这里写图片描述

操作方法

第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推…..
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。

算法实现

C++编写简单选择排序算法

#pragma mark -------简单选择排序

void printArray(int a[], int n)
{
    for (int i = 0; i < n; i++) {
        printf("%d ",a[i]);
    }
}
/**
 *  交换数组中的两个位置的数字
 *
 *  @param a <#a description#>
 *  @param i <#i description#>
 *  @param k <#k description#>
 */
void swapSort(int a[], int i, int k)
{
    int temp = a[k];
    a[k] = a[i];
    a[i] = temp;
}

/**
 *  找出数组中最小的数字的位置
 *
 *  @param a <#a description#>
 *  @param n <#n description#>
 *  @param i 从序列i开始往后面找
 *
 *  @return 返回的是最小值得序列值
 */
int selectMinKey(int a[], int n, int i)
{
    int k = i;
    for (int j=k; j < n; j++) {
        if (a[k] > a[j]) {
            k = j;
        }
    }
    return k;
}

void selectSort(int a[],int n)
{
    for (int i = 0; i< n; i++) {
        int minIndex = selectMinKey(a, n, i);
        swapSort(a, i, minIndex);
    }
}

4. 选择排序—堆排序(Heap Sort)


有点复杂,稍后再进行分析

5.交换排序—冒泡排序(Bubble Sort)


基本思想

在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

实例分析

冒泡排序的示例:

这里写图片描述

算法分析

#pragma mark --冒泡排序
void bubbleSort(int a[], int n){
    for (int i = 0; i < n; i++) {
        for (int j = i+1; j < n; j++) {
            if (a[i] > a[j]) {
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
    }
}

虽然代码简单,但是时间复杂度是O(n*n)。

6.交换排序—快速排序(Quick Sort)


参考 白话经典算法系列之六 快速排序 快速搞定

基本思想

  1. 选择一个基准元素,通常选择第一个元素或者最后一个元素
  2. 通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
  3. 此时基准元素在其排好序后的正确位置
  4. 然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

算法实现

int AdjustArray(int s[], int l, int r) //返回调整后基准数的位置  
{  
    int i = l, j = r;  
    int x = s[l]; //s[l]即s[i]就是第一个坑  
    while (i < j)  
    {  
        // 从右向左找小于x的数来填s[i]  
        while(i < j && s[j] >= x)   
            j--;    
        if(i < j)   
        {  
            s[i] = s[j]; //将s[j]填到s[i]中,s[j]就形成了一个新的坑  
            i++;  
        }  

        // 从左向右找大于或等于x的数来填s[j]  
        while(i < j && s[i] < x)  
            i++;    
        if(i < j)   
        {  
            s[j] = s[i]; //将s[i]填到s[j]中,s[i]就形成了一个新的坑  
            j--;  
        }  
    }  
    //退出时,i等于j。将x填到这个坑中。  
    s[i] = x;  

    return i;  
}  

void quick_sort1(int s[], int l, int r)  
{  
    if (l < r)  
    {  
        int i = AdjustArray(s, l, r);//先成挖坑填数法调整s[]  
        quick_sort1(s, l, i - 1); // 递归调用   
        quick_sort1(s, i + 1, r);  
    }  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值