Java实现各大经典排序算法(插入、希尔、简单选择、冒泡、快速、归并)

1.插入排序

    基本思想:数组中有n个元素,那么我们先把第一个元素看成一个有序数列,将第2个元素插入到前面的有序数列中,直至将第n个元素插入到前面长度为n-1的有序数列中就实现了排序。时间复杂度为O(n^2);


2.希尔排序(递减增量排序算法)

    基本思想:针对直接插入排序的效率问题(1.插入 排序在对几乎已经排好序的数据 操作时,效率高,即可以达到线性排序的效率;2.一般来说插入排序是低效的,每次只能将数据移动一位。),提出的改进与升级,希尔排序是非稳定排序算法。思想如下,数组中有n个元素,首先求出k=n/2,将下标差值为k的元素利用插入排序变为有序序列,再次求出k=k/2,将下标差值为k的元素利用插入排序变为有序序列,重复上述做法,直至k=1时,执行简单插入排序,这时数组的排序完成。


3.简单选择排序

     基本思想:将数组分为两个部分,第一部分为已排序数组(初始为空),第二部分为待排序数组(初始为整个数组),那么排序的过程为:第一次在待排序数组中找到最小(最大)的元素,放在已排序数组中,直至待排序数组为空,排序完成。尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序。

4.冒泡排序

     基本思想:将数组中的元素两两比较,找出最大的放到最右边(或者找出最小的放在最左边),也就是一次冒泡,下一次冒泡开始的时候,对已经找到位置的元素(已经确定顺序位置的元素)不参与本次的冒泡,直到参与冒泡的元素为0,则整个数组有序。

    改进思路:设置标志位,明显如果有一趟没有发生交换(flag = false),说明排序已经完成。


5.快速排序

    基本思想:在待排序数组中找到一个基准值(一般为数组的首元素),第一趟扫描,将比基准值小的放在基准值的左边,将比基准值大的放在基准值右边,这样基准值的位置就正确了,再用同样的方法递归基准值左右两个部分,直至整个数组有序。时间复杂度为O(nlogn) 。


6.归并排序(二路归并)

     基本思想 :首先将原始的无序序列划分成两个子序列,然后分别对每个子序列递归进行排序,最后再将有序子列合并。二路归并排序是首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。以此类推,直至得到长度为n的有序序列。 时间复杂度 为O(nlogn),空间复杂度为O(n+logn),如果非递归实现归并,则避免了递归时深度为logn的栈空间,那么空间复杂度为O(n)。


各类排序算法的时间复杂度与空间复杂度对比表


 

7.代码实现

  • 生成乱序数组的工具类
   
package com.norte.util;

import java.util.Random;

public class ArrayUtil {
	
	/**
	 * @author Norte
	 * Date:2018-5-18
	 * 
	 * 功能:
	 * 利用随机函数生成一个指定大小的随机数组
	 * */
	
	public ArrayUtil() {
	}
	
	public int[] MakeArray(int n) {
		int[] a = new int[n];
		for(int i = 0; i < a.length; i++) {
			Random random = new Random();   //产生一个Random实例,调用nextInt(int n)生成的随机数是介于0到n的int类型整数。
			a[i] = random.nextInt(100);
//			a[i] = (int) (Math.random() * 100);  //Math.random()生成的随机数是介于0.0到1.0的double类型数值。
		}
		
		return a;
	}
	
	public static void main(String[] args) {
		ArrayUtil arrayUtil = new ArrayUtil();
		int[] a = arrayUtil.MakeArray(10);
		for(int i = 0; i < a.length; i++) {
			System.out.println("a[" + i +"]:" + a[i]);
		}
	}

}

  • 实现各种排序算法的排序类
    
package com.norte.sort;

public class Sort {

	/**
	 * @author Norte
	 * Date:2018-5-18
	 * 
	 * 功能:直接插入排序
	 * 
	 * 基本思想:数组中有n个元素,那么我们先把第一个元素看成一个有序数列,将第2个元素插入
	 * 到前面的有序数列中,直至将第n个元素插入到前面长度为n-1的有序数列中就实现了排序。
	 * 
	 * */
	
