【算法篇】八种内排序算法

常用的八种内排序算法分别是:

  1. 交换排序:冒泡排序、快速排序
  2. 选择排序:简单选择排序、堆排序
  3. 插入排序:直接插入排序、希尔排序
  4. 归并排序
  5. 基数排序

内排序巧记:选(选择)舰(简单选择)队(堆)的时候脚(交换)毛(冒泡)快(快速),需要把轨(归并)迹(基数)擦(插入)仔(直接插入)细(希尔)

一、算法的概念和编码实现(Java)

1、冒泡排序

冒泡排序的基本思想是,对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序

代码实现(升序)

	public static void selectSort(int[] array) {
		int len = array.length;
		boolean flag = true;
		for (int i = 0; i < len - 1 && flag; i++) {
			flag = false;
			for (int j = 0; j < len - i - 1; j++) {
				if (array[j] > array[j + 1]) {
					int temp = array[j];
					array[j] = array[j + 1];
					array[j + 1] = temp;
					flag = true;
				}
			}
		}
	}

最好情况的时间复杂度最坏情况的时间复杂度特点稳定性
T(n)=O(n)T(n)=O(n2)如果数据已经排序好,一次都不交换或者交换次数很少,效率很高;如果是完全逆序,则要不停的两两交换,效率很低稳定

2、简单选择排序

简单选择排序的基本思想是,每一趟从待排序的数据元素中选择一个最小(或者最大)的元素作为首元素,直到所有元素排完为止。

代码实现(升序):

	public static void selectSort(int[] array) {
		int len = array.length;
		for (int i = 0; i < len-1; i++) {
			int min = i;// 每趟排序默认第一个元素是最小的元素,记住下标
			for (int j = i + 1; j < len; j++) {
				// 从i+1的位置开始,依次同最小元素比较,若比最小元素小,则记住当前下标
				if (array[j] < array[min]) {
					min = j;
				}
			}
			// 无序区最小元素同无序区的第一个元素交换
			if (min != i) {
				int temp = array[min];
				array[min] = array[i];
				array[i] = temp;
			}
		}
	}
时间复杂度特点稳定性
T(n)=O(n2)简单,相对于冒泡排序来说交换次数少不稳定

3、直接插入排序

直接插入排序基本思想是不断将无序区的元素插入到有序区的适当位置,直到无序区没有元素排完为止

代码实现(升序):

	private static void insertSort(int[] array) {
		for (int i = 1; i < array.length; i++) {
			int j ;
			int temp = array[i];
			for (j = i; j>0 && temp < array[j-1]; j--) {
				array[j] = array[j-1];
			}
			array[j] = temp;
		}
	}

最好情况的时间复杂度最坏情况的时间复杂度特点稳定性
T(n)=O(n)T(n)=O(n2)简单稳定

4、快速排序

对冒泡排序的一种改进,它的思想是:将元素分为两组,一组的元素比另外一组的所有元素都要小,然后再按照此方法对两组元素进行排序。

快速排序我这里就不写了,快速排序相对于前面三种排序方式来说要复杂很多,看了一些博客,发现写得都不怎么样,这里推荐一个讲快速排序的视频java快速排序讲解_哔哩哔哩_bilibili

附上代码实现(参考视频中的):

public static void quickSort(int[] arr,int left,int right){
		//如果左边索引比右边索引更大或者相等,直接使用return结束这个方法
		if(left >= right){
			return;
		}
		//定义变量保存基准数
		int base = arr[left];
		//定义变量i,指向最左边
		int i = left;
		//定义变量j,指向最右边
		int j = right;
		
		//当i和j不相遇的时候,在循环中进行检索
		while(i!=j){
			//先由j从右向左检索比基准数小的就停下,否则继续检索
			while(arr[j]>=base && i<j){
				j--;
			}
			//在由i从左向右检索比基准数大的就停下,否则继续检索
			while(arr[i]<=base && i<j){
				i++;
			}
			
			//代码走到这里,i停下了,j也停下了,然后交换i和j位置的元素
			int temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
		}
		
		//交换基准数和相遇位置的元素
		arr[left] = arr[i];
		arr[i] = base;
		
		//排基准数左边的
		quickSort(arr,left,i-1);
		
		//排基准数右边的
		quickSort(arr,j+1,right);
	}
最好情况的时间复杂度最坏情况的时间复杂度平均时间复杂度特点稳定性
T(n)=O(nlogn)T(n)=O(n2)T(n)=O(nlogn)较复杂不稳定

 如何计算快排的时间复杂度以及快排的改进快速排序时间复杂度分析 - 知乎

C语言算法:程序7.3 - 非递归快速排序_哔哩哔哩_bilibili 

5、希尔排序

希尔排序是对直接插入排序的优化,原理是:将数据按照步长dk分为dk个子列,然后再对每个子列运用直接插入排序。

当步长为5的时候,分组情况如下:

每个子列排好序后,变成:

代码实现(升序):

	public static void main(String[] args) {
		
		int[] array = {49,38,65,97,76,13,27,49,55,4};
		print(array);
		ShellSort(array,5);
		print(array);
		ShellSort(array,3);
		print(array);
		ShellSort(array,1);
		print(array);
		
		
	}

	private static void print(int[] array){
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i]+"\t");
		}
		System.out.println("\n");
	}

	//希尔排序,dk表示步长
	private static void ShellSort(int[] array,int dk) {
		for(int k = 0;k<dk;k++){
			//每个子组做直接插入插入排序
			for(int i = dk+k;i<array.length;){
				int j ;
				int temp = array[i];
				for (j = i; j-dk>=0 && temp < array[j-dk]; ) {
					j -= dk;//上一个元素的索引需要减去dk,而不是减1
					array[j] = array[j-dk];
				}
				array[j] = temp;
				i += dk;//下一个元素索引加上dk,而不是加1
			}
		}
	}

说明:从代码层面来看,希尔排序相对于直接插入排序的不同点在于希尔排序外层多了一层循环,用来将原序列分层若干个子列。内部的直接插入排序做减减或者加加时要改成减去步长或者加上步长

二、时间复杂度和空间复杂度分析

排序的稳定性:根据关键字相同(如果数值比较的话是指大小)的记录排序前后相对位置的变化,可以分为稳定性排序算法和不稳定性排序算法。在排序的序列中,如果存在两个记录Ri和Rj,其关键字分别为Ki和Kj,并且i<=j,Ki=Kj,即记录Ri在Rj之前,排序完成后,如果记录Ri和Rj的相对位置不发生改变,那么该算法是稳定性排序,否则是不稳定排序。

排序方法时间复杂度(最坏情况)空间复杂度(辅助空间)稳定性复杂性
简单选择排序O(n2)O(1)不稳定简单
冒泡排序O(n2)O(1)稳定简单
快速排序O(n2)不稳定较复杂
直接插入排序O(n2)O(1)稳定简单
希尔排序O(n2)O(1)不稳定较复杂
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值