有序(迴圈)陣列查詢元素-二分查詢法
1.我們將一個有序陣列(n個元素)從i(號位置)之前放到n位置之後形成的陣列為有序迴圈陣列。
2.陣列 1 2 4 5 6 7 8
的有序迴圈陣列有
1 2 4 5 6 7 8 (元陣列是位置0處的有序迴圈陣列).
2 4 5 6 7 8 1
4 5 6 7 8 1 2
5 6 7 8 1 2 4
6 7 8 1 2 4 5
7 8 1 2 4 5 6
8 1 2 4 5 6 7
3.我們要在有序迴圈陣列中找到一個元素出現的位置(此處假設每個元素出現次數小於等於一)。
a.順序差 O(n)
b.(二次)二分查詢O(2logn) ~ O(logn)。
此處先說明一下二分查詢.
若陣列有序那麼我們取首個位置為l,最後位置為r,中間位置為m=(l+r) /2,要找的元素為val
l<=r時
若A [ m] ==val則找到.
若A[m]>val 則在左邊找l=m-1;
若A[m]<val則在右邊找r=m+1;
重複過程直到找到.
有下列.
在0 2 3 4 7 8 10 11 70 100 101 中查詢 7 ,則會在第三步找到.
若查詢5則會在第四步找不到退出。
給出程式碼:
#include <stdio.h> #define SIZE 100 int binary_find(int number[],int l,int r,int val){ int lft = l, rit = r ,mid = lft; while( lft <= rit ){ mid = (rit + lft)/2; if( number[mid] == val){ return mid; }else if(number[mid] > val){ rit = mid - 1; }else{ lft = mid + 1; } } return -1; } int main(){ int number[SIZE] = {5,8,10,11}; int i,count=4,val=11,mid=-1; mid = binary_find(number,0,count-1,,val); if(mid!=-1){ printf("find :%d at pos:%d!\n",number[mid],mid); }else{ printf("nomber %d !\n",val); } getchar(); return 0; }
現在來考慮問題在迴圈有序中的陣列中查詢元素出現的位置。
假設你知道那個邊界點位置為i(i和前面元素有序並且大於每一個i後面的有序序列中的元素元素。
10 11 70 100 101 0 2 3 4 7 8 此處的邊界點i為4(從零計算下標)
那麼可以將0-4挪到11~15位置,在使用二分查詢從(5,15)查詢即可。
那麼就有新的問題如何找到原序列中的邊界點。
1.若果原序列有序,或者只有一個元素直接二分查詢。
2.否則為迴圈多元素的陣列,那麼需要找分界點.
分界點的特點就在於.
i是分界點下表 a[i] > a[i+1]
若a[pos] < a[r] 則分界點在pos左邊(r右邊界下標)
若a[pos] > a[l] 則分界點在pos右邊(l左邊界下標)
相當於換了一次二分查詢的條件來查詢分界點位置.
找到之後就是來對映分界點左邊的元素到分界點右邊元素之後。如圖
若當前位置為pos,原來右下標為r-1 pos >= r 是真實位置是pos - (r - l - pos);
否則就是本身。
給出程式碼:
#include <stdio.h>
#define SIZE 100
int main(){
int number[SIZE] = {5,6,7,8};
int i,count=4,val=6;
int lft = 0, rit = count - 1 ,mid = lft,pos = -1,key_pos;
for(i=0;i < count;i++){
printf("%d ",number[i]);
}
if( number[0] <= number[count - 1] ){ //原本為有序串,直接二分查詢
while( lft <= rit ){
mid = (rit + lft)/2;
if( number[mid] == val){
printf("find :%d at pos:%d\n",number[mid],mid);
break;
}else if(number[mid] > val){
rit = mid - 1;
}else{
lft = mid + 1;
}
}
if(lft>rit)
printf("no meber :%d\n",val);
}else{ //查詢分界點
while( lft <= rit ){
mid = (rit + lft)/2;
if( number[mid] >= number[mid + 1] ){
break;
}else if(number[mid] <= number[count - 1 ]){
rit = mid - 1;
}else{
lft = mid + 1;
}
}
lft = mid + 1,rit = count + mid ,key_pos = mid;
while( lft <= rit ){ //進行查詢元素
mid = (rit + lft)/2;
int pos = mid >= count ? mid -(count - key_pos + 1) : mid;
if( number[pos] == val){
printf("find :%d at pos:%d\n",number[pos],pos);
break;
}else if(number[pos] > val){
rit = mid - 1;
}else{
lft = mid + 1;
}
}
if(lft>rit)
printf("no meber :%d\n",val);
}
return 0;
}