C++ 算法竞赛中的排序算法

本文详细介绍了C++算法竞赛中常用的排序算法,包括冒泡排序、归并排序和快速排序,以及STL的sort和unique函数。文章讨论了各个算法的流程、正确性、稳定性、C++实现、时间复杂度以及特殊情况,提供了代码示例和测试框架,旨在帮助读者深入理解排序算法及其在竞赛中的应用。
摘要由CSDN通过智能技术生成
IMG_1866

C++ 算法竞赛中的排序算法

算法背景

对于给定的由整数组成的长度为 n n n 的数组 a a a,将其重新排列顺序,变成从左到右非递减的数组。

非递减: 即对于排序好的数组中的每一个元素 a i    ( 1 < i ⩽ n ) a_i\;(1<i\leqslant n) ai(1<in),都有 a i ⩾ a i − 1 a_i \geqslant a_{i-1} aiai1

非递增: 即对于排序好的数组中的每一个元素 a i    ( 1 < i ⩽ n ) a_i\;(1<i\leqslant n) ai(1<in),都有 a i ⩽ a i − 1 a_i \leqslant a_{i-1} aiai1

除特殊说明外,如下介绍算法的算法流程默认均为排序成非递减的。

排序算法的稳定性: 若在某个排序算法完成后,数组中的值相同的元素的相对位置不变,则称使用的排序算法是稳定的;反之,则称所使用的的排序算法是不稳定的。


冒泡排序Bubble Sort

算法流程

设数组中后 i + 1 ∼ n i+1\sim n i+1n 项元素已经排序成整个数组中的第 i + 1 ∼ n i+1 \sim n i+1n 大的元素,设指针 j = 1 j=1 j=1

执行以下操作:

  1. a j > a j + 1 a_j>a_{j+1} aj>aj+1,则将这两项元素交换;
  2. j = i j=i j=i,则停止,否则令 j = j + 1 j=j+1 j=j+1

对于 i = n ∼ 1 i=n \sim 1 i=n1 都执行一次该算法流程,即可完成排序。

特殊地,对于 i = n i=n i=n,数组内并没有所谓第 n + 1 n+1 n+1 项元素,但这不影响算法的实现。

算法的正确性与稳定性

正确性:

算法流程保证了数组中的前 1 ∼ i 1 \sim i 1i 项中的最大值移动到数组的第 i i i 个位置,故该算法是正确的。

就像所有数字都沉于水中,而每次最大的数字都会『咕咕冒泡』般地浮上来,因此该算法被称为冒泡排序Bubble Sort

稳定性:

当遇到相同值的元素时,该算法并不交换两项元素的顺序,故该算法是稳定的。

C++ 代码实现

void Bubble_Sort(vector<int> &a) {
   
	int n = a.size();
	for (int i = n - 1; i >= 0; --i) {
   
		for (int j = 0; j < i; ++j) {
   
			if (a[j] > a[j + 1]) {
   
				swap(a[j], a[j + 1]);
			}
		}
	}
}

如果读者不熟悉 vector,可以把它想象成一个不定长的动态数组,而 a.size() 是返回 a 这个不定长数组长度的值的函数。

  • 代码第 1 1 1 行,vector<int> &a 有两个目的:
  1. 防止 a 的改变只在函数内,并不实际改变 a

  2. 防止程序复制一遍 a,降低代码效率。

  • 由于 vector 的下标从 0 0 0 开始,故下标可能与算法流程描述有所不同。

  • 算法流程体现在代码中的:

  • 代码第 5 ∼ 6 5 \sim 6 56 行即为算法流程的第 1 1 1 步;

  • 代码第 4 4 4 行,for 循环的终止条件为 j=i,若不终止则执行 ++j,即 j = j + 1 j=j+1 j=j+1,体现了算法流程的第 2 2 2 步。

使用方法:

  • 传入想要排序的 vector a 即可,函数会将 a 自动排序成非递减的。
  • 如果想要排序成非递增的,则将代码第 5 5 5 行的 if 判断语句内的条件改为 a[j]<a[j+1] 即可。
  • 需要引用的头文件和命名空间:
#include <vector>
#include <algorithm>
using namespace std;

算法的时间复杂度

代码由两层 for 循环组成,每层的最坏循环次数为 n n n 次,其中 n n n 为数组内的元素个数,故该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

算法拓展

冒泡排序Bubble Sort的交换次数

Q:在冒泡排序Bubble Sort中,两项元素的交换次数总共为多少次?

A:交换次数为逆序对的数量

逆序对:

  1. 在排序要求是非递减的情况下,对于原始数组中的两个不同的元素 a i , a j a_i,a_j ai,aj,若 a i > a j a_i>a_j ai>aj,则称 ( a i , a j ) (a_i,a_j) (ai,aj) 1 1 1 对逆序对;
  2. 在排序要求是非递增的情况下,对于原始数组中的两个不同的元素 a i , a j a_i,a_j ai,aj,若 a i < a j a_i<a_j ai<aj,则称 ( a i , a j ) (a_i,a_j) (ai,aj) 1 1 1 对逆序对;

证明:

  • 对于算法流程的第 1 1 1 步来说,若 a j > a j + 1 a_j>a_{j+1} aj>aj+1,则说明 ( a j , a j + 1 ) (a_j,a_{j+1}) (aj,aj+1) 1 1 1 对逆序对,经过交换后,则这 1 1 1 对逆序对就消除了;
  • 但是对于数组 1 ∼ j − 1 1\sim j-1 1j1 j + 2 ∼ n j+2 \sim n j+2n 项来说, a j ,    a j + 1 a_j,\;a_{j+1} aj,aj+1 与它们之间的相对位置没有改变,故只会减少这 1 1 1 对逆序对;
  • 所以只要存在逆序对,冒泡排序Bubble Sort就不应该停止,但每次交换只会消除 1 1 1 对逆序对,故冒泡排序Bubble Sort的交换次数为逆序对的数量。

□ \Box


归并排序Merge Sort

算法流程

设当前数组 a a a 中需要排序的范围为 [ l , r ] [l,r] [l,r],令 m i d = ⌊ l + r 2 ⌋ mid=\left\lfloor \frac{l+r}{2}\right \rfloor mid=2

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值