47

Reddit でこの質問を見ましたが、肯定的な解決策は提示されていませんでした。ここで質問するのに最適な質問だと思いました。これは、インタビューの質問に関するスレッドにありました。

サイズ m の int 配列を取り、配列が n...n+m-1 の数値、その範囲のすべての数値、およびその範囲の数値のみで構成されている場合に (True/False) を返すメソッドを作成します。配列がソートされる保証はありません。(たとえば、{2,3,4} は true を返します。{1,3,1} は false を返し、{1,2,4} は false を返します。

これに関して私が抱えていた問題は、インタビュアーが最適化 (より高速な O(n)、より少ないメモリなど) を私に求め続け、一定量のメモリー。それを理解したことがありません。

解決策とともに、配列に一意のアイテムが含まれていると想定しているかどうかを示してください。また、ソリューションがシーケンスが 1 から始まると想定しているかどうかも示してください。

編集:私は今、重複を処理する線形の時間と一定の空間アルゴリズムが存在しないという意見です。誰でもこれを確認できますか?

重複の問題は、O(n) 時間、O(1) 空間で配列に重複が含まれているかどうかを確認するテストに要約されます。これができれば、最初に簡単にテストできます。重複がない場合は、投稿されたアルゴリズムを実行します。では、O(n) 時間 O(1) 空間で重複をテストできますか?

4

39 に答える 39

19

1 未満の数値は許可されず、重複がないという仮定の下で、これには単純な合計 ID があります。 から までの数値の合計1はです。その後、配列を合計して、この ID を使用できます。m1(m * (m + 1)) / 2

上記の保証に加えて、数が m を超えていないか n 未満ではないという保証の下で重複があるかどうかを確認できます (これは でチェックできますO(N)) 。

疑似コードのアイデア:
0) N = 0 から開始
1) リストの N 番目の要素を取得します。
2) リストがソートされていて正しい場所にない場合は、あるべき場所を確認してください。
3) あるべき場所に既に同じ番号がある場合は、だまされています - RETURN TRUE
4) そうでない場合は、番号を交換します (最初の番号を正しい場所に配置するため)。
5) 差し替えた番号は合っていますか?
6) いいえの場合は、ステップ 2 に戻ります。
7) それ以外の場合は、N = N + 1 でステップ 1 から開始します。これがリストの最後を過ぎた場合、重複はありません。

そして、はい、それは次のO(N)ように見えるかもしれませんが実行されますO(N ^ 2)

皆さんへの注意事項(コメントから収集したもの)

このソリューションは、配列を変更できるという前提の下で機能し、インプレース基数ソートを使用します (これによりO(N)速度が向上します)。

他の数学的解決策が提示されていますが、それらのいずれかが証明されているかどうかはわかりません. 役に立つかもしれない合計がたくさんありますが、それらのほとんどは、合計を表すために必要なビット数の爆発に遭遇します。これは、一定の余分なスペースの保証に違反します。また、特定の数値セットに対して個別の数値を生成できるものがあるかどうかもわかりません。私は二乗和がうまくいくかもしれないと思う.

新しい洞察 (まあ、それを解決するのには役立たないものの、興味深いものであり、私は寝るつもりです):

したがって、おそらく合計+平方和を使用することが言及されています。これが機能するかどうかは誰にもわかりませんでしたが、2 + 2 = 1 + 3 のように (x + y) = (n + m) の場合にのみ問題になることに気付きました。ピタゴラス数 (3^2 + 4^2 + 25^2 == 5^2 + 7^2 + 24^2 となり、二乗和は機能しません)。フェルマーの最終定理を使用すると、これは n^3 では起こり得ないことがわかります。しかし、これに x + y + z = n が存在しないかどうかもわかりません (存在していて私が知らない場合を除きます)。したがって、これも壊れないという保証はありません。この道を進み続けると、すぐにビットが足りなくなります。

しかし、うれしくて、二乗和を破ることができることに注意するのを忘れていましたが、そうすると、有効ではない通常の合計が作成されます。両方できるとは思いませんが、すでに述べたように、どちらの方法でも証拠はありません。


反例を見つけることは、物事を証明することよりもはるかに簡単な場合があると言わざるを得ません! 次の数列を考えてみましょう。これらはすべて合計が 28 で、平方和が 140 です。

[1, 2, 3, 4, 5, 6, 7]
[1, 1, 4, 5, 5, 6, 6] 
[2, 2, 3, 3, 4, 7, 7]

長さ 6 以下の例は見つかりませんでした。適切な最小値と最大値も持つ例が必要な場合は、長さ 8 の次の例を試してください。

[1, 3, 3, 4, 4, 5, 8, 8]

より単純なアプローチ (hazzen のアイデアを変更):

長さ m の整数配列には、n から n+m-1 までのすべての数値が含まれます。

  • すべての配列要素は n と n+m-1 の間です
  • 重複はありません

(理由: 指定された整数範囲には m 個の値しかないため、配列にこの範囲内の m 個の一意の値が含まれている場合は、それらすべてを一度に含める必要があります)

配列の変更が許可されている場合は、hazzen のアルゴリズムのアイデアを変更したバージョンを使用して、リストを 1 回のパスで両方をチェックできます (合計を行う必要はありません)。

  • 0 から m-1 までのすべての配列インデックス i に対して、
    1. If array[i] < n or array[i] >= n+m => RETURN FALSE (「範囲外の値が見つかりました」)
    2. j = array[i] - n を計算します (これは、 n から n+m-1 までの値を持つ並べ替えられた配列内の array[i] の 0 ベースの位置です)
    3. j は i と等しくありませんが、
      1. list[i] が list[j] と等しい場合 => RETURN FALSE (「重複が見つかりました」)
      2. list[i] を list[j] と入れ替える
      3. j = array[i] - n を再計算します
  • TRUEを返す

元の配列の変更が O(1) の最大許容追加スペースにカウントされるかどうかはわかりませんが、そうでない場合、これは元の投稿者が望んでいた解決策になるはずです。

于 2008-10-07T03:25:38.347 に答える
6

a[i] % a.length代わりにを使用することで、問題を軽減して、 の数値をa[i]取得したかどうかを判断する必要があります。0a.length - 1

この観察結果を当然のこととして、配列に [0,m) が含まれているかどうかを確認しようとします。

正しい位置にない最初のノードを見つけます。

