六种排序算法代码实现

排序算法时间复杂度、空间复杂度、稳定性比较_小黑皮的技术博客-CSDN博客_选择排序时间复杂度

一、各种排序算法的时间空间复杂度、稳定性⭐⭐⭐⭐⭐

1.排序算法分类:

在这里插入图片描述

2.排序算法比较:

在这里插入图片描述

3.辅助记忆

1、时间复杂度记忆

  • 冒泡、选择、直接 排序需要两个for循环,每次只关注一个元素,平均时间复杂度为O(n * n)O(n * n)(一遍找元素O(n)O(n),一遍找位置O(n)O(n))
  • 快速、归并、希尔、堆基于二分思想,log以2为底,平均时间复杂度为O(nlogn)O(nlogn)(一遍找元素O(n)O(n),一遍找位置O(logn)O(logn))

2、排序算法的稳定性

排序前后相同元素的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。

不稳定排序算法速排序、尔排序、择排序、排序(快牺牲稳定性)

稳定排序算法:冒泡排序、插入排序、归并排序和基数排序

二、各种排序算法什么时候有最好情况、最坏情况(尤其是快排) ⭐⭐⭐⭐

  • 见上表

三、排序代码实现

1.冒泡排序⭐⭐⭐⭐

冒泡排序(Bubble Sort): 一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

//冒泡排序,两两相邻元素比较是否交换
void BubbleSort(int* arr, int len)
{
	for (int i = 0; i < len; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			if (arr[j] > arr[j+1]) //相邻比较
			{
				int tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tmp;
			}
		}
	}
}

2.选择排序⭐⭐⭐⭐

简单选择排序法(Simple Selection Sort): 就是通过n-i次关键字间比较,从n-j+1个记录中选择出关键字最小的记录,并和第i(1≤i≤n)个记录交换。(比较完再交换),是一种简单直观的排序算法。它与冒泡排序很相似,都是比较 n-1 轮,每轮都是比较 n–1–i 次,每轮找出一个最大值或最小值。

//简单选择排序,for循环找出最小(最大)值的索引再交换
void SelectSort(int *arr, int len)
{
	for (int i = 0; i < len; i++)
	{
		int minIndex = i;
		for (int j = i + 1; j < len; j++)
		{
			if (arr[j] < arr[minIndex]) //寻找索引
				minIndex = j;
		}
		if (minIndex != i)
		{
			swap(arr[i], arr[minIndex]);
		}
	}
}

3.插入排序⭐⭐⭐⭐

直接插入排序(Straight Insertion Sort): 将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录增加1的序表。(插入排序)

//直接插入排序、类比希尔排序
void InsertSort(int* arr, int len)
{
	for (int i = 1; i < len; i++) //i=1开始
	{
		if (arr[i] < arr[i - 1]) //前一个值小于当前位置值,则需要移动
		{
			int tmp = arr[i];
			int j = i;
			for (; arr[j-1] > tmp && j > 0; j--)//循环判断条件 arr[j-1] > tmp && j > 0;
			{
				arr[j] = arr[j - 1]; //向后移动一位
			}
			arr[j] = tmp; //将最初最小值赋值给当前位置
		}
	}
}

4.希尔排序⭐⭐⭐⭐

希尔排序(Shell Sort): 希尔排序算法在时间复杂度上突破O(n²)的第一批算法,在基本有序的序列中(基本有序:就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间),利用“增量”分割子序列使整个序列向基本有序发展,在子序列内分别进行直接插入排序后使得序列有序。希尔排序是插入排序改良的算法,希尔排序步长从大到小调整,第一次循环后面元素逐个和前面元素按间隔步长进行比较并交换,直至步长为1,步长选择是关键

//Shell排序,同属于插入排序,相比下有更加灵活的步长
void ShellSort(int* arr, int len)
{
	int increment = len; //increment表示步长
	do
	{
		increment = increment / 3 + 1; //将len分为三部分
		for (int i = increment; i < len; i++)
		{
			if (arr[i] < arr[i - increment]) //当前值小于前一个步长值时
			{
				int tmp = arr[i];
				int j = i;
				for (; arr[j - increment] > tmp && j > 0; j -= increment)
				{
					arr[j] = arr[j - increment]; //每次跳过一个步长进行判断
				}
				arr[j] = tmp;
			}
		}
	} while (increment > 1); //do{}while循环条件:步长>1
}

