您的位置:首页 > 其它

careercup-递归和动态规划 9.3

2014-12-07 20:27 253 查看
9.3 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个有序整数数组,元素值给不相同,编写一个方法,在数组A中找出一个魔术索引,若存在的话。

进阶:

如果数组元素有重复值,又该如何处理。?

解法一,选择蛮力法,我们可以直接迭代访问整个数组,找出符号条件的元素。

int magicSlow(int arr[],int n)
{
if(n==0)
return -1;
int i;
for(i=0;i<n;i++)
{
if(arr[i]==i)
return i;
}
return -1;
}


解法二:既然给的数组是有序的,我们理应充分利用这个条件。

你看你会发现这个问题与经典的二分查找问题非常相似。充分运用匹配法,就能找到适当的算法,我们又该怎样运用二分查找法呢?

在二分查找中,要找出元素k,我们会先拿它跟数组中间的元素x比较,确定k位于x的左边还是右边。

以此为基础,是否通过检查中间元素就能确定魔术索引的位置?

通过比较A[mid]<mid,可以确定该魔术索引一定在mid的右边,因此left=mid+1;

如果A[mid]>mid,可以取得该魔术索引一定在mid 的左边,因此right=mid-1;

算法如下:

int magicQuick(int arr[],int n)
{
if(n==0)
return -1;
int mid;
int left=0;
int right=n-1;
while(left<=right)
{
mid=(left+right)/2;
if(mid==arr[mid])
return mid;
else if(mid<arr[mid])
right=mid-1;
else
left=mid+1;
}
return -1;
}


进阶:

如果存在重复元素,前面的算法就会失效。以下面的数组为例:

-10 -5 2 2 2 3 4 7 9 12 13

0 1 2 3 4 5 6 7 8 9 10 11

看到A[mid]<mid时,我们无法判定魔术索引位于数组哪一边。它可能在数组右侧,跟前面一样。或者,也可能在左侧(在本例中的确在左侧)。

它有没有可能在左侧的任意位置呢?不可能。由A[5]=3可知,A[4]不可能是魔术索引。A[4]必须等于4,其索引才能成为魔术索引,但数组是有序的,故A[4]必定小于A[5]。

事实上,看到A[5]=3时按照前面的做法,我们需要递归搜索右半部分。不过,如搜索左半部分,我们可以跳过一些元素,值递归搜索A[0]到A[3]的元素。A[3]是第一个可能成为魔术索引的元素。

综上:我们得到一种搜索模式,先比较midIndex和midValue是否相同。然后,若两者不同,则按如下方式递归搜索左半部分和右半部分。

左半部分:搜索索引从start到min(midIndex-1,midValue)的元素。

右半部分:搜索索引从max(midIndex+1,minValue)到end的元素。

可以看做前面的基础上求左右两边的交集,例如A[mid]<mid,需要搜索mid左边的部分元素,即区间从start到min(midIndex-1,midValue)的元素和mid右边的全部元素。

A[mid]>mid,需要搜索mid左边的全部元素和右边的部分元素,即区间从max(midIndex+1,minValue)到end,然后两者求交集,就得到上面的区间了。

算法如下:

int magicHelper(int arr[],int n,int l,int r)
{
if(l>r||l<0||r>=n)
return -1;
int mid=(l+r)/2;
if(mid==arr[mid])
return mid;
int rightindex=min(mid-1,arr[mid]);
int left=magicHelper(arr,n,l,rightindex);
if(left>=0)
return left;
int leftindex=max(mid+1,arr[mid]);
int right=magicHelper(arr,n,leftindex,r);
return right;
}
//有重复元素
int magicFast(int arr[],int n)
{
if(n==0)
return -1;
return magicHelper(arr,n,0,n-1);
}


完整代码:

#include<iostream>
using namespace std;

int magicSlow(int arr[],int n) { if(n==0) return -1; int i; for(i=0;i<n;i++) { if(arr[i]==i) return i; } return -1; }

int magicQuick(int arr[],int n) { if(n==0) return -1; int mid; int left=0; int right=n-1; while(left<=right) { mid=(left+right)/2; if(mid==arr[mid]) return mid; else if(mid<arr[mid]) right=mid-1; else left=mid+1; } return -1; }

int magicHelper(int arr[],int n,int l,int r) { if(l>r||l<0||r>=n) return -1; int mid=(l+r)/2; if(mid==arr[mid]) return mid; int rightindex=min(mid-1,arr[mid]); int left=magicHelper(arr,n,l,rightindex); if(left>=0) return left; int leftindex=max(mid+1,arr[mid]); int right=magicHelper(arr,n,leftindex,r); return right; } //有重复元素 int magicFast(int arr[],int n) { if(n==0) return -1; return magicHelper(arr,n,0,n-1); }

int main()
{
int arr[10]={-1,0,1,2,3,4,5,6,7,9};
cout<<magicSlow(arr,10)<<endl;
cout<<magicQuick(arr,10)<<endl;
cout<<magicFast(arr,10)<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: