图解快速排序
(1)选取基准值,并设置两个指针:left,right
通常我们选取第一个元素为基准值(注意:这并不是一个好的办法)。所以4就是基准值。
left指向序列的最左端位置,right指向序列的最右端。
(2)从右至左开始寻找,移动right指针,直到找到比基准值小的元素。然后将该元素赋值给left指针所指的位置。
(3)然后从左至右开始寻找,移动left指针,直到找到比基准值大的元素。然后将该元素赋值给right指针所指的位置。
(4)不断重复步骤二、三。直到left指针与right指针重合。然后将基准值赋值给left指针所指的位置,此时已经完成第一次排序。基准值左边的数一定比基准值要小,基准值右边的数一定比基准值要大。
(5)以基准值2为界限,将其分成左右两个序列。分别对这左右两个序列进行上述步骤。
二、代码实现
实现快速排序有两种办法:递归形式和非递归形式
(1)递归形式:
#include <iostream>
#include <vector>
using namespace std;
int division(vector<int> &list, int left, int right)
{
// 以最左边的数(left)为基准
int base = list[left];
while (left < right)
{
// 从序列右端开始,向左遍历,直到找到小于base的数
while (left < right && list[right] >= base)
right--;
// 找到了比base小的元素,将这个元素放到最左边的位置
list[left] = list[right];
// 从序列左端开始,向右遍历,直到找到大于base的数
while (left < right && list[left] <= base)
left++;
// 找到了比base大的元素,将这个元素放到最右边的位置
list[right] = list[left];
}
// 最后将base放到left位置。此时,left位置的左侧数值应该都比left小;
// 而left位置的右侧数值应该都比left大。
list[left] = base;
return left;
}
// 快速排序
void DealQuickSort(vector<int> &list, int left, int right)
{
// 左下标一定小于右下标,否则就越界了
if (left < right)
{
// 对数组进行分割,取出下次分割的基准标号
int base = division(list, left, right);
// 对“基准标号“左侧的一组数值进行递归的切割,以至于将这些数值完整的排序
DealQuickSort(list, left, base - 1);
// 对“基准标号“右侧的一组数值进行递归的切割,以至于将这些数值完整的排序
DealQuickSort(list, base + 1, right);
}
}
void QuickSort(vector<int>& list, int length)
{
DealQuickSort(list, 0, length - 1);
}
int main()
{
int arr[] = { 6, 4, 8, 9, 2, 3, 1 };
vector<int> test(arr, arr + sizeof(arr) / sizeof(arr[0]));
cout << "排序前" << endl;
for (int i = 0; i < test.size(); i++)
{
cout << test[i] << " ";
}
cout << endl;
vector<int> result = test;
QuickSort(result, result.size());
cout << "排序后" << endl;
for (int i = 0; i < result.size(); i++)
{
cout << result[i] << " ";
}
cout << endl;
system("pause");
}
上面的代码实际上是一个升序排序,如果想要改为降序排序,那么只需要将步骤二改为寻找比基准值大的数,步骤三改为寻找比基准值小的数即可。
(2)非递归形式
#include <iostream>
#include <vector>
#include<stack>
using namespace std;
int division(vector<int> &list, int left, int right)
{
// 以最左边的数(left)为基准
int base = list[left];
while (left < right)
{
// 从序列右端开始,向左遍历,直到找到小于base的数
while (left < right && list[right] >= base)
right--;
// 找到了比base小的元素,将这个元素放到最左边的位置
list[left] = list[right];
// 从序列左端开始,向右遍历,直到找到大于base的数
while (left < right && list[left] <= base)
left++;
// 找到了比base大的元素,将这个元素放到最右边的位置
list[right] = list[left];
}
// 最后将base放到left位置。此时,left位置的左侧数值应该都比left小;
// 而left位置的右侧数值应该都比left大。
list[left] = base;
return left;
}
//仅仅改变了这个函数
void DealQuickSort(vector<int> &list, int left, int right)
{
stack<int> s;
s.push(left); //将值用栈保存下来
s.push(right);
while (!s.empty())
{
int end = s.top();
s.pop();
int start = s.top();
s.pop();
int base = division(list, start, end);
if (start < base - 1)
{
s.push(start);
s.push(base-1);
}
if (end > base + 1)
{
s.push(base + 1);
s.push(end);
}
}
}
void QuickSort(vector<int>& list, int length)
{
DealQuickSort(list, 0, length - 1);
}
int main()
{
int arr[] = { 6, 4, 8, 9, 2, 3, 1 };
vector<int> test(arr, arr + sizeof(arr) / sizeof(arr[0]));
cout << "排序前" << endl;
for (int i = 0; i < test.size(); i++)
{
cout << test[i] << " ";
}
cout << endl;
vector<int> result = test;
QuickSort(result, result.size());
cout << "排序后" << endl;
for (int i = 0; i < result.size(); i++)
{
cout << result[i] << " ";
}
cout << endl;
system("pause");
}
三、性能分析
(1)时间复杂度
- 最坏的情况下:对一个有序的序列进行排序,比如说将一个降序序列排为升序序列。以第一个数为基准值将其分割为两个子序列,其中一个子序列一定为空,此时的时间复杂度最高,为O(N*N)
- 最好情况下:数据随机分布,以第一个数为基准值分割为两个子序列,此时两个子序列中的元素个数几乎相同,此时时间复杂度最低,为O(N*logN)
- 平均情况下:同样也为O(N*logN)
(2)空间复杂度
在每次分割的时候,需要一个空间存储基准值。而快排中需要进行logN次分割,所以占用空间也为logN