web-dev-qa-db-ja.com

O(n)時間内に重複する予定を見つけますか?

私は最近、面接でこの質問をされました。 [〜#〜] o [〜#〜]n²)解決策、インタビュアーは[〜#〜] o [〜#〜]n)ソリューション。また、[〜#〜] o [〜#〜]nlogn)理解しましたが、[〜#〜] o [〜# 〜]n)ソリューションは、開始時刻でソートされた予定を想定している私のお茶ではありません。

誰かがこれを説明できますか?

問題の説明:nの予定が与えられます。各予定には、開始時刻と終了時刻が含まれています。競合するすべての予定を効率的に再調整する必要があります。

人:1、2、3、4、5
アプリの開始:2、4、29、10、22
アプリの終了:5、7、34、11、36

回答:2x1 5x3

[〜#〜] o [〜#〜]nlogn)アルゴリズム:次のように開始点と終了点を分離します:

2秒、4秒、29秒、10秒、22秒、5e、7e、34e、11e、36e

次に、このすべてのポイントを並べ替えます(簡単にするために、各ポイントが一意であると仮定します)。

2s、4s、5e、7e、10s、11e、22s、29s、34e、36e

終了のない連続した開始がある場合、それはオーバーラップしています:2秒、4秒が隣接しているため、オーバーラップがあります

「s」のカウントを保持し、遭遇するたびに+1し、eに遭遇すると、カウントを1ずつ減らします。

23
Dude

この問題の一般的な解決策は不可能 in[〜#〜] o [〜#〜]n)。

少なくとも、予定の開始時間で並べ替える必要があります。これには、[〜#〜] o [〜#〜]nログn)。

[〜#〜] o [〜#〜]n)解決策リストがすでにソートされている場合。アルゴリズムには基本的に、次の予定が前の予定と重複していないかどうかを確認することが含まれます。リストを実行するときに実際にリストに2つのポインターが必要になるため、これには少し微妙な点があります。

  • チェックされている現在の予定
  • これまでに発生した最も遅い終了時刻の予定(前回の予定ではない可能性があります)

[〜#〜] o [〜#〜]n)ソートされていない場合の解決策は他の制約がある場合にのみ存在する、例えば固定数の予定タイムスロット。この場合、HashSetsを使用して、各タイムスロットをカバーする予定を決定できます。アルゴリズムはおおまかに次のようになります。

  • タイムスロット番号は固定定数なので、タイムスロットごとにHashSetを作成します-[〜#〜] o [〜#〜](1)
  • 予定ごとに、そのID番号をカバーするスロットのハッシュセットに格納します-[〜#〜] o [〜#〜]n)一定数のタイムスロットを更新するのは[〜#〜] o [〜#〜](1)各予定
  • スロットを実行し、オーバーラップをチェックします-[〜#〜] o [〜#〜](1)(または[〜#〜] o [〜#〜]n)繰り返したい場合は、結果として返すための重複する予定)
17
mikera

開始時間と終了時間、およびスケジューリングを行う解像度に何らかの制約があると仮定すると、各予定を使用する時間と使用しない時間のビットマップに変換するのはかなり簡単なようです。使用中のスロットでのカウントソート(別名バケットソート)。これらは両方とも線形であるため、結果は線形である必要があります(ただし、正しく考えている場合は、予定の数ではなく、タイムスロットの数で線形である必要があります)。

少なくともこれを面接の質問として尋ねた場合、私が望んでいる主なことは、候補者がそれらの制約について質問することです(つまり、それらの制約が許可されているかどうか)。今から1000年後に予定を立てたり、1分(ナノ秒のようなものは言うまでもなく)の精度で予定を立てたりするのは非現実的であるため、合理的な制約として私を襲いますが、想定する前に尋ねる必要があります。

4
Jerry Coffin

単純なアプローチは、2つの並列ツリーを構築することです。1つは開始点で順序付けられ、もう1つは各間隔の終了点で順序付けられます。これにより、各ツリーの半分をO(log n)時間で破棄できますが、結果をマージする必要があり、O(n)時間が必要です。これにより、O(n + log n)でクエリが実行されます。 = O(n)。

2
Rami Jarrar

これは、恐ろしい擬似コードで、私が考えることができる最高のものです。私は可能な限り問題を減らすように努めました。これはOn ^ 2よりも少ないだけです(私は思います)。

最後の出力には、特定の予定がその予定の特定の出力行で競合するすべての予定が表示されるわけではありませんが、ある時点ですべての競合が表示されることに注意してください。

また、開始時間の順に予定の名前を数値で変更したことにも注意してください。

output would be something like the following:

Appointment 1 conflicts with 2
Appointment 2 conflicts with
Appointment 3 conflicts with
Appointment 4 conflicts with 5
Appointment 5 conflicts with
appt{1},appt{2},appt{3} ,appt{4} ,appt{5}
  2      4       10       22      29
  5      7       11       36      34

擬似コード

list=(1,2,3,4,5)
for (i=1,i<=5,i++)
    list.shift()   **removes first element
    appt{i}.conflictswith()=list

for (i=1,i<=n,i++)
{   number=n
    done=false
    while(done=false)
        {if (number>i)
            {if (appt(i).endtime() < appt(number).startime())
                {appt{i}.conflictswith().pop()}
             else
                {done=true}
             number--
             }
        else
            {done=true}
        }
}
for (i=1,i<=n,i++)
    print "Appointment ",i," conflicts with:",appt{i}.conflictswith()  
1
protist

間隔ツリーと呼ばれるデータ構造に出くわしました。提供されたデータに応じて、O(n log(n))時間未満で間隔を見つけることができます。

1
Dude