Bagaimana membangun tumpukan menjadi O (n) kompleksitas waktu?

494

Dapatkah seseorang membantu menjelaskan bagaimana membangun tumpukan menjadi O (n) kompleksitas?

Memasukkan item ke tumpukan adalah O(log n), dan memasukkan diulang n / 2 kali (sisanya adalah daun, dan tidak dapat melanggar properti tumpukan). Jadi, ini berarti kerumitannya O(n log n), saya pikir.

Dengan kata lain, untuk setiap item yang kami "timbihkan", ia berpotensi untuk menyaring satu kali untuk setiap level untuk tumpukan sejauh ini (yang merupakan level log n).

Apa yang saya lewatkan?

GBa
sumber
apa tepatnya yang Anda maksud dengan "membangun" tumpukan?
mfrankli
Seperti yang Anda lakukan di heapsort, ambil array yang tidak disortir dan saring setiap elemen setengah bagian atas hingga sesuai dengan aturan
heaport
2
Satu-satunya hal yang dapat saya temukan adalah tautan ini: Kompleksitas Buildheap tampaknya Θ (n lg n) - n panggilan ke Heapify dengan biaya Θ (lg n) per panggilan, tetapi hasil ini dapat ditingkatkan menjadi Θ (n) cs.txstate.edu/~ch04/webtest/teaching/courses/5329/lectures/…
GBa
2
@Gba tonton video ini dari MIT: Dia menjelaskan dengan baik bagaimana kita mendapatkan O (n), dengan sedikit matematika youtube.com/watch?v=B7hVxCmfPtM
CodeShadow
2
Tautan langsung ke penjelasan @CodeShadow menyebutkan: youtu.be/B7hVxCmfPtM?t=41m21s
sha1

Jawaban:

435

Saya pikir ada beberapa pertanyaan yang terkubur dalam topik ini:

  • Bagaimana Anda menerapkannya buildHeapsehingga berjalan dalam waktu O (n) ?
  • Bagaimana Anda menunjukkan bahwa buildHeapberjalan dalam O (n) waktu ketika diimplementasikan dengan benar?
  • Mengapa logika yang sama itu tidak berfungsi untuk membuat heap sort berjalan pada waktu O (n) daripada O (n log n) ?

Bagaimana Anda menerapkannya buildHeapsehingga berjalan dalam waktu O (n) ?

Seringkali, jawaban atas pertanyaan-pertanyaan ini fokus pada perbedaan antara siftUpdan siftDown. Membuat pilihan yang tepat antara siftUpdan siftDownsangat penting untuk mendapatkan kinerja O (n)buildHeap , tetapi tidak melakukan apa pun untuk membantu orang memahami perbedaan antara buildHeapdan heapSortsecara umum. Memang, implementasi yang tepat dari keduanya buildHeapdan hanyaheapSort akan digunakan . The Operasi hanya dibutuhkan untuk melakukan sisipan ke dalam tumpukan yang ada, sehingga akan digunakan untuk mengimplementasikan antrian prioritas menggunakan tumpukan biner, misalnya.siftDownsiftUp

Saya telah menulis ini untuk menjelaskan cara kerja tumpukan maks. Ini adalah tipe tumpukan yang biasanya digunakan untuk mengurutkan tumpukan atau untuk antrian prioritas di mana nilai yang lebih tinggi menunjukkan prioritas yang lebih tinggi. Tumpukan min juga berguna; misalnya, saat mengambil item dengan kunci integer dalam urutan menaik atau string dalam urutan abjad. Prinsip-prinsipnya persis sama; cukup alihkan urutan.

The properti tumpukan menetapkan bahwa setiap node dalam tumpukan biner harus setidaknya sama besar dengan kedua anak-anaknya. Secara khusus, ini menyiratkan bahwa item terbesar di heap adalah di root. Memilah ke bawah dan memilah pada dasarnya adalah operasi yang sama di arah yang berlawanan: pindahkan simpul yang menyinggung sampai memenuhi properti tumpukan:

  • siftDown menukar simpul yang terlalu kecil dengan anak terbesarnya (dengan demikian memindahkannya ke bawah) hingga setidaknya sama besar dengan kedua simpul di bawahnya.
  • siftUp menukar simpul yang terlalu besar dengan induknya (dengan demikian memindahkannya ke atas) sampai tidak lebih besar dari simpul di atasnya.

