2

n のバイナリ表現に従って、n 個の要素を格納するために、それぞれの長さが 2^k の複数の配列を使用するという考え方です。

上記のデータ構造では、SEARCH は各配列のバイナリ検索のシーケンスによって実行されます。INSERT は、空の配列に到達するまで、同じ長さの配列の一連のマージによって実行されます。

詳細:長さ 2^k の垂直配列があり、その配列の各ノードに長さ 2^k の水平配列が接続されているとします。

つまり、垂直配列の最初のノードには、長さ 2^0=1 の水平配列が接続され、垂直配列の 2 番目のノードには、長さ 2^1= 2 の水平配列が接続されます。したがって、挿入は最初の水平配列で最初に実行され、2 番目の挿入では最初の配列は空になり、2 番目の水平配列は 2 つの要素でいっぱいになり、3 番目の挿入では 1 番目と 2 番目の配列水平になります。配列が満たされているなどです。次のように、検索と挿入の通常のバイナリ検索を実装しました。

int main()
  {
   int a[20]= {0}; 
    int n, i, j, temp;
    int *beg, *end, *mid, target;

   printf(" enter the total integers you want to enter (make it less then 20):\n");
    scanf("%d", &n);
   if (n >= 20) return 0;      
   printf(" enter the integer array elements:\n" );
    for(i = 0; i < n; i++)
    {

      scanf("%d", &a[i]);
    }

    // sort the loaded array, binary search! 
    for(i = 0; i < n-1; i++)    
    {  
      for(j = 0; j < n-i-1; j++)
      {
        if (a[j+1] < a[j])
       {
          temp = a[j];
          a[j] = a[j+1];
          a[j+1] = temp;
        }
      }
    }
    printf(" the sorted numbers are:");
    for(i = 0; i < n; i++)
    {
      printf("%d ", a[i]);
    }

    // point to beginning and end of the array
    beg = &a[0];
    end = &a[n];  // use n = one element past the loaded array!
    // mid should point somewhere in the middle of these addresses
    mid = beg += n/2;


    printf("\n enter the number to be searched:");
    scanf("%d",&target);

    // binary search, there is an AND in the middle of while()!!!
    while((beg <= end) && (*mid != target))
    {
      // is the target in lower or upper half?
      if (target < *mid)
      {
        end = mid - 1;     // new end
        n = n/2;
        mid = beg += n/2;  // new middle
      }
      else
      {
        beg = mid + 1;     // new beginning
        n = n/2;
        mid = beg += n/2;  // new middle      
      }
    }

    // find the target?
    if (*mid == target)
    {
      printf("\n %d found!", target);
    }
    else
   {
      printf("\n %d not found!", target);
    }

    getchar();  // trap enter
    getchar();  // wait
    return 0;
  }

上記のように機能する動的二分探索を実装するために、このプログラムまたは新しいプログラムを変更する方法を誰か提案してください!!

4

2 に答える 2

1

デザインを実装するためのより良い方法が一般的にあるので、宿題のようなにおいがします。

要件を明確にしましょう。連続する整数の配列が与えられた場合、それらは次の順序であると見なすことができます。

row 0: array[0] #
row 1: array[1] # #
row 2: array[3] # # # #
row 3: array[7] # # # # # # # #

私の理解によると、検索アルゴリズムは次のとおりです。

1.外部二分探索

最初の「列」に二分探索を適用します。結果は検索する行を見つけます。

2.行二分探索

行に二分探索を適用して、正確な値を見つけます。

アウターバイナリ検索

次のステップは、既存の二分探索アルゴリズムを変更して、配列レイアウトに従って「最低」および「最高」のインデックスを進めることです。

