Apakah ada antrian prioritas dengan ekstrak ?

46

Ada banyak sekali struktur data yang mengimplementasikan antarmuka antrian prioritas:

  • Sisipkan: masukkan elemen ke dalam struktur
  • Get-Min: kembalikan elemen terkecil dalam struktur
  • Extract-Min: menghapus elemen terkecil dalam struktur

Struktur data umum yang mengimplementasikan antarmuka ini adalah (min) tumpukan .

Biasanya, waktu berjalan (diamortisasi) dari operasi ini adalah:

  • Sisipkan: (kadang-kadang )O ( log n )O(1)O(logn)
  • Get-Min:O(1)
  • Extract-Min:O(logn)

The Fibonacci tumpukan mencapai masa berjalan misalnya. Sekarang, pertanyaan saya adalah sebagai berikut:

Apakah ada struktur data dengan waktu berjalan (diamortisasi) berikut ini?

  • Sisipkan:O(logn)
  • Get-Min:O(1)
  • Extract-Min:O(1)

Jika kita dapat membangun struktur seperti itu dalam waktu diberikan input yang diurutkan, maka kita dapat misalnya menemukan persimpangan garis pada input yang sudah diurutkan sebelumnya dengan persimpangan benar-benar lebih cepat daripada jika kita menggunakan antrian prioritas 'biasa'.o ( nO(n)o(nlogn)

Alex ten Brink
sumber
Saya pikir menggunakan BST seimbang, yang tidak akan menyeimbangkan kembali ketika melakukan Extract-Min bisa bekerja. Atau mungkin daftar lewati.
svick
@svick: lewati daftar secara acak, yang bukan itu yang saya cari. Jika Anda dapat melakukannya dengan BST, maka itu bagus, tapi saya pikir Anda akan harus melakukan beberapa jenis menyeimbangkan.
Alex ten Brink
Di samping catatan: ini adalah pertanyaan penyemaian dan saya tahu jawabannya, tapi senang melihat bahwa itu tidak begitu mudah dipecahkan. Jika ada yang tahu jawabannya, jangan ragu untuk memberikannya :)
Alex ten Brink
Jika Anda menerima waktu pembaruan yang diamortisasi, maka Anda dapat menyimpan struktur tumpukan standar, dan hanya melakukan sedikit modifikasi pada analisis Anda. Lihat jawaban saya di bawah ini.
Joe

Jawaban:

27

Gagasan kami adalah menggunakan pohon ulir berulir . Selain artikel Wikipedia, kami akan mengaitkan pohon sehingga setiap node memiliki pointer ke penggantinya dalam urutan traversal; kami juga memegang pointer ke elemen terkecil di pohon.nextstart

Sangat mudah untuk melihat bahwa mengekstraksi elemen terkecil dimungkinkan dalam waktu (terburuk) : cukup ikuti pointer, hapus minimum dan ubah pointer ke minimum . Minimal tidak pernah bisa memiliki anak kiri; jika memiliki anak yang tepat, kita letakkan di tempat minimum di pohon. Kami tidak melakukan operasi splay pohon splay biasanya akan lakukan. Hasilnya adalah pohon pencarian yang masih cukup seimbang: karena kami hanya menghapus node di sisi kiri, kami tahu bahwa ketika jumlah node (dalam subtree yang terpengaruh) turun menjadi sekitar setengah dari jumlah asli karena penghapusan, maka (sub ) tinggi pohon berkurang satu.O(1)startnext

Penyisipan dimungkinkan dalam waktu ; operasi zig-zag (dan apa yang tidak) di sini juga akan menyeimbangkan kembali pohon dengan baik.O(logn)

Ini adalah sketsa kasar yang terbaik. Kredit diberikan kepada F. Weinberg yang bingung dengan pertanyaan saya dan penasihat kami M. Nebel yang menyebutkan pohon melebar, tentang satu-satunya varian pohon yang belum kami coba.

