排序算法之----归并排序,快速排序,堆排序

1 归并排序

分而治之(divide - conquer);每个递归过程涉及三个步骤
第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素.
第二, 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
第三, 合并: 合并两个排好序的子序列,生成排序结果.
时间复杂度:O(nlogn)

public static void mergeSort(int[] arr){
	if(arr == null || arr.length < 2){
		return;
	}
	mergeSort(arr,0,arr.length-1);
}
public static void mergeSort(int[] arr,int L,int R){
	if(L == R){
		return;
	}
	int mid = L + (R-L)/2;
	mergeSort(arr,L,mid);
	mergeSort(arr,mid+1,R)
	merge(arr,L,mid,R);
}
public static void merge(int[] arr,int L,int mid,int R){
	int[] help = new int[R - L + 1];//辅助数组
	int p1 = L;
	int p2 = mid + 1;
	int i = 0;
	 // 把较小的数先移到新数组中
	while(p1 <= mid && p2 <= R){
		help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
	}
	 // 把左边剩余的数移入数组 
	while(p1 <= mid){
		help[i++] = arr[p1++];
	}
	// 把右边剩余的数移入数组
	while(p2 <= R){
		help[i++] = arr[p2++];
	}
	 // 把新数组中的数覆盖nums数组
	for(int j=0; j < help.length; j++){
		arr[L + j] = help[j];
	}
}

2快速排序

本文为经典快排的改进版–随机快排

  • 是一种比较快的排序,适合基本无序的数据
  • 从数列中挑取最后一个元素(最后一个元素随机选出)做基准,重新排列,所有比基准小的放前面,比基准大的放后面
  • 分成两拨后,继续递归的使用上述方法,最终有序
  • 时间复杂度:O(nlogn)
public static void quickSort(int[] arr) {
	if (arr == null || arr.length < 2) {
		return;
	}
	quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr,int L,int R){
	if(L < R){
		swap(arr, L + (int)(Math.random()*(R - L + 1)), R);//在数组中随机选取一个作为基准
		int[] p = partition(arr, L, R);
		quickSort(arr, L, p[0] - 1);
		quickSort(arr, p[1] + 1);
	}
}
/*
less指针为小于基准的区域,初始指向-1,
more指针为大于基准的区域,初始指向基准位置,
开始时让数组第一位即L与基准相比,小于则交换L与less前一位,L+1;
大于则交换L与more的后一位,L继续与基准比较;相等则L+1
由于基准没参与交换,所以每次遍历完之后要交换more位置与基准位置,确保有序
*/
public static int[] partition(int[] arr,int L,int R){
	int less = L - 1;
	int more = R;
	while(L < more){//
		if(arr[L] < arr[R]){
			swap(arr,++less,L++);
		}else if(arr[L] > arr[R]){
			swap(arr,--more,L);
		}else{
			i++;
		}
	}
	swap(arr,more,R);
	return new int[]{less + 1, more};//返回基准值相等的左右边界
}
public static void swap(int[] arr, int i, int j) {
	int tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

3堆排序

1 将序列构建成大顶堆。
2 将根节点与最后一个节点交换,然后断开最后一个节点
3 重复第一、二步,直到所有节点断开。

  • 时间复杂度:O(nlogn)
public static void heapSort(int[] arr){
	if(arr == null || arr.length < 2){
		return;
	}
	for(int i=0; i<arr.length; i++){
		heapInsert(arr,i);
	}
	int size = arr.length;
	swap(arr, 0, --size);
	while(size > 0 ){
		heapify(arr, 0, size);
		swap(arr, 0, --size);
	}
}
public static void heapInsert(int[] arr,int index){
	while(arr[index] > arr[index - 1] / 2){
		swap(arr, index, (index - 1) / 2);
		index = (index - 1) / 2;
	}
}
public static void heapify(int[] arr,int index,int size){
	int left = index * 2 + 1;
	while(left < size){
		int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
		largest = arr[largest] > arr[index] ? largest : index;
		if(largest == index){
			break;
		}
		swap(arr, largest, index);
		index = largest;
		left = index * 2 + 1;
	}
}
public static void swap(int[] arr, int i, int j) {
	int tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定
冒泡排序 O(n2) O(n2) O(1)
选择排序 O(n2) O(n2) O(1) 不是
直接插入排序 O(n2) O(n2) O(1)
归并排序 O(nlogn) O(nlogn) O(n)
快速排序 O(nlogn) O(n2) O(logn) 不是
堆排序 O(nlogn) O(nlogn) O(1) 不是
希尔排序 O(nlogn)O (ns) O(1) 不是
计数排序 O(n+k) O(n+k) O(n+k)
基数排序 O(N∗M) O(N∗M) O(M)

猜你喜欢

转载自blog.csdn.net/Felix_ar/article/details/83242820