常见内部排序方法的总结

常见内部排序方法的总结

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
插入排序O(n2)O(n2)O(n)O(1)稳定
希尔排序O(nlog2n)O(n2)O(n)O(1)不稳定
选择排序O(n2)O(n2)O(n2)O(1)不稳定
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定
冒泡排序O(n2)O(n2)O(n)O(1)稳定
快速排序O(nlog2n)O(n2)O(nlog2n)O(nlog2n)不稳定
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定

1. 插入排序

基本思想

在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

代码实现

直接插入排序

寻找插入位置时使用从前往后直接查找

void InsertSort(vector<int>& v) {
	int len = v.size(),cur_num = 0;
	for (int i = 1; i < len; i++)
	{
		if (v[i] > v[i - 1])
		{
			continue;
		}
		cur_num = v[i];
		// 找到插入位置
		int j = 0;
		while (j < i)
		{
			if (cur_num <= v[j])
			{
				break;
			}
			j++;
		}
		// 挪动插入位置的元素
		for (int k = i; k > j; k--)
		{
			v[k] = v[k - 1];
		}
		// 插入元素
		v[j] = cur_num;
	}
	return;
}

折半插入排序

寻找插入位置时使用二分查找

void BinaryInsertSort(vector<int>& v)
{
	int len = v.size(), cur_num = 0;
	for (int i = 1; i < len; i++)
	{
		if (v[i] > v[i - 1])
		{
			continue;
		}
		cur_num = v[i];
		// 找到插入位置(由于前面元素已排好序,二分查找)
		int l = 0, r = i - 1, mid;
		while (l < r)
		{
			mid = (r - l) / 2 + l;
			if (cur_num < v[mid])
			{
				r = mid;
			}
			else if (cur_num > v[mid])
			{
				l = mid + 1;
			}
			else
			{
				r = mid;
				break;
			}
		}
		// 挪动插入位置的元素
		for (int k = i; k > r; k--)
		{
			v[k] = v[k - 1];
		}
		// 插入元素
		v[r] = cur_num;
	}
	return;
}

2. 希尔排序

基本思想

在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。

希尔排序

代码实现

void ShellSort(vector<int>& v) {
	int len = v.size();
	for (int dk = len / 2; dk >= 1; dk /= 2) {
		// dk为步长(增量)
		for (int i = dk; i < len; i++)
		{
			// 对以dk为步长的组进行插入排序
			if (v[i] < v[i - dk])
			{
				int temp = v[i];
				// 寻找插入位置,并后移元素
				int j;
				for (j = i - dk; j >= 0 && v[j] > temp; j -= dk) {
					v[j + dk] = v[j];
				}
				// 插入元素
				v[j + dk] = temp;
			}
		}
	}
	return;
}

3. 选择排序

基本思想

在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换
第二次遍历n-2个数,找到最小的数值与第二个元素交换;

第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成

代码实现

void SelectSort(vector<int>& v) {
	int len = v.size();
	for (int i = 0; i < len; i++)
	{
		// min记录目前最小元素的位置
		int min = i;
		for (int j = i + 1; j < len; j++)
		{
			if (v[j] < v[min])
			{
				min = j;
			}
		}
		// 将目前最小元素放在最终的位置
		if(i != min)
		{
			swap(v[i], v[min]);
		}		
	}
	return;
}

4. 堆排序

基本思想

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

堆排序创建堆

堆节点调整:将堆的末端子节点作调整,使得子节点永远小于父节点

创建按最大堆:从最后一个非叶子结点开始(叶结点自然不用调整),从后往前进行调整。

堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算

堆排序

代码实现

void HeapSort(vector<int>& v) {
	int len = v.size();

	/*
	* 使用STL库的大根堆实现
	priority_queue<int> q(v.begin(),v.end());
	for (int i = len - 1; i >= 0; i--)
	{
		v[i] = q.top();
		q.pop();
	}
	
	*/

	// 利用自建的堆进行排序
	BuildMaxHeap(v);
	for (int i = len - 1; i > 0; i--)
	{
		swap(v[i], v[0]);
		AdjustHeap(v, i, 0);
	}	
	return;
}