Raphael
sumber
2
Tidak jelas bagi saya bagaimana cara membuat analisis diamortisasi berfungsi jika Anda tidak menggunakan extractMin. Bisakah Anda memberi petunjuk?
jbapple
Kami belum melakukannya secara detail. Idenya adalah bahwa rangkaian operasi ekstrak-min tidak mengacaukan pohon, oleh karena itu tidak perlu merentangkan dan analisis normal harus bekerja untuk penyisipan.
Raphael
9
Cermat! Pohon merentang belum tentu seimbang. Node yang belum diakses dalam waktu yang lama mungkin sangat dalam di pohon. Untuk membuat analisis berjalan, Anda harus berdebat dalam hal fungsi potensial yang sama yang digunakan untuk menganalisis splays.
JeffE
20
  • Sisipkan:O(logn)
  • Get-Min:O(1)
  • Extract-Min:O(1)

Waktu yang diamortisasi

Implementasi sederhana dari antrian prioritas (misalnya BST seimbang, atau tumpukan biner standar) dapat mencapai waktu berjalan (diamortisasi) ini dengan hanya membebankan biaya Ekstrak-Min untuk menyisipkan, dan mempertahankan penunjuk ke elemen minimum. Misalnya, Anda dapat memiliki fungsi potensial yaitu . Kemudian memasukkan elemen baru meningkatkan potensi sebesar , dan sehingga biaya amortisasi insert masih , tetapi Extract-Min () mengurangi potensi oleh , dan jadi biaya yang diamortisasi hanya .O ( log n ) O ( log n ) Ω ( log n ) O ( 1 )cnlognO(logn)O(logn)Ω(logn)O(1)

Kasus terburuk

Anda dapat menggunakan struktur data yang ada dalam literatur: pohon pencarian jari, dan cukup mempertahankan pointer ke elemen minimum. Lihat survei ini untuk ikhtisar, dan makalah 1988 oleh Levcopoulos dan Overmars untuk versi yang dapat diterapkan yang memenuhi kebutuhan Anda.

Joe
sumber
1
Sangat licik. Kau benar, kurasa aku seharusnya meminta sesuatu yang lebih kuat untuk mengecualikan ini. Ide bagus :)
Alex ten Brink
@AlextenBrink Anda dapat meminta penghapusan terburuk . (yang kelihatannya seperti apa beberapa jawaban lain pergi) Saya menambahkan paragraf ke jawaban saya untuk mengatasi kasus itu. O(1)
Joe
14

2-4 pohon telah diamortisasi modifikasi di lokasi yang diketahui. Artinya, jika Anda memiliki pointer ke beberapa lokasi di pohon, Anda dapat menghapus atau menambahkan elemen di sana dalam waktu diamortisasi.O ( 1 )O(1)O(1)

Anda bisa menyimpan pointer ke elemen minimum dan simpul root di pohon 2-4. Sisipan harus melalui simpul akar. Memperbarui pointer ke minimum adalah sepele setelah deleteMin, dan deleteMins adalah (diamortisasi) waktu.O(1)

Catatan menarik: pohon merah-hitam hanyalah cara memandang 2-4 pohon. Perancang pelaksana perpustakaan standar C ++ 98 diharapkan untuk memasok wadah berbasis pohon merah-hitam, dan standar tersebut menetapkan bahwa memasukkan dan menghapus harus waktu diamortisasi di lokasi yang diketahui (yang mereka sebut "iterators" ). Namun, ini sebenarnya jauh lebih sulit untuk pohon merah-hitam daripada untuk 2-4 pohon, karena membutuhkan simpul penandaan malas yang perlu di-recolored. Sepengetahuan saya, tidak ada implementasi dari perpustakaan standar C ++ 98 yang memenuhi persyaratan tertentu.O(1)

jbapple
sumber
8

Berdasarkan permintaan, berikut adalah struktur yang saya temukan setelah saya merumuskan pertanyaan:

