算法----Magic Index

时间:2022-01-13 12:41:54

给定一个数组 A,如果 某个下标 i, 满足 A[i] = i, 则 i 称为 Magic Index。

现在假设 A 中的元素是递增有序的、且不重复,找出 Magic Index.

更进一步,当数组中有重复的元素呢??

分析:

首先题目不难。

最简单的当然是 扫描一遍数组,当然这个 O(N)的算法不是最优的。

进一步思考,如今数组是递增的,可否采用 二分搜索,从而加速到 O(lgN)?

   if  a[mid] == mid,  return mid;

   if  a[mid]  <  mid,  search in range[mid+1,  right];  

Why? 左半部分一定不会有 Magic index。 反证法: 假如左半部分存在一个 K 是Magic index, 则 a[K] = K.

        由于没有重复的元素,所以数组元素从左到右递增的时候,每次增加至少是 1。

       从而有 a[mid] > a[K] + mid-K > K + mid - K = mid, 得到矛盾。

   if  a[mid]  > mid,   search in range[left, mid-1];



考虑下面的一个例子:

index:      0    1    2    3    4    5     6

value:    -10   -5   1    2    4   10   12

a[mid] = 2 < mid = 3, 继续在右半部分找即可,。

二、假设存在重复的元素

由于存在重复的元素,所以数组元素从左到右递增的时候,每次增加不一定大于1了, 有可能是 0。

二分搜索不再使用。每次都必须对 左,右两半都进行搜索。

但是这里还是有一个小 trick,

如果 a[mid] < mid, 左边仅需要搜索 (left,     a[mid]), 右边还是搜索 (mid+1, right).

如果 a[mid] > mid, 右边仅需要搜索 (a[mid],  right), 左边还是搜索 (left, mid-1).

拿个例子来说明:

index:      0    1    2    3    4    5     6

value:    -10   1    1    1   6   10   12

mid = 1 < a[mid] = 3

// copyright @ L.J.SHOU Feb.22, 2014
// magic index
#include <iostream>
using namespace std; // if found, return index;
// if not found, return -1;
int MagicIndex(int A[], int left, int right)
{
int index(-1); if(left <= right)
{
int mid = left + ((right - left) >> 1); if(A[mid] == mid) return mid;
else if(A[mid] < mid){
index = MagicIndex(A, left, A[mid]);
if(index != -1) return index; index = MagicIndex(A, mid+1, right);
if(index != -1) return index;
}
else{
index = MagicIndex(A, left, mid-1);
if(index != -1) return index; index = MagicIndex(A, A[mid], right);
if(index != -1) return index;
}
} return -1;
}