上記のレイアウトを見ると、各行の配列インデックスを持つパターンがあるように見えます。次のようになります:

  [Equation 1] index = power(2, row #) - 1

二分探索では、各反復で中間点が選択されます。これは、通常、次のように計算される、最高点と最低点の中間にあります。

[Equation 2} midpoint = ((highest - lowest) / 2) + lowest

理解を容易にするために、 行インデックス列インデックスの2つのインデックス規則を採用しましょう。行インデックスは、レイアウトに応じた行番号です。列インデックスは、行内の位置になります。上記のレイアウトには4行が含まれています。行2には4つの列があります。

したがって、行を見つけるために、中点式を使用します。

   row_midpoint = ((row_highest + row_lowest) / 2) + row_lowest

値を比較する前に、最初に値を特定する必要があります。位置は、row_midpoint値を式1に代入することによって取得されます。

array_midpoint_index = (1 << row_midpoint) - 1

次に、このarray_midpoint_indexを使用して値を取得します。value = array [array_midpoint_index]

計算の繰り返しを避けるために、例としてrow_low_valuerow_high_valueなどの値を保存することをお勧めします。

正確な行が見つかったら、...

行二分探索

行に適用される二分探索は、拡張二分探索です。拡張は、行の最初と最後の列の配列インデックスを決定します。これらの列インデックスは、式1を使用して計算できます。

詳細は読者の練習問題として残されています。
(ところで、写真や図を作成することは、コンピューターアルゴリズムであろうと物理学の文章題であろうと、問題に悩まされているときに常に役立つ方法です。)

データ構造の維持

このデータ構造の保守、要素の挿入と削除は、単一の配列として扱うことで最も簡単に実行できます。挿入インデックスが見つかったら、要素を下に移動して別の要素用のスペースを作り、新しい要素を挿入します。

より良いデータ構造

より良い[value, pointer, length]データ構造は、要素の配列を持つことかもしれません。ポインタは別の配列を指します。長さフィールドは、配列の長さを示します。これにより、値フィールドで標準の二分探索を使用できます。ポインタフィールドと長さフィールドを使用して、標準のバイナリ検索を配列に適用できます。便利なのは、CおよびC ++言語には標準のバイナリ検索機能が付属しており、書き換えに時間を浪費する必要がないことをすでにテスト済みです

于 2010-04-08T19:30:56.150 に答える
1

動的二分探索は実に優れたアルゴリズムです。参考文献は、Introduction to Algorithms (Cormen, Leiserson and Rivest) problem 18-2 であり、オンライン マージソート (Knuth TAOCP ex 5.2.4-17) と密接に関連しています。O(log(n)) 平均検索成功時間です。最悪の場合、検索の成功と失敗は両方とも O(log 2 (n)) です。また、バランス検索ツリーよりもコーディングがはるかに簡単です。

検索はかなり簡単です。何かが見つかるまで各行を検索するだけです (最大のものから始めます)。以下に挿入を実装しました。マージ ルーチンは、すべての並べ替えを行います。各行は int * であり、int (または NULL) の配列と同じであることに注意してください。高性能バージョンを作成している場合は、malloc と free が遅くなる傾向があるため、いくつかの小さなサイズの配列をキャッシュすることを検討します。


int *row[30];
int lastrow=0;
void dbs_insert(int v);
int *dbs_merge(int *a, int *b, int len);

void dbs_insert(int v) {
    int *new_row;
    int i;
    new_row=malloc(sizeof(int));
    new_row[0]=v;
    i=0;
    while (row[i]!=NULL) {
        new_row=dbs_merge(row[i],new_row,1<<i);
        row[i]=NULL;
        i++;
    }
    row[i]=new_row;
    if (i>lastrow) lastrow=i;
}

int *dbs_merge(int *a, int *b, int len) {
    int ai=0;
    int bi=0;
    int ci=0;
    int *c;
    c=malloc((2*len)*sizeof(int));
    while (ai <len && bi < len) {
        if (a[ai]<=b[bi]) {
            c[ci++]=a[ai++];
        } else {
            c[ci++]=b[bi++];
        }
    }
    while (ai<len)
        c[ci++]=a[ai++];
    while (bi<len)
        c[ci++]=b[bi++];
    free(a);
    free(b);
    return c;
}

于 2010-06-07T00:35:30.263 に答える