這兩個都是用到分治的思想很容易搞混。而且即使binarySearch是用到分治也不一定意味着一定要用遞歸去實現,可以通過循環實現。二分查找和快速排序屬於面試筆試的高頻問題有必要總結一下。
由於循環相比遞歸少了很多內存分配和壓棧的操作開銷會少很多,所以binarySearch最好的實現方式是通過循環實現。binarySearch有一個大前提是數組是有序的,接下來沒什麼好解釋的直接代碼:
int binarySearch(int *a,int l,int r,int number)
{
int mid = (l+r)/2;
while(l<=r){
if(a[mid]<number)
l = mid+1;
else if(a[mid]>number)
r = mid-1;
else if(a[mid] == number )
return mid;
mid = (r+l)/2;
}
return -1;
}
快速排序主要思想也是分治,在升序(降序)排序的過程中:
兩個指針分別從數組頭和尾部向中間遍歷,當尾部指針遇到比樞紐小(大)的值,頭部指針遇到比樞紐大(小),交換兩個指針所指向的值的位置。
這裏涉及到兩個比較關鍵容易混淆的問題:
1.樞紐的選取。
2.停止條件的設置。
3.當頭尾指針交叉後,樞紐和哪個指針進行交換。
如圖所示當劃分完畢後,在樞紐左邊的應該是都比樞紐小(大);在樞紐右邊的應該都比樞紐大(小),總之他們相對於樞紐來說是相對有序的。所以最後樞紐所在的位置很關鍵。
樞紐的初始位置選擇有兩種最簡單的方法:選頭部和選尾部。
以下的解釋統一以升序爲例子
樞紐的初始位置選擇有兩種最簡單的方法:選頭部和選尾部。
這時候劃分完畢之後交換的方案也有兩種主要根據樞紐的選擇位置來決定。
(1)如果樞紐選的是頭部,那麼劃分完畢後要和尾指針進行位置交換。因爲要求交換完成後,樞紐左邊的都是小於樞紐的數,而此時在不越界的情況下尾指針所指位置是剛好小於樞紐。
(2)如果樞紐選的是尾部的指針,則劃分完畢後要和頭指針進行交換。因爲此時頭指針所指的位置恰好大於樞紐,因爲在不越界的情況下樞紐元右側的均要大於樞紐元。
停止條件:
停止條件的設置不要在循環判斷體中設置,要在循環體內設置。理論上來講每一次比對的停止條件都是頭指針大於樞紐元素,尾指針指向小於樞紐元素。即使最後一次也不例外,因爲要找分界點方便交換樞紐。
所以代碼:
void myQuickSort(int *a,int left,int right)
{
if(left>=right)
return;
int l = left, r = right;
int pivot = left;//樞紐元選擇的是頭部
for(;;)
{
while(*(a+l) <= *(a+pivot)&& l<right)//防止越界
l++;
while(*(a+r) >= *(a+pivot)&& r>L)//防止越界
r--;
if(l<r)
myswap(a,l,r);
else
break;//如果屬於指針位置交叉的情況則停止
}
if(*(a+pivot)<*(a+r)) //如果此時尾指針指向的元素剛好大於樞紐元素
myswap(a,pivot,r);//樞紐和尾指針進行交換使得樞紐元右側的元素全部大於樞紐元,
myQuickSort(a,left,r-1);
myQuickSort(a,r+1,right);
}
選尾部作樞紐:
void myQuickSort(int *a,int left,int right)
{
if(left>=right)
return;
int l = left, r = right;
int pivot = right;//樞紐元選的尾部
for(;;)
{
while(*(a+l) <= *(a+pivot))
l++;
while(*(a+r) >= *(a+pivot))
r--;
if(l<r)
myswap(a,l,r);
else
break;
}
if(*(a+l) > *(a+pivot))
myswap(a,pivot,l);//和頭指針交換
myQuickSort(a,left,l-1);//不包含pivot
myQuickSort(a,l+1,right);//不包含pivot
}