假设你有一个整数数组(例如[1 5 3 4 6])。根据以下规则重新排列元素。每个元素都可以向前跳(向左),并在跳转的那些索引中滑动元素。该过程从第二个索引中的元素开始(即5)。它可以选择跳过元素1或者它可以保持在自己的位置。如果它选择跳跃,元素1向下滑动到索引2.让我们假设它确实选择跳跃,然后我们得到的数组将是[5 1 3 4 6]。元素3现在可以跳过1或2个位置并重复该过程。如果在一个位置上跳3次,则阵列现在将是[5 3 1 4 6]并且如果它跳过两个位置,则现在将是[3 5 1 4 6]。
很容易证明以这种方式可以对元素进行所有可能的排列。此外,任何最终配置都可以通过一组唯一的出现来达到。
问题是,给定最终数组和源数组,找到从源到达最终数组所需的总跳数。 O(N ^ 2)实现很容易找到,但我相信这可以在O(N)或O(NlogN)中完成。此外,如果不可能做得比O(N2)更好,那么很高兴知道。
例如,如果最终数组是[3,5,1,4,6]和源数组[1,5,3,4,6],则答案为3。
我的O(N2)算法是这样的:你从末尾遍历源数组的所有位置,因为我们知道这是最后移动的元素。这里它将是6,我们检查它在最终数组中的位置。我们计算必要的跳数,并且需要重新排列最终数组以将该元素放置在源数组中的原始位置。重新排列步骤遍历阵列中的所有元素,并且该过程在所有元素上循环,因此O(N ^ 2)。使用Hashmap或map可以帮助进行搜索,但是在O(N ^ 2)之后的每个步骤之后都需要更新地图。
P.S。我试图以贝叶斯方式模拟两个排列之间的相关性,这是一个子问题。任何关于修改生成过程以使问题更简单的想法也是有帮助的。
编辑:我找到了答案。这正是Kendall Tau距离的作用。有一种简单的基于合并排序的算法可以在O(NlogN)中找到它。答案 0 :(得分:1)
将目标数组视为排序。目标数组[2 5 4 1 3]
只能通过重新标记看作[1 2 3 4 5]
。您只需要知道映射就能够在恒定时间内比较元素。在此示例中,要比较4
和5
,请检查:index[4]=2 > index[5]=1
(在目标数组中),因此4 > 5
(意思是:4
必须是最后5
的权利。
所以你真正拥有的只是一个香草排序问题。排序与通常的数字排序不同。唯一改变的是你的比较功能。其余基本相同。可以在O(nlgn)
,甚至O(n)
(基数排序)中实现排序。也就是说,你有一些额外的限制:你必须就地排序,你只能交换两个相邻的元素。
一个强大且简单的候选人将是selection sort,这将在O(n^2)
时间内完成。在每次迭代中,您将识别“未放置”部分中的“最左侧”剩余元素并将其交换,直到它落在“放置”部分的末尾。通过使用适当的数据结构(用于在O(nlgn)
时间内识别“最左边”剩余元素的优先级队列),它可以提高到O(lgn)
。由于nlgn
是基于比较的排序的下限,我真的认为你不能做得更好。
编辑:所以你根本不对交换顺序感兴趣,只需要最小数量的交换。这正是数组中inversions的数量(适合您的特定需求:“非自然排序”比较函数,但它不会改变数学)。有关该断言的证明,请参见this answer。
查找反转次数的一种方法是调整合并排序算法。由于您必须对数组进行实际排序以对其进行计算,因此结果仍为O(nlgn)
时间。有关实施,请参阅this answer或this(再次请记住,您必须进行调整)。
答案 1 :(得分:0)
根据你的回答,我假设跳数是将原始数组转换为最终数组所需的相邻元素的交换总数。
我建议使用类似插入排序的东西,但没有插入部分 - 数组中的数据不会被更改。
您可以将停滞的料斗的队列 t 作为具有计数器的平衡二进制搜索树(子树中的元素数量)。
您可以将元素添加到 t ,从 t 中删除元素,平衡 t 并在 t 中找到元素位置>在O(log C)时间内,其中C是 t 中元素的数量。
找到元素位置的几句话。它包含二进制搜索,其中包含跳过的左侧(如果将元素保留在分支上,则为中间元素+1)计数。
平衡/添加/删除几乎没有。您必须从已删除/添加的元素/子树向上遍历并更新计数器。对于插入+余额和删除+余额,操作的总操作数仍为O(log C)。
让 t 是(平衡搜索树)队列, p 是当前原始数组索引, q 是最终数组index,原始数组是 a ,最终数组是 f 。
现在我们从左侧开始有一个循环(例如, p = 0, q = 0):
如果 a [ p ] == f [ q ],那么原始数组元素跳过整个队列。将 t.count 添加到答案中,增加 p ,增加 q 。
如果 a [ p ]!= f [ q ]和 f [ q ]不在t中,然后将 a [ p ]插入 t 并增加 p
如果 a [ p ]!= f [ q ]和f [q]在 t 中,然后在队列中添加f [q]的位置来回答,从 f [ q ] > t 并增加 q 。
如果数组真的是一个数组的排列,我喜欢这种魔法可以确保这个过程同时将p和q移动到数组的末尾。不过你应该检查p和q溢出来检测不正确的数据,因为我们没有更快的方法证明数据是正确的。