Jumlah operasi yang diperlukan untuk siftDowndan siftUpsebanding dengan jarak node mungkin harus bergerak. Sebab siftDown, itu jarak ke bagian bawah pohon, jadi siftDownmahal untuk node di bagian atas pohon. Dengan siftUp, pekerjaan sebanding dengan jarak ke puncak pohon, jadi siftUpmahal untuk simpul di bagian bawah pohon. Meskipun kedua operasi adalah O (log n) dalam kasus terburuk, dalam heap, hanya satu node di bagian atas sedangkan setengah dari node terletak di lapisan bawah. Jadi seharusnya tidak terlalu mengejutkan bahwa jika kita harus menerapkan operasi ke setiap node, kita akan memilih siftDownlebih siftUp.

The buildHeapFungsi mengambil array dari item disortir dan bergerak mereka sampai mereka semua memenuhi properti heap, sehingga menghasilkan tumpukan valid. Ada dua pendekatan yang mungkin diambil untuk buildHeapmenggunakan siftUpdan siftDownoperasi yang telah kami jelaskan.

  1. Mulai di bagian atas tumpukan (awal array) dan panggil siftUpsetiap item. Pada setiap langkah, item yang diayak sebelumnya (item sebelum item saat ini dalam array) membentuk heap yang valid, dan menyaring item berikutnya menempatkannya ke posisi yang valid di heap. Setelah menyaring setiap node, semua item memenuhi properti heap.

  2. Atau, pergi ke arah yang berlawanan: mulai dari ujung array dan bergerak mundur ke arah depan. Pada setiap iterasi, Anda menyaring suatu barang sampai berada di lokasi yang benar.

Untuk implementasi mana buildHeapyang lebih efisien?

Kedua solusi ini akan menghasilkan tumpukan yang valid. Tidak mengherankan, yang lebih efisien adalah operasi kedua yang digunakan siftDown.

Biarkan h = log n mewakili ketinggian tumpukan. Pekerjaan yang dibutuhkan untuk siftDownpendekatan diberikan oleh penjumlahan

(0 * n/2) + (1 * n/4) + (2 * n/8) + ... + (h * 1).

Setiap istilah dalam penjumlahan memiliki jarak maksimum, sebuah simpul pada ketinggian tertentu harus bergerak (nol untuk lapisan bawah, h untuk root) dikalikan dengan jumlah simpul pada ketinggian itu. Sebaliknya, jumlah untuk memanggil siftUpsetiap node adalah

(h * n/2) + ((h-1) * n/4) + ((h-2)*n/8) + ... + (0 * 1).

Harus jelas bahwa jumlah kedua lebih besar. Istilah pertama saja adalah hn / 2 = 1/2 n log n , jadi pendekatan ini memiliki kompleksitas paling baik O (n log n) .

Bagaimana kita membuktikan bahwa jumlah untuk siftDownpendekatan itu memang O (n) ?

Salah satu metode (ada analisis lain yang juga berfungsi) adalah mengubah jumlah terbatas menjadi seri tak terbatas dan kemudian menggunakan seri Taylor. Kami dapat mengabaikan istilah pertama, yaitu nol:

Seri Taylor untuk kompleksitas buildHeap

Jika Anda tidak yakin mengapa masing-masing langkah itu berhasil, berikut ini adalah pembenaran untuk proses ini dengan kata-kata:

  • Persyaratannya semua positif, sehingga jumlah yang terbatas harus lebih kecil dari jumlah yang tak terbatas.
  • Seri ini sama dengan seri daya yang dievaluasi pada x = 1/2 .
  • Rangkaian daya itu sama dengan (waktu konstan) turunan dari deret Taylor untuk f (x) = 1 / (1-x) .
  • x = 1/2 berada dalam interval konvergensi dari seri Taylor itu.
  • Oleh karena itu, kita dapat mengganti seri Taylor dengan 1 / (1-x) , membedakan, dan mengevaluasi untuk menemukan nilai seri infinite.

