在不知道长度的情况下找到数组的中间
Posted
技术标签:
【中文标题】在不知道长度的情况下找到数组的中间【英文标题】:Finding the middle of an array without knowing the length 【发布时间】:2011-10-22 19:47:41 【问题描述】:找到长度未知的字符串或数组的中间。你可以 不遍历列表来查找长度。您不得使用任何东西 帮助您找到长度 - 因为它是“未知的”。 (即没有 sizeof (C) 或 count(C#) 等...)
我有这个问题作为面试问题。我只是想知道答案是什么。我确实问过我是否可以使用 sizeof,他说“不,字符串或数组的大小是未知的 - 你只需要到中间。”
顺便说一句,我不确定这是否真的可以在不遍历的情况下解决。我几乎觉得他可能想看看我对我的回答有多自信:S 不确定......
他的英语很糟糕——也不确定这是否会导致误解。他直接告诉我,我不需要遍历列表即可到达中间 :S :S 我假设他的意思根本不遍历..... :S
【问题讨论】:
这是不可能的,除非你可以“寻找”到最后。 您将数据结构描述为“字符串”、“数组”和“列表”。究竟是什么东西,什么操作对它有效? 本周有几个人面试同一个职位?我确定这是几天前出现的,但我找不到第一个。 数组或字符串的中间是r
:)
如果您只知道片段的开头,则无法找到中间。想象一下,你站在一片开阔的田野里,我告诉你朝某个方向走一半;你应该怎么做?在这种情况下,开放字段是计算机内存。
【参考方案1】:
有两个计数器,c1 和 c2。开始遍历列表,每次递增 c1,每隔一次递增 c2。当 c1 到达终点时,c2 将在中间。
您还没有“遍历列表以找到长度”然后将其除以二,您只是经历了一次。
我能想到的唯一另一种方法是继续删除列表中的第一项和最后一项,直到你只剩下中间的一项。
【讨论】:
OP 提到不能遍历。尽管您的答案似乎是我认为正确的答案。 这是Hare and the Tortoise Algorithm
的小修改版。
它说“可能无法遍历找到长度”,但我想知道该措辞是否允许此解决方案,因为您实际上并没有明确找到长度。
@Als 我同意,但是在 H&T 中,您为大多数人的膝跳 O(n^2) 解决方案提供了 O(n) 解决方案。在这里,问题的算法优势似乎不那么明显......
请注意,这对于 C 中的数组是不可能的,因为您不知道何时停止,(除非数组已知为字符串或其他明智的具有 senitel 值)。即使在 C# 中,您也必须依靠抛出的异常来找到结束,因为 .Count/.Size 属性是禁止使用的(或者您可以使用 foreach 侥幸逃脱)【参考方案2】:
您(或您的面试官)对数据的含义非常模糊(您提到了“字符串”和“数组”);没有可以做出的假设,所以它可以是任何东西。
你提到字符串的长度是未知的,但从你的措辞看来你(或面试官)实际上是想说不可知。
a) 如果只是未知,那么问题是,如何确定?例如,在字符串的情况下,您可以认为结尾是'\0'
。然后,您可以应用一些算法,例如其他答案所建议的算法。
b) 如果它是不可知的,那么这个谜就没有解。中间的概念没有开始和结束就没有意义。
底线,你不能谈论没有开始和结束或长度的中间。要么这个问题是故意无法回答的,要么你没有正确理解它。您必须知道的不仅仅是内存段的开头,可能还有它的类型。
【讨论】:
+1 反对无正当理由的反对票(不是说答案不好,但我只有一票可投)。 从技术上讲,不可能程序“不知道”数组的大小,而字符串或列表的全部目的就是遍历它。唯一好的答案是:问题是错误的。面试官应该没有通过面试。【参考方案3】:以下代码将在不遍历列表的情况下找到数组的中间
int thearray[80];
int start = (int)&thearray;
int end = (int)(&thearray+1);
int half = ((end-start) / 4)/ 2;
std::cout << half << std::endl;
编辑:
此代码假定您正在处理一个实际数组,而不是指向第一个元素的指针,因此代码如下:int *pointer_to_first_element = (int *)malloc(someamount);
将不起作用,与将数组引用降级为指向第一个元素的指针的任何其他表示法一样。基本上任何使用*
的符号。
【讨论】:
如果thearray
是一个实际的大小为 4 的对象数组,而不是指向第一个元素的指针,这将起作用。
我将 thearray
初始化为 thearray[80]
并得到结果 40,但是您对指向第一个元素的指针是正确的,但这是唯一符合 ' 的可用方法没有遍历规则,sizeof(int)
可以是任意的,因为它只是一个硬编码的数字。
你确定? codepad.org/pWRYSkw8 BTW,如果 typeof(thearray) 是 char* 那么 end - start = 1
这是一个char指针,这是为一个int数组制作的,啊我看到我没有粘贴第一行代码,可能会清除一些东西。是的,在这种情况下,除以 4 会很愚蠢,但这不是一个非常普遍的问题,因此必须使用非常专业的答案,例如,OP 声明的数组,而不是指向内存的指针,和 int 是相当标准的,所以我使用了一个 int 数组。
@BlackBear:您的thearray
不是数组,它是指向数组第一个元素的指针。元素的大小也不是 4。通过一些调整,这是针对此问题的特定实例的完全合理的解决方案。 codepad.org/D882UvEx 但是,到那时,您还不如直接 return N/2;
省去麻烦。【参考方案4】:
您只需使用第一个和最后一个元素的地址之间的差异。
【讨论】:
你怎么知道最后一个元素的地址? @nhed 经过一番挖掘后,我 现在 看到在事后对面试官的问题添加了限制,这是在另一个答案中的 20 个 cmets 中提出的。它也被标记为 c++; c++ 类型具有可用于获取前后元素地址的方法和迭代器。尽管如此,它仍然优于遍历,并且比公认的答案更符合要求;) @anonymous_downvoter 这没有帮助,除非你证明你的行为是正当的。 我不同意......事实上,有些类型可以报告它们的大小是无关紧要的,如果您使用该功能,您会忽略问题的要求。我也不喜欢接受的答案(虽然没有否决任何事情),我认为问题的措辞有问题,它可能试图简化 T&H 问题但把它搞砸了。我最喜欢保罗的回答 @nhed 有些类型可以报告它们的大小这一事实是无关紧要的,如果您使用该功能,您将忽略问题的要求。 占主导地位的 c++stdlib数组的类表示为std::vector
;我们将使用它进行演示。我的回答和我的扩展解决方案(使用 c++ 容器)不依赖于std::vector::size()
。 (续)【参考方案5】:
我认为这个问题也旨在测试您在问题分析和需求收集方面的技能。正如其他人之前所说,我们至少需要另一条数据来解决这个问题。
我的方法是让面试官明白我们可以在函数调用中通过一个约束来解决问题:调用者必须提供 2 个指针,一个指向数组的开头,另一个指向数组的结尾。给定这两个指针,并使用基本的指针算法,我得到了这个解决方案;请让我知道您对此的看法。
int *findMiddleArray( int const *first, int const *last )
if( first == NULL || last == NULL || first > last )
return NULL;
if( first == last )
return (int *)first;
size_t dataSize= ( size_t )( first + 1 ) - ( size_t )first,
addFirst= ( size_t )first,
addLast= ( size_t )last,
arrayLen= ( addLast - addFirst) / dataSize + 1,
arrayMiddle= arrayLen % 2 > 0 ? arrayLen / 2 + 1 : arrayLen / 2;
return ( int * )( ( arrayMiddle - 1 ) * dataSize + addFirst );
【讨论】:
【参考方案6】:您可以找到数组中点的一种方法是(对于奇数长度数组) 只需使用两个循环,第一个循环从 0 索引开始遍历,另一个(嵌套)循环将从数组的最后一个索引遍历。现在只需比较相同的元素......这将是数组的中点。即 if(arr[i]== arr[j]) 。希望你明白了!
对于偶数长度数组..你可以这样做 if(arr[i] == arr[j-1]) 或 if(arr[i] == arr[j+1]) 因为它们永远不会相同。试一试!
【讨论】:
这仅在所有数组值都是唯一时才有效。例如,如果第一个和最后一个元素具有相同的值,则循环立即停止。 问题指出数组的长度是未知的,所以你不能只使用数组的最后一个索引,因为那是未知的。以上是关于在不知道长度的情况下找到数组的中间的主要内容,如果未能解决你的问题,请参考以下文章