简单易懂排序算法【总结】

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/86221562

相关概念

稳定:如果 a a 原本在 b b 前面,而 a = b a=b ,排序之后 a a 仍然在 b b 的前面。
不稳定:如果 a b a原本在b 的前面,而 a = b a=b ,排序之后 a 可能会出现在 b b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当 n n 变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n n 的函数。

排序分类

以下对之前介绍的七种排序进行一个简单的分类
在这里插入图片描述

各种性能指标对比

排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性
冒泡排序 O ( n 2 ) O(n^2) O ( n ) O(n) O ( n 2 ) O(n^2) O ( 1 ) O(1) 稳定
简单选择排序 O ( n 2 ) O(n^2) O ( n 2 ) O(n^2) O ( n 2 ) O(n^2) O ( 1 ) O(1) 稳定
直接插入排序 O ( n 2 ) O(n^2) O ( n ) O(n) O ( n 2 ) O(n^2) O ( 1 ) O(1) 稳定
希尔排序 O ( n l o g n ) O(nlogn) ~ O ( n 2 ) O(n^2) O ( n 1.3 ) O(n^{1.3}) O ( n 2 ) O(n^2) O ( 1 ) O(1) 不稳定
堆排序 O ( n l o g n ) O(nlogn) O ( n l o g n ) O(nlo g n) O ( n l o g n ) O(nlogn) O ( 1 ) O(1) 不稳定
归并排序 O ( n l o g n ) O(nlogn) O ( n l o g n ) O(nlogn) O ( n l o g n ) O(nlogn) O ( n ) O(n) 稳定
快速排序 O ( n l o g n ) O(nlogn) O ( n l o g n ) O(nlogn) O ( n 2 ) O(n^2) O ( l o g n ) O(logn) ~ O ( n ) O(n) 不稳定

动图详解

1.冒泡排序

代码

void BubbleSort(vector<int>& vec)
{
	int n = vec.size();
	for (int i = 0; i < n - 1; ++i)
	{
		for (int j = i + 1; j < n; ++j)
		{
			if (vec[i] > vec[j]) 
				swap(vec[i], vec[j]);
		}
	}
}

在这里插入图片描述

2.简单选择排序:

代码

void SelectSort(vector<int>& vec)
{
	int n = vec.size();
	int minIndex;
	for (int i = 0; i < n - 1; ++i)
	{
		minIndex = i;
		for (int j = i + 1; j < vec.size(); ++j)
		{
			if (vec[j] < vec[minIndex])
				minIndex = j;
		}
		if (i != minIndex)
			swap(vec[i], vec[minIndex]);
	}
}

在这里插入图片描述

3.直接插入排序

代码

void InsertSort(vector<int>& vec)
{
	int n = vec.size();
	int i, j, tmp;
	for (i = 1; i < vec.size(); ++i)
	{
		tmp = vec[i];  //将当前待插入的元素提出
		for (j = i - 1; j >= 0 && vec[j] > tmp; --j)
			vec[j + 1] = vec[j];  //将已经排好序的序列往后移一位,找到插入位置则停止
		vec[j + 1] = tmp;  //将元素插入其中
	}
}

在这里插入图片描述

4.希尔排序:

代码

void ShellSort(vector<int>& vec)
{
	int i,j;
	int tmp;
	int increment = vec.size(); 
	do 
	{
		increment = increment/3 + 1;  //初始跳跃间隔
		for (i = increment + 1; i <= vec.size(); ++i)
		{
			if (vec[i - 1] < vec[i - increment - 1])
			{
				//对每一个小序列执行直接插入排序
				tmp = vec[i - 1];
				for (j = i - increment - 1; j >= 0 && vec[j] > tmp; j -= increment)
					vec[j + increment] = vec[j];
				vec[j + increment] = tmp;
			}
		} 
	}while (increment > 1);  //当跳跃间隔大于1继续
}

在这里插入图片描述

5.堆排序:

代码