Karena jumlah tak terhingga tepat n , kami menyimpulkan bahwa jumlah terbatas tidak lebih besar, dan karenanya, O (n) .

Mengapa tumpukan membutuhkan waktu O (n log n) ?

Jika dimungkinkan untuk berjalan buildHeapdalam waktu linier, mengapa sortir heap membutuhkan waktu O (n log n) ? Nah, heap sort terdiri dari dua tahap. Pertama, kita memanggil buildHeaparray, yang membutuhkan waktu O (n) jika diterapkan secara optimal. Tahap selanjutnya adalah berulang kali menghapus item terbesar di heap dan meletakkannya di akhir array. Karena kami menghapus item dari heap, selalu ada tempat terbuka tepat setelah akhir heap di mana kami dapat menyimpan item. Jadi heap sort mencapai urutan diurutkan dengan secara berturut-turut menghapus item terbesar berikutnya dan memasukkannya ke dalam array mulai dari posisi terakhir dan bergerak ke arah depan. Kompleksitas dari bagian terakhir inilah yang mendominasi dalam heap sort. Loop terlihat seperti ini:

for (i = n - 1; i > 0; i--) {
    arr[i] = deleteMax();
}

Jelas, loop menjalankan O (n) kali ( n - 1 tepatnya, item terakhir sudah ada di tempatnya). Kompleksitas deleteMaxuntuk heap adalah O (log n) . Ini biasanya diterapkan dengan menghapus root (item terbesar yang tersisa di heap) dan menggantinya dengan item terakhir di heap, yang merupakan daun, dan karena itu salah satu item terkecil. Root baru ini hampir pasti akan melanggar properti heap, jadi Anda harus menelepon siftDownsampai Anda memindahkannya kembali ke posisi yang dapat diterima. Ini juga memiliki efek memindahkan item terbesar berikutnya ke root. Perhatikan bahwa, berbeda dengan buildHeaptempat sebagian besar simpul yang kita panggil siftDowndari bawah pohon, kita sekarang memanggil siftDowndari atas pohon pada setiap iterasi!Meskipun pohon menyusut, ia tidak menyusut cukup cepat : Ketinggian pohon tetap konstan sampai Anda telah menghapus setengah bagian pertama dari simpul (ketika Anda membersihkan lapisan bawah sepenuhnya). Kemudian untuk kuartal berikutnya, ketinggiannya adalah h - 1 . Jadi total pekerjaan untuk tahap kedua ini adalah

h*n/2 + (h-1)*n/4 + ... + 0 * 1.

Perhatikan sakelar: sekarang kasus kerja nol berhubungan dengan satu node dan kasus kerja h sesuai dengan setengah node. Jumlah ini adalah O (n log n) sama seperti versi tidak efisien buildHeapyang diimplementasikan menggunakan siftUp. Tetapi dalam kasus ini, kami tidak punya pilihan karena kami mencoba menyortir dan kami membutuhkan item terbesar berikutnya dihapus berikutnya.

Singkatnya, pekerjaan untuk heap sort adalah jumlah dari dua tahap: O (n) waktu untuk buildHeap dan O (n log n) untuk menghapus setiap node secara berurutan , sehingga kompleksitasnya adalah O (n log n) . Anda dapat membuktikan (menggunakan beberapa ide dari teori informasi) bahwa untuk jenis berbasis perbandingan, O (n log n) adalah yang terbaik yang bisa Anda harapkan, jadi tidak ada alasan untuk kecewa dengan ini atau mengharapkan tumpukan tumpukan untuk mencapai O (n) batas waktu yang buildHeapberlaku.

