排序算法总结

直接插入排序

 

1、将待排序的记录放入数组 arr[n] 中;

2、循环 n-1 次,使用顺序查找法,判断 arr[i] 在序列 arr[0]~arr[i-1] 中的位置,然后将 arr[i] 插入序列 arr[0]~arr[i] 中,得到 arr[0]~arr[i] 的有序序列,继续循环,最终得到长度为 n 的有序序列 arr[n]。

 

演示:

无序数组 p[] = {51, 22, 56, 81, 17, 23, 90, 16};  黄色标记位表示插入的位置。

 C++代码实现:

using namespace std;
void InsertSort(int l[], int n){
    for(int j,i=1; i<n; i++){
        if(l[i]<l[i-1]){
            int x = l[i];
            l[i] = l[i-1];
            for(j=i-2; j>=0&&x<l[j]; j--){
                l[j+1] = l[j];
            }
            l[j+1] = x;
        }
    }
    for(int i=0; i<n; i++){
        cout << l[i] <<" ";
    }
}
int main(){
    int p[] = {51, 22, 56, 81, 17, 23, 90, 16};
    InsertSort(p, 8);
    return 0;
}

时间复杂度为 O(N^2),空间复杂度为O(1)

算法评价:(1)稳定排序;

     (2)算法简便,且容易实现;

     (3)同时适用于链式查找结构,且在查找过程中不需要移动记录,只需修改相应指针;

     (4)更适用于初始记录有序(正序)的情况,当初始记录无序,且 N 较大时,此算法的时间复杂度较高,不宜采用。

 


折半插入排序

 

1、将待排序的记录放在数组 arr[n] 中;

2、循环 n-1 次,每次循环使用折半查找法,判断 arr[i] 在有序序列 a[0]~a[i-1] 中的位置,然后将 arr[i] 插入序列 arr[0]~arr[i] 中,得到 arr[0]~arr[i] 的有序序列,继续循环,最终得到长度为 n 的有序序列 arr[n]

演示:无序数组 p[] = {51, 22, 56, 81, 17, 23, 90, 16};

下划线表示第一次折半判断位置,黄色标志位表示插入位置。

C++代码实现:

 1 //折半插入排序
 2 void BInsertSort(int l[], int n){
 3     for(int i=1; i<n; i++){
 4         int x = l[i];
 5         int low = 0;
 6         int high = i-1;
 7         while(low <= high){
 8             int m = (low + high) / 2;
 9             if(l[m] > x)    high = m-1;
10             else low = m+1;
11         }
12         for(int j=i-1; j>=high+1;j--)
13             l[j+1] = l[j];
14         l[high+1] = x;
15     }
16     for(int i=0; i<n; i++){
17         cout << l[i] <<" ";
18     }
19 }
20 int main(){
21     int p[] = {51, 22, 56, 81, 17, 22, 90, 16};
22     //InsertSort(p, 8);
23     BInsertSort(p, 8);
24     return 0;
25 }

 

算法特点:(1)稳定排序;

     (2)只适用于顺序结构,不适用与链式结构;

     (3)适合初始记录无序,较大时情况。


希尔排序

 

希尔排序又称为缩小增量排序。

希尔排序实质上是采用分组排序的方法。先将待排序记录分割成几组,对每组都进行直接插入排序。然后增加每组的数据量,继续进行插入排序,最后当经过几次分组排序后,整个序列处于”基本有序“状态,在对所有记录进行一次直接插入排序。

希尔排序的基础是根据每次相隔 d(增量)的记录分为一组进行直接插入排序,d(增量)< N 且逐次减少。

 

演示:无序数组 p[] = {51, 22, 56, 81, 17, 23, 90, 16, 55, 99};

增量数组 d[] = {5, 3, 1};

第一步:

根据增量 d[0] = 5进行组合 :{51, 23}, {22, 90},{56, 16},{81, 55},{17, 99}

对每组都进行直接插入排序 :{23, 51}, {22, 90},{16, 56},{55, 81},{17, 99}

此时的无序数组进行了第一次组合排序后变化:

{ 23,  22,  16 ,  55,  17,  51 , 90, 56 , 81,  99 }

 

第二步:

根据增量d[1] = 3进行组合 :{23,55,90,99},{22,17,56},{16,51,81}

对每组都进行直接插入排序 :{23,55,90,99},{17,22,56},{16,51,81}

此时的无序数组进行了第二次组合排序后变化:

{ 23,  17,  16 ,  55,  22,  51 , 90, 56 , 81,  99 }

 

第三步:

根据增量 d[2] = 1进行组合:{ 23,  17,  16 ,  55,  22,  51 , 90, 56 , 81,  99 }

对该组合进行直接插入排序,最终得到有序序列:{16 17 22 23 51 55 56 81 90 99}

 

