找到对给定数组进行排序所需的remove-then-append操作的数量

问题描述 投票:20回答:13

这是一个面试问题。 swap表示从数组中删除任何元素并将其附加到同一数组的后面。给定一个整数数组,找到排序数组所需的最小swaps数。

有没有比O(n^2)更好的解决方案?

例如:

输入数组:[3124]。

swaps的数量:2([3124] - > [1243] - > [1234])。

arrays algorithm sorting language-agnostic
13个回答
8
投票

即使我们不假设连续值的数组,这可能在O(nlogn)中有效。 如果我们这样做 - 它可以在O(n)完成。一种方法是使用O(n)空间和O(nlogn)时间。 给定数组A将它(O(nlogn))排序成第二个数组B。 现在...(数组从1索引)

swaps = 0
b = 1
for a = 1 to len(A)
  if A[a] == B[b]
    b = b + 1
  else
    swaps = swaps + 1

1
投票
for(int count = 1; count<=length; count++)
{
    tempSwap=0; //it will count swaps per iteration
    for(int i=0; i<length-1; i++)
        if(a[i]>a[i+1])
        {
           swap(a[i],a[i+1]);
           tempSwap++;
        }
    if(tempSwap!=0) //check if array is already sorted!
        swap += tempSwap;
    else
        break;
}
System.out.println(swaps);

1
投票

这是一个适用于所有输入的O(n)解决方案:

static int minimumSwaps(int[] arr) {
        int swap=0;
        boolean visited[]=new boolean[arr.length];

        for(int i=0;i<arr.length;i++){
            int j=i,cycle=0;

            while(!visited[j]){
                visited[j]=true;
                j=arr[j]-1;
                cycle++;
            }

            if(cycle!=0)
                swap+=cycle-1;
        }
        return swap;
    }
}

1
投票
def minimumSwaps(arr):
    swaps = 0
    '''
    first sort the given array to determine the correct indexes 
    of its elements
    '''
    temp = sorted(arr)

    # compare unsorted array with the sorted one
    for i in range(len(arr)):
        '''
        if ith element in the given array is not at the correct index
        then swap it with the correct index, since we know the correct
        index because of sorting. 
        '''
        if arr[i] != temp[i]: 
          swaps += 1
          a = arr[i]
          arr[arr.index(temp[i])] = a
          arr[i] = temp[i]    
    return swaps

0
投票
int temp = 0, swaps = 0;
        for (int i = 0; i < arr.length;) {
            if (arr[i] != i + 1){ 
            //  System.out.println("Swapping --"+arr[arr[i] - 1] +"  AND -- "+arr[i]);
                temp = arr[arr[i] - 1];
                arr[arr[i] - 1] = arr[i];
                arr[i] = temp;
                ++swaps;
            } else
                ++i;
                //  System.out.println("value at position -- "+ i +" is set to -- "+ arr[i]);
        }
        return swaps;

这是我发现的最优化的答案。这很简单。您可能会通过循环了解一下。感谢Darryl在黑客级别。


0
投票

O(1)空间和O(N)(〜2 * N)解决方案,假设min元素为1,并且数组包含从1到N-1的所有数字,没有任何重复值。其中N是数组长度。

int minimumSwaps(int[] a) {
    int swaps = 0;
    int i = 0;
    while(i < a.length) {
        int position = a[i] - 1;
        if(position != i) {
            int temp = a[position];
            a[position] = a[i];
            a[i] = temp;
            swaps++;
        } else {
            i++;
        }
    }
    return swaps;
}

22
投票

问题归结为找到排序数组的最长前缀,该前缀在输入数组中显示为子序列。这确定了不需要排序的元素。其余的元素需要逐个删除,从最小到最大,并附加在后面。

在你的例子中,[3, 1, 2, 4],已经排序的子序列是[1, 2]。最佳解决方案是删除重新生成的两个元素34,并将它们附加在后面。因此,最佳解决方案是两次“交换”。

