Apakah ada struktur data yang memelihara kumpulan himpunan (himpunan terbatas hingga) yang mendukung operasi berikut? Setiap waktu menjalankan sublinear akan dihargai?
- Init set kosong.
- Tambahkan elemen ke set.
- Diberi dua set, laporkan apakah mereka berpotongan.
data-structures
sets
Dawei Huang
sumber
sumber
Jawaban:
Jika setiap set menyimpan catatan apa yang ada, dan Anda memiliki total set, Anda dapat dengan mudah mengubah struktur data apa pun untuk koleksi ( mis. Pohon pencarian biner, dll. ) Menjadi satu di mana Anda dapat mengambil unsur persimpangan dua set dalam waktu O ( log s ) .s>0 O(logs)
Setiap set harus memiliki pengidentifikasi unik dari beberapa set yang dipesan secara total. Jika Anda secara eksplisit memberi nama set Anda maka pengidentifikasi hanya bisa menjadi indeks.S1,S2,…
Anda harus menerapkan "registri" set; struktur data yang memelihara koleksi semua set yang telah Anda tetapkan. Registri harus diimplementasikan sebagai struktur data pohon pencarian, untuk memungkinkan pengambilan mudah ( misalnya jika Anda ingin menghapus set) dan traversal linear-time dari set.
Setiap set juga mempertahankan "index" dari masing-masing set lain - bukan salinan dari mereka, melainkan struktur data yang diindeks oleh label set lainnya. Indeks ini akan digunakan untuk mempertahankan, untuk setiap set S k , sebuah pohon pencarian biner dari semua elemen dari S j ∩ S k . (Dua set S j dan S k membagikan satu salinan dari pohon pencarian itu.)Sj Sk Sj∩Sk Sj Sk
Inisialisasi
Inisialisasi set terdiri dari O ( 1 ) operasi untuk menginisialisasi pohon unsur-unsurnya, O ( s ) operasi seperti yang Anda menginisialisasi (menyalin dari registri) indeks untuk set T , dan O ( s log s ) operasi saat Anda melintasi registri untuk menambahkan T ke indeks masing-masing set lainnya S j . Dalam indeks T , kami membuat pohon pencarian yang mewakili T ∩ S j = ∅T=∅ O(1) O(s) T O(slogs) T Sj T T∩Sj=∅ untuk perangkat lainnya ; kami menyalin pointer yang sama untuk indeks S j .Sj Sj
Menambahkan elemen ke setT
Menambahkan beberapa ke himpunan T membutuhkan waktu O ( log n T ) seperti biasa, di mana n T = | T | . Kami juga tes untuk keanggotaan x di masing-masing set lain S 1 , S 2 , ... , yang membutuhkan waktu O ( log n S 1 + log n S 2 + ⋯ ) ⊆ O ( s log nx∈V T O(lognT) nT=|T| x S1,S2,… mana n = | V | adalah ukuran alam semesta (atau himpunan terbesar S j ) dan s adalah jumlah himpunan dalam registri. Untuk setiap set S j sehingga x ∈ S j , juga insert x ke dalam indeks untuk set S j ∩ T . Untuk setiap seperti set S j , ini membutuhkan waktu O ( log s + log n T ) waktu, untuk mencari S j
Jika Anda tidak mengizinkan duplikat di set, kita dapat menghemat waktu dalam kasus yang sudah dengan berpantang tes keanggotaan dan sisipan untuk set lain T . "Penyisipan" dalam hal x sudah ada maka hanya membutuhkan waktu O ( log n T ) .x∈S T x O(lognT)
Pengujian titik-temu
Indeks setiap set dipertahankan dengan tepat untuk memungkinkan evaluasi cepat apakah dua set dan S k berpotongan. Untuk satu set S j , hanya dengan memeriksa indeks untuk set S k , kita tidak bisa hanya menentukan dalam waktu O ( log s ) apakah S j berpotongan S k , tetapi kita juga bisa mengambil sebuah pohon biner yang berisi seluruh himpunan S j ∩ S k .Sj Sk Sj Sk O(logs) Sj Sk Sj∩Sk
Penghapusan Elemen
Untuk menghapus elemen dari himpunan T , kami menghapusnya tidak hanya dari pohon pencarian untuk T itu sendiri, tetapi dari masing-masing persimpangan S j ∩ T untuk himpunan S j dalam indeksnya. Ini membutuhkan waktu O ( s log n T ) , di mana n T = | T | .x T T Sj∩T Sj O(slognT) nT=|T|
Tetapkan Penghapusan
Karena overhead mencari registri, jika Anda memiliki banyak set, mungkin diinginkan untuk menghapus set setelah mereka tidak lagi diperlukan. Dengan melintasi seluruh registri, kami dapat menghapus dari indeks semua set lainnya S j dalam waktu O ( s n T ) , didominasi oleh biaya menghapus pohon pencarian yang mewakili S j ∩ T untuk masing-masing set lainnya S j , di mana n T = | T | .S Sj O(snT) Sj∩T Sj nT=|T|
Catatan
Jika Anda hanya berharap untuk mengimplementasikan jumlah set yang konstan, maka run-times di atas berkurang menjadi:
inisialisasi:O(1)
penyisipan elemen:O(logn)
pengujian persimpangan (dan pengambilan persimpangan):O(1)
penghapusan elemen:O(lognT)
setel penghapusan:O(nS)
di mana adalah ukuran set terbesar dalam registri, dan n T = | T | untuk set T yang Anda operasikan.n nT=|T| T
Jika Anda berharap memiliki set , di mana V adalah semesta Anda, Anda mungkin memerlukan struktur data yang berbeda jika Anda ingin operasi ini beroperasi dalam waktu sub-linear. Namun, jika Anda memiliki pasangan set yang persimpangannya Anda tahu tidak akan pernah Anda uji, Anda mungkin dapat mengurangi ukuran indeks untuk set tersebut (dengan tidak termasuk set yang persimpangannya akan Anda uji) atau menggunakan lebih dari satu registry ( satu untuk setiap kumpulan set yang persimpangannya mungkin Anda uji). Bahkan, registri hanya berguna jika Anda ingin kontrol terpusat untuk memastikan bahwa setiap pasangan set memiliki catatan satu sama lain dalam indeks: itu mungkin praktis dalam beberapa kasus, pada inisialisasi set S , hanya untuk merekamO(|V|) V S ad hoc setiap set baru ke dalam indeks set lainnya yang simpang dengan S yang Anda minati.T S
sumber
Ada struktur data yang memungkinkan Anda melakukan ini dalam waktu kurang dari linear, bahkan untuk input terburuk. Lihat http://research.microsoft.com/pubs/173795/vldb11intersection.pdf (dan referensi makalah di sana).
Jika dua set S dan T Anda memiliki persimpangan besar dan Anda memiliki kamus untuk S, mencari elemen T dalam urutan acak akan dengan cepat memberi Anda elemen umum. Kasus yang paling sulit adalah ketika ukuran persimpangan adalah 0 atau 1.
sumber
Biasanya bahasa pemrograman pilihan Anda akan mendukung struktur data dengan elemen unik. Secara umum ada tiga pendekatan populer: Pohon, Hash dan Bitmask. Elemen pohon harus dapat dibandingkan, elemen hash harus hashable dan elemen Bitmask harus memiliki cara konversi ke integer.
Rangkaian pohon akan mendukung penyisipan dalam O (log n) dan pengujian persimpangan di Worst Case O (n log n).
Hash-set akan mendukung penyisipan dalam Amortized O (1 * h) di mana 'h' adalah waktu berjalan dari algoritma hashing, dan tes persimpangan di Worst Case O (n).
Set bitmask umumnya tidak digunakan seperti set pohon dan hash.
sumber
Jika kasing Anda memberikan jawaban positif palsu, saya akan menggunakan Bloom Filter dengan fungsi hash tunggal.
Anda dapat menerapkannya sebagai berikut:
Init set kosong
Tambahkan elemen ke set.
Diberi dua set (B1, B2), laporkan apakah mereka berpotongan.
Kompleksitas
sumber