冒泡排序与快速排序(代码实例)

冒泡排序(Bubble Sort)和快速排序(Quick Sort)都属于交换排序类。今天我们将从最基本的冒泡排序入手,介绍冒泡排序和快速排序。

一.冒泡排序(Bubble Sort)

1.基本思想

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。

它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

2.算法实现

将序列中的左右元素,如果第一个比第二个大,就交换他们两个。依次比较,保证右边的元素始终大于左边的元素(第一轮结束后,序列最后一个元素一定是当前序列的最大值)
对序列当中剩下的n-1个元素,再执行上一步操作,直到没有反序的记录为止。
对长度为n的序列,一共需要执行n-1轮比较。
在这里插入图片描述

3.时间复杂度

原始的冒泡排序是稳定的,由于该排序算法的每一轮都要遍历一遍所有的元素,轮转的次数和元素数量相当,所以时间复杂度为O(N^2)。

4.代码实现

void BubbleSort(int *a, int len)
{
    int i,j,k,temp;
    
    for(i=0;i<len-1;i++)
    {
        for{j=len-1;j>i;j--)
        {
            if(a[j-1]>a[j])
            {
               temp=a[j-1];
               a[j-1]=a[j];
               a[j]=temp;
            }
        }
    }
}

这个代码很简单,使用双循环的方式进行排序。外部的循环控制所有回合,内部循环代表每一轮的冒泡处理,先进行元素比较,再进行元素交换。那么这个代码该怎么进行优化呢??我们现在来回顾一下之前的描述细节,仍然以 5,8,6,3,9,2,1,7 为例,当排序伏安法分别执行到第六、第七、第八轮的时候,数列的状态其实已经变为有序的了。

第六轮状态:
在这里插入图片描述
第七轮状态:
在这里插入图片描述
第八轮状态:
在这里插入图片描述
在这种情况下,我们就不必要对这几次在重新进行排序,这样就会减少执行的次数,因此,我们可以进行一个优化,就是设置一个flags,如果已经排序了那么设置为0;如果不是有序的,那么设置为1。现在我们来看看优化后的代码。

void BubbleSort(int *a, int len)
{
    int i,j,k,temp;
    int flags = 0;
    
    for(i=0;i<len-1;i++)
    {
        for{j=len-1;j>i;j--)
        {
            if(a[j-1]>a[j])
            {
               temp=a[j-1];
               a[j-1]=a[j];
               a[j]=temp;
               flags = 1;//不是有序的,flags设置为1;
            }
        }
        if (flags == 0)
		return;

    }
}

这一个版本的代码只是做了一个小小的改动,利用fags做标记。如果在本轮排序中,元素有交换,则说明数列无序,如果没有交换,则说明数列已然有序,直接返回。

二.快速排序(Quick Sort)

1.基本思想

快速排序和冒泡排序类似,都是基于交换排序思想。但是快速排序是对冒泡排序的一种改进,从而提高了执行效率。

快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

2.算法实现

一趟快速排序的算法是:
2.1、设置两个变量 low、high,排序开始时:low=0,high=size-1。

2.2、整个数组找基准正确位置,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面
(1)默认数组的第一个数为基准数据,赋值给key,即key=array[low]。
(2)因为默认数组的第一个数为基准,所以从后面开始向前搜索(high–),找到第一个小于key的array[high],就将 array[high] 赋给 array[low],即 array[low] = array[high]。(循环条件是 array[high] >= key;结束时 array[high] < key)
(3)此时从前面开始向后搜索(low++),找到第一个大于key的array[low],就将 array[low] 赋给 array[high],即 array[high] = array[low]。(循环条件是 array[low] <= key;结束时 array[low] > key)
(4)循环 2-3 步骤,直到 low=high,该位置就是基准位置。
(5)把基准数据赋给当前位置。

2.3、第一趟找到的基准位置,作为下一趟的分界点。

2.4、递归调用(recursive)分界点前和分界点后的子数组排序,重复2.2、2.3、2.4的步骤。

2.5、最终就会得到排序好的数组。
在这里插入图片描述
快速排序之所以比较快,是因为与冒泡排序相比,每次的交换时跳跃式的,每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。

在C语言中可以用函数qsort()可以直接为数组进行排序。

3.时间复杂度

快速排序涉及到递归调用,所以该算法的时间复杂度还需要从递归算法的复杂度开始说起;
递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n) ;对于递归算法的时间复杂度这里就不展开来说了;
最优情况下时间复杂度
快速排序最优的情况就是每一次取到的元素都刚好平分整个数组;
此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]为平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;
下面来推算下,在最优的情况下快速排序时间复杂度的计算(用迭代法):
T[n] = 2T[n/2] + n ----------------第一次递归
令:n = n/2 = 2 { 2 T[n/4] + (n/2) } + n ----------------第二次递归
= 2^2 T[ n/ (2^2) ] + 2n
令:n = n/(2^2) = 2^2 { 2 T[n/ (2^3) ] + n/(2^2)} + 2n ----------------第三次递归
= 2^3 T[ n/ (2^3) ] + 3n
令:n = n/( 2^(m-1) ) = 2^m T[1] + mn ----------------第m次递归(m次后结束)

当最后平分的不能再平分时,也就是说把公式一直往下跌倒,到最后得到T[1]时,说明这个公式已经迭代完了(T[1]是常量了)。

得到:T[n/ (2^m) ] = T[1] ===>> n = 2^m ====>> m = logn;
T[n] = 2^m T[1] + mn ;其中m = logn;
T[n] = 2^(logn) T[1] + nlogn = n T[1] + nlogn = n + nlogn ;其中n为元素个数
又因为当n >= 2时:nlogn >= n (也就是logn > 1),所以取后面的 nlogn;

综上所述:快速排序最优的情况下时间复杂度为:O( nlogn )

最差情况下时间复杂度
最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)

这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

综上所述:快速排序最差的情况下时间复杂度为:O( n^2 )

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

4.代码实现

void QuickSort(int *arr, int left, int right)
{
    int base,t;
    int rtemp,ltemp;

    ltemp=left;
    rtemp=right;
    base=arr[(left+right)/2];       //确定分界值

    while(ltemp<rtemp)
    {
        while(arr[ltemp]<base)
        {
             ++ltemp;
        }
        while(arr[rtemp]>base)
        {
            ++rtemp;
        }

        if(ltemp<=rtemp)
        {
           t=arr[ltemp];
           arr[ltemp]=arr[rtemp];
           arr[rtemp]=t;
           --rtemp;
           ++ltemp;
        }
   }
   
   if(ltemp == rtemp)
   {
      ltemp++;
   }

   if(left<rtemp)
   {
       QuickSort(arr, left, ltemp-1);      //递归调用
   }
   if(ltemp<right)
   {
       QuickSort(arr, rtemp+1, right);      //递归调用
   }

}

程序中首先确定分界值base为数组中间的值,也可以选在其他的位置,比如数组的第一个数据。然后按照快速排序的思路进行处理。接着通过递归调用,处理分界值左侧和右侧的元素。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值