关于快速排序中元素调整方法的分析
快速排序的相关资料网上有很多,基本的思路也比较简单:找一个基准值,将元素分成两部分,然后递归继续。
其中一直困扰我的一个点就是将元素分成两部分的问题,大部分文章都只讲方法,或者演示方法,但说实话我有点懵,因为我并没有搞清楚为什么这样那样做就能将元素调整为两部分,只是勉强记住了方法。
最近又碰到了这个问题,在网上查了很多资料,感觉终于搞清楚了,总结如下:
方法一:单向调整,最简单易懂
1 /*单向调整,将元素按基准值分成三部分*/ 2 int pa(int a[],int left,int right) 3 { 4 /*为什么要按基准值分成三部分? 5 如何分成两部分的话,某些情况会出现死循环 6 例如将 2 3 4 5 3 分成两部分,得到的将是 |2 3 4 5 3 ,等于没分 7 下次递归会导致 left与mid值相等,进入死循环*/ 8 9 int key=a[left];//选取基准值 10 int t,i,j; 11 /*核心思路:让i左边的元素必须小于key,或者说让小于key的元素都在i左边 12 此循环将元素分成两部分: 13 a[left+1]~a[i-1]小于key,a[i]~a[right]大于等于key*/ 14 for(i=j=left+1;j<=right;j++) 15 { 16 if(a[j]<key) 17 { 18 t=a[i];a[i]=a[j];a[j]=t; 19 i++; 20 } 21 } 22 /*a[left]等于key,a[i-1]肯定小于key,交换后可以保证: 23 i-1左边的元素小于key,i-1指向的元素等于key,i-1右边的元素大于等于key*/ 24 t=a[i-1];a[i-1]=a[left];a[left]=t; 25 return i-1; 26 }
方法二:双向调整,其实只是对方法一的优化
/*双向调整1:参考单向调整的方法,将元素分成三部分*/ int pa(int a[],int left,int right) { int i,j,t,key; key=a[left]; i=left+1,j=right; while(i<=j) { //核心思路:让i左边的元素必须小于key,或者说让小于key的元素都在i左边 while(a[i]<key&&i<=j)i++;//循环后a[i]>=key while(a[j]>=key&&i<=j)j--;//循环后a[j]<key //为什么要考虑i==j的情况?所有元素都小于key的情况下会出错 if(i<=j) { t=a[i];a[i]=a[j];a[j]=t; i++;j--; } } t=a[left];a[left]=a[i-1];a[i-1]=t; return i-1; }
//sort()函数同上一个方法
方法三:双向调整,最常用的一种方法。
/*双向调整2:将元素分成 [i,right]、(j,i)、[left,j]三部分*/ void sort(int a[],int left,int right) { int i,j,t,key; key=a[left]; i=left,j=right; while(i<=j)//为什么要取等于?为了将元素分成三部分 { //让i左边元素小于key,循环后a[i]大于等于key while(a[i]<key)i++; //让j右边元素大于key,循环后a[j]小于等于key while(a[j]>key)j--; if(i<=j) { /*交换a[i] a[j]的值,i,j继续前进 因为交换,所以 a[i]左边的元素实际上小于等于key a[j]右边的元素实际上大于等于key*/ t=a[i];a[i]=a[j];a[j]=t; i++,j--; } } /*循环结束后j<i [i,right]的元素大于等于key, [left,j]的元素小于等于key (j,i)的元素等于key */ if(i<right)sort(a,i,right); if(left<j)sort(a,left,j); }