快排的思想很简单,就是找到一个基准值,然后将原序列分成两部分,左边的部分都小于该基准值,右边的部分大于等于该基准值。但是思路虽简单,实现起来往往由于边界情况导致不容易一把bug-free过,本文简单介绍一种易于实现的代码思路。
废话不多说,先直接上代码:
void swap(int* left, int* right) {
int temp = *left;
*left = *right;
*right = temp;
}
int partition(int arr[], int start, int end) {
int temp = arr[start];
int* left = &arr[start];
int* right = &arr[end];
while(left < right) {
while(*right > temp)
right--;
while(*left < temp)
left++;
if(*left == *right && left<right) {
left++;
}else{
swap(left, right);
}
}
return left - arr;
}
void quick_Sort(int arr[], int start, int end) {
if(start >= end) {
return;
}
int temp_index = partition(arr, start, end);
quick_Sort(arr, start, temp_index - 1);
quick_Sort(arr, temp_index + 1, end);
}
上述代码分为两部分:
- 函数quick_Sort将一段长度的序列进行快排
- 函数partition从该序列中找到基准值的索引,并返回该索引值
思路很简单,就是递归。找到索引值后,递归调用quick_Sort函数将分成的两部分序列再进行快排。实现起来主要问题点集中在partition函数中,下面来仔细分析一下partition函数的实现细节:
- 每次都是选取最左边的那个数作为基准值,然后设置左指针和右指针分别从两端遍历序列
- 当左指针指向的值小于基准值时,左指针++;当右指针指向的值大于基准值时,右指针--。这样,当两指针相遇的时候,说明其指向的值等于基准值,此时指针位置就是基准值的索引
- 在左右指针从两端遍历的过程中,当左指针指向的值大于等于基准值且右指针指向的值小于等于基准值时,此时交换左指针和右指针指向的值。但注意一种情况,就是在左右指针指向的值都等于基准值时,将会陷入死循环,不停地相互交换。所以,为了防止死循环的发生,我们做一个判断,即:
if(*left == *right && left<right) {
left++;
}
这样,就使得left指针的左边的数从小于基准值变成小于等于基准值,不影响最终的结果。值得注意的是,条件判断里的left < right必须要有,如果没有这句话,可能会导致left指针在right指针的右边,从而返回的索引left - arr会出错。
综上,快排的思路及模板代码就完成了,是不是很简单,没有那么多繁琐的边界细节要考虑了。