毎日のアルゴリズム: データストリームの中央値

毎日のアルゴリズム: データストリームの中央値

[[431427]]

この記事はWeChatの公開アカウント「3分でフロントエンドを学ぶ」から転載したもので、著者はsisterAnです。この記事を転載する場合は、「3分で学ぶフロントエンド」公式アカウントまでご連絡ください。

中央値は、順序付けられたリスト内の中央の数値です。リストの長さが偶数の場合、中央値は中央の 2 つの数値の平均になります。

例えば、

[2,3,4]の中央値は3

[2,3]の中央値は(2 + 3) / 2 = 2.5である。

次の 2 つの操作をサポートするデータ構造を設計します。

  • void addNum(int num) - データ ストリームからデータ構造に整数を追加します。
  • double findMedian() – これまでのすべての要素の中央値を返します。

例:

  1. 追加番号(1)
  2. 追加番号(2)
  3. 中央値を見つける() -> 1.5
  4. 追加番号(3)
  5. 中央値を見つける() -> 2

高度な:

  • データ ストリーム内のすべての整数が 0 ~ 100 の範囲内にある場合、アルゴリズムをどのように最適化しますか?
  • データ ストリーム内の整数の 99% が 0 ~ 100 の範囲内にある場合、アルゴリズムをどのように最適化しますか?

この動的配列の中央値問題を見ても、興奮しすぎないでください。これはヒープを使用する完璧な例です。ヒープの典型的な応用である中央値問題をテストします。詳細については、「Advanced Front-end Algorithms 9」を参照してください。この記事を読めば、ヒープ ソート、Top K、中央値問題に関する面接を恐れることはなくなります。

解決策: ヒープの使用

解決:

維持するヒープは 2 つあります。

  • 最大ヒープ: 最初の n/2 個の小さな要素にアクセスするために使用されます。n が奇数の場合、最初の Math.floor(n/2) + 1 個の要素にアクセスするために使用されます。
  • 小さなトップヒープ: 最後のn/2個の小さな要素を保存およびアクセスするために使用されます。

すると、質問によれば、中央値は次のようになります。

  • nは奇数です: 中央値は最大ヒープの最上位要素です
  • nは偶数です。中央値は、大きなヒープの最上位要素と小さなヒープの最上位要素の平均です。

配列が動的である場合、配列に要素を挿入するたびにヒープをどのように調整しますか?

挿入された要素が最大ヒープの上限より大きい場合、その要素は最小ヒープ内に挿入され、小さい場合は最大ヒープ内に挿入されます。

挿入が完了した後、ビッグトップヒープまたはスモールトップヒープ内の要素数が上記の要件を満たさない場合は、要件が満たされるまで、ビッグトップヒープの最上位要素またはスモールトップヒープの最上位要素を別のヒープへ継続的に移動する必要があります。

コード実装:

  1. MedianFinder =関数() {
  2. // 最初の n/2 個の最小要素を格納するための大きなヒープ
  3. this.lowHeap = 新しい MaxHeap()
  4. // 小さなトップヒープ。次の n/2 個の最小要素を格納するために使用します。
  5. this.hightHeap = 新しい MinHeap()
  6. };
  7. // 要素を挿入
  8. MedianFinder.prototype.addNum =関数(数値) {
  9. // 最大ヒープが空の場合、または最大ヒープの先頭の要素がnumより小さい場合は、それを最大ヒープに挿入します
  10. // それ以外の場合は、小さなトップヒープに挿入します
  11. if(!this.lowHeap.getSize() || num < this.lowHeap.getHead()) {
  12. // 最大ヒープの上限より小さい場合は、最大ヒープに挿入します
  13. this.lowHeap.insert (数値)
  14. }それ以外{
  15. // 小さなヒープの先頭より大きい場合は、小さなヒープに挿入します
  16. this.hightHeap.insert (数値)
  17. }
  18.  
  19. // 最上位ヒープのサイズを比較して、まだバランスが取れているかどうかを確認します
  20. this.lowHeap.getSize() が this.hightHeap.getSize() より大きい場合、
  21. // 大きなトップヒープから小さなトップヒープに移行する
  22. this.hightHeap.insert ( this.lowHeap.removeHead ())
  23. }
  24. this.hightHeap.getSize() が this.lowHeap.getSize() より大きい場合、
  25. // 小さなトップヒープから大きなトップヒープに移行する
  26. this.lowHeap.insert (this.hightHeap.removeHead())
  27. }
  28. };
  29. // 中央値を取得する
  30. MedianFinder.prototype.findMedian =関数() {
  31. this.lowHeap.getSize() と this.lowHeap.getSize() が等しい場合、 this.hightHeap.getSize() が返されます。
  32. (this.lowHeap.getHead() + this.hightHeap.getHead())/2を返します
  33. }
  34. this.lowHeap.getHead()を返す
  35. };

