Java八大排序方法详解

排序 是对数据元素序列建立某种有序排列的过程。我们通常从三个方面来衡量排序算法的优劣,分别是 时间复杂度空间复杂度稳定性 1

1. 直接插入排序

  • 基本思想: 遍历数组中元素,插入到已排序数组的正确位置。
    在这里插入图片描述

  • 算法设计: 设置两个循环,一个循环遍历整个数组,另一个循环将 a[i] 与已排序部分比较,找到插入位置。

public void insertSort(int[] a){
	for(int i=1; i<a.length; i++){		//循环遍历获取元素a[1],a[2],a[3]...
		for(int j=i; j>0; j--){
			//j=i,每次都先与已排序部分的最后一位比较大小。如果小往前继续比较。
			if(a[j] < a[j-1]){
			//swap(a[j],a[j-1])
				int temp = a[j-1];
				a[j-1] = a[j];
				a[j] = temp;
			}
		}
	}
}
  • 算法性能:
    时间复杂度:O(n2)
    空间复杂度:O(1)
    稳定

2. 希尔排序

  • 基本思想: 希尔排序是在分组概念上的直接插入排序。小组内用直接插入排序,再把若干小组合并为一个小组。
    希尔排序需有一个增量序列,所谓增量就是每次分组的个数,我们选择{n、n/2, (n/2)/2, … , 1} 作为我们的增量序列,这个是比较常用的。

在这里插入图片描述

  • 算法设计: 最外层循环为增量序列。内层的两个循环与直接插入排序算法一致,只是在最内层循环比较时,每次不是 -1,而是 -step
public static void shellSort(int[] a) {
	//增量序列,每次 step/2
	for(int step=a.length/2; step>0; step/=2) {
		
		for(int i=step; i<a.length; i++) {
			for(int j = i; j>=step; j-=step) {
				if(a[j] < a[j-step]) {
				//swap(a[j],a[j-step])
					int temp = a[j];
					a[j] = a[j-step];
					a[j-step] = temp;
				}
			}
		}
		
	}
}
  • 算法性能:
    时间复杂度:O(n(lbn)2)
    空间复杂度:O(1)
    不稳定

3. 直接选择排序

  • 基本思想: 先从待排数组中选择最小与数组中的第一个元素交换。然后将除第一个外剩余元素中最小的与数组中的第二个元素交换,以此类推。
    在这里插入图片描述
  • 算法设计: 设置两个循环。第一个循环遍历整个数组,第二个循环是从 i+1 开始,即总是在寻找未排序部分的最小值,然后与 i 交换。
public void selectSort(int[] a){
	int n = a.length;
	for(int i=0; i<n; i++){
		int min = i;
		//找到剩余元素的最小值的 下标 赋给 min
		for(int j=i+1; j<n; j++){
			if(a[j] < a[min]) min = j;
		}
		//下标为 min 的与未排序部分的第一位进行交换
		if(min != i){
		//swap(a[i],a[min])
			int temp = a[i];
			a[i] = a[min];
			a[min] = temp;
		}
	}
}
  • 算法性能:
    时间复杂度:O(n2)
    空间复杂度:O(1)
    不稳定

4. 堆排序

  • 基本思想: 将待排序序列构造成一个大顶堆 2,此时整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列。

一、 将无序序列构造成一个大顶堆
在这里插入图片描述
从最后一个非叶子结点开始(叶结点自然不用调整),从左至右,从下至上进行调整,构造大顶堆。
在这里插入图片描述
成功构造大顶堆后,整个序列的最大值就是堆顶的根节点。

二、将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端

在这里插入图片描述

三、重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
在这里插入图片描述
在这里插入图片描述

  • 算法设计:
