7

同じだが未知のソート順で任意にソートされた 3 つのリストがあるとします。これらのリストを 1 つにマージし、その後も同じ順序でソートするアルゴリズムはありますか?

例:

List1: a b c f h

List2: b c e h

List3: c d e f

これらのリストはソートされているが、使用されるソート順は不明であると仮定します。これらのリストを組み合わせて、重複を含まず、ソート順を維持した結果を出したい: a b c d e f h

上記のように、指定されたリストがソートされていることはわかっていますが、どの順序でソートされているかはわかりませんが、マージされたリストが同じ(不明な)順序でソートされていることが要件です。

上記の例では、要素 "f" が "e" と "h" の間に配置されていることがわかっています。List1 からわかっているからです。

"c" < "f" < "h",

リスト2から私はそれを知っています

"c" < "e" < "h"

そしてList3から私はそれを知っています

"e" < "f" および "c" < "e"

これは次のように結合します。

"c" < "e" < "f" < "h"

指定されたリストのいずれによってもソート順を決定できない場合は、要素を結果リストの末尾に追加するだけでかまいません。また、一連の要素の並べ替え順序を決定できない場合、それらが正しい場所にある限り、それらを任意の順序でリストに挿入することができます (たとえば、"b" と "c" が必要であることがわかっている場合)。 "a" と "d" の間に挿入されますが、それが abcd または acbd のどちらであるかはわかりません。両方が許容されます。)

もちろんこれはほんの一例です。実際のリストはより長く (ただし、含まれる要素は 100 未満)、単一ではなく複数の文字要素が含まれ、並べ替え順序はアルファベット順ではありません。また、私は最大5つのリストを持っています。

このアルゴリズムを Delphi で実装する必要があります (いいえ: これは宿題ではなく、実際の問題です)。ただし、コンパイラの魔法や複雑なライブラリ関数があまり含まれていない言語でアルゴリズムを使用します。

これは 1 回で済むため、パフォーマンスはそれほど問題になりません。

4

5 に答える 5

4

良い質問です。トポロジカル ソートが最も推奨される方法かもしれませんが、最初に入力を解析して依存関係リストを作成する必要があります。注文の定義を設定するために、複数のリストに含まれるアイテムを見つけることに基づいて、より直接的なアプローチを考えました。

時間の複雑さについては何も予測できませんが、パフォーマンスは気にしないので、特に項目の総数が最大で 500 であることを考慮すると、このアルゴリズムはうまく機能すると思います。

アルゴリズム

  • すべてのリストは一時的なリストにまとめられ、すべての重複項目を識別してふるいにかけるために自然に並べ替えられます。Keysという名前のこれらの複製は、最終的な並べ替え順序の唯一の定義を構成します。
  • キー リストは、2 つの項目ごとに比較することにより、入力の並べ替え順序で並べ替えられます。2 つのキーが同じ入力リストにある場合、そのリストの最初のキーが出力リストの 2 番目のキーよりも前になります。どの入力リストでも 2 つのキーが同時に発生しない場合、それらは等しいと見なされます。
  • その後、ループがキーを循環します。
  • サイクルごとに、各入力リスト内で、前のキーと現在のキーの間の各項目が出力リストに追加されます。サイクルは、現在のキーの追加で終了します。

実装

type
  TSorterStringList = class(TStringList)
  protected
    Id: Integer;
    KeyId: Integer;
    function Current: String;
  public
    constructor Create;
  end;

  TSorterStringLists = class(TObjectList)
  private
    function GetItem(Index: Integer): TSorterStringList;
  public
    property Items[Index: Integer]: TSorterStringList read GetItem; default;
  end;

  TSorter = class(TObject)
  private
    FInput: TSorterStringLists;
    FKeys: TStringList;
    procedure GenerateKeys;
    function IsKey(const S: String): Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Sort(Output: TStrings);
    property Input: TSorterStringLists read FInput;
  end;

{ TSorterStringList }

constructor TSorterStringList.Create;
begin
  inherited Create;
  KeyId := -1;
end;

function TSorterStringList.Current: String;
begin
  Result := Strings[Id];
end;

{ TSorterStringLists }

function TSorterStringLists.GetItem(Index: Integer): TSorterStringList;
begin
  if Index >= Count then
    Count := Index + 1;
  if inherited Items[Index] = nil then
    inherited Items[Index] := TSorterStringList.Create;
  Result := TSorterStringList(inherited Items[Index]);
end;

{ TSorter }

constructor TSorter.Create;
begin
  inherited Create;
  FInput := TSorterStringLists.Create(True);
  FKeys := TStringList.Create;
end;

destructor TSorter.Destroy;
begin
  FKeys.Free;
  FInput.Free;
  inherited Destroy;
end;

threadvar
  CurrentSorter: TSorter;

function CompareKeys(List: TStringList; Index1, Index2: Integer): Integer;
var
  Input: TSorterStringLists;
  I: Integer;
  J: Integer;
  K: Integer;
begin
  Result := 0;
  Input := CurrentSorter.Input;
  for I := 0 to Input.Count - 1 do
  begin
    J := Input[I].IndexOf(List[Index1]);
    K := Input[I].IndexOf(List[Index2]);
    if (J > - 1) and (K > -1) then
    begin
      Result := J - K;
      Break;
    end;
  end;
end;

procedure TSorter.GenerateKeys;
var
  All: TStringList;
  I: Integer;
begin
  All := TStringList.Create;
  try
    All.Sorted := True;
    All.Duplicates := dupAccept;
    for I := 0 to FInput.Count - 1 do
      All.AddStrings(FInput[I]);
    for I := 0 to All.Count - 2 do
      if (All[I] = All[I + 1]) then
        if (FKeys.Count = 0) or (FKeys[FKeys.Count - 1] <> All[I]) then
          FKeys.Add(All[I]);
  finally
    All.Free;
  end;
  CurrentSorter := Self;
  FKeys.CustomSort(CompareKeys);
end;

function TSorter.IsKey(const S: String): Boolean;
begin
  Result := FKeys.IndexOf(S) > -1;
end;

procedure TSorter.Sort(Output: TStrings);
var
  KeyId: Integer;
  I: Integer;
  List: TSorterStringList;
begin
  if FInput.Count = 0 then
    Exit;
  Output.BeginUpdate;
  try
    GenerateKeys;
    for KeyId := -1 to FKeys.Count - 1 do
    begin
      for I := 0 to FInput.Count - 1 do
      begin
        List := FInput[I];
        if List.KeyId <= KeyId then
          while (List.Id < List.Count) and not IsKey(List.Current) do
          begin
            Output.Add(List.Current);
            Inc(List.Id);
          end;
        while (List.Id < List.Count) and IsKey(List.Current) do
        begin
          List.KeyId := FKeys.IndexOf(List.Current);
          Inc(List.Id);
        end;
      end;
      if KeyId + 1 < FKeys.Count then
        Output.Add(FKeys[KeyId + 1]);
    end;
  finally
    Output.EndUpdate;
  end;
end;

使用例

procedure TForm1.Button1Click(Sender: TObject);
var
  Sorter: TSorter;
begin
  Sorter := TSorter.Create;
  try
    Sorter.Input[0].CommaText := '1, 2, 4, 9, 10, 11, 22, 46, 48, 51, 70, 72';
    Sorter.Input[1].CommaText := '3, 9, 23, 43, 44, 45, 47, 48, 51, 71, 90, 91';
    Sorter.Input[2].CommaText := '0, 3, 4, 7, 8, 11, 23, 50, 51, 52, 55, 70';
    Sorter.Input[3].CommaText := '2, 6, 14, 15, 36, 37, 38, 39, 51, 65, 66, 77';
    Sorter.Input[4].CommaText := '5, 27, 120, 130';
    ListBox1.Items.Assign(Sorter.Input[0]);
    ListBox2.Items.Assign(Sorter.Input[1]);
    ListBox3.Items.Assign(Sorter.Input[2]);
    ListBox4.Items.Assign(Sorter.Input[3]);
    ListBox5.Items.Assign(Sorter.Input[4]);
    Sorter.Sort(ListBox6.Items);
    // Results in:
    // 1, 0, 5, 27, 120, 130, 3, 2, 6, 14, 15, 36, 37, 38, 39, 4, 7, 8, 9, 10,
    // 11, 22, 46, 23, 43, 44, 45, 47, 50, 48, 51, 71, 90, 91, 52, 55, 65, 66,
    // 77, 70, 72
  finally
    Sorter.Free;
  end;