Jeremy West
sumber
2
Saya mengedit jawaban saya untuk menggunakan max heap karena tampaknya kebanyakan orang merujuk pada itu dan itu adalah pilihan terbaik untuk heap sort.
Jeremy West
28
Inilah yang membuatnya jelas secara intuitif bagi saya: "hanya satu node di atas sedangkan setengah node terletak di lapisan bawah. Jadi seharusnya tidak terlalu mengejutkan bahwa jika kita harus menerapkan operasi ke setiap node, kita akan lebih suka siftDown daripada siftUp. "
Vicky Chijwani
3
@JeremyWest "Pertama adalah mulai dari atas heap (awal array) dan panggil siftUp pada setiap item." - Apakah maksud Anda mulai dari bagian bawah tumpukan?
aste123
4
@ aste123 Tidak, ini benar seperti yang tertulis. Idenya adalah untuk mempertahankan penghalang antara bagian array yang memenuhi properti heap dan bagian array yang tidak disortir. Anda bisa mulai dari awal bergerak maju dan memanggil siftUpsetiap item atau mulai dari akhir bergerak mundur dan memanggil siftDown. Apa pun pendekatan yang Anda pilih, Anda memilih item berikutnya di bagian array yang tidak disortir dan melakukan operasi yang sesuai untuk memindahkannya ke posisi yang valid di bagian array yang dipesan. Satu-satunya perbedaan adalah kinerja.
Jeremy West
2
Ini adalah jawaban terbaik yang pernah saya lihat untuk setiap pertanyaan di dunia. Itu dijelaskan dengan sangat baik, saya seperti apakah itu benar-benar mungkin ... terima kasih banyak.
HARSHIL JAIN
314

Analisis Anda benar. Namun, tidak ketat.

Tidak mudah menjelaskan mengapa membangun heap adalah operasi linier, Anda sebaiknya membacanya.

Sebuah analisis yang besar dari algoritma dapat dilihat di sini .


Gagasan utamanya adalah bahwa dalam build_heapalgoritma, heapifybiaya sebenarnya bukan O(log n)untuk semua elemen.

Ketika heapifydipanggil, waktu berjalan tergantung pada seberapa jauh suatu elemen mungkin bergerak turun di pohon sebelum proses berakhir. Dengan kata lain, itu tergantung pada ketinggian elemen di heap. Dalam kasus terburuk, elemen mungkin turun sampai ke tingkat daun.

Mari kita hitung pekerjaan yang dilakukan level demi level.

Di level paling bawah, ada 2^(h)node, tapi kami tidak memanggil heapifysemua ini, jadi pekerjaannya adalah 0. Pada level berikutnya ada 2^(h − 1)node, dan masing-masing mungkin turun 1 level. Di level 3 dari bawah, ada 2^(h − 2)node, dan masing-masing mungkin turun 2 level.

Seperti yang Anda lihat tidak semua operasi heapify O(log n), inilah mengapa Anda mendapatkannya O(n).

emre nevayeshirazi
sumber
17
Ini adalah penjelasan yang bagus ... tapi mengapa kemudian heap-sort berjalan di O (n log n). Mengapa alasan yang sama tidak berlaku untuk heap-sort?
hba
49
@ hba Saya pikir jawaban atas pertanyaan Anda terletak pada pemahaman gambar ini dari artikel ini . Heapifyadalah O(n)ketika dilakukan dengan siftDowntetapi O(n log n)saat dilakukan dengan siftUp. Penyortiran yang sebenarnya (menarik item dari tumpukan satu per satu) harus dilakukan dengan siftUpdemikian adalah karena itu O(n log n).
The111
3
Saya sangat suka penjelasan intuitif dokumen eksternal Anda di bagian bawah.
Lukas Greblikas
1
@hba Jawaban di bawah ini oleh Jeremy West menjawab pertanyaan Anda dengan lebih detail, mudah dipahami, menjelaskan lebih lanjut jawaban komentar The111 di sini.
cellepo
Pertanyaan. Menurut saya # perbandingan yang dibuat untuk sebuah simpul yang tingginya idari bawah pohon yang tinggi h harus membuat 2* log(h-i)perbandingan juga dan harus diperhitungkan juga @ The111. Bagaimana menurut anda?
Sid
94

Secara intuitif:

"Kompleksitasnya harus O (nLog n) ... untuk setiap item yang kami" timbun ", ia berpotensi untuk menyaring satu kali untuk setiap level untuk heap sejauh ini (yang merupakan level log n)."

