1. 程式人生 > >有序(迴圈)陣列查詢元素-二分查詢法

有序(迴圈)陣列查詢元素-二分查詢法

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;
}