Ide dasarnya adalah menggunakan pohon kambing hitam berulir bersama dengan pointer ke minimum (dan untuk ukuran yang baik, maksimum juga). Alternatif yang lebih sederhana untuk threading adalah mempertahankan pointer pendahulu dan penerus di setiap node (yang setara, lebih sederhana, tetapi memiliki lebih banyak overhead). Saya datang untuk menyebutnya tumpukan kambing hitam , hanya untuk memberinya beberapa nama.

Hanya struktur dasar ini memberi Anda operasi ini:

  • Cari: diberi kunci, mengembalikan pointer ke simpul yang sesuai pada waktu .O(logn)
  • Sisipkan: diberikan kunci, memasukkan kunci ke dalam struktur, mengembalikan pointer ke simpul itu dalam waktu .O(logn)
  • Predecessor / successor: diberi pointer, mengembalikan penerus atau pendahulunya dalam waktu .O(1)
  • Get-Min / Max: mengembalikan pointer ke minimum atau maksimum.

Dalam analisis pohon kambing hitam, keseimbangan biaya penghapusan dianalisis sebagai , tetapi analisis sebenarnya memberikan keseimbangan overhead dari (yang diabaikan dalam makalah karena mereka juga menghitung waktu untuk menemukan simpul yang akan dihapus). Jadi, jika kita memiliki pointer ke sebuah node, kita dapat menghapusnya dalam waktu konstan (Anda dapat melakukan ini di pohon pencarian biner dalam waktu ) dan dikombinasikan dengan overhead of balancing, ini memberi waktu hapus:O ( 1 ) O ( log n ) O ( 1 ) O ( 1 ) O ( 1 )O(logn)O(1)O(logn)O(1)O(1)O(1)

  • Hapus: diberi pointer, menghapus simpul dalam waktu .O(1)

Menggabungkan ini:

  • Extract-Min / Max: menghapus simpul minimum / maksimum dalam waktu .O(1)

Anda dapat melakukan sedikit lebih banyak dengan pointer: misalnya tidak sulit untuk mempertahankan pointer ke median atau statistik urutan lainnya, sehingga Anda dapat mempertahankan jumlah pointer yang konstan jika Anda membutuhkannya.

Beberapa hal lain:

  • Membangun: diberikan kunci dalam urutan diurutkan, membangun tumpukan kambing hitam dalam waktu .nO(n)
  • Saldo: menyeimbangkan pohon sehingga membentuk pohon pencarian biner seimbang sempurna (mengurangi overhead pencarian) dalam waktu (Anda dapat melakukan ini dengan faktor konstan lebih cepat dari yang disarankan oleh makalah, dengan memanfaatkan dari pendahulu / penggantinya).O(n)

Dan akhirnya, saya cukup yakin Anda dapat mendukung operasi ini, tetapi saya perlu memikirkan ini lebih banyak sebelum mengetahui ini dengan pasti:

  • Sisipkan-New-Min / Max: diberi kunci yang lebih kecil / lebih besar dari kunci apa pun yang ada dalam struktur, memasukkan kunci ke dalam struktur, mengembalikan pointer ke simpul itu dalam waktu .O(1)
Alex ten Brink
sumber
Wawasan utama adalah bahwa pohon kambing hitam meyakinkan Anda bahwa menghapus sembarang simpul tanpa penyeimbangan ulang tidak memengaruhi kinerja operasi lain dalam jangka panjang, bahkan jika Anda menghapus banyak node.
Raphael
Saya tahu dua cara melakukan penghapusan di pohon kambing hitam. One way mirror sisipan, dan waktu diamortisasi. Cara lain saya telah mendengar tentang menggunakan pembangunan kembali global dan diamortisasi, tapi saya tidak tahu bagaimana mempertahankan threading dalam kasus itu. Bayangkan memasukkan kunci baru ke bagian pohon yang semua kunci yang dihapus belum dihapus. Bagaimana Anda menemukan pendahulu kunci yang akan dimasukkan dalam waktu ? O(lgn)O(1)O(lgn)
jbapple
2
@ jbapple: ada dua variasi tentang cara menghapus dalam waktu untuk pohon kambing hitam. Salah satunya adalah meninggalkan node, tandai sebagai dihapus dan menghapus semua node yang dihapus ini dengan global membangun kembali, dan yang lainnya adalah untuk benar-benar menghapus node. Yang pertama lebih mudah untuk dianalisis (dan juga memberi Anda batasan pada yang kedua, itulah sebabnya biasanya dijelaskan) tetapi yang kedua adalah yang saya cari: Anda dapat menghapus dalam waktu dalam pohon pencarian biner vanili jika Anda dapat melakukan kueri pendahulu / penerus dalam waktu , dan menyeimbangkan dalam waktu diamortisasi memberi Anda sisa dari batasan. O(1)O(1)O(1)O(1)
Alex ten Brink
Ah, saya mengerti sekarang.
jbapple
2

