zoukankan      html  css  js  c++  java
  • 部分有序中查找给定值-【百度面试题】循环有序数组的查找问题

    问题:

    有一个循环有序数组A,如{7,8,9,0,1,2,3,4,5,6},不知道其最小值的位置。

    那么如何从这样的数组中寻找一个特定的元素呢?

    (循环有序满足以下条件之一:

    1.数组所有元素相等

    2.数组是个有序数列,这时候分界点就是第一个元素的位置

    3.总能找到一个位置(也是只有唯一一个位置)从那个位置开始,左边是个递增序列,右边也是一个有序序列,且最后一个元素小于或者等于第一个元素。(也就是首尾相连为 有序的)。

    )

     问题分析:

    我们可以把循环有序数组分为左右两部分(以mid = (low+high)/ 2为界),由循环有序数组的特点知,左右两部分必有一部分是有序的,我们可以找出有序的这部分,然后看所查找元素是否在有序部分,若在,则直接对有序部分二分查找,若不在,对无序部分递归调用查找函数

    #include<iostream>
    using namespace std;
    
    int binarySearch(int a[],int low,int high,int key)
    {
        if(low>high)
            return -1;
    
        int mid=low+(high-low)/2;
        if(a[mid]==key)
        {
            return mid;
        }
        else if(a[mid]<key)
        {
            return binarySearch(a,mid+1,high,key);
        }
        else
        {
            return binarySearch(a,low,mid-1,key);
        }
     
    }
    
    int search(int a[],int low,int high,int key)
    {
        if(low>high)
            return -1;
        
        int mid=(low+high)/2;
        if(a[mid]>a[low])//左有序
        {
            if(a[low]<=key && a[mid]>=key)//说明所查找的key在左边,直接二分查找
            {
                return binarySearch(a,low,mid,key);
            }
            else
            {
                return search(a,mid+1,high,key);//  说明key在右边
            }
        }
        else //右边有序
        {
            if(a[mid]<=key && a[high]>=key)
            {
                return binarySearch(a,mid,high,key);
            }
            else
            {
                return search(a,low,mid-1,key);
            }
        }
    }
    
    int main()
    {
        int a[]={7,8,9,0,1,2};
        int n=sizeof(a)/sizeof(a[0]);
        cout<<search(a,0,n-1,6);
    }

    如果不采用递归,则麻烦点,需要把分界和二分查找结合在一起:

    int search2(int a[],int n,int key)
    {
        int low=0,high=n-1,mid;
        
        while(low<=high)
        {
            mid=(low+high)/2;
            if(a[mid]==key)
            {
                return mid;
            }
            if(a[mid]>=a[low])
            {
                if(a[low]<=key && key<a[mid])
                {
                    high=mid-1;
                }
                else
                {
                    low=mid+1;
                }
            }
            else
            {
                if( a[mid]<key && key<=a[high])
                {
                    low=mid+1;
                }
                else
                {
                    high=mid-1;
                }
            }
        }
        return -1;
    }

    类似于二分查找的形式,注意我们把a[mid]==key单独拿了出来,这点很重要。

    该题可以进一步抽象,一个大的抽象是在部分有序中查找给定的值,有许多变种:

    1.第一种 将一个有序数组循环移位K位后,最少要几个数就可以判断原数组的增减性

      解答:将三个数假定为是abc,如果大小关系为[bac,acb,cba]可以知道原数组是递增的.反之为递减

    2.在一个已知循环移位的有序数组上查找原数组的开始位置【根据1中方法判断递增或者递减】

     解答:【假如是递增】采用二分搜索的方法,每次搜索的时候将当前的值和[low]低端的值进行比较判断处于哪一边

             如果s[mid] > s[low]那么说明开始位置在 [mid,high] 区间中

             如果s[mid] < s[low] 那么说明开始位置在[low,mid]区间中

    int findStart(int a[],int i,int j)
    {
        int m=0;
        while(i<=j)
        {
            if(j-i==1)
            {
                return a[i]<a[j]?i:j;
            }
            m=(i+j)/2;
    
            if(a[m]>a[i])
            {
                i=m;
            }
            else
            {
                j=m;
            }
        }
    }

    以上程序默认数组是递增的。返回的是原数组开始位置

    (上面的程序有几点值得注意,当j-i=1时就结束了,返回的i和j的最小值。还有:

    if(a[m]>a[i])
            {
                i=m;
            }
            else
            {
                j=m;
            }
    里i=m;不能是i=m+1;(二分查找我们这么写),因为如果是7,8,9,0,1,2;
    a[mid]=9; 这是需要把9和0比较查出谁是分界点


    3.在一个已知循环移位的有序数组上查找给定的值

     解答:首先通过1将数组的增减性判断好,假设是递增,综合mid 当前查找的值 以及两端的值判断 要查找的值的范围,不断缩小这个范围,即可。

    int find(int* s,int low,int high,int m)
    {
        int mid;
        while (low+1<high)
        {
            mid = low + (high -low)/2;
            if (s[mid] == m)
            {
                return mid;
            }
            if (m > s[mid])
            {
                if (s[mid] > s[low])
                {
                    low = mid;
                }
                else
                    if (m>=s [high])
                    {
                        high = mid;
                    }
                    else
                        low = mid;
            }
            else
            {
                if (s[mid] > s[low])
                {
                    if (m >= s[low])
                    {
                        high = mid;
                    }
                    else
                        low = mid;
                }
                else
                    high = mid;
            }
        }
        if (s[low] == m)
        {
            return low;
        }
        if (s[high] == m)
        {
            return high;
        }
        return -1;

    4.如果一个数组 前一部分是递增后一部分也是递增的,在其中查找给定值

     这个比较纠结

    5. 果一个数组 前一部分是递增后一部分是递减的,在其中查找给定值

     解答:判断数组的后两个元素,测试后一部分是否有递减的部分。没有,直接二分就可以

    如果存在递减的部分,那么先用二分搜出两部分区域,然后再二分搜索。 

    转自:http://www.cnblogs.com/davidluo/articles/1837561.html

     

  • 相关阅读:
    微信小程序 登录
    小程序验证码输入框 连续输入 自动跳到下一个input
    微信小程序支付方法
    判断屏幕是否过大 或国小 进行 缩放达到自适应
    reactnative 启动
    第二届中国云计算应用论坛圆满闭幕 北达软
    什么是EA? 北达软
    第二届中国云计算应用论坛2012.01.08北京大学隆重揭幕 北达软
    北达软主办的企业架构与数据规划培训圆满结束 北达软
    北达软主办的“第九期中国EA沙龙”圆满举行 北达软
  • 原文地址:https://www.cnblogs.com/youxin/p/3349477.html
Copyright © 2011-2022 走看看