在O(n)时间中:在不传递比较的集合中找到最大元素


21

标题说明了问题。

作为输入,我们可以比较一个元素列表(确定最大)。没有元素可以相等。

关键点:

  1. 比较不是传递性的(想像石头剪刀布):这可能是正确的:A> B,B> C,C> A (请注意,这不是有效的输入,因为此处没有有效的答案,我仅描述“非及物比较”)
  2. 每个输入数组将保证有一个答案
  3. 最大表示元素必须大于其他所有元素
  4. 拥有逆属性,即A> B表示B <A

例:

Input: [A,B,C,D]
A > B, B > C, C > A
D > A, D > B, D > C
Output: D

我无法找出在O(n)时间内完成此操作的方法,我最好的解决方案是O(n ^ 2)。

因为要确定答案,所以必须将每种元素与其他所有元素进行显式比较,以证明它确实是答案(因为比较不是可传递的),因此我对每种方法都感到困惑。

这排除了堆的使用,排序等。


8
目前尚不清楚如何定义“最大要素”?例如,如果,哪个元素最大一种>>CC>一种?您还有其他比较规则吗?
fade2black

6
我无法想象我们将如何选择一组至少没有部分排序的最大元素。请参阅最大和最小元素的定义。传递性的缺乏排除了部分顺序。
fade2black

3
@ fade2black为什么将我链接到“最伟大”的另一个定义。我在此明确说明“最大”的定义。最大的意思是,该元素大于其他所有元素。没有元素相等。这就是全部。这不清楚吗?
James Wierzba

2
您最后一个关于A,B,C,D的示例将有助于您理解是否将其包含在OP中的问题。
fade2black

3
C#编译器小组的一个成员曾经将其作为面试问题问过;这很重要,因为在C#中,重载解析算法必须在给定的“更好”关系(通常但不一定是可传递的)的情况下,选择集合中唯一的最佳成员。(或者,如果没有这种独特的最佳成员,则给出适当的答案;可以建立联系。)尽管我设法回答正确,但我从未认为这是一个特别好的面试问题,因为它依赖于“ aha”洞察力来获得线性算法。
埃里克·利珀特

Answers:


38

查找最大值的标准算法仍然有效。开始用与去年的元素,如果你看到一个较大的值,更新最大的是该值。之所以起作用,是因为您跳过的每个元素都小于至少一个元素,因此不能为最大值。a1

明确地说,“标准算法”的意思是:

max <- a_1
for i=2 to n
   if a_i > max
      max <- a_i
output max

为了完整起见,我将在此处讨论评论中提出的问题。上面讨论中的设置是找到相对于反对称关系的最大值,其中如果对于所有j i,我们都有一个i > a j,则a i是最大值。上面的算法在假设存在最大值的情况下工作,但是,如果不知道最大值,则可以使用它来验证最大值的存在(检查返回的元素是否确实大于所有其他元素,这在Chi的评论中已提及)并在Ilmari Karonen中回答)。<aijiai>aj

如果不一定是反对称的,则上述算法将失败(如评论中的Emil所述)。如果<是任意关系(即我们同时放松了传递性和反对称性),那么就不难证明不可能找到线性时间的最大值。通过表示一个的次数一个参加了一个查询,我们定义在某种程度上敌对关系,最大不能没有足够的查询显示出来。由于查询一个 > 一个Ĵ,回答一个 > Ĵ如果一个<<#aiaiai>?ajai>aj,否则 a i < a j。如果查询数为 o n 2,则尚未看到最大值,可以将其设置为集合中的任意一个元素。#ai<n1ai<ajo(n2)


1
@JamesWierzba(我认为)他只是说一个“跳过”元素是不大于您当前最大值的元素。考虑一下标准算法:对照当前最大值检查列表中的每个值。您已经说过,每个列表中都有一个最大的元素。有时,您会将其与当前的最大值进行比较,并且由于该最大值更大,因此该值将成为您的新最大值。因为此值大于列表中的所有其他值,所以您永远找不到更大的元素,并且最大的值也不会被替换。经过n比较之后,您当前的最高值将是答案
Lord Farquaad

1
为了清楚起见,该算法不假定传递性。如果您觉得难以置信,请遵循正确性证明的细节(出于矛盾的目的,假设返回值不是最大值,请使用第一段中的想法)。
Ariel

7
这取决于问题中的假设2:数组中始终有最大值。如果不是这种情况,则max只能是子数组的最大值。即使没有假设2,也可以找到一个试探性最大值,然后在O(n)范围内使用第二次扫描在整个阵列上对其进行验证。

7
该答案假定B > A不能同时成立。据我所知,这个问题并不排除在外。A>BB>A
埃米尔·杰拉贝克(EmilJeřábek)

4
@ oconnor0不遵循。举一个具体的例子,假设A> B,A> C,B> A和C>B。则A大于集合中的任何其他元素(并且是具有此属性的唯一元素),但是如果这些元素是在顺序的遭遇,B,C,则该算法将输出C.
周华健耶扎贝克支持莫妮卡

24

正如Ariel所指出的,下面给出了标准的最大查找算法:

def find_maximum(a):
    m = a[0]
    for x in a:
        if x > m: m = x
    return m

实际上,只要保持以下条件,它们将可以正常工作:

  • 可以比较任何一对元素,并且
  • 确保输入中包含最大元素,即成对大于输入中任何其他元素的元素。