Tumpukan lunak adalah modifikasi halus dari antrian binomial. Struktur data diperkirakan dengan parameter kesalahan . Ini mendukung masukkan, hapus, berbaur dan temukan. The diamortisasi kompleksitas setiap operasi adalah , kecuali untuk insert yang mengambil waktu. Kebaruan dari heap lunak adalah dalam mengalahkan batas logaritmik pada kompleksitas tumpukan dalam model berbasis perbandingan. Untuk memecahkan penghalang teoretis informasi, entropi dari struktur data dikurangi dengan secara artifisial meningkatkan nilai beberapa kunci. Ini disebut merusak kunci. Struktur data sepenuhnya berbasis pointer (tidak ada array atau asumsi numerik) dan optimal untuk setiap nilaiϵO(1)log(1/ϵ)ϵ dalam model berbasis perbandingan.

Aplikasi soft heap termasuk menghitung pohon spanning minimum untuk grafik, menjaga persentil secara dinamis dan statistik urutan waktu linier. Itu dapat juga digunakan untuk perhitungan perkiraan, seperti perkiraan penyortiran di mana peringkat elemen tidak pernah berbeda lebih dari dari peringkat sebenarnya.ϵn

Untuk makalah asli, jelas dan ditulis dengan baik, lihat Bernard Chazelle, The Soft Heap: Antrian Prioritas Perkiraan dengan Tingkat Kesalahan Optimal, Jurnal ACM, 47 (6), hlm. 1012-1027, 2000 . Untuk implementasi dan analisis alternatif yang mengklaim lebih sederhana dan lebih intuitif dari SODA'09, lihat Kaplan H. & Zwick U., Implementasi yang lebih sederhana dan analisis tumpukan lunak Chazelle, 2009 .

Juho
sumber
Meskipun struktur data yang sangat menarik, tumpukan lunak tidak tepat: findmin dapat mengembalikan nilai yang bukan minimum, tetapi hanya perkiraan minimum. Terima kasih atas tautannya :)
Alex ten Brink
1
@AlextenBrink: titik struktur data (seperti banyak algoritma probabilistik) adalah bahwa Anda dapat menggunakan struktur data perkiraan untuk mendapatkan jawaban yang tepat. Memang perkiraan sifat tumpukan lunak tidak mencegahnya digunakan dalam algoritma waktu linear hanya dikenal untuk pohon spanning minimum.
Jérémie
2

Oke, akhirnya membuat Anda kompleksitas yang Anda cari, dan apa yang terbaik, saya menemukannya dalam literatur:

Kompleksitas Kasus Terburuk

Hapus :O(1)

Hapus-min :O(1)

Cari-mnt :O(1)

Sisipkan :O(log n)

Referensi

JIKA MELD diizinkan untuk mengambil waktu linier, dimungkinkan untuk mendukung DELETE-MIN dalam waktu konstan kasus terburuk dengan menggunakan pohon pencarian jari Dietz dan Raman [3]. Dengan menggunakan struktur data mereka MAKEQUEUE , FINDMIN , DELETEMIN , DELETE dapat didukung dalam waktu kasus terburuk , INSERT dalam waktu kasus terburuk dan MELD dalam waktu kasus terburuk .O(1)O(log n)O(n)