end;
于 2013-10-10T23:52:20.713 に答える
2

だからあなたは持っています

List1: a b c f h
List2: b c e h
List3: c d e f

リストごとに移動し、グラフに入ります。したがって、最初のリストの後に次のものがあります。

A -> B -> C -> F -> H

次に、リスト 2 から始めます。B は既にそこにあります。次に、B が既に知っている C に接続することがわかります。次に、 C が E に接続していることがわかりますが、これはまだそこにないため、次のようになります。

A -> B -> C -> F -> H
          |          
          E

次に、E が H に接続することがわかります。

A -> B -> C -> F -> H
          |         ^ 
          E --------|

次に、リスト 3 に移動します。C がそこにあり、D を指していることがわかります。

          D
          ^
          |
A -> B -> C -> F -> H
          |         ^ 
          E --------|

次に、D が E を指していることがわかります。C->E は C -> D -> E と同じ先祖を持つため、C-> E からのリンクを解除できます。したがって、次のようになります。

          D -> E ---|
          ^         |
          |         |
A -> B -> C -> F -> H

そして最後に、E が F の前に来ることを知っています。以前は E が H に直接つながっていたことを知っていたので、E から H への別のパス (E->F->H) があるため、F は E と H の間にある必要があることがわかります。 E -> H からリンクをドロップできます。したがって、次のようになります。

          D -> E
          ^    |     
          |    |     
A -> B -> C -> F -> H

あなたが知っていることは、次のように短縮できます

A -> B -> C -> D -> E -> F -> H

ここで、次のような結果になったとしましょう。

  E -> T
  |    |
  A -> Z
  |    ^
  R -> W

E/T が R/W の前に来るかどうかを判断するのに十分な情報はありませんが、両方が Z の前と A の後に来ることはわかっています。したがって、パスの 1 つをランダムに選択し、次に次のパスを選択するだけです。 、したがって、AETRWZ または ARWETZ のいずれかになる可能性があります。各パスからランダムに 1 つ取得することもできます。これにより、それらのレッグがソートされたままになることが保証され、マージもソートされたことが幸運になるかもしれません。したがって、E/T が相対的にソートされ、R/W が相対的にソートされている AREWTZ を使用することができます。または、E レッグから開始する場合は、運が良くて AERTWZ が得られます。

于 2013-10-10T16:05:48.467 に答える
1

グラフ理論は、最初の直感のように思えます。

リストの要素が頂点である有向グラフを作成し、各リスト要素から後続要素に有向辺を挿入できます。その場合、ノード A は、グラフをトラバースすることによって A から B に到達できる場合にのみ、別のノード Bよりも小さくなります。

グラフ内の循環 (A は B 未満、B は A 未満) は、入力データが破損しているか、名前が異なる 2 つの同等の要素が存在することを示します。

サイクルがない場合、与えられた小なり関係の下でのマージは簡単です。グラフから他のノードが到達できないノードを繰り返し削除し、それらを出力リストに追加します。

于 2013-10-10T15:21:47.617 に答える
0

ハッシュテーブルを使用できますか? このような 2 つのリストをマージするアルゴリズムを次に示します。

T = new HashMap
for(i = 1 to length B)
  T.put(B[i],i)
N = array[length A]
for(i = 1 to length A){
  if(T containsKey A[i])
    N[i] = T.get(A[i])
  else
    N[i] = -1
}

R = array[length A + length B]
j = 1
k = 1
for(i = 1 to length A){
  if(N[i] = -1)
    R[j++] = N[i]
  else{
    while(k <= N[i])
      R[j++] = B[k++]
  }
}
while(k <= length B)
  R[j++] = B[k++]
return R[1 ... j-1]

N[i]>0 の要素 A[i] は B の要素と一致し、その他は有効な順序で配置されます。そこにバグがあるかもしれませんが、これは一般的な考え方です。

3 つの配列をマージするには、最初の 2 つをマージしてから、3 つ目をマージした配列にマージします。 @RobKennedyが指摘したように、編集時にこの最後の文は誤りです。おそらくアルゴリズムを変更して 3 つのリストを処理することができますが、それほど単純ではないため、代わりにトポロジカル ソートを使用することをお勧めします。

于 2013-10-10T15:37:35.267 に答える