C++ 代码实现:

 
#include <iostream>
using namespace std;
void ShellInsert(int *p, int d, int plen){
    for(int i=d; i<plen;i++){
        if(p[i] < p[i-d]){
            int k, x = p[i]; //x 为临时变量
            for(k=i-d; k>=0&&x<p[k]; k-=d){
                p[k+d] = p[k]; //记录后移
            }
            p[k+d] = x;
        }
    }
}
//按照增量序列 d[n],对 p 进行希尔排序
void InsertSort(int *p, int d[], int dlen, int plen){
    for(int i=0;i<dlen;i++){
        ShellInsert(p, d[i], plen);
    }
}
int main()
{
    //待排序记录
    int p[] = {51, 22, 56, 81, 17, 23, 90, 16, 55, 99};
    int d[] = {5, 3, 1}; //增量数组
    InsertSort(p, d, sizeof(d)/sizeof(d[0]), sizeof(p)/sizeof(p[0]));
    for(int i=0; i<sizeof(p)/sizeof(p[0]);i++)
        cout << p[i] << " ";
    return 0;
}

 

算法特点:(1)记录跳跃式的移动导致算法是不稳定的;

     (2)只能用于顺序结构,不能用于链式表;

     (3)增量序列可以有各种取法,但应该使增量序列中的值除了1之外没有其他公子,并且最后一个增量必须为1

     (4)记录总的比较次数和移动次数比直接插入排序少,越大时,效果越明显。适用于初始记录无序,很大时的情况。


 

冒泡排序

 

冒泡排序是最简单的一种交换排序。

它通过比较相邻的交换记录,如果发现逆序,则进行交换,从而使得较小的记录不断往前“浮动”(左移),或者是较大的记录向下“坠落”(右移),从而达到排序的结果。

 

演示: p[] = {51, 22, 56, 81, 17, 23, 90, 16};

 

第一次排序:

经过第一次冒泡排序后最大值90已经到了最右侧,此时的序列为 {22 51 56 17 23 81 16 90}

所以第二次排序结果为 :{22 51 56 17 23 16 81 90}

第三次 :                       {22 51 17 23 16 56 81 90}

第四次:                        {22 17 23 16 51 56 81 90}

第五次:                        {22 17 16 23 51 56 81 90}

第七次:                        {16 17 22 23 51 56 81 90}

 

C++代码实现:

#include <iostream>
using namespace std;
void BubbleSort(int *p, int len){
    int m = len;
    int flag = 1;
    while(m>0 && flag == 1){
        flag = 0;
        for(int i=0;i<m-1;i++){
            if(p[i] > p[i+1]){
                int x = p[i];p[i] = p[i+1]; p[i+1] = x;
                flag = 1;
            }
        }
        m--;
    }
}
int main()
{
    int p[] = {51, 22, 56, 81, 17, 23, 90, 16};
    BubbleSort(p, sizeof(p)/sizeof(p[0]));
    for(int i=0;i<sizeof(p)/sizeof(p[0]);i++)
        cout << p[i] << " ";
    return 0;
}

时间复杂度 O(N^2),空间复杂度 O(1)

算法特点:(1)稳定排序;

     (2)可用于链式存储结构;

     (3)移动次数较多,算法平均时间性能比直接插入排序差。不适合初始记录无序,较大的情况。


快速排序

在待排序的记录 p[n] 中任取一个作为支点 pri,将记录表中小于pri的记录放在 pri 的左边,大于pri 的记录放在pri的右边,将记录表 p[n] 分为两个子表,然后对两个子表继续进行分表,将两个子表分为4个子表…..一直到记录表 p[n] 中的记录有序。

实例:int p[] = {51, 22, 56, 81, 17, 23, 90, 16};

 

初始 51, 22, 56, 81, 17, 23, 90, 16

第一次排序:{22 17 23 16} 51 {56 81 90}

第二次排序:{17 16} 22 {23} 51 56 81 90

第三次排序:16 17 22 23 51 56 81 90

#include <iostream>
using namespace std;
int Sort(int *p, int low, int high){
    int x = p[low];
    int pri = p[low];
    while(low<high){
        while(low<high && pri<=p[high]) --high;
        p[low] = p[high];
        while(low<high && pri>=p[low])  ++low;
        p[high] = p[low];
    }
    p[low] = x;
    return low;
}
void SQ(int *p, int low, int high){
    if(low<high){
        int pio = Sort(p, low, high);
        SQ(p, low, pio-1);
        SQ(p, pio+1, high);
    }
}
void QuitSort(int *p, int len){
    SQ(p,0,len);
}
int main()
{
    int p[] = {51, 22, 56, 81, 17, 23, 90, 16};
    QuitSort(p, sizeof(p)/sizeof(p[0]));
    for(int i=0;i<sizeof(p)/sizeof(p[0]);i++)   cout<< (p[i]) << " ";
    return 0;
}

时间复杂度:O(nlog2n)

空间复杂度:O(n)

算法特点:(1)记录非顺序的移动导致排序算法是不稳定的

     (2)适用于顺序结构,不适用于链式结构

 


等风来,不如追风去。

 

 

 

 

 

 

 

posted @ 2019-05-28 21:00  我星伤悲  阅读(245)  评论(0编辑  收藏  举报