算法——常用排序算法(下)(C++实现)

博客介绍了归并排序、快排和堆排的思路与代码。归并排序基于分治思想,有稳定性;快排常数项少,常用随机快排;堆排基于完全二叉树。三种排序时间复杂度均为O(NlogN)。还总结了工程中综合排序算法的考虑项,以及非比较排序的特点。

归并排序

思路

归并排序是分治思想,涉及到。其时间复杂度为O(NlogN)O(Nlog^{N})O(NlogN),具有稳定性。

代码

#include <iostream>
using namespace std;

//对已排好的两个数组进行合并
void merge(int arr[], int low, int mid, int high)
{
	int i = low, j = mid + 1;
	int count = 0;
	int *temp = new int[high - low + 1];//辅助数组,长度与原数组相同
	//谁小填谁
	for (i = low, j = mid + 1; i <= mid && j <= high;)
	{
		if (arr[i] <= arr[j])
		{
			temp[count++] = arr[i++];
		}
		else
		{
			temp[count++] = arr[j++];
		}
	}
	//判断i,j越界情况,将不越界的数组里的数直接补到temp中
	if (i <= mid)
	{
		for (; i <= mid; i++)
		{
			temp[count++] = arr[i];
		}
	}
	else
	{
		for (; j <= high; j++)
		{
			temp[count++] = arr[j];
		}
	}
	//将temp拷贝会原数组
	i = low;
	for (int k = 0; k < count&&i <= high; k++, i++)
	{
		arr[i] = temp[k];
	}
	delete[] temp;
}
//分而治之,将长数组一直进行二等分,当分到只剩一个时返回
void Msort(int* arr, int low, int high)
{
	if (low >= high) return;
	int mid = (low + high) / 2;
	Msort(arr, low, mid);
	Msort(arr, mid + 1, high);
	merge(arr, low, mid, high);
}

int main()
{
	int len;
	cout << "Please enter the length of array you want to sort : ";
	cin >> len;
	int *arr = new int[len];
	cout << "The data is : ";
	for (int i = 0; i < len; i++)
	{
		cin >> arr[i];
	}
	Msort(arr, 0, len - 1);
	cout << "After sort : ";
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

快排

思路

其时间复杂度为O(NlogN)O(Nlog^{N})O(NlogN),不具有稳定性,其常数项较少,在样本数量较少时使用。最常用的是随机快排,即分大小区间时的基准数值随机选择。

代码

#include<iostream>
using namespace std;
void quickSort(int array[], int left, int right)
{
	//划分大小两个区间,设置一个temp装需要判断的值
	int less = left;
	int more = right;
	int temp = array[less];//将最左值设定为基数
	//在非越界条件下开始快排
	if (less < more)
	{
		while (less < more)
		{
			while (less < more &&  array[more] >= temp)
				more--;
			if (less < more)
			{
				array[less] = array[more];
				less++;
			}
			while (less < more && temp > array[less])
				less++;
			if (less < more)
			{
				array[more] = array[less];
				more--;
			}
		}
		//把基准数放到less位置
		array[less] = temp;
		//递归方法
		quickSort(array, left, less - 1);
		quickSort(array, less + 1, right);
	}
}
int main()
{
	int len;
	cout << "键入快排数组长度 : ";
	cin >> len;
	int *arr = new int[len];
	cout << "键入快排数组 : ";
	for (int i = 0; i < len; i++)
	{
		cin >> arr[i];
	}
	quickSort(arr, 0, len - 1);
	cout << "快排结果 : ";
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

堆排

思路

概念:完全二叉树(可以不满,但必须是从左到右)可用数组表示。节点i(i为节点值而不是该位置的数)的左孩子2i+1,右孩子2i+2.其父节点为(i-1)/2。只要满足上面的公式,就可以将数脑补为一颗完全二叉树。堆就是一颗完全二叉树。大跟堆,任何一颗子树的最大值就是这个子树的头部,小跟堆同理。
堆排序的时间复杂度为O(NlogN)O(Nlog^{N})O(NlogN),不具有稳定性。
大根堆建立过程:大根堆未必有序!
1)数组变大根堆
2)堆顶弹出(将堆顶与最后一个数字交换,然后堆的长度–即可减去堆顶)
3)剩下的堆继续heapify,形成新的大根堆
循环如此,最后的数组就形成了有序数组(从小到大)

代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//实现大跟堆,较大数值上升的过程
void heapify(vector<int> &arr, int len, int index)
{
	//将输入数组定义为一个完全二叉树,设定左右孩子节点与父节点
	int left = 2 * index + 1;
	int right = 2 * index + 2;
	int maxindex = index;
	//左孩子大于父节点的值时,将左孩子赋值给maxindex
	if (left<len&&arr[left]>arr[maxindex])
		maxindex = left;
	//右孩子大于父节点的值时,将右孩子赋值给maxindex
	if (right<len&&arr[right]>arr[maxindex]) 
		maxindex = right;
	//将maxindex上的值即最大值与父节点的值交换
	if (maxindex != index)
	{
		swap(arr[maxindex], arr[index]);
		heapify(arr, len, maxindex);
	}
}
//堆排序
void heapsort(vector<int> &arr, int size)//size 数组长度,i
{
	//先构建大跟堆,从最后一个非叶子节点向上
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		heapify(arr, size, i);
	}
	//调整大根堆
	for (int i = size  - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);
		heapify(arr, i, 0);
	}
}
int main()
{
	vector<int> arr = { 2,6,3,5,1,7 };
	heapsort(arr, arr.size());
	for (int i = 0; i < arr.size(); i++)
	{
		cout << arr[i] <<"";
	}
	cout << endl;
	return 0;
}

总结

工程中的综合排序算法考虑项:数据量、常数项、时间复杂度、稳定性
先判断数据状况,数据量小时(<=60)直接使用插排(因为其常数项低,即使时间复杂度高,但依然不影响其速度快);,数据量大时,基础类型时用快排(因为基础类型的相同值无差别不用考虑稳定性),若是自己定义的则用归并排序(此时稳定性有意义)。
不是基于比较的排序,与实际数据状况有关,所以实际不常用;时间复杂度和空间复杂度均为O(N);具有稳定性。例如桶排序:是一种排序概念(一个萝卜一个坑),其中包括计数排序和基数排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值