web-dev-qa-db-ja.com

リンクリストを使用してバケットを実装する場合、バケットの並べ替えの複雑さはどのようにO(n + k)になりますか?

リンクされたリストで実装されたバケットを使用する場合、なぜバケットのソートにO(n + k)のランタイムがあるのか​​興味があります。たとえば、次の入力があるとします。

n = no of element= 8
k = range = 3

array = 2,2,1,1,1,3,1,3

バケットは次のようになります。

1: 1 -> 1 -> 1 -> 1
2: 2 -> 2
3: 3 -> 3

これらのバケットへの挿入に費やされた合計時間はO(n)です。これは、リンクされたリストにテールポインターを格納すると仮定した場合です。

削除するには、各バケットに移動してから、そのバケット内の各ノードを削除する必要があります。したがって、各リンクリストをたどるとき、複雑度はO(K *バケットのリンクリストの平均長)になるはずです。

ただし、バケットのソートの複雑さはO(n + k)であると読みました。なぜこれが私の分析に同意しないのですか?まだ計算の複雑さを学んでいるので、訂正してください。

23
Suri

あなたの分析はほとんど正しいですが、あなたが見逃している重要な詳細があります。

現時点では、要素をバケットに分散するために入力配列全体を反復処理すると、時間O(n)がかかることは間違いありません。ただし、配列を組み立てるのに必要な合計時間はO(k *(バケットあたりの平均要素数))であると言うと、少しずれます。 n個の要素とk個のバケットがあるため、O(n)の合計実行時間に対して、これはO(k *(n/k))= O(n)となることに注意してください。実際の答えがO(n + k)である理由を確認するには、そのbig-O項をより注意深く調べる必要があります。

手始めに、各バケットに費やす平均時間はO(n/k)であることは完全に正しいことです。次に、k個のバケットがあるため、総実行時間はO(k *(n/k))= O(n)になると言います。ただし、これは正しくありません。特に、k * O(n/k)= O(n)であるnot trueです。この理由は、O(n/k)という項が定数因子を隠しているためです。各バケットにアクセスして、そこに含まれる要素を確認すると、n/k時間、またはn/k時間の一定の倍数さえもかかりません。たとえば、バケットが空の場合はどうなりますか?その場合、要素を繰り返し処理してはならないことを判断する必要があるため、バケットを確認するのにある程度の時間を費やしています。したがって、バケットごとに必要な時間のより正確な表現は、cのようなものです。(n/k)+ c1、ここでc そしてc1 実装固有の定数です。もちろん、この式はO(n/k)です。

キャッチは、この式にkを掛けて、作業の総量を取得したときに何が起こるかです。計算すると

k *(c(n/k)+ c1

あなたが得る

cn + c1k

ご覧のとおり、この式はkに直接依存しているため、総実行時間はO(n + k)です。

この結果に到達するためのより直接的な方法は、バケットのソートの2番目のステップのコードを確認することです。これは次のようになります。

For each bucket b:
    For each element x in b:
        Append x to the array.

全体的にどれだけの作業が行われますか?まあ、k個の異なるバケットがあるので、各バケットを調べる必要があるため、最も外側のループには少なくともO(k)時間かかる)必要があります。内部では、内側のループは合計でO(n)回全体、バケット全体に分散された合計n個の要素があるため。これから、O(n + k)合計ランタイムが得られます。

これが重要な理由は、膨大な数のバケット(たとえば、nをはるかに超える)でバケットのソートを実行しようとすると、ランタイムは、すべてのバケットをスキャンしてスキャンするために必要な時間によって支配される可能性があることを意味しますほとんどが空の場合でも、実際に使用したバケット。基数ソートが役立つのは、時間内に実行されるバケットが2つしかない場合にバケットソートの複数の反復を使用するためです。O(n + 2)= O(n)。これのO(lg U)反復を実行するだけでよいので(Uは配列内の最大値です)、ランタイムはバケットから取得するO(n + U)ではなくO(n lg U)です。ソート、それははるかに悪いです。

お役に立てれば!

55
templatetypedef