快排原理
每一趟快速排序,都是给基准值找其正确的位置(即寻找基准值最终的位置)
该基准值的位置将一个无序的数组划分成两部分,前一部分均小于该基准值,后一部分均大于该基准值
这就是一趟快速排序。
接下来我用两种方法求解快速排序,其实它们的基本思想一样,只是实现方式不同而已
快排1的基本思想
1. 在数组中选一个基准值(通常为数组第一个);
2.分区过程,将比基准值大的数全放到它的右边,小于或等于它的数全放到它的左边;
3.对基准值左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
快排的图解过程1
给出一个无序的数组 23,46,0,8,11,18,将该无序数组排成从小到大的数组
如下图所示,假设最开始的基准值为数组第一个元素23,则首先用一个临时变量去存储基准值即tmp=23;然后分别从数组的两端扫描数组,设两个标志指针:low指向起始位置,high指向末尾.
①首先从右半部分开始扫描(切记不能从左半部分开始扫描,否则会出错,因为它是将左边第一个值作为基准值的,相反如果是将最右边的值作为基准值,要从左半部分扫描),如果扫描到的值大于基准值就让high减1,如果发现有元素比该基准值的值小(如上图中18<=tmp),就将high位置的值赋值给low位置 ,结果如下:
②然后开始从左到右扫描扫描,如果扫描到的值小于基准值就让low加1,如果发现有元素大于基准值(如上图46=>tmp),就再将low位置的值赋值给high位置的值,指针移动并且数据交换后的结果如下:
③然后开始右到左开始扫描,发现右边指针的值大于基准值,hign指针向左移动一位
④然后发现上图11<=tmp,则将high位置的值赋值给low位置的值,结果如下:
⑤然后再开始从左往右遍历,发现其余值均小于等于基准值,直到low=high结束循环,此时low或high的下标就是基准值在该数组中的正确位置.如下图所示.
交换arr[low]与基准值23,结果如下图
这样一趟快速排序结束了,基准值23放在了最终(正确)的位置了,返回的位置是序列i是4,此时该基准值将无序数组分成了两部分,前一部分均小于等于基准值,后一部分均大于基准值
以后重复采用递归分治的方式分别对前半部分【0~i-1】和后半部分【i+1~5】进行排序,当前半部分【0~i-1】和后半部分【i+1~5】均有序时整个数组就自然有序了。
源码实现(python)
def swap(arr,a,b):
temp=arr[b]
arr[b]=arr[a]
arr[a]=temp
#合并partion函数,这里rear相当于解析中的high,front相当于low
arr=[1,3,4,7,8,6,9,5,2,10]
left=0
right=len(arr)-1
def main(arr,left,right):
if(right>=left):
front=left
rear=right
temp=arr[front]
while(rear>front):
while(rear>front) and(arr[rear]>temp):
rear-=1
#swap(arr,front,rear) #当前的右指针值小于基准值时,与左指针值进行交换
arr[front]=arr[rear] #也可以覆盖,反正最后需要填值的
while(rear>front) and(temp>=arr[front]):
front+=1
#swap(arr,front,rear)#当前的左指针值大于基准值时,与右指针值进行交换
arr[rear]=arr[front] #也可以覆盖,反正最后需要填值的
arr[front]=temp #将基准值置于最终的位置,一趟快排结束
t=front
main(arr,left,t-1) #左分治
main(arr,t+1,right) #右分治
main(arr,left,right)
print(arr)
c语言源码:
#include<stdio.h>
int p;
int t;
void swap(int a[],int m,int n){
int temp;
temp=a[m];
a[m]=a[n];
a[n]=temp;
}
int partion(int a[],int left,int right){
int k=left;
int s=a[k]; //将中间的这个数和第一个数交换
while(left<right){
while(left<right&&a[right]>s){ // 从右向左找第一个小于x的数
right--;
}
swap(a,right,left);
while(left<right&&a[left]<=s){ // 从左向右找第一个大于等于x的数
left++;
}
swap(a,left,right);
}
a[left]=s; //一趟划分结束,left为轴心
return left;
}
void quicksort(int a[],int left,int right){
if(right>=left){
p=partion(a,left,right);
quicksort(a,left,p-1); //轴心的左递归
quicksort(a,p+1,right); //轴心的右递归
}
}
int main(){
int a[]={1,2,5,7,4,3,10,6,9,8};
t=9;
quicksort(a,0,t);
for(int i=0;i<=t;i++){
printf("%d " ,a[i]);
}
return 0;
}
快排2的基本思想
1. 在数组中选一个基准值(通常为数组最右边的一个);
2.指针i从左到右扫描过程:
①若指针i指向的值比基准值大,则首先将指针i的值与rear指针的前一个位置(rear-1)的值互换,然后rear指针向左移动一位。即rear-1
②若指针i指向的值比基准值小,则首先将指针i的值与front指针的值互换,然后front指针向右移动一位,指针i也向右移动一位
③若指针i指向的值等于基准值,则只有指针i向右移动一位
3.对基准值左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
快排的图解过程2
给出一个无序的数组 1,3,2,4,6,5,将该无序数组排成从小到大的数组
如下图所示,假设最开始的基准值为数组最后一个个元素5,则首先用一个临时变量去存储基准值即tmp=5;然后设三个标志指针: front指向起始位置,rear指向末尾. 当前移动指针i指向移动位置
①当前移动指针i从左到右扫描,判断当前移动指针的值是大于,小于还是等于基准值,从图中可以看出,当前移动指针的值是小于基准值,所以互相交换arr[i]与arr[front], i指针和front指针都向右移动一步,如下图
②判断当前移动指针的值是大于还是小于基准值,从图中可以看出,当前移动指针的值是小于基准值,所以互相交换arr[i]与arr[front], i指针和front指针都向右移动一步(即i+1,front+1)
③重复上面的步骤,直到当前移动指针指向6时,这时6>temp,所以arr[i]与arr[rear-1]互相交换,如下图
交换arr[i]与arr[rear-1], 这里为什么不与arr[rear]交换呢?因为最右边这个位置是用来存储基准值的,最后会把基准值与最后大于基准值的数进行交换,以保证前一部分均小于基准值,后一部分均大于基准值。
此时右指针rear向左移动一位(即rear-1),如下图
③这时移动指针i==rear,移动指针i停止扫描,将最右边基准位置的值与当前移动指针i的值进行交换,如下图
一趟快速排序结束,该基准值temp=5,返回的位置是序列i是4,这样基准值放在了最终(正确)的位置了,将无序数组分成了两部分,前一部分均小于5,后一部分均大于5
以后重复采用递归分治的方式分别对前半部分【0,i-1】和后半部分【i+1,5】进行排序,当前半部分和后半部分均有序时整个数组就自然有序了。
ps: 其实这种快排的实现方式可以计算每个基准值相等的数据有多少个,只需要当每趟快排结束后,分别计算i-front+1的值就可以得出该数组中所有数据的相等个数值
源码实现(python)
def swap(arr,a,b):
temp=arr[b]
arr[b]=arr[a]
arr[a]=temp
def main(arr,left,right):
global j
if(right>=left):
front=left
i=left #移动指针
rear=right
temp=arr[right] #注意移动指针从左便利时,以最右边作为基值
while(rear>i): #当移动指针小于右边界时
if(arr[i]>temp): #当前指针的值大于基值时,
swap(arr,i,rear-1) #将当前指针的值与右边界指针的前一位值进行交换
rear-=1 #右边界指针向右移动一位
elif(arr[i]<temp): #当前指针的值小于基值时,
swap(arr,i,front) #将当前指针的值与左边界指针的值进行交换
front+=1 #左边界指针向右移动一位
i+=1 #当前移动指针也移动一位
else: #当前指针的值等于基值时,
i+=1 #当前指针向右移动一位
swap(arr,i,right) #当i==rear时,将最后的i值与基准值进行交换,一趟快排结束
j+=1
print("第",j,"趟:",arr)
main(arr,left,i-1) #左分治
main(arr,i+1,right) #右分治
arr=[1,3,2,4,6,5]
left=0
right=len(arr)-1
j=0
main(arr,left,right)
print(arr)
c语言源码:
#include<stdio.h>
int p;
int t;
void swap(int a[],int m,int n){
int temp;
temp=a[m];
a[m]=a[n];
a[n]=temp;
}
int partion(int a[],int left,int right){
int k=right; //k作为标准值的下标,为最后一位
int i=left; // i作为移动指针,与标准值进行比较判断
int s=a[k]; //标准值
while(i<right){ //当i小于right时
if(a[i]<s){ //当前移动指针i指向的值小于标准值时
swap(a,i,left); //当前i指向的值与left进行交换,保证小于标准值的在标准值的左边
i++; // i向前移动一位
left++; // 同时left向右移动
}
else if(a[i]>s){ //当前移动指针i指向的值大于标准值时
swap(a,i,right-1); //当前i指向的值与right-1位进行交换,保证大于标准值的在标准值的右边
right--; // right向左边移动一位
}
else{ //当前移动指针i指向的值等于标准值时
i++; //i向前移动一位
}
}
swap(a,i,k); //跳出while循环后i==right,交换最后的i值与标准值,一趟分治结束
return i; //返回标准值的位置
}
void quicksort(int a[],int left,int right){ //递归
if(right>=left){
p=partion(a,left,right); // 将数组分为两部分,以p为轴心
quicksort(a,left,p-1); // 左边递归
quicksort(a,p+1,right); //右边递归
}
}
int main(){
int a[]={1,2,2,7,4,3,10,6,6,9};
t=9;
quicksort(a,0,t);
for(int i=0;i<=t;i++){
printf("%d " ,a[i]);
}
return 0;
}