在作一道劍指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開始噢。