目录
算法思想
通过快速排序将要排序的数据分为独立的两部分,其中一部分的数据比另外一部分的数据都要小,然后再按照此方法对这两部分数据分别再进行快速排序,整个过程可以递归进行,以此达到整个序列都有序。快速排序和归并排序是互补的:归并排序将序列分成两个子序列分别排序,并将有序的子序列归并以将整个序列排序;而快速排序将序列排序的方式则是当两个子序列都有序时整个序列也就自然有序了。前者递归调用发生在处理整个序列之前;后者的递归调用则发生在处理整个序列之后。在归并排序中,一个序列被等分为两半;在快速排序中,切分的位置取决于序列的位置。
实现步骤
首先选取任意元素作为基准元素(一般是第一个元素),将所有比基准元素小的元素都排到其前面,比基准元素大的都排到其后面,这样就完成了一趟快速排序。然后从基准元素分出的两个子序列中再分别选出基准元素执行上述操作,直到不可再切分。
源码
C++实现
递归版本
void QuickSort (vector<int> &vec, int first, int last)
{
if (first < last)
{
int i = first, j = last, key = a[first];
while (i != j)
{
while (j > i && a[j] >= key)
{
--j;
}
a[i] = a[j];
while (i < j && a[i] <= key)
{
++i;
}
a[j] = a[i];
}
a[i] = key;
QuickSort(vec, first, i-1);
QuickSort(vec, i+1, last);
}
}
C++非递归版本
int patition(vector<int> &vec, int left, int right)
{
int key = vec[left], i = left, j = right;
while (i != j)
{
while (i < j && a[j] >= key)
{
--j;
}
a[i] = a[j];
while (i < j && a[i] <= key)
{
++i;
}
a[j] = a[i];
}
a[i] = key;
return i;
}
void QuickSort(vector<int> &vec)
{
if (vec.empty())
{
return ;
}
stack<int> st;
st.push(0);
st.push(static_cast<int>(st.size()-1));
while (!st.empty())
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
if (left < right)
{
int boundary = patition(vec, left, right);
st.push(left);
st.push(boundary-1);
st.push(boundary+1);
st.push(right);
}
}
}
Python实现
def QuickSort(sequence, first, last):
i, j = first, last
if first < last:
key = sequence[first]
while i != j:
while j > i and sequence[j] >= key:
j -= 1
sequence[i] = sequence[j]
while i < j and sequence[i] <= key:
i += 1
sequence[j] = sequence[i]
sequence[i] = key
QuickSort(sequence, first, i-1)
QuickSort(sequence, i+1, last)
Go实现
func QuickSort(arr []int, first, last int) {
if first < last {
i, j, key := first, last, arr[first]
for i != j {
for j > i && arr[j] >= key {
j--
}
arr[i] = arr[j]
for i < j && arr[i] < key {
i++
}
arr[j] = arr[i]
}
arr[i] = key
QuickSort(arr, first, i-1)
QuickSort(arr, i+1, last)
}
}
时间复杂度
快速排序比较特殊,它相当依赖数据的随机性。假设对一个已经排序好的序列进行排序,快速排序的时间复杂度将会退化到O(n²),这是最坏情况。而在最好情况下,每次都能将序列二等分,此时快速排序的时间复杂度为线性对数级O(nlog2n)。尽管不能保证每次切分都是等分,但即使这样其平均性能相较于最好情况也相差不大(证明略)。因此保证数据的随机性是维持快速排序时间复杂度的关键,在绝大部分情况下快速排序的运行时间都不会退化到n²级别。
性能特点
快速排序是原地排序的(只需要一个很小的辅助栈),且时间复杂度为线性对数级别,很好的将时间复杂度和空间复杂度都维持在一个较优的层级。另外,快速排序的内循环比大多数排序算法都要简单短小,这意味着快速排序无论是从理论上还是实际中都要更快。然而,快速排序的主要缺点是非常脆弱,在实现时要非常小心才能避免低劣的性能。
针对原始快速排序的缺点,扩展出许多快速排序的改进算法,例如平衡快排、外部快排、三向切分快排、三路基数快排等等(可以另写一篇文章了)。
稳定性
分析算法原理可以知道,快速排序对相同元素不能维持最初的相对顺序,因此快速排序是不稳定的排序。