常见的排序算法

排序算法比较的几个点:

  • 时间复杂度
  • 空间复杂度
  • 排序算法稳定性

排序算法稳定性的定义:

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

以下按照自己的想法总结的内容:

想看详细可以观看下面的博客,有详细的流程图:

十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数-1_天津 唐秙的博客-CSDN博客

算法的复杂度和稳定性

排序算法平均时间复杂度最坏的时间复杂度最好的时间复杂度空间复杂度稳定性
冒泡排序O(n^2)O(n^2)O(n)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)数组不稳定、链表稳定
插入排序O(n^2)O(n^2)O(n)O(1)稳定
快速排序O(n*log2(n))O(n^2)O(n^2)O(log2n)不稳定
计数排序O(n+m)O(n+m)O(n+m)O(n+m)稳定
堆排序O(n*log2(n))O(n*log2(n))O(nlog2(n))O(1)不稳定
希尔排序O(n*log2(n))O(n^2)O(nlog2(n))O(1)不稳定
归并排序O(n*log2(n))O(n*log2(n))O(nlog2(n))O(n)稳定
桶排序O(n+k)O(n^2)O(n^2)O(n+k)稳定
基数排序O(k*n)O(k*n)O(k*n)O(k+n)稳定

冒泡排序:

实现思路:从第一个数据开始,拿该数据和其他数据轮番比较,(从小到大排序)如果该数据大于其他数据的话,则交换数据

//升序
//从前往后排
void Bubbling_sort(vector<int>& p)//冒泡排序
{
	for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
	{
		for (int j = i+1; j < p.size(); j++)//遍历的次数为数组长度减去已找到的数据
		{
			if (p[i] > p[j ])
			{
				swap(p[i], p[j]);
			}
		}
	}
}
//从后往前排
void Bubbling_sort(vector<int>& p)//冒泡排序
{
	for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
	{
		for (int j = 0; j < p.size()- 1-i; j++)//遍历的次数为数组长度减去已找到的数据
		{
			if (p[j] > p[j + 1])
			{
				swap(p[j], p[j + 1]);
			}
		}
	}
}

时间复杂度:O(n^2)      空间复杂度:O(1)       排序算法稳定

选择排序: 

实现思路:从第一个数据开始,用一个数据来存储下标,找到最小数的下标,然后交换这两个数。

//升序
void Select_Sort(vector<int>& p)//选择排序
{
	int min;//存储下标
	for (int i = 0; i < p.size()-1; i++)//只需要遍历i-1次
	{
		min = i;//假设第一个数为最小
		for (int j = i + 1; j < p.size(); j++)
		{
			if (p[j] < p[min])//找到小值
			{
				min = j;//更换下标
			}
		}
		if (min != i)//如果下标不匹配
		{
			swap(p[i],p[min]);//交换数值
		}
	}
}

时间复杂度:O(n^2)      空间复杂度:O(1)        排序算法不稳定(链表的话稳定)

不稳定的体现:

插入排序: 

 实现思路:假设第一个已排序,从第二个数据开始,与前面的数据进行比较,如果比前面的数据小,则交换两个数据,如果比前面的数据大,则结束。

//升序
void Insert_Sort(vector<int>& p)//插入排序
{
	for (int i = 1; i < p.size(); i++)//从第二个数据开始
	{
		
		if (p[i] < p[i - 1])//当前面的数大于该数时
		{
			int data = p[i];//获取插入的数据
			int n = i-1;//获取i-1的位置
			p[n+1] = p[n];
			while (n > 0 && data < p[n-1])
			{
				p[n] = p[n - 1];//把数据前移
				n--;//位置往后移
			}
			p[n] = data;//找到位置插入
		}
	}
}

 时间复杂度:O(n^2)      空间复杂度:O(1)        排序算法稳定

快速排序:

 实现思路:(分治算法基础上设计出来的一种排序算法

  1. 先取一个值P作为中轴,
  2. 把比它小的数放到左边
  3. 把比它大的数放到右边
  • 重复对左右两个区域进行1-3步。
void Quick_Sort(vector<int>& p, int begin, int end)//快速排序
{
	if (begin >= end)//结束条件
		return;
	int head = begin;//头指针
	int tail = end;//尾指针
	int data = p[begin];//以头节点为中轴
	while (head < tail)//当头小于尾时执行
	{
		//从右边开始找小的数
		while (head<tail && p[tail]>=data) { tail--; }//如果尾部的数大于中枢的值,跳过,指向上一个
		p[head] = p[tail];//把值给赋给头指针
		//从左边开始找大的数
		while (head < tail && p[head]<= data) { head++; }//头部的值小于中枢的值,跳过,指向下一个
		p[tail] = p[head];//把值给赋给头指针
	}
	//当两个指针相等时就是中枢节点的位置
	p[head] = data;
	Quick_Sort(p, begin, head - 1);//递归左半部分
	Quick_Sort(p, head + 1,end );//递归右半部分
}

 时间复杂度:O(nlog2n)      空间复杂度:O(log2n))        排序算法不稳定 

计数排序:

实现思路为:

  • 获取数组中的最大最小值,获取两个数的差值
  • 开辟一个数组,大小为 :差值+1   初始化为0
  • 利用新数组来存储,元素的个数
  • 得到个数后,从头开始输出数组元素并赋值给原数组
