各种排序法大全

排序的概念

排序:所谓排序,就是使⼀串记录,按照其中的某个或某些关键字的⼤⼩,递增或递减的排列起来的

插入排序

基本思想
直接插⼊排序是⼀种简单的插⼊排序法,其基本思想是:把待排序的记录按其关键码值的⼤⼩逐个插
⼊到⼀个已经排好序的有序序列中,直到所有的记录插⼊完为⽌,得到⼀个新的有序序列 。


实际中我们玩扑克牌时,就⽤了插⼊排序的思想

直接插入排序

当插⼊第 i(i>=1) 个元素时,前⾯的 array[0],array[1],...,array[i-1] 已经排好序,此时
⽤ array[i] 的排序码与 array[i-1],array[i-2],... 的排序码顺序进⾏⽐较,找到插⼊位置
即将 array[i] 插⼊,原来位置上的元素顺序后移

实现

cpp 复制代码
void Insertsort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int c = i;
		int temp = a[c+1];
		while (c >= 0)
		{
			if (a[c]>temp)
			{
				a[c+1] = a[c];
				c--;
			}
			else
				break;//从小到大排,前面已经排好了
		}
		a[c + 1] = temp;
	}
}

特点:

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:稳定

适用场景:基本有序的情况或者数据量小

希尔排序

上面的直接插入,我们会发现,如果是在一个逆序的数据中,效率并不高,那是否可以对其先进行预排序,,再进行插入呢?

此时就是希尔排序

希尔排序法⼜称缩⼩增量法。希尔排序法的基本思想是:先选定⼀个整数(通常是gap = n/3+1),把 待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排 序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插⼊排序,当gap=1时,就相当于 直接插⼊排序。
它是在直接插⼊排序算法的基础上进⾏改进⽽来的,综合来说它的效率肯定是要⾼于直接插⼊排序算法的。
动图总结:

  1. 希尔排序是对直接插⼊排序的优化。
  2. 当 gap > 1 时都是预排序,⽬的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序
    的了,这样就会很快。这样整体⽽⾔,可以达到优化的效果。

实现

cpp 复制代码
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap >1)
	{
			gap = gap / 3+1;
		for (int i = 0; i < n - gap; i++)
		{
			int right = i;
			int temp = a[right + gap];
			while (right >= 0)
			{
				if (a[right] > a[right+gap])
				{
					a[right + gap] = a[right]; 
					right -= gap;
				}
				else
					break;
			}
			a[right + gap] = temp;
		}
	}	
}

特点:

时间复杂度:O(N^1.3)左右

空间复杂度:O(1)

稳定性:不稳定

选择排序

选择排序的基本思想:
每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部 排序的数据元素排完 。

直接选择排序

  1. 在元素集合 array[i]--array[n-1] 中选择关键码最⼤(⼩)的数据元素
  2. 若它不是这组元素中的最后⼀个(第⼀个)元素,则将它与这组元素中的最后⼀个(第⼀个)元素
    交换
  1. 在剩余的 array[i]--array[n-2](array[i+1]--array[n-1]) 集合中,重复上述步 骤,直到集合剩余 1 个元素

代码实现:

我们会发现要遍历n*n次,效率似乎太慢

如果我们一次选出未被选择区域的最大与最小数,效率可提升部分

下面实现

cpp 复制代码
void SelectSort(int* a, int n)
{
	int left = 0;
	int right = n - 1;
	while (left<right)
	{
	int mini = left;
	int maxi = right;
		for (int i = left+1; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			 if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}    
        //必须判断是否,因为有可能出现left与最大值的下标相等的情况,在第一次交换后,maxi对应的max值就不是最大值了
		if (maxi == left)
			maxi = mini;
		swap(&a[left], &a[mini]);
		swap(&a[right], &a[maxi]);
		left++;
		right--;
	}
}

特点:

时间复杂度 :O(N^2)

空间复杂度:O(1)

稳定性:不稳定

堆排序

在前面已讲

交换排序

冒泡排序

快速排序
快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素
序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩
于基准值,右⼦序列中所有元素均⼤于基准值,然后最左右⼦序列重复该过程,直到所有元素都排列
在相应位置上为⽌