小さなトップヒープの定義は次のとおりです。

  1. // 小さなトップヒープ
  2. MinHeap =関数() {
  3. ヒープ = [,] とする
  4. // ヒープ内の要素数
  5. this.getSize = () => ヒープの長さ - 1
  6. // 入れる
  7. this.insert = (キー) => {
  8. heap.push(キー)
  9. // 保存場所を取得する
  10. i = ヒープの長さ - 1 とします。
  11. (Math.floor(i/2) > 0 && heap[i] < heap[Math.floor(i/2)]) の場合 {
  12. swap(heap, i, Math.floor(i/2)); // スワップ
  13. i = Math.floor(i/2);
  14. }
  15. }
  16. // ヒープヘッダーを削除して戻ります
  17. this.removeHead = () => {
  18. ヒープの長さが1より大きい場合
  19. (heap.length === 2)の場合、heap.pop()を返します
  20. num = ヒープ[1]とする
  21. ヒープ[1] = ヒープ.pop()
  22. ヒープ化(1)
  23. 数値を返す
  24. }
  25. 戻る ヌル 
  26. }
  27. // 山の先頭を取得する
  28. this.getHead = () => {
  29. heap.length > 1を返すか、heap[1]: null  
  30. }
  31. // ヒープ
  32. ヒープ化 = (i) => {
  33. k = ヒープの長さ - 1 とします。
  34. // トップダウンスタッキング
  35. while( true ) {
  36. minIndex = i とします
  37. 2*i <= k && ヒープ[2*i] < ヒープ[i]) {
  38. 最小インデックス = 2*i
  39. }
  40. 2*i+1 <= k && ヒープ[2*i+1] < ヒープ[最小インデックス]) {
  41. 最小インデックス = 2*i+1
  42. }
  43. 最小インデックスが i の場合
  44. swap(ヒープ, i, minIndex)
  45. i = 最小インデックス
  46. }それ以外{
  47. 壊す
  48. }
  49. }
  50. }
  51. swap = (arr, i, j) => {とします。
  52. temp = arr[i]とします。
  53. arr[i] = arr[j]
  54. arr[j] =一時 
  55. }
  56. }

最大ヒープの定義は次のとおりです。

  1. // ビッグトップヒープ
  2. MaxHeap =関数() {
  3. ヒープ = [,] とする
  4. // ヒープ内の要素数
  5. this.getSize = ()=>ヒープの長さ - 1
  6. //最大ヒープに挿入
  7. this.insert = (キー) => {
  8. heap.push(キー)
  9. // 保存場所を取得する
  10. i = ヒープの長さ - 1 とします。
  11. (Math.floor(i/2) > 0 && heap[i] > heap[Math.floor(i/2)]) の場合 {
  12. swap(heap, i, Math.floor(i/2)); // スワップ
  13. i = Math.floor(i/2);
  14. }
  15. }
  16. // 山の先頭を取得する
  17. this.getHead = () => {
  18. heap.length > 1を返すか、heap[1]: null  
  19. }
  20. // ヒープヘッダーを削除して戻ります
  21. this.removeHead = () => {
  22. ヒープの長さが1より大きい場合
  23. (heap.length === 2)の場合、heap.pop()を返します
  24. num = ヒープ[1]とする
  25. ヒープ[1] = ヒープ.pop()
  26. ヒープ化(1)
  27. 数値を返す
  28. }
  29. 戻る ヌル 
  30. }
  31. // ヒープ
  32. ヒープ化 = (i) => {
  33. k = ヒープの長さ - 1 とします。
  34. // トップダウンスタッキング
  35. while( true ) {
  36. maxIndex = iとする
  37. 2*i <= k && ヒープ[2*i] > ヒープ[i]) {
  38. 最大インデックス = 2*i
  39. }
  40. 2*i+1 <= k && ヒープ[2*i+1] > ヒープ[maxIndex]) {
  41. 最大インデックス = 2*i+1
  42. }
  43. if(maxIndex !== i) {
  44. swap(ヒープ, i, 最大インデックス)
  45. i = 最大インデックス
  46. }それ以外{
  47. 壊す
  48. }
  49. }
  50. }
  51. swap = (arr, i, j) => {とします。
  52. temp = arr[i]とします。
  53. arr[i] = arr[j]
  54. arr[j] =一時 
  55. }
  56. }

