<LeetCode OJ> 34. Search for a Range

题目翻译:

给定一个已经排序的正数数组(已经在提示用二分法了),找到给定的目标值的起始和结束位置。
你的算法的时间复杂度必须为o(log(n))。
如果目标不在数组中找到,返回[- 1,-1 ]。

例如,
给定数组[ 5,7,7,8,8,10 ]与目标值8,
返回[ 3,4 ]。


分析:DONE

1,典型的二分法

算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。 

基本思想:假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,

如果当前位置arr[k]值等于key,则查找成功;

若key小于当前位置值arr[k],则在数列的前半段 中查找;

若key大于当前位置值arr[k],则在数列的后半段中继续查找,直到找到为止。

时间复杂度:O(logn)。


上面的思想就是最最简单的二分法,即从一个排好序的数组之查找一个key值。 如下面的程序:

  1. int search(int *arr, int n, int key)
  2. {
  3.     int left = 0, right = n-1;
  4.     while(left<=right) {//慎重截止条件
  5.         int mid = left + ((right - left) << 1);//防止溢出
  6.         if (arr[mid] == key) //找到了
  7.             return mid; 
  8.         else if(arr[mid] > key) 
  9.             right = mid - 1;//给定值key一定在左边,并且不包括当前这个中间值
  10.         else 
  11.             left = mid + 1;//给定值key一定在右边,并且不包括当前这个中间值
  12.     }
  13.     return -1;
  14. }

这个程序,相信只要是一个合格的程序员应该都会写。 稍微注意一点, 每次移动left和right指针的时候,根据实际的意义在mid的基础上+1或者-1(两个指针总是会移动到一个未被比较过得新位置), 防止出现死循环, 那么问题来了,如果找不到关键值key什么时候截止呢?当left小于right的时候肯定要继续判断,那么问题就在于left==right是否要继续判断?当然要!前面说了,两个指针移动情况可以看出他们所指向的位置总是没有与key比较过,所以必须再进行一次比较,程序也就能够正确的运行。


2,二分法的两种变形

但如果条件稍微变化一下, 还会写吗?其实,二分法真的不那么简单,尤其是二分法的各个变种。
如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标;更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。 这些,虽然只有一点点的变化,实现的时候确实要更加的细心。 下面列出了这些二分检索变种的实现。


a. 找出第一个与key相等的元素
  1. int searchFirstEqual(int *arr, int n, int key)
  2. {
  3.     int left = 0, right = n-1;
  4.     while(left<=right) //相等时必须再判断一次
  5.     {
  6.         int mid = (left+right)/2;
  7.         if(arr[mid] >= key) 
  8.             right = mid - 1;//不相等是肯定跨步移动,相等时呢?有可能是解哦?但是还是移动,通过left==right再判断回来即可
  9.         else if(arr[mid] < key) 
  10.             left = mid + 1;//一定在mid位置的右边,不包括当前mid位置
  11.     }
  12.     if( left < n && arr[left] == key) 
  13.             return left;
  14.     return -1;
  15. }

b. 找出最后一个与key相等的元素
  1. int searchLastEqual(int *arr, int n, int key)
  2. {
  3.     int left = 0, right = n-1;
  4.     while(left<=right) {//相等时进行一次误差判断
  5.         int mid = (left+right)/2;
  6.         if(arr[mid] > key) 
  7.             right = mid - 1;//key一定在mid位置的左边,不包括当前mid位置
  8.         else if(arr[mid] <= key) 
  9.             left = mid + 1; //不相等一定在mid位置的右边,相等时答案有可能是当前mid位置
  10.     }
  11.     if( right>=&& arr[right] == key) 
  12.             return right;
  13.     return -1;
  14. }

那么本题只需要分两步走即可,

1,寻找目标值的起始位置,找不到直接返回结果即可

2,寻找目标值的末尾位置,如果能到这一步一定可以找到末尾位置,大不了和其实位置一样嘛!

class Solution {  
public:  
    vector<int> searchRange(vector<int>& nums, int target) {  
        vector<int>  result(2,-1);  
          
        //一,寻找目标值的起始位置  
        int left = 0, right = nums.size()-1;  
        while(left<=right) //相等时必须再判断一次,暂时理解为误差判断吧  
        {  
            int mid = (left+right)/2;  
            if(nums[mid] >= target)   
                right = mid - 1;//即使mid位置的值与target相等,我们也应该将其往左边移动,但是当前位置有可能是要找的位置  
            else if(nums[mid] < target)   
                left = mid + 1;//一定在mid位置的右边,并且不包括当前mid位置  
        }  
        if( left < nums.size() && nums[left] == target)   
            result[0]=left;  
        else  
            return result;  
        //二,寻找目标值的末尾位置      
        right = nums.size()-1;  
        while(left<=right) {//相等时进行一次误差判断  
            int mid = (left+right)/2;  
            if(nums[mid] > target)   
                right = mid - 1;//key一定在mid位置的左边,并且不包括当前mid位置  
            else if(nums[mid] <= target)   
                left = mid + 1; //不相等一定在mid位置的右边,相等时答案有可能是当前mid位置  
        }  
        result[1]= right;  
        return result;  
          
    }  
}; 






注:本博文为EbowTang原创,后续可能继续更新本文。如果转载,请务必复制本条信息!

原文地址:http://blog.csdn.net/ebowtang/article/details/50772107

原作者博客:http://blog.csdn.net/ebowtang

本博客LeetCode题解索引:http://blog.csdn.net/ebowtang/article/details/50668895


参考资源:

【1】前半部分原作者,liubird,博文地址,http://blog.chinaunix.net/uid-1844931-id-3337784.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值