5.归并排序⭐⭐⭐⭐

归并排序(Merging Sort): 利用归并的思想(合并、并入)实现排序,原理是假设初始序列中含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列;再两两归并…直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。

  • 递归实现:
//归并Merge子函数,sArr排序后的数组
void Merge(int* sArr, int* dArr, int sLen, int dLen)
{
	int i = 0, j = 0, k = 0;
	int tmp[1024]; //临时数组保存排序后的数组
	while (i < sLen && j < dLen)
	{
		if (sArr[i] < dArr[j])
		{
			tmp[k++] = sArr[i++];
		}
		else
		{
			tmp[k++] = dArr[j++];
		}
	}
	while (i < sLen) //当i值未遍历完时
	{
		tmp[k++] = sArr[i++];
	}
	while (j < dLen) //当j值未遍历完时
	{
		tmp[k++] = dArr[j++];
	}
	for (int i = 0; i < sLen + dLen; i++) //将临时数组tmp值赋值给sArr数组
	{
		sArr[i] = tmp[i];
	}
}
//2路归并排序,递归实现
void MergeSort(int* arr, int len)
{
	if (len > 1) //递归直至len=1
	{
		int* arr1 = arr;
		int len1 = len / 2;
		int* arr2 = arr + len1; //数组2的指针 = arr移动len1
		int len2 = len - len1; //arr2的长度
		MergeSort(arr1, len1); //递归
		MergeSort(arr2, len2);
		Merge(arr1, arr2, len1, len2);
	}
}
  • 非递归实现

(14条消息) 排序算法时间复杂度、空间复杂度、稳定性比较_小黑皮的技术博客-CSDN博客_排序算法 时间复杂度

6.快速排序⭐⭐⭐⭐⭐

快速排序(QuickSort): 通过一趟排序将待排的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可以分别对这两部分记录继续进行排序,已达到整个序列有序的目的。

//快排Partition子函数,将pivot放入数组正确的位置,并返回其索引
int Partition(int *arr, int left, int right)
{
	while (left < right) //循环直至pivot元素放在正确位置,每次循环只进行左右各一次交换
	{
		int pivot = arr[left]; //选择第一个元素为轴元素
		while (left < right && arr[right] > pivot) //每次循环找出一个右侧小于pivot的索引
			right--;
		swap(arr[left], arr[right]); //轴pivot = right
		while (left < right && arr[left] < pivot) //循环找到左侧一个大于pivot的索引
			left++;
		swap(arr[left], arr[right]); //pivot = left
	}
	return right;
}
//快排,递归实现
void QuickSort(int *arr, int left, int right)
{
	int pivot;
	if (left < right)
	{
		pivot = Partition(arr, left, right);//调用一次Partition就将轴元素放入正确的位置
		QuickSort(arr, left, pivot - 1);
		QuickSort(arr, pivot + 1, right);
	}
}

主函数:

int main()
{
	int arr[10] = { 5,6,4,7,3,8,2,9,1,0 };
	int len = sizeof(arr) / sizeof(int);

	BubbleSort(arr, len); //冒泡排序
	SelectSort(arr, len); //选择排序
	InsertSort(arr, len);
	ShellSort(arr, len);
	MergeSort(arr, len);
	QuickSort(arr, 0, len - 1);//传入左右索引

	for (auto a : arr) //遍历输出排序后的结果
	{
		cout << a << ' ';
	}
	cout << endl;
	system("pause");
	return 0;
}

四、快排的Partition函数与归并的Merge函数⭐⭐⭐

  • Partition函数,交换轴元素至正确位置(轴左边小于轴值,右边大于轴值),并返回轴索引;
  • Merge函数,将两个子数组归并至一个数组(创建一个temp数组,记录正确排序,再赋值给sArr数组)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值