複雑性分析:

時間計算量: ヒープに要素を挿入する時間計算量は O(logn) で、これはツリーの高さです。ヒープの最上位要素を移動するにはヒープ化も必要なので、時間計算量も O(logn) です。したがって、挿入 ( addNum ) の時間計算量は O(logn) であり、各挿入後に中央値を見つけるにはヒープの最上位要素を返すだけでよく、 findMedian の時間計算量は O(1) です。

空間計算量: O(n)

データストリーム内のすべての整数が0から100の範囲にある場合、カウントソートを使用することができますが、カウントソートの時間計算量はO(n + m)であり、ここでmはデータ範囲を表し、計算量が高いため、ここでは適していません。カウントソートは、静的配列の上位k値に適しています。Leetcode347:上位K高頻度要素

リートコード: https://leetcode-cn.com/problems/find-median-from-data-stream/solution/javascriptshu-ju-liu-de-zhong-wei-shu-by-user7746o/

<<:  機械学習がサプライチェーン管理を変える10の方法

>>:  将来、ロボットが私たちを支配するようになるのでしょうか?

ブログ    

推薦する

清華大学は顔認識技術に脆弱性を発見、セキュリティ問題を真剣に受け止める必要がある

このテストでは合計20台の携帯電話が選ばれ、そのうち1台は海外製、残りの19台は国内トップ5の携帯電...

音声によるやりとりをより自然にするにはどうすればよいでしょうか?まずはこれら 6 つの重要な知識ポイントをマスターしましょう。

最近、ロボットに関する非常に良い記事をいくつか読んだので、自分の考えを書き留めながら翻訳してみようと...

GitHub が機械学習コードの脆弱性スキャンを無料で提供、JavaScript / TypeScript もサポート

現在、JavaScript および TypeScript リポジトリで開発およびテストが行​​われて...

なぜ医療においてAIを信頼できないのか?データセットが小さく信頼性が低いため、AI医療にはまだまだ課題がある

近年、医療診断における AI の応用がますます注目されており、薬物スクリーニングや AI 診断など、...

...

...

ロボット「シェフ」がニューヨークに登場、1時間で300個の巻き寿司を作れる!

マンハッタンのファストカジュアルチェーン「ダルプ・モダン・インディアン」にあるドーサを自動で作る機械...

今後3~5年で、機械学習の人材が不足する領域はどこでしょうか?

基本的な紹介学術的なニーズを別にすれば、ほとんどの人はアルゴリズムの研究に従事するのではなく、第一線...

IDC が製造業の予測を発表。AI によるリスク意思決定がリストに含まれているのはなぜですか?

製造業の実際の発展状況は、国の経済発展と社会の安定に関係しています。伝統的な製造業のインテリジェンス...

LSTMに匹敵するTransformerは機械学習界に火をつけました。それは万能です。

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

検査業界は大きな変革期を迎えており、人工知能が次世代の検査をリードしている。

[[283895]]モバイルインターネットの隆盛時代を経て、中国のモバイルアプリケーションエコシス...

スーパードライグッズ: データサイエンスの全体像を概観する記事: 法則、アルゴリズム、問題の種類...

Pradeep Menon 氏は、ビッグデータ、データ サイエンス、データ アーキテクチャの分野で...

...

...

モデルの好みはサイズだけですか?上海交通大学は32の大規模モデルについて人間の嗜好の定量的要素を包括的に分析した。

現在のモデルトレーニングパラダイムでは、嗜好データの取得と使用が不可欠な部分になっています。トレーニ...