快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),简称快排。
基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
通俗的说,就是先选一个标准值,比标准值小的放在左边,比标准值大的放在右边,再将标准值放在中间(这个位置一定是标准值的最终位置)。然后再分别对左右两边的数据进行同样的操作。
快速排序有三种方法:hover(左右指针法),挖坑法,快慢指针法。虽然有三种方法,但这三种方法的核心思想都是一样的。
hover(左右指针法)
首先说明,这里虽然叫指针,但实际还是通过下标来处理
步骤:
- 选择最后一个元素为基准值
- begin从前面开始走,找到比基准值大的元素
- end从后面开始走,找到比基准值小的元素
- 将这两个元素交换,直到begin == end结束
- 将基准值放在中间位置
- 基准值左右两边的数据重复此过程
int arr[10] = { 5, 2, 6, 8, 1, 9, 0, 3, 7, 4 };
代码如下:
int Partition_01(int arr[], int left, int right)
{
int begin = left;
int end = right;
while (begin < end)
{
while (begin < end && arr[begin] <= arr[right])
{
begin++;
}
while (begin < end && arr[end] >= arr[right])
{
end--;
}
Swap(arr + begin, arr + end);
}
Swap(arr + begin, arr + right);
return begin;
}
void __QuickSort(int arr[], int left, int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_01(arr, left, right);
__QuickSort(arr, left, div - 1);
__QuickSort(arr, div + 1, right);
}
挖坑法
挖坑法和左右指针法思想都差不多
步骤:
- 将最后一个元素(基准值)先保存起来,然后把这个位置当做一个坑
- begin从前面找一个比基准值大的元素a,将a放入上述坑中,a原来的位置就是新坑
- end开始从后面找一个比基准值小的元素b,将b放入上述坑中,b原来的位置就是新坑
- 直到begin == end结束
- 最后将基准值放入中间的坑中
- 基准值左右两边的数据按照同样的方法进行操作
int arr[10] = { 5, 2, 6, 8, 1, 9, 0, 3, 7, 4 };
代码如下:
int Partition_02(int arr[], int left, int right)
{
int begin = left;
int end = right;
int priot = arr[right];
while (begin < end)
{
while (begin < end && arr[begin] <= priot)
{
begin++;
}
arr[end] = arr[begin];
while (begin < end && arr[begin] >= priot)
{
end--;
}
arr[begin] = arr[end];
}
arr[begin] = priot;
return begin;
}
void __QuickSort(int arr[], int left, int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_02(arr, left, right);
__QuickSort(arr, left, div - 1);
__QuickSort(arr, div + 1, right);
}
快慢指针
快慢指针思想:慢指针找一个比基准值大的数,快指针找一个比基准值小的数,将两者交换位置。
步骤:
- 快慢指针都从头开始
- 快指针找到比基准值小的数,将快慢指针交换位置,慢指针走一步
这样就能保证,慢指针要么和快指针指向头一个元素,要么就指向比基准值大的元素 - 最后将基准值放在慢指针的位置
- 对基准值左右两边的数据重复上述过程
int arr[10] = { 5, 2, 6, 8, 1, 9, 0, 3, 7, 4 };
代码如下:
int Partition_03(int arr[], int left, int right)
{
int cur = left;
int div = left;
for (; cur < right; cur++)
{
if (arr[cur] < arr[right])
{
Swap(arr + cur, arr + div);
div++;
}
}
Swap(arr + div, arr + right);
return div;
}
void __QuickSort(int arr[], int left, int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_03(arr, left, right);
__QuickSort(arr, left, div - 1);
__QuickSort(arr, div + 1, right);
}
总结:
上述快排的三种方法都运用了分而治之的思想,也就是将一个大的问题分割成许多个规模相同的小问题来解决。这三种方法都用到了基准值,都是最后将基准值放在中间位置,确定了基准值的位置,然后将基准值两边的数据按照同样的方法进行操作。