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開始噢。

相關文章
相關標籤/搜索