快速排序算法采用了分治法以及递归作为解决问题的思想。在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
快速排序算法
算法思路
快速排序算法的思路是,先在arr[s,t]中随意选取一个点作为排序的基准点x,再确定基准点在数组中的下标,一定下标i确定后,该下标i左边的所有元素均小于x,右边的所有元素均大于x.此时采用递归继续对数组[s,i-1]以及[i+1,t]做快速排序,左右区间不再合法即可退出循环。分治的思想就体现在同时对基准点的左右区间再次做快速排序上。
找基准点
首先,姑且认为区间左端的第一个元素就是基准点x,再定义两个下标i与j分别记录区间的原始左端点与右端点,先从右端点开始往左查找,如果arr[j]>=x且i<j,则j--,这样能够确保基准点右边的元素都大于或等于基准点;若遇到arr[j]<x,则将arr[j]放到基准点原来的位置;紧接着下标i往右查找,如果arr[i]<=x且i<j,则i++,这样能够保障基准点左边的元素都小于或等于基准点;若遇到arr[i]>x,则将arr[i]放到上次j的位置;重复上述操作,直到i==j,将基准点放在arr[i]上,即arr[i]=x.
代码实现
#include<iostream>
using namespace std;
#include<algorithm>
//快速查找算法,查找第k小的元素
void quick_sort(int*arr,int l,int r){
//递归退出条件
if(l>=r){
return ;
}
int i = l;
int j = r;
//以区间最左侧的元素最为基准点
int x = arr[l];
//调整基准点
while(i<j){
//找到一个比基准点小的数
while(i<j && arr[j]>=x) j--;
if(i<j){
//将arr[j]放到最左边
arr[i] = arr[j];
}
//找一个比基准点大的数
while(i<j && arr[i]<=x) i++;
if(i<j){
arr[j] = arr[i];
}
}
arr[i] = x;
//调整基准点
//对基准点的左区间排序
quick_sort(arr,l,i-1);
//对基准点的右区间排序
quick_sort(arr,i+1,r);
}
void Myprint(int val){
cout<<val<<" ";
}
int main(){
int arr[12]={10,2,1,3,6,5,4,7,9,8,42,99};
int len = sizeof(arr)/sizeof(int);
quick_sort(arr,0,len-1);
for_each(arr,arr+len,Myprint);
cout<<endl;
return 0;
}
快速定位算法
问题引入
已知定长为len的int数组,需要查出第k小的元素。
算法思路
借鉴快速排序的思路,基准点必定大于或等于其左区间的元素,小于或等于右区间的元素,因此找到一个下标为k-1的基准点等价于找到第k小的元素。我们只需要在原快速排序算法删改一些代码即可获得快速排序算法的代码实现。
代码实现
#include<iostream>
using namespace std;
//快速查找算法,查找第k小的元素
int quick_select(int*arr,int l,int r, int k){
int i = l;
int j = r;
//以区间左端点为基准点
int x = arr[l];
//调整基准点
while(i<j){
//找到一个比基准点小的数
while(i<j && arr[j]>=x) j--;
if(i<j){
//将arr[j]放到最左边
arr[i] = arr[j];
}
//找一个比基准点大的数
while(i<j && arr[i]<=x) i++;
if(i<j){
arr[j] = arr[i];
}
}
arr[i] = x;
//调整基准点
//判断基准点x的下标i是否与k-1相同
if(i==k-1)
return arr[i];
else if(i<k-1)
return quick_select(arr,i+1,r,k);
else
return quick_select(arr,l,i-1,k);
}
int main(){
int arr[12]={10,2,1,3,6,5,4,7,9,8,42,99};
int k = 12;
int len = sizeof(arr)/sizeof(int);
cout<<quick_select(arr,0,len-1,k)<<endl;
//答案无疑是99
return 0;
}
可见,当i<k-1时,说明第k小的元素在基准点的右侧,只需要再查找基准点的右侧区间;当i>k-1时,说明第k小的元素在基准点的左侧,只需要再查找基准点的左侧区间