	public void insertSort(int[] arr) {
		int len = arr.length;  //单独拿出数组长度,提高效率
		int tmp;   //要插入有序数列的数
		for(int i = 1; i < len; i++) {  //第一个元素不需要插入,从第二个开始
			tmp = arr[i]; 
			int j = i - 1;   //有序数列的元素个数
			while(j >= 0 && tmp <= arr[j]) {  //将有序数列的元素从后向前,将大于tmp的依次后移
				arr[j+1] = arr[j];
				j--;
			}
			arr[j+1] = tmp;  //找到tmp的插入位置
		}
	}
	
	/**
	 * @author Norte
	 * Date:2018-5-18
	 * 
	 * 功能:希尔排序(递减增量排序算法)
	 * 
	 * 基本思想:针对直接插入排序的效率问题(1.插入 排序在对几乎已经排好序的数据 操作时,效率高,即可以达到线性排序的效率;
	 * 2.一般来说插入排序是低效的,每次只能将数据移动一位。),提出的改进与升级,希尔排序是非稳定排序算法。思想如下,数组
	 * 中有n个元素,首先求出k=n/2,将下标差值为k的元素利用插入排序变为有序序列,再次求出k=k/2,将下标差值为k的元素利用插
	 * 入排序变为有序序列,重复上述做法,直至k=1时,执行简单插入排序,这时数组的排序完成。
	 * */
	
	public void sheelSort(int[] arr) {
		int len = arr.length; 
		while(len != 0) {
			len = len >> 1;   
			for(int i = 0; i < len; i++) {  //分组,就是整个数组从0开始到结束,符合下标差值为len的子序列组数
				for(int j = i + len; j < arr.length; j += len) {   //子序列进行插入排序,直至len=0
					int k = j - len;
					int tmp = arr[j];
					while(k >= 0&&arr[k] >= tmp) {
						arr[k+len] = arr[k];
						k -= len;
					}
					arr[k+len] = tmp;
				}
			}
		}
	}
	
	/**
	 * @author Norte
	 * Date:2018-5-18
	 * 
	 * 功能:简单选择排序
	 * 
	 * 基本思想:将数组分为两个部分,第一部分为已排序数组(初始为空),第二部分为待排序数组(初始为整个数组),那么排序的过程
	 * 如下:第一次在待排序数组中找到最小(最大)的元素,放在已排序数组中,直至待排序数组为空,排序完成。
	 * 
	 * */
	
	public void selectSort(int[] arr) {
		int len = arr.length;
		int minIndex;
		for(int i = 0; i < len - 1; i++) { //控制选择的次数
			minIndex = i;
			for(int j = i + 1; j < len; j++) {  //找出待排序数组中的最小元素的下标minIndex
				if(arr[minIndex] > arr[j]) {
					minIndex = j;
				}
			}
			
			if(minIndex != i) { //下标比已排序数组的最后一位小,则进行交换
				arr[minIndex] = arr[minIndex] ^ arr[i]; //利用位运算交换提高效率
				arr[i] = arr[minIndex] ^ arr[i];
				arr[minIndex] = arr[minIndex] ^ arr[i];
			}
		}
		
	}
	
	/**
	 * @author Norte
	 * Date:2018-5-20
	 * 
	 * 功能:冒泡排序
	 * 
	 * 基本思想:将数组中的元素两两比较,找出最大的放到最右边(或者找出最小的放在最左边),也就是一次冒泡,
	 * 下一次冒泡开始的时候,对已经找到位置的元素(已经确定顺序位置的元素)不参与本次的冒泡,直到参与冒泡
	 * 的元素为0,则整个数组有序。
	 * 
	 * */
	
	public void bubbleSort(int[] arr) {
		int len = arr.length;
		for(int i = 0; i < len; i++) { //控制循环次数
			for(int j = 0; j < len - i - 1; j++) { //len - i - 1为未排序的元素个数
				if(arr[j] > arr[j + 1]) {   //前数比后数大则进行交换
					arr[j] = arr[j] ^ arr[j + 1];
					arr[j + 1] = arr[j] ^ arr[j + 1];
					arr[j] = arr[j] ^ arr[j + 1];
				}
			}
		}
	}
	
