Sudah lama sejak tahun-tahun sekolah itu. Mendapat pekerjaan sebagai spesialis IT di rumah sakit. Mencoba pindah untuk melakukan pemrograman aktual sekarang. Saya sedang mengerjakan pohon biner sekarang, dan saya bertanya-tanya apa cara terbaik untuk menentukan apakah pohon itu seimbang tinggi.
Saya sedang memikirkan sesuatu seperti ini:
public boolean isBalanced(Node root){
if(root==null){
return true; //tree is empty
}
else{
int lh = root.left.height();
int rh = root.right.height();
if(lh - rh > 1 || rh - lh > 1){
return false;
}
}
return true;
}
Apakah ini implementasi yang baik? atau apakah saya melewatkan sesuatu?
java
algorithm
data-structures
binary-tree
pengguna69514
sumber
sumber
Jawaban:
Menemukan pertanyaan lama ini saat mencari sesuatu yang lain. Saya perhatikan bahwa Anda tidak pernah mendapatkan jawaban yang lengkap.
Cara untuk mengatasi masalah ini adalah memulai dengan menulis spesifikasi untuk fungsi yang Anda coba tulis.
Spesifikasi: Pohon biner yang terbentuk dengan baik dikatakan "tinggi seimbang" jika (1) kosong, atau (2) anak kiri dan kanannya seimbang tinggi dan tinggi pohon kiri dalam 1 dari ketinggian pohon yang tepat.
Sekarang setelah Anda memiliki spesifikasinya, kode itu mudah untuk ditulis. Ikuti saja spesifikasinya:
Menerjemahkannya ke dalam bahasa pemrograman pilihan Anda harusnya sepele.
Latihan bonus : sketsa kode naif ini melintasi pohon terlalu sering saat menghitung ketinggian. Bisakah Anda membuatnya lebih efisien?
Latihan bonus super : misalkan pohon itu besar - besaran tidak seimbang. Seperti, sejuta node jauh di satu sisi dan tiga di sisi lain. Apakah ada skenario di mana algoritma ini menghancurkan tumpukan? Dapatkah Anda memperbaiki penerapan sehingga tidak pernah merusak tumpukan, bahkan ketika diberikan pohon yang sangat tidak seimbang?
PEMBARUAN : Donal Fellows menunjukkan dalam jawabannya bahwa ada definisi berbeda dari 'seimbang' yang dapat dipilih seseorang. Misalnya, seseorang dapat mengambil definisi yang lebih ketat dari "tinggi seimbang", dan mengharuskan panjang jalur ke turunan kosong terdekat berada dalam salah satu jalur ke turunan kosong terjauh . Definisi saya tidak seketat itu, dan karenanya mengakui lebih banyak pohon.
Seseorang juga bisa kurang ketat dari definisi saya; dapat dikatakan bahwa pohon yang seimbang adalah pohon yang panjang jalur maksimumnya ke pohon kosong pada setiap cabang berbeda tidak lebih dari dua, atau tiga, atau konstanta lainnya. Atau panjang jalur maksimum adalah sebagian kecil dari panjang jalur minimum, seperti setengah atau seperempat.
Biasanya tidak masalah. Inti dari algoritma penyeimbangan pohon adalah untuk memastikan bahwa Anda tidak berakhir dalam situasi di mana Anda memiliki satu juta node di satu sisi dan tiga di sisi lain. Definisi Donal baik-baik saja secara teori, tetapi dalam praktiknya sangat merepotkan dengan algoritma penyeimbang pohon yang memenuhi tingkat keketatan itu. Penghematan kinerja biasanya tidak membenarkan biaya implementasi. Anda menghabiskan banyak waktu untuk melakukan penataan ulang pohon yang tidak perlu untuk mencapai tingkat keseimbangan yang dalam praktiknya hanya membuat sedikit perbedaan. Siapa yang peduli jika kadang-kadang dibutuhkan empat puluh cabang untuk sampai ke daun terjauh dalam satu juta simpul pohon yang tidak seimbang sempurna padahal secara teori hanya butuh dua puluh cabang dalam pohon yang seimbang sempurna? Intinya adalah bahwa tidak perlu satu juta. Beralih dari kasus terburuk satu juta ke kasus terburuk empat puluh biasanya cukup baik; Anda tidak harus menggunakan casing yang optimal.
sumber
Keseimbangan adalah properti yang benar-benar halus; Anda pikir Anda tahu apa itu, tetapi sangat mudah untuk salah. Secara khusus, bahkan jawaban (baik) Eric Lippert tidak aktif. Itu karena pengertian ketinggian saja tidak cukup. Anda harus memiliki konsep tinggi minimum dan maksimum sebuah pohon (di mana tinggi minimum adalah jumlah langkah paling sedikit dari akar ke daun, dan maksimumnya adalah ... yah, Anda mengerti gambarannya). Mengingat itu, kita dapat mendefinisikan keseimbangan menjadi:
(Ini sebenarnya menyiratkan bahwa cabang-cabang itu sendiri seimbang; Anda dapat memilih cabang yang sama untuk maksimum dan minimum.)
Yang perlu Anda lakukan untuk memverifikasi properti ini hanyalah pelacakan traversal pohon sederhana dari kedalaman saat ini. Pertama kali Anda mundur, itu memberi Anda kedalaman dasar. Setiap kali setelah itu ketika Anda mundur, Anda membandingkan kedalaman baru dengan baseline
Dalam kode:
Saya kira Anda dapat melakukan ini tanpa menggunakan pola Observer, tetapi saya merasa lebih mudah untuk bernalar seperti ini.
[EDIT]: Mengapa Anda tidak bisa hanya mengukur tinggi setiap sisi. Pertimbangkan pohon ini:
OK, berantakan sedikit, tapi setiap sisi akar yang seimbang:
C
adalah kedalaman 2,A
,B
,D
,E
yang kedalaman 3, danF
,G
,H
,J
yang mendalam 4. Ketinggian cabang kiri adalah 2 (ingat ketinggian berkurang ketika Anda melintasi cabang), tinggi cabang kanan adalah 3. Namun pohon secara keseluruhan tidak seimbang karena ada perbedaan ketinggian 2 antaraC
danF
. Anda memerlukan spesifikasi minimax (meskipun algoritme sebenarnya bisa kurang rumit karena hanya boleh ada dua ketinggian yang diizinkan).sumber
Ini hanya menentukan jika tingkat teratas pohon seimbang. Artinya, Anda dapat memiliki pohon dengan dua cabang panjang dari paling kiri dan paling kanan, dengan tidak ada apapun di tengah, dan ini akan menjadi kenyataan. Anda perlu memeriksa secara rekursif
root.left
danroot.right
untuk melihat apakah mereka seimbang secara internal juga sebelum mengembalikan true.sumber
Respon latihan bonus. Solusi sederhana. Jelas dalam implementasi nyata seseorang mungkin membungkus ini atau sesuatu untuk menghindari keharusan pengguna untuk memasukkan tinggi dalam respon mereka.
sumber
out height
notasi variabel " ")Solusi pasca pesanan, lintasi pohon hanya sekali. Kompleksitas waktu adalah O (n), spasi adalah O (1), lebih baik daripada solusi top-down. Saya memberi Anda implementasi versi java.
sumber
left == -1
maksudnya Kapan itu akan terjadi? Apakah kita menganggap panggilan rekursif menyiratkan bahwaleft == -1
benar jika semua subpohon dari anak kiri tidak seimbang?left == 1
berarti subpohon kiri tidak seimbang, maka seluruh pohon tidak seimbang. Kami tidak perlu lagi memeriksa subtree kanan, dan dapat kembali-1
.Definisi dari pohon biner dengan keseimbangan tinggi adalah:
Jadi, pohon biner kosong selalu memiliki keseimbangan tinggi.
Pohon biner yang tidak kosong memiliki keseimbangan tinggi jika:
Pertimbangkan pohonnya:
Seperti yang terlihat, subtree kiri
A
adalah tinggi-seimbang (karena kosong) dan begitu pula subtree kanannya. Tetapi tetap saja pohon tersebut tidak seimbang ketinggiannya karena kondisi 3 tidak terpenuhi karena tinggi sub-pohon kiri0
dan tinggi sub-pohon kanan adalah2
.Juga pohon berikut tidak seimbang ketinggiannya meskipun tinggi sub-pohon kiri dan kanan sama. Kode Anda yang ada akan kembali menjadi true untuk itu.
Jadi kata setiap def sangat penting.
Ini akan berhasil:
Tautan Ideone
sumber
Jika pohon biner seimbang atau tidak dapat diperiksa oleh Level order traversal:
sumber
Ini dibuat jauh lebih rumit dari yang sebenarnya.
Algoritmanya adalah sebagai berikut:
Misalkan B = kedalaman node level terendah
Jika abs (AB) <= 1, maka pohon tersebut seimbang
sumber
Apa arti seimbang tergantung sedikit pada struktur yang ada. Misalnya, B-Tree tidak dapat memiliki node lebih dari kedalaman tertentu dari root, atau kurang dalam hal ini, semua data berada pada kedalaman tetap dari root, tetapi bisa tidak seimbang jika distribusi daun ke daun. -tetapi-satu node tidak rata. Daftar yang dilewati Sama sekali tidak memiliki gagasan tentang keseimbangan, sebaliknya mengandalkan kemungkinan untuk mencapai kinerja yang layak. Pohon Fibonacci sengaja tidak seimbang, menunda penyeimbangan kembali untuk mencapai performa asimtotik superior sebagai ganti pembaruan yang kadang-kadang lebih lama. Pohon AVL dan Red-Black melampirkan metadata ke setiap node untuk mencapai invarian keseimbangan kedalaman.
Semua struktur ini dan lainnya ada di pustaka standar sistem pemrograman yang paling umum (kecuali python, RAGE!). Menerapkan satu atau dua adalah praktik pemrograman yang baik, tetapi mungkin ini bukan penggunaan waktu yang baik untuk menggulirkan waktu Anda sendiri untuk produksi, kecuali masalah Anda memiliki beberapa kinerja khusus yang tidak perlu dipenuhi oleh koleksi off-the-shelf.
sumber
Catatan 1: Tinggi setiap sub-pohon dihitung hanya sekali.
Catatan 2: Jika sub-pohon kiri tidak seimbang maka perhitungan sub-pohon kanan, yang berpotensi mengandung jutaan elemen, dilewati.
sumber
Penyeimbangan biasanya bergantung pada panjang jalur terpanjang di setiap arah. Algoritme di atas tidak akan melakukannya untuk Anda.
Apa yang Anda coba terapkan? Ada pohon yang menyeimbangkan diri di sekitar (AVL / Merah-hitam). Padahal, pohon Jawa itu seimbang.
sumber
Jika ini untuk pekerjaan Anda , saya sarankan:
sumber
sumber
1
(sama untuk minDepth). Kedalaman yang benar seharusnya0
. Akar pohon selalu0
dalamBerikut adalah solusi lengkap yang telah diuji di C # (maaf saya bukan java dev) (cukup salin tempel di aplikasi konsol). Saya tahu bahwa definisi keseimbangan bervariasi sehingga tidak semua orang menyukai hasil pengujian saya, tetapi harap lihat pendekatan yang sedikit berbeda untuk memeriksa kedalaman / tinggi dalam loop rekursif dan keluar pada ketidakcocokan pertama tanpa menyimpan ketinggian / level / kedalaman node pada setiap node (hanya mempertahankannya dalam panggilan fungsi).
sumber
sumber
RE: solusi @ lucky menggunakan BFS untuk melakukan traversal tingkat-tingkat.
Kami melintasi pohon dan menyimpan referensi ke vars min / max-level yang menggambarkan level minimum di mana node adalah daun.
Saya yakin bahwa solusi @lucky memerlukan modifikasi. Seperti yang disarankan oleh @codaddict, daripada memeriksa apakah sebuah node adalah daun, kita harus memeriksa apakah salah satu anak kiri atau kanan adalah null (bukan keduanya). Jika tidak, algoritme akan menganggap ini sebagai pohon seimbang yang valid:
Dengan Python:
Solusi ini harus memenuhi semua ketentuan yang diberikan dalam pertanyaan awal, beroperasi dalam ruang O (n) waktu dan O (n). Kelebihan memori akan diarahkan ke heap daripada meniup tumpukan panggilan rekursif.
Alternatifnya, awalnya kita bisa melintasi pohon untuk menghitung + ketinggian maksimum cache untuk setiap subpohon akar secara berulang. Kemudian dalam proses iteratif lainnya, periksa apakah ketinggian cache dari subpohon kiri dan kanan untuk setiap root tidak pernah berbeda lebih dari satu. Ini juga akan berjalan dalam O (n) waktu dan O (n) ruang tetapi secara berulang agar tidak menyebabkan stack overflow.
sumber
Nah, Anda membutuhkan cara untuk menentukan ketinggian kiri dan kanan, dan apakah seimbang kiri dan kanan.
Dan aku baru saja
return height(node->left) == height(node->right);
Mengenai menulis
height
fungsi, baca: Memahami rekursisumber
Pohon jenis apa yang kamu bicarakan? Ada pohon penyeimbang diri di luar sana. Periksa algoritme mereka di mana mereka menentukan apakah mereka perlu menyusun ulang pohon untuk menjaga keseimbangan.
sumber
Ini adalah versi berdasarkan traversal kedalaman-pertama yang umum. Harus lebih cepat dari jawaban benar lainnya dan menangani semua "tantangan" yang disebutkan. Maaf untuk gayanya, saya tidak terlalu tahu Java.
Anda masih dapat membuatnya lebih cepat dengan kembali lebih awal jika maks dan min sama-sama ditetapkan dan memiliki perbedaan> 1.
sumber
sumber
sumber
Inilah yang saya coba untuk latihan bonus Eric. Saya mencoba untuk melepas loop rekursif saya dan kembali segera setelah saya menemukan subtree tidak seimbang.
sumber
sumber
Pohon kosong memiliki keseimbangan tinggi. Pohon biner yang tidak kosong T diseimbangkan jika:
1) Subpohon kiri T seimbang
2) Subpohon kanan T seimbang
3) Perbedaan antara tinggi sub pohon kiri dan kanan tidak lebih dari 1.
Kompleksitas Waktu: O (n)
sumber
Untuk memiliki kinerja yang lebih baik khususnya pada pohon besar, Anda dapat menyimpan ketinggian di setiap node sehingga ini merupakan trade off space Vs kinerja:
Contoh penerapan penambahan dan sama untuk penghapusan
sumber
sumber
Bukankah ini akan berhasil?
Setiap pohon yang tidak seimbang akan selalu gagal dalam hal ini.
sumber