快速排序

快速排序

关于快速排序(Quick Sort)

快速排序(Quick Sort)是一种分治算法(Divide-and-Conquer)
分治的思想是将一个大问题分解成一个个小问题,各个击破,合并子问题的解得到大问题的解。
具体快速排序的思路是: 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。参见百科

举栗子

还是栗子有助于思维啊,我们要对一个长度为n(n=7)的数组array 进行排序:

array = [6, 2, 7, 3, 8, 9, 6]

第1级排序(第1级递归)
我们将<=6的值排到6的左边,>=6的数排到6的右边。我们设
leftPos = 0, rightPos = 6, key = array[leftPos] (及key=6 )
以下分析可以参照更下面些的代码

 Step 1.1.1: leftPos = 0, rightPos = 6, 从后往前,寻找比key小的数, 并交换位置
 [3, 2, 7, 6, 8, 9, 6] 各个变量状态leftPos:0, rightPos:3, key:6
 Step 1.1.2: 从前往后,寻找比key大的数, 并交换位置
 [3, 2, 6, 7, 8, 9, 6] 各个变量状态leftPos:2, rightPos:3, key:6

 //[While A]第一次循环结束,继续[While A],第二次循环开始:
 Step 1.2.1: 从后往前,寻找比key小的数, 并交换位置(状态:leftPos:2, rightPos:3, key:6)
 [3, 2, 6, 7, 8, 9, 6] 各个变量状态leftPos:2, rightPos:2, key:6
 Step 1.2.2: 从前往后,寻找比key大的数, 并交换位置
 [3, 2, 6, 7, 8, 9, 6] 各个状态不变, 因为leftPos == rightPos
 //[While A]结束, 因为leftPos == rightPos, 届时leftPos==rightPos,key:6, array leftPos左边的数都 <= key(6), leftPos右边的数都 >= key(6)

 递归调用quickSort, 以便用同样的方式, 分别处理leftPos 左边的数据 & leftPos右边的数据

第一轮递归得到的数组就是

array = [3, 2, 6, 7, 8, 9, 6]

第2级排序(第2级递归)
就是数据规模更小的,解法相同的子问题咯。对array[3] (也就是6)的左右两边分别排序,
及看作一下两个子数组的求解

subArr1 = [3, 2], subArr1 = [7, 8, 9, 6]

分别以array[0] (subArr1[0], 值为3), array[3] (subArr2[0], 值为7)为key得到

array = [2, 3, 6, 6, 7, 9, 8]

嗯,以上大概就是这么个意思。subArr1 和 subArr1 的处理是同一级的,但代码执行的时候会先把subArr1递归处理完,才能轮到subArr2。

Talk is cheap, Show code

下面就是java版本的递归实现了,有注释,附带一些时间复杂度的思考,有问题的话欢迎互相交流咯。

	/**
	 * 快速排序array的某个区间[i,j]
	 * 递归实现, 平均时间复杂度O(nLog(n))
	 * @param array
	 * @param start
	 * @param end
	 * e.g. 举例分析
	 * [6, 2, 7, 3, 8, 9, 6]
	 * 
	 * Step 1.1.1: leftPos = 0, rightPos = 6, 从后往前,寻找比key小的数, 并交换位置
	 * [3, 2, 7, 6, 8, 9, 6] 各个变量状态leftPos:0, rightPos:3, key:6
	 * Step 1.1.2: 从前往后,寻找比key大的数, 并交换位置
	 * [3, 2, 6, 7, 8, 9, 6] 各个变量状态leftPos:2, rightPos:3, key:6
	 * 
	 * //第一次循环结束,继续[While A],第二次循环开始:
	 * Step 1.2.1: 从后往前,寻找比key小的数, 并交换位置(状态:leftPos:2, rightPos:3, key:6)
	 * [3, 2, 6, 7, 8, 9, 6] 各个变量状态leftPos:2, rightPos:2, key:6
	 * Step 1.2.2: 从前往后,寻找比key大的数, 并交换位置
	 * [3, 2, 6, 7, 8, 9, 6] 各个状态不变, 因为leftPos == rightPos
	 * //[While A]结束, 因为leftPos == rightPos, 届时leftPos==rightPos,key:6, array leftPos左边的数都 <= key(6), leftPos右边的数都 >= key(6)
	 * 
	 * 递归调用quickSort, 以便用同样的方式, 分别处理leftPos 左边的数据 & leftPos右边的数据
	 */
	public static void quickSort(int array[], int i, int j) {
		//边界条件
		if (null == array || array.length < 2 || i < 0 || j >= array.length || i>=j) {
			return;
		}
		
		int leftPos = i;
		int rightPos = j;
		int key = array[i];
		while(leftPos < rightPos) {//[While A]
			/** 从后往前,寻找比key小的数. =是必须的,以确保rightPos能往左移 */
			while(array[rightPos] >= key && rightPos > leftPos) {//[While A.1], 比较最多执行m1次(m1 = j - i + 1)
				--rightPos;
			}
			if (rightPos > leftPos) {
				/* 交换位置. 这种跳跃式的交换, 不能保证相同值的顺序不变, 注定快速排序是一种不稳定的排序*/
//				swap(array, rightPos, leftPos);
				array[leftPos] = array[rightPos];// 因为key==array[leftPos]
				array[rightPos] = key;//key==array[rightPos]
			}
			/** 从前往后,寻找比key大的数 */
			while(array[leftPos] <= key && rightPos > leftPos){//[While A.2], 比较最多执行m2次(m2 = j - i + 1)
				//实际上[While A.1] + [While A.2] 判断语句最大总次数为(j - i + 1 + 1)次记为m, 所以记[While A]的时间复杂度为O(m)
				++leftPos;
			}
			if (rightPos > leftPos) {
				/* 交换位置 */
//				swap(array, rightPos, leftPos);
				array[rightPos] = array[leftPos];//因为key==array[rightPos]
				array[leftPos] = key;
			}
		}
		{//这两段代码看做一个代码块, 平均情况下调用log(n)次
			quickSort(array, i, leftPos - 1);
			quickSort(array, leftPos + 1, j);
		}
	}

测试代码:

		int[] array2 = { 12, 12, 5, 25, 6, 8, 17, 9, 3, 22, 2, 65, /*12,*/ 72, 13, 12 };
//		int[] array2 = {6, 2, 7, 3, 8, 9, 6};
		quickSort(array2, 0, array2.length - 1);
		//LogHelper.v(array2);

测试结果

pos 0:2
pos 1:3
pos 2:5
pos 3:6
pos 4:8
pos 5:9
pos 6:12
pos 7:12
pos 8:12
pos 9:13
pos 10:17
pos 11:22
pos 12:25
pos 13:65
pos 14:72

That’s all! 好啦,结束鸟!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值