Tidak terlalu. Logika Anda tidak menghasilkan ikatan yang ketat - logika ini memperkirakan kerumitan setiap tumpukan. Jika dibangun dari bawah ke atas, penyisipan (heapify) bisa menjadi jauh lebih sedikit O(log(n)). Prosesnya adalah sebagai berikut:

(Langkah 1) Elemen pertama n/2pergi di baris bawah tumpukan. h=0, jadi heapify tidak diperlukan.

(Langkah 2) Elemen berikutnya berjalan di baris 1 ke atas dari bawah. , heapify filter 1 level ke bawah.n/22h=1

(Langkah i ) Elemen berikutnya berjalan berurutan dari bawah. , naikkan tingkat filter ke bawah.n/2iih=ii

(Langkah log (n) ) Elemen terakhir berjalan berturut - turut dari bawah. , naikkan tingkat filter ke bawah.n/2log2(n) = 1log(n)h=log(n)log(n)

PEMBERITAHUAN: bahwa setelah langkah pertama, 1/2elemen (n/2)sudah ada di heap, dan kami bahkan tidak perlu memanggil heapify sekali. Juga, perhatikan bahwa hanya satu elemen, root, yang sebenarnya menimbulkan log(n)kompleksitas penuh .


Secara teoretis:

Langkah-langkah Total Nuntuk membangun tumpukan ukuran n, dapat ditulis secara matematis.

Pada ketinggian i, kami telah menunjukkan (di atas) bahwa akan ada elemen yang perlu disebut heapify, dan kami tahu heapify pada ketinggian itu . Ini memberi:n/2i+1iO(i)

masukkan deskripsi gambar di sini

Solusi untuk penjumlahan terakhir dapat ditemukan dengan mengambil turunan dari kedua sisi dari persamaan deret geometri yang terkenal:

masukkan deskripsi gambar di sini

Akhirnya, memasukkan x = 1/2ke dalam persamaan menghasilkan di atas 2. Memasukkan ini ke persamaan pertama memberi:

masukkan deskripsi gambar di sini

Dengan demikian, jumlah langkah adalah ukuran O(n)

bcorso
sumber
35

Ini akan menjadi O (n log n) jika Anda membangun heap dengan memasukkan elemen berulang kali. Namun, Anda dapat membuat tumpukan baru lebih efisien dengan memasukkan elemen-elemen dalam urutan sewenang-wenang dan kemudian menerapkan algoritma untuk "menumpuk" mereka ke dalam urutan yang tepat (tergantung pada jenis tumpukan tentu saja).

Lihat http://en.wikipedia.org/wiki/Binary_heap , "Membangun heap" sebagai contoh. Dalam hal ini Anda pada dasarnya bekerja dari tingkat bawah pohon, bertukar simpul induk dan anak hingga kondisi tumpukan terpenuhi.

mike__t
sumber
12

Sudah ada beberapa jawaban bagus tetapi saya ingin menambahkan sedikit penjelasan visual

masukkan deskripsi gambar di sini

Sekarang, lihat gambar, ada
n/2^1 simpul hijau dengan tinggi 0 (di sini 23/2 = 12)
n/2^2 simpul merah dengan tinggi 1 (di sini 23/4 = 6)
n/2^3 simpul biru dengan tinggi 2 (di sini 23/8 = 3)
n/2^4 simpul ungu dengan tinggi 3 (di sini 23/16 = 2)
sehingga ada n/2^(h+1)simpul untuk tinggi h
Untuk menemukan kompleksitas waktu mari kita hitung jumlah pekerjaan yang dilakukan atau maks tidak dari iterasi yang dilakukan oleh setiap node
sekarang dapat diperhatikan bahwa setiap node dapat melakukan iterasi (paling jauh) == ketinggian node

Green  = n/2^1 * 0 (no iterations since no children)  
red    = n/2^2 * 1 (heapify will perform atmost one swap for each red node)  
blue   = n/2^3 * 2 (heapify will perform atmost two swaps for each blue node)  
purple = n/2^4 * 3 (heapify will perform atmost three swaps for each purple node)   

