20

アルゴリズムのファイナルに関する最後の質問は、この 1 か月間私を夢中にさせてきました。質問は次のとおりです。

array がありA[0...n]、O(n) で実行されるアルゴリズムを (「適切な」疑似コードで) 記述します。このアルゴリズムは、この配列が既に何らかのインデックスに対して分割されているかどうかを判断できkますk。そうでない場合は -1 を返します。

明確にするために、によってPartition

の各要素について、 の「左」に配置する場合eは、そうでない場合は の「右」に配置します。A[0...n]e < A[k]eA[k]eA[k]

したがって、分割された配列の例 (wrt k = 11):

A = [4 2 5 3 7 4 2 6 8 4 11010 10 20 11 15 13 28 99 11]

それから

myAlgo(A) -> (11)

また

A = [10, 20, 30, 40, 11,100, 150, 101, 125]

それから

myAlgo(A) -> (5)

だがしかし:

A = [10, 20, 30, 40, 5]

myAlgo(A) -> (-1)

私の最初の考え(信じられないほどナイーブだった)は、文字通り言葉にできないほどひどかった. 基本的に、配列がソートされているかどうかを誤ってチェックし、かなりランダムな値を真ん中から取り出しました。

私の次の考えは、リストをスキャンし、最初にヒットした最大の数値を見つけてから、減少する数値をヒットしてそれらの数値をすべて除外することでした...基本的に最大値と最小値を保持し、両方の外にある場合は、可能性のあるパーティション インデックスをサブセットの最後にシフトします。

これを(テストケースで)実装しようと(非常にひどく)試みた場所は次のとおりです。

int myAlgo(const int* A, int n);

int main() {

    const int A[] = {10, 20, 30, 40, 11, 100, 150, 101, 125};

    int index;
    if((index = myAlgo(A, 9)) != -1) {
        printf("A[%d] = %d", index, A[index]);
    }
    else {
        printf("Not Partitioned >:/");
    }

    return 0;
}

int myAlgo(const int* A, int n) {
    // the index of the smallest possible number in the remainder of the list
    int minIdx = 0;

    // the index of the largest number we've encountered
    int maxIdx = 0;

    // index of possible partition "center"
    int kIdx = 0;

    bool isPart = false;

    for(int i=0; i < n; ++i) {
        if( A[maxIdx] <= A[i] )  {
            maxIdx = i;
            if(isPart == false)  { kIdx = i; minIdx = i;} // if we flipped then this is a good time to grab a partitioner index
            isPart = true;
        }
        else { isPart = false; minIdx = i; }
        printf("A[%d] = %d <==> A[%d]: %d : %c\n", maxIdx, A[maxIdx], i, A[i], (isPart?'T':'F'));
        if( A[minIdx] > A[i] ) { isPart = false; }
        printf("A[%d] = %d <==> A[%d]: %d : %c\n", minIdx, A[minIdx], i, A[i], (isPart?'T':'F'));
    }

    printf("A[%d] = %d : %c\n\n", kIdx, A[kIdx], (isPart?'T':'F'));

    // We gotta check this to make sure it is a valid list...
    if(isPart) return kIdx;
    else return -1;
}

しかし、当然のことながら、私の出力は次のとおりです。


A[0] = 10 <==> A[0]: 10 : T
A[0] = 10 <==> A[0]: 10 : T
A[1] = 20 <==> A[1]: 20 : T
A[0] = 10 <==> A[1]: 20 : T
A[2] = 30 <==> A[2]: 30 : T
A[0] = 10 <==> A[2]: 30 : T
A[3] = 40 <==> A[3]: 40 : T
A[0] = 10 <==> A[3]: 40 : T
A[3] = 40 <==> A[4]: 11 : F
A[4] = 11 <==> A[4]: 11 : F
A[5] = 100 <==> A[5]: 100 : T
A[5] = 100 <==> A[5]: 100 : T
A[6] = 150 <==> A[6]: 150 : T
A[5] = 100 <==> A[6]: 150 : T
A[6] = 150 <==> A[7]: 101 : F
A[7] = 101 <==> A[7]: 101 : F
A[6] = 150 <==> A[8]: 125 : F
A[8] = 125 <==> A[8]: 125 : F
A[5] = 100 : F                 <-- The index is right... but isPart is wrong

Not Partitioned >:/

今夜はぐっすり眠りたいので、ヒント/ヒント/アイデア/その他を教えていただければ幸いです。


ウー!@Amitは私の問題を解決するのに役立ちました.これが私の更新された関数です:

