常见排序算法实现分析与优化:C++实现

在学习数据结构和算法的过程中,排序算法是最基础也是最重要的一个模块。通过对排序算法的学习和实现,不仅能帮助我们更好地理解算法的复杂度,还能提升我们解决实际问题的能力。本文将详细介绍几种常见的排序算法,包括插入排序、冒泡排序、快速排序、堆排序、归并排序以及计数排序。每种算法都将通过 C++ 实现,进行代码分析,并提供一些优化建议。

一、插入排序 (Insertion Sort)

插入排序是一种简单的排序算法,它通过将每个待排序元素插入到已经排好序的部分。尽管插入排序的时间复杂度是 O(n^2),但它在小规模数据上表现优秀,并且是稳定的排序算法。

//插入排序算法
void insertSort(int* arr, int n) {
	int i, j;
	for (i = 1; i < n; i++)//第二个元素开始 遍历数组
	{
		if (arr[i]=0;  j--)
			{
				arr[j + 1] = arr[j];
			}
			arr[j+1] = temp;//直接定义j变量的好处
		}
		
	}
	printfarr(arr, n);
}

优化建议:

  • 减少无效交换:在插入排序中,如果数组已经有序,就会进行不必要的比较和交换。我们可以在比较时加入提前结束的标志,避免无用的操作。

二、冒泡排序 (Bubble Sort)

冒泡排序通过反复交换相邻的元素来将最大(或最小)元素“冒泡”到数组的一端。它的平均和最坏时间复杂度是 O(n^2),因此对大规模数据排序时效率较低。

void BubbleSort(int* arr, int n) {//冒泡排序
	
	for (size_t i = 0; i < n-1; i++)
	{
		bool flag = false;
		for (size_t j =0 ; j < n-1-i; j++)//jarr[j+1])
			{
				swap(arr[j], arr[j + 1]);
				flag = true;
			}
		}
		if (!flag) {
			break;
		}
	}
	printfarr(arr, n);
}

优化建议:

  • 提前终止:通过 flag 判断每一轮排序是否有交换,如果没有交换,就可以提前终止排序。

三、快速排序 (QuickSort)

快速排序是一种分治法的排序算法,它通过选取一个基准值,将数组分为两个部分,再递归地排序这两个部分。快速排序的时间复杂度平均为 O(n log n),但是最坏情况下会退化为 O(n^2)

void QuickSort(int * arr,int low ,int high) {//快速排序
	if (low low) {
		while (high > low&&arr[high]>=pivot) {//不加等于可能出现循环
			high--;
		}
		arr[low] = arr[high];
		while (high>low&&arr[low]<=pivot)
		{
			low++;
		}
		arr[high] = arr[low]; 
	}
	arr[low] = pivot;
	return low;
}

优化建议:

  • 基准值的选择:选择不同的基准值可能影响排序的效率,常见的优化方法是采用“三数取中”法,避免选择极端的基准值。

四、堆排序 (HeapSort)

堆排序是一种利用堆数据结构的排序算法。堆排序的时间复杂度是 O(n log n),并且它是一个不稳定的排序算法。堆排序利用大顶堆或小顶堆来快速找到最大或最小元素。

void HeadAdjudt(int * arr,int root,int len) {
	for (size_t i = root*2; i <=len; i*=2)//
	{
		if (iarr[i])
		{
			break;//跳出循环 
		}
		else {
			swap(arr[root], arr[i]);
			root = i;//现在向下检查
		}
	}
}


void BuildMaxheap(int *arr,int len) {
	for (int i = len/2; i >0; i--)
	{
		HeadAdjudt(arr, i, len);
	}
}

void HeapSort(int* arr, int len) {
	BuildMaxheap(arr, len);
	for (size_t i = len-1; i > 1; i--)
	{
		swap(arr[i], arr[1]);
		HeadAdjudt(arr, 1, i - 1);//只需要对最上面根节点下降就行 
	}
	
}

void testHeapSort() {//因为HeapSort的特性 直接重新弄了一个从1开始的数组方便堆排序
	int arr2[8] = { -1,4,6,2,6,9,1,3 };//第一个arr2[0]的元素没有意义
	int len2 = 8;
	HeapSort(arr2, len2);
	for (size_t i = 1; i < len2; i++)
	{
		cout << arr2[i] << " ";
	}
}

