最近我接受了一次采访,他们问我一个“ 搜索 ”问题。
问题是:
假设有一个(正)整数数组,每个元素都是
+1
或-1
与其相邻元素比较。例:
array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
现在搜索
7
并返回其位置。
我给了这个答案:
将值存储在临时数组中,对其进行排序,然后应用二进制搜索。
如果找到该元素,则返回其在临时数组中的位置。
(如果数字出现两次,则返回其第一次出现)
但是,他们似乎对此答案并不满意。
正确的答案是什么?
最近我接受了一次采访,他们问我一个“ 搜索 ”问题。
问题是:
假设有一个(正)整数数组,每个元素都是
+1
或-1
与其相邻元素比较。例:
array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
现在搜索
7
并返回其位置。
我给了这个答案:
将值存储在临时数组中,对其进行排序,然后应用二进制搜索。
如果找到该元素,则返回其在临时数组中的位置。
(如果数字出现两次,则返回其第一次出现)
但是,他们似乎对此答案并不满意。
正确的答案是什么?
Answers:
您可以使用通常大于1的步长进行线性搜索。关键的观察结果是,如果eg array[i] == 4
和7尚未出现,则7的下一个候选项位于index处i+3
。使用while循环可重复直接进入下一个可行的候选对象。
这是一个实现,略有概括。它会k
在数组中找到第一个匹配项(受+ = 1限制),或者-1
如果没有出现:
#include <stdio.h>
#include <stdlib.h>
int first_occurence(int k, int array[], int n);
int main(void){
int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8};
printf("7 first occurs at index %d\n",first_occurence(7,a,15));
printf("but 9 first \"occurs\" at index %d\n",first_occurence(9,a,15));
return 0;
}
int first_occurence(int k, int array[], int n){
int i = 0;
while(i < n){
if(array[i] == k) return i;
i += abs(k-array[i]);
}
return -1;
}
输出:
7 first occurs at index 11
but 9 first "occurs" at index -1
O(N)
,但我认为没有更快的方法。
您的方法太复杂了。您无需检查每个数组元素。第一个值4
,所以7
是至少 7-4
元素了,你可以跳过这些。
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
int array[] = {4,5,6,5,4,3,2,3,4,5,6,7,8};
int len = sizeof array / sizeof array[0];
int i = 0;
int steps = 0;
while (i < len && array[i] != 7) {
i += abs(7 - array[i]);
steps++;
}
printf("Steps %d, index %d\n", steps, i);
return 0;
}
程序输出:
Steps 4, index 11
编辑:从@Raphael Miedl和@Martin Zabel发表评论后得到改进。
if ((skip = 7 - array[i]) < 1) skip = 1;
似乎比它复杂化,在我看来它pessimize。如果array[i] == 200
您-193
每次都能跳过全部193,即使每次都跳过1,那么为什么不i += abs(7 - array[i])
呢?
skip
为7和之间的绝对差array[i]
。
200
,您会过去的7
。
+1
/ -1
。因此可能就是这样array[0] == 200
,而其他大多数都是-1
。
常规线性搜索的一种变体可能是个不错的选择。让我们选择一个元素说array[i] = 2
。现在,array[i + 1]
将为1或3(奇数),array[i + 2]
将为(仅正整数)2或4(偶数)。
这样继续下去,就可以观察到一个模式- array[i + 2*n]
将保留偶数,因此所有这些索引都可以忽略。
另外,我们可以看到
array[i + 3] = 1 or 3 or 5
array[i + 5] = 1 or 3 or 5 or 7
因此,i + 5
下一个应该检查索引,并且可以使用一个while循环来确定下一个要检查的索引,具体取决于在index处找到的值i + 5
。
虽然这具有复杂性O(n)
(就渐进复杂性而言为线性时间),但由于不访问所有索引,因此在实际意义上优于常规线性搜索。
显然,如果array[i]
(我们的起点)很奇怪,那么所有这些都将被逆转。
约翰·科尔曼(John Coleman)提出的方法很可能是面试官所希望的。
如果您愿意复杂得多,则可以增加预期的跳过长度:
调用目标值k。从位置p的第一个元素的值v开始,并用绝对值av调用差kv dv。为了加快否定搜索的速度,请窥视最后一个元素作为位置o处的另一个值u:如果dv×du为负,则存在k(如果可以接受k的任何出现,则可以按照二进制搜索的方式在此处缩小索引范围)。如果av + au大于数组的长度,则k不存在。(如果dv×du为零,则v或u等于k。)
省略索引的有效性:探测(“ next”)位置,在该位置序列可能返回到v,中间的k为:o = p + 2*av
。
如果dv×du为负,则从p + av到o-au求k(递归?);
如果为零,则u等于k。
如果杜等于DV和中间的值不是k,或Au超过AV,
或者你无法找到从pk信息+ AV于邻AU,
让p=o; dv=du; av=au;
并保持探测。
(要完整回顾60年代的文字,请使用Courier查看。我的“第一第二想法”是使用o = p + 2*av - 1
,因此du du等于dv除外。)
第1步
从第一个元素开始,然后检查它是否为7。假设c
是当前位置的索引。因此,最初 c = 0
。
第2步
如果为7,则找到索引。是c
。如果您已到达数组的末尾,请突破。
步骤3
如果不是,则必须|array[c]-7|
相距至少7个位置,因为每个索引只能添加一个单位。因此,添加|array[c]-7|
到当前索引c,然后再次转到STEP 2进行检查。
在最坏的情况下,当存在交替的1和-1s时,时间复杂度可能达到O(n),但平均情况会很快得到解决。
|c-7|
在哪里 |array[c]-7|
找。)
array[c]-7
可以是正数或负数。您需要先申请abs()
才能跳过。
array[c] - 7
模数运算符|array[c] - 7|
。
在这里,我给出了Java的实现...
public static void main(String[] args)
{
int arr[]={4,5,6,5,4,3,2,3,4,5,6,7,8};
int pos=searchArray(arr,7);
if(pos==-1)
System.out.println("not found");
else
System.out.println("position="+pos);
}
public static int searchArray(int[] array,int value)
{
int i=0;
int strtValue=0;
int pos=-1;
while(i<array.length)
{
strtValue=array[i];
if(strtValue<value)
{
i+=value-strtValue;
}
else if (strtValue==value)
{
pos=i;
break;
}
else
{
i=i+(strtValue-value);
}
}
return pos;
}
这是分而治之的解决方案。以牺牲(更多)记账为代价,我们可以跳过更多元素。而不是从左到右扫描,而是在中间进行测试并向两个方向跳过。
#include <stdio.h>
#include <math.h>
int could_contain(int k, int left, int right, int width);
int find(int k, int array[], int lower, int upper);
int main(void){
int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8};
printf("7 first occurs at index %d\n",find(7,a,0,14));
printf("but 9 first \"occurs\" at index %d\n",find(9,a,0,14));
return 0;
}
int could_contain(int k, int left, int right, int width){
return (width >= 0) &&
(left <= k && k <= right) ||
(right <= k && k <= left) ||
(abs(k - left) + abs(k - right) < width);
}
int find(int k, int array[], int lower, int upper){
//printf("%d\t%d\n", lower, upper);
if( !could_contain(k, array[lower], array[upper], upper - lower )) return -1;
int mid = (upper + lower) / 2;
if(array[mid] == k) return mid;
lower = find(k, array, lower + abs(k - array[lower]), mid - abs(k - array[mid]));
if(lower >= 0 ) return lower;
upper = find(k, array, mid + abs(k - array[mid]), upper - abs(k - array[upper]));
if(upper >= 0 ) return upper;
return -1;
}
const findMeAnElementsFunkyArray = (arr, ele, i) => {
const elementAtCurrentIndex = arr[i];
const differenceBetweenEleAndEleAtIndex = Math.abs(
ele - elementAtCurrentIndex
);
const hop = i + differenceBetweenEleAndEleAtIndex;
if (i >= arr.length) {
return;
}
if (arr[i] === ele) {
return i;
}
const result = findMeAnElementsFunkyArray(arr, ele, hop);
return result;
};
const array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
const answer = findMeAnElementsFunkyArray(array, 7, 0);
console.log(answer);
希望包括该问题的递归解决方案。请享用