(上面的第一个假设实际上可以放宽,即使不必修改算法,只要我们假设最大元素与其他所有元素都具有可比性,并且x > y如果这些元素xy不可比,则始终为假。)

特别是,您声称:

[…]可以肯定地说,该元素需要与其他所有元素进行显式比较(因为比较不是可传递的)。

在上面给出的假设下是不正确的。实际上,要证明上述算法将始终找到最大元素,只需观察到以下内容即可:

  1. 由于循环遍历所有输入元素,因此在某些迭代x中将是最大元素;
  2. 由于最大元素成对大于每个其他元素,因此在迭代结束时m将成为最大元素;和
  3. 由于没有其他元素可以成对大于最大元素,因此m在任何后续迭代中都不会改变。

因此,m如果输入包含一个元素,则在循环结束时,它将始终是最大元素。


附言 如果输入的信息并不一定总是包含一个极大元,然后验证事实的确需要测试的候选答案对所有其他元素,以验证它确实是最大的。但是,运行上面的最大查找算法之后,我们仍然可以在O(n)时间执行此操作:

def find_maximum_if_any(a):
    # step 1: find the maximum, if one exists
    m = a[0]
    for x in a:
        if x > m: m = x

    # step 2: verify that the element we found is indeed maximal
    for x in a:
        if x > m: return None  # the input contains no maximal element
    return m  # yes, m is a maximal element

(我在这里假设关系>是不自反的,即没有元素可以大于自身。如果不是这种情况,x > m则应将第2步中的比较替换为x ≠ m and x > m,其中表示身份比较。或者我们可以应用优化如下所述。)

为了证明算法变体的正确性,请考虑两种可能的情况:

  • 如果输入包含最大元素,则第1步将找到它(如上所示),第2步将确认它。
  • 如果输入没有含有最大元素,则步骤1将最终挑选一些任意元素m。不管是哪个元素,因为它在任何情况下都是非最大的,因此步骤2将检测到该元素并返回None

如果我们将min 的索引存储在输入数组中a,则实际上可以优化步骤2以仅检查min 之前的那些元素a,因为已经将较晚的元素与m步骤1 进行了比较。但是此优化不会改变渐近时间的复杂性的算法,仍然是O(n)。


3
实际上,OP跳过了许多细节。例如,关于该关系的自反性什么也没说,因此,如果它不是自反性的,则if x > m:不确定。
fade2black

4

“最大意味着该元素必须大于所有其他元素”是如何执行此操作的重要提示。O(n)

如果遍历列表中的比较元素,则“丢失”比较的任何元素都将被立即丢弃,因为为了达到最大效果,它必须大于所有其他元素,因此单项损失将取消其资格。

一旦您以这种方式考虑到它,那么很明显,您实际上可以进行比较,并且通过每次都丢弃一个失败者来得到最大的元素,这是最后一次比较的结果。n1

这种解决方案的精妙之处在于:“没有元素可以相等”,再加上永远存在最大元素这一事实。如果我们将获胜关系映射为有向图,则很明显,只要遵循获胜,我们就能达到最大要素。


1
非循环有向图 ”是错误的模型:它应该是“ 锦标赛 ”。允许循环,并且至关重要的是,每个边沿都只沿一个方向存在。
彼得·泰勒

@PeterTaylor,你是绝对正确的,我只是在考虑那些导致“最伟大”元素的胜利,其他胜利的意义不大,但可能会在发现最大胜利的道路上走遍,所以你是对的,他们可以不能打折
Danikov '17

3

我假设至少一个元素具有反对称关系(这保证了最大元素的存在),否则该任务是不可能的。如果有限集中的所有元素都是可比的,则通常的最大发现程序起作用。

如果某些元素不可比较,则以下过程将起作用

max = nil
For i=1 to n
   if max is nil then
      max = a[i]
   if max and a[i] are not comparable then
      max = nil
   else if max < a[i] then
      max = a[i]
End

A,B,C,D

A>B,B>C,C>A
D>A,D>B,D>C


i=1: max=A
i=2: max=AA>B
i=3: max=CA<C
i=4: max=DD>C

m>aa<mamm<aamam


我认为else if不需要第一个。如果max为最大值,则会触发该触发,如果尚未达到最大值,则值max是什么都无关紧要。
rici

是的,那是第一个。另一个是第二个:)
rici

您的意思是ifs不带elses?这只是一个习惯:与elses甚至不进行比较。:)
fade2black

1
仅初始化max到列表中的任何元素,然后在循环内进行操作,是否更简单if max and a[i] are comparable and max < a[i] then max = a[i](如果我们假设尝试比较两个不可比较的元素总是产生false,则可以省略条件的第一部分)吗?
Ilmari Karonen

1
OP假定@badallen,始终存在最大的元素。在您的示例中,没有最大的要素。
fade2black

2

A<BB<A

A1...AnAi<Aj

n2

Ai<Ajj

j0Ai<Aj0ijijAi<AjiijAij<Ajj0ij

我希望这是可以理解的。随意提问或编辑。

基本思想是,如果允许完全任意的关系,则无法从已知的元素中收集有关其余元素的任何信息。

A<Ann2n


1

A > Bn

这样,您可以仅遍历条目,并从可能的解决方案集中删除比另一个少的元素。使用哈希集或类似的哈希集,这在是可能的O(n)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.