排序算法~快速排序
快速排序在平均情况下排序 n n n 个项目需要 O ( n l o g n ) O(nlog n) O(nlogn) 次比较,在最坏情况下则需要 O ( n 2 ) O(n^2) O(n2) 次比较
快速排序通常比其它时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn) 的排序算法要快
分治法Divide and conquer
策略是把一个串行list
分为两个子串行sub-list
快速排序是分治法的一种典型应用
快速排序的步骤简而言之就是选择一个基准值,然后将数列的其它值放到基准值的左右两边
- 从数列中挑选一个元素,称为基准(
pivot
) - 重新排序数列,所有比基准值小的放在基准值左边,所有比基准元素大的放到右边(
相同的数字可以放在容易一边
),这一轮操作后,这个操作成为分区(partition
)操作 - 然后递归的把每一个分区操作得到的子数列进行分区操作
快速排序的基准值的选择没有做特别说明
快速排序的不稳定是指排序后可能会使两个相同的值的位置交换
快速排序的实现
pub fn quick_sort(vector: &mut Vec<i32>) {
quick_sort_inside(vector, 0, vector.len());
}
fn quick_sort_inside(vector: &mut Vec<i32>, left: usize, right: usize) {
// 当需要排序的分区长度小于 2 时就可以结束递归了
if right - left < 2 { return; }
// 对数列的 [left, right) 部分进行排序,并返回第一个大于或者等于基准数所在的位置
let p = partition(vector, left, right);
// 对数列的 [left, p) 部分进行排序
quick_sort_inside(vector, left, p);
// 对数列的 [p, right) 部分进行排序
quick_sort_inside(vector, p, right);
}
fn partition(vector: &mut Vec<i32>, p: usize, l: usize) -> usize {
// 选取基准值(这里可以进行优化,使用随机数或者三数取中等)
let pivot = vector[(l + p) / 2];
// 对数列进行交互时需要使用到的中间变量
let mut temp: i32;
// 比基准值小的数的当前指针
let mut prev = p;
// 比基准值大的数的当前指针
let mut last = l - 1;
loop {
// 将左指针移动到比基准值大的数
while vector[prev] < pivot && prev < last { prev += 1; }
// 将右指针移动到比基准值小的数
while vector[last] > pivot && prev < last { last -= 1; }
// 如果移动后,左指针大于等于右指针了,则表示当前分区已经排序完成了
if prev >= last { break }
// 交换当前的大数与小数
temp = vector[last];
vector[last] = vector[prev];
vector[prev] = temp;
// 使当前的前指针向后移动一位
prev += 1;
}
return prev
}