快速排序

快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素
序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩
于基准值,右⼦序列中所有元素均⼤于基准值,然后最左右⼦序列重复该过程,直到所有元素都排列 在相应位置上为⽌

hoare版本

1.创建左右指针,确定基准值
2.从右向左找出⽐基准值⼩的数据,从左向右找出⽐基准值⼤的数据,左右指针数据交换,进⼊下次 循环

cpp 复制代码
void Quicksort(int* a, int left, int right)
{
	if (left >= right)
		return;
		int keyi = left;
		int begin = left;
		int end = right;
		while (begin < end)
		{
			while (a[end] >= a[keyi] && end > begin)//加个等于号,那么符合,再减就算end<begin了
			{
				end--;
			}
			while (a[begin] <= a[keyi] && begin < end)
			{
				begin++;
			}
			swap(&a[end], &a[begin]);
		}
		swap(&a[keyi], &a[begin]);
		keyi = begin;
		Quicksort(a, left, keyi - 1);
		Quicksort(a, keyi + 1, right);
	
}

挖空法

创建左右指针。⾸先从右向左找出⽐基准⼩的数据,找到后⽴即放⼊左边坑中,当前位置变为新 的"坑",然后从左向右找出⽐基准⼤的数据,找到后⽴即放⼊右边坑中,当前位置变为新的"坑",结 束循环后将最开始存储的分界值放⼊当前的"坑"中,返回当前"坑"下标(即分界值下标)

cpp 复制代码
void QuickSort1(int* a, int left,int right)
{
	if (left >= right)
		return;
	if (right - left <= 10)
	{
		InsertSort(a+left, right - left + 1);//起始位置不定
		return;
	}
	else
	{
		int key = a[keyi];
		while (begin < end)
		{
			while (a[end] >= key && begin < end)
				end--;
			a[keyi] = a[end];
			keyi = end;
			while (a[begin] <= key && begin < end)
				begin++;
			a[end] = a[begin];
			keyi = begin;
		}
		//begin==end
		a[keyi] = key;
		QuickSort1(a, left, keyi - 1);
		QuickSort1(a, keyi + 1, right);
	}
}

lomuto前后指针

创建前后指针,从左往右找⽐基准值⼩的进⾏交换,使得⼩的都排在基准值的左边。

操作

如果cur指针找到了比key值小的数据,那么prev指针就与cur指针的数据交换,然cur++,prev++

cpp 复制代码
void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 <= 10)
	{
		InsertSort(a + left, right - left + 1);//起始位置不定
		return;
	}
	else
	{
		int keyi = left;
		int prev = left;
		int cur = left + 1;
		while (cur < right)
		{
			if (a[right] < a[keyi] && ++prev != cur)
				swap(&a[prev], &a[cur]);
			cur++;
		}
		keyi = prev;
		QuickSort2(a, left, keyi - 1);
		QuickSort2(a, keyi + 1, right);
}
相关推荐
树在风中摇曳1 小时前
带哨兵位的双向循环链表详解(含 C 代码)+ LeetCode138 深度解析 + 顺序表 vs 链表缓存机制对比(图解 CPU 层级)
c语言·链表·缓存
少许极端1 小时前
算法奇妙屋(十四)-简单多状态dp问题
算法·动态规划·图解算法·简单多状态dp·打家劫舍问题·买卖股票问题全解
hd51cc2 小时前
MFC多线程学习笔记三:线程间的通信
笔记·学习
hd51cc2 小时前
MFC多线程学习笔记四:线程间的同步
笔记·学习·mfc
雨落在了我的手上2 小时前
C语言入门(二十一):字符函数和字符串函数(1)
c语言
星空的资源小屋2 小时前
VNote:程序员必备Markdown笔记神器
javascript·人工智能·笔记·django
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [block]bfq-iosched
linux·笔记·学习
2301_823438022 小时前
解析论文《复杂海上救援环境中无人机群的双阶段协作路径规划与任务分配》
人工智能·算法·无人机
摇滚侠2 小时前
Vue 项目实战《尚医通》,实名认证模块静态的搭建,笔记53
vue.js·笔记