排序算法——交换类排序

来源:互联网 发布:转行做程序员 编辑:程序博客网 时间:2024/06/05 09:09

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

一、冒泡排序

算法思想:

冒泡排序是一种简单的交换类排序算法,它是通过交换相邻两个数据元素,逐步将排序序列变成有序序列。基本算法思想描述如下:
假设待排序元素有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;i++){//n-1趟排序         int flag=0;//每趟排序前标记为0,未进行交换         for(int j=0;j<n-i;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<stdio.h>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;i<n;i++)        printf("%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<j && a[j] >= pivot)             j--;        a[i]=a[j];        while(i<j && a[i] <= pivot)             i++;        a[j]=a[i];        a[i]=pivot;    }//完成一个序列的划分     //以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)。

1 0
原创粉丝点击