快速排序的解析

快排原理

每一趟快速排序,都是给基准值找其正确的位置(即寻找基准值最终的位置)

该基准值的位置将一个无序的数组划分成两部分,前一部分均小于该基准值,后一部分均大于该基准值

这就是一趟快速排序。

 

接下来我用两种方法求解快速排序,其实它们的基本思想一样,只是实现方式不同而已

 

快排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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值