web-dev-qa-db-ja.com

特定の配列をソートするために必要な削除後追加操作の数を見つける

これはインタビューの質問です。 swapは、配列から要素を削除し、同じ配列の後ろに追加することを意味します。整数の配列が与えられたら、配列のソートに必要なswapsの最小数を見つけます。

O(n^2)よりも良い解決策はありますか?

例えば:

入力配列:[3124]。

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

23
Michael

これは、連続する値の配列を想定していなくても、O(nlogn)で機能する可能性があります。
この場合、O(n)で実行できます。それを行う1つの方法は、O(n) spaceとO(nlogn) timeを使用することです。
指定された配列Aは、2番目の配列Bに並べ替えます(O(nlogn))。
now ...(配列は1からインデックス付けされます)

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

問題は、入力配列のサブシーケンスとして表示されるソートされた配列の最長のプレフィックスを見つけることに要約されます。これにより、notをソートする必要がある要素を決定します。残りの要素は、最小のものから最大のものまで1つずつ削除し、最後に追加する必要があります。

例の_[3, 1, 2, 4]_では、既にソートされたサブシーケンスは_[1, 2]_です。最適な解決策は、残りの2つの要素_3_および_4_を削除し、それらを最後に追加することです。したがって、最適なソリューションは2つの「スワップ」です。

サブシーケンスの検索は、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
_

あなたの例のように、配列に_1_からnまでの整数の順列が含まれている場合、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
_

より一般的には、2番目の方法は、出力配列をアプリオリに知っている場合に使用できます。したがって、ソートによって見つける必要はありません。

23
NPE

観察:要素が後ろにスワップされる場合、その前の位置は重要ではありません。要素を複数回交換する必要はありません。

観察:最後のスワップ(ある場合)は、最大の要素を移動する必要があります。

観察:スワップの前に、配列(最後の要素を除く)をソートする必要があります(以前のスワップによって、または最初に)

ソートアルゴリズム、値がコーンカットであると仮定した場合: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
  • シーケンスの各要素に対して
    • 要素==が期待される場合
      • 予想される+ = 1
  • 戻り値-1

次に、スワップの数=入力の長さ-最長のソートされたサブシーケンス。

代替ソリューション(O(n ^ 2))入力が1..nの順列でない場合:

  • スワップ= 0
  • ループ
    • 最大の要素の最初のインスタンスを見つけ、配列がソートされているかどうかを検出します
    • 配列がソートされている場合、スワップを返します。
    • それ以外の場合は、見つかった要素を配列から削除し、スワップをインクリメントします。

まだ別のソリューション(O(n log n))、一意の要素を想定:

  • 各要素を{oldPos、newPos、value}でラップします
  • 配列の浅いコピーを作成します
  • 値で配列をソートします
  • 各要素の新しい位置を保存します
  • (ソートされていない)コピーのnewPos 'で順列のアルゴリズムを実行する

入力配列をコピーしたくない場合は、代わりに最後のステップの前にoldPosでソートします。

4
John Dvorak

これは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;
    }
}
2
MFCDev

これはO(n log n)で実行できます。

まず、配列内の最小要素を見つけます。次に、この要素の前にあるmax要素を見つけます。これを_max_left_と呼びます。配列の最小要素の前にすべての要素に対してswap()を呼び出す必要があります。

ここで、値がmax_leftよりも大きい要素をスキップするという制約とともに、min要素の右側で最も長く増加するサブシーケンスを見つけます。必要なスワップの数はsize(array) - size(LIS)です。

たとえば、配列について考えてみましょう。

_7 8 9 1 2 5 11 18_

配列の最小要素は1です。したがって、最小要素の前に最大値を見つけます。

_7 8 9 | 1 2 5 11 18_

max_left = 9

ここで、要素<9 LIS = 1,2,5を持つminの右側のLISを見つけます

スワップ数= 8-3 = 5

Max要素がnull、つまりminが最初の要素である場合、配列のLISを見つけ、必要な答えはsize(array)-size(LIS)です

例えば

_2 5 4 3_

max_leftはnullです。 LISは_2 3_です

スワップの数=サイズ(配列)-サイズ(LIS)= 4-2 = 2

2
nkskalyan

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))
2
GraphicalDot
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
vipul

配列をソートし、スワップの数を見つけるための非常に単純な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
dinesh_malhotra
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
1
user7188158

配列をショートするために必要な最小数のスワップを解決するための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;
    }
}
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]。最初に6を4と交換します。したがって、B []-> [1,2,4,6,8,7,9]です。次に7で8。したがって-> [1,2,4,6,7,8,9]

アルゴリズムは、O(インデックスiの値<インデックスi-1の値)〜O(N)のペアの数)で実行されます。

1
user3245033

@ all、@ Itay karoおよび@NPEが提供する受け入れられたソリューションは完全に間違っています交換された要素の将来の順序を考慮しないため...

次のような多くのテストケースでfails

1 2 5 4

正しい出力:4

しかし、それらのコードは ...として出力を提供します.

説明: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
user3207754

O(1)space and O(N)(〜2 * N)solution 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;
}
0
Sumukha H S
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
user2867654