排序算法:快速排序(quick sort)的思想与实现

ps: 如果有任何问题可以评论留言,我看到后会及时解答,评论或关注,您的鼓励是我分享的最大动力

转载请注明出处:
https://blog.csdn.net/qq_40938301/article/details/88365408

介绍:

快速排序是历史上最著名的算法之一。1959年由 托尼 霍尔 (Tony Hoare)发明。

目标:

将一个数组按照由低到高(或者由高到低)的顺序排序。

核心思想:

1、基准:在数组中选一个数,作为基准(pivot)

2、分区:将数组根据基准分为三部分(即三个数组):

                less:数组中所有数小于基准
                equal:数组中所有数等于基准
                high:数组中所有数大于基准

                三者组合就是分区上的升序,再在3中对分区升序细化至每个元素

3、递归:对 less 和 high 递归做2中的分区,直到数组中只有一个元素不能再分

实现:

快排根据分区的实现方法和基准的选择不同,有不同的版本,性能也有所不同

1、Kotlin (最容易理解的实现版本)

function quickSort(arr){
	if(arr.length<2) return arr;

	var mid = arr[Math.floor(arr.length/2)];

	var less = arr.filter((x)=>x<mid);
	var equal = arr.filter((x)=>x==mid);
	var high = arr.filter((x)=>x>mid);

	return quickSort(less)+equal+quickSort(high);
}

扫描二维码关注公众号,回复: 9559452 查看本文章

2、Lomuto 分区方案

function partitionLomuto(arr, low, high){
	var pivot = arr[high];
	
	var i = low;
	for(j=low;j< high;j++){
		if(arr[j] <= pivot){
			swap(arr,i,j);
			i++;
		}
	}

	swap(arr,i,high);
	return i;
}

function swap(arr,x,y){
	var temp = arr[x];
	arr[x] = arr[y];
	arr[y] = temp;
}
function quickSortLomuto(arr,low,high){
	if(low < high){
		var p = partitionLomuto(arr,low,high);
		quickSortLomuto(arr, low, p-1);
		quickSortLomuto(arr, p+1, high);
	}
}

quickSortLomuto(arr,0,arr.length-1);

3、Hoare 分区方案

function partitionHoare(arr, low, high){
	var pivot = arr[low];
	var i = low-1;
	var j = high+1;
	while(true){
		do{
			j--;
		}while(arr[j] > pivot);

		do{
			i++;
		}while(arr[i] < pivot);

		if(i<j){
			swap(arr,i,j);
		}
		else{
			return j;
		}
	}
}
function quickSortHoare(arr,low,high){
	if(low < high){
		var p = partitionHoare(arr,low,high);
		quickSortLomuto(arr, low, p);
		quickSortLomuto(arr, p+1, high);
	}
}

分析:

快速排序的性能,与基准的选择以及具体的数组有关

如果选择了好的基准,平均的时间复杂度在 O(n log n)

如果选择了糟糕的基准,时间复杂度会降至 O(n^2)

平均的时间复杂度也是 O(n log n)

快速排序算法不是一个稳定的算法

证明:

1、最优情况

在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为 [log2n]+1( [x] 表示不大于 x 的最大整数),即仅需递归 log2n 次,需要时间为T(n)的话,第一次Partiation应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。于是不断地划分下去,就有了下面的不等式推断:

在最优的情况下,快速排序算法的时间复杂度为O(nlogn)。

2、最坏情况

然后再来看最坏情况下的快排,当待排序的序列为正序或逆序排列时,且每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为

最终其时间复杂度为O(n^2)。

3、一般情况

最后来看一下一般情况,平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n),那么:

算法导论中的证明:

参考资料:

https://www.cnblogs.com/fengty90/p/3768827.html

发布了57 篇原创文章 · 获赞 12 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_40938301/article/details/88365408