O(nlogn)时间复杂度排序(归并、快速排序)算法总结

归并和快速排序都是利用分治的思想来把大问题拆分成一个个子问题,子问题都解决了,大问题也就解决了。

归并排序

归并排序算法的求解过程可以解释为层层递进式的将数列折半,当数列无法折半的时候(也就是数列内仅剩一个元素)终止递进,数列内仅有一个元素,数列就是有序的,然后回退到上一层等待另一半数列也递进终止回退到这一层,接着合并这两个有序数列,然后再回退到上一层重复逻辑,如此层层合并回退,最终完整的数列达到有序。

针对这个逻辑,我们可以写出推导公式:f(n)  = m(s...p) + m(p + 1...n),s表示队列起始位置,p表示队列中点,n表示队列末端。

归并排序将数列折半分解后的形态可以看成是一棵完全二叉树,排序的时间主要花在合并数列,树的每一层在递归回退时进行一次合并操作,每次合并操作都是作用于所有元素的,所以消耗时间为n,数高k层,那么总耗时为nk,k=log2n,带入得nlog2n,用O表示法系数可以省略,所以时间复杂度为O(nlogn)。归并排序不管数列的有序度是满有序还是逆有序两个极端,都需要进行log2n次分解合并操作,所以最好和最坏时间复杂度都是O(nlogn),平均时间复杂度为O(nlogn)。在每一层进行合并操作时,归并排序都要额外开辟n大小的临时空间来暂存合并后的数值,函数栈退出时释放,所以空间复杂度为O(n),总的空间复杂度为O(nlogn)。

归并排序是稳定排序。

C语言版实现代码如下:

void mergeSortFunc(int arr[], int left, int right){
	int pivot;
	int* tmpArr;
	int i, ii, jj;
	if(left >= right){
		return;
	}
	pivot = left + (right - left) / 2;
	mergeSortFunc(arr, left, pivot);
	mergeSortFunc(arr, pivot + 1, right);
	
	tmpArr = (int*)malloc((right - left + 1) * sizeof(int));
	i = 0;
	ii = left;
	jj = pivot + 1;
	while(ii <= pivot && jj <= right){
		if(arr[ii] >= arr[jj]){
			tmpArr[i] = arr[ii];
			ii++;
		}
		else{
			tmpArr[i] = arr[jj];
			jj++;
		}
		i++;
	}
	while(ii <= pivot){
		tmpArr[i] = arr[ii];
		ii++;
		i++;
	}
	while(jj <= right){
		tmpArr[i] = arr[jj];
		jj++;
		i++;
	}
	i = 0;
	for(; i < right - left + 1; i++){
		arr[left + i] = tmpArr[i];
	}
	free(tmpArr);
}

void mergeSort(int arr[], unsigned int size){
	if(size < 2){
		return;
	}
	mergeSortFunc(arr, 0, size - 1);
}

快速排序

快速排序的求解过程为递进式将数列进行分区,选取数列中任一位置的值,将数列分成左右两部分,左边的大于右边的,或者右边的大于左边的,层层递进重复逻辑,当所有区内数列都分解得不能再分了(数列内元素仅剩一个),整个数列也就有序了。

递推公式和归并排序差不多:f(n)  = partition(s...p - 1) + partition(p + 1...n)

如果快速排序每次分区都能把数列分成数量均衡的两部分,那么快速排序的就能达到最优的效率,递归分解后的形态就是一棵完全二叉树的形态,快速排序消耗时间主要集中在每层的分区消耗,每层分区的元素总数为完整的数列长度,所以时间复杂度为n,而分区次数为树的层数,即log2n,总的时间复杂度为O(nlogn)。如果分每次分区的左右区间的数据极度倾斜(比如都是左边有元素右边没元素),那么排序效率就是将会退化为O(n^2),可想象为由之前最优的完全二叉树退化成了链表。

因为快速排序是原地排序,所以空间复杂度为O(1),这也是为什么快速排序要比归并排序受欢迎的原因。

快速排序在进行数据分区时,无法保证相同元素分区前后的排列顺序不发生变化,所以不是稳定排序。

C语言版本实现代码如下:

void quickSortFunc(int arr[], int left, int right){
	int pivotValue;
	int i, j;
	int temp;
	if(left >= right){
		return;
	}
	pivotValue = arr[right];
	i = j = left;
	while(j < right){
		if(arr[j] > pivotValue){
			temp = arr[j];
			arr[j] = arr[i];
			arr[i] = temp;
			i++;
		}
		j++;
	}
	arr[right] = arr[i];
	arr[i] = pivotValue;

	quickSortFunc(arr, left, i - 1);
	quickSortFunc(arr, i + 1, right);
}

void quickSort(int arr[], unsigned int size){
	quickSortFunc(arr, 0, size - 1);
}

 

你可能感兴趣的:(编程)