jadi untuk setiap node dengan tinggi h pekerjaan maksimum yang dilakukan adalah n / 2 ^ (h + 1) * h

Sekarang total pekerjaan yang dilakukan adalah

->(n/2^1 * 0) + (n/2^2 * 1)+ (n/2^3 * 2) + (n/2^4 * 3) +...+ (n/2^(h+1) * h)  
-> n * ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

sekarang untuk nilai h , urutannya

-> ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

tidak akan pernah melebihi 1
Dengan demikian kompleksitas waktu tidak akan pernah melebihi O (n) untuk membangun timbunan

Julkar9
sumber
7

Seperti yang kita ketahui ketinggian heap adalah log (n) , di mana n adalah jumlah total elemen.    Mari kita menggambarkannya sebagai h
Ketika kita melakukan operasi heapify, maka elemen pada level terakhir ( h ) tidak akan bergerak bahkan satu pun langkah.
   Jumlah elemen pada level terakhir kedua ( h-1 ) adalah 2 jam-1 dan mereka dapat bergerak pada level maksimal 1 (selama heapify).
   Demikian pula, untuk i th , tingkat kita memiliki 2 i elemen yang dapat bergerak hi posisi.

Oleh karena itu jumlah total gerakan = S = 2 jam * 0 + 2 jam-1 * 1 + 2 jam-2 * 2 + ... 2 0 * jam

                                               S = 2 jam {1/2 + 2/2 2 + 3/2 3 + ... jam / 2 jam } ----------------------- -------------------------- 1
ini adalah seri AGP , untuk menyelesaikan ini bagi kedua belah pihak dengan 2
                                               S / 2 = 2 jam {1/2 2 + 2/2 3 + ... h / 2 h + 1 } --------------------------------- ---------------- 2
pengurangan persamaan 2 dari 1 menghasilkan
                                               S / 2 = 2 jam { 1/2 + 1/2 2 + 1/2 3 + ... + 1 / 2 jam + h / 2 jam + 1 }
                                               S = 2 h + 1 {1/2 + 1/2 2 + 1/2 3 + ... + 1/2 h + h / 2 h + 1 }
sekarang 1/2 + 1/2 2 + 1/2 3 + ... + 1/2 jam adalah penurunan GP yang jumlahnya kurang dari 1 (ketika h cenderung tak terhingga, jumlahnya cenderung ke 1). Dalam analisis lebih lanjut, mari kita ambil batas atas jumlah yaitu 1.
Ini memberi S = 2 jam + 1 {1 + jam / 2 jam + 1 }
                    = 2 jam + 1 + jam
                    ~ 2 jam + jam
sebagai h = log (n) , 2 jam = n

Oleh karena itu S = n + log (n)
T (C) = O (n)

Tanuj Yadav
sumber
6

Saat membangun tumpukan, katakanlah Anda mengambil pendekatan dari bawah ke atas.

  1. Anda mengambil setiap elemen dan membandingkannya dengan anak-anak untuk memeriksa apakah pasangan sesuai dengan aturan tumpukan. Jadi, oleh karena itu, daun dimasukkan ke dalam tumpukan gratis. Itu karena mereka tidak memiliki anak.
  2. Bergerak ke atas, skenario kasus terburuk untuk simpul tepat di atas daun adalah perbandingan 1 (maksimal mereka akan dibandingkan dengan hanya satu generasi anak-anak)
  3. Beranjak lebih jauh ke atas, orang tua langsung mereka dapat maksimal dibandingkan dengan dua generasi anak.
  4. Melanjutkan ke arah yang sama, Anda akan memiliki perbandingan (n) perbandingan untuk root dalam skenario kasus terburuk. dan log (n) -1 untuk anak-anak terdekatnya, log (n) -2 untuk anak-anak langsung mereka dan seterusnya.
  5. Jadi, simpulkan semuanya, Anda tiba pada sesuatu seperti log (n) + {log (n) -1} * 2 + {log (n) -2} * 4 + ..... + 1 * 2 ^ {( logn) -1} yang tidak lain adalah O (n).
Jones
sumber
2