void Count_Sort(vector<int>& p)//计数排序
{
	int min = 0;//存储最小值
	int max = 0;//存储最大值
	for (int i = 0; i < p.size(); i++)
	{
		if (p[i] < min)min = p[i];//找最小值
		if (p[i] > min)max = p[i];//找最大值
	}
	int num = max - min;//存储差值,用来创建数组
	vector<int>count(num + 1, 0);//用来存储元素个数
	for (int i = 0; i < p.size(); i++)
	{
		count[p[i - min]]++;//统计元素个数
	}
	int j = 0;
	for (int i = 0; i < p.size();)
	{
		while (j <= num && count[j])
		{
			count[j--];//个数减一
			p[i] = j + min;//把数值放到数值中
			i++;
		}
		j++;
	}
}

 时间复杂度:O(n+m)      空间复杂度:O(n+m)        排序算法稳定 

堆排序: 

推荐一个视频方便理解:

排序算法:堆排序【图解+代码】_哔哩哔哩_bilibili

实现思路:

  1. 建堆 小到大排序(建大堆),大到小排序(建小堆)
  2. 替换第一个和最后一个数据,把树的元素个数减1
  3. 重复1-2,直到只剩一个元素

建堆的一些概念:

void heapify(vector<int>& p, int n, int i)//建堆,n为数组长度,i为待维护节点下标
{
	int mid = i;//
	int lchild = i * 2 + 1;//i的左节点
	int rchild = i * 2 + 2;//i的右节点
	//获取三点的最大值
	if (lchild < n && p[mid] < p[lchild])
	{
		mid = lchild;
	}
	if (lchild < n && p[mid] < p[rchild])
	{
		mid = rchild;
	}
	//如果最大值不为父节点
	if (mid != i)
	{
		swap(p[mid], p[i]);//交换数值,使父节点成为最大值
        heapify(p, n, mid);//继续比较完成建堆
	}
}
void Heap_Sort(vector<int>& p, int n)
{
	//建堆
	int i;
	//由于n的父节点为(n-1)/2 相当于(n-1-1)/2=n/2-1
	for (i = n / 2 - 1; i >= 0; i--)//从后往前建堆
	{
		heapify(p, n, i);
	}
	//排序
	for (i = n - 1; i >= 0; i++)
	{
		swap(p[0], p[i]);//交换第一和最后一个值
		heapify(p, i, 0);//调整位置
	}
}

 时间复杂度:O(n*log2n)      空间复杂度:O(1)        排序算法不稳定 

希尔排序: 

实现思路:

是插入排序的增强版,希尔排序将数组分隔成n组来进行插入排序,每一次,将数据排的相对有序。

void shell_Sort(vector<int>& p)//希尔排序
{
	int n = p.size();//获取数组长度
    //设置增量,每次循环/2
	for (int i = n / 2; i > 0; i /= 2)
	{
        //起始值从i开始,进行插入排序
		for (int j=i; j < n; j++)
		{
			int k = i;
			int temp = p[k];
			while (k - i >= 0 && p[k - i] > temp)
			{
				p[k] = p[k - i];
				k = k - i;
			}
			p[k] = temp;
		}
	}
}

时间复杂度:O(n*log2n)      空间复杂度:O(1)        排序算法不稳定 

归并算法:

方法为:

  • 先把长度为n的数据分为两块
  • 通过递归对左右半区进行拆分
  • 拆分完后,分别对左右两边的数据,进行对比(排序),放入临时数组中
  • 然后拷贝到数组中

排序算法:归并排序【图解+代码】_哔哩哔哩_bilibili

void merge(vector<int>& n, vector<int>& m, int left , int mid, int right)合并
{
	//左边第一个元素
	int x = left;
	//右边第一个元素
	int y = mid + 1;
	//记录临时数组下标
	int z = left;
	while (x <= mid && y <= right)
	{
		if (n[x] < n[y])
		{
			m[z++] = n[x++];
		}
		else 
		{
			m[z++] = n[y++];
		}
	}
	//左边还有剩余元素
	while (x <= mid)
	{
		m[z++] = n[x++];
	}
	//右边还有剩余元素
	while (y <= right)
	{
		m[z++] = n[y++];
	}
	//把数据复制给数组
	while (left <= right)
	{
		n[left] = m[left];
		left++;
	}
}
void msort(vector<int>& n, vector<int>& m, int left, int right)
{
	if (left < right)
	{
		int mid = left + (right - left) / 2;//取中间点
		//递归拆分左边
		msort(n, m, left, mid);
		//递归拆分右边
		msort(n, m, mid + 1, right);
		//合并
		merge(n,m,left,mid,right);
	}
}
void merge_Sort(vector<int>&p,int n)//归并排序
{
	vector<int>G(n, 0);//辅助数组
	msort(p, G, 0, n - 1);//归并入口
}

 桶排序:

类似于哈希表,解决冲突的方法为地址法,把每个头节点当作一个桶。

把数据分别放入对应的桶中,然后从第一个桶开始,取数据放到数组中

基数排序:

  1. 取得数组中的最大数,并取得位数。
  2. arr为原始数组,从最低位开始取每个位组成radix数组。
  3. 对radix进行计数排序(利用计数排序适用于小范围数的特点)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值