0 1 2 3 7 5 6 8 4 ;     the original dataset (after the renaming we discussed)
        ^
        `---this is position 4 and the 7 shouldn't be here

その番号をあるべき場所に入れ替えます。つまり、 を と交換7します8

0 1 2 3 8 5 6 7 4 ; 
        |     `--------- 7 is in the right place.
        `--------------- this is now the 'current' position

これを繰り返します。現在の位置をもう一度見てみましょう。

「これはここの正しい番号ですか?」

  • そうでない場合は、正しい場所に交換します。
  • 適切な場所にある場合は、右に移動してこれを繰り返します。

再びこの規則に従うと、次のようになります。

0 1 2 3 4 5 6 7 8 ;     4 and 8 were just swapped

これにより、リストが左から右に徐々に正しく構築され、各数値が最大 1 回移動されるため、これは O(n) になります。

重複がある場合は、リスト内の番号を交換しようとするとすぐにわかりますbackwards

于 2008-10-08T12:02:36.243 に答える
2

ワンパス アルゴリズムには、Omega(n) ビットのストレージが必要です。

逆に、o(n) ビットを使用するワンパス アルゴリズムが存在するとします。パスは 1 つしかないため、最初の n/2 値を o(n) スペースに要約する必要があります。C(n,n/2) = 2^Theta(n) S = {1,...,n} から引き出された n/2 値の可能なセットがあるため、n/ の 2 つの異なるセット A と B が存在します。メモリの状態が両方の後に同じになるような 2 つの値。A' = S \ A が A を補完する「正しい」値のセットである場合、アルゴリズムは入力に対して正しく答えることができない可能性があります

A A' - はい

B A' - いいえ

最初のケースと 2 番目のケースを区別できないためです。

QED

于 2008-10-14T05:44:49.300 に答える
2

他のソリューションがすべての値の合計を使用するのはなぜですか? O(n) 個のアイテムを 1 つの数値に追加すると、技術的には O(1) 個以上のスペースを使用することになるため、これは危険だと思います。

より簡単な方法:

ステップ 1、重複があるかどうかを調べます。これが O(1) 空間で可能かどうかはわかりません。とにかく、重複がある場合は false を返します。

ステップ 2、リストを繰り返し処理し、最下位最上位のアイテムを追跡します。

ステップ 3、(最高 - 最低) は m に等しいか? その場合は、true を返します。

于 2008-10-07T04:36:53.557 に答える
1

CでのHazzenのアルゴリズムの実装

#include<stdio.h>

#define swapxor(a,i,j) a[i]^=a[j];a[j]^=a[i];a[i]^=a[j];

int check_ntom(int a[], int n, int m) {
    int i = 0, j = 0;
    for(i = 0; i < m; i++) {
        if(a[i] < n || a[i] >= n+m) return 0;   //invalid entry
        j = a[i] - n;
        while(j != i) {
            if(a[i]==a[j]) return -1;           //bucket already occupied. Dupe.
            swapxor(a, i, j);                   //faster bitwise swap
            j = a[i] - n;
            if(a[i]>=n+m) return 0;             //[NEW] invalid entry
        }
    }
    return 200;                                 //OK
}

int main() {
    int n=5, m=5;
    int a[] = {6, 5, 7, 9, 8};
    int r = check_ntom(a, n, m);
    printf("%d", r);
    return 0;
}

編集:不正なメモリアクセスを排除するためにコードに加えられた変更。

于 2010-07-26T15:48:57.563 に答える
1
boolean determineContinuousArray(int *arr, int len)
{
    // Suppose the array is like below:
    //int arr[10] = {7,11,14,9,8,100,12,5,13,6};
    //int len = sizeof(arr)/sizeof(int);

    int n = arr[0];

    int *result = new int[len];
    for(int i=0; i< len; i++)
            result[i] = -1;
    for (int i=0; i < len; i++)
    {
            int cur = arr[i];
            int hold ;
            if ( arr[i] < n){
                    n = arr[i];
            }
            while(true){
                    if ( cur - n >= len){
                            cout << "array index out of range: meaning this is not a valid array" << endl;
                            return false;
                    }
                    else if ( result[cur - n] != cur){
                            hold = result[cur - n];
                            result[cur - n] = cur;
                            if (hold == -1) break;
                            cur = hold;

                    }else{
                            cout << "found duplicate number " << cur << endl;
                            return false;
                    }

            }
    }
    cout << "this is a valid array" << endl;
    for(int j=0 ; j< len; j++)
            cout << result[j] << "," ;
    cout << endl;
    return true;
}
于 2012-01-17T02:14:06.023 に答える
1

配列の長さだけを知っていて、配列を変更できると仮定すると、O(1) 空間と O(n) 時間で実行できます。

このプロセスには、2 つの簡単な手順があります。1.配列を「モジュロソート」します。[5,3,2,4] => [4,5,2,3] (O(2n)) 2. 各値の隣接値がそれ自体よりも 1 つ大きいことを確認します (モジュロ) (O(n))

アレイを通過するパスは最大で 3 つ必要です。

モジュロソートは「トリッキー」な部分ですが、目的は単純です。配列内の各値を取得し、それを独自のアドレス (モジュロ長) に格納します。これには、配列を 1 回通過する必要があり、各場所をループして、値を正しい場所にスワップし、値を移動先に移動することで値を「削除」します。追い出したばかりの値と一致する値に移動した場合、重複があり、早期に終了できます。最悪の場合、それは O(2n) です。

このチェックは、配列を 1 回通過することで、各値を 2 番目に高い隣接値で検査します。常にオン)。

組み合わせたアルゴリズムは O(n)+O(2n) = O(3n) = O(n)

私のソリューションからの疑似コード:

foreach(値[])
  while(values[i] i と合同ではありません)
    削除される = 値[i]
    evict(values[i]) // 「適切な」場所にスワップ
    if(values[i]%length == to-be-evicted%length)
      false を返します。// その番号を削除したときに「重複」が到着しました
  終了する
foreach を終了
foreach(値[])
  if((値[i]+1)%の長さ!=値[i+1]%の長さ)
    false を返す
foreach を終了

以下にJavaコードの概念実証を含めました。きれいではありませんが、作成したすべての単体テストに合格しています。私はこれらを「StraightArray」と呼んでいます。これは、これらがストレート (スーツを無視した連続シーケンス) のポーカー ハンドに対応するためです。

public class StraightArray {    
    static int evict(int[] a, int i) {
        int t = a[i];
        a[i] = a[t%a.length];
        a[t%a.length] = t;
        return t;
    }
    static boolean isStraight(int[] values) {
        for(int i = 0; i < values.length; i++) {
            while(values[i]%values.length != i) {
                int evicted = evict(values, i);
                if(evicted%values.length == values[i]%values.length) {
                    return false;
                }
            }
        }
        for(int i = 0; i < values.length-1; i++) {
            int n = (values[i]%values.length)+1;
            int m = values[(i+1)]%values.length;
            if(n != m) {
                return false;
            }
        }
        return true;
    }
}
于 2008-10-10T02:16:05.663 に答える
1

しばらく前に、電話会社で働いていた人から、非常に巧妙なソート アルゴリズムについて聞いたことがあります。彼らは膨大な数の電話番号を分類しなければなりませんでした。さまざまな並べ替え方法を試した後、彼らは最終的に非常に洗練された解決策にたどり着きました。ビット配列を作成し、ビット配列へのオフセットを電話番号として扱っただけです。次に、各番号のビットを 1 に変更する 1 回のパスでデータベースをスイープしました。その後、ビット配列を 1 回スイープし、ビットが高く設定されたエントリの電話番号を吐き出しました。

これらに沿って、配列自体のデータをメタデータ構造として使用して、重複を探すことができると思います。最悪の場合、別の配列を使用することもできますが、多少のスワッピングが気にならない場合は、入力配列を使用できると確信しています。

当分の間 n パラメーターを除外します。b/c は混乱を招くだけです。インデックス オフセットを追加するのは非常に簡単です。

検討:

for i = 0 to m
  if (a[a[i]]==a[i]) return false; // we have a duplicate
  while (a[a[i]] > a[i]) swapArrayIndexes(a[i], i)
  sum = sum + a[i]
next

if sum = (n+m-1)*m return true else return false

これは O(n) ではありません - おそらく O(n Log n) に近いですが、一定のスペースを提供し、問題に対して異なる攻撃ベクトルを提供する可能性があります。

O(n) が必要な場合、バイトの配列といくつかのビット操作を使用すると、余分な n/32 バイトのメモリが使用された重複チェックが提供されます (もちろん、32 ビットの整数を想定しています)。

編集: 上記のアルゴリズムは、合計チェックをループの内側に追加することでさらに改善でき、以下をチェックできます。

if sum > (n+m-1)*m return false

そうすれば、すぐに失敗します。

于 2008-10-07T04:52:50.387 に答える
1

間違っている場合は反対票を投じてください。ただし、重複があるかどうか、分散を使用していないかどうかを判断できると思います。事前に平均がわかっているので (n + (m-1)/2 など)、数と差の 2 乗を合計して、合計が式 (mn + m(m-1) と一致するかどうかを確認できます。 )/2) であり、分散は (0 + 1 + 4 + ... + (m-1)^2)/m です。分散が一致しない場合は、重複している可能性があります。

編集:要素の半分は平均よりも小さく、残りの半分は平均より大きい。

重複がある場合、別の重複が平均値の変化を完全にキャンセルしたとしても、上の式の項は正しいシーケンスとは異なります。したがって、関数は、合計と分散の両方が事前に計算できる望ましい値と一致する場合にのみ true を返します。

于 2008-10-07T05:33:14.080 に答える
1

これがO(n)の実用的なソリューションです

これは、Hazzen によって提案された疑似コードと、いくつかの私自身のアイデアを使用しています。負の数でも機能し、平方和は必要ありません。

function testArray($nums, $n, $m) {
    // check the sum. PHP offers this array_sum() method, but it's
    // trivial to write your own. O(n) here.
    if (array_sum($nums) != ($m * ($m + 2 * $n - 1) / 2)) {
        return false;    // checksum failed.
    }
    for ($i = 0; $i < $m; ++$i) {
        // check if the number is in the proper range
        if ($nums[$i] < $n || $nums[$i] >= $n + $m) {
            return false;  // value out of range.
        }

        while (($shouldBe = $nums[$i] - $n) != $i) {
            if ($nums[$shouldBe] == $nums[$i]) {
                return false;    // duplicate
            }
            $temp = $nums[$i];
            $nums[$i] = $nums[$shouldBe];
            $nums[$shouldBe] = $temp;
        }
    }
    return true;    // huzzah!
}

var_dump(testArray(array(1, 2, 3, 4, 5), 1, 5));  // true
var_dump(testArray(array(5, 4, 3, 2, 1), 1, 5));  // true
var_dump(testArray(array(6, 4, 3, 2, 0), 1, 5));  // false - out of range
var_dump(testArray(array(5, 5, 3, 2, 1), 1, 5));  // false - checksum fail
var_dump(testArray(array(5, 4, 3, 2, 5), 1, 5));  // false - dupe
var_dump(testArray(array(-2, -1, 0, 1, 2), -2, 5)); // true
于 2008-10-07T08:51:46.427 に答える
0

配列がソートされていない場合、「nickf」からの回答は機能しません var_dump(testArray(array(5, 3, 1, 2, 4), 1, 5)); //「重複」を与える!!!!

また、 sum([n...n+m-1]) を計算する式は正しくないようです....正しい式は (m(m+1)/2 - n(n-1)/2) です

于 2008-11-21T04:30:14.860 に答える
0

XOR アルゴリズムの反例。

(コメントとして投稿することはできません)

@ポポポメ

a = {0, 2, 7, 5,}それは返すため(が範囲 の順列であるtrueことを意味します)、しかしこの場合は返さなければなりません (は明らかに の順列ではありません)。a[0, 4)falsea[0, 4)

別の反例: {0, 0, 1, 3, 5, 6, 6}-- すべての値は範囲内ですが、重複があります。

popopome のアイデア (またはテスト) を誤って実装する可能性があるため、コードは次のとおりです。

bool isperm_popopome(int m; int a[m], int m, int  n)
{
  /** O(m) in time (single pass), O(1) in space,
      no restrictions on n,
      no overflow,
      a[] may be readonly
  */
  int even_xor = 0;
  int odd_xor  = 0;

  for (int i = 0; i < m; ++i)
    {
      if (a[i] % 2 == 0) // is even
        even_xor ^= a[i];
      else
        odd_xor ^= a[i];

      const int b = i + n;
      if (b % 2 == 0)    // is even
        even_xor ^= b;
      else
        odd_xor ^= b;
    }

  return (even_xor == 0) && (odd_xor == 0);
}
于 2008-10-08T23:33:34.307 に答える
0

Python の場合:

def ispermutation(iterable, m, n):
    """Whether iterable and the range [n, n+m) have the same elements.

       pre-condition: there are no duplicates in the iterable
    """ 
    for i, elem in enumerate(iterable):
        if not n <= elem < n+m:
            return False

    return i == m-1

print(ispermutation([1, 42], 2, 1)    == False)
print(ispermutation(range(10), 10, 0) == True)
print(ispermutation((2, 1, 3), 3, 1)  == True)
print(ispermutation((2, 1, 3), 3, 0)  == False)
print(ispermutation((2, 1, 3), 4, 1)  == False)
print(ispermutation((2, 1, 3), 2, 1)  == False)

時間は O(m)、空間は O(1) です。重複は考慮されません。

代替ソリューション:

def ispermutation(iterable, m, n): 
    """Same as above.

    pre-condition: assert(len(list(iterable)) == m)
    """
    return all(n <= elem < n+m for elem in iterable)
于 2008-10-10T01:26:16.183 に答える
0

配列に N 個の数値が含まれており、そのうちの 2 つの数値の合計が特定の数値 K になるかどうかを判断したいと考えています。 )。数字は 2 回使用できます。以下をせよ。a. この問題を解くための O(N2) アルゴリズムを与えてください。b. この問題を解くための O(N log N) アルゴリズムを与えてください。(ヒント: 最初に項目を並べ替えます。その後、線形時間で問題を解くことができます。) c. 両方のソリューションをコーディングし、アルゴリズムの実行時間を比較します。4.

于 2009-01-31T20:52:01.943 に答える
0

Greg Hewgill の基数ソートのアイデアが気に入っています。重複を見つけるには、この配列の値に対する制約を考慮して、O(N) 時間で並べ替えることができます。

リストの元の順序を復元するインプレース O(1) スペース O(N) 時間の場合、その番号に対して実際のスワップを行う必要はありません。フラグでマークするだけです:

//Java: assumes all numbers in arr > 1
boolean checkArrayConsecutiveRange(int[] arr) {

// find min/max
int min = arr[0]; int max = arr[0]
for (int i=1; i<arr.length; i++) {
    min = (arr[i] < min ? arr[i] : min);
    max = (arr[i] > max ? arr[i] : max);
}
if (max-min != arr.length) return false;

// flag and check
boolean ret = true;
for (int i=0; i<arr.length; i++) {
    int targetI = Math.abs(arr[i])-min;
    if (arr[targetI] < 0) {
        ret = false; 
        break;
    }
    arr[targetI] = -arr[targetI];
}
for (int i=0; i<arr.length; i++) {
    arr[i] = Math.abs(arr[i]);
}

return ret;
}

指定された配列内にフラグを格納することは一種の不正行為であり、並列化ではうまく機能しません。私はまだ、O(N) 時間と O(log N) 空間で配列に触れずにそれを行う方法を考えようとしています。合計と最小二乗和 (arr[i] - arr.length/2.0)^2 に対してチェックすると、うまくいくように感じます。重複のない 0...m 配列についてわかっている特徴の 1 つは、それが均一に分散されていることです。それを確認するだけです。

今、私がそれを証明できれば。

階乗を含む上記のソリューションは、階乗自体を格納するために O(N) スペースを必要とすることに注意してください。ん!> 2^N、格納に N バイトかかります。

于 2009-06-17T21:35:03.843 に答える
0

ciphwn は正しいです。それはすべて統計と関係があります。問題が問うているのは、統計用語で言えば、一連の数が離散的な一様分布を形成するかどうかです。離散一様分布は、可能な値の有限セットのすべての値が等しく確率が高い場合です。幸いなことに、離散集合が一様かどうかを判断するための便利な公式がいくつかあります。まず、集合 (a..b) の平均を (a+b)/2、分散を (nn-1)/12 と決定します。次に、与えられたセットの分散を決定します。

variance = sum [i=1..n] (f(i)-mean).(f(i)-mean)/n

次に、予想される分散と比較します。これには、平均を決定するためと分散を計算するために、データを 2 回渡す必要があります。

参考文献:

于 2008-10-07T09:52:57.303 に答える
0

これは、重複を見つけるための O(N) 時間と O(1) 余分なスペースでの解決策です:-

public static boolean check_range(int arr[],int n,int m) {

        for(int i=0;i<m;i++) {
            arr[i] = arr[i] - n;
            if(arr[i]>=m)
                return(false);
        }

        System.out.println("In range");

        int j=0;
        while(j<m) {
            System.out.println(j);
            if(arr[j]<m) {

                if(arr[arr[j]]<m) {

                    int t = arr[arr[j]];
                    arr[arr[j]] = arr[j] + m;
                    arr[j] = t;
                    if(j==arr[j]) {

                        arr[j] = arr[j] + m;
                        j++;
                    }

                }

                else return(false);

            }

            else j++;

        }

説明:-

  1. arr[i] = arr[i] - n によって数値を範囲 (0,m-1) にします。範囲外の場合は false を返します。
  2. それぞれについて、arr [arr [i]] が占有されていないかどうか、つまり m 未満の値を持っているかどうかを確認します
  3. もしそうなら swap(arr[i],arr[arr[i]]) と arr[arr[i]] = arr[arr[i]] + m 占有されていることを知らせる
  4. arr[j] = j の場合、単純に m を加算して j をインクリメントします
  5. arr[arr[j]] >=m の場合は、占有されていることを意味し、現在の値が重複しているため、false を返します。
  6. arr[j] >= m の場合はスキップ
于 2014-02-11T09:36:30.120 に答える
0

これを考えると -

サイズ m ...のint 配列を取るメソッドを作成します。

最大のintの値(2^32 が典型的)に等しい m の上限があると結論付けるのは公平だと思います。つまり、m が int として指定されていなくても、配列が重複できないという事実は、32 ビットから形成できる値の数を超えることはできないことを意味します。つまり、m がint にも制限されます。

このような結論が受け入れられる場合、(2^33 + 2) * 4 バイト = 34,359,738,376 バイト = 34.4GB の固定スペースを使用して、考えられるすべてのケースを処理することを提案します。(入力配列とそのループに必要なスペースは数えません)。

もちろん、最適化のために、最初に m を考慮し、実際に必要な量 (2m+2) * 4 バイトのみを割り当てます。

これが O(1) スペースの制約 (前述の問題) で許容できる場合は、アルゴリズムの提案に進みます... :)

仮定: m int の配列、正または負、4 バイトを超えることはありません。重複は処理されます。最初の値は任意の有効な int にすることができます。上記のように m を制限します。

まず、長さ 2m-1 の int 配列aryを作成し、 3 つの int 変数leftdiff、およびrightを指定します。2m+2 になることに注意してください...

次に、入力配列から最初の値を取得し、それを新しい配列の位置 m-1 にコピーします。3 つの変数を初期化します。

  • set ary [m-1] - nthVal // n=0
  • 左に設定=== 0

3 番目に、入力配列の残りの値をループし、反復ごとに次の操作を行います。

  • set diff = nthVal - ary [m-1]
  • if ( diff > m-1 + right || diff < 1-m + left ) return false // 範囲外
  • if ( ary [m-1+ diff ] != null) return false // 複製
  • set ary [m-1+ diff ] = nthVal
  • if ( diff > left ) left = diff // 左境界をさらに右に制限します
  • if ( diff < right ) right = diff // 右境界をさらに左に制限します

これをコードに入れることにしましたが、うまくいきました。

C# を使用した実際のサンプルを次に示します。

public class Program
{
    static bool puzzle(int[] inAry)
    {
        var m = inAry.Count();
        var outAry = new int?[2 * m - 1];
        int diff = 0;
        int left = 0;
        int right = 0;
        outAry[m - 1] = inAry[0];
        for (var i = 1; i < m; i += 1)
        {
            diff = inAry[i] - inAry[0];
            if (diff > m - 1 + right || diff < 1 - m + left) return false;
            if (outAry[m - 1 + diff] != null) return false;
            outAry[m - 1 + diff] = inAry[i];
            if (diff > left) left = diff;
            if (diff < right) right = diff;
        }
        return true;
    }

    static void Main(string[] args)
    {
        var inAry = new int[3]{ 2, 3, 4 };
        Console.WriteLine(puzzle(inAry));
        inAry = new int[13] { -3, 5, -1, -2, 9, 8, 2, 3, 0, 6, 4, 7, 1 };
        Console.WriteLine(puzzle(inAry));
        inAry = new int[3] { 21, 31, 41 };
        Console.WriteLine(puzzle(inAry));
        Console.ReadLine();
    }

}
于 2008-10-07T08:30:05.393 に答える
0

おっとっと!私は重複した質問に巻き込まれ、すでに同一の解決策がここに表示されませんでした。そして、ついにオリジナルの何かをやったと思いました!これは、私が少し喜んでいたときの歴史的なアーカイブです。


このアルゴリズムがすべての条件を満たしているかどうかはわかりません。実際、私が試したいくつかのテストケースを超えて機能することを検証していません. 私のアルゴリズムに問題があったとしても、うまくいけば、私のアプローチがいくつかの解決策のきっかけとなることを願っています。

私の知る限り、このアルゴリズムは定数メモリで動作し、配列を 3 回スキャンします。おそらく追加のボーナスは、それが元の問題の一部でなかった場合、整数の全範囲で機能することです。

私はあまり疑似コードの人ではなく、コードは単に言葉よりも意味があると本当に思っています。これは私がPHPで書いた実装です。コメントに注意してください。

function is_permutation($ints) {

  /* Gather some meta-data. These scans can
     be done simultaneously */
  $lowest = min($ints);
  $length = count($ints);

  $max_index = $length - 1;

  $sort_run_count = 0;

  /* I do not have any proof that running this sort twice
     will always completely sort the array (of course only
     intentionally happening if the array is a permutation) */

  while ($sort_run_count < 2) {

    for ($i = 0; $i < $length; ++$i) {

      $dest_index = $ints[$i] - $lowest;

      if ($i == $dest_index) {
        continue;
      }

      if ($dest_index > $max_index) {
        return false;
      }

      if ($ints[$i] == $ints[$dest_index]) {
        return false;
      }

      $temp = $ints[$dest_index];
      $ints[$dest_index] = $ints[$i];
      $ints[$i] = $temp;

    }

    ++$sort_run_count;

  }

  return true;

}
于 2010-05-20T21:46:34.980 に答える
0

Kent Fredric の Ruby ソリューションのAC バージョン

(テストを容易にするため)

反例 (C 版の​​場合): {8, 33, 27, 30, 9, 2, 35, 7, 26, 32, 2, 23, 0, 13, 1, 6, 31, 3, 28, 4, 5、18、12、2、9、14、17、21、19、22、15、20、24、11、10、16、25}。ここで n=0、m=35 です。このシーケンスは欠落34しており、2 つのがあり2ます。

これは、時間では O(m)、空間では O(1) のソリューションです。

範囲外の値は、時間では O(n)、空間では O(1) で簡単に検出されるため、テストは範囲内 (すべての値が有効な範囲内にあることを意味します[n, n+m)) シーケンスに集中します。それ以外の場合{1, 34}は反例です(C バージョンの場合、sizeof(int)==4、数値の標準バイナリ表現)。

C と Ruby のバージョンの主な違い: <<演算子は C では sizeof(int) が有限であるため値をローテーションしますが、Ruby では数値が結果に合わせて大きくなります。

ルビー:1 << 100 # -> 1267650600228229401496703205376

子:int n = 100; 1 << n // -> 16

Ruby では:check_index ^= 1 << i;と同等check_index.setbit(i)です。同じ効果を C++ で実装できます。vector<bool> v(m); v[i] = true;

bool isperm_fredric(int m; int a[m], int m, int n)
{
  /**
     O(m) in time (single pass), O(1) in space,
     no restriction on n,
     ?overflow?
     a[] may be readonly
   */
  int check_index = 0;
  int check_value = 0;

  int min = a[0];
  for (int i = 0; i < m; ++i) {

    check_index ^= 1 << i;
    check_value ^= 1 << (a[i] - n); //

    if (a[i] < min)
      min = a[i];
  }
  check_index <<= min - n; // min and n may differ e.g., 
                           //  {1, 1}: min=1, but n may be 0.
  return check_index == check_value;
}

上記の関数の値は、次のコードに対してテストされました。

bool *seen_isperm_trusted  = NULL;
bool isperm_trusted(int m; int a[m], int m, int n)
{
  /** O(m) in time, O(m) in space */

  for (int i = 0; i < m; ++i) // could be memset(s_i_t, 0, m*sizeof(*s_i_t));
    seen_isperm_trusted[i] = false;

  for (int i = 0; i < m; ++i) {

    if (a[i] < n or a[i] >= n + m)
      return false; // out of range

    if (seen_isperm_trusted[a[i]-n])
      return false; // duplicates
    else
      seen_isperm_trusted[a[i]-n] = true;
  }

  return true; // a[] is a permutation of the range: [n, n+m)
}

入力配列は次のように生成されます。

void backtrack(int m; int a[m], int m, int nitems)
{
  /** generate all permutations with repetition for the range [0, m) */
  if (nitems == m) {
    (void)test_array(a, nitems, 0); // {0, 0}, {0, 1}, {1, 0}, {1, 1}
  }
  else for (int i = 0; i < m; ++i) {
      a[nitems] = i;
      backtrack(a, m, nitems + 1);
    }
}
于 2008-10-09T17:29:52.340 に答える
0
def test(a, n, m):
    seen = [False] * m
    for x in a:
        if x < n or x >= n+m:
            return False
        if seen[x-n]:
            return False
        seen[x-n] = True
    return False not in seen

print test([2, 3, 1], 1, 3)
print test([1, 3, 1], 1, 3)
print test([1, 2, 4], 1, 3)

に含まれる線形検索を考慮せずに、これは最初の配列を 1 回だけ通過させることに注意してくださいnot in。:)

python を使用することもできsetましたが、パフォーマンス特性をset考慮する必要がない単純なソリューションを選択しました。

更新: Smashery は、「一定量のメモリ」を誤って解析したことを指摘しましたが、この解決策は実際には問題を解決しません。

于 2008-10-07T03:26:18.253 に答える
0

私の現在の最良の選択肢

def uniqueSet( array )
  check_index = 0; 
  check_value = 0; 
  min = array[0];
  array.each_with_index{ |value,index|
         check_index = check_index ^ ( 1 << index );
         check_value = check_value ^ ( 1 << value );
         min = value if value < min
  } 
  check_index =  check_index  << min;
  return check_index == check_value; 
end

O(n) とスペース O(1)

失敗する可能性のある力ずくの組み合わせを作成するスクリプトを作成しましたが、何も見つかりませんでした。この関数に違反する配列がある場合は、教えてください。:)


@JFセバスチャン

真のハッシュアルゴリズムではありません。技術的には、「見られた」値の非常に効率的なパックされたブール配列です。

ci = 0, cv = 0
[5,4,3]{ 
  i = 0 
  v = 5 
  1 << 0 == 000001
  1 << 5 == 100000
  0 ^ 000001  = 000001
  0 ^ 100000  = 100000

  i = 1
  v = 4 
  1 << 1 == 000010
  1 << 4 == 010000
  000001 ^ 000010  = 000011
  100000 ^ 010000  = 110000 

  i = 2
  v = 3 
  1 << 2 == 000100
  1 << 3 == 001000
  000011 ^ 000100  = 000111
  110000 ^ 001000  = 111000 
}
min = 3 
000111 << 3 == 111000
111000 === 111000

これのポイントは、ほとんどの問題ケースを「偽造」するために、重複を使用することです。このシステムでは、XOR は同じ値を 2 回使用するとペナルティを課し、代わりに 0 回使用したと想定します。

もちろん、ここでの注意事項は次のとおりです。

  1. $x入力配列の長さと配列の最大値の両方が、 inの最大値によって制限されます( 1 << $x > 0 )
  2. 最終的な有効性は、基盤となるシステムが次の機能をどのように実装するかによって異なります。

    1. 1 ビット n 桁右にシフトします。
    2. xor 2 レジスタ。(「レジスタ」は、実装によっては、複数のレジスタにまたがる場合があります)

    編集 注意、上記のステートメントは紛らわしいようです。「整数」が無限精度のレジスタであり、O(1) 時間で a ^ b を実行できる完全なマシンを想定します。

しかし、これらの仮定に失敗すると、単純な数学のアルゴリズムの複雑さを問い始める必要があります。

  • 1 == 1 はどのくらい複雑ですか? 必ず O(1) になるはずですよね?.
  • 2^32 == 2^32 はどうでしょう。
  • O(1)? 2^33 == 2^33? ここで、レジスターのサイズとその基礎となる実装について質問があります。
  • 幸いなことに、XOR と == は並行して実行できるため、無限の精度と、無限の精度に対処するように設計されたマシンを想定する場合、XOR と == の値に関係なく一定の時間がかかると想定しても安全です (幅が無限であるため、無限の 0 パディングがあります. 明らかにこれは存在しません. しかしまた, 000000 を 000100 に変更してもメモリ使用量は増加しません.
  • ただし、一部のマシンでは、 ( 1 << 32 ) << 1より多くのメモリを消費しますが、その量は不明です。
于 2008-10-07T03:36:14.473 に答える
0

:このコメントは質問の元のテキストに基づいています(その後修正されました)

質問が上記とまったく同じように提起され (単なるタイプミスではない)、サイズ n の配列の場合、配列が数値 1...n+1 で構成されている場合、関数は (True/False) を返す必要があります。

...すべての数字 1...n+1 を含む配列のサイズは n ではなく n+1 になるため、答えは常に false になります。したがって、質問は O(1) で答えることができます。:)

于 2008-10-07T04:00:48.650 に答える
0

他のソリューションがすべての値の合計を使用するのはなぜですか? O(n) 個のアイテムを 1 つの数値に追加すると、技術的には O(1) 個以上のスペースを使用することになるため、これは危険だと思います。

O(1) は、n の数だけ変化しない一定の空間を示します。定数であれば1変数でも2変数でも構いません。O(1) スペース以上だと言うのはなぜですか? 一時変数に累積して n 個の数値の合計を計算している場合は、とにかく 1 つの変数を使用することになります。

システムではまだコメントを書くことができないため、回答にコメントします。

更新(コメントへの返信):この回答では、「スペース」または「時間」が省略された場所ではO(1)スペースを意味していました。引用されたテキストは、これが返信である以前の回答の一部です。

于 2008-10-07T04:55:13.377 に答える
0

数値の合計を知りたい場合は、[n ... n + m - 1]この式を使用してください。

var sum = m * (m + 2 * n - 1) / 2;

これは、n が 10 進数であっても、正または負の任意の数値に対して機能します。

于 2008-10-07T04:55:31.880 に答える
0

私は次のことを提案します。

素数 P_1、P_2、...、P_Kの有限集合を選択し、各 P_i を法として入力シーケンス (最小値を差し引いたもの) 内の要素の出現を計算します。有効なシーケンスのパターンは既知です。

たとえば、17 個の要素のシーケンスの場合、モジュロ 2 では [9 8]、モジュロ 3: [6 6 5]、モジュロ 5: [4 4 3 3 3] などのプロファイルが必要です。

いくつかのベースを使用してテストを組み合わせると、より正確な確率テストが得られます。エントリは整数サイズによって制限されているため、正確なテストを提供する有限のベースが存在します。これは、確率的疑似素数性テストに似ています。

S_i is an int array of size P_i, initially filled with 0, i=1..K
M is the length of the input sequence
Mn = INT_MAX
Mx = INT_MIN

for x in the input sequence:
  for i in 1..K: S_i[x % P_i]++  // count occurrences mod Pi
  Mn = min(Mn,x)  // update min
  Mx = max(Mx,x)  // and max

if Mx-Mn != M-1: return False  // Check bounds

for i in 1..K:
  // Check profile mod P_i
  Q = M / P_i
  R = M % P_i
  Check S_i[(Mn+j) % P_i] is Q+1 for j=0..R-1 and Q for j=R..P_i-1
  if this test fails, return False

return True
于 2009-05-14T14:28:57.780 に答える
0

b3 の疑似コードのAC バージョン

(疑似コードの誤解を避けるため)

反例: {1, 1, 2, 4, 6, 7, 7}.

int pow_minus_one(int power)
{
  return (power % 2 == 0) ? 1 : -1;
}

int ceil_half(int n)
{
  return n / 2 + (n % 2);
}

bool isperm_b3_3(int m; int a[m], int m, int n)
{
  /**
     O(m) in time (single pass), O(1) in space,
     doesn't use n
     possible overflow in sum
     a[] may be readonly
   */
  int altsum = 0;
  int mina = INT_MAX;
  int maxa = INT_MIN;

  for (int i = 0; i < m; ++i)
    {
      const int v = a[i] - n + 1; // [n, n+m-1] -> [1, m] to deal with n=0
      if (mina > v)
        mina = v;
      if (maxa < v)
        maxa = v;

      altsum += pow_minus_one(v) * v;
    }
  return ((maxa-mina == m-1)
          and ((pow_minus_one(mina + m-1) * ceil_half(mina + m-1)
                - pow_minus_one(mina-1) * ceil_half(mina-1)) == altsum));
}
于 2008-10-09T19:22:53.187 に答える
0

したがって、入力配列を変更する必要がなく、一定のスペースを取る O(n^2) を取るアルゴリズムがあります。

nまず、 と を知っていると仮定しますm。これは線形操作であるため、複雑さが増すことはありません。n次に、 に等しい 1 つの要素と に等しい 1 つの要素が存在n+m-1し、残りはすべて にあるとし[n, n+m)ます。それを考えると、問題を の要素を持つ配列に減らすことができます[0, m)

ここで、要素が配列のサイズによって制限されていることがわかっているので、各要素を別の要素への単一のリンクを持つノードとして扱うことができます。つまり、配列は有向グラフを記述します。この有向グラフでは、重複する要素がない場合、すべてのノードがサイクルに属します。つまり、ノードはそれ自体からm以下のステップで到達可能です。重複する要素がある場合、それ自体からはまったく到達できないノードが 1 つ存在します。

したがって、これを検出するには、配列全体を最初から最後まで調べて、各要素が段階的にそれ自体に戻るかどうかを判断し<=mます。ステップで要素に到達できない<=m場合は、重複があり、false を返すことができます。それ以外の場合は、すべての要素へのアクセスが終了したら、true を返すことができます。

for (int start_index= 0; start_index<m; ++start_index)
{
    int steps= 1;
    int current_element_index= arr[start_index];
    while (steps<m+1 && current_element_index!=start_index)
    {
        current_element_index= arr[current_element_index];
        ++steps;
    }

    if (steps>m)
    {
        return false;
    }
}

return true;

追加情報を保存することで、これを最適化できます。

  1. 各要素からのサイクルの長さの合計を記録します。サイクルがその要素の前の要素を訪問しない限り、それを と呼びますsum_of_steps
  2. すべての要素について、m-sum_of_stepsノードのみをステップ アウトします。開始要素に戻らず、開始要素の前の要素にアクセスしていない場合は、重複する要素を含むループが見つかり、 を返すことができfalseます。

これはまだ O(n^2) です。たとえば{1, 2, 3, 0, 5, 6, 7, 4}ですが、少し高速です。

于 2010-05-21T22:07:30.503 に答える
0

連続する配列 [ n, n+1, ..., n+m-1 ] は、モジュロ演算子を使用して「ベース」間隔 [ 0, 1, ..., m ] にマッピングできます。間隔内の各 i に対して、基本間隔内に i%m が 1 つだけ存在し、その逆も同様です。

連続する配列には、そのサイズに等しい「スパン」 m (最大 - 最小 + 1) もあります。

これらの事実を使用して、最初にすべての false を含む同じサイズの「検出された」ブール配列を作成し、入力配列にアクセスしているときに、関連する「検出された」要素を true に設定できます。

このアルゴリズムは、空間では O(n)、時間では O(n) であり、重複をチェックします。

def contiguous( values )
    #initialization
    encountered = Array.new( values.size, false )
    min, max = nil, nil
    visited = 0

    values.each do |v|

        index = v % encountered.size

        if( encountered[ index ] )
            return "duplicates"; 
        end

        encountered[ index ] = true
        min = v if min == nil or v < min
        max = v if max == nil or v > max 
        visited += 1
    end

    if ( max - min + 1 != values.size ) or visited != values.size
        return "hole"
    else
        return "contiguous"
    end

end

tests = [ 
[ false, [ 2,4,5,6 ] ], 
[ false, [ 10,11,13,14 ] ] , 
[ true , [ 20,21,22,23 ] ] , 
[ true , [ 19,20,21,22,23 ] ] ,
[ true , [ 20,21,22,23,24 ] ] ,
[ false, [ 20,21,22,23,24+5 ] ] ,
[ false, [ 2,2,3,4,5 ] ]
]

tests.each do |t|
    result = contiguous( t[1] )
    if( t[0] != ( result == "contiguous" ) )
        puts "Failed Test : " + t[1].to_s + " returned " + result
    end
end
于 2009-05-15T10:28:27.023 に答える
-1

元の投稿(実線の下)で自分自身をうまく説明できなかったと思います。たとえば、[1 2 3 4 5]の入力の場合、アルゴリズムは次の合計を計算します。

-1 + 2 - 3 + 4 - 5 

これは等しいはずです

-1^5 * ceil(5/2)

以下の擬似コードは、1で始まらないベクトルがどのようにチェックされるかを示しています。 アルゴリズムは、入力ベクトルがソートされていない場合や重複が含まれている場合を処理します。


次のアルゴリズムは、ベクトル要素の交互の合計を計算することによって問題を解決します。

-1 + 2 - 3 + 4 - 5 + .... + m = (-1)^m * ceil(m/2)

ここで、ceilは最も近い整数に切り上げられます。つまり、現在の合計から奇数が減算され、偶数が加算されます。

function test(data, m)
    altSum = 0
    n = Inf
    mCheck = -Inf
    for ii = 1:m
    {
        if data(ii) < n
            n = data(ii)
        if data(ii) > mCheck
            mCheck = data(ii)
        altSum = altSum + (-1)^data(ii) * data(ii)
    }
    if ((mCheck-n+1!=m) || (-1)^(n+m-1) * ceil((n+m-1)/2) - ((-1)^(n-1) * ceil((n-1)/2)) != altSum
        return false
    else
        return true
于 2008-10-07T08:17:24.203 に答える
-1

時間的に線形、空間ソリューションでは一定int m

一定のスペースは、符号ビットを活用することによって実現されます。が正でない場合、つまり、入力範囲を範囲にシフトできる場合は、任意の可変int範囲に対して実行できます。実際には、入力が変更可能な場合、前提条件はほとんどの場合真です。mINT_MAX[n, n+m)[1, m+1)n

/** gcc -std=c99 ... */
#include <assert.h>
#include <iso646.h>  // and, or
#include <limits.h>  // INT_MIN
#include <stdbool.h> // bool
#include <stdlib.h>  // abs()

bool inrange(int m; int a[m], int m, int n)
{
  /** Whether min(a[]) == n and max(a[]) == n+(m-1)
  */
  if (m == 0) return true; // empty range is a valid range

  // check out-of-range values
  int max = INT_MIN, min = INT_MAX;
  for (int i = 0; i < m; ++i) {
    if (min > a[i]) min = a[i];
    if (max < a[i]) max = a[i];
  }
  return (min == n and max == n+(m-1));
}

bool isperm_minus2(int m; int a[m], int m, int n)
{
  /** O(m) in time, O(1) in space (for 'typeof(m) == typeof(*a) == int')

      Whether a[] is a permutation of the range [n, n+m).

      feature: It marks visited items using a sign bit.
  */
  if (not inrange(a, m, n))
    return false; // out of range

  assert((INT_MIN - (INT_MIN - 1)) == 1); // check n == INT_MIN
  for (int *p = a; p != &a[m]; ++p) {
    *p -= (n - 1); // [n, n+m) -> [1, m+1)
    assert(*p > 0);
  }

  // determine: are there duplicates
  bool has_duplicates = false;
  for (int i = 0; i < m; ++i) {
    const int j = abs(a[i]) - 1;
    assert(j >= 0);
    assert(j < m);
    if (a[j] > 0)
      a[j] *= -1; // mark
    else { // already seen
      has_duplicates = true;
      break;
    }
  }

  // restore the array
  for (int *p = a; p != &a[m]; ++p) {
    if (*p < 0) 
      *p *= -1; // unmark
    // [1, m+1) -> [n, n+m)
    *p += (n - 1);        
  }

  return not has_duplicates; // no duplicates? (+ inrange)
}
于 2008-11-22T16:39:52.300 に答える
-1

問題は要約すると、それを確実にすることだと思います

(maximum - minimum + 1) == array_size

これは明らかに、次のように O(N) 時間と O(1) 空間で実行できます。

int check_range(int input[], int N){
    int max = -INFINITY, min = INFINITY, i;
    for(i=0; i<N; i++){
        if(input[i] < min) min=input[i];
        if(input[i] > max) max=input[i];
    }
    return (max - min + 1) == N;
}

このアプローチは、重複の可能性を処理することに注意してください。解決策に矛盾がある場合は報告してください。

于 2011-10-22T14:08:01.787 に答える
-1

すべての数値 n...n+m を乗算し、その値を重複のない数列の予想される積m!/(n-1)!と比較することで、重複をチェックできるようです。(これは、シーケンスが期待合計テストと期待製品テストの両方に合格することは不可能であると想定していることに注意してください)。

したがって、hazzen の疑似コードに追加すると、次のようになります。

is_range(int[] nums, int n, int m) {
  sum_to_m := (m * (m + 1)) / 2
  expected_sum := sum_to_m - (n * (n - 1)) / 2
  real_sum := sum(nums)
  expected_product := m! / (n - 1)!
  real_product := product(nums)
  return ((real_sum == expected_sum) && (expected_product == real_product))


編集:平方和を使用して重複をチェックする Java での私のソリューションは次のとおりです。また、シーケンスをシフトして 1 から開始することにより、任意の範囲 (負の数を含む) を処理します。

// low must be less than high
public boolean isSequence(int[] nums, int low, int high) {
    int shift = 1 - low;
    low += shift;
    high += shift;

    int sum = 0;
    int sumSquares = 0;
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i] + shift;

        if (num < low)
            return false;
        else if (num > high)
            return false;

        sum += num;
        sumSquares += num * num;
    }

    int expectedSum = (high * (high + 1)) / 2;

    if (sum != expectedSum)
        return false;

    int expectedSumSquares = high * (high + 1) * (2 * high + 1) / 6;

    if (sumSquares != expectedSumSquares)
        return false;

    return true;
}
于 2008-10-07T04:12:04.027 に答える
-1

それがタイプミスであり、質問がすべての数値が範囲 1...n にあるということである場合は、次のようになります。

def try_arr(arr):
    n = len(arr)
    return (not any(x<1 or x>n for x in arr)) and sum(arr)==n*(n+1)/2

$ print try_arr([1,2,3])
True

$ print try_arr([1,3,1])
False

$ print try_arr([1,2,4])
False

ノート:

  • 数字は 1 から始まるという元のバージョンの定義を使用しています。別の数字から始まるようにコードを変更することもできます。

  • 配列のサイズ (n) がわかっている場合は、これを変更して、入力ファイルなどからデータをストリーミングし、メモリをほとんど使用しないようにすることができます (sum() 内の 1 つの一時変数と、ストリームから取得した現在のアイテム用の 1 つの変数)。

  • any() は python 2.5 の新機能です (ただし、以前のバージョンの python では同じことを表現する別の方法があります)。

  • O(n) 時間 O(1) スペースを使用します。(更新:重複を説明すると書きましたが、ここの別の回答へのコメントで示されているように、明らかにそうではありません)。

于 2008-10-07T04:20:32.667 に答える
-1
Fail := False;
Sum1 := 0;
Sum2 := 0;
TSum1 := 0;
TSum2 := 0;

For i := 1 to m do
  Begin
    TSum1 := TSum1 + i;
    TSum2 := TSum2 + i * i;
    Item := Array[i] - n;
    If (Item < 0) or (Item >= m) then 
      Fail := True
    Else 
      Begin
        Sum1 := Sum1 + Item;
        Sum2 := Sum2 + Item * Item;
      End;
  End;
Fail := Fail Or (Sum1 <> TSum1) or (Sum2 <> TSum2);

疲れていてコンパイラはありませんが、これにより O(m) ランタイムが得られ、だまされることはないと思います。

于 2008-10-07T04:46:24.753 に答える
-1

偶数と奇数を別々に XOR を使用するのはどうでしょうか。整数値自体ではなく、ビットレベルについて考えてください。

bool is_same(const int* a, const int* b, int len)
{
    int even_xor = 0; 
    int odd_xor = 0;

    for(int i=0;i<len;++i)
    {
        if(a[i] & 0x01) odd_xor ^= a[i];
        else even_xor ^= a[i];

        if(b[i] & 0x01) odd_xor ^= b[i];
        else even_xor ^= b[i];
    }

    return (even_xor == 0) && (odd_xor == 0);
}
于 2008-10-07T08:50:47.660 に答える
-2

合計を使用する必要はまったくないと思います。最小値と最大値を確認し、重複がないか確認してください。事前に n がわからないため、重複をチェックするのは難しい部分です。そのため、1 回のパスでソートすることはできません。これを回避するには、(edit:destination) 配列の条件を緩和します。並べ替えを要求する代わりに、並べ替えられたシーケンスの巡回シフトを行って、配列が [k,k+1,..., n+m-2, n+m-1,n,n+] になるようにします。 1, ... ,k-2,k-1] ある k に対して。

上記の条件では、a[0] が既に正しい位置にあると想定できます。その場合、要素の正しい位置dは であり(d-a[0]) mod m、ゼロベースの配列インデックスを想定しています。たとえば、[4,?,?,?] の場合、[4,5,6,7] または [4,1,2,3] または [4,5,6,3] または [4,5, 2,3]。

次に、配列を 1 回スキャンし、各要素を計算された位置に配置し、最小値と最大値を更新し、衝突をチェックします。衝突がなく、max-min=m の場合、条件が満たされます。それ以外の場合は false です。

于 2008-10-09T09:29:12.857 に答える