快速排序

算法思路

  1. 每次都确定一个元素的最后位置,同时这个位置左边的数都是比它小,右边的数都是比它大
  2. 挖坑思路:
    • 两个指针,一个left,一个right
    • 每次大循环选一个枢轴pivotkey(选最左边的,其实选什么都没关系),相当于挖了一个坑
    • 我们要从右边找到一个比v[pivotkey]小的元素,填上这个坑
    • 填上最初的坑,right的位置又多了一个新的坑,我们要从左边找出一个比v[pivotkey]大的元素,填上新的坑
    • 循环直到left==right,这个位置就是flag最后的位置
    • 再进行递归,分别flag左边的数组和右边的数组进行排序
  3. 非递归和递归实现类似,非递归就是用显式stack代替递归中使用的系统栈,存储每次排序后枢轴左右两边的两个数组的起始位置和结束位置

书面概括

待排序的n个元素中任取一个元素(通常取第一个元素)作为枢轴(或支点),设其关键字为pivotkey。经过一趟排序后,把所有关键字小于pivotkey的元素交换到前面,把所有关键字大于pivotkey的元素交换到后面,结果将待排序记录分成两个子表,最后将枢轴放置在分界处的位置。然后,分别对左、右子表重复上述过程,直至每一子表只有一个元素时,排序完成。
其中,一趟快速排序的具体步骤如下。

  1. 选择待排序表中的第一个元素作为枢轴,把枢轴暂存在r[0]位置上。附设两个指针low和high,初始时分别指向表的下界和上界,(第一趟,low = 1;high = L.length)。
  2. 从表的最右侧位置依次向左搜索,找到第一个关键字小于枢轴关键字pivotkey的元素,将其移到low处。具体操作时:当low<high时,若high所指元素的关键字大于等于pivotkey,则向左移动指针high(执行操作:high--);否则将high所指元素与枢轴元素交换。
  3. 然后再从表的最左侧位置,依次向右搜索找到第一个关键字大于pivotkey的元素和枢轴元素交换。具体操作是:当low<high时,若low所指元素的关键字小于等于pivotkey,则向右移动指针low(执行操作:low++);否则将low所指元素与枢轴元素交换。
  4. 重复步骤2和3,直到low和high相等为止,此时low或high的位置即为枢轴在此趟排序中的最终位置,原表被分为两个子表。

在上述过程中,元素的交换都是与枢轴之间发生的,每次交换都要移动3次元素,可以先将枢轴元素暂存在r[0]位置上,排序过程中只需移动要与枢轴交换的元素,即只做r[low]和r[high]的单向移动,直至一趟排序结束后再将枢轴元素移至正确的位置。


考虑情况

排序过程会有三种情况

  1. 待排序元素大于有序序列中最大的元素;
  2. 待排序元素小于有序序列中的元素,但不小于有序序列中最小的元素;
  3. 待排序元素小于有序序列中最小的元素,但是因为监视哨的存在,2和3的情况归为一类。

算法效率

  • 最好情况:待排序序列为顺序序列
    • 比较次数KCN:n-1;
    • 移动次数RMN:0;
  • 最坏情况:待排序序列为逆序序列
    • 比较次数KCN:2 + 3 + ... + n ≈ n²/2;
    • 移动次数RMN:(2 + 1) + (3 + 1) + ... + (n + 1) ≈ n²/2;

所以时间复杂度为O(nlogn)
总结一句,就是递归树深度是logn,每次递归都要进行一次O(n)的交换,所以是O(nlogn)

空间复杂度为O(1),监视哨。


算法特点

  • 不稳定排序;
  • 链式存储结构也适合;
  • 更适合于初始记录基本有序(正序)的情况,当序列完全无序,尤其是逆序,且元素过多,时间复杂度会大大提高。

算法代码

#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int partition(vector<int>& v, int l, int r) {
	int flag = v[l];
	while (l < r) {
		while (l < r && v[r] >= flag) r--;
		v[l] = v[r];
		while (l < r && v[l] <= flag) l++;
		v[r] = v[l];
	}
	v[l] = flag;
	return l;
}
//递归
void quickSortRecursion(vector<int>& v, int l, int r) {
	if (l >= r) return;
	int pivotkey = partition(v, l, r);
	quickSortRecursion(v, l, pivotkey-1);
	quickSortRecursion(v, pivotkey+1, r);
}
//非递归
void quickSortNonRecursion(vector<int>& v, int l, int r) {
	stack<pair<int, int>> s;
	s.push(pair<int,int>(l, r));
	while (!s.empty()) {
		pair<int, int> p= s.top();
		s.pop();
		int left =p.first, right = p.second;
		int leftRec = p.first, rightRec = p.second;
		if (left >= right) continue;
		int flag = v[left];
		while (left < right) {
			while (left < r && v[right] >= flag) right--;
			v[left] = v[right];
			while (left < right && v[left] <= flag) left++;
			v[right] = v[left];
		}
		v[left] = flag;
		s.push(pair<int, int>(leftRec, left-1));
		s.push(pair<int, int>(left+1, rightRec));
	}
}
int main() {
	vector<int> v = { 49,38,65,97,76,13,27,49 };
	vector<int> s = { 49,38,65,97,76,13,27,49 };
	quickSortRecursion(v, 0, v.size() - 1);
	quickSortNonRecursion(s, 0, s.size()-1);
	for (auto a : v) cout << a << " ";
	cout << endl;
	for (auto a : s) cout << a << " ";
	cout << endl;

	return 0;
}

运行结果


posted @ 2020-10-12 21:56  肥斯大只仔  阅读(139)  评论(0编辑  收藏  举报