排序算法模板-C++实现

前言

本文对常见排序算法做了总结,简单分析了排序算法的复杂度,适合复习回顾,不适合第一次学习。

准备工作

逆序数

// 如果你学习过线性代数,请跳过这个部分。
逆序数的角度定性分析排序算法的时间复杂度,有时比直接分析代码容易,所以有必要了解逆序数的定义:

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

百度百科:逆序数
一个由N个元素组成的排列,最大逆序数是N(N-1)/2,这可以解释为什么简单排序的最坏情况时间复杂度是O(N^2)。

命名规范

本文所有排序算法模板使用统一的命名风格:

//注释
template<typename _Ty>
void x_sort(_Ty*element, int Size) {
	//insert code
}

_Ty是排序数据的类型。x是排序算法的名称,函数名统一为x_sort,如:冒泡排序的函数名为bullle_sort。element和Size是统一的参数名,其中element是指向排序数据的指针,Size为排序数据量。

调试宏

为了随时检查排序结果,定义宏OUTPUT:

#ifndef OUTPUT
#define OUTPUT {\
for(int count=0;count!=Size;count++)\
cout<<element[count]<< ' '; \
cout.put(10);\
}
#endif

使用OUTPUT宏,输出element数组内的数据。

内联交换函数

排序离不开数据的交换,为增加代码可读性,定义SWAP函数:

template<typename _Ty>
inline void SWAP(_Ty&x, _Ty&y) {
	auto swap = x;
	x = y;
	y = swap;
}

检查函数

为检测庞大的测试数据是否完成排序,定义isOrdered函数:

template<typename _Ty>
bool isOrdered(_Ty*element, int Size) {
	_Ty*it = element;
	for (int count = 1; count != Size; count++, it++)
		if (*it > it[1])
			return false;
	return true;
}

使用isOrdered函数遍历element数组,检查数据是否有序。

简单排序

冒泡排序

遍历element数组,比较相邻两个元素大小,如果前一个元素大于后一个元素,则交换前后元素位置。
由于冒泡排序每次只能交换相邻两个元素,而交换相邻两个元素最多减少一个逆序数,当数据规模是N时,最大逆序数是N(N-1)/2,所以冒泡排序的时间复杂度是O(N^2)。
冒泡排序没有申请额外的空间,空间复杂度是O(1)。

//冒泡排序
template<typename _Ty>
void bullle_sort(_Ty*element, int Size) {
	for (int j = Size - 1; j; j--) {
		bool flag = false;
		for (int i = 0; i != j; i++)
			if (element[i] > element[i + 1]) {
				SWAP(element[i], element[i + 1]);
				flag = true;
			}
		if (!flag)return;
	}
}

插入排序

插入排序的思路类似于整理一手扑克牌。
设需要排序的数据集合为U,已经排好序的数据集合为U1,未排好序的数据集合为U2,每次从U2中抽取一个数据p,然后遍历U1,找出合适的位置插入p。
插入排序相当于从U1末尾向前做冒泡排序,所以插入排序的时间复杂度依然是O(N^2)。
插入排序没有申请额外的空间,空间复杂度是O(1)。

//插入排序
template<typename _Ty>
void insertion_sort(_Ty*element, int Size) {
	for (int i = 1; i != Size; i++) {
		_Ty insert = element[i];
		int j;
		for (j = i - 1; j >= 0; j--)
			if (element[j] > insert)
				element[j + 1] = element[j];
			else break;
		element[j + 1] = insert;
	}
}

本例中,需要排序的数据集合只有一个,当数据量庞大时,一个集合内的操作次数是N^2数量级的,这决定了插入排序不适合处理大规模数据。
如果把数据集合分成几个,情况就会有所改善,这就是希尔排序,这会在后文提到。

选择排序