	/**
	 * @author Norte
	 * Date:2018-5-20
	 * 
	 * 功能:快速排序
	 * 
	 * 基本思想:在待排序数组中找到一个基准值(一般为数组的首元素),第一趟扫描,将比基准值小的放在基准值的左边,
	 * 将比基准值大的放在基准值右边,这样基准值的位置就正确了,再用同样的方法递归基准值左右两个部分,直至整个
	 * 数组有序。
	 * */
	
	public int getMiddle(int[] arr, int start, int end) {
		int tmp = arr[start];  //数组的第一个作为基准值
		while(start < end) {
			while(end > start && arr[end] >= tmp) { //从后向前比较,不小于基准值则向前移动(end--)
				end --;
			}
			arr[start] = arr[end];  //小于基准值,则把该值移动到低端
			while(end > start && arr[start] < tmp) {  //从前向后比较,小于基准值则向后移动(start++)
				start++;
			}
			arr[end] = arr[start]; //不小于基准值,则把该值移动到高端
		}
		arr[start] = tmp; //基准值到位(此时的start = end)
		return start; //返回基准值的下标
	}
	
	public void quickSort(int[] arr, int start, int end) {
		if(start < end) {
			int middle = getMiddle(arr, start, end); //获取基准值的位置下标,将数组一分为二
			quickSort(arr, start, middle - 1); //对低端序列递归排序
			quickSort(arr, middle + 1, end);  //对高端序列递归排序
		}
	}
	
	/**
	 * @author Norte
	 * Date:2018-5-20
	 * 
	 * 功能:归并排序(二路归并排序)
	 * 
	 * 基本思想 :首先将原始的无序序列划分成两个子序列,然后分别对每个子序列递归进行排序,最后再将有序子列合并。
	 * 二路归并排序是首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]
	 * 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,
	 * 得到若干个长度为4的有序子序列。以此类推,直至得到长度为n的有序序列。
	 * */
	
	public void merge(int[] arr, int start, int middle, int end) {
		int[] temp = new int[end - start + 1];  //临时数组,两个序列合并后存储于此
		int i = start;  //第一个序列的指针
		int j = middle + 1;  //第二个序列的指针
		int k = 0;  //临时数组的指针
		while(i <= middle && j <= end) { //终止条件:某个序列已经全部比较完毕,代表已经有序列全部移入到临时数组中
			if(arr[i] < arr[j]) {  //第一个序列中较小的元素放到临时数组中
				temp[k++] = arr[i++]; 
			} else { //第二个序列中较小的元素放到临时数组中
				temp[k++] = arr[j++]; 
			}
		}
		while(i <= middle) { //如果第一个序列没有移完,则将剩下的移到临时数组中
			temp[k++] = arr[i++];
		}
		while(j <= end) {   //如果第二个序列没有移完,则将剩下的移到临时数组中
			temp[k++] = arr[j++];
		}
		for(int index = 0; index < temp.length; index++) { //将临时数组的值赋给原始数组
			arr[index + start] = temp[index];
		}
	}
	
	public void mergeSort(int[] arr, int start, int end) {
		int middle = (end + start) / 2;
		if(start < end) { 
			mergeSort(arr, start, middle);   //左半边
			mergeSort(arr, middle + 1, end); //右半边
			merge(arr, start, middle, end);  //左右合并
		}
	}
}   
  • 测试类
    
package com.norte.test;

import java.util.Arrays;

import com.norte.sort.Sort;
import com.norte.util.ArrayUtil;

public class SortTest {

	public static void main(String[] args) {
		ArrayUtil arrayUtil = new ArrayUtil();
		Sort sort = new Sort();
		int[] array = arrayUtil.MakeArray(10);
		System.out.println("排序前:" + Arrays.toString(array));
	//	sort.insertSort(array); //插入排序
	//	sort.sheelSort(array);  //希尔排序
	//	sort.selectSort(array); //简单选择排序
	//	sort.bubbleSort(array); //冒泡排序
	//	sort.quickSort(array, 0, array.length - 1); //快速排序
		sort.mergeSort(array, 0, array.length - 1); //归并排序
		System.out.println("排序后:" + Arrays.toString(array));
	}
}
  • 测试结果
         

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值