2.3 起泡排序、归并排序

一、排序器:统一入口

template <typename T>
void Vector<T>:: sort(Rank lo, Rank hi){//区间[lo, hi)
	switch(rand() % 5){//可试具体问题的特点灵活选取或扩充
		case 1: bubbleSort(lo, hi); break;//起泡排序
		case 2: selectionSort(lo, hi); break;//选择排序
		case 3: mergeSort(lo, hi); break;//归并排序
		case 4: heapSort(lo, hi); break;//堆排序
		default: quickSort(lo, hi); break;//快速排序
	}
}//在此统一接口下,具体算法可以采用不同实现

二、起泡排序

  • 起泡排序实现
template<typename T>void Vector<T>::bubbleSort(Rank lo, Rank hi)
{while(!bubble(lo, hi--));}//逐趟做扫描交换,直至全序
  • 改进:记录每扫描一趟后有没有交换元素位置,如果没有则表示前半部分元素已经是有序的,可以结束排序算法
template <typename T>bool Vector<T>::bubble(Rank lo, Rsnk hi){
	bool sorted = true;//整体有序标志
	while(++lo < hi)//自左向右,逐一检查各对相邻元素
		if(_elem[lo - 1] > _elem[lo]){//若逆序,则
			sorted = false;//意味着尚未整体有序,并需要
			swap(_elem[lo - 1], _elem[lo]);//交换
		}
	return sorted;//返回有序标志
}//时间复杂度,由三角形优化为梯形
  • 再改进:存在一种特殊情况,乱序部分集中在未扫描部分的前部,在每次扫描中消耗多余时间在已经有序但尚未扫描的部分中。故提出改进方法,记录每一趟扫描中最后一次交换的逆序对的位置。
template <typename T> void Vector<T>::bubbleSort(Rank lo, Rank hi)
{while (lo < (hi = bubble(lo, hi)));}//逐趟扫描交换,直至全序

template <typename T> Rank Vector<T>::bubble(Rank lo, Rank hi){
	Rank last = lo;//最右侧的逆序对初始化为[lo - 1, lo]
	while(++lo < hi)//自左向右,逐一检查各相邻元素
		if(_elem[lo - 1] > _elem[lo]){//若逆序,则
			last = lo;//更新最右侧逆序对位置记录,并
			swap(_elem[lo - 1], _elem[lo]);//交换
		}
	return last;//返回最右侧的逆序对位置
}//将前一版本中的逻辑型标志sorted,改为秩last
//复杂度由原来的三角形降为几个梯形的和
  • 综合评价
    • 效率最好是O(n), 最坏是O(n^2)
    • 输入含重复元素时,算法的稳定性(重复元素在输入、输出序列中的相对次序是否保持不变)是更为细致的要求。
    • 起泡排序算法是稳定的。在起泡排序中,元素a和元素b的相对位置发生变化,只有一种可能。经分别与其它元素的交换中,二者相互接近直至相邻,在接下来的一轮扫描中,二者因为逆序而交换位置。

三、归并排序

  • 原理
    • 分治策略。
    • 序列一分为二。O(1)
    • 子序列递归排序。2*T(n/2)
    • 合并有序子序列。O(n)
    • 归并排序的允许时间为O(nlogn)
  • 实现
template <typename T>
void Vector<T>::mergeSort(Rank lo, Rank hi){//[lo, hi)
	if(hi - lo < 2) return;//单元素区间自然有序
	int mi = (lo + hi) >> 1;//以中点为界
	mergeSort(lo, mi);//对前半段排序
	mergeSort(mi, hi);//对后半段排序
	merge(lo, mi, hi);//归并
}
  • 二路归并的基本实现
template <typename T> void Vector<T>::merge(Rank lo, Rank mi, Rank hi){
	T* A = _elem + lo;//合并后的向量A[0, hi - lo) = _elem[lo, hi)
	int lb = mi - lo; T* B = new T[lb];//前子向量B[0, lb) = _elem[lo, hi)
	for(Rank i = 0; i < lb; B[i] = A[i++]);//复制前子向量B
	int lc = hi - mi; T* C = _elem + mi;//后子向量C[0, lc) = _elem[mi, hi)
	for(Rank i = 0, j = 0, k = 0; (j < lb) || (k < lc);){//B[j]和C[k]中小者转至A的末尾
		if((j < lb) && (lc <= k || (B[j] <= C[k])))A[i++] = B[j++];//C[k]以无或者不大
		if((k < lc) && (C[k] < B[j])))A[i++] = C[k++];//B[j]已无或者不大
	}//该循环实现紧凑;但就效率而言,不如拆分处理
	delete [] B;//释放临时空间B
}

二路归并演示

  • 二路归并改进:B提前耗尽,因为C本来就是A的一部分,故此时归并可直接结束。
for(Rank i= 0, j = 0, k = 0; (j < lb); ){//B全部复制到A则归并结束
	if((k < lc) && (C[k] < B[j])) A[i++] = C[k++];
	if((lc <= k) || (B[j] <= C[k])) A[i++] = B[j++];//即使C已经全部复制到A,B仍然要复制到A
}//交换循环体内两句的次序,删除冗余逻辑
  • 复杂度分析
    • merge()总体迭代不超过O(n)次,累计只需线性时间
    • 注意:待归并序列不必等长,即允许 lb ≠ lc, mi ≠ (lo + hi) / 2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
起泡排序归并排序是两种常见的排序算法,它们在实现方式和性能上有一些区别。 起泡排序(Bubble Sort)是一种简单的排序算法,它通过多次遍历数组,比较相邻元素的大小并交换位置,将最大(或最小)的元素逐渐“冒泡”到数组的一端。具体步骤如下: 1. 从数组的第一个元素开始,依次比较相邻的两个元素,如果顺序不对则交换位置。 2. 继续遍历数组,重复上述比较和交换的过程,直到整个数组有序。 归并排序(Merge Sort)是一种分治算法,它将待排序的数组不断地分割成更小的子数组,然后将这些子数组合并成一个有序的数组。具体步骤如下: 1. 将待排序的数组递归地分成两个子数组,直到每个子数组只有一个元素。 2. 将相邻的子数组进行合并,合并时按照顺序比较两个子数组的元素,并将较小(或较大)的元素放入新的数组中。 3. 重复上述合并的过程,直到所有子数组合并成一个有序的数组。 起泡排序归并排序的主要区别在于实现方式和性能: 1. 实现方式:起泡排序是通过比较和交换相邻元素来实现排序,而归并排序是通过分割和合并子数组来实现排序。 2. 时间复杂度:起泡排序的平均和最坏时间复杂度都是O(n^2),而归并排序的时间复杂度是O(nlogn)。 3. 空间复杂度:起泡排序的空间复杂度是O(1),即原地排序;而归并排序的空间复杂度是O(n),需要额外的空间来存储临时数组。 4. 稳定性:起泡排序是稳定的排序算法,相等元素的相对顺序不会改变;而归并排序也是稳定的排序算法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值