Brodal, Gerth Stølting. 'Antrian Prioritas yang Dapat Dicetak Cepat'. Dalam Prosiding Lokakarya Internasional ke-4 tentang Algoritma dan Struktur Data, 282–290. Gumpalan '95. London, Inggris, Inggris: Springer-Verlag, 1995.

[3]: Dietz, Paul F, dan Rajeev Raman. 'Pohon Pembaruan Waktu Pencarian Jari yang Konstan'. Pemrosesan Informasi Surat 52, no. 3 (1994): 147 - 154.

Meskipun ini menggunakan model perhitungan RAM :

Struktur data kami menggunakan model mesin akses-acak (RAM) dengan ukuran biaya unit dan ukuran kata logaritmik;

Baru-baru ini, model komputasi Pointer-Machine telah diberikan [1].

[1]: Brodal, Gerth Stølting, George Lagogiannis, Christos Makris, Athanasios Tsakalidis, dan Kostas Tsichlas. 'Pohon Pencarian Jari Optimal di Mesin Pointer'. J. Comput. Syst. Sci. 67, tidak. 2 (September 2003): 381-418.

DI
sumber
2

Mendekati masalah ini dengan mempertahankan dua struktur data: Array dan Binary Tree.

Untuk mempertahankan pengindeksan dalam array, sebelumnya Anda memiliki ; tetapi baru-baru ini telah diatasi dengan memodifikasi analisis dari teknik kronogram. Ikatan [bawah] baru telah terbukti untuk masalah yang serupa dalam model probe-sel 1 . Dari membaca artikel itu; itu adalah pemahaman saya bahwa terikat berlaku untuk masalah representasi daftar juga.Ω(lognloglogn)Ω(logn)

Sekarang jika Anda memasukkan pohon biner ke dalam array Anda dan menyeimbangkan kembali + reindex setiap pembaruan , maka Anda akan memiliki: kompleksitas.O(logn)O(logn)

Jangka waktu terpanjang Anda — lebih dari nullelemen yang dihapus — akan menjadi . Ini jelas tidak meninggalkan keuntungan teoritis atas penyeimbangan ulang + pengindeksan ulang setiap pembaruan.O(logn)

Bergantung pada distribusi Anda, Anda dapat membuat asumsi untuk hanya menyeimbangkan kembali setiap insert; sehingga menarik kompleksitas dari ekstrak. Ekstrak — dari kedua ujungnya - hanya akan mengambil ; karena tidak perlu reindex (lacak saja indeks offset untuk menyimpannya di ).O(1)O(1)

Jika Anda tidak dapat membuat asumsi itu, mereka pendekatan saya akan meninggalkan Anda dengan memasukkan, menyeimbangkan, dan mengekstrak. Itu memang memiliki keunggulan dibandingkan beberapa pendekatan lain, di mana Anda bisa mendapatkan min / max dan di mana saja di antara - misalnya: beri saya nilai median - dalam . Selain itu memang memiliki fungsionalitas.O(logn)O(1)delete_at(idx)


1 Patrascu, Mihai, dan Erik D. Demaine. "Batas Bawah Logaritmik dalam Model Sel-Probe." SIAM J. Comput. 35, tidak. 4 (April 2006): 932–963. doi: 10.1137 / S0097539705447256.

