排序总结(java版本)

本文总结的排序算法有简单选择排序、直接插入排序、冒泡排序、希尔排序、快速排序、堆排序、归并排序,桶排序,基数排序。
简单选择排序

private void selectSort(int[] nums){
		for(int i = 0; i < nums.length-1; i++){
			int index = i;
			for(int j = i+1; j < nums.length; j++){
				if(nums[j] < nums[index]){
					index = j;
				}
			}
			if(index != i){
				int tmp = nums[i];
				nums[i] = nums[index];
				nums[index] = tmp;
			}
		}
	}

插入排序

private void insertSort(int[] nums){
		for(int i = 0; i < nums.length-1; i++){
			int index = i+1;
			while(index > 0 && nums[index] < nums[index-1] ){
				int tmp = nums[index];
				nums[index] = nums[index-1];
				nums[index-1] = tmp;
				index--;
			}
		}
	}

冒泡排序

void bobbleSort(int[] nums){
	for(int i = 0; i < nums.length; i++){
		for(int j = 0; j < nums.length - i -1; j++){
			if(nums[j] > nums[j+1]){
				int tmp = nums[j];
				nums[j] = nums[j+1];
				nums[j+1] = tmp;
			}
		}
	}
}

归并排序

	private void mergeSort(int[] nums, int low, int high){
		if(low < high){
			int mid = (low + high)/2;
			mergeSort(nums, low, mid);
			mergeSort(nums, mid+1, high);
			merge(nums, low, mid, high);
		}
	}
	private void merge(int[] nums, int low, int mid, int high){
		int[] tmp = new int[high-low+1];
		int i = low, j = mid+1;
		int index = 0;
		while(i <= mid && j <= high){
			if(nums[i] > nums[j]){
				tmp[index++] = nums[j++];
			}else{
				tmp[index++] = nums[i++];
			}
		}
		for(; i <= mid;){
			tmp[index++] = nums[i++];
		}
		for(; j <= high;){
			tmp[index++] = nums[j++];
		}
		for(int k = 0; k < tmp.length; k++){
			nums[low+k] = tmp[k];
		}
	}

快速排序
partition函数
快排里面的partition函数用来解决这样的问题:给定一个数组arr[]和数组中任意一个元素a,重排数组使得a左边都小于它,右边都不小于它。

public class Partition {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Partition p = new Partition();
		int[] a = {1,4,3,6,7,9,2};
		System.out.println(p.partition(a,0,6,2));
		for(int i : a){
			System.out.print(i + " ");
		}
		
	}
	 // arr[]为数组,start、end分别为数组第一个元素和最后一个元素的索引
	 // povitIndex为数组中任意选中的数的索引
	 int partition(int arr[], int start, int end, int pivotIndex)
	 {
	     int pivot = arr[pivotIndex];
	     int tmp = arr[pivotIndex];
	     arr[pivotIndex] = arr[end];
	     arr[end] = tmp;
	     int storeIndex = start;
	     for(int i = start; i < end; ++i) {
	         if(arr[i] < pivot) {
	        	 tmp = arr[i];
	        	 arr[i] = arr[storeIndex];
	        	 arr[storeIndex] = tmp;
	             ++storeIndex;
	         }
	     }
	     tmp = arr[storeIndex];
	     arr[storeIndex] = arr[end];
	     arr[end] = tmp;
	     return storeIndex;
	 }
}

以上代码作为思想,改进后的快排代码:

void sort(int nums){
	quickSort(nums, 0, nums.length - 1);
}
void quickSort(int[] nums, int low, int high){
	if(low >= high){
		return;
	}
	int tmp = nums[low];
	int i = low;
	int j = high;
	while(i < j){
		while(i < j && nums[j] >= tmp){
			j--;
		}
		if(i < j){
			nums[i++] = nums[j];
		}
		while(i < j && nums[i] < tmp){
			i++;
		}
		if(i < j){
			nums[j--] = nums[i];
		}
	}
	nums[i] = tmp;
	quickSort(nums, low, i-1);
	quickSort(nums, i+1, high);
}

希尔排序
比较清晰的思路,作为算法思想的参考,可以直接跳过这个:

private void shellSort1(int[] nums)//Simple implementation
	{
	    int i , j ,gap;
	    int n = nums.length;
	    for(gap = n/2; gap > 0; gap /= 2){
	    	for(i = 0; i < gap; i++){
	    		for(j = i + gap; j < n; j += gap){
	    			if(nums[j] < nums[j-gap]){
	    				int temp = nums[j];
	    				int k = j - gap;
	    				while(k >= 0 && nums[k] > temp){
	    					nums[k + gap] = nums[k];
	    					k -= gap;
	    				}
	    				nums[k + gap] = temp;
	    			}
	    		}
	    	}
	    }
	}

比较简洁的代码,还是要用三重循环实现:

private void shellSort2(int[] nums){
		int gap, i, j;
		for(gap = nums.length/2; gap > 0; gap /= 2){
			for(i = gap; i < nums.length; i++){
				int temp = nums[i];
				for(j = i - gap; j >= 0; j -= gap){
					if(temp < nums[j]){
						nums[j + gap] = nums[j];
					}else{
						break;
					}
				}
				nums[j + gap] = temp;
			}
		}
	}

