循环有序数组的查找笔记

背景:

雷达方位角的初步定位.

方法:

定义

一个循环有序数组(如:3,4,5,6,7,8,9,0,1,2),不知道其最小值的位置,要查找任一数值的位置。要求算法时间复杂度为log2(n)。

对于循环有序数组,一种简单的定义是:

循环有序数组是将一个有序数组切成两段,并交换位置得到引用块内容

另一种比较严格的定义是:

对于一个循环有序数组 a1,a2,,an ,存在一个i,满足1< i < n,使得 a1,a2,,ai ai,ai+1,,an 同为单调不减,或单调不增数组。且 a1,a2,,ai 中的任意一个元素恒大与等于或恒小于等于 ai,ai+1,,an 中的任意一个元素。

性质:

1.将一个循环有序数组一分为二,一定得到一个有序数组和另一个循环有序数组
2.长度不超过2的循环有序数组其实就是有序数组。

解答思路:

第一步:我们要先弄清楚这个循环有序数组的原数组是单调减的还是单调增,如果 a1>an ,那么a一定是增加型的循环有序数组,如果 a1<an ,那么a一定是减少型的循环有序数组。

注意: a1=an 这种情况。

第二步:判断左边一半和右边一半哪一个是有序的。这里以增加型的举例,减少型的同理。如果a[mid] >= a[start],那么左边一定是有序的。因为如果左边是循环有序的,那么最大值点一定出现在左侧,且最大值点左侧的数恒大于最大值点右侧的数。这与a[mid] >= a[start]矛盾。反之同理。

第三步:确定了有序的一侧后,就要判断是不是在这一侧搜索了。这个判断非常简单,只要确定待搜索的数的值是否在有序数列的两个端点值之间即可。

第四步:最后通过循环,就可以类似二分法,找到待搜索的数的位置。

代码

注: a1=an 这种情况没有考虑。

#include <iostream>

using namespace std;

int getIndex(int a[],int seek,int arrayLength){
    int start = 0;
    int end = arrayLength;
    if (a[start] >= a[end]) {//单增
        while (start <= end) {
            int mid = start + (end - start)/2;
            int midValue = a[mid];
            //说明这是一个在增加的循环有序数组
            if (midValue >= a[start]) {
                //左侧单调递增
                if (seek == a[mid]) {
                    return mid;
                } else if (seek < a[mid] && seek >= a[start]){
                    //一定是在左侧查找
                    end = mid - 1;
                }else{
                    //在右侧查找
                    start = mid + 1;
                }
            }
            else{
                //右侧单调递增,同理
                if (seek == a[mid]) {
                    return mid;
                }
                else if (seek > a[mid] && seek <= a[end]){
                    //一定是在右侧查找
                    start = mid + 1;
                }
                else{
                    //在左侧查找
                    end = mid - 1;
                }
            }
        }//  while end
        //没找到元素
        return -1;
    }
    else{
        while (start <= end) {
            int mid = start + (end - start)/2;
            int midValue = a[mid];

            //说明这是一个在减少的循环有序数组
            if (midValue >= a[start]) {
                //右侧单调递减
                if (seek == a[mid]) {
                    return mid;
                }
                else if (seek < a[mid] && seek >= a[end]){
                    //一定是在右侧查找
                    start = mid + 1;
                }
                else{
                    //在右侧查找
                    end = mid - 1;
                }
            }
            else{
                //左侧单调递减,同理
                if (seek == a[mid]) {
                    return mid;
                }
                else if (seek <= a[start] && seek > a[mid]){
                    //一定是在左侧查找
                    end = mid - 1;
                }
                else{
                    //在左侧查找
                    start = mid + 1;
                }
            }
        }
        //没找到元素
        return -1;
    }
}
int main(int argc,char * argv[]) {
//    int a[] = {12,16,18,20,41,100,1,4,6,9};
    int a[] = {9,6,4,1,100,41,20,18,16,12};
    int seek = 20;
    int arrayLength = sizeof(a)/sizeof(a[0]) - 1;

    int index = getIndex(a,seek,arrayLength);
    index == -1 ? cout<<"not found" : cout<<index;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值