Dalam hal membangun heap, kita mulai dari ketinggian, logn -1 (di mana logn adalah ketinggian pohon elemen n). Untuk setiap elemen yang hadir pada ketinggian 'h', kita turun pada ketinggian max upto (logn -h).

    So total number of traversal would be:-
    T(n) = sigma((2^(logn-h))*h) where h varies from 1 to logn
    T(n) = n((1/2)+(2/4)+(3/8)+.....+(logn/(2^logn)))
    T(n) = n*(sigma(x/(2^x))) where x varies from 1 to logn
     and according to the [sources][1]
    function in the bracket approaches to 2 at infinity.
    Hence T(n) ~ O(n)
Kartik Goyal
sumber
1

Penyisipan yang berurutan dapat dijelaskan dengan:

T = O(log(1) + log(2) + .. + log(n)) = O(log(n!))

Oleh n! =~ O(n^(n + O(1)))karena itu, pendekatan StarlingT =~ O(nlog(n))

Semoga ini bisa membantu, cara optimal O(n)menggunakan algoritma build heap untuk himpunan tertentu (pemesanan tidak masalah).

Tomer Shalev
sumber
1

Pada dasarnya, pekerjaan dilakukan hanya pada node non-daun sambil membangun heap ... dan pekerjaan yang dilakukan adalah jumlah swapping untuk memenuhi kondisi heap ... dengan kata lain (dalam kasus terburuk) jumlahnya sebanding dengan tinggi dari node ... semuanya kompleksitas masalah sebanding dengan jumlah ketinggian semua node non-daun .. yang (2 ^ h + 1 - 1) -h-1 = nh-1 = Di)

Shubham Jindal
sumber
1

@ bcorso telah menunjukkan bukti analisis kompleksitas. Namun demi analisis kompleksitas yang masih dipelajari, saya ingin menambahkan ini:

Dasar dari kesalahan awal Anda adalah karena salah tafsir dari makna pernyataan, "memasukkan ke tumpukan membutuhkan O (log n) waktu". Memasukkan ke dalam tumpukan memang O (log n), tetapi Anda harus mengenali bahwa n adalah ukuran tumpukan selama penyisipan .

Dalam konteks memasukkan n objek ke dalam tumpukan, kompleksitas dari penyisipan ke-i adalah O (log n_i) di mana n_i adalah ukuran tumpukan seperti pada saat penyisipan i. Hanya penyisipan terakhir yang memiliki kompleksitas O (log n).

N.Vegeta
sumber
1

Mari kita anggap Anda memiliki elemen N di tumpukan. Maka tingginya akan menjadi Log (N)

Sekarang Anda ingin memasukkan elemen lain, maka kompleksitasnya adalah: Log (N) , kita harus membandingkan semua jalan UP ke root.

Sekarang Anda memiliki N + 1 elemen & tinggi = Log (N + 1)

Menggunakan teknik induksi dapat dibuktikan bahwa kompleksitas penyisipan akan menjadi logo .

Sekarang menggunakan

log a + log b = log ab

Ini disederhanakan menjadi: ∑logi = log (n!)

yang sebenarnya O (NlogN)

Tapi

kami melakukan sesuatu yang salah di sini, karena dalam semua kasus kami tidak mencapai di atas. Karena itu ketika menjalankan sebagian besar waktu kita mungkin menemukan itu, kita bahkan tidak akan naik setengah pohon. Dari mana, ikatan ini dapat dioptimalkan untuk memiliki ikatan lain yang lebih ketat dengan menggunakan matematika yang diberikan dalam jawaban di atas.

Kesadaran ini datang kepada saya setelah detail & percobaan pada tumpukan.

Fooo
sumber
0

Saya sangat suka penjelasan oleh Jeremy west .... pendekatan lain yang sangat mudah dipahami diberikan di sini http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity

karena, buildheap tergantung menggunakan tergantung pada heapify dan pendekatan shiftdown digunakan yang tergantung pada jumlah ketinggian semua node. Jadi, untuk menemukan jumlah tinggi node yang diberikan oleh S = penjumlahan dari i = 0 ke i = h dari (2 ^ i * (hi)), di mana h = logn adalah ketinggian penyelesaian pohon s, kita dapatkan s = 2 ^ (h + 1) - 1 - (h + 1) karena, n = 2 ^ (h + 1) - 1 s = n - h - 1 = n-logn - 1 s = O (n), dan kompleksitas buildheap adalah O (n).

