纯手撸——快速排序
运用递归思想:
- 随机找主元(一般为第一个、中间、最后一个元素)
- 对数组进行分割处理,小的元素放在主元前面,大的放在后面
- 在对小的元素部分再取主元再分割,递归下去(同理大的元素部分)
- 建议:下拉看图~
一张图归纳我的上述思想:
重难点操作:分割 图片参考美文——不要在问我快速排序,写的实在太好了!
1️⃣单向调整
选一个主元,如选最后一个为主元。假设数组arr的范围为[left, right],即起始下标为left,末尾下标为right。源数组如下:
然后可以用一个下标 i 指向 left,即 i = left ;用一个下标 j 也指向left,即j = left
接下来 j 从左向右遍历,遍历的范围为 [left, right-1] ,遍历的过程中,如果遇到比主元小的元素,则把该元素与 i 指向的元素交换,并且 i = i +1
如:当j指向1时,1比4小,此时把i和j指向的元素交换,之后 i++
就这样让j一直向右遍历,直到 j = right
遍历完成之后,把 i 指向的元素与主元进行交换,交换之后,i 左边的元素一定小于主元,而 i 右边的元素一定大于或等于主元。这样,就 i 完成了一次分割了
有了上面的引导,一切真的非常简单了,直接开写吧:
int partition(int a[],int left,int right)
{
int tmp,pivot; //tmp作为临时存储,pivot指向主元
int i,j;
pivot=a[right]; //照着图片写的,主元取最后一个元素
for(j=left;j<right;j++)
{
if(a[j]<pivot)
{
//swap
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
i++; //记得i要移一下哟
}
}
//i指向的元素与主元元素交换
a[right]=a[i];
a[i]=pivot;
return i;
}
void QuickSort(int a[],int left ,int right)
{
int center;
//至少存在两个元素
if(left<right)
{
center=partition(a,left,right);
QuickSort(a,left,center-1); //左区间递归
QuickSort(a,center+1,right); //右区间递归
}
}
2️⃣双向调整
还是用我的第一个元素充当主元吧。哈哈,源数组如下:
然后用令变量i = left + 1,j = right。然后让 i 和 j 从数组的两边向中间扫描
i 向右遍历的过程中,如果遇到大于或等于主元的元素时,则停止移动,j向左遍历的过程中,如果遇到小于或等于主元的元素则停止移动
当i和j都停止移动时,如果这时i < j,则交换 i, j 所指向的元素。此时 i < j,交换8和3
然后继续向中间遍历,直到i >= j
此时i >= j,分割结束。
最后在把主元与 j 指向的元素交换(当然,与i指向的交换也行)
这个时候,j 左边的元素一定小于或等于主元,而右边则大于或等于主元。
到此,分割调整完毕
有了上面的引导,一切又非常简单了,直接开写吧:
int partition(int a[],int left,int right)
{
int pivot=a[left]; //这次把主元定在第一个元素
int i=left+1; //i指向主元旁第一个元素
int j=right;
while(i<j) //从两端向中间扫描,直到i=j为止
{
//从左向右扫描
while(i<j&&a[i]<=pivot) i++;
//从右向左扫描
while(i<j&&a[j]>=pivot) j--;
if(i>=j)
break;
//swap
int tmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
//a[j]或a[i]与主元交换
a[left]=a[j];
a[j]=pivot;
return j;
}
void QuickSort(int a[],int left ,int right)
{
int center;
//至少存在两个元素
if(left<right)
{
center=partition(a,left,right);
QuickSort(a,left,center-1); //左区间递归
QuickSort(a,center+1,right); //右区间递归
}
}
一点心得:对于这种算法,其实如果直接读书看代码或记忆真的还挺有难度,不妨记住实现的步骤图,跟着图自行拟出代码即可。