8种基本排序算法详解

几种基本排序算法详解

排序算法描述

1、概念
通过一定的方法将杂乱的数据按照一定的规则(比如升序,降序等)排列的过程叫做排序。
2、分类
非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。

线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
常见的排序算法
3.比较
几种常用排序算法的复杂度

冒泡排序

冒泡排序,重复地走访过要排序的元素列,按照需要的顺序(比如升序或降序)依次比较两个相邻的元素,如果他们的顺序错误就交换位置。走访元素的工作是重复地进行直到没有相邻元素需要交换,即完成冒泡排序

<a class=冒泡排序" />
算法实现

void BubbleSort(int a[], int size)
{
	int temp = 0;
	for (int i = 0; i < size; i++)
	{
		for (int j = 1; j<size; j++)
		{
			if (a[j]<a[j - 1])//升序
			{
				temp = a[j];
				a[j] = a[j - 1];
				a[j - 1] = temp;
			}
		}
	}
}

从算法的实现思路展示的动图,我们可以对代码稍微进行减枝,去掉已经无需在进行排序的末尾端数据和数据是顺序的情况。

void BubbleSort(int a[], int size)
{
	int temp = 0;
	for (int i = 0; i < size; i++)
	{
	 	// 标志位,用于判断一轮查询里面是否全是顺序的情况
		int flag = 0;
		// size - i 剪枝,过滤已完成排序的末端
		for (int j = 1; j < size - i; j++)
		{
			if (a[j] < a[j - 1])// < 升序, > 降序
			{
				temp = a[j];
				a[j] = a[j - 1];
				a[j - 1] = temp;
				flag = 1;
			}
		}
		if (flag == 0) break;
	}
}

选择排序

选择排序的实现原理,第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

<a class=选择排序" />
算法实现

// 方式一(和冒泡类似,注意区别)
void Selectsort(int a[], int n)
{
	int temp = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (a[i] < a[j]) // < 降序,> 升序
			{
				temp = a[i];
				a[i] = a[j];
				a[j] = temp;
			}
		}
	}
}
// 方式二,减少替换次数
void Selectsort(int a[], int n)
{
	int nMinIndex = 0;
	int temp = 0;
	for (int i = 0; i < n; i++)
	{
		nMinIndex = i; //找最小元素的位置
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] < a[nMinIndex]) 
			{
				nMinIndex = j;
			}
		}
		//将这个元素放到无序区的开头
		temp = a[i];
		a[i] = a[nMinIndex];
		a[nMinIndex] = temp;
	}
}

插入排序

插入排序,在要排序的元素中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

插入排序
算法实现

void Insertionsort(int a[], int size)
{
	//循环从第2个元素开始
	int temp = 0;
	for (int i = 1; i < size; i++)
	{
		if (a[i] < a[i - 1])
		{
			int j = i - 1;
			temp = a[i];
			for (; j >= 0 && a[j] > temp; j--)
			{
				a[j + 1] = a[j];
			}
			a[j + 1] = temp;
		}
	}
}

希尔算法

1、概念
希尔排序,又称“缩小增量排序”,也是插入排序的一种,希尔排序在时间效率上其他排序有很大的改进。

2.实现思路
在使用直接插入排序算法时,如果表中的记录只有个别的是无序的,多数保持有序,这种情况下算法的效率也会比较高;除此之外,如果需要排序的记录总量很少,该算法的效率同样会很高。希尔排序就是从这两点出发对算法进行改进得到的排序算法。
希尔排序的具体实现:先将整个记录表分割成若干部分,分别进行直接插入排序,然后再对整个记录表进行一次直接插入排序。
在这里插入图片描述
算法实现

void ShellSort(int data[], int len) 
{
	//这里的初始增量是len/2,以后增量每次缩小一倍
	int gap = len / 2;
	while (gap > 0) 
	{
		//对每个子序列进行直接插入排序
		for (int i = gap; i < len; i++) 
		{
			int temp = data[i];
			int k = i - gap;
			while (k >= 0 && data[k] > temp)
			{
				data[k + gap] = data[k];
				k -= gap;
			}
			data[k + gap] = temp;
		}
		gap /= 2;
	}
}

归并排序

