码迷,mamicode.com
首页 > 编程语言 > 详细

排序算法--快速排序

时间:2015-05-04 22:02:28      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:

快速排序之所以特别快,主要是由于非常精炼和高度优化的内部循环。

像归并排序一样,快速排序也是一种分治的递归算法。数组S排序的基本算法由下列简单的四部组成:

1.如果S中元素个数是0或1,则返回

2.取S中任一元素v,称之为pivot(枢纽元,主元,基准)

3.将S-{v}分成两个不想交的集合:S1={x∈S-{v} | x <= v} 和 S2={x∈S-{v} | x > v}

4.返回{QuickSort(S1)后,继随v,继而QuickSort(S2)

 

为了完成上述的步骤3,有两种常用方法,可以分别简称为:一前一后两个指针,一左一右两个指针。这两种方法只是实现上的差异,效率是一样的。

其实主要影响快速排序性能的是pivot的选取。

  1. 我们常用的一种选取方式(选取第一个或最后一个为pivot)是错误的,因为它有很大的可能会影响快速排序的性能。
  2. 一种安全的做法是,随机选取pivot。
  3. 最好的一种方法称为,三数中值分割法(Median-of-Three Partitioning)。就是利用数组的中值作为pivot,显然这很难算出,而且明显减慢排序的速度。所以常用的方法是,选取最左端、最右端、中心位置上的三个元素的中值作为pivot。

 

代码如下:

#include <iostream>
#include <random>
#include <utility>
#include <ctime>
#include <algorithm>
using namespace std;

void QuickSort(int* arr, size_t first, size_t last);
size_t Partition(int* arr, size_t first, size_t last);	        // 一前一后两个指针
size_t Partition_2(int* arr, size_t first, size_t last);	// 一左一右两个指针

int main() {
	const int ArrSize = 10;
	int* arr = new int[ArrSize];
	static default_random_engine e(time(0) % 100);
	static uniform_int_distribution<int> u(0, 100);	// 随机数范围[0, 100]

	for (size_t i = 0; i < ArrSize; ++i) {
		arr[i] = u(e);
	}

	QuickSort(arr, 0, ArrSize - 1);
	cout << boolalpha;
	cout << is_sorted(arr, arr + ArrSize) << endl;

	return 0;
}

void QuickSort(int* arr, size_t first, size_t last) {
	if (first < last) {
		size_t pivotIndex = Partition(arr, first, last);
		QuickSort(arr, first, pivotIndex - 1);
		QuickSort(arr, pivotIndex + 1, last);
	}
}

size_t Partition(int* arr, size_t first, size_t last) {
	int value = arr[first];	// 以第一个元素为主元
	size_t i = first;    // 后指针
	size_t j = first + 1;   // 前指针

	while (j <= last) {
		if (arr[j] <= value) {
			++i;
			swap(arr[i], arr[j]);
		}
		++j;
	}

	swap(arr[first], arr[i]);

	return i;
}

size_t Partition_2(int* arr, size_t first, size_t last) {
	int value = arr[first];
	size_t i = first;	//左指针 // **1
	size_t j = last;    // 右指针

	while (i < j) {
		while (arr[i] <= value) {
			++i;
		}

		while (arr[j] > value) {
			--j;
		}

		if (i < j) {
			swap(arr[i], arr[j]);
		}
	}

	swap(arr[first], arr[j]);  // **2

	return j;
}

 

这里,为了减少复杂度,我选取第一个元素为pivot。

两种划分方式中,一前一后两个指针方式,较好理解,i为后指针,j为前指针。i所指元素以及之前元素都是<=pivot的,j每次+1,然后判断arr[j]和pivot的关系。

一左一右两个指针方式中,我认为有两处难以理解的地方(都已在代码中标注)。

**1,选取当前范围内第一个元素为pivot,为什么还把i(左指针)从first开始?

answer: 快速排序是以递归方式进行的,递归的终止条件时,!(first < last),所以只有当前范围的元素个数>=2时,排序算法才会一直执行下去。

只有两个元素时,这两个元素之间的大小关系只有三种情况。当第一个元素大于第二个元素时,不妨设第一个元素为10,在数组中索引为0,第二个元素为0,在数组中索引为1。

如果说,左指针为first+1(这里为1),则后面的while循环不会被执行,所以步骤3没有得到满足,排序会失败。

 

**2,为什么是{ swap(arr[first], arr[j]); return j; },而不是{ swap(arr[first], arr[i]); return i; }

answer: while 循环的结束条件为!(i < j),循环结束后,i在j之后,i所指的元素为,第一个大于pivot的元素,j所指的元素为,最后一个小于等于pivot的元素。所以执行的是,{ swap(arr[first], arr[j]); return j; }。

 

参考:《数据结构与算法分析--C语言描述》

排序算法--快速排序

标签:

原文地址:http://www.cnblogs.com/wuhanqing/p/4477533.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!