使用O(n logn)额外内存可以在O(n)时间内找到子序列。以下伪代码将执行此操作(代码也恰好是有效的Python):

l = [1, 2, 4, 3, 99, 98, 7]
s = sorted(l)
si = 0
for item in l:
  if item == s[si]:
    si += 1
print len(l) - si

如果,如在你的例子中,数组包含从1n的整数排列,问题可以使用O(n)内存在O(1)时间解决:

l = [1, 2, 3, 5, 4, 6]
s = 1
for item in l:
  if item == s:
    s += 1
print len(l) - s + 1

更一般地,只要我们先验地知道输出数组,就可以使用第二种方法,因此不需要通过排序找到它。


4
投票

观察:如果元素交换到后面,它的先前位置无关紧要。不需要多次交换任何元素。

观察:最后一次交换(如果有的话)必须移动最大的元素。

观察:在交换之前,必须对数组(不包括最后一个元素)进行排序(通过前交换,或最初)

排序算法,假设值是连续的:找到从1开始的连续(按值)元素的最长排序子序列:

3 1 5 2 4

依次交换所有更高元素:

1 5 2 4 3

1 5 2 3 4

1 2 3 4 5

要查找O(n)中的交换数,请查找从1开始的连续元素的最长排序子序列的长度:

  • 预期= 1
  • 对于每个元素的顺序 if element == expected 预期+ = 1
  • 返回预期-1

那么swaps的数量=输入的长度 - 它最长的排序子序列。

如果输入不是1..n的置换,则替代解决方案(O(n ^ 2)):

  • 交换= 0
  • 环 找到最大元素的第一个实例,并检测数组是否已排序 如果数组已排序,则返回交换。 否则从数组中删除找到的元素并增加交换。

还有另一个解决方案(O(n log n)),假设有独特的元素:

  • 包装{oldPos,newPos,value}中的每个元素
  • 制作数组的浅表副本
  • 按值排序数组
  • 存储每个元素的新位置
  • 在(未排序的)副本中运行newPos'上的排列算法

如果您不想复制输入数组,请在最后一步之前按oldPos排序。


2
投票

这可以在O(n log n)中完成。

首先找到数组中的最小元素。现在,找到此元素之前出现的最大元素。叫这个max_left。你必须在数组的min元素之前调用swap()for所有元素。

现在,找到min元素右侧最长的增加子序列,以及应跳过值大于max_left的元素的约束。交换所需的数量是size(array) - size(LIS)

例如考虑数组,

7 8 9 1 2 5 11 18

数组中的最小元素是1.因此我们在最小元素之前找到最大值。

7 8 9 | 1 2 5 11 18

max_left = 9

现在,找到min的右边的LIS,元素<9 LIS = 1,2,5

没有掉期= 8 - 3 = 5

在max element为null的情况下,即.min是第一个元素,找到数组的LIS,所需的答案是size(array)-size(LIS)

例如

2 5 4 3

max_left为null。 LIS是2 3

没有交换=大小(数组) - 大小(LIS)= 4 - 2 = 2


2
投票

这是python中用于最小交换次数的代码,

def find_cycles(array):
   cycles = []
   remaining = set(array)
   while remaining:
      j = i = remaining.pop()
      cycle = [i]
      while True:
         j = array[j]
         if j == i:
             break
         array.append(j)
         remaining.remove(j)
      cycles.append(cycle)
   return cycles

def minimum_swaps(seq):
    return sum(len(cycle) - 1 for cycle in find_cycles(seq))

1
投票

@all,@ Itay karo和@NPE提供的公认解决方案是完全错误的,因为它不考虑交换元素的未来排序......

许多测试用例都失败了,例如:

3 1 2 5 4

正确输出:4

但他们的代码输出为3 ......

解释:3 1 2 5 4 ---> 1 2 5 4 3 ---> 1 2 4 3 5 ---> 1 2 3 5 4 ---> 1 2 3 4 5

PS:我不能因为声誉低而在那里发表评论


