交换排序(冒泡排序,优化冒泡排序,快速排序,以及快速排序优化的三种方法详解)

交换排序:利用交换元素的位置进行排序的方法称为交换排序。常用的交换排序有冒泡排序和快速排序。

冒泡排序算法:

基本思想:n个元素,序列进行N-1次循环,依次比较相邻两个元素的大小,如果array[i]>array[i+1],则交换两个元素,反之则不交换。这样每次循环都能比较出数值最大的那个元素。

最好最坏情况:

最好情况是正序,时间复杂度是O(N),比较次数为n-1次

最坏情况是逆序,时间复杂度是O(N2),比较次数为n(n-1)/2

步骤:

初始序列:49 38 65 97 76 13 27 49*

  1.       38 49 65 76 13 27 49* 97

  2.       38 49 65 13 27 49* 76 97

  3.       38 49 13 27 49* 65 76 97

  4.       38 13 27 49 49* 65 76 97

  5.       13 27 38 49 49* 65 76 97

  6.       13 27 38 49 49* 65 76 97

  7.       13 27 38 49 49* 65 76 97

  8.       13 27 38 49 49* 65 76 97

根据过程可以看出冒泡排序是一个稳定排序算法。

算法代码:

//冒泡排序(稳定算法)
void MaoPaoSort(int *array,int len)
{
        int i, j;
        int temp;
        for (i = 0; i < len; ++i)
        {
               for (j = 1; j < len - i; j++)
               {
                       if (array[j] <array[j-1])
                       {
                              temp = array[j];
                              array[j] = array[j - 1];
                              array[j - 1] = temp;
                       }
               }
        }
}

可以将冒泡排序进行优化:

如果某一趟已经将序列排好,程序再继续运行就是多余的,我们可以将其终止。

算法代码:

void MaoPaoSort(int *array,int len)
{
        int i, j;
        int temp;
        int flag = 0;
        for (i = 0; i < len&&flag==0; ++i)
        {
               flag = 1;
               for (j = 1; j < len - i; j++)
               {
                       if (array[j] <array[j-1])
                       {
                              temp = array[j];
                              array[j] = array[j - 1];
                              array[j - 1] = temp;
                              flag = 0;
                       }
               }
        }
}

快速排序是冒泡排序的一种改进。

基本思想:

在待排序的序列中选取一个基准值,通常为第一个数字

然后经过一趟排序将这个序列分割成独立的两部分,

其中,比基准值小的数字放在左边,比基准值大的数字放在右边,

则可对这两部分记录继续递归进行排序,以达到整个序列有序。

步骤过程(动图):

算法代码:


//从两端交替扫描
int Partition(int *array, int low, int high)
{
        //选取一个基准值
        int pivokey = array[low];
        while (low < high)//当low==high时循环结束,按照基准值排序两边完成
        {
               //扫描右边,直至遇到一个小于基准值的数停止
               while (low<high&&array[high]>=pivokey)
                       --high;
               //将这个小于基准值的数取出放入左边
               array[low] = array[high];
               //扫描左边,直至遇到一个大于基准值的数停止扫描
               while (low < high&&array[low]<=pivokey)
                       ++low;
               //将这个大于基准值的数放入右边
               array[high] = array[low];
        }
        array[low] = pivokey;//将基准值填入
        return low;//返回基准值所在的位置
}
//快速排序(不稳定算法)
void QSort(int *array,int low,int high)
{
        if (low < high)//长度大于1
        {
               //找到最后填入基准值位置
               int pivotic = Partition(array,low,high);
               //再对左边序列进行排序
               QSort(array,low,pivotic-1);
               //对序列右边进行排序
               QSort(array,pivotic+1,high);
        }
        return;
}

   快速排序最佳情况下时间复杂度是 O(nlogn)。但是最坏情况下可能达到O(n^2)。说明快速排序达到最坏情况的原因。并提出改善方案并实现之。

快速排序算法的几种改进:

1.随机化算法

    基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望复杂度。

随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降到O(n^2)。

算法描述:

void swap(int *array, int index, int n)
{
        int temp;
        temp = array[index];
        array[index] = array[n];
        array[n] = temp;
}
int Partition1(int *array,int low,int high)
{
        if (array == nullptr || low <0)
               throw new std::exception("Invalid parameters\n");
        //选取一个随机值
        int index = (rand() % (high - low)) + low;
        //将随机数与第一位交换,接着就是经典的partition
        swap(array, index, low);
        //保存这个随机数
        int privokey = array[low];
        while (low < high)
        {
               //找到小于基准值的数
               while (low<high&&array[high]>=privokey)
                       high--;
               if (low < high)
               {
                       array[low] = array[high];
               }
               
               while (low < high&&array[low] <=privokey)
                       low++;
               if (low < high)
               {
                       array[high] = array[low];
               }
               
        }
        array[low] = privokey;
        return low;
}

2.三平均分区法

    与一般的快速排序方法不同,它并不是选择待排数组的第一个待排数组第一个数作为中轴。而是选用待排数组最左边、最右边和最中间的三个元素的中间值作为轴。

算法代码:

//求三个数中位数,并将其放在初始位置。       
void GetThreeValue(int *array, int low, int high)
{
        int mid = (low + (high - low)) >> 1;
        //先把最大的数放入到high位置,然后比较mid和low,如果mid比low大,则说明mid是中间值,放入low位置
        if (array[mid] > array[high])
        {
               swap(array,mid,high);
        }
        if (array[low] > array[high])
        {
               swap(array,low,high);
        }
        if (array[mid] > array[low])
        {
               swap(array,mid,low);
        }
}
//三平均分区法,可以使最坏情况的几率大大减小(减小退化问题)
int Partition2(int *array, int low, int high)
{
        if (array == nullptr || low < 0||low>high)
               throw new std::exception("Invalid Parameters\n");
        
         GetThreeValue(array, low, high);
         int privokey = array[low];
        while (low < high)
        {
               while (low < high&&array[high] >= privokey)
                       high--;
               if (low<high)
               {
                       array[low] = array[high];
               }
               while (low < high&&array[low] <= privokey)
               {
                       low++;
               }
               if (low < high)
               {

                    array[high] =array[low];                       }
        }
        array[low] = privokey;
        return low;
}

3.每次选取数据集中的中位数做基准值。

其他可以改进的地方(网络搜集) :

1、  快速排序在处理小规模数据时的表现不好.

这个时候可以改用插入排序。

当数据规模小于一定程度时,改用插入排序。具体小到何种规模时,采用插入排序,这个理

论上还不解,一些文章中说是 5 到 25 之间。SGI STL 中的快速排序采用的值是 10.

2、对于一个每个元素都完全相同的一个序列来讲,快速排序也会退化到 O(n^2)。

要将这种情况避免到,可以这样做:

在分区的时候,将序列分为 3 堆,一堆小于中轴元素,一堆等于中轴元素,一堆大于中轴元

素,下次递归调用快速排序的时候,只需对小于和大于中轴元素的两堆数据进行排序,中间

等于中轴元素的一堆已经放好。

参考博客:https://blog.csdn.net/lingfengtengfei/article/details/12376889

猜你喜欢

转载自blog.csdn.net/suoyudong/article/details/88139516