八种常见的排序算法实现以及其性能比较

一,直接插入排序

其原理是,将序列分为一排序的有序子序列,和待排序的无序子序列。将待排序序列中的第一个元素插入到已排序子序列中去。使已排序序列+1,待排序序列-1。

	/**
	 * 直接插入排序
	 * */
	public static void straightInsertionSort(int[] array) {
		for(int i = 1;i<array.length;i++) {
			int t = array[i];
			int j;
			for(j = i-1;j>=0&&array[j]>t;j--) {
				array[j+1] = array[j];
			}
			array[j+1] = t;
		}
	}

二,折半插入排序

折半插入排序是直接插入的一种改进,在查找插入位置时,因为是在有序的序列上查找,所以可以使用折半查找(二分查找)

	/**
	 * 折半插入排序:查找插入位置时,使用二分查找
	 * */
	public static void bInsertSort(int[] array) {
		for(int i = 1;i<array.length;i++) {
			int low = 0;
			int high = i -1;
			int t = array[i];
			while(high>=low) {//二分查找插入位置
				int m = (low + high)/2;
				if(t>array[m]) low = m +1;
				else high = m -1;
			}
			for(int j = i-1;j>=low;j--) array[j+1] = array[j];//移动元素
			array[low] = t;
		}
	}

三,希尔排序

希尔排序也是直接插入排序的一种变体,其基本思想是,将一个待排序列分为若干个子序列,分别对若干个子序列进行插入排序,待整个序列基本有序之后,再对全体进行一次直接插入排序。这样可以减少元素移动的次数,从而大大的提高排序效率。

	//增量序列计算比较复杂,且不同的增量序列,会大大的影响算法的效率,所以这里直接使用网上推荐的比较好的增量序列
	public static int[] dlta = {9814,3280,1093,364,121,40,13,4,1};

	/**
	 * 希尔排序
	 * */
	public static void shellSort(int[] array) {
		for(int i = 0;i<dlta.length;i++) {
			shellInsert(dlta[i],array);
		}
	}
	
	/**
	 * @param dk 增量,普通的插入排序其增量为1
	 * */
	private static void shellInsert(int dk,int[] array) {
		for(int i = dk;i<array.length;i++) {
			int t = array[i];
			int j;
			for(j = i - dk;j>=0&&t<array[j];j -= dk) {
				array[j+dk] = array[j];
			}
			array[j+dk] = t;
		}
	}

优点:速度快
缺点:不稳定,增量序列的取值影响排序效率,且尚未找到最合适的增量序列

4,冒泡排序

其基本思想是,一躺冒泡从序列的最后一个元素开始和倒数第二个元素比较,若小于倒数第二个元素就将两个元素交换位置,依次进行,直到最小的元素被交换到最上为止。进行n-1躺冒泡之后序列整体有序。

	/**
	 * 冒泡排序
	 * */
	public static void bubbleSort(int[] array) {
		for(int i = 0;i<array.length;i++) {
			for(int j = array.length-1;j>i;j--) {
				if(array[j-1]>array[j]) {
					int t = array[j];
					array[j] = array[j-1];
					array[j-1] = t;
				}
			}
		}
	}

5,快速排序

快速排序是效率很高的一种排序算法。其基本思想是,通过一躺排序,将序列以一个枢轴为中间点分为两部分,其中枢轴左侧的都要小于枢轴元素,枢轴右侧都要大于枢轴元素。然后分别再对递归的对左右两边的子序列进行排序。

	public static void quickSort(int[] array){
		qSort(0,array.length -1,array);
	}
	
	/**
	 * 快速排序,对low到high的元素进行排序
	 * */
	private static void qSort(int low,int high,int[] array) {
		if(low < high) {
			int t = partition(low,high,array);
			qSort(low,t-1,array);
			qSort(t+1,high,array);
		}
	}
	
	/**
	 * 一次快速排序,将序列分为两部分,pivotkey的右边的元素均小于pivotkey
	 *pivotkey的左边的元素均大于pivotkey
	 * */
	private static int partition(int low,int high,int[] array) {
		int pivotkey = array[low];
		while(low<high) {
			while(low<high && array[high]>=pivotkey) high--;
			array[low] = array[high];
			while(low<high && array[low]<=pivotkey) low++;
			array[high] = array[low];
		}
		array[low] = pivotkey;
		return low;//返回pivotkey的下标
	}

优点:平均速度目前最快
缺点:不稳定,若序列有序时退化成冒泡排序,效率大大下降(有改进算法解决此问题)

6,选择排序

选择排序其思想是,每次从待排序子序列中找到最小的元素,然后和待排序子序列的第一个元素进行交换,然后待排序子序列-1,直到待排序子序列=1为止。

	/**
	 * 选择排序
	 * */
	public static void selectSort(int[] array) {
		int min;//记录最小值
		for(int i = 0;i<array.length-1;i++) {
			min = i;
			int j;
			for(j = i+1;j<array.length;j++) {//寻找最小值
				if(array[j]<array[min]) min = j;
			}
			int t = array[i];//交换位置
			array[i] = array[min];
			array[min] = t;
		}
	}

7,归并排序

归并排序也是一种效率很高的排序算法。归并,意思是将两个有序的序列合并为一个新的有序的序列。
而归并排序则则是利用了归并算法的一种排序算法。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列长度为一,然后两两进行归并,得到n/2个有序子序列,然后在两两归并,直到得到一个长度为n的有序序列为止。

public static void mergingSort(int[] array) {
		int temp[] = new int[array.length];
		mSort(0,array.length-1,array,temp);
	}
	
	/**
	 * 归并排序
	 * */
	private static void mSort(int low,int high,int[] array,int[] temp) {
		if(low == high) {
			temp[low] = array[low];
		}
		else {
			int m = (low + high)/2;
			mSort(low,m,array,temp);
			mSort(m+1,high,array,temp);
			merging(low,m,high,array,temp);
		}
	}
	
	/**
	 * 将两个有序的子序列,归并为新的有序子序列
	 * L[low...m]&&L[m+1...high]
	 * */
	private static void merging(int low,int m,int high,int[] array,int[] temp) {
		int i = low,j = m+1;
		int dt = 0;
		while(i<=m && j<=high) {
			if(array[i]<array[j]) {
				temp[low+dt] = array[i]; 
				i++;
			}
			else {
				temp[low+dt] = array[j];
				j++;
			}
			dt++;
		}
		while(i<=m) {
			temp[low+dt] = array[i];
			i++;
			dt++;
		}
		while(j<=high) {
			temp[low+dt] = array[j];
			j++;
			dt++;
		}
		//将排序好的数据复制回源数组
		for(int a = low;a<=high;a++) {
			array[a] = temp[a];
		}
	}

优点:稳定,快速
缺点:需要辅助空间

8,堆排序

堆的定义,一颗完全二叉树其根节点是整个树中最小的或者最大的元素,那么这个完全二叉树就是堆(大根堆/小根堆)。完全二叉树可以对于一个一维数组。第i个节点其左孩子为2i其右孩子为2i+1。
堆排序就是利用堆的一种排序算法,因为堆顶元素是最小或最大的元素,因此输出堆顶元素,之后使得剩下的序列重新建成一个堆,在输出堆顶元素,反复进行即可获得有序序列。

/**
	 * 调整堆头为i的堆,使其最小的元素成为堆头(大堆)
	 * 其左孩子下标为 2i 右孩子下标为2i+1;
	 * 这里数组下标从0开始,所以左右孩子下标为2i+1与2i+2
	 * 
	 * */
	private static void heapAdjust(int s,int m,int[] array) {
		int rc = array[s];
		for(int j = 2*s+1;j<=m;j=2*j+1) {
			if(j<m && array[j+1]>array[j]) j++;//选择较大的孩子
			if(rc>array[j]) break;
			array[s] = array[j];
			s = j;
		}
		array[s] = rc;
		
	}
	
	/**
	 * 将一个混乱的序列,构造成一个堆,最后一个非终端节点为(n-1)/2(n为数组最后一个元素下标)
	 * */
	private static void createHeap(int[] array) {
		int end = (array.length-2)/2;//计算最后一个非终端节点下标
		for(int i = end;i>=0;i--) heapAdjust(i,array.length-1,array);//从最后一个非终端节点开始,往上依次调整堆
	}

	public static void heapSort(int[] array) {
		createHeap(array);
		for(int j = array.length-1;j>0;j--) {
			int t = array[0];
			array[0] = array[j];
			array[j] = t;
			heapAdjust(0,j-1,array);
		}
	}

优点:速度快
缺点:不稳定

9,效率测试实验

package cn.sort;

import java.util.Arrays;
import java.util.Random;

public class MainClass {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		System.out.println(Arrays.toString(createArray(100000)));
		long stime = System.currentTimeMillis();
		long etime = System.currentTimeMillis();
		int n = 100000;
		int[] array1 = createArray(n);
		int[] array2 = Arrays.copyOf(array1, array1.length);
		int[] array3 = Arrays.copyOf(array1, array1.length);
		int[] array4 = Arrays.copyOf(array1, array1.length);
		int[] array5 = Arrays.copyOf(array1, array1.length);
		int[] array6 = Arrays.copyOf(array1, array1.length);
		int[] array7 = Arrays.copyOf(array1, array1.length);
		int[] array8 = Arrays.copyOf(array1, array1.length);
		stime = System.currentTimeMillis();
		Sort.bubbleSort(array4);
		etime = System.currentTimeMillis();
		System.out.println("冒泡排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.straightInsertionSort(array1);
		etime = System.currentTimeMillis();
		System.out.println("直接插入排序\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.bInsertSort(array2);
		etime = System.currentTimeMillis();
		System.out.println("二分插入排序\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.selectSort(array6);
		etime = System.currentTimeMillis();
		System.out.println("选择排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.shellSort(array3);
		etime = System.currentTimeMillis();
		System.out.println("希尔排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.quickSort(array5);
		etime = System.currentTimeMillis();
		System.out.println("快速排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.mergingSort(array7);
		etime = System.currentTimeMillis();
		System.out.println("归并排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
		stime = System.currentTimeMillis();
		Sort.heapSort(array8);
		etime = System.currentTimeMillis();
		System.out.println("堆排序\t\t"+n+"数据排序耗时\t"+(etime - stime)+"ms");
	}
	//生成长度为n的随机数数组
	public static int[] createArray(int n) {
		int[] array = new int[n];
		Random r = new Random();
		for(int i = 0;i<array.length;i++) {
			array[i] = r.nextInt(100000);
		}
		return array;
	}
	
	public static boolean isSort(int[] array) {
		int p = array[0];
		for(int i = 1;i<array.length;i++) {
			if(array[i]<p) return false;
			else p = array[i];
		}
		return true;
	}

}

实验结果:
冒泡排序 100000数据排序耗时 15483ms
直接插入排序 100000数据排序耗时 3323ms
二分插入排序 100000数据排序耗时 3467ms
选择排序 100000数据排序耗时 2266ms
希尔排序 100000数据排序耗时 20ms
快速排序 100000数据排序耗时 19ms
归并排序 100000数据排序耗时 19ms
堆排序 100000数据排序耗时 22ms

10,不同的增量序列堆希尔排序效率的影响实验

增量序列
{9814,3280,1093,364,121,40,13,4,1}
实验结果
希尔排序 100000数据排序耗时 19ms
增量序列
public static int[] dlta = {10000,1000,100,10,1};
实验结果
希尔排序 100000数据排序耗时 61ms

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值