有序(循环)数组查找元素-二分查找法

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


  

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值