Saya perlu menemukan elemen terkecil ke-k di pohon pencarian biner tanpa menggunakan variabel statis / global. Bagaimana cara mencapainya secara efisien? Solusi yang ada dalam pikiran saya adalah melakukan operasi di O (n), kasus terburuk karena saya berencana untuk melakukan penelusuran inorder seluruh pohon. Tetapi jauh di lubuk hati saya merasa bahwa saya tidak menggunakan properti BST di sini. Apakah solusi asumsi saya benar atau ada solusi yang lebih baik yang tersedia?
112
Jawaban:
Inilah garis besar idenya:
Dalam BST, subpohon kiri dari node
T
hanya berisi elemen yang lebih kecil dari nilai yang disimpanT
. Jikak
lebih kecil dari jumlah elemen di subtree kiri,k
elemen terkecil harus dimiliki subtree kiri. Sebaliknya, jikak
lebih besar, maka filek
elemen terkecil ada di subtree kanan.Kita dapat menambah BST agar setiap node di dalamnya menyimpan jumlah elemen di subtree kirinya (asumsikan bahwa subtree kiri dari node tertentu menyertakan node tersebut). Dengan informasi ini, mudah untuk melintasi pohon dengan berulang kali menanyakan jumlah elemen di subtree kiri, untuk memutuskan apakah akan melakukan perulangan ke subtree kiri atau kanan.
Sekarang, misalkan kita berada di simpul T:
T
.k
terkecil. Jadi, kami mengurangi masalah untuk menemukank - num_elements(left subtree of T)
elemen terkecil dari subpohon kanan.k
terkecil ada di suatu tempat di subpohon kiri, jadi kita mengurangi masalahnya untuk mencarik
elemen terkecil di subpohon kiri.Analisis kompleksitas:
Ini membutuhkan
O(depth of node)
waktu, yangO(log n)
dalam kasus terburuk pada BST seimbang, atauO(log n)
rata-rata untuk BST acak.BST membutuhkan
O(n)
penyimpanan, dan BST membutuhkan penyimpanan lainO(n)
untuk menyimpan informasi tentang jumlah elemen. Semua operasi BST membutuhkanO(depth of node)
waktu, dan perlu waktuO(depth of node)
ekstra untuk mempertahankan informasi "jumlah elemen" untuk penyisipan, penghapusan, atau rotasi node. Oleh karena itu, menyimpan informasi tentang jumlah elemen di subpohon kiri menjaga kompleksitas ruang dan waktu dari BST.sumber
Solusi yang lebih sederhana adalah melakukan penelusuran inorder dan melacak elemen yang saat ini akan dicetak (tanpa mencetaknya). Saat kita mencapai k, cetak elemen dan lewati traversal pohon lainnya.
sumber
ini adalah implementasi saya di C # berdasarkan algoritma di atas, saya pikir saya akan mempostingnya sehingga orang-orang dapat lebih memahami cara kerjanya untuk saya
terima kasih IVlad
sumber
Solusi yang lebih sederhana adalah melakukan penelusuran inorder dan melacak elemen yang saat ini akan dicetak dengan penghitung k. Saat kita mencapai k, cetak elemennya. Runtime adalah O (n). Ingat jenis fungsi yang dikembalikan tidak boleh kosong, ia harus mengembalikan nilai k yang diperbarui setelah setiap panggilan rekursif. Solusi yang lebih baik untuk ini adalah augmented BST dengan nilai posisi yang diurutkan di setiap node.
sumber
// tambahkan versi java tanpa rekursi
sumber
Anda dapat menggunakan traversal inorder iteratif: http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal dengan pemeriksaan sederhana untuk elemen k setelah mengeluarkan node dari tumpukan.
sumber
Diberikan hanya pohon pencarian biner biasa, hampir semua yang dapat Anda lakukan adalah mulai dari yang terkecil, dan melintasi ke atas untuk menemukan node yang tepat.
Jika Anda akan sering melakukan ini, Anda dapat menambahkan atribut ke setiap node yang menandakan berapa banyak node di sub-pohon kirinya. Dengan menggunakan itu, Anda dapat menurunkan pohon langsung ke simpul yang benar.
sumber
Rekursif In-order Walk dengan penghitung
Idenya mirip dengan solusi @prasadvk, tetapi memiliki beberapa kekurangan (lihat catatan di bawah), jadi saya memposting ini sebagai jawaban terpisah.
Catatan (dan perbedaan dari solusi @ prasadvk):
if( counter == k )
tes diperlukan di tiga tempat: (a) setelah subpohon kiri, (b) setelah akar, dan (c) setelah subpohon kanan. Ini untuk memastikan bahwa elemen k terdeteksi untuk semua lokasi , yaitu terlepas dari subpohon tempatnya berada.if( result == -1 )
tes diperlukan untuk memastikan hanya elemen hasil yang dicetak , jika tidak semua elemen mulai dari k terkecil hingga akar yang dicetak.sumber
O(k + d)
, di manad
kedalaman maksimum pohon. Oleh karena itu menggunakan variabel globalcounter
tetapi ilegal untuk pertanyaan ini.Untuk pohon pencarian yang tidak seimbang, dibutuhkan O (n) .
Untuk pohon pencarian yang seimbang , dibutuhkan O (k + log n) dalam kasus terburuk tetapi hanya O (k) dalam pengertian Amortisasi .
Memiliki dan mengelola integer ekstra untuk setiap node: ukuran sub-pohon memberikan kompleksitas waktu O (log n) . Pohon pencarian yang seimbang seperti itu biasanya disebut RankTree.
Secara umum, ada solusi (tidak berdasarkan pohon).
Salam.
sumber
Ini bekerja dengan baik: status: adalah larik yang menampung apakah elemen ditemukan. k: adalah elemen k yang akan ditemukan. count: melacak jumlah node yang dilintasi selama traversal pohon.
sumber
Meskipun ini jelas bukan solusi optimal untuk masalah ini, ini adalah solusi potensial lain yang menurut saya menarik bagi beberapa orang:
sumber
tanda tangan:
panggil sebagai:
definisi:
sumber
Nah ini 2 sen saya ...
sumber
Inilah yang saya pikirkan dan berhasil. Ini akan berjalan di o (log n)
sumber
Kita cukup menggunakan in order traversal dan mendorong elemen yang dikunjungi ke tumpukan. pop k beberapa kali, untuk mendapatkan jawabannya.
kita juga bisa berhenti setelah elemen k
sumber
Solusi untuk kasus BST lengkap: -
sumber
Kernel Linux memiliki struktur data pohon merah-hitam augmented yang sangat baik yang mendukung operasi berbasis peringkat di O (log n) di linux / lib / rbtree.c.
Porta Java yang sangat kasar juga dapat ditemukan di http://code.google.com/p/refolding/source/browse/trunk/core/src/main/java/it/unibo/refolding/alg/RbTree.java , bersama dengan RbRoot.java dan RbNode.java. Elemen ke-n dapat diperoleh dengan memanggil RbNode.nth (RbNode node, int n), meneruskan root pohon.
sumber
Berikut adalah versi ringkas di C # yang mengembalikan elemen terkecil ke-k, tetapi perlu meneruskan k sebagai argumen ref (pendekatannya sama dengan @prasadvk):
Itu O (log n) untuk menemukan yang simpul terkecil, dan kemudian O (k) untuk melintasi k-th node, sehingga O itu (k + log n).
sumber
http://www.geeksforgeeks.org/archives/10379
ini adalah jawaban yang tepat untuk pertanyaan ini: -
1. menggunakan inorder traversal pada waktu O (n) 2. menggunakan pohon Augmented dalam waktu k + log n
sumber
Saya tidak bisa menemukan algoritma yang lebih baik..jadi memutuskan untuk menulis satu :) Koreksi saya jika ini salah.
}
sumber
Ini kode java-nya,
max (Node root, int k) - untuk mencari k terbesar
min (Node root, int k) - untuk mencari kth Smallest
sumber
ini akan berhasil juga. panggil saja fungsi dengan maxNode di pohon
def k_largest (self, node, k): if k <0: return None
if k == 0: return node else: k - = 1 return self.k_largest (self.predecessor (node), k)
sumber
Saya pikir ini lebih baik daripada jawaban yang diterima karena tidak perlu memodifikasi simpul pohon asli untuk menyimpan jumlah simpul anak-anaknya.
Kita hanya perlu menggunakan in-order traversal untuk menghitung simpul terkecil dari kiri ke kanan, berhenti mencari setelah hitungannya sama dengan K.
sumber
Pendekatan terbaik sudah ada. Tapi saya ingin menambahkan Kode sederhana untuk itu
}
sumber
Menggunakan kelas Hasil tambahan untuk melacak jika node ditemukan dan k saat ini.
sumber
Solusi Python Kompleksitas Waktu: O (n) Kompleksitas Ruang: O (1)
Ide adalah menggunakan Morris Inorder Traversal
sumber
saya menulis fungsi rapi untuk menghitung elemen terkecil ke-k. Saya menggunakan in-order traversal dan berhenti ketika mencapai elemen terkecil ke-k.
sumber
sumber
sumber
}
sumber