Java 排序算法 (希尔、归并、快速排序)

排序算法总览

在这里插入图片描述

希尔排序

定义: 希尔排序是插入排序的一种,又称缩小增量排序,是插入排序算法的一种更高效的改进版

需求来源

  • 插入排序不足,已排序的分组元素为{2,5,7,9,10},未排序的分组元素为{1,8},那么下一个待插入元素为1,我们需要拿着1从后往前,依次和10,9,7,5,2进行交换位置,才能完成真正的插入,每次交换只能和相邻的元素交换位置。那如果我们要提高效率,直观的想法就是一次交换,能把1放到更前面的位置,比如一次交换就能把1插到2和5之间,这样一次交换1就向前走了5个位置,可以减少交换的次数。

增量h的定义

   int h = 1 ;
   while(h < N/2){   // N 为待排序总数
    h = 2h + 1;
} 


// 循环结束后确定 h 的最大值
// h 的 减小规则    向下取整
 h = h/2 
 

算法图解

在这里插入图片描述
参考链接

Java 代码

package Sort;


import java.util.Arrays;

public class ShellSort {

	
	public static void sort(int [] arr) {

		// 计算  h  
		int n = arr.length ;
		int h = 1 ;
		while( h < n/2) {
			h = 2*h + 1;
		}
		
		// 开始排序
		while (h >= 1) {
			for (int i = h ; i < n ; i++) {
				// 这里 arr[i] 为待插入的元素
				for( int j = i ; j >= h ; j = j - h) {
					// 这里 arr[j] 为待插入的元素
					if( greater(arr[j - h],arr[j])) {
						exch(arr, j, j-h ) ;
					} else {
						break ;
					}
				}
			}
		}
		
	}
	
	
	private static boolean greater(int v ,int w) {
		
		return v > w ;
		
	}
	
	private static void exch(int[] a, int i , int j) {
		int temp = a[i] ;
		a[i] = a[j] ;
		a[j] = temp ;
	}

}


class Test4{
	
	public static void main(String [] args) {
		  int a [] = {9,1,2,5,7,4,8,6,3,5} ;
		  
		     SelectionSort.sort(a) ;      // 注意外层有  主函数
		     System.out.println(Arrays.toString(a));
		      
	}
}

希尔与插入排序比较

在这里插入图片描述

归并排序

  • 归并排序是建立在递归操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

排序原理

  • 尽可能的将一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。
  • 将相邻的两个子组进行合并成一个有序的大组
  • 不断的重复步骤2,直到最终只有一个组为止。

算法图解

在这里插入图片描述
在这里插入图片描述

Java 代码

package Sort;


import java.util.Arrays;


/**
 * @author LZH.create
 *  Date : 2021.3.14
 *    好好学习,天天向上
 *    lo --> low   hi  --. high
 */
public class MergeSort {

	
	// 辅助数组
	private static int [] assets ; 
	
	
	
	//  对数组内的元素进行排序  
	public static void sort(int [] a) {

		// 初始化辅助数组
		assets = new int[a.length] ;
		// 获取 lo 与 hi
		int lo = 0;
		int hi = a.length - 1 ;
		
		// 进行排序
		sort(a,lo,hi) ;
		
	}
	
	
	
	// 对数组 a  从索引lo 到 索引hi  之间的元素进行排序 ( 对区间内的元素进行排序 )
	private static void sort(int [] a,int lo ,int hi) {
		
		if( hi <= lo) {
			return ;
		}
		
	   int mid = lo + (hi - lo) / 2   ;   // int mid = (hi + lo) / 2 ;  
		sort(a,lo,mid) ;
		sort(a,mid + 1 , hi) ;
		merge(a ,lo ,mid ,hi ) ;
	}
	
	
	
	// 从索引 lo 到 mid  为一个 子组 ,索引 mid + 1 到 hi  为一个子组  ,把数组a 这两个子组合并为一个有序的大组
	private static void merge(int [] a,int lo ,int mid ,int hi ) {
		
		int i = lo ;
		int p1 = lo ;
		int p2 = mid + 1 ;
		
		
		while( p1 <= mid && p2 <= hi) {
			if(less(a[p1],a[p2])) {
				assets[i++] = a[p1++] ;
			} else {
				assets[i++] = a[p2++] ;
			}
		}
		
		
		// 循环上述还有多余的处理
		
		while( p1 <= mid) {
			// 左子树没循环完
			assets[i++] = a[p1++] ;
		} 
		
		
		while(p2 <= hi) {
			// 右子树没循环完
			assets[i++] = a[p2++]  ;
		}
		
		// 将辅组数组的值复制到 原数组中
		
		for (int j = lo ; j <= hi ; j++ ) {
			a[j] = assets[j] ;
	 	}
		
	}
	
	
	
	
	// 判断 v 是否 小于  w
	private static boolean less(int v ,int w) {
		
		return v < w ;
		
	}
	
	private static void exch(int[] a, int i , int j) {
		int temp = a[i] ;
		a[i] = a[j] ;
		a[j] = temp ;
	}
	
	
	

	
	

}


class Test5{
	
