数据结构--堆排序、计数排序、归并排序

排序的动图演示链接

1. 堆排序

(1)算法描述
① 将初始待排序的序列构建成大堆,即为初始的无序区间;
② 将堆顶元素与最后一个元素进行交换(此时,无序区间与有序区间分别是[1,n-1]、[n]);
③ 对无序区间的序列继续建大堆,执行②,更新无序区间和有序区间;
④ 重复③,直至有序区间的元素个数为n-1个,则数据有序。

(2)代码实现

//堆排序  时间复杂度:O(n(logn))  不稳定
//建大堆,从最后一个
void shiftDown(int* arr, int n, int parent)
{
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child + 1] > arr[child])
			++child;
		if (arr[child] > arr[parent])
		{
			swap(arr, child, parent);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}
void heapSort(int* arr, int n)
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		shiftDown(arr, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		swap(arr, end, 0);
		shiftDown(arr, end, 0);
		end--;
	}
}

在这里插入图片描述

2. 归并排序

算法描述 (分治法)
① 把长度为n的序列,分成n/2的的两个子序列;
② 对两个子序列进行归并排序;
③ 将两个排好序的子序列,合并成一个序列,即排序完成。

(1)递归实现

//相邻子序列合并:
void merge(int* arr, int begin,int mid, int end, int* tmp)
{
	//递增
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	//辅助空间的起始位置
	int idx = begin;
	//合并有序序列  稳定
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])
			tmp[idx++] = arr[begin1++];
		else
			tmp[idx++] = arr[begin2++];
	}
	//判断是否有未合并的元素
	if (begin1 <= end1)
		memcpy(tmp + idx, arr + begin1, sizeof(int)*(end1 - begin1 + 1));
	if (begin2 <= end2)
		memcpy(tmp + idx, arr + begin2, sizeof(int)*(end2 - begin2 + 1));
	//合并之后的序列拷贝到原始数组的对应区间
	memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}
//归并排序  递归  n(logn)
void _mergeSort(int* arr, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = begin + (end - begin) / 2;
	//首先合并子序列
	_mergeSort(arr, begin, mid, tmp);
	_mergeSort(arr, mid + 1, end, tmp);
	//合并两个有序的子序列
	merge(arr, begin, mid, end, tmp);
}
void mergeSort(int* arr, int n)
{
	//申请辅助空间
	int* tmp = (int*)malloc(sizeof(int)*n);
	_mergeSort(arr, 0, n - 1, tmp);
	free(tmp);
}

在这里插入图片描述

(2)非递归实现

//归并排序  非递归
void mergeSortNOR(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	//子序列步长
	int step = 1;
	while (step < n)
	{
		for (int idx = 0; idx < n; idx += 2 * step)
		{
			//找到两个待合并的子序列区间
			//[begin,mid] [mid+1,end]
			int begin = idx;
			int mid = idx + step - 1;
			//判断是否存在第二个序列
			if (mid >= n - 1)
				//不存在第二个子序列,直接跳过
				continue;
			int end = idx + 2 * step - 1;
			//判断第二个子序列是否越界
			if (end >= n)
				end = n - 1;
			merge(arr, begin, mid, end, tmp);
		}
		//更新步长
		step *= 2;
	}
}

在这里插入图片描述

3. 计数排序

(1)算法描述
① 找到待排序的数组中最大值和最小值;
② 依次统计数组中每个值出现的次数,存入另一个计数数组;
③ 遍历计数数组中的元素,即排序完成。

(2)代码实现

//计数排序
void countSort(int* arr, int n)
{
	//找到最大和最小值
	int max, min;
	min = max = arr[0];
	for (int i = 1; i < n; ++i)
	{
		if (arr[i] > max)
			max = arr[i];
		if (arr[i] < min)
			min = arr[i];
	}
	//计数范围
	int range = max - min + 1;
	//创建一个计数数组  初始化为0
	int* countArr = (int*)calloc(range, sizeof(int));
	//计数
	for (int i = 0; i < n; ++i)
	{
		countArr[arr[i] - min]++;
	}
	//遍历计数数组,排序
	int idx = 0;
	for (int i = 0; i < range; ++i)
	{
		while (countArr[i]--)
			arr[idx++] = i + min;
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值