在昨天做过“数组中第K个最大元素”的题目时,也就是上一篇博客中,我写到了关于快排的一点思考,今天再次对快排的一些存在问题进行一些优化
首先呢,先写一下最常规朴素的快排代码:
void quick_sort(int *q, int l, int r) {
if (l >= r) return;
int x = q[l], i = l, j = r;
while (i < j) {
while (i < j && q[j] >= x) j--;
q[i] = q[j];
while (i < j && q[i] <= x) i++;
q[j] = q[i];
}
q[i] = x;
quick_sort(q, l, i - 1);
quick_sort(q, i + 1, r);
}
这里的代码与上一篇的均有所不同,但又有一些的微小的差异,相比较而言,这种风格形式的代码,在选取哨兵主元后,移动指针的顺序是固定的,否则出现错误
然后需要提一下,上一篇博客的两种写法:
第一种:
这种类型的写法,与上面是相似的,都是最终指针会相遇到一个位置而不是交错开来,更方便对排序的位置进行获取,同时这种写法也无需在意哨兵主元的选取和指针移动的顺序,但是,该写法在后续优化过程中并不适合
void Quick_Sort(vector<int>& arr, int begin, int end){
if(begin == end) return ;
int tmp = arr[begin];
int i = begin;
int j = end;
while(i != j){
while(arr[j--] <= tmp && j > i)
while(arr[i++] >= tmp && j > i)
if(j > i){
swap(arr[i],arr[j]);
}
}
arr[begin] = arr[i];
arr[i] = tmp;
Quick_Sort(arr, begin, i-1);
Quick_Sort(arr, i+1, end);
}
第二种:
结束条件的状态为指针的交错状态,不易辨别具体的元素的位置,但是可以解决,数组元素基本一致的情况
void Quick_Sort2(int *arr, int begin, int end) {
if (begin >= end) return;
int x = arr[begin], i = begin, j = end; //定义初始化分界点和左右指针
while (i < j) {
while (arr[j] > x) j--;
while (arr[i] < x) i++; //移动左右指针
if (i < j) swap(arr[i], arr[j]);
}
Quick_Sort2(arr, begin, j); //递归处理左右两段
Quick_Sort2(arr, j + 1, end);
}
接下来,关于快排的优化,众所周知,快排在解决两种情况的时候会很乏力,一种是基本有序,另一种是基本一致,基于这两种情况,对快排的代码进行优化改进(可以直接记下来当做模板)
void quicker_sort(int *q, int l, int r) {
if (l >= r) return;
// 将主元选取的位置更改为随机选取,可以有效解决数组元素基本一致的情况
int flag = rand() % (r - l + 1) + l;
swap(q[l], q[flag]);
int x = q[l], i = l, j = r;
while (i < j) {
while (i < j && q[j] > x) j--; // 去掉等号
if (i < j) { // 增加交换频率,加速排序速度
q[i] = q[j];
i++;
}
while (i < j && q[i] < x) i++;// 去掉等号
if (i < j) {
q[j] = q[i];
j--;
}
}
q[i] = x;
quick_sort(q, l, i - 1);
quick_sort(q, i + 1, r);
}
当然,也可以使用数据结构书上的关于快排的基于C语言的代码逻辑改进,但是,由于不太符合我的代码书写习惯,因此,大多数情况下并不喜欢使用
class Solution {
int partition(vector<int>& nums, int l, int r) {
int pivot = nums[r];
int i = l - 1;
for (int j = l; j <= r - 1; ++j) {
if (nums[j] <= pivot) {
i = i + 1;
swap(nums[i], nums[j]);
}
}
swap(nums[i + 1], nums[r]);
return i + 1;
}
int randomized_partition(vector<int>& nums, int l, int r) {
int i = rand() % (r - l + 1) + l; // 随机选一个作为我们的主元
swap(nums[r], nums[i]);
return partition(nums, l, r);
}
void randomized_quicksort(vector<int>& nums, int l, int r) {
if (l < r) {
int pos = randomized_partition(nums, l, r);
randomized_quicksort(nums, l, pos - 1);
randomized_quicksort(nums, pos + 1, r);
}
}
public:
vector<int> sortArray(vector<int>& nums) {
srand((unsigned)time(NULL));
randomized_quicksort(nums, 0, (int)nums.size() - 1);
return nums;
}
};
最后,关于排序算法的相关测试,可以用排序数组这道题测试一下你习惯的排序写法速度上是否合格