void HeapSort(vector<int>& vec)
{
	int i;
	//这里vec.size() - 1是在数组前面加了一个0,方便下标进行操作
	//除以2是因为i 小于 n/2的节点**才有子节点**,这里用到了上述提到过的节点关系的思想
	for (i = (vec.size() - 1)/2;i > 0; --i)
	{
		HeapAdjust(vec, i, vec.size() - 1);  //将元素构造成大顶堆
	}
	for (i = vec.size() - 1;i > 0;--i)
	{
		swap(vec[i], vec[1]);  //交换堆顶元素与当前未经排序的子序列的最后一个记录
		HeapAdjust(vec, 1, i - 1); //将vec[1..i-1]重新调整为大顶堆
	}
}


void HeapAdjust(vector<int>& vec, int max_index, int length)
{
	int tmp, j;
	tmp = vec[max_index];  //记录下堆顶元素
	//以下沿元素值较大的孩子节点往下找
	for (j = 2 * max_index; j <= length; j *= 2) //这里j *= 2,之所以递增2倍也是节点关系讨论的结果
	{
		//找到当前节点的较大孩子节点的下标,即为j
		if (vec[j] < vec[j+1] && j < length)
			++j;
		//若当前父节点比其两个孩子节点都大,则不用调整
		if (tmp >= vec[j])
			break;
		//否则将将较大值与当前父节点交换,并记录下重新出发的点
		vec[max_index] = vec[j];
		max_index = j;  
	}
	vec[max_index] = tmp;
}

在这里插入图片描述

6.归并排序:

代码

void MergeSort(vector<int>& vec)
{
	MSort(vec, vec, 0, vec.size() - 1);
}
//TR2定义为全局变量
#define MAXSIZE 9
vector<int> TR2(MAXSIZE, 0);

void MSort(vector<int> SR, vector<int>& TR1, int start, int end)
{
	int m;
	if (start == end)   //单个序列直接赋值到目标序列中
		TR1[start] = SR[start];
	else
	{
		m = (start + end)/2;  //将SR[s..t]平分为SR[sstart..m]、SR[m+1..end]
		MSort(SR, TR2, start, m); //递归将SR[start..m]归并为有序的TR2[start..m]
		MSort(SR, TR2, m + 1, end);//递归将SR[m+1..end]归并为有序的TR2[m+1..end]
		Merge(TR2, TR1,start, m, end);//将TR2[start..m]、TR2[m+1..end]归并到TR1中
	}	
}
void Merge(vector<int> SR, vector<int>& TR, int i, int m, int n)
{
	int j,k,l;
	for (j = m + 1,k = i;i <= m && j <= n; ++k)
	{
		if (SR[i] < SR[j])  //判断两个子序列中对应位置元素哪个更大,将大的加入到目标序列TR中
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	//若前序列有余,则加入到目标序列尾后
	if (i <= m)
	{
		for (l = 0; l <= m - i; ++l)
			TR[k] = SR[i]; //将剩余的SR[i..m]复制到TR
	}
	//若后序列有余,则加入到目标序列尾后
	if (j <= n)
	{
		for (l = 0;l <= n - j; ++l)
			TR[k] = SR[j];   //将剩余的SR[j..n]复制到TR
	}
}

在这里插入图片描述

7.快速排序

代码

void QuickSort(vector<int>& vec)
{
	QSort(vec, 0, vec.size() - 1);
}

void QSort(vector<int>& vec, int low, int high)
{
	int pivot; //记录枢轴值pivot
	if (low < high)
	{
		pivot = Partition(vec, low, high);  //将序列分割为符合要求的两部分
											//算出枢轴值pivot
		QSort(vec, low, pivot - 1);       //对低子表递归排序
		QSort(vec, pivot + 1, high);       //对高子表递归排序
	} 
}

int Partition(vector<int>& vec, int low, int high)
{
	int pivotkey;         
	pivotkey = vec[low];   //对子表的第一个作枢轴记录
	while (low < high)     //从表的两端进行扫描
	{
		while (low < high && vec[high] >= pivotkey)  //从后找到第一个比枢轴小的值
			high--;
		swap(vec[low], vec[high]);  //交换该值与枢轴值
		while (low < high && vec[low] <= pivotkey) //从前找到第一个比枢轴大的值
			low++;
		swap(vec[low], vec[high]);  //交换该值与枢轴值
	}
	return low;    //返回枢轴下标
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/86221562