1、概念
归并排序(英语:Merge sort,或mergesort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
2.实现思想
归并排序的核心思想是将两个有序的数列合并成一个大的有序的序列。通过递归,层层合并,即为归并。
③动图演示:在这里插入图片描述
算法实现

void merge(int data[], int lt, int mid, int rt)
{
	int len = rt - lt + 1;
	//开辟一个新的数组,将原数组映射进去 
	int *temp = new int[len];
	for (int m = lt; m <= rt; m++)
	{
		temp[m - lt] = data[m];
	}
	int i = lt, j = mid + 1;//i和j分别指向两个子数组开头部分
	for (int k = lt; k <= rt; k++)
	{
		if (i > mid)
		{
			data[k] = temp[j - lt];
			j++;
		}
		else if (j > rt)
		{
			data[k] = temp[i - lt];
			i++;
		}
		else if (temp[i - lt] < temp[j - lt])
		{
			data[k] = temp[i - lt];
			i++;
		}
		else
		{
			data[k] = temp[j - lt];
			j++;
		}
	}
	delete[] temp;
}
//递归的使用归并排序,对data[l....r]排序 
void merge_sort(int data[], int l, int r)
{
	if (l >= r)
		return;
	int mid = (l + r) / 2;
	merge_sort(data, l, mid);
	merge_sort(data, mid + 1, r);
	merge(data, l, mid, r);
}

快速排序

1、概念
快速排序(Quicksort)是对冒泡排序的一种改进。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
2、时间复杂度
最好情况(待排序列接近无序)时间复杂度为O(nlog2n),最坏情况(待排序列接近有序)时间复杂度为O(n2),平均时间复杂度为O(nlog2n)。
3、实现思路
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

算法实现

void quickSort(int data[], int left, int right) 
{
	int i, j, temp;
	if (left > right)
		return;
	temp = data[left]; //temp中存的就是基准数
	i = left;
	j = right;
	while (i != j) 
	{ 
		while (data[j] >= temp && i < j)//先从右边开始找
			j--;
		while (data[i] <= temp && i < j)//再找右边的
			i++;
		if (i < j)//交换两个数在数组中的位置
		{
			swap(data[i], data[j]);
		}
	}
	//最终将基准数归位
	data[left] = data[i];
	data[i] = temp;
	quickSort(data, left, i - 1);//继续处理左边的(递归)
	quickSort(data, i + 1, right);//继续处理右边的(递归)
}

堆排序

1、概念
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

2、实现思路
堆排序是将待排序的数组在排序开始时建成大顶堆或者小顶堆,而后将堆顶元素与数组最后一个元素交换,将剩下的元素作为新的堆进行调整,反复这一过程直到所有堆中只剩一个元素。通过大顶堆的方式得到的数据序列是升序序列,而小顶堆则相反。

在这里插入图片描述
算法实现
大顶堆算法实现:

//将子树调整为大根堆,升序序列
void adjustMaxHeap(int *arr, int adjustPoint, int len)
{
	int son = adjustPoint * 2 + 1;//传入的是数组下标,需要+1修正,son是左子树的下标
	int dad = adjustPoint;//父节点下标
	while (son < len)
	{
		if (son + 1 < len && arr[son] < arr[son + 1])//找到子节点中最大的节点
		{
			son++;
		}
		if (arr[son] > arr[dad])//如果子节点大于父节点,交换父子节点
		{
			swap(arr[son], arr[dad]);
			dad = son;//以当前节点为父节点向下调整
			son = 2 * dad + 1;//获得下次检查的子节点下标
		}
		else{
			break;
		}
	}
}

void myMaxHeapSort(int *arr, int len)
{
	//step1 建立初始大根堆
	//从最后一个叶子节点的父亲节点开始调整
	for (int i = len / 2 - 1; i >= 0; i--)
	{
		adjustMaxHeap(arr, i, len);
	}
	//step2 将堆顶元素逐一调整到列表最后(输出)
	swap(arr[0], arr[len - 1]);
	for (int i = len - 1; i > 0; i--)
	{
	    //将堆顶元素输出后对剩下的元素做大顶堆调整
		adjustMaxHeap(arr, 0, i);
		swap(arr[0], arr[i - 1]);
	}
}

小顶堆算法实现:

//将数据调整为小顶堆
void adjustMinHeap(int *arr, int adjpoint, int len)
{
	int dad = adjpoint;
	int son = dad * 2 + 1;
	while (son < len)
	{
		if (son + 1 < len && arr[son] > arr[son + 1])//获得较小的子节点
		{
			son++;
		}
		if (arr[son] < arr[dad])
		{
			swap(arr[son], arr[dad]);
			dad = son;
			son = dad * 2 + 1;
		}
		else{
			break;
		}		
	}
}

//小顶堆排序,降序
void myMinHeapSort(int *arr, int len)
{
	int i;
	//step 1 初始建立小顶堆
	for (i = len / 2 - 1; i >= 0; i--)
	{
		adjustMinHeap(arr, i, len);
	}
	//step2 将小顶堆输出
	swap(arr[len - 1], arr[0]);
	for (i = len - 1; i > 0; i--)
	{
		adjustMinHeap(arr, 0, i);
		swap(arr[i - 1], arr[0]);
	}
}

基数排序

1、概念
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort。,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
基数排序有两种,一种是高位优先,一种是低位优先。

2.时间复杂度
基数排序的时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数
3、实现思路
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

在这里插入图片描述
算法实现
大顶堆算法实现:

int getDes(int data[], int n){
	int max = data[0];
	for (int i = 1; i < n; i++){
		if (max < data[i]){
			max = data[i];
		}
	}
	int k = 0;
	while (max)	{
		max /= 10;
		k++;
	}
	return k;
}
void basesort(int date[], int n)
{
	int maxx = getDes(date, n);//取得最大位数
	int num = 1;
	//位数决定排序循环次数
	for (int i = 0; i < maxx; i++){
		//for (int ct = 0; ct < n; ct++)
		//	printf("%d  ", date[ct]);
		//	printf("\n");
		int count[10] = { 0x0 };//声明count为了统计每个桶放了几个数
		//int temp[10][len] = { 0x0 };//temp相当于桶,前一个数标记第几个篮子,后一个为了标记放的个数
		//这里由于vs不支持动态数组,方便调试直接使用1024
		int temp[10][1024] = { 0x0 };
		for (int j = 0; j < n; j++){
			//num是变量,因为每次循环比较的位是不同的
			int mod = (date[j] / num) % 10;
			count[mod]++;
			int te = count[mod] - 1;
			temp[mod][te] = date[j];//把数据放mod桶的te位上存储
		}
		int b = 0;
		for (int h = 0; h < 10; h++){
			if (count[h] > 0)
			{	
				//count[h]是h桶的存储个数			
				for (int v = 0; v < count[h]; v++){
					date[b] = temp[h][v];//把桶内排好的数全都倒给要排序的数组,进行下轮排序
					b++;
				}
			}
		}
		num = num * 10;
	}
}

参考资料
https://www.toutiao.com/a6593273307280179715/?iid=6593273307280179715
https://www.jianshu.com/p/cccf5b6e0e86


http://www.niftyadmin.cn/n/757615.html

相关文章

Windows系统服务编程

Windows系统服务编程 1.简介 系统服务是后台进程运行的程序&#xff0c;没有界面。它是通过服务管理控制器(Service Control Manager, SCM)管理的&#xff0c;SCM可以操作系统服务启动、停止、自动运行等。 SCM提供的功能&#xff1a; (1)服务程序: 一种为一个或多个服务提供…

目前最流行的网页自动运行EXE文件

大家对木马都不陌生了&#xff0c;它可能要算是计算机病毒史上最厉害的了&#xff0c;相信会使木马的人千千万万&#xff0c;但是有很多人苦于怎么把木马发给对方&#xff0c;现在随着计算机的普及&#xff0c;在网络上我相信很少有人会再轻易的接收对方的文件了&#xff0c;所…

汇编学习笔记2 标志位和指令跳转表

1.标志位 标志位缩写说明溢出标志&#xff08;overflow flag&#xff09;OF操作数超出机器能表示的范围表示溢出,溢出时为1&#xff0c;否则为0符号标志&#xff08;sign flag&#xff09;SF记录运算结果的符号,结果负时为1&#xff0c;否则为0零标志&#xff08;zero flag&…

Android笔记(三十七) 如何停止AsyncTask?

当我们加载一张图片的时候&#xff0c;加载的过程中我们想要取消操作&#xff0c;该怎么办呢&#xff1f;调用Asynctask的 cancel() 方法就可以了&#xff0c;我们看代码&#xff1a; 先看一个例子&#xff1a; MainAciticty.java package cn.lixyz.asynctest;import android.a…

Ollydbg 常用快捷方式整理

(F1) 选择要附加的线程 (F2) 在当前光标处设置/取消断点 (F3) 打开文件对话框&#xff0c;可以选择加载一个可执行程序&#xff0c;进行调试分析 (F4) 程序执行到光标处(所选行) (F5) 缩小、还原当前窗口 (F6) 切换到下一个窗口 (F7) 单步步入&#xff0c;进入被调试程序…

boost 编译 windows7+vs2013

1.下载boost源码 boost官网下载boost&#xff08;我这边下载的是1.72.0&#xff0c; 编译环境 win7_x64, vs2013_sp5&#xff09; 2.使用vs2013命令工具,编译b2.exe 用管理员权限打开vs2013命令工具&#xff0c;进入boost所在路径&#xff0c;然后运行命令 bootstrap.bat m…

新手疑惑的import导包 [golang 学习笔记1]

一 包的导入语法 在写Go代码的时候经常用到import这个命令用来导入包文件&#xff0c;看到的方式参考如下&#xff1a; import "fmt" //如果只有一个包可以直接在import后面加上要导入的包 //如果包含一个或以上包可以使用 import()&#xff0c;在挂号里依次加入…

在.NET MVC下不用iframe实现局部加载html

最近在做个后台系统&#xff0c;之前都是用iframe来实现加载内容&#xff0c;左侧菜单不刷新。但一直不喜欢这种方法&#xff0c;有许多弊端。今天自己在网上查找了一番后找到了比较好的替代方案&#xff1a; 一、利用html的锚点标记来实现无刷新页面加载&#xff1a; Index.cs…