选择排序也是把需要排序的数据分为两个集合,已经排好序的数据集合为U1,未排好序的数据集合为U2,每次从U2中选择一个最大的元素p,与U1末尾元素交换。
设U2的数据规模为N2,最坏情况下,U2中的最大元素在末尾,选择操作需要扫描N2次。
如果每次选择都是最坏情况,则U2的逆序数为N2(N2-1)/2,一次交换消除的逆序数为N2-1。
N2(N2-1)/2:N2-1∝N2,U2交换的时间复杂度为O(N),扫描的时间复杂度也为O(N),所以选择排序的时间复杂度为O(N^2)。
选择排序没有申请额外的空间,空间复杂度是O(1)。

//选择排序
template<typename _Ty>
void selection_sort(_Ty*element, int Size) {
	for (int i = 0; i != Size; i++) {
		int min = i;
		for (int j = i + 1; j != Size; j++)
			if (element[min] > element[j])
				min = j;
		if (min != i)
			SWAP(element[i], element[min]);
	}
}

简单排序的改进

希尔排序:改进插入排序

希尔排序的实现过程
希尔排序把数据集合分成几个子集,子集的数据量由增量序列决定,先对子集插入排序,再对全集插入排序。
增量序列
为了逐步使全集数据有序,子集的数据量应该逐步递增为全集数据量N,子集的数量应该逐步递减为1,我们称这个逐步递减为1的子集数量序列的反向序列为增量序列。
下面以Hibbard增量序列为例实现希尔排序:

Hibbard增量序列:{1, 3, …, 2^k-1}

//希尔排序:Hibbard增量序列
template<typename _Ty>
void shell_sort(_Ty*element, int Size) {
	for (int step = log2(1 + Size); step; step = (step - 1) / 2)
		for (int i = step; i < Size; i += step) {
			_Ty insert = element[i];
			int j;
			for (j = i - step; j >= 0; j -= step)
				if (element[j] > insert)
					element[j + step] = element[j];
				else break;
			element[j + step] = insert;
		}
}

堆排序:改进选择排序

//堆排序
template<typename _Ty>
void heap_sort(_Ty*element, int Size) {
	//Create a MaxHeap:O(NlogN)
	for (int i = 0; i != Size; i++) {
		int j = i;
		while (j&&element[(j - 1) / 2] < element[j]) {
			auto swap = element[(j - 1) / 2];
			element[(j-1) / 2] = element[j];
			element[j] = swap;
			j = (j - 1) / 2;
		}
	}
	//O(NlogN)
	for (int j = Size - 1; j; j--) {
		//Swap first and last node:O(1)
		auto swap = element[0];
		element[0] = element[j];
		element[j] = swap;
		//Heaplify reduced array:O(lonN)
		int i = 0;
		while (2 * i + 2 <= j - 1)
			if (element[2 * i + 1] > element[2 * i + 2])
				if (element[i] < element[2 * i + 1]) {
					auto swap = element[i];
					element[i] = element[2 * i + 1];
					element[2 * i + 1] = swap;
					i = 2 * i + 1;
				}
				else break;
			else if (element[i] < element[2 * i + 2]) {
				auto swap = element[i];
				element[i] = element[2 * i + 2];
				element[2 * i + 2] = swap;
				i = 2 * i + 2;
			}
			else break;
		if (j - 1 == 2 * i + 1)
			if (element[i] < element[j - 1]) {
				auto swap = element[i];
				element[i] = element[j - 1];
				element[j - 1] = swap;
			}
	}
}

分而治之:归并排序

归并

//归并
template<typename _Ty>
void merge(_Ty*element1, int Size1, _Ty*element2, int Size2, _Ty*buffer) {
	_Ty*APtr = element1, *BPtr = element2, *BufPtr = buffer;
	int i, j;
	for (i = Size1, j = Size2; i&&j; BufPtr++)
		if (*APtr < *BPtr) {
			*BufPtr = *APtr++;
			i--;
		}
		else {
			*BufPtr = *BPtr++;
			j--;
		}
	while (i--)
		*BufPtr++ = *APtr++;
	while (j--)
		*BufPtr++ = *BPtr++;
}

