常用排序算法汇总(二)

想要了解冒泡排序、选择排序、希尔排序等算法的,请移步常用排序算法汇总(一)

归并排序

  • 平均时间复杂度:O(nlog2n),分治策略,有一个二分的过程;
  • 最优时间复杂度:O(nlog2n);
  • 稳定性:稳定,相邻元素比较,相对位置不变;
  • 应用场景:大规模排序;
// 合并2个子序列
void MergeBranch(int A[], int left, int mid, int right)
{
	int len = right - left + 1;
	int *tempArr = new int[len];
	int i = left;
	int j = mid + 1;
	int k = 0;	// 临时元素下标

	while (i <= mid || j <= right)
	{
		if (i <= mid && j <= right)
		{
			// 添加较小的元素
			tempArr[k++] = A[i] <= A[j] ? A[i++] : A[j++];
		}
		else if (i <= mid)
		{
			// 左边元素未添加完
			tempArr[k++] = A[i++];
		}
		else
		{
			// 右边元素未添加完
			tempArr[k++] = A[j++];
		}
	}

	// 拷贝到原数组
	memcpy(A + left, tempArr, len*sizeof(int));
	delete[] tempArr;
}

void MergeSort(int A[], int left, int right)
{
	if (left >= right)
	{
		// 不需要继续划分
		return;
	}

	int mid = (left + right) / 2;
	MergeSort(A, left, mid);
	MergeSort(A, mid + 1, right);

	// 合并子序列
	MergeBranch(A, left, mid, right);
}

堆排序

  • 平均时间复杂度:O(nlog2n),构造二叉树逻辑结构;
  • 最优时间复杂度:O(nlog2n);
  • 稳定性:不稳定,非相邻元素比较,相对位置可能改变;
  • 应用场景:大规模排序;
// 调整堆:沿某一条调整线路,自顶向下调整
void AdjustHeap(int A[], int topIndex, int size)
{
	int left = topIndex * 2 + 1;	// 左子树
	int right = topIndex * 2 + 2;	// 右子树
	int maxIndex = topIndex;

	if (left < size && A[left] > A[maxIndex])
	{
		maxIndex = left;
	}

	if (right < size && A[right] > A[maxIndex])
	{
		maxIndex = right;
	}

	if (maxIndex != topIndex)
	{
		// 做一次有效交换
		A[maxIndex] ^= A[topIndex] ^= A[maxIndex] ^= A[topIndex];

		AdjustHeap(A, maxIndex, size);
	}
}

void HeapSort(int A[], int size)
{
	// 建堆时,从非叶节点开始,自底向上整理,使局部堆有序
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		AdjustHeap(A, i, size);
	}

	while (size > 1)
	{
		// 交换顶部元素,置于最底部
		A[0] ^= A[size - 1] ^= A[0] ^= A[size - 1];
		AdjustHeap(A, 0, --size);
	}
}

堆排序主要分2步:(1)创建堆,使每个子堆局部有序;(2)调整堆,沿某一条线路调整,找出一个最大元素;

快速排序

  • 平均时间复杂度:O(nlog2n),分治策略,有一个二分的过程;
  • 最优时间复杂度:O(nlog2n);
  • 稳定性:不稳定,非相邻元素比较,相对位置可能改变;
  • 应用场景:大规模排序,通常比其他O(nlogn)算法更快;
int BinaryPart(int a[], int left, int right)
{
	int baseElem = a[right]; // 将最后一个元素,作为基准对象

	for (int i = left; i < right; i++)
	{
		if (a[i] < baseElem)
		{
			if (i != left)
			{
				// 小于基准值的,统一挪至左边
				a[left] ^= a[i] ^= a[left] ^= a[i];
			}

			left++;
		}
	}

	// 交换元素,将基准元素放到新的基准位置
	a[left] ^= a[right] ^= a[left] ^= a[right];

	return left;
}

void QuickSort(int a[], int left, int right)
{
	if (left >= right)
	{
		return;
	}

	int basePos = BinaryPart(a, left, right);
	QuickSort(a, left, basePos - 1);
	QuickSort(a, basePos + 1, right);
}

BinaryPart函数实现相对有点绕,目的避免开辟额外存储空间

计数排序

  • 平均时间复杂度:O(n+k),创建一个规模为k的数组计数;
  • 最优时间复杂度:O(n+k);
  • 稳定性:稳定,不存在元素交换;
  • 应用场景:大规模排序,元素数值相差不大;