Nitish Jain
sumber
0

"Batas waktu linier build Heap, dapat ditunjukkan dengan menghitung jumlah ketinggian semua node di heap, yang merupakan jumlah maksimum garis putus-putus. Untuk pohon biner sempurna tinggi h berisi N = 2 ^ ( h + 1) - 1 node, jumlah dari ketinggian node adalah N - H - 1. Dengan demikian itu adalah O (N). "

sec3
sumber
0

Bukti O (n)

Buktinya tidak mewah, dan cukup mudah, saya hanya membuktikan kasus untuk pohon biner penuh, hasilnya bisa digeneralisasi untuk pohon biner lengkap.

Yi Y
sumber
0

Kita mendapatkan runtime untuk heap build dengan mencari tahu langkah maksimum yang dapat diambil setiap node. Jadi kita perlu tahu berapa banyak node di setiap baris dan seberapa jauh dari masing-masing node.

Mulai dari simpul akar setiap baris berikutnya memiliki dua kali lipat node daripada baris sebelumnya, jadi dengan menjawab seberapa sering kita dapat menggandakan jumlah node sampai kita tidak memiliki node yang tersisa kita mendapatkan ketinggian pohon. Atau dalam istilah matematika ketinggian pohon adalah log2 (n), n menjadi panjang array.

Untuk menghitung node dalam satu baris kita mulai dari belakang, kita tahu n / 2 node berada di bagian bawah, jadi dengan membagi 2 kita mendapatkan baris sebelumnya dan seterusnya.

Berdasarkan ini kita mendapatkan formula ini untuk pendekatan Siftdown: (0 * n / 2) + (1 * n / 4) + (2 * n / 8) + ... + (log2 (n) * 1)

Istilah dalam paranthesis terakhir adalah ketinggian pohon dikalikan dengan satu simpul yang ada di akar, istilah dalam paranthesis pertama adalah semua simpul di baris bawah dikalikan dengan panjangnya, 0. Formula yang sama dalam cerdas: masukkan deskripsi gambar di sini

Matematika

Membawa n kembali kita memiliki 2 * n, 2 dapat dibuang karena itu konstan dan tada kita memiliki runtime kasus terburuk dari pendekatan Siftdown: n.

Max Tromp
sumber
-6

pikir kamu membuat kesalahan. Lihatlah ini: http://golang.org/pkg/container/heap/ Membangun heap bukan O (n). Namun, memasukkan adalah O (lg (n). Saya mengasumsikan inisialisasi adalah O (n) jika Anda menetapkan ukuran tumpukan b / c tumpukan perlu mengalokasikan ruang dan mengatur struktur data. Jika Anda memiliki n item untuk dimasukkan ke heap maka ya, setiap insert adalah lg (n) dan ada item n, jadi Anda mendapatkan n * lg (n) seperti yang dinyatakan

Mike Schachter
sumber
2
tidak itu tidak ketat. analisis yang lebih ketat dari build heap yields O (n)
emre nevayeshirazi
sepertinya itu perkiraan. Kutipan dalam artikel yang dia referensikan adalah "Intuisi adalah bahwa sebagian besar panggilan untuk heapify berada di tumpukan yang sangat singkat" Namun ini membuat beberapa asumsi. Agaknya, untuk tumpukan besar, skenario kasus terburuk akan tetap O (n * lg (n)), bahkan jika biasanya Anda bisa mendekati O (n). Tapi saya bisa saja salah
Mike Schachter
Ya itu adalah jawaban intuitif saya juga, tetapi referensi seperti status wikipedia "Tumpukan dengan n elemen dapat dibangun dari bawah ke atas di O (n)."
GBa
1
Saya sedang memikirkan struktur data yang sepenuhnya diurutkan. Saya lupa sifat spesifik tumpukan.
Mike Schachter