归并排序

//归并排序
template<typename _Ty>
void merge_sort(_Ty*element, int Size) {
	_Ty*buffer = new _Ty[Size];
	//O(N)
	for (int count = 0; count != Size; count++)
		buffer[count] = element[count];
	//O(NlogN)
	for (int step = 1; step < Size; step *= 2) {
		_Ty*LPtr = buffer, *RPtr = buffer + step, *BufPtr = element;
		while (RPtr + step < buffer + Size) {
			merge(LPtr, step, RPtr, step, BufPtr);
			LPtr += 2 * step;
			RPtr += 2 * step;
			BufPtr += 2 * step;
		}
		if (RPtr < buffer + Size)
			merge(LPtr, step, RPtr, buffer + Size - RPtr, BufPtr);
		for (int count = 0; count != Size; count++)
			buffer[count] = element[count];
	}
	delete buffer;
}

随机应变:快速排序

选主元

//选主元
template<typename _Ty>
_Ty median(_Ty*element, int Size) {
	_Ty&left = element[0], &mid = element[Size / 2], &right = element[Size - 1];
	if (left > mid)
		SWAP(left, mid);
	if (mid > right)
		SWAP(mid, right);
	if (left > mid)
		SWAP(left, mid);
	SWAP(mid, right);
	return right;
}

设置阈值

#ifndef CUTOFF
#define CUTOFF 100
#endif

快速排序

//快速排序
template<typename _Ty>
void quick_sort(_Ty*element, int Size) {
	struct SubQuickSort{
		_Ty*sub_head;
		int sub_size;
	}sub_quick_sort{ element,Size };
	stack<SubQuickSort>S;
	S.push(sub_quick_sort);
	while (!S.empty()) {
		sub_quick_sort = S.top();
		S.pop();
		_Ty*head = sub_quick_sort.sub_head;
		int size = sub_quick_sort.sub_size;
		int pivot = median(head, size);
		_Ty*i = head, *j = head + size - 2;
		while (i <= j) {
			for (; *i < pivot; i++);
			for (; *j > pivot; j--);
			if (i <= j)
				SWAP(*i++, *j--);
		}
		SWAP(*i, head[size - 1]);
		if (j - head >= CUTOFF) {
			sub_quick_sort.sub_size = j - head + 1;
			S.push(sub_quick_sort);
		}
		else insertion_sort(head, j - head + 1);
		if (head + size - 1 - i >= CUTOFF) {
			sub_quick_sort.sub_head = i;
			sub_quick_sort.sub_size = head + size - i;
			S.push(sub_quick_sort);
		}
		else insertion_sort(i, head + size - i);
	}
}

复杂数据类型:表排序

做表:插入排序

//表排序
template<typename ElementType, typename KeyType>
void table_sort(ElementType*element, const KeyType*key, int Size) {
	//插入排序:O(NlogN)
	int*table = new int[Size], i, j;
	for (i = 0; i != Size; i++)
		table[i] = i;
	for (j = 1; j != Size; j++) {
		int insert = table[j];
		for (i = j - 1; i >= 0; i--)
			if (key[table[i]] > key[insert])
				table[i + 1] = table[i];
			else break;
		table[i + 1] = insert;
	}

物理排序

	//物理排序:O(mN)
	i = 0;
	while (true) {
		while (table[i] == i)
			if (Size == ++i)
				goto _LOOP_END;
		if (table[table[i]] == i) {
			SWAP(element[table[i]], element[i]);
			table[table[i]] = table[i];
			table[i] = i;
		}
		else {
			ElementType temp = element[i];
			element[i] = element[table[i]];
			SWAP(table[i], i);
			while (table[table[i]] != table[i]) {
				element[i] = element[table[i]];
				SWAP(table[i], i);
			}
			element[i] = temp;
			SWAP(table[i], i);
		}
	}
_LOOP_END:
	delete[]table;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值