DI
sumber
1
Maksud Anda menggunakan pohon AVL atau pohon seimbang serupa? Bagaimana Anda memastikan bahwa menghapus minimum tidak menyebabkan lebih dari banyak rotasi terus-menerus lebih jauh daripada terus-menerus menjauh (dari situs menghapus atau root)? Secara khusus, menghapus elemen dari pohon AVL dapat menyebabkan rotasi , jadi Anda perlu berdebat bagaimana Anda mencegahnya. O(logn)
Raphael
Apa yang dimaksud dengan "thread pohon pencarian biner ke dalam array"?
jbapple
@AT: Saya membagikan sentimen jbapple.
Raphael
Menyimpan pohon biner dalam array dengan cara itu (seperti tumpukan biner klasik) membuat setiap putaran membutuhkan waktu mana adalah ukuran subtree yang di-root pada simpul yang diputar. Dalam hal itu, melakukan "hanya" rotasi pada pembaruan masih bisa memakan waktu lama. Ω(k)kO(1)
jbapple
Pembaruan Anda, tempat Anda menjelaskan cara menerapkan rotasi dalam waktu konstan, tidak berfungsi dalam array. Jawaban ini masih salah. Kertas Tarjan yang Anda referensi adalah tentang pohon yang disimpan dengan node dan pointer.
jbapple
-2

Temukan-mnt dalam dengan waktu pembaruan yang diharapkan dariO(1)O(log log n)

Lihat makalah 2007: Kesetaraan antara antrian prioritas dan pengurutan oleh Mikkel Thorup.

Catatan: Ia merujuk ke artikel 2002 oleh Han & Thorup: Integer Sorting dalam Waktu yang Diharapkan dan Ruang LinierO(n log log n) .

DI
sumber
Meskipun makalah yang Anda tautkan menarik, antrian prioritas yang mereka tampilkan tidak memiliki penghapusan waktu yang konstan (jika saya membaca abstrak dengan benar), dan karenanya bukan itu yang saya minta.
Alex ten Brink
-2

Analisis

Sisipkan :o(n log log n)

Cari :o(log log n)

Hapus :O(1)

Spasi :O(n)

Get-Min :O(1)

Extract-Min :O(1)

Penerapan

  1. Bentuk daftar dari sejumlah elemen yang sewenang-wenang (konstan), katakanlah 6:O(1)
  2. Sortir daftar: =O(6)O(1)
  3. Titik penyisipan untuk setiap simpul berikutnya , akan menjadi elemen kedua (') pos (-1 untuk <, +1 untuk> dan dengan dua args tergantung pada mana Anda mulai / selesai di: dan akan ditemukan menggunakan Interpolasi Dinamis Cari [1] di:k±
    ((k>nsize1)(k<n0)((k<ni)(k>ni+1)))
    o(log log n)

[1]: Andersson, Arne, dan Christer Mattsson. 'Pencarian Interpolasi Dinamis dalam O (log log n) Waktu'. Dalam Automata, Bahasa dan Pemrograman, diedit oleh Andrzej Lingas, Rolf Karlsson, dan Svante Carlsson, 700: 15–27. Catatan Kuliah di Ilmu Komputer. Springer Berlin / Heidelberg, 1993. http://dx.doi.org/10.1007/3-540-56939-1_58 .

DI
sumber
2
Nah, waktu memasukkan jauh dari sasaran.
Raphael
Ini terlalu samar. Sebagai contoh, apa yang , , dan ? nsize1n0nini+1
Juho
Membaca abstrak makalah yang Anda tautkan, sepertinya batas-batas ini diharapkan batas untuk input dari distribusi tertentu, yang karenanya bukan yang saya cari: Saya ingin batas yang saya sebutkan pada input apa pun.
Alex ten Brink
@ Raphael: Tidak. mrm: posisi dalam daftar. AlextenBrink: Ini dapat dengan mudah diubah menjadi kasus terburuk pada distribusi apa pun menggunakan algoritma pencarian Biner untuk menemukan titik penyisipan Anda. O(log n)
AT
@AT Pencarian biner logaritmik membutuhkan akses acak. Seperti apa daftar mendasar yang diterapkan? Anda harus benar-benar memperdebatkan batasan yang diklaim. Juga, "posisi dalam daftar" tidak jelas - posisi apa dan apa yang dimaksud dengan simbol? Tidak semua orang memiliki akses ke kertas yang Anda tautkan. Cobalah untuk membuat jawaban Anda (lebih) lengkap dan setidaknya merangkum fakta. Pada titik ini saya tidak percaya jawaban Anda benar.
Juho