	public static void main(String [] args) {
		  int a [] = {8,4,5,7,1,3,6,2} ;
		  
		    MergeSort.sort(a) ;      // 注意外层有  主函数
		     System.out.println(Arrays.toString(a));
		      
	}
}

算法特点

因为需要申请额外的数组空间,典型的以空间换取时间

快速排序

排序原理

  • 首先设定一个分界值,通过该分界值将数组分成左右两部分
  • 将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值
  • 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  • 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。

算法图解

在这里插入图片描述

详细步骤

  • 从右往左找到一个比基准值小的数据
  • 从左往右找到一个比基准值大的数据

1、 首先进行如下步骤

注意 : right 从下图 8 之后的位置开始
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

2、其次进行如下步骤

在此基础上进行分治

在这里插入图片描述在这里插入图片描述

Java 代码

package Sort;


import java.util.Arrays;


/**
 * @author LZH.create
 *  Date : 2021.3.16
 *    静心学习
 *   
 */
public class QuickSort {


	
	//  对数组内的元素进行排序  
	public static void sort(int [] a) {

         //  获取  lo 与  hi
		int lo = 0 ;
		int hi = a.length - 1 ;
		
		sort(a,lo,hi) ;
		
	}
	
	
	
	// 对数组 a  从索引lo 到 索引hi  之间的元素进行排序 ( 对区间内的元素进行排序 )
	private static void sort(int [] a,int lo ,int hi) {
	    
		
		//  递归的终止条件
		if( hi < lo) {
			return ;
		}   
		
		// 对 a 数组中 从 lo 到 hi 进行切分
		
		int p = partition(a,lo,hi) ;
		
		// 对左边分组的元素进行排序
		sort(a,lo,p-1) ;
		sort(a,p + 1,hi) ;
	}
	
	
	
	//   核心代码
	private static int partition(int [] a , int lo , int hi) {
		// 定义基准  左右 指针
		int key = a[lo] ;
		int left = lo ;
		int right = hi + 1 ;   / 一定要加 1 超过  
		
		while(true) {
			// 从右往左扫描
			while(less(key,a[--right])) {
				if(right == lo) {
					break ;
				}
			}
			
			// 从左往右扫描
			while(less(key,a[++left])) {
				if(left == hi) {
					break ;
				}
			}
			
			if(left >= right) {
				// 扫描完所有元素,结束循环
				break ;
			} else {
				exch(a,left ,right) ;
			}
		}
	
		// 最后交换 right 和 基准处
		exch(a,lo,right) ;
	  return right  ;
	}
	
	
	
	// 判断 v 是否 小于  w
	private static boolean less(int v ,int w) {
		
		return v < w ;
		
	}
	
	private static void exch(int[] a, int i , int j) {
		int temp = a[i] ;
		a[i] = a[j] ;
		a[j] = temp ;
	}
	
	
	

}


class Test6{
	
	public static void main(String [] args) {
		  int a [] = {6,1,2,7,9,3,4,5,8} ;
		  
		    MergeSort.sort(a) ;      // 注意外层有  主函数
		     System.out.println(Arrays.toString(a));
		      
	}
}

关于快排的改进

  • 代码整合
  • 效率改进(从左边找到一个大于等于基准值的数,从右边找到一个小于等于基准值的数,然后直接交换两个数的位置)
package Sort;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author LZH.create
 *     关于快排的改进
 */
public class QuickSort_plus {

	
	 static int[] arr = new int[100010];
	    
	    public static void q_sort(int l,int r){
	        if(l>=r)return;
	        int i = l - 1;
	        int j = r + 1;
	        
	        int x = arr[l + r >> 1];
	        while(i < j){
	            do i++;while(arr[i] < x);
	            do j--;while(arr[j] > x);
	            if(i<j){
	                int temp = arr[i];
	                arr[i] = arr[j];
	                arr[j] = temp;
	            }
	        }
	        q_sort(l,j);
	        q_sort(j+1,r);
	    }
	    
	    public static void main(String[] args) throws IOException{
	      
	        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	        
	        int n  = Integer.parseInt(in.readLine());
	        
	        String s[] = in.readLine().split(" ");
	        for(int i = 0; i < s.length; i++){
	            arr[i] = Integer.parseInt(s[i]);
	        }
	       
	        q_sort(0, n-1);
	       
	        for(int i = 0; i < n; i++){
	            System.out.print(arr[i]+" ");
	        }
	    }

	
}

快速排序与归并排序的区别

  • 快速排序是另外一种分治的排序算法,它将一个数组分成两个子数组,将两部分独立的排序。

  • 快速排序和归并排序是互补的:归并排序将数组分为两个子数组分别排序,并将有序的子数组归并从而将整个数组排序,而快速排序的方式则是当两个数组都有序时,整个数组自然就有序了。

  • 在归并排序中,一个数组被等分为两半,归并调用发生在处理整个数组之前,在快速排序中,切分数组的位置取决于数组的内容,递归调用发生在处理整个数组之后。

  • 快速排序最优情况如下图
    在这里插入图片描述

  • 快速排序最坏情况如下图
    在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值