给定一个随机顺序的整数数组,你必须找到最小数量的交换,将其转换为循环排序数组

时间:2013-03-12 14:55:07

标签: algorithm data-structures dynamic-programming bubble-sort

如果以随机顺序给出数组,则必须输出转换为循环排序数组所需的最小交换次数。

e.g。给出的数组是3 5 4 2 1

所以第一次交换将是5< - > 4结果:3 4 5 2 1 第二次交换将是2< - > 1结果:3 4 5 1 2(最终)

输出:2

我无法理解这个问题背后的逻辑。

添加更多: 只能在相邻元素之间进行交换,数字介于1到N之间

4 个答案:

答案 0 :(得分:5)

好吧,不知道它是否是最好的算法,但我能想到一个O(n ^ 2)解决方案:

首先,忽略循环数组的可能性。让我们解决一个更简单的问题:对数组进行排序的最小交换次数是多少。

这里要小心,因为这不是排序算法。基于比较的排序算法将具有至少O(n log n)的最坏情况。在此问题中,您需要的最大交换次数为n

为什么呢?因为它是您可以实现的最大permutation cycle size。您需要的最小交换数量恰好是排列周期大小减去1。我的意思是你可以将数组的任何排列表示为排列周期,例如:

3 2 1 4 5 - > (2)(4)(5)(1 3)

对于大小为1的排列周期,您不需要任何交换。对于大小为2的置换周期,您只需要1个交换。这可以缩写为:

2 3 4 5 1 - > (1 2 3 4 5)

忽略此数组已经循环排序,此数组完全混乱。为了正常排序,我需要4个交换,基本上将1移动到它的正常位置。

计算排列周期非常简单,只需跟踪数字到数组排序的位置即可。使用前面的例子

3 2 1 4 5

  • A[0];
  • 开始
  • 因为A[0]==3和3将是排序数组中的第3个元素,所以是第3个位置;
  • 因为A[2]==1和1将是......,所以跟随第一个位置。由于我们已经在那里,我们有一个大小为2的周期;

  • 在下一个未访问的位置(1)再次开始

  • A[1]==2处于正确的位置,所以我们不需要做任何事情,这里我们有一个大小为1的周期

  • 依此类推......

这个算法是O(n),但是因为我们需要为每个可能位置的数组做这个(因为它是圆形的),我们会做n次,所以,整个算法是O(n ^ 2)。

<强> UPDATE;一些python代码显示我的算法:

def offset_swaps(A, S, offset):
    visited = [False]*len(A)
    swaps = 0

    for i in xrange(len(A)):
        if visited[i]: continue

        cycle, current = 0, i
        while not visited[current]:
            cycle += 1
            visited[current] = True
            current = (S[A[current]] + offset) % len(A)

        swaps += cycle - 1

    return swaps       

def number_of_swaps(A):
    S = {x:i for i,x in enumerate(sorted(A))}
    min_swaps = len(A)
    for i in xrange(len(A)):
        min_swaps = min(min_swaps, offset_swaps(A, S, i))
    return min_swaps

print number_of_swaps((3, 5, 4, 2, 1))

答案 1 :(得分:1)

我认为这里的方法应该是 - 将所有数字排序到辅助数组中。然后,对于每个循环移位,计算使原始阵列达到该循环移位所需的交换次数。选择最小的那些。

要找到将数组A到阵列B所需的最小数量的交换,只需计算交换值的数量(即,值a在A的值b左侧,但在数组B中则相反)。这个问题对于计算给定数组中的反转是相同的,并且可以在O(n*log(n))中再次使用修改的合并排序来解决。

我的方法的复杂性是O(n^2*log(n))(因为你对大小为n的数组的所有循环移位进行合并排序)。

我想不出更快解决问题的方法。

答案 2 :(得分:0)

假设:

  1. 数组元素是整数1到N。
  2. 步骤:

    1. 分配长度为N的第二个(直方图)数组H.
    2. 在单次通过初始数组A时,对于每个索引,我增加H [(A [i] - i)%N]
    3. 在单次通过H时,使用最大计数
    4. 标识H的元素R.
    5. 清除H.
    6. 在R旋转下识别原始阵列中的轨道数量(通过H前进直到H [i] = 0,然后踩踏A [i]是旋转R下的成员的轨道) )为轨道的每个成员设置H [i] = 1,并重复直到元素H [N-1]已被处理(随着时间的推移计算轨道的nubmer)。这也是O(N),因为A的每个元素只被访问一次。
    7. 所需的互换次数= N - 轨道。
    8. 这是O(N)。

      更新:我已更新算法以考虑多个轨道。在处理每个轨道时,最终交换放置两个元素而不仅仅是1.

      我怀疑轨道的数量在任何旋转下都是不变的,这会有效地简化算法,但不会影响它的复杂性,它保持在O(N)。

答案 3 :(得分:0)

def number_of_swaps(A):
    l=len(A)
    if l < 2:
        return 0
    S1={x:i for i,x in enumerate(A)}
    pos_of_0=S1[0]
    pos_of_N=S1[l-1]
    if pos_of_0 > 0:
        if pos_of_N != (l-1):
            if pos_of_N < pos_of_0:
                n=(pos_of_0+(l-1)-pos_of_N-1)
            else:
                    n=(pos_of_0+(l-1)-pos_of_N)
        else:
            n=(pos_of_0)
    else :
        n=(l-pos_of_N-1)
    A.remove(0)
    A.remove(l-1)
    B=[x-1 for x in A ]
    return n+number_of_swaps(B)

def min_number_of_swaps(A):
    B=sorted(A)
    swaps=[]
    for i in range(len(A)):
        if i == 0:
            C=B
        else:
            C=B[-i:]+B[0:len(A)-i]

        S = {x:i for i,x in enumerate(C)}
        D=[S[i] for i in A]
        swaps.append(number_of_swaps(D))
    return min(swaps)

print min_number_of_swaps([8,5,7,1,2,4,3,6])

7

以上代码是解决问题复杂度O(N ^ 3)

的递归方法 已编辑

代码,仅打印最小数量的交换。