注意:这里对于堆排序是使用数组下标为1的开始存储数据

优化建议:

  • 调整堆的根节点:每次交换后,堆顶元素需要重新调整,因此堆的结构需要重新保持最大(或最小)堆。

五、归并排序 (MergeSort)

归并排序是一种分治法的排序算法,它将一个大数组分为两个小数组,分别排序后再合并。归并排序的时间复杂度是 O(n log n),是稳定的排序算法。

void Merge(int *arr,int low,int mid,int high) {
	int k = low;//用于辅助数组的填入
	int p = low;
	int q = mid + 1;
	while (p<=mid&&q<=high)
	{
		if (arr[p]

优化建议:

  • 空间优化:归并排序需要额外的存储空间,如果空间较紧张,可以考虑使用其他排序算法。

六、计数排序 (CountSort)

计数排序是一种非比较排序算法,适用于范围较小的整数数据。它的时间复杂度是 O(n + k),其中 n 是数组大小,k 是数据的范围。

void CountSort(int* arr, int len) {
	int min = arr[0];
	int max = arr[0];
	for (size_t i = 0; i < len; i++)
	{
		if (arr[i]max)
		{
			max = arr[i];
		}
	}
	int num = max - min + 1;//这个是计数数组长度
	int* count = new int[num];//构造num长度的数组
	for (size_t i = 0; i < num; i++) {//初始化count数组
		count[i] = 0;
	}
	int* arr2 = new int[len];//辅助数组
	for (size_t i = 0; i < len; i++)//遍历数组 计数
	{
		count[arr[i] - min]++;
	}
	for (size_t i = 1; i < num; i++)//将count数组的值表示小于该值的个数
	{
		count[i] = count[i - 1] + count[i];
	}
	for (int  i = len-1; i >=0; i--)//从后往前遍历数组 保证稳定性
	{
		arr2[--count[arr[i]-min]]=arr[i];
	}
	for (size_t i = 0; i < len; i++)
	{
		arr[i] = arr2[i];
	}
	delete[] arr2;
	delete[] count;
	printfarr(arr, len);
}

优化建议:

  • 负数的处理:如果数组中存在负数,计数排序需要特别处理,确保索引不会越界。

七、希尔排序 (ShellSort)

希尔排序是插入排序的一种优化,它通过对数组进行分组并在各组内进行插入排序来减少元素的移动。希尔排序的时间复杂度依赖于增量序列的选择,最优情况下为 O(n log n)

void ShellSort(int* arr, int n) {//分组用插入排序
	int d, i, j,temp;
	for (d = n/2; d >=1 ; d/=2)//增量的变化
	{
		for ( i = d+1; i < n; i++)//i++交替对不同的组进行插入排序
		{
			if (arr[i]= 0&&arr[j]>temp; j-=d)
				{
					arr[j + d] = arr[j];
				}
				arr[j + d] = temp;
			}
		}
	}
	printfarr(arr, n);
}

优化建议:

  • 增量序列的选择:不同的增量序列会显著影响希尔排序的效率。常见的增量序列有 n/2, n/4, ...,或者使用更优化的序列,如 Hibbard 序列。

八、简单选择排序 (Simple Selection Sort)

简单选择排序通过每次选择未排序部分中的最小元素,并将其交换到已排序部分的末尾。它的时间复杂度为 O(n^2),适用于小规模数据的排序。

void SimpleSelect(int* arr, int n) {//简单选择排序
	for (size_t i = 0; i < n-1; i++)//循环n-1轮
	{
		for (size_t j = i+1; j < n; j++)
		{
			if (arr[j]

优化建议:

  • 减少交换次数:可以通过记录最小元素的下标并只进行一次交换,来减少交换的次数。

总结

本文介绍了几种常见的排序算法及其 C++ 实现。每种排序算法都有其优缺点,在选择排序算法时,应根据实际情况选择适合的算法。例如,快速排序和归并排序适用于大规模数据排序,而插入排序和冒泡排序适合小规模数据。

在实现这些排序算法时,我们还可以做一些优化,例如提前终止不必要的循环,选择合适的基准值等。这些优化能提高排序算法的执行效率。

通过这些排序算法的学习,我不仅掌握了常见的排序技巧,还了解了算法的时间复杂度和空间复杂度,为后续的算法学习奠定了基础。

你可能感兴趣的:(算法,排序算法,数据结构)