public static void sort(int []a){

	int len = a.length;
    for(int i=len/2-1;i>=0;i--){
        //从第一个非叶子结点从下至上,从右至左调整结构
        adjustHeap(a,i,len);
    }
  
    for(int j=len-1;j>0;j--){
    	//交换堆顶元素与末尾元素
        int temp = a[0]; a[0] = a[j]; a[j] =temp;
        //重新对堆进行调整,len 为 j
        adjustHeap(a,0,j);
    }
}
//调整根节点大于子节点
public static void adjustHeap(int []a,int i,int len){
    int temp = a[i];//先存下当前节点的值
    for(int k=i*2+1;k<len;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
        if(k+1<len && a[k]<a[k+1]){//如果左子结点小于右子结点,k指向右子结点
            k++;
        }
        if(a[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
            a[i] = a[k];
            i = k;
        }else{
            break;
        }
    }
    a[i] = temp;//将temp值放到最终的位置
}

学习参考: 图解排序算法之堆排序

  • 算法性能:
    时间复杂度:O(lbn)
    空间复杂度:O(1)
    不稳定

5. 冒泡排序

  • 基本思想: 依次比较两个相邻的元素,将较大的放在后面。最大元素会被交换到数组最右边。
    在这里插入图片描述

  • 算法设计: 两个循环
    外循环控制循环次数,每一次循环都会使剩余元素中的最大元素排到最后。
    内循环依次比较各元素,执行交换,使最大元素沉至底部。

public static void bubbleSort(int[] a) {
	int n = a.length;
	for(int i=0; i<n-1; i++) {
		for(int j=0; j<n-1-i; j++) {
			//比较相邻的两个元素,比较 n-1-i 次
			if(a[j]>a[j+1]) {
				int temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}
}
  • 算法性能:
    时间复杂度:O(n2)
    空间复杂度:O(1)
    稳定

6. 快速排序

  • 基本思想: 对 nums[left … right] 进行排序
    寻找一个点 (nums[p]) 作为基准元素,循环整个数组,比较交换元素使得 nums[left…p-1] 都小于等于 nums[p],nums[p+1…right]都大于nums[p]。
    然后对nums[left…p-1]nums[p+1…right] 递归的用同样方法排序。

在这里插入图片描述

  • 算法设计:
    此算法将a[0] 作为基准元素
    将数组 a[1… a.length-1] 中的元素循环与a[0]比较,使小于a[0]的在前,大于a[0]的在后。
    经过这次循环后,p 所在的位置就是最后一个小于a[0]元素所在的位置,将基准元素与此元素交换(swap(a[p], a[0])),就可使nums[left…p-1] 都小于等于 nums[p],nums[p+1…right]都大于nums[p]
	public static void quickSort(int[] a,int left, int right) {
		int sign,p,temp;
		if(left < right) {
			p = left;
			sign = a[p];
			for(int i=left+1; i<=right; i++) {
				if(a[i] < sign) {
					p++;
					temp = a[p];
					a[p] = a[i];
					a[i] = temp;
				}
			}
			temp = a[left];
			a[left] = a[p];
			a[p] = temp;			
			
			quickSort(a,left,p-1);
			quickSort(a,p+1,right);
		}
		
	}
  • 算法性能:
    时间复杂度:O(n2)
    空间复杂度:O(n)
    不稳定

7. 归并排序

  • 基本思想: 对 nums[left … right] 进行排序
    先对 nums[left … mid] 排序,再对 nums[mid+1 … right] 排序,最后把这两个有序的子数组合并,整个数组就排好序了。
    在这里插入图片描述

  • 算法设计:
    将数组分开分别排序,再合并。
    注意合并算法。

	public static int[] sort(int[] a, int left, int right) {
		int mid = (left+right)/2;
		if(left<right) {
			//对 nums[left ... mid] 排序
			sort(a,left,mid);
			//对 nums[mid+1 ... right] 排序
			sort(a,mid+1,right);
			//合并
			merge(a,left,mid,right);
		}
		return a;
	}
	
	//合并函数
	public static void merge(int[] a, int left, int mid, int right) {
		int[] temp = new int[right-left+1];
		int i = left;
		int j = mid + 1;
		int k = 0;
		
		while(i<=mid && j<= right)
			if(a[i]<a[j]) {
				temp[k++]=a[i++];
			}else {
				temp[k++]=a[j++];
			}
		while(i<=mid) temp[k++]=a[i++];
		while(j<=right) temp[k++]=a[j++];
		
		for(int x=0; x<temp.length; x++) 
			a[x+left] = temp[x];
		
	}
  • 算法性能:
    时间复杂度:O(nlbn)
    空间复杂度:O(n)
    稳定

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

  2. 是一个完全二叉树。
    每个结点的值都大于或等于其左右孩子结点的值,称为 大顶堆
    每个结点的值都小于或等于其左右孩子结点的值,称为 小顶堆
    (一般升序采用大顶堆,降序采用小顶堆) ↩︎

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值