您的浏览器过于古老 & 陈旧。为了更好的访问体验, 请 升级你的浏览器
Ready 发布于2013年08月31日 11:55 最近更新于 2019年05月27日 19:43

原创 快速排序入门详解

3183 次浏览 读完需要≈ 11 分钟 排序算法

内容目录

前面我们已经介绍了冒泡排序选择排序插入排序等常见的数据排序算法。现在,我们接着介绍快速排序

快速排序可以说是排序算法中的一个重头戏,由于其排序效率在同为O(N*logN)的几种排序方法中相对较高,因此在编程开发中经常被采用。不仅如此,诸如腾讯、微软、百度等许多互联网公司的笔试面试题中,也经常会涉及到与快速排序相关的算法,还有大大小的程序方面的考试如软考、考研中也常常出现快速排序的身影。

相对冒泡排序、选择排序等算法而言,快速排序的具体算法原理及实现有一定的难度。为了更好地理解快速排序,我们仍然以举例说明的形式来详细描述快速排序的算法原理。在前面的排序算法中,我们以5名运动员的身高排序问题为例进行讲解,为了更好地体现快速排序的特点,这里我们再额外添加3名运动员。实例中的8名运动员及其身高信息详细如下(F、G、H为新增的运动员): A(181)、B(169)、C(187)、D(172)、E(163)、F(191)、G(189)、H(182)

在前面的排序算法中,这些排序都是由教练主导完成了,现在运动员人数增加了,教练也想趁机去休息一下,于是教练叫来了两名助手,让这两名助手以快速排序法的排序方式来实现对8名运动员的身高从左到右、从低到高的排序。

按照快速排序法的算法原理,两名助手分别站在运动员排列的两侧,如下图所示:

快速排序示意图1

首先,助手1从排列中选出一名运动员(一般选择左侧第1位运动员或最中间的运动员),这里选择运动员A(181)。由于排序是从左到右、从低到高,所以助手1需要一个身高比A(181)更小的运动员(选出来的A(181)作为比较的基准,本轮所有的比较,都是与最初选出来的运动员A(181)比较):

快速排序示意图2

下面我们来继续参考快速排序第一轮的详细示意图。

快速排序示意图3

快速排序示意图4

快速排序示意图5

快速排序示意图6

快速排序示意图7

快速排序示意图8

快速排序示意图9

当两名助手在排序寻找的过程中相遇后,就停止当前轮次的比较,并把最初选出来的运动员A(181)放在两名助手相遇的空位上:

快速排序示意图10

在快速排序中,当两名助手相遇时,本轮排序就结束了。此时,我们发现,以两名运动员相遇的位置A(181)作为分割点,排列左边的都是身高比A(181)小的运动员,排列右侧的都是身高比A(181)大的运动员。这个时候,我们再把A(181)左侧和右侧的两个排序分开来看,如果我们继续使用上述两名助手的排序方法分别对两边的排列进行排序,那么经过多次排列后,最后就能够得到我们所需要的排序结果。

上面就是快速排序的整个排序实现过程。快速排序就是利用上述的排序规则,将一个排列分为两个排列,两个排列分为四个排列,直到无排列可分为止,最后就得到了我们所需要的排序结果。

现在,我们依然编程Java代码来实现使用快速排序对上述8名运动员的身高进行排序:

/**
 * 对指定的数组中索引从start到end之间的元素进行快速排序
 * 
 * @param array 指定的数组
 * @param start 需要快速排序的数组索引起点
 * @param end 需要快速排序的数组索引终点
 */
public static final void quickSort(int[] array, int start, int end) {
	// i相当于助手1的位置,j相当于助手2的位置
	int i = start, j = end;
	int pivot = array[i]; // 取第1个元素为基准元素
	int emptyIndex = i; // 表示空位的位置索引,默认为被取出的基准元素的位置
	// 如果需要排序的元素个数不止1个,就进入快速排序(只要i和j不同,就表明至少有2个数组元素需要排序)
	while (i < j) {
		// 助手2开始从右向左一个个地查找小于基准元素的元素
		while (i < j && pivot <= array[j])
			j--;
		if (i < j) {
			// 如果助手2在遇到助手1之前就找到了对应的元素,就将该元素给助手1的"空位",j成了空位
			array[emptyIndex] = array[emptyIndex = j];
		}
		
		// 助手1开始从左向右一个个地查找大于基准元素的元素
		while (i < j && array[i] <= pivot)
			i++;
		if (i < j) {
			// 如果助手1在遇到助手2之前就找到了对应的元素,就将该元素给助手2的"空位",i成了空位
			array[emptyIndex] = array[emptyIndex = i];
		}
	}		
	// 助手1和助手2相遇后会停止循环,将最初取出的基准值给最后的空位
	array[emptyIndex] = pivot;
	
	// =====本轮快速排序完成=====
	
	// 如果分割点i左侧有2个以上的元素,递归调用继续对其进行快速排序
	if (i - start > 1) {
		quickSort(array, 0, i - 1);
	}
	// 如果分割点j右侧有2个以上的元素,递归调用继续对其进行快速排序
	if (end - j > 1) {
		quickSort(array, j + 1, end);
	}
}

//主方法
public static void main(String[] args) {
	// =====使用快速排序法对表示8名运动员身高的数组heights进行从低到高的排序=====
	// A(181)、B(169)、C(187)、D(172)、E(163)、F(191)、G(189)、H(182)
	int[] heights = { 181, 169, 187, 172, 163, 191, 189, 182 };
	// 调用快速排序方法
	quickSort(heights, 0, heights.length - 1);
	// 输出排序后的结果
	for (int height : heights) {
		System.out.println(height);
	}
}

以上Java代码运行结果输出如下:

163
169
172
181
182
187
189
191

注意:由于局部思维差异,上述快速排序的代码实现可能存在多种变体。不过,无论何种形式的变体,快速排序的核心思想并不会发生改变。

  • CodePlayer技术交流群1
  • CodePlayer技术交流群2

0 条评论

撰写评论