快速排序过程为
1.选出待排序数列中的第一个数n。
2.把比n小的数放到n的左边,比n大的数放到n的右边。
3.再对左右两个子数列重复1、2。
可以使用挖坑法实现,以下为Python代码:
def QuickSort(lst,left,right):
if left >= right:
return
head = lst[left]
l = left
r = right
while l < r:
while lst[r] >= head and l < r:
r -= 1
lst[l] = lst[r]
while lst[l] <= head and l < r:
l += 1
lst[r] = lst[l]
lst[l] = head
QuickSort(lst,left,l - 1)
QuickSort(lst,l + 1,right)
nums = [5,3,9,7,4,7,1,5]
QuickSort(nums,0,len(nums) - 1)
print(nums)
C++代码如下:
#include <iostream>
using namespace std;
int QuickSort(int a[],int left,int right){
if(left >= right){
return 0;
}
int head = a[left];
int l = left;
int r = right;
while(l < r){
while(l < r && a[r] >= head){
r--;
}
a[l] = a[r];
while(l < r && a[l] <= head){
l++;
}
a[r] = a[l];
}
a[l] = head;
QuickSort(a,left,l - 1);
QuickSort(a,l + 1,right);
}
int main(){
int nums[10] = {8,6,3,0,1,4,7,9,2,5};
QuickSort(nums,0,9);
for(int i = 0;i < 10;i++){
cout<<nums[i];
}
return 0;
}
对代码的理解:
1.判断l或r指向的值与基准值的大小时候,为什么是<=(>=)而不是<(>)?
因为如果判断r处的值和基准值时以小于为判断依据,与基准相同的数就会被交换到l处,而下一步就会判断l与基准值的大小,显然基准值不会大于本身,l处的值又被移动到了r处,从而导致无限循环。
2.为什么判断l、r值与基准值大小时要加一个l与r关系的比较?
如果基准值为数列中最小值,当判断r与基准值大小时,r会永远>=基准值,即每次循环r的值都会-1,如果没有判断l与r大小关系,r最终会出列表的左界。
3.以下代码作用:
if left >= right:
return
提供了递归时的边界条件,如不设置此条件,会无限递归。
4.l与left、r与right的关系
每下一次递归时对数组的修改范围很可能会变小,left与right限定了此次递归对数组的哪一部分生效。由于在一次递归结束开始下一轮递归开始时需要使用到此次递归时使用的上下边界条件,left、right值需保存,可以将其值分别赋给l和r,让l、r代替边界值实现循环。
快速排序的平均时间复杂度为nlogn,最差时间复杂度为n²。
快速排序是不稳定的排序,举一个例子,当l指向的值等于基准值时,l会+1,而本轮递归结束时会把基准值放到l处,与本轮递归开始时的相同大小值的相对位置发生改变。
算法的优化:
1.三数取中法:
当数列本身接近有序时,第一次排序后会将第一个数放到第一位,第二次排序后把第二个数放到第二位,此时时间复杂度为n²。越有序的数组排序时间越长,这与我们选择的基准点的大小有关,如果我们选择的基准点大小适中,就不会出现极端情况,因此我们可以通过选择一个合适的基准点来优化快排算法。
以下Python代码用的方法是选择待排序列中的开头、结尾和中间位置的数字的中位数作为基准点:
def QuickSort_Mid(lst,left,right):
if left >= right:
return
mid = int((left + right) / 2)
if lst[mid] >= lst[left] and lst[mid] <= lst[right] or lst[mid] <= lst[left] and lst[mid] >= lst[right]:
lst[left],lst[mid] = lst[mid],lst[left]
elif lst[right] >= lst[left] and lst[right] <=lst[mid] or lst[right] >= lst[mid] and lst[right] <= lst[left]:
lst[right],lst[left] = lst[left],lst[right]
head = lst[left]
l = left
r = right
while l < r:
while lst[r] >= head and l < r:
r -= 1
lst[l] = lst[r]
while lst[l] <= head and l < r:
l += 1
lst[r] = lst[l]
lst[l] = head
QuickSort_Mid(lst,left,l - 1)
QuickSort_Mid(lst,l + 1,right)
nums = [5,3,9,7,4,7,1,5,10]
QuickSort_Mid(nums,0,len(nums) - 1)
print(nums)
C++代码如下:
#include <iostream>
using namespace std;
int QuickSort_Mid(int a[],int left,int right){
if(left >= right){
return 0;
}
int mid = (left + right) / 2;
if(a[mid] >= a[left] && a[mid] < a[right] || a[mid] >= a[right] && a[mid] <= a[left]){
int temp = a[left];
a[left] = a[mid];
a[mid] = temp;
}
else if(a[right] >= a[left] && a[right] <= a[mid] || a[right] <= a[left] && a[right] >= a[mid]){
int temp = a[right];
a[right] = a[left];
a[left] = temp;
}
int head = a[left];
int l = left;
int r = right;
while(l < r){
while(a[r] >= head && l < r){
r--;
}
a[l] = a[r];
while(a[l] <= head && l < r){
l++;
}
a[r] = a[l];
}
a[l] = head;
QuickSort_Mid(a,left,l - 1);
QuickSort_Mid(a,l + 1,right);
}
int main(){
int nums[10] = {8,6,3,0,1,4,7,9,2,5};
QuickSort_Mid(nums,0,9);
for(int i = 0;i < 10;i++){
cout<<nums[i];
}
return 0;
}
2.插入排序优化法
插入排序的最优时间复杂度为n,当已知数组近似有序时可直接使用插入排序法。
当快排的数组划分为比较小的块时(10~20)个元素的子数组时,子数组的有序性增加,可以使用快排来对划分到一定大小的数据进行排序:
以下Python代码对元素划分为10个以下的子块进行插入排序:
def InsertSort(lst,left,right):
for i in range(left + 1,right + 1):
while i > left and lst[i] < lst[i - 1]:
lst[i],lst[i - 1] = lst[i - 1],lst[i]
i -= 1
def QuickSort_Insert(lst,left,right):
if right - left < 10:
InsertSort(lst,left,right)
else:
head = lst[left]
l = left
r = right
while l < r:
while l < r and lst[r] >= head:
r -= 1
lst[l] = lst[r]
while l < r and lst[l] <= head:
l += 1
lst[r] = lst[l]
lst[l] = head
QuickSort_Insert(lst,left,l - 1)
QuickSort_Insert(lst,l + 1,right)
nums = [5,3,9,7,4,7,1,5,10,5,9,6,3,2,1,4,8,7,9,5,3,1,5]
QuickSort_Insert(nums,0,len(nums) - 1)
print(nums)
C++:
#include <iostream>
using namespace std;
void InsertSort(int a[],int left,int right){
for(int i = left + 1;i <= right;i++){
for(int j = i;j > left;j--){
if(a[j] < a[j - 1]){
int temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
}
}
}
}
void QuickSort_Insert(int a[],int left,int right){
if(right - left < 10){
InsertSort(a,left,right);
return ;
}
int head = a[left];
int l = left;
int r = right;
while(l < r){
while(l < r && a[r] >= head){
r--;
}
a[l] = a[r];
while(l < r && a[l] <= head){
l++;
}
a[r] = a[l];
}
a[l] = head;
QuickSort_Insert(a,left,l - 1);
QuickSort_Insert(a,l + 1,right);
}
int main(){
int nums[11] = {8,6,3,0,1,4,7,9,2,5,9};
QuickSort_Insert(nums,0,10);
for(int i = 0;i < 11;i++){
cout<<nums[i];
}
return 0;
}