2012-04-15 12 views
27

Python, yığın veri yapısını kullanan ve bazı temel işlemleri (push, pop) destekleyen heapq modülüne sahiptir.Python: öbekten öğe silme

O (log n) içindeki ögeden i-th öğesi nasıl kaldırılır? heapq ile mümkün mü yoksa başka bir modül kullanmalı mıyım? - Bu ne istiyorum değil http://docs.python.org/library/heapq.html olası bir yaklaşımı tavsiye ediyorum:

Not belgelerin altındaki bir örnek yoktur. Öğenin kaldırılmasını, yalnızca kaldırıldığı gibi işaretlememesini istiyorum.

cevap

30

Sen oldukça kolay bir yığından i-inci elemanı kaldırabilirsiniz:

h[i] = h[-1] 
h.pop() 
heapq.heapify(h) 

Sadece yığın yeniden heapify son eleman ile kaldırmak ve daha sonra son öğe kaldırmak istediğiniz öğeyi değiştirin. O (n), eğer isterseniz O (log (n)) 'de de aynı şeyi yapabilirsiniz, ancak birkaç tane dahili heapify işlevini çağırmanız gerekir veya larsların işaret ettiği gibi daha iyi kendi koduna heapq.py dışına _siftup/_siftdown:

h[i] = h[-1] 
h.pop() 
if i < len(h): 
    heapq._siftup(h, i) 
    heapq._siftdown(h, 0, i) 

Not her durumda sadece o kadar h[i] = h.pop() eğer i referanslar son öğe başarısız olur yapamam. Eğer özel bir durumda son elemanı kaldıramazsanız üzerine yazmayı ve pop'i birleştirebilirsiniz.

Not Yığınınızda tipik boyutuna bağlı olarak bu teorik olarak daha az verimli _siftup/_siftdown yeniden kullanarak daha hızlı olabilir çıkarırken sadece heapify arayarak bulabileceği: içgözlem biraz heapify muhtemelen C uygulandığını ortaya çıkaracaktır ancak iç fonksiyonların C uygulaması ortaya çıkmaz. Performans sizin için önemliyse, hangisinin en iyi olduğunu görmek için tipik veriler üzerinde bazı zamanlama testleri yapmayı düşünün. Büyük kütlelere sahip olmadığınız sürece büyük-O en önemli faktör olmayabilir.

Düzenleme:

_siftdown gerekli değildir: birisi o bir yorum ile _siftdown çağrısını kaldırmak için bu cevabı düzenlemek için çalıştı. Yeni h [i], eski h [i] 'nin en küçüğüydü, ki bu da hala eski h [i]' nin ebeveyni (yeni h [i] 'nin ebeveyni)' den daha büyüktür. _siftdown bir op-op olacaktır. Henüz henüz bir yorum eklemek için yeterli temsilciniz olmadığından düzenlemeliyim. bu açıklamada kaçırdığınızı ne

h[-1] hiç h[i] bir çocuk olma ihtimalidir. h[i]'a eklenen yeni değer, yığının tamamen farklı bir dalından gelebilir, bu nedenle her iki yönde de elenmiş olması gerekebilir. Sadece yığın geri sort() kullanmayın neden soran yorumuna da

: _siftup ve _siftdown arayarak hem Ey heapify çağırarak, işlemleri (n log) olan O (n) 'dir.sort() çağrısı, bir O (n log n) işlemidir. Çağırma türünün yeterince hızlı olması mümkündür, ancak büyük yığınlar için gereksiz bir ek yüktür. @Seth Bruder tarafından işaret edilen sorundan kaçınmak için. i son öğeye başvurduğunda, _siftup() çağrısı başarısız olur, ancak bu durumda öbekten bir ögeyi haşatlamak, yığın değişmezini bozmaz.

+2

+1, “_siftup” tanımının @AlexMartelli tarafından önerilen programa kopyalanmasının daha temiz olacağını not ederek, [burada] (http://stackoverflow.com/questions/1465662/how-can- i uygulamaya geçirilen bir azalma-anahtarı-özelliğe-içinde-piton-heapq). –

+0

Teşekkürler, '_siftup' kesinlikle ilginç görünüyor! Btw., Neden sadece 'pop()' yerine pop (-1) '? –

+0

@EcirHana sadece kafamın tepesindeki varsayılanı hatırlayamadığım için. Ben hallettim. – Duncan

8

(a) Neden silmek istemediğinizi düşünün. Birçok durumda doğru çözümdür.

(b) Yığın bir listedir. Bir öğeyi diğer herhangi bir liste gibi dizine göre silebilirsiniz, ancak daha sonra, yığın değişmezini artık tatmin etmeyeceği için yeniden yığınlaştırmanız gerekecektir.

+0

(b)? Için bir referans ekleyebilir misiniz? – Zenon

+0

@Zenon b Hangi bölüm? Tercümanınızdaki bir nesnenin türüne bakabilir veya OP'nin bağladığı dokümanları okuyabilirsiniz; Yeniden toparlanmaya ihtiyaç duyulduğunda, bu tür bir işlemin yığın değişmezini ihlal eden bir listeye (bu belgede de verilmiştir) yol açtığı gerçeğinin bir sonucudur. – Marcin

+0

(a) - tembel silme tamamen geçerli, sadece yığınları daha iyi anlamak istiyorum. (b) En az O (log n) ile ilgileniyorum, yığınlama O (n) –