java的经典排序算法

排序算法有多种分类方式,按照是否需要外存储器分为内部排序和外部排序,按照稳定性分为稳定排序和不稳定排序。今天我这里只是涉及到内部排序的几种经典算法,也供自己以后复习所用。

内部排序基本分为四大类:插入排序(Insertion),交换排序(exchange),选择排序(selection)和归并排序(merging)。

插入排序:直接插入排序(straight Insert)、希尔排序(shell)
交换排序:冒泡(buddle)、快速排序(Quick)
选择排序:直接(简单)选择排序、堆排序(Heap)
归并排序:只有自己咯

今天不讨论buddle sort和simple selection,主要讨论其他几种。先来个表,让各位有个直观的印象:

序号

排序类别

平均时间复杂度

最好情况

最坏情况

空间复杂度(辅助存储)

稳定

1

直接插入排序

O(n2)

O(n)

O(n2)

1

2

希尔排序

O(n2)

 

 

1

×

3

冒泡排序

O(n2)

 

 

1

4

快速排序

O(Nlogn)

 

O(n2)

O(logn)

×

5

简单选择排序

O(n2)

 

O(n2)

1

×

6

堆排序

O(Nlogn)

 

O(Nlogn)

1

×

7

归并排序

O(Nlogn)

 

O(Nlogn)

O(n)


插入排序:

      1.直接插入排序

直接插入排序的基本思想为:将一个记录插入到一个已经有序的列表中,从而得到新的有序的列表。
整个过程分为n-1趟插入,即:先将序列的第一个元素看作是一个有序的子序列。
public class StraightInsertion {

	public static void insertSort(int[] data) {
		int length = data.length;
		
		//从数组的第二个元素开始:相当于第一个元素为以排序的数组array2
		for (int i = 1; i < length; i++) {
			
			//tmp即为待插入的元素
			int tmp = data[i];
			
			//由于是逐次的,所以若tmp大于有序的array2的第一个元素,则tmp为最大元素
			if ( data[i - 1] > tmp) {
				int j = i - 1;
				
				//当待插入元素逐次和array2比较(没有交换,这里仅仅是后移,给tmp预留空间)
				//当tmp大于data[j]时,跳出循环,则数据插入到j + 1的位置上
				for (; j >= 0 && data[j] > tmp; j--) {
					//元素后移操作
					data[j + 1] = data[j];
				}
				//
				data[j + 1] = tmp;
			}
		}
	}

}

2.希尔排序

shell sort又称Diminishing Increment Sort(缩小增量排序)。
希尔排序是改良版的直接插入排序,原因有二:1.当记录基本有序时,直接插入排序的时间复杂度,从O(n2)提高至O(n).2.当n值很小时效率也比较高。
希尔排序的基本思想是:先将整个待排序记录分割为若干子序列分别进行直接插入排序,待整个记录基本有序时,再对整体进行一次直接插入排序。
/**
 * shell排序,这里的增量(delta value),使用的 
 * <br>1.首先確定轮数,公式为2^n < length, 通过轮数确定第一个增量为2^n - 1.
 * <br>2.
 *
 */
public class Shell {

	    public static void loop(Integer[] data){
	    	if(data == null) return;
	    	
	    	//先找出最大圈数和最大的delta
	    	int times = 1;
	    	int delta = -1;
	    	while(Math.pow(2, times) < data.length){
	    		delta = (int) (Math.pow(2, times) - 1);
	    		times++;
	    	}
	    	
	    	do{
		    	for (int i = 0; i < delta; i++) {
					insertSort(data, i, delta);
				}
		    	times = (int) ((Math.log(delta + 1)/Math.log(2)) - 1);
		    	delta = (int) (Math.pow(2, times) - 1);
	    	}while(delta > 0);
	    	
	    }
	    
	    /**
	     * 这里就是插入排序的实现,不同点是待排序array为非连续的
	     * @param data
	     * @param from 从下表from开始
	     * @param delta 增量值
	     */
	    public static void insertSort(Integer[] data, int from , int delta) {
			if(data == null) return;
	    	int length = data.length;
			
			//从数组的第二个元素开始:相当于第一个元素为以排序的数组array2
	    	int size = (length - from) % delta == 0 ? (length - from) / delta : ((length - from) / delta) + 1 ;
			for (int i = 1; i < size; i++) {
				
				//tmp即为待插入的元素
				int tmp = data[from + i * delta];
				
				//由于是逐次的,所以若tmp大于有序的array2的第一个元素,则tmp为最大元素
				if ( data[from + (i - 1) * delta] > tmp) {
					int j = i - 1;
					
					//当待插入元素逐次和array2比较(没有交换,这里仅仅是后移,给tmp预留空间)
					//当tmp大于data[j]时,跳出循环,则数据插入到j + 1的位置上
					for (; from + j * delta >= from && data[from + j * delta] > tmp; j--) {
						//元素后移操作
						data[from + (j + 1) * delta] = data[from + j * delta];
					}
					//
					data[from + (j + 1) * delta] = tmp;
				}
			}
		}
}

 交换排序:快速排序

快速排序思想:通过一趟排序,将待排序记录分为独立的两部分(找到枢轴),其中一部分的均比另一部分小(通过和枢轴比较,大于枢轴的放在枢轴的后面,否则反之,让后将枢轴放在移动后的空缺位置,即枢轴位置),则可分别对这两部分记录继续进行排序。
/**
 * 快速排序,同样为不稳定排序
 * 循环去寻找一个数组中的枢轴,使得这个枢轴的左端元素小于等于枢轴,右端元素大于等于枢轴
 */
public class Quick {
	
	public static void sort(Integer[] data, int low, int high){
		
		if(low < high){
			int pivotLoc = partition(data, low, high);
			if(pivotLoc > low)
				sort(data, low, pivotLoc - 1);
			if(pivotLoc < high)
				sort(data, pivotLoc + 1, high);
		}
	}
	
	/**
	 * 寻找枢轴
	 * @param data
	 * @param low
	 * @param high
	 * @return
	 */
	public static int partition(Integer[] data, int low , int high){
		int pivot = data[high];
		//high -=1;
		while(low < high){
			while(low < high && pivot >= data[low])	
				low++;
			data[high] = data[low];
			while(low < high && pivot <= data[high]) 
				high--;
			data[low] = data[high];
		}
		data[high] = pivot;
		return high;
	}

}

选择排序:

选择排序的基本思想:每一趟在n-i+1(i=1,2,、、n-1)个记录中,选取最小的记录作为有序序列的第i个元素。

简单选择排序:

通过n-i次比较,从n-i+1个记录中选择最小的记录,并和第i个记录进行交换。

堆排序

将序列排为大顶堆,将第一个元素输出之后,n-1个元素再次调整为大顶堆,得到次小值,如此反复执行,得到有序序列,成为堆排序。
n个节点的完全二叉树的深度为(log2n向下取整+1)
public class Heap {
	
	public static void heapSort(Integer[] data){
		//第一轮,从最后一个非终端节点开始,将数组建成大顶堆
		for (int i = data.length / 2 - 1 ; i >= 0 ; i--) {
			heapAdjust(data, i, data.length - 1);
		}
		
		
		//将交换过后的堆,继续筛选为大顶堆
		for (int last = data.length - 1; last > 0; last--) {
			//将堆顶和堆的最后一个元素进行交换
			int tmp = data[last];
			data[last] = data[0];
			data[0] = tmp;

			heapAdjust(data, 0, last - 1);
			
		}
	}
	
	/**
	 * 
	 * @param data 待排序的数组
	 * @param s 非终端节点的下标
	 * @param m 带排序的数组的length
	 */
	public static void heapAdjust(Integer[] data, int s , int m){
		int parent = data[s];
		int child = -1;
		for ( child = s * 2 + 1; child <= m; child = child * 2 + 1) {
			if(child < m && data[child] < data[child + 1]) child++;
			if(parent >= data[child]) break;
			data[s] = data[child];
			s = child;
		}
		data[s] = parent;
	}

}

归并排序

归并的含义是:将两个或两个以上的有序表,组合成为一个新的有序表。
思路:假设含有n个记录,则可看为是n个有序的子序列,每个子序列长度为1,两两归并,得到长度为n/2向上取整个长度为2或者1的有序子序列,再两两归并……
public class Merging {
	
	public static Integer[] sort(Integer[] data){
		if( data.length <= 1){
			return data;
		}
		
		Integer[] left = Arrays.copyOfRange(data, 0, data.length / 2);
		Integer[] right = Arrays.copyOfRange(data, data.length / 2, data.length);
		
		left = sort(left);
		right = sort(right);
		
		Integer[] result = merge(left, right);
		return result;
	}
	
	public static Integer[] merge(Integer[] left, Integer[] right){
		Integer[] result = new Integer[left.length + right.length];
		int i = 0;
		while(left.length > 0 && right.length > 0){
			if(left[0] > right[0]){
				result[i++] = left[0];
				left = Arrays.copyOfRange(left, 1, left.length);
			}else{
				result[i++] = right[0];
				right = Arrays.copyOfRange(right, 1, right.length);
			}
		}
		if(left.length > 0){
			System.arraycopy(left, 0, result, i, result.length - i);
		}
		if(right.length > 0){
			System.arraycopy(right, 0, result, i, result.length - i);
		}
		return result;
	}
}
这个归并排序用了性能很差的递归,一般情况下都不这样用,会调整为消耗更大存储空间的循环来做。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值