void CountingSort(int A[], int size)
{
	// 计算基数
	int maxVal = -1;
	int minVal = 1 << 30;

	for (int i = 0; i < size; i++)
	{
		if (A[i] > maxVal)	maxVal = A[i];
		if (A[i] < minVal)	minVal = A[i];
	}

	int baseN = maxVal - minVal + 1;
	int *countArr = new int[baseN];
	memset(countArr, 0, baseN*sizeof(int));

	// 计数
	for (int i = 0; i < size; i++)
	{
		countArr[A[i] - minVal]++;
	}

	for (int i = 1; i < baseN; i++)
	{
		countArr[i] = countArr[i] + countArr[i - 1];
	}

	// 存储新的排序结果
	int *B = new int[size];
	for (int i = size - 1; i >= 0; i--)	// 从后往前排序,保证相对有序
	{
		B[--countArr[A[i] - minVal]] = A[i];
	}
	memcpy(A, B, size*sizeof(int));

	// 释放内存
	delete[] countArr;
	delete[] B;
}

基数排序

  • 平均时间复杂度:O(n*dn),dn为元素的位数或字符串长度;
  • 最优时间复杂度:O(n*dn);
  • 稳定性:稳定,不存在元素交换;
  • 应用场景:大规模排序,元素结构可拆分为小规模计数排序;
// 获取数字的指定位
int GetDigit(int num, int iBit)
{
	int iRet = 0;

	while (iBit--)
	{
		iRet = num % 10;
		num /= 10;
	}

	return iRet;
}

// 按“数字的位”排序
void BitSort(int A[], int size, int iBit)
{
	int countArr[10] = { 0 };

	for (int i = 0; i < size; i++)
	{
		countArr[GetDigit(A[i], iBit)]++;
	}

	for (int i = 1; i < 10; i++)
	{
		countArr[i] = countArr[i] + countArr[i - 1];
	}

	int *B = new int[size];
	for (int i = size - 1; i >= 0; i--)
	{
		B[--countArr[GetDigit(A[i], iBit)]] = A[i];
	}
	memcpy(A, B, size*sizeof(int));

	delete[] B;
}

void RadixSort(int A[], int size)
{
	// 计算一下,最大数的位数
	int maxVal = -1;
	int bitNum = 0;

	for (int i = 0; i < size; i++)
	{
		if (A[i] > maxVal)	maxVal = A[i];
	}

	do
	{
		maxVal /= 10;
		bitNum++;
	} while(maxVal);

	// 按位排序(低->高)
	for (int i = 1; i <= bitNum; i++)
	{
		BitSort(A, size, i);
	}
}

桶排序

  • 平均时间复杂度:O(n),主要取决于桶的映射函数和桶内元素均匀程度;
  • 最优时间复杂度:O(n),每个桶内只有一个元素;
  • 稳定性:稳定,不存在元素交换;
  • 应用场景:大规模排序,元素可通过某种映射关系均匀划分;
int *countArr = NULL;

// 分桶
int MapToBarrel(int elemVal)
{
	return elemVal / 10;
}

// 外部排序(桶之间)
void BarrelOutsideSort(int A[], int size, int bn)
{
	countArr = new int[bn];
	memset(countArr, 0, bn*sizeof(int));

	// 计数
	for (int i = 0; i < size; i++)
	{
		int barrelIndex = MapToBarrel(A[i]);
		countArr[barrelIndex]++;
	}

	for (int i = 1; i < bn; i++)
	{
		countArr[i] = countArr[i] + countArr[i - 1];
	}

	// 存储新的排序结果
	int *B = new int[size];
	for (int i = size - 1; i >= 0; i--)	// 从后往前排序,保证相对有序
	{
		int barrelIndex = MapToBarrel(A[i]);
		B[--countArr[barrelIndex]] = A[i];
	}
	memcpy(A, B, size*sizeof(int));

	// 释放内存
	delete[] B;
}

void BarrelSort(int A[], int size)
{
	int maxVal = -1;

	for (int i = 0; i < size; i++)
	{
		if (A[i] > maxVal)	maxVal = A[i];
	}
	
	// 外部排序
	int bn = maxVal / 10 + 1; // 桶的数量
	BarrelOutsideSort(A, size, bn);

	// 桶内排序
	for (int i = 0; i < bn; i++)
	{
		int left = countArr[i];         // countArr[i]为i号桶第一个元素的位置
		int right = (i == bn - 1 ? size - 1 : countArr[i + 1] - 1);// countArr[i+1]-1为i号桶最后一个元素的位置
		QuickSort(A, left, right);
	}

	delete[] countArr;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值