void BuildMaxHeap(vector<int>& v) {
	int len = v.size();
	for (int i = len / 2; i >= 0; i--)
	{
		// 从堆节点最后一个非叶子节点开始调整
		AdjustHeap(v, len, i);
	}
}

void AdjustHeap(vector<int>& v, int end, int pos) {
	// 将元素pos为根的子树进行调整
	int temp = v[pos];
	for (int i = 2 * pos; i < end; i *= 2)  // 沿着key较大的子节点向下筛选
	{
		// 让i为key较大的子节点下标
		if (i < end - 1 && v[i] < v[i + 1])
		{
			i++;
		}
		if (temp >= v[i])
		{
			// 筛选结束
			break;
		}
		else
		{
			// 将v[i]调整到双亲节点上
			v[pos] = v[i];
			// 修改pos值继续向下筛选
			pos = i;
		}
	}
	// 将元素pos放在最终位置
	v[pos] = temp;
	return;
}

5. 冒泡排序

基本思想

从后向前比较相邻的两个数据,如果第二个数小,就交换位置,最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。

重复上述过程即可。

代码实现

void BubbleSort(vector<int>& v) {
	int len = v.size();
	for (int i = 0; i < len; i++)
	{
		bool flag = true;  // 标志一趟冒泡是否发生交换
		for (int j = len - 1; j > i; j--)
		{
			// 一趟冒泡过程
			if (v[j] < v[j-1])
			{
				swap(v[j], v[j-1]);
				flag = false;
			}
		}
		// 未发生交换则表示已经有序
		if (flag)
		{
			return;
		}
	}
	return;
}

6. 快速排序

基本思想

分治:先从数列中取出一个数作为枢轴值,将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边,最终这个数被放在最终位置。对左右两个小数列重复第二步,直至各区间只有1个数。

代码实现

void QuickSort(vector<int>& v, int low, int high) {
	if (low < high)
	{
		// 划分子数组
		int pivotpos = Partition(v, low, high);	
		// 依次递归对子数组进行快排
		QuickSort(v, low, pivotpos - 1);
		QuickSort(v, pivotpos + 1, high);
	}
	return;
}

int Partition(vector<int>& v, int low, int high) {
	// 以当前第一个元素作为枢轴元素pivot,对数组进行划分
	int pivot = v[low];

	while (low < high)
	{
		// 从右往左找到比枢轴元素小的元素
		while (low < high && v[high] >= pivot)
		{
			high--;
		}
		v[low] = v[high];
		
		// 从左往右找到比枢轴元素大的元素
		while (low < high && v[low] <= pivot)
		{
			low++;
		}		
		v[high] = v[low];
	}
	// 将枢轴元素pivot放在最终位置
	v[low] = pivot;

	return low;
}

7. 归并排序

基本思想

分治:将数组分成2组A,B,如果这2组组内的数据都是有序的,那么就可以很方便的将这2组数据进行排序。如何让这2组组内数据有序了?可以将A,B组各自再分成2组。依次类推,当分出来的小组只有1个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的2个小组就可以了。这样通过先递归的分解数列再合并数列就完成了归并排序。

代码实现

void MergeSort(vector<int>& v, int start, int end) {
    // 先分解数组进行归并排序,再合并数组
	if (end - start > 1)
	{
		int mid = (end - start) / 2 + start;
		MergeSort(v, start, mid);
		MergeSort(v, mid, end);
		Merge(v, start, mid, end);
	}
	return;
}

void Merge(vector<int>& v, int start, int mid, int end) {
	// 先创建辅助数组,将待排序的数组全放进辅助数组中
	vector<int> temp;
	for (int i = start; i < end; i++)
	{
		temp.push_back(v[i]);
	}
	// 合并有序数组
	int cur = start, i = 0, j = mid - start;
	mid -= start;
	end -= start;
	
	while ( i < mid && j < end)
	{
		if (temp[i] < temp[j]) {
			v[cur++] = temp[i++];
		}
		else {
			v[cur++] = temp[j++];
		}
	}

	// 其中一个数组比较完了,将剩下的数组元素全放进v中
	while (i < mid)
	{
		v[cur++] = temp[i++];
	}
	while (j < end)
	{
		v[cur++] = temp[j++];
	}
	return;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值