排序算法——交换类排序

今天我们讨论一下交换类排序。
交换排序的算法思想:通过交换逆序的元素实现交换排序。交换排序主要有两种:一种是冒泡排序,一种是快速排序。

一、冒泡排序

算法思想:

冒泡排序是一种简单的交换类排序算法,它是通过交换相邻两个数据元素,逐步将排序序列变成有序序列。基本算法思想描述如下:
假设待排序元素有n个,从第一个元素开始一次交换相邻的两个逆序元素,直到最后一个元素为止。第一趟排序结束,将最大的元素移动到序列末尾。按照上述方式进行第二趟排序,次大的元素将会被移动到倒数第二个位置。依次类推,经过n-1趟排序后,整个序列都是有序序列。每趟排序过程,值小的元素向前移动,值大的元素向后移动,就像气泡一样向上升,因此被称为冒泡排序。
排序过程:{}中为有序序列
初始状态:56 72 44 31 99 21 69 80
第一趟:56 44 31 72 21 69 80 {99}
第二趟:44 31 56 21 69 72 {80 99}
第三趟:31 44 21 56 69 {72 80 99}
第四趟:31 21 44 56 {69 72 80 99}
第五趟:21 31 44 {56 69 72 80 99}
第六趟:21 31 {44 56 69 72 80 99}
第七趟:21 {31 44 56 69 72 80 99}
当第七趟完成后,序列第一个数即为最小元素,所以全部序列有序。咦,怎么回事,第五趟排序完成以后序列就全部有序了,怎么还要进行后面的第六七啊?所以我们就对其做一个小小的优化,当第K趟排序过后,发现没有做任何交换,说明序列全部有序不需要再进行下面几趟的排序了。
冒泡排序函数的实现:

void BubbleSort(int a[],int n) {
    for(int i=1;i//n-1趟排序 
        int flag=0;//每趟排序前标记为0,未进行交换 
        for(int j=0;j//每趟排序将无序序列中最大数移至无序序列最后面 
            if(a[j] > a[j+1]) {//将较大的元素后移 
                flag=1;//标记交换 
                //交换相邻两元素 
                int temp=a[j];
                a[j]=a[j+1];
                a[j+1]=temp;
            }
        }
        if(!flag) break;//如果未发生交换,排序完成 
    }
}

一下给出主函数的书写,包括排序算法和输出序列函数的调用。

#include
void Print(int a[],int n);
void BubbleSort(int a[],int n);
int main() {
    int a[]={56,72,44,31,99,21,69,80};
    BubbleSort(a,8);
    Print(a,8);
    return 0;
}
void Print(int a[],int n) {
    for(int i=0;iprintf("%d ",a[i]);
    printf("\n");
} 

主要用途:

冒泡算法简单易懂,适用于待排序元素较少且对时间要求不高的场合。

稳定性与复杂度:

冒泡排序是一种稳定的排序方法。若有n个元素,则一般需要n-1趟排序,每趟排序需要进行n-i次比较,其中i=1,2,3…,n-1。时间复杂度为O(n2),空间复杂度为O(1)。

二、快速排序

接下来咱们就来看看号称快速排序的排序算法。

算法思想:

快速排序是冒泡排序算的改进,也属于交换类排序算法。基本算法思想描述如下:
若待排序元素个数n个,放在a[0…n-1]中,在序列中选择一个pivot中,以pivot为基准,将小于pivot的元素放左边,大于pivot元素放右边。序列又被分为pivot左边的序列和pivot右边的序列,重复上述过程,直到每个序列都只有一个元素为止。我们这里为了讨论方便,向将pivot选为每个序列的第一个元素。(可能这样做存在问题,可以随机选取pivot将提高效率,今天先不讨论)
未排序序列为:55 22 44 67 35 77 18 69
pivot=55,我们定义 i,j指针开始分别指向序列左端和右端,从右边向左找一个小于pivot(55)的数,j移动到18的位置,a[j] 赋值给a[i],从左边向右找个大于pivot(55)的数和i移动到67的位置,a[i] 赋值给a[j],直至i>=j为止。最后将pivot赋值给a[i],完成此趟排序,{18 22 44 35} 55 {77 67 69}。重复上述过程,一直进行到每个序列只有一个元素为止。
第二趟结果:18 {22 44 35} 55 {69 67} 77
第三趟结果:18 22 {44 35} 55 {67} 69 77
第四趟结果:18 22 {35} 44 55 {67} 69 77
第五趟结果:18 22 35 44 55 67 69 77
以上的排序过程,我们可以看出来快速排序可以通过递归调用实现,通过不断的划分,将一个大问题划分成子问题然后再合并,这个就是分治的思想。最后我们可以写出快速排序的算法。


void QuickSort(int a[],int n){
//统一接口实现 
    QSort(a,0,n);
}
void QSort(int a[],int low,int high){
    //将a[low...high-1]元素排序,不包括右端点 
    if(low >= high-1) return ;//当该序列只有一个元素是结束 
    int i=low,j=high-1,pivot=a[i];

    while(i < j){
    //当i>=j时,此趟排序结束。 
        while(i= pivot) 
            j--;
        a[i]=a[j];
        while(i//完成一个序列的划分 
    //以i为界分成两个序列 
    QSort(a,low,i);//a[low..i-1],不包括a[i] 
    QSort(a,i+1,high);//a[i+1...high-1],不包括a[high] 
} 

主要用途:

快速排序是冒泡排序的改进,实现起来比较复杂,主要用于数据量较大的数据排序,它的时间效率远高于冒泡排序。

稳定性与复杂性:

快速排序是不稳定的排序算法,在数据量大时明显。最坏时间复杂度O(n2),最好时间复杂度O(n2),平均时间复杂度为O(nlog2n)。快速排序空间复杂度O(log2n)。

你可能感兴趣的:(排序算法)