Java实现快排+小坑+partition的两种思路

在作一道剑指Offer的题的时候,有道题涉及到快排的思路,一开始就很快根据之前的思路写出了代码,但彷佛有些细节不太对劲,本身拿数据试了下果真。而后折腾了下并记录下一些小坑,还有总结下划分方法partition的两种思路。数组

 

partition思路1——交换思路spa

以待排序数组的第一个元素为基准值key,而后两个指针i和j,先从后面开始找(这个是个坑后面会总结)第一个比基准key小的数字,停下来,而后再从前面开始找第一个比基准key大的数字,停下来。指针

而后交换这两个指针的元素。code

 

当两个指针相碰的时候,也就是i>=j了,或者说就i==j了(由于按照代码应该不存在i>j的状况),就再把a[begin]的值和a[i]或者a[j]的值交换就行了。blog

大概代码以下:排序

int key = a[begin];
        int i = begin, j = end;//坑三i的值
        
        while(i < j) {
            //坑2顺序
            //while(i < j && a[i] <= key)i++;//从左边开始找到第一个比key大的数字而后停下来
            while(i < j && a[j] >= key)j--;//从右边开始找到第一个比key小的数字而后停下来
            while(i < j && a[i] <= key)i++;//从左边开始找到第一个比key大的数字而后停下来
            
            swap(a, i, j);
        }
        
        swap(a, begin, i);//最后是i仍是j的位置和begin交换都行,由于最后是i==j

 

 

 

partition思路1——填坑思路递归

想下咱们的swap通常是怎么实现的: it

int temp = a[i];
a[i] = a[j];
a[j] = temp;

一开始把i位置的值存在一个地方,这至关于i位置有个坑了,由于其余数字能够覆盖i位置了。io

填坑思路的写法就是根据这个思想。class

 

同样是以待排序数组的第一个元素为基准,咱们将a[begin]的值存在key的位置,而后begin就有个坑了吧。

而后两个指针,i从begin开始,j从end开始,一开始从后面也就是j从后往前遍历,找到第一个比key小的数字,而后把这个数字覆盖在i的位置上,由于i是从begin开始,而begin位置有个坑是能够填的。

好了j的东西既然写在了i的位置,意味着j的位置也是个坑了,这个时候i就开始往前遍历,找到第一个比key大的值,而后填在j的坑处,一直以此类推。

 

最后,i和j指针相碰后,只要把key的值填到最后i或者说是j的位置就行了。

 

看个大概代码:

int key = a[begin];//begin的位置被存了起来,这个时候能够利用begin的位置存其余值了
        
        int i = begin, j = end;
        
        while(i < j) {
            while(i < j && a[j] >= key) j--;
            //一开始进来i就是begin,原本begin的值已经在key那里至关于begin或者说是i这里有个坑因此能够覆盖
            a[i] = a[j];
            
            while(i < j && a[i] <= key) i++;
            //a[j]的值刚刚已经放在以前的i位置了,至关于j的位置有坑能够覆盖
            a[j] = a[i];
        }
        
        a[i] = key;//最后填i或j都行了,由于最后一次确定是i==j了

 

 

 

关于写快排代码过程当中遇到的小坑

坑1:

这个其实也不是坑拉,就递归流程中对结束条件的理解。一开始我就想着begin==end就结束,若是是begin>end就出错了。

但其实递归过程当中是确定会出现begin>end的状况的,由于partition+1和partition-1这一步。

 

因此正确的递归终结条件应该直接是begin >= end。    (提醒一下归并排序中的终结条件是begin == end)

 

 

坑2:

坑2就是究竟是先从后面往前找仍是先从前面日后面找。

若是是填坑的思路就不会陷入这个坑中,由于你要先填a[i]或者说是a[begin]的坑,那么确定是先从后往前遍历。

 

但若是是交换的思路就emmm我就踩辽。

由于若是你是先从前日后找,那么出去第一个循环的条件必定是找到第一个比key大的数字了。(不多是由于i >=j,由于外层循环确保了i < j才进来);

但这个时候第二个循环可能由于i>=j而出去,那么这个时候,若是指针相碰了,i和j一块儿指向一个比key大的值,而后和a[begin]交换的话,就会出现一个比key大的值出如今key的左边,就错辽。

 

因此应该要先从后往前遍历这样就会找到第一个比key小的值就出去循环,第二个循环即便i=j了出去也不怕,由于这个时候和begin交换是没有问题的。

 

补充:这个while(i < j && a[i] >= key)中的i<j也是少不了的,否则的话有越界的危险

 

 

坑3:

坑3是i是从哪里开始的,这个在填坑思路中也不会出错,由于第一个坑是在begin那里嘛因此确定i是从begin开始……

而后我用交换思路的时候又采坑了,就想着反正是比较begin后面的数字嘛,就直接i从begin+1开始。

 

这样会有什么问题呢?

考虑只有三个数字:11,32,41;这其实已是有序的了,那么i若是从begin+1开始,那么i会直接原地跳出循环,由于32是第一个比key11大的数字嘛;

而后j从右边开始遍历,也会停在32的位置,由于虽然42,32都比key大讲道理应该继续往下遍历的,但咱们条件中还有i < j这一项,因此就停下来了。

而后出去循环,和begin交换——就变成32,11,41的错误答案了。

 

因此 i 要从begin开始噢。

相关文章
相关标签/搜索