快速排序:
双指针法:
方法是找到一个数作为基准数,(一般将第一个数作为基准数),将序列中比基准数大的放在基准数的右边,比基准数小的放在基准数的左边。怎样实现呢?既然左右都找,那就从两边进行探测,大的放右边,小的放左边,就反过来找,一个从右边找小的,一个从左边找大的。如 5 1 4 7 6 2 8 3 ,这里用两个探测器,变量 i 和 j ,分别指向序列的最左边和最右边,我们将第一个数 5 作为基准数,让左边 j 出动,找到比 5 小的数 3 停下,然后再让右边 i 出动,找到比 5 大的数 7 就停,现在交换 i 和 j 所指向元素的值 3 和 7,第一次交换实现后,序列中的数是这样子的 5 1 4 3 6 2 8 7 , j 继续向左挪动,找到比 5 小的数 2 ,停,看 i ,i向右走,找到比 5 大的数 6 ,好停住,交换2 ,6,现在数的顺序为 5 1 4 3 2 6 8 7 ,现在 j 指向 6,j再继续往左走时,找到比5 小的数 2 ,此时,i 与 j 都指向 2,两个相遇了,相遇不能交换了,那就将 j 找到比 5 小的数 2 与基准数 5 交换,现在顺序是 2 1 4 3 5 6 8 7 ,现在基准数左边比5 小,右边比 5 大,第一轮交换完毕,左边数是 2 1 4 3 ,右边数是 6 8 7,之后再模拟第一轮交换,分别处理这两边的数。2 1 4 3,首先找基准数2 ,使得 基准数 左边的数小于等于 基准数,基准数右边的数大于等于基准数 ,其次变量 i 和 j 分别指向最左边 2 和 最右边 3,j 先走,找到了 1 ,i 往右走,i 指向 1 处时,和 j 碰撞上了,止步,将j 指向的数1和基准数2交换,此时顺序为 1 2 4 3 ,基准数 2 归位后,处理,基准数左边的 1 和右边的4 3 ,左边的 1 不用排序,右边 4 3 ,还是之前的方法,找出基准数 4 ,i和j 分别指向 4 3 ,j指向3 ,比 4 小,不用再向左走,j先停,看 i,i向右走,与j碰撞,停,将j指向的数3与基准数交换。此时得到1 2 3 4,5 右边的数也是以此类推。最后排完序,1 2 3 4 5 6 7 8 .
总结:
1.找基准数,将 变量 i和 j 分别指向 序列的最左边和最右边
2. 先从最右边 j 开始往左找比基准数小的,找到后 j 停,看 i,i从最左边向右开始找比基准数大的 ,找到后,i止步,交换 i和j指向的数。如果没找到,j 找到大的数,i没找到小的数,i 和 j走到一处了,停住,将j指向的数与基准数交换,基准数归位。一轮交换结束。
3. 基准数左边是比基准数大的数,右边比基准数小的数,之后分别处理这两边的数,重新找基准数,这里用递归的思想,以此类推,当左边变量大于等于右边变量,排序完毕,返回。
这里思考一个问题,为什么刚开始就强调先从右边开始找?
就拿上面的 2 1 4 3 来说,如果先从最左边开始找,i向右走,走走走,找到了比 2 大的数4,停下,之后j 向右走,没找到比 2 小的就和 i碰上了,同时在4 处停下来,4 比 2 小吗,不,4 比 2 大,那按照上面的方法来做,要和基准数交换啦,就变成 4 1 2 3 ,此时 基准数左边的并不是全部比它大的数,所以入场的顺序还是很重要的。就像人生的入场顺序,很多人换一个时间认识,就会有不同的结局,当一个人在你的生命里先出现,心里装的全是他。后来者,再怎么好,也不及之前的千万分之一,心里容下的只有一个人,后者已无法再去将就。
不弃一心念念的只有陈煜
奈何东方炻苦苦追不到不弃,无奈,他来晚了,媳妇已经被莲衣客拐走咯,眉毛扭成一团也不行喽。
快速排序的核心就是将基准数归位。
快速排序是基于“二分”思想,它是跳跃式的,最差时间复杂度与冒泡排序一样,O(n2),它的平均复杂度是O(nlogn).
C++:
#include<iostream>
using namespace std;
int a[101];
void quicksort(int left ,int right)
{
if (left >= right)//left = rigth 时,还排啥嘞
return ;
int i,j,base,temp;
base = a[left];
i = left;
j = right;
while (i<j)
{
while (a[j] >= base && j>i)//先从右向左找小的
{
j--;
}
while (a[i] <= base && i<j)//再从左向右找大的
{
i++;
}
if (i<j)//在两者相遇之前,大小数交换
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
//当两者相遇将基准数归位
a[left] = a[j];
a[j] = base;
quicksort(left, j-1);
quicksort(j+1,right);
}
int main()
{
int n;
cin>>n;
for (int i=0; i<n; i++)
cin>>a[i];
quicksort(0,n-1);
for (int i=0; i<n; i++)
cout<<a[i]<<endl;
return 0;
}
还有一点是贯穿整个算法始终的:左边指针小于右边指针,要时刻控制
填空法:
我们还是以 5 1 4 7 6 2 8 3 为例子,取最左边的数 5 为基准数,i指向最左边的数,j 指向最右边的数,j向左挪,寻找比基准数大的数,找到之后停,将j指向的数3 放到i 指向的地方 ,此时是 5,现在就是 3 1 4 7 6 2 8 3 ,咦,怎么少了一个基准数,别担心,基准数一开始就定义一个变量存下来了,之后 i 开始走,向右挪,找到比基准数大的数 7 之后,停下,将i 指向的数放到j 指向的地方 ,3 1 4 7 6 2 8 7。看过 i之后,再来看 j, j挪挪挪,找到了比基准数小的数 2 ,将它放到 i 指向的地方,就将之前的 7 覆盖掉了,3 1 4 2 6 2 8 7。再回头看i ,i 向右挪,找到比基准数大的 6 ,将 6 放到 j 所指向的2处,现在是 3 1 4 2 6 6 8 7 。看过i 之后,看 j,j向左挪,挪到了 i处,与i 碰撞,将 基准数 5 放在 j 所指的地方,现在是 3 1 4 2 5 6 8 7 。基准数左边的数都比 5 小, 右边的数都比5 大,第一轮结束。
接下来将 5 左边和右边的数分别 像第一轮一样做处理,下面来看左边 比5 小的数 , 3 1 4 2 。第一步,找基准数 3,将基准数用一个变量存起来,将变量 i 和 j 分别指向 最左边 的数 3 和 最右边的数 2。第二步,从最左边开始找比 基准数 小的数 ,现在 j 指向的 最右边的数 2 就是比 3 小的数,找到了将 它填到 i 所指向的地方 3 处,2 1 4 2 。看完 右边 j 再看 左边 的 i, i现在指向 2 ,2 比 3 小,i向右挪,1 比3 小,继续向右挪,挪到了 4 处,4 比 3 大,好停,将现在 i所指的 4 填到 j 所指的地方,现在是 2 1 4 4,j 和 i指的数都为 4。看 j,j向右挪,找比基准数小的数,挪到了 i处 ,没找到,与 i 碰撞了,将 j指向的地方填上 基准数,此时是 2 1 3 4 ,基准数3左边的全部比它小,右边比它大。此轮结束,再分别处理 基准数两边的 2 1 和 4。 2 1 找基准数 2 ,先将基准数 2 村下来,i和 j分别指向 2 1 ,先来看最左边的 j,j指向的 1 比基准数小,将 j所指的数 1 填到 i所指向的地方,此时就是 1 1 。再来看i,i指向的 1 比基准数 2 小,向右挪,没找到 ,挪到了j处,与j碰撞,将j所指的地方填上基准数,就是1 2 了,3左边的数好排完了,之后看右边的数 4 ,就一个数不需要再排了。5 右边的数与5左边的数排法一样,最后就得到了1 2 3 4 5 6 7 8 。
总结:
1.找基准数,将基准数存起来,变量i 和 j 分别指向一个序列最左边和最右边的数
2.先从最右边开始找比基准数小的数,从最右边第一个开始比较,没找到的,j向左挪,找到就停,将j 指向的数填到 i所指的地方。之后回过来看i,从左边找比基准数大的数,没找到i向右挪,找到之后就将 i所指的数填到 j上。再来看j,如果j 没找到比基准数小的数,向左挪,与i相遇了,i和j同时指向一个地方,就将基准数填到j所指向的地方。
3.基准数归位之后,将序列分为两组,一组是基准数左边全是比基准数小的数,一组是基准数右边全都比基准数大的数。分别处理这两边的数,再像开始那样找基准数,利用递归思想,以此类推。
C++:
#include<iostream>
using namespace std;
int a[101];
void quicksort(int left , int right)
{
if (left >= right)
return ;
int base,i,j;
base = a[left];
i = left;
j = right;
while (i<j)
{//要保持i<j ,如果基准数右边没找到比基准数小的就要继续往左挪,
//没有 i<j这个条件,i和j相遇了还会往左挪,就会溢出
// a[j] >= base,没有等于也可以,有等于号时遇到与基准数一样的数就放在了右边
//没有等于号时遇到与基准数一样大的数,就填过去,放在了基准数的左边
while (a[j] >= base && j>i)
{
j--;
}
if (i < j)
{
a[i] = a[j];
}
while (a[i] <= base && j>i)
{
i++;
}
if (i < j)
{
a[j] = a[i];
}
}
a[j] = base;
quicksort (left , j-1);
quicksort (j+1 , right);
}
int main()
{
int n;
cin>>n;
for (int i=0; i<n; i++)
{
cin>>a[i];
}
quicksort (0,n-1);
for (int i=0; i<n; i++)
{
cout<<a[i]<<endl;
}
return 0;
}
如有错误,敬请指出~