1
投票
int numSwaps(int arr[], int length) {
bool sorted = false; 
int swaps = 0;
while(!sorted) {
    int inversions = 0;
    int t1pos,t2pos,t3pos,t4pos = 0;
    for (int i =  1;i < length; ++i)
    {
        if(arr[i] < arr[i-1]){
            if(inversions){
                tie(t3pos,t4pos) = make_tuple(i-1, i);
            }
            else tie(t1pos, t2pos) = make_tuple(i-1, i);
            inversions++;
        }
        if(inversions == 2)
            break;
    }
    if(!inversions){
        sorted = true;
    }
    else if(inversions == 1) {
        swaps++; 
        int temp = arr[t2pos];
        arr[t2pos] = arr[t1pos];
        arr[t1pos] = temp;
    }
    else{
        swaps++;
        if(arr[t4pos] < arr[t2pos]){
            int temp = arr[t1pos];
            arr[t1pos] = arr[t4pos];
            arr[t4pos] = temp;
        }
        else{
            int temp = arr[t2pos];
            arr[t2pos] = arr[t1pos];
            arr[t1pos] = temp;
        }
    }
}
return swaps;
}

此代码返回对数组进行排序所需的最小交换数。

例如,A [] = [7,3,4,1]通过交换1和7,我们得到[1,3,4,7]。类似地B [] = [1,2,6,4,8,7,9]。我们先用4交换6,所以,B [] - > [1,2,4,6,8,7,9]。然后是7和8.所以 - > [1,2,4,6,7,8,9]

该算法在O中运行(其中索引i处的值<索引i-1处的值)~O(N)。


1
投票

编写一个非常简单的JavaScript程序来对数组进行排序并查找交换次数:

  function findSwaps(){

    let arr = [4, 3, 1, 2];
    let swap = 0
    var n = arr.length
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            if (arr[i] > arr[j]) {
                arr[i] = arr[i] + arr[j];
                arr[j] = arr[i] - arr[j];
                arr[i] = arr[i] - arr[j]
                swap = swap + 1
            }
        }
    }
    console.log(arr);
    console.log(swap)
  }

1
投票

听到我在c#中的解决方案,以解决缩短数组所需的最小交换次数。当时我们只能交换2个元素(在任何索引位置)。

public class MinimumSwaps2
{
    public static void minimumSwapsMain(int[] arr)
    {

        Dictionary<int, int> dic = new Dictionary<int, int>();
        Dictionary<int, int> reverseDIc = new Dictionary<int, int>();
        int temp = 0;
        int indx = 0;
  //find the maximum number from the array
        int maxno = FindMaxNo(arr);

        if (maxno == arr.Length)
        {
            for (int i = 1; i <= arr.Length; i++)
            {
                dic[i] = arr[indx];
                reverseDIc.Add(arr[indx], i);
                indx++;
            }
        }
        else
        {
            for (int i = 1; i <= arr.Length; i++)
            {
                if (arr.Contains(i))
                {
                    dic[i] = arr[indx];
                    reverseDIc.Add(arr[indx], i);
                    indx++;
                }

            }
        }

        int counter = FindMinSwaps(dic, reverseDIc, maxno);


    }
    static int FindMaxNo(int[] arr)
    {
        int maxNO = 0;
        for (int i = 0; i < arr.Length; i++)
        {
            if (maxNO < arr[i])
            {
                maxNO = arr[i];
            }
        }
        return maxNO;
    }
    static int FindMinSwaps(Dictionary<int, int> dic, Dictionary<int, int> reverseDIc, int maxno)
    {
        int counter = 0;
        int temp = 0;
        for (int i = 1; i <= maxno; i++)
        {
            if (dic.ContainsKey(i))
            {
                if (dic[i] != i)
                {
                    counter++;
                    var myKey1 = reverseDIc[i];
                    temp = dic[i];
                    dic[i] = dic[myKey1];
                    dic[myKey1] = temp;

                    reverseDIc[temp] = reverseDIc[i];
                    reverseDIc[i] = i;
                }
            }
        }
        return counter;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.