int partIdx2(const int* A, int n) {

    int* max = malloc(n * sizeof(int));
    int* min = malloc(n * sizeof(int));

    for(int i=0; i < n; i++)
    {
        if(i==0) {
            max[i] = A[i];
            min[n - 1] = A[n-1];
        }
        else {
            max[i] = MAX(max[i-1], A[i]);
            min[n - 1 - i] = MIN(min[n - 1 - i + 1], A[n - 1 - i]);
        }
    }

    for(int i=1; i < n-1; i++) {
        if(A[i] >= max[i-1] && A[i] <= min[i+1]) { 
            free(max);
            free(min);
            return i; 
        }
    }

    free(max);
    free(min);

    return -1;
}
4

2 に答える 2

20

O(n)時間と空間の解決策は、2 つの配列maxmin.

max[i] = max{arr[0],arr[1],...,arr[i]}
min[i] = min{arr[i],arr[i+1],...,arr[n-1]}

両方の配列を線形時間で作成できることに注意してください。

これらの配列を取得したら、次のkようなインデックスがあるかどうかを確認する必要があります。

arr[k] >= max[k-1] && arr[k] <= min[k+1]

これは線形時間でも実行できます

これが機能するのは、上記が成り立つ場合、後の各要素kは より高いか等しいことが保証され、それよりarr[k]前の各要素は より低いか等しいarr[k]であることが保証されているためです。これはほとんどパーティションの定義です。

于 2013-08-08T15:04:00.247 に答える
1

興味深い問題

余分なバッファスペースに頼ることなく、これを解決できるはずだと私には思えます。

ピボット要素がある場合は、

  • (不明な) ピボット位置の左側にあるすべての要素は、ピボット要素以下です
  • (不明な) ピボット位置の右側にあるすべての要素は、ピボット要素以上です

このことから、

  • ピボットの左側にあるすべての要素が、ピボットの右側にあるすべての要素よりも小さいか等しい。
  • ピボットの右側にあるすべての要素が、ピボットの左側にある任意の要素より大きいか等しい

これの特殊なケースは、

  • ピボットの左側にあるすべての要素は、最も右の要素以下です
  • ピボットの右側にあるすべての要素は、最も左の要素以上です

このような理論的根拠を使用すると、ピボット位置があれば、そこに再帰的に「ホームイン」できるはずです。

擬似コード:

Set highest value found on low side to value of first element
Set lowest value found on high side to value of last element
Set low index to first element
Set high index to last element
repeat
  increment low index
  if low index >= array length -> fail
  if value at new low index > highest so far on the low side
    set new highest-on-low-side value
      if new value greater than lowest value so far on right side,
        set low index back to what it was and mark it as stuck
        set highest-on-low-side value back to what it was
  decrement high index
  if high index < 0 -> fail
  if value at new high index < lowest so far on the high side
    set new lowest-on-high-side value
      if new value less than the highest value so far on the left side,
        set high index back to what it was and mark it as stuck
        set lowest-on-high-side value back to what it was
until both low and high index is stuck or until low index >= high index
if low index = high index
  pivot position = low index
else
  failure

これは、いくつかのテスト入力でこのアイデアを簡単に検証するために使用した実際の Pascal 実装ですが、現時点では本格的な検証を行う時間はありません。

function PivotIndex(a: array of integer): Integer;
var
  HighestValueOnLeftSide: Integer;
  LowestValueOnRightSide: Integer;
  LowIndex: Integer;
  HighIndex: Integer;
  LowStuck, HighStuck: Boolean;
begin
  HighestValueOnLeftSide := -1;
  LowestValueOnRightSide := MaxInt;
  LowIndex := -1;
  HighIndex := length(a);
  LowStuck := False;
  HighStuck := False;
  repeat
    if not LowStuck then begin
      inc(LowIndex);
      if LowIndex >= length(A) then begin
        Result := -1;
        exit;
      end;
      if A[LowIndex] > HighestValueOnLeftSide then
        if A[LowIndex] > LowestValueOnRightSide then begin
          LowStuck := True;
          dec(LowIndex);
        end else
          HighestValueOnLeftSide := A[LowIndex];
    end;
    if not HighStuck then begin
      dec(HighIndex);
      if HighIndex < 0 then begin
        Result := -1;
        exit;
      end;
      if A[HighIndex] < LowestValueOnRightSide then
        if A[HighIndex] < HighestValueOnLeftSide then begin
          HighStuck := True;
          inc(HighIndex);
        end else
          LowestValueOnRightSide := A[HighIndex];
    end;
  until LowStuck and HighStuck or (LowIndex >= HighIndex);
  if LowIndex = HighIndex then
    Result := LowIndex
  else
    Result := -1;
end;

これはもっとエレガントで効率的なものにできると確信していますが、差し迫った問題があればお知らせください。

于 2013-08-09T00:04:14.240 に答える