算法思想
一趟快排:找一个数据,以它的作为“枢轴(pivot)”,凡其小于枢轴的数据均移动至该数据之前,反之,凡大于枢轴的数据均移动至该数据之后。
一趟快排之后,记录的无序序列 arr[s…t] 将分割成两部分:arr[s…i-1] 和 arr[i+1…t] ,且
a
r
r
[
m
]
≤
a
r
r
[
i
]
≤
a
r
r
[
n
]
s
≤
m
≤
i
−
1
,
i
+
1
≤
n
≤
t
,
a
r
r
[
i
]
i
s
t
h
e
p
i
v
o
t
arr[m] \leq arr[i] \leq arr[n] \qquad s \leq m \leq i-1 ,\ i+1 \leq n \leq t ,\ arr[i]\ is \ the \ pivot
arr[m]≤arr[i]≤arr[n]s≤m≤i−1, i+1≤n≤t, arr[i] is the pivot
快速排序:通过一趟快排将待排数据分隔成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法时间复杂度分析
- 最好的情况:每次划分所取的基准都是当前无序区的"中值"数据,划分的结果是基准的左、右两个无序子区间的长度大致相等。时间复杂度:O(nlogn)
- 最坏的情况: 每次划分选取的基准都是当前无序区中最小(或最大)的数据,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中数据数目,仅仅比划分前的无序区中记录个数减少一个。时间复杂度为O(n*n)
算法实现与改进
基础版
int partition1(vector<int>& arr, int low, int high) {
int pivot = arr[low];//定义基准
while (low < high) {
//必须先从右往左扫描,防止最右侧的数据丢失
while (low < high && arr[high] >= pivot) //寻找小于基准的数据的下标
high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot)//寻找大于基准的数据的下标
low++;
arr[high] = arr[low];
}
arr[low] = pivot;//写回
return low;
}
void Qsort1(vector<int>& arr, int low, int high) {
if (low < high ) {//递归出口: low>=high
//对arr[1ow...high]进行一趟快排,并返回枢轴下标
int pivotloc = partition1(arr, low, high);
Qsort1(arr, low, pivotloc - 1);
Qsort1(arr, pivotloc + 1, high);
}
}
随机数改进版
随机生成基准(pivot),但是由于生成随机数的成本也较高,不一定能够优化时间。
int partition2(vector<int>& arr, int low, int high) {
srand(time(nullptr));
int random = rand() % (high - low + 1) + low;
int pivot = arr[random];
swap(arr[random], arr[low]);
while (low < high) {
//必须先从右往左扫描,防止最右侧的数据丢失
while (low < high && arr[high] >= pivot)
high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot)
low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
void Qsort2(vector<int>& arr, int low, int high) {
if (low < high) {//递归出口: low>=high
//对arr[1ow...high]进行一趟快排,并返回枢轴下标
int pivotloc = partition2(arr, low, high);
Qsort2(arr, low, pivotloc - 1);
Qsort2(arr, pivotloc + 1, high);
}
}
近似取中值改进版
基准取arr[low]、arr[high]、arr[mid]的中间值,其中mid=(low+high)/2;
int partition3(vector<int>& arr, int low, int high) {
int pivot, mid = (low + high) / 2;
if ((low -mid) * (mid - high) > 0) {
pivot = arr[mid];
swap(arr[low], arr[mid]);
}
else if ((mid - low) * (low - high) > 0) {
pivot = arr[low];
}
else {
pivot = arr[high];
swap(arr[low], arr[high]);
}
swap(arr[low], arr[mid]);
while (low < high) {
//必须先从右往左扫描,防止最右侧的数据丢失
while (low < high && arr[high] >= pivot)
high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot)
low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
void Qsort3(vector<int>& arr, int low, int high) {
if (low < high) {//递归出口: low>=high
//对arr[1ow...high]进行一趟快排,并返回枢轴下标
int pivotloc = partition3(arr, low, high);
Qsort3(arr, low, pivotloc - 1);
Qsort3(arr, pivotloc + 1, high);
}
}
小区间优化
小区间的排序,选择用 二分查找的插入排序进行优化
void BiInsertionSort(vector<int>& arr,int begin,int end) {//arr[begin...end]
// 作折半插入排序
int low, high, mid,temp;
for (int i = begin + 1; i <= end; i++) {
temp = arr[i];//暂存arr[i]
//在 arr[begin...i-1]中折半查找插入位置(high+1)
low = begin;
high = i - 1;
while (low <= high) {
mid = (low + high) / 2;
if (temp < arr[mid]) //升序"<",降序“>”
high = mid - 1; // 插入点在低半区
else low = mid + 1; // 插入点在高半区
}
for (int j = i - 1; j >= high + 1; j--) {
arr[j + 1] = arr[j];//数据后移
}
arr[high + 1] = temp;
}
}
int partition2(vector<int>& arr, int low, int high) {
srand(time(nullptr));
int random = rand() % (high - low + 1) + low;
int pivot = arr[random];
swap(arr[random], arr[low]);
while (low < high) {
//必须先从右往左扫描,防止最右侧的数据丢失
while (low < high && arr[high] >= pivot)
high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot)
low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
void Qsort2(vector<int>& arr, int low, int high) {//arr[low..high]
if (high - low < 10) {
BiInsertionSort(arr, low, high);
}
if (low < high) {//递归出口: low>=high
//对arr[1ow...high]进行一趟快排,并返回枢轴下标
int pivotloc = partition2(arr, low, high);
Qsort2(arr, low, pivotloc - 1);
Qsort2(arr, pivotloc + 1, high);
}
}