• 待字闺中之Magic Index 分析


    给定一个数组A,当中有一个位置被称为Magic Index,含义是:如果i是Magic Index。则A[i] = i。

    如果A中的元素递增有序、且不反复,请给出方法,找到这个Magic Index。更进一步。当A中同意有反复的元素,该怎么办呢?

    没有反复元素的情况

    一些同学在遇到这个题目的时候,往往会认为比較简单。扫描一遍,不就ok了么?O(n)的。非常easy呀。但是,大家要注意到,另一个条件没实用:A中的元素是有序递增的。

    这个条件,并非放在这里迷惑大家的。而是有更大的作用的。

    这个时候,该怎样想呢?O(n)不是最好的方法,更好的是什么呢?怎么利用数组有序呢?在有序的数组中查找一个满足特定元素的条件。我们一般会想到二分查找。 我们来回想一下二分查找,对于要查找的目标t,我们首先与数组中间的元素比較,假设t大于中间的元素,则在右半部分继续查找;假设t小于中间的元素,则在左半部分,继续查找。

    那么。我们的题目可以利用上述的思想呢?我们来看一个详细的样例:

    0    1    2    3    4    5    6
    -10    -5    1    2    4    10    12

    mid=3。A[mid] = 2,即A[mid] < mid。接下来,我们应该在哪一边查找呢?我们知道数组的元素是递增有序,且不反复的。也就是说,在A[mid]左边的元素,比A[mid]都要小。没有反复,意味着什么呢?每向左移动一位,至少减1。所以,在mid左边,不可能有一个i,A[i]=i的。假设有。依据前面的分析。我们知道A[mid] - A[i] >= mid - i, 假设A[i] = i。则,A[mid] >= mid, 这与事实A[mid] < mid相悖。所以,接下来,仅仅能在右边进行查找。

    代码与二分查找也非常像。

    详细代码例如以下:

    int MagicIndex(vector<int>& data)
    {
    	int begin = 0,end = data.size()-1;
    	while(begin <= end)
    	{
    		int mid = begin + ((end - begin) >> 1);
    		if(data[mid] > mid)end = mid -1;
    		else if(data[mid] < mid)begin = mid + 1;
    		else return mid;
    	}
    	return -1;
    }

    有反复元素的情况

    假设数组A中,有反复元素,是什么情况呢?经过前面的分析。我们知道,是否有反复的主要区别在,数组的元素从右到左进行递减,每次不一定至少是1了。有可能是0了。

    让我们直观的看一下影响吧。

    0    1    2    3    4    5    6
    -10    2    2    2    9    10    12

    看上面的数组,相同A[mid] < mid。我们应该继续查右边么?显然,右边并不存在Magic Index。查找右边,就会找不到这种Magic Index。

    此时,应该怎样处理呢?我们无法确定,Magic Index是在左边,还是在右边了。那就两边都递归进行处理吧。

    在这里另一个小技巧,我们就是要分别递归处理[0, mid - 1]和[mid + 1, end](end是数组长度-1)么?我们看一个详细的样例:

    0      1    2    3    4    5     6    7     8
    -10    2    2    2    2    10    12   15   20

    这个样例,当我们进行左半部分递归处理的时候。须要考虑的范围是[0, 3]。可实际上。我们仅仅须要考虑[0, 2]。原因是,数组元素在mid=4的左边的值都要小于或者等于A[mid]=2,所以最大的一个有可能是Magic Index的。就是index为A[mid]的情况。所以,这时右边的边界应该是min(mid - 1, A[mid])。

    那么,右边的情况呢?例如以下样例:

    0      1    2    3    4    5     6    7    8  
    -10    2    2    2    9    10    12  15   20

    此时,要在右半部分进行查找。范围通常是[5, 8]。可是,因为数组有序,后面的值,一定是大于等于A[mid]=9的。

    所以。有可能是Magic Index的最小Index是9,也就是说右边的递归。应该是从索引为9的位置開始。此例,就意味着,无需处理右边了。

    详细代码例如以下:

    int MagicIndexDup(vector<int>& data,int begin,int end)
    {
    if(begin > end)return -1;
    int mid = begin + ((end - begin) >> 1);
    if(data[mid] == mid)return mid;
    int res = MagicIndexDup(data,begin,min(mid-1,data[mid]));//递归左边
    if( res == -1)res = MagicIndexDup(data,max(mid+1,data[mid]),end);//递归右边
    return res;
    }
    int MagicIndexDup(vector<int>& data)
    {
    return MagicIndexDup(data,0,data.size()-1);
    }

  • 相关阅读:
    mac os 基本命令
    一个程序员的郁闷吐槽
    域名那些事儿
    EventEmitter事件派发器
    Array类型的操作方法
    居中与垂直居中
    Web Storage —— 登录时记住密码
    字符串字符统计
    颜色字符串转换(正则)
    将字符串转换为驼峰格式
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7068728.html
Copyright © 2020-2023  润新知