数据结构和算法分析之排序算法--交换排序篇(冒泡排序和快速排序)

1. 交换排序–冒泡排序

基本思想:
在要排序的一组数中,对当前未排序好的范围全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的数往上冒,俗称冒泡。
总结:每当两相邻的数比较后发现他们的排序与排序要求相反时,就将他们互换。嵌套排序完成整个冒泡过程

冒泡排序的示例:
数据结构和算法分析之排序算法--交换排序篇(冒泡排序和快速排序)_第1张图片

算法的实现:
1、每次排序把最大的数排在最后一个;
2、然后下次排序可以少计算一次,因此是j-i;
3、j

void BubbleSort(int a[],int n)
{
    for(int i=0;i1;i++)
    {
        for(int j=0;j1;j++)//重要,每一轮把最大数排到最后
        {
            if(a[j]>a[j+1])
            {
                int tmp=a[j+1];
                a[j+1]=a[j];
                a[j]=tmp;
            }
        }
    }
}

冒泡排序算法的改进
改进方法:
加入一个标志变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程,减少时间损耗。(比如:有时排序到中间的步骤时,就全部排序完成,后面的排序过程是可以省略的,因此可以关闭开关)

改进的地方:
1、普通的冒泡算法是大的一致下沉,本次改进为小的一致上升;
2、加入一个标志位,用于判断是否需要进行后续排列;

代码如下:

void BubbleSorter( int a[],int n)
{
    int flag=1;
    for(int i=0;i1;i++)
    {

        for(int j=n-1;j>i;j--)
        {
            int flag=0;
            if(a[j]1])
            {
                int tmp=a[j];
                a[j]=a[j-1];
                a[j-1]=tmp;
                flag=1;
            }

        }
    }
}

算法效率–冒泡排序:

最好情况:若原数组本身就是有序的,仅需要n-1次比较就可以完成,时间复杂度为O(n);

一般情况:比较次数为:n-1+n-2+….+1=n(n-1)/2=O(n^2),因此时间复杂度依然为O(N^2);


2. 交换排序–快速排序

快速排序是在实践中最快的已知排序算法,它的平均运行时间是O(NlogN)。

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

如下是快速排序的示意图:
第一次快速排序的示意图:
数据结构和算法分析之排序算法--交换排序篇(冒泡排序和快速排序)_第2张图片

代码实现分析:
快速排序主要是由这几部完成的:1、通过一次快速排序完成中间位置的确定;2、通过迭代完成左右两测序列的排序,使用方式同第一步一样;

实现起来,伪代码如下:

void QuickSort(int a[],int p, int r,int n)
{
    if(pint q=Partiton(a,p,r);
        QuickSort(a,p,q-1,n);
        QuickSort(a,q+1,r,n);
    }
    return ;
}

数组的排序方式
这个是快速排序的重点内容,也是算法的核心内容。
核心算法可以简述为以下几步:
1、选取一个参考位,一般是第一个或最后一个,记为r。确定数组的开始位置,记为q;
2、快速排序是通过两个指针的移动完成的。由于本题是数组,给定下标为i, j。 i是给定数组起始位置的前一个位置,j是起始位置;(以上都是用到工具的初始化)
3、比较过程是由一个循环和一个判断完成的。循环过程中,j每次向前移动一个位置,表示循环位置;再判断当前位置a[j]

#include 
using namespace std;


int Partiton(int a[],int p,int r )
{
    int i,j;
    i=p-1;
    j=p;
    while(jif(a[j]//位置很关键
            int tmp=a[j];
            a[j]=a[i];
            a[i]=tmp;
        }
        (j)++;
    }
    //对最后一个数进行交换
    i++;
    int tmp=a[r];
    a[r]=a[i];
    a[i]=tmp;


    return i;
}
void print(int a[],  int n);
int count1=1;
void QuickSort(int a[],int p, int r,int n)
{
    if(pint q=Partiton(a,p,r);
        printf("第%d次快速排序:",count1);
        print(a,n);
        count1++;
        QuickSort(a,p,q-1,n);
        QuickSort(a,q+1,r,n);
    }
    return ;
}
void print(int a[],  int n)
{
    for(int j=0;jcout<" ";
    }
    cout<int main()
{
    int a[]={2,8,7,1,3,5,6,4};
    int n=8;
    int p=0;
    int r=n-1;
    QuickSort(a,p,r,n);
    /*int length;
    int* A;
    while(1)
    {
    cout<<"请输入数组的长度"<>length;
    if(length>0)
    {
    cout<<"请输入数组中的元素"<>A[i];
    }
    QuickSort(A,0,length-1);
    print(A,length);
    }
    }*/
    system("pause");
    return 0;
}

输入结果如下:

数据结构和算法分析之排序算法--交换排序篇(冒泡排序和快速排序)_第3张图片

算法效率–快速排序:

快速排序涉及到递归调用,所以该算法的时间复杂度还需要从递归算法的复杂度开始分析:
递归算法的时间复杂度为:T[n]=aT[n/b]+f(n);

最优情况下的时间复杂度
快速排序最优的情况就是每一次取到的元素(取的参考值)都刚好平分整个数组
此时的时间复杂度公式为:T[n]=2T[n/b]+f(n);其中2T[n/b]为平分后的子数组的时间复杂度;f(n)是解决这次平分数组所花费的时间;
计算得:
T[n] = 2^(logn) T[1] + nlogn = n T[1] + nlogn = n + nlogn ;其中n为元素个数;
又因为当n >= 2时:nlogn >= n (也就是logn > 1),所以取后面的 nlogn;
综上所述:快速排序最优的情况下时间复杂度为:O( nlogn )

最差情况下时间复杂度:
最差的情况就是每一次取到的元素就是数组中最大/最小的,这种情况其实就是冒泡排序。也就是每一次排好一个元素。
综上所述:快速排序最差的情况下时间复杂度为:O(n^2)

平均时间复杂度
快速排序的平均时间复杂度是:O(nlogn)


补充:快速排序的改进

由于快速排序的最坏情况是:取的参考值就是最大值或者最小值,这种情况的快速排序算法复杂度最高,和冒泡排序类似。

改进方法:
每次排序前取三个位置的数进行比较,分别是起始位置,终点位置和中间位置。对着三个位置的数的要求是:参考位的取值是取中间数。因此在每次快速排序需要对起始位置,终点位置和中间位置的值进行排序,尽量使得取得参考点是中间大小的值。

伪代码参考如下:

数据结构和算法分析之排序算法--交换排序篇(冒泡排序和快速排序)_第4张图片

你可能感兴趣的:(数据结构)