冒泡排序、快速排序

一、冒泡排序

【算法步骤】以n个数排序,从小到大排为例说明。

(1)待排序数中,从第1个数开始,每个数与它后面的数比较,若逆序则交换。完成后则最大数排到最后,待排序数个数减少一个。

(2)若待排序数个数为1,则它就是最小数,排在首位。否则,回到第(1)步。

具体讲解如下:

第一趟:第1、2个数比较,若逆序则交换;第2、3个数比较,若逆序则交换;……;第n-1、n个数比较,若逆序则交换。

比较次数为n-1次。比较结束后,最大数排到第n位。

第二趟:第1、2个数比较,若逆序则交换;第2、3个数比较,若逆序则交换;……;第n-2、n-1个数比较,若逆序则交换。

比较次数为n-2次。比较结束后,第二大的数排到第n-1位。

……

第n-1趟:第1、2个数比较,若逆序则交换。

比较次数为1次。比较结束后,第二小的数排在第2位。

第一小的数不用再比较。冒泡排序结束。

【注意】1、共进行n-1趟。第一趟比较n-1次,第二趟比较n-2次,……第n-1趟比较1次。2、可能会发生相邻元素间的交换。

【优化】引入布尔变量no_sort=1,如果某趟有交换,则no_sort=0,否则no_sort=1,内循环结束后判断no_sort是否为1,若为1,则退出外循环,否则,继续外循环。

【稳定性】稳定

【时间复杂度】

1、没有优化时为O(n^2)。

2、优化后:

最好情况下,待排序数已经按要求排好序,则只进行n-1次比较,不用交换,故时间复杂度为O(n)。

最坏情况下,待排序数刚好倒序,则要进行(n-1)+(n-2)+...+1=n*(n-1)/2次比较及交换,故时间复杂度为O(n^2)。

【示例代码】

//冒泡排序(优化后)
void bubblesort(int a[],int n){
	bool no_sort;//判断是否有交换,如果有一轮没有交换,则随之终止循环 
	for(int i=n-1;i>=1;i--){//循环的次数为数组长度-1,排剩下的最后一个数不需要排序
		no_sotr=true;
		for(int j=1;j<=i;j++){//循环次数为待排序数-1
			swap(a[j],a[j+1]);
			no_sort=false;
		}
		if(no_sort)break;
	}	
}

二、快速排序

【算法步骤】以n个数排序,从小到大排为例说明。

(1)随机选取一个元素(通常取中间元素)作为枢轴或支点。

(2)将所有小于等于支点的元素放在左边序列,所有大于等于支点的元素放在右边序列。这样完成一趟快速排序。

(3)继续分别对这两个子序列进行快速排序,直到整个序列有序。

具体做法如下:

(1)引入两个指针i与j,分别指向待排序元素的首l与尾r。支点记为mid。

(2)i从左往右扫描,指向第一个大于等于mid的元素。j从右往左扫描,指向第一个小于等于mid的元素。交换这两个元素。如果i<=j,则重复(2)。

(3)如果左子序列元素个数大于等于2,则对左序列进行快速排序。右序列同样处理。如果元素个数为1个,则不用继续快速排序。

【注意】1、这是对冒泡算法的改进,采用了分治的思想。2、只关心支点的值,不用关心支点的位置。3、可能发生不相邻元素间的交换,从而改变两个相等元素的先后位置,所以快速排序是不稳定排序。

【稳定性】不稳定

【时间复杂度】平均为O(nlogn),最坏为O(n^2)。

平均情况下,每趟快排结束后,每次划分使两个子序列的长度大致相等,时间复杂度为O(nlogn)。

【证明】 已知T(n)=2T(n/2)+n

=2*(2T(n/4)+n/2)+n=2^2T(n/2^2)+2n

=2^2(2T(n/8)+n/4)+2n=2^3T(n/2^3)+3n

=…

令n=2^m,则

T(n)=2^mT(1)+mn=2^lognT(1)+nlogn=n+nlogn=O(nlogn)

补充说明:递归logn层,每层比较O(n)次,故时间复杂度为O(nlogn)。

最坏情况下,是待排序记录已经排好序。以每次快排都以第一个待排序元素为支点为例说明:第一趟经过n-1次比较后第一个记录保持位置不变,并得到一个n-1个元素的子序列;第二趟经过n-2次比较,将第二个记录定位在原来的位置上,并得到一个包括n-2个元素的子序列,依次类推,这样总的比较次数是: (n-1)+(n-2)+…+1=n(n-1)/2,从而时间复杂度为O(n^2)。

【示例代码】

下面两种不同代码有比较大的区别:

1、以中间位置元素为支点,将小于等于支点的数放在左边序列,大于等于支点的数放在右边序列。i与j是两个哨兵,i从左往右, j从右往左,直到两个哨兵错过身,也就是说,两个哨兵碰面了,也要作交换!

//快速排序,以中间位置元素为支点
void qsort(int l, int r)
{
    int i=l,j=r,mid=a[(l+r)/2];//指针i,j。支点 
    while(i<=j){//不能少等号
        while(a[i]<mid)i++;//不能有等号
        while(a[j]>mid)j--;//不能有等号
        if(i<=j){//不能少等号
        	swap(a[i],a[j]);i++;j--;
        }
    }
    if(l<j)qsort(l,j);
    if(i<r)qsort(i,r);
}

2、以序列第一个元素为支点。将小于支点的元素归在左子序列,大于支点的元素归在右子序列。支点不进入下一层递归。

//快速排序,以序列第一个元素为支点 
void qsort(int l, int r)
{
    if(l>=r) return;
    int i=l,j=r;
    int key=a[i];    /*用数组的第一个记录作为分区元素*/
    while(i!=j){
        while(i<j&&a[j]>=key) j--; //从右向左扫描,找第一个码值小于key的记录,并交换到a[i]  
        a[i]=a[j];
        while(i<j&&a[i]<=key) i++; //从左向右扫描,找第一个码值大于key的记录,并交换到a[j]    
        a[j]=a[i];    
    }
    a[i]=key;    //分区元素放到正确位置
    qsort(l,i-1);
    qsort(i+1,r);
}

 

 

参考文献:

1、https://blog.csdn.net/K346K346/article/details/50791102

2、c++信息学奥赛一本通

3、https://blog.csdn.net/hn_gsf/article/details/52249621

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
冒泡排序快速排序都是常见的排序算法,它们各自有不同的优缺点。 冒泡排序的基本思想是通过相邻元素的比较和交换来将较大的元素逐渐“冒泡”到数组的末尾。具体步骤如下: 1. 从数组的第一个元素开始,依次比较相邻的两个元素,如果前一个元素大于后一个元素,则交换它们的位置。 2. 继续比较下一对相邻元素,重复上述操作,直到最后一对元素。 3. 重复以上步骤,每次比较的元素个数减少一,直到所有元素都排好序。 冒泡排序的优点是实现简单,代码易于理解和实现。然而,冒泡排序的缺点是效率较低,特别是在处理大规模数据时,时间复杂度为O(n^2),性能较差。 快速排序是一种分治法的排序算法,它通过选择一个基准元素将数组分成两个子数组,然后递归地对子数组进行排序。具体步骤如下: 1. 选择一个基准元素(通常选择第一个或最后一个元素)。 2. 将数组分成两个子数组,小于基准元素的放在左边,大于基准元素的放在右边。 3. 递归地对左右子数组进行快速排序。 4. 合并左右子数组和基准元素。 快速排序的优点是在平均情况下具有较高的效率,时间复杂度为O(nlogn)。它也是一种原地排序算法,不需要额外的空间。然而,快速排序的缺点是在最坏情况下(如已经有序的数组),时间复杂度可能达到O(n^2),性能下降。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值