排序算法六 快速排序

本文介绍了快速排序的基本思想、实现方式及其特性。快速排序在平均情况下具有O(n*logn)的时间复杂度,是平均性能最优的排序算法之一。为了避免最坏情况,选取枢轴元素时通常采用“三者取中”的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、快速排序基本思想

快速排序时C.R.A.Hoare在1962年提出的一种划分交换排序。采用分治策略(Divide-and-ConquerMethod)
该方法的基本思想是:
(1)先从数列中取出一个数作为基准数
(2)分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边,获得一个index位置,将数组划分为左右两部分(挖坑填数+分治法)
(3)再对左右区间重复第二步,直到各区间只有一个数。(递归实现)
较难理解的地方在于第二步的区间划分,在此采用挖坑填数的方法。
举例分析:
int[] arr = {3,7,5,1,6,2,4};
0123456
3751624
(1)取第一个数作为基准数。初始化为low = 0;high = 6;priv = arr[low] = 3;    解释:由于已经将arr[0]中 的数据保存到priv中,可以看作是在数组索引为0的位置挖坑了
(2)从high开始向左找到第一个比priv小或等于priv的数。当high=5时,小于priv,将arr[5]挖出赋值给arr[low],即arr[low]=arr[high] = 2,此时索引5的位置是坑,需要找元素来进行填充
(3)从low开始向右找到第一个比priv大或等于priv的数。当low=1时,arr[low] = 7>priv,符合条件则挖出arr[1]赋值给arr[high],即arr[high] = arr[low] = 7;此时索引low=1的位置是坑。
此时数组变成了
0123456
2351674
low = 1,high = 5  priv = 3
后面再重复上面的步骤:先从后往前找第一个小于等于基准元素的,再从前向后找第一个大于等于基准元素的。
从high开始向前找,当high=3,符合条件,将arr[3]挖出,填到上一个坑,即arr[low] =arr[1] = arr[high] = 1,此时arr[3]处为坑
从low开始向后找,当low = 2,符合条件,将arr[2]挖出,填到上一个坑即arr[3] = arr[2] = 5
此时high=3.low=2.再开始找会发现low==high,所以退出。
最后将最后一个坑arr[2]填充,用基准元素。arr[2] = priv = 3;
所得数组为:
0123456
2135674
第一轮用arr[low]将整个数组分为两部分,以arr[2]分界,左边的数字都小于arr[2],右边数字都大于arr[2]。下一步即是对左右两个区间重复上述步骤。递归操作。

2、快速排序实现

挖坑填数法具体实现:
(1)low数组左边界,high数组右边界,priv = arr[low]将基准数挖出形成一个坑arr[low]
(2)high由后向前找到第一个比它小的,找到后挖出此数填到前一个坑arr[low]中
(3)low由前向后找到第一个比它大的,找到后挖出此数,填到前一个坑arr[high]中
(4)再重复执行(2)、(3)步,知道low==high,将基准数priv填入到arr[low]中,返回low,low即为左右两部分的分界处。

java代码实现:
                              public int partition(int[] arr,int low,int high)是挖坑填数的具体实现,返回分界点的位置
                              public void qSort(int[] arr,int low,int high)是分治方法的具体实现,采用递归
public class quickSort {
	/**
	 * @param arr  待排序数组
	 * @param low  刚开始表示排序范围的第一个元素。逐渐向右移动,找到第一个比枢纽值val大的
	 * @param high 刚开始表示排序范围的最后一个元素。逐渐向左移动,找到第一个比枢纽值val小的
	 * @return     返回分割点值所在的位置
	 */
	public int partition(int[] arr,int low,int high){
		int val = arr[low];//设置枢纽元素为每个序列的第一个元素
		while(low<high){  //要循环比较
			while(low<high&&arr[high]>=val){
				high--;
			}
			arr[low] = arr[high];
			while(low<high&&arr[low]<=val){
				low++;
			}
			arr[high] = arr[low];
		}
		//low = high,返回枢纽的位置
		arr[low] = val;
		return low;
	}
	
	public void qSort(int[] arr,int low,int high){
		//递归调用
		int pos;
		if(low<high){
			pos= partition(arr,low, high);
			qSort(arr, low, pos-1);
			qSort(arr, pos+1, high);
		}
	}
	
	public static void main(String[] args){
		int[] arr = {3,7,5,1,6,2,4};
		new quickSort().qSort(arr,0,arr.length-1);
		for(int i = 0;i<arr.length;i++){
			System.out.print(arr[i]+" ");
		}
	}
	
   /**
    *快速排序 被认为在所有同数量级(平均复杂度为O(n*logn))的排序方法中,平均性能最好。
    *选取枢纽元素:但是若初始记录已经基本有序,这样每次如果还选择第一个元素作为枢轴元素,则再通过枢轴划分子序列时,便会出现“一边倒”的情况,
    *		   此时快速排序就完全成了冒泡排序,这便是最坏的情况,时间复杂度为O(n*n)。
    *        所以通常枢轴元素的选择一般基于“三者取中”的原则,即比较首元素、末元素、中间元素的值,取三者中中间大小的那个。
    *快速排序的空间复杂度为O(logn)。
    */
}

3、快速排序特性

时间复杂度 O(n*logn),空间复杂度:O(logn) ,稳定性:不稳定

       快速排序 被认为在所有同数量级(平均复杂度为O(n*logn))的排序方法中,平均性能最好。
选取枢纽元素:但是若初始记录已经基本有序,这样每次如果还选择第一个元素作为枢轴元素,则再通过枢轴划分子序列时,便会出现“一边倒”的情况,此时快速排序就完全成了冒泡排序,这便是最坏的情况,时间复杂度为O(n*n)。所以通常枢轴元素的选择一般基于“三者取中”的原则,即比较首元素、末元素、中间元素的值,取三者中中间大小的那个。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值