处理下面的算法难题。发布问题陈述和解决方案。问题是,我们是否需要"搜索两半"部分是为了保证安全吗?或者当a[left] == a[mid]
时,我们只能搜索正确的部分而不检查是否a[mid] == a[right]
- 因为当a[left] == a[mid]
时,我认为左边的所有元素都是相同的,并且无法满足搜索条件来查找值。
更详细地说,我的意思是,如果as,
是否可以安全地写下最后一个else if (a[left] == a[mid]) {
return search(a, mid + 1, right, x);
}
问题陈述
给定已旋转未知次数的n个整数的排序数组,编写代码以查找元素 在数组中,您可以假设该数组最初按递增顺序排序
实施例, 输入在{15,16,19,20,25,1,3,4,5,7,10,14}中找到5 输出,8(数组中的索引为5)
代码
public static int search(int a[], int left, int right, int x) {
int mid = (left + right) / 2;
if (x == a[mid]) { // Found element
return mid;
}
if (right < left) {
return -1;
}
/* While there may be an inflection point due to the rotation, either the left or
* right half must be normally ordered. We can look at the normally ordered half
* to make a determination as to which half we should search.
*/
if (a[left] < a[mid]) { // Left is normally ordered.
if (x >= a[left] && x < a[mid]) {
return search(a, left, mid - 1, x);
} else {
return search(a, mid + 1, right, x);
}
} else if (a[mid] < a[left]) { // Right is normally ordered.
if (x > a[mid] && x <= a[right]) {
return search(a, mid + 1, right, x);
} else {
return search(a, left, mid - 1, x);
}
} else if (a[left] == a[mid]) { // Left is either all repeats OR loops around (with the right half being all dups)
if (a[mid] != a[right]) { // If right half is different, search there
return search(a, mid + 1, right, x);
} else { // Else, we have to search both halves
int result = search(a, left, mid - 1, x);
if (result == -1) {
return search(a, mid + 1, right, x);
} else {
return result;
}
}
}
return -1;
}
答案 0 :(得分:2)
就您的问题而言,您的假设是完全正确的,您不必在那里搜索两半。您可以搜索右半部分,因为如果a[mid] != key
,您的元素肯定在右半部分,如果它在数组中那么。
编辑:
上述结论不完整。我现在写一个新的,进一步考虑这个。
首先,如果在任何时间点,您正在搜索两半,那么二进制搜索没有意义,因为搜索的复杂性变为O(n)
。
现在,你就在那里,如果a[mid] == a[left]
,你的钥匙可以处于任何一半。但是,有一点你可以肯定的是,其中一半将使所有元素相等。
eg. Suppose a[12] = [1,1,1,2,2,2,2,2,2,2,2,2]
rotate r=2 times, a`[12] = [2,2,1,1,1,2,2,2,2,2,2,2]
so, a[mid] = a[left] = 2
if key = 1, it is in left half.
now, rotate r=9 times,
a``[12] = [2,2,2,2,2,2,2,2,2,1,1,1]
so, a[mid] = a[left] = 2
if key = 1, it is in right half
因此,您需要搜索此数组的两半以获取密钥,这种情况实际上会使二进制搜索变得多余。
所以,现在,我甚至建议你使用我在下面提到的解决方案。
虽然如果没有限制,我想提出一个解决方案:
这是Binary Search
算法的简单变体。
首先必须在数组中应用二进制搜索,并使用终止条件:
a[mid-1] > a[mid]
满足此条件后,您将获得索引k = mid
,它为您提供了2个排序数组。
第一个数组A[0...k-1]
和第二个数组A[k...n-1]
,假设n
元素。
现在,请进行检查。
if(key > A[0])
Binary Search in A[0...k-1]
else
Binary Search in A[k...n-1]
一个例外,如果数组已轮换n
次,您将无法获得k
的值。二进制搜索整个数组以获取密钥。
EDIT2:通过评论回答您的问题
我想知道我的原始方法,如果[左]&lt; a [mid],x&gt; = a [left]&amp;&amp; x&lt; a [mid],如果只使用搜索(a,left,mid - 1,x)在左侧搜索是安全的,如果数组可以旋转n次?
如果a[left] < a[mid]
,那么我们可以确定a[left...mid]
升序(排序)。因此,如果x >= a[left] && x < a[mid]
,只在左半边搜索就足够了。
如果您还可以澄清[left] == a [mid]和[mid] == a [right]的时间,我们需要搜索两个方向吗?
是。在这种情况下,我们需要搜索两个方向。比方说,例如。您的原始数组是{1,2,2,2,2,2,2,2,2,2}
,它会旋转一次。数组变为{2,1,2,2,2,2,2,2,2,2}
。如果它旋转了8次,则变为{2,2,2,2,2,2,2,2,1,2}
。现在,如果您的搜索关键字是1
,则在开始时,在两种情况下都是a[mid] == a[left] == a[right]
,并且您的密钥位于不同的一半。因此,您需要搜索两半的密钥。
答案 1 :(得分:1)
您需要考虑两件事情。
首先,因为数组被旋转意味着它就像一个循环缓冲区,所以你需要以不同的方式访问数组以避免离开数组边界。
Normal
array[index]
Circular
array[index % size]
其次你需要考虑轮换,第一个有序项不是索引0
,让我们说它是索引f
作为第一个,所以要访问有序索引i
,你使用{ {1}}。
i+f
要找到旋转的有序数组的第一项,我们可以检查排序条件的中断位置。
Normal
array[index]
Circular
array[index % size]
Rotated
array[(index + first) % size]
现在你只需要在排序之前获得第一个索引..
// assuming ascending order a[0] <= a[1]
int find_first_index (int [] arr)
{
for(int i = 0; i < arr.length; i++)
{
if(arr[i] > arr[(i+1) % arr.length])
{
return (i+1) % arr.length;
}
}
return 0;
}
然后使用int f = find_first_index(arr);
而不是[(i+f) % arr.length]
替换排序算法中的所有数组访问。所以你的代码就像这样......
arr[i]