堆排序
用数组来存储堆,下标为i的结点,父节点的编号为(i-1)/2,子结点的编号为2i+1, 2i+2。
**建立堆:**每次插入一个元素并调整堆的过程。
**插入一个元素:**插入到数组最后,更新堆。
**删除一个元素:**删除发生在nums[0],将最后一个元素调整到nums[0]处,更新堆。
**堆排序:**堆排序主要包括两步,一是构建堆,二是交换堆顶元素与最后一个元素的位置。

	private void minHeapSort(int[] nums) {
		int i;
		int len = nums.length;
		for(i = len/2-1; i >= 0; i--){
			adjustMinHeap(nums, i, len -1);
		}
		for(i = len-1; i >= 0; i--){
			int tmp = nums[0];
			nums[0] = nums[i];
			nums[i] = tmp;
			adjustMinHeap(nums, 0, i - 1);
		}
	}
	private void adjustMinHeap(int[] nums, int pos, int len){
		int temp;
		int child;
		for(temp = nums[pos]; 2 * pos + 1 <= len; pos = child){
			child = pos * 2 + 1;
			if(child < len && nums[child] > nums[child + 1]){
				child++;
			}
			if(nums[child] < temp){
				nums[pos] = nums[child];
			}else{
				break;
			}
		}
		nums[pos] = temp;
	}

基数排序
参考http://blog.youkuaiyun.com/lg1259156776/article/details/48783753
以10进制排序为例,
思想:分配和收集。新建10个链表(或数组或其他容器),迭代n次(n为最大数的位数),对迭代i,将数字分配从右到左的第i位数字的桶号,最后按照顺序收集。
用链表来收集(n个),并增加指向链表的尾指针,尾指针2r(基,此处为10),增加总的空间(n+2r)。
一般分配代价O(n),收集代价O®,总代价O(d(r+n))
时间复杂度,平均O(d(r+n)),最坏O(d(n+rd)),最好O(d(r+n))
空间复杂度,O(rd+n)(r代表关键字的基数,d代表长度,n代表关键字的个数)
稳定。
桶排序
建立桶,将一个数据表分到各个桶(每个桶按照分区来接收数据),对非空桶各自排序再按顺序合并。(分治)

算法性能比较

排序算法最好时间平均时间最坏时间辅助空间稳定性备注
简单选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定n小时较好
直接插入排序 O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)稳定大部分已有序时较好
冒泡排序 O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)稳定n小时较好
希尔排序 O ( n ) O(n) O(n) O ( n l o g n ) O(nlog n) O(nlogn) O ( n s ) O(n^s) O(ns) 1 < s < 2 1<s<2 1<s<2O(1)不稳定s是所选分组
快速排序 O ( n l o g n ) O(nlog n) O(nlogn) O ( n l o g n ) O(nlog n) O(nlogn) O ( n 2 ) O(n^2) O(n2) O ( l o g n ) O(log n) O(logn)不稳定n大时较好
堆排序 O ( n l o g n ) O(nlog n) O(nlogn) O ( n l o g n ) O(nlog n) O(nlogn) O ( n l o g n ) O(nlog n) O(nlogn) O ( 1 ) O(1) O(1)不稳定n大时较好
归并排序 O ( n l o g n ) O(nlog n) O(nlogn) O ( n l o g n ) O(nlog n) O(nlogn) O ( n l o g n ) O(nlog n) O(nlogn) O ( n ) O(n) O(n)稳定n大时较好

后来又添加的一些排序算法比较:
排序比较

可参考常用排序算法时间复杂度和空间复杂度
在这个表格中,n是要被排序的纪录数量以及k是不同键值的数量。
稳定的排序
冒泡排序(bubble sort)— O(n2)
鸡尾酒排序(cocktail sort)—O(n2)
插入排序(insertion sort)—O(n2)
桶排序(bucket sort)—O(n);需要O(k)额外空间
计数排序(counting sort)—O(n+k);需要O(n+k)额外空间
归并排序(merge sort)—O(n log n);需要O(n)额外空间
原地归并排序— O(n2)
二叉排序树排序(binary tree sort)— O(n log n)期望时间; O(n2)最坏时间;需要O(n)额外空间
鸽巢排序(pigeonhole sort)—O(n+k);需要O(k)额外空间
基数排序(radix sort)—O(n·k);需要O(n)额外空间
侏儒排序(gnome sort)— O(n2)
图书馆排序(library sort)— 时间复杂度通常是O(n log n),需要(1+ε)n额外空间

不稳定的排序
选择排序(selection sort)—O(n2)
希尔排序(shell sort)—O(n log2 n)如果使用最佳的现在版本
Clover排序算法(Clover sort)—O(n)期望时间,O(n^2/2)最坏情况
梳排序— O(n log n)
堆排序(heap sort)—O(n log n)
平滑排序(smooth sort)— O(n log n)
快速排序(quick sort)—O(n log n)期望时间, O(n2)最坏情况;对于大的、乱数列表一般相信是最快的已知排序
内省排序(introsort)—O(n log n)
耐心排序(patience sort)—O(n log n + k)最坏情况时间,需要额外的O(n + k)空间,也需要找到最长的递增子序列(longest increasing subsequence)

不实用的排序
Bogo排序— O(n × n!),最坏的情况下期望时间为无穷。
Stupid排序—O(n3);递归版本需要O(n2)额外存储器
珠排序(bead sort)— O(n) or O(√n),但需要特别的硬件
煎饼排序—O(n),但需要特别的硬件
臭皮匠排序(stooge sort)算法简单,但需要约n^2.7的时间

平均时间复杂度由高到低为:
冒泡排序O(n2)
选择排序O(n2)
插入排序O(n2)
希尔排序O(n1.25)
堆排序O(n log n)
归并排序O(n log n)
快速排序O(n log n)
基数排序O(n)
说明:虽然完全逆序的情况下,快速排序会降到选择排序的速度,不过从概率角度来说(参考信息学理论,和概率学),不对算法做编程上优化时,快速排序的平均速度比堆排序要快一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值