Kuyruk, Yığın, Bağlı Listeler: 17. Kuyruk Ve Yığın Yapısının Kütüphane Ile Uygulanması

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

Kuyruk, yığın, bağlı listeler

17. Kuyruk ve yığın yapısının kütüphane ile uygulanması


Önceki dersimizde kuyruk ve yığın uygulaması yapmıştık. Bu hafta benzer işlemleri farklı bir
kütüphane ile yapacağız. Çünkü; Python’da hazır gelen list veri tipi her ne kadar kuyruk/yığın için
kullanılabiliyor olsa da doğrudan bu iş için tasarlanmamıştır; list türü, genel amaçlı bir türdür. Kuyruk
ve yığın uygulamalarında performansın önemli olduğu bir çalışma yapılacaksa, diğer araçlar daha
verimli olabilmektedir. Ayrıca birazdan kullanacağımız araçların uygulamada katkı sağlayacak başka
özellikleri de bulunmaktadır.
Python ile kuyruk/yığın uygulaması yapmak için kullanılabilecek araçlar şu şekildedir:
• list
• collections modülü (dequeue)
• queue modülü (biz dersteki uygulamada bunu tercih edeceğiz)
Bu araçlardan ilkini zaten kullandık. Şimdi de queue modülünü kullanacağız. Queue modülü, oldukça
gelişmiş imkanlar sunar. List ile yapılan uygulamaya göre bazı avantajlarını şu şekilde sıralayabiliriz:
• Kuyruk boyutu belirtebiliriz. Mesela 10 kontenjanlı bir kuyruk için; maxsize = 10 yazabiliriz.
• list ile yapılan örneğe göre hata ihtimalini biraz daha azaltır. Çünkü list uygulamalarında
aradaki bir elemanı (mesela 3. eleman için kuyruk[3] gibi) çağırabiliyoruz. Oysa bu
uygulamada aradaki elemanlara erişilememektedir.
• Çok görevli (multitasking) çalışma için uygundur. Bunu açıklamak için bir örnek düşünelim. Bir
fabrikada yürüyen bant sistemi olsun. Bu bant üzerine işlenecek ham maddeleri koyan makineler
var. Bandın diğer ucunda da bu ham maddeleri işleyecek olan farklı makineler var. Bant
üzerinde 10 elemanlık yer var. Bildiğimiz gibi bu bir kuyruk yapısı. Bant dolu ise, eleman
yerleştirici makineler beklemelidir. Diğer taraftan; bantta ürün yoksa, bu sefer de banttan ürün
alıp işleyen makineler beklemelidir. Bu tarz sistemlere çok üreticili – çok tüketicili (multi-
producer, multi-consumer) sistemler denmektedir. Kuyruğa eleman ekleyenler üretici, kuyruktan
eleman çekenler ise tüketicidir. Aynı anda çok sayıda üretici ve tüketici aynı kuyrukta bu şekilde
çalışabilmektedir. Günümüzde bir çok sistemde bu yapı kullanılmaktadır. Mesela e-devlet
sistemi üzerinde bir iş yapmak istediğimizde “İsteğiniz alınmıştır, bilgiler hazırlanmaktadır.
Yoğunluğa göre bekleme süresi değişebilir” tarzında bir mesaj gösterilerek bu istek işlem
kuyruğuna alınmaktadır.
• Yukarıda da değinildiği gibi, performans açısından list kullanımına göre daha avantajlıdır.
Aşağıda queue modülü kullanılan basit bir kuyruk örneği verilmiştir. Bu modülde kuyruğa eklemek için
put, kuyruktan veri çekmek için ise get kullanılmaktadır.

Veri Yapıları tüm haftalar(M.Ö) 35/68


from queue import Queue kuyruğun eleman sayısı: 2
k = Queue(maxsize = 2) #k isminde 2 kuyruk dolu mu: True
kontenjanlı kuyruk oluştur
Kuyruktaki elemanlar sırayla
# Kuyruğa eleman ekleme çekiliyor...
k.put('a') a
k.put('b') b
print("kuyruğun eleman sayısı: işlem tamamlandı
",k.qsize())
print("kuyruk dolu mu: ", k.full())

# Kuyruktaki elemanlar çekiliyor


print("\nKuyruktaki elemanlar sırayla
çekiliyor...")
print(k.get())
print(k.get())
print("işlem tamamlandı")
Programda görüldüğü gibi; put ve get metodları kuyruğa eklemek ve çıkarmak için kullanılıyor. size
ve full adında iki metot daha görüyoruz kodlar içinde. Bunlar da kuyruktaki eleman sayısını ve
kuyruğun dolu olup olmadığını veren metotlardır.
Yukarıda avantajları sayarken; çok üreticili – çok tüketicili ifadesi kullanmıştık. Buna da bir örnek
vermek istiyorum. Aşağıdaki kodlarda 2 kontenjanlı bir kuyruk oluşturuluyor. Kuyruğa 2 eleman
ekleniyor. Sonra kuyruktan 3 kere eleman çekilmeye çalışılıyor. İki eleman çekildikten sonra, 3. eleman
çekilmek istendiğinde, program hata vermiyor, beklemeye giriyor. Kuyruğa veri girişi yapılmasını
bekliyor.
kuyruğun eleman sayısı: 2
kuyruk dolu mu: True
from queue import Queue
k = Queue(maxsize = 2) # k isminde 2 Kuyruktaki elemanlar sırayla
kontenjanlı kuyruk oluştur çekiliyor...
a
# Kuyruğa eleman ekleme b
k.put('a')
k.put('b')
print("k'nın eleman sayısı: ",k.qsize())

# Kuyruktaki elemanlar çekiliyor


print("\nKuyruktaki elemanlar sırayla
çekiliyor...")
print(k.get()) # ilk eleman geldi
print(k.get()) # ikinci eleman geldi
print(k.get()) # ** üçüncü eleman yok ! **
print("işlem tamamlandı")
Programın çıktısına dikkat edersek; son satırdaki print işletilemiyor. Çünkü bir önceki satırda,
kuyruktan eleman beklemeye devam ediyor.
Yığın işlemlerini uygulamak için de aynı modülü kullanabiliyoruz. Tek değişiklik; Queue yerine
LifoQueue sözcüğü kullanılması şeklinde oluyor. Aşağıda basit bir yığın uygulaması örneği
verilmiştir:

Veri Yapıları tüm haftalar(M.Ö) 36/68


from queue import LifoQueue b
k = LifoQueue(maxsize = 2) # 2 kontenjanlı a
yığın işlem tamamlandı

# Yığına eleman ekleme


k.put('a')
k.put('b')

# Yığındaki elemanlar çekiliyor


print(k.get())
print(k.get())
print("işlem tamamlandı")
Bir LifoQueue nesnesi oluşturarak yığın uygulaması yaptık. İlk girdiğimiz veriyi son sırada elde ettik.
Özetlersek; kuyruk ve yığın uygulamaları için farklı imkanlarımız olduğunu görmüş olduk. Basit
uygulamalarda list kullanabiliriz ama profesyonel uygulamalarda Queue modülü kullanmak daha
uygun olabilir.

18. Bağlı Listeler


Bağlı liste, her elemanın bir değer ve bir de referans içerdiği veri yapısıdır. Bağlı listeler en basit ve en
çok kullanılan veri yapılarındandır. Bağlı listelerin avantajı, hafızayı dinamik olarak kullanmasıdır.
Hafızada eleman sayısı kadar yer ayrılmasına gerek yoktur. Çünkü her bir eleman listedeki sonraki
elemanın bellek adresini göstermektedir. Bu şekilde bellekte farklı yerlerde bulunan veriler birbirine
kolayca bağlanabilmektedir. Bağlı listelerin dezavantajı ise sıralı erişim yapmasıdır. Büyük verilerle
çalışırken bağlı listeye alternatif daha hızlı yöntemler kullanmak gerekebilir.
Yandaki görselde 4 düğümlü (elemanlı) bir bağlı liste gösterilmiştir. Görseldeki her bitişik ikili kutu bir
düğümü temsil eder. Bitişik kutuların sol tarafında düğümün
değeri vardır. Sağdaki kutuda ise bu düğümün referans ettiği
düğümün bellek adresi vardır. Kutuların altında yazan 2003,
20043 3225, 3226 gibi değerler her bir hücrenin bellek
adresidir.
Örneğin; görseldeki ilk düğümde tutulan veri değeri “2”dir.
Bu düğümün referans kısmında “2184” değerini görüyoruz.
Bu bir sonraki düğümün bellek adresidir. 2184 adresindeki
düğümün değeri “4”tür. Bu düğümün referans ettiği düğüm ise
3225 adresindedir. Bu şekilde tüm liste elemanları gezilebilir.
Listenin son düğümünün referans ettiği bir bellek adresi bulunmamaktadır (null ~ boş küme).
Bağlı listeye yeni veri ekleneceğinde, bir düğüm oluşturulur ve listeye bağlanır. Eleman silineceğinde
ise düğümlerden birisi silinir, kopan bağ tekrar sağlanır.
Kuyruk ve yığın veri yapılarını list türü ile gerçekleştirmek mümkün olsa da, bağlı liste yapısını
gerçekleştirmek için list çok uygun değildir. Bağlı liste için list kullanılırsa, araya eleman
eklenmesi/çıkartılması gerektiğinde, o elemandan sonra gelen tüm elemanların birer kademe ileri/geri
kaydırılması gereklidir. Bağlı liste yapısı, listedeki sıralamayı bir bağ ile göstererek bu gerekliliği
ortadan kaldırmıştır.
Bağlı listeler; düğümlerin birbirine bağlanma şekline göre farklı şekillerde olabilmektedir. En yaygın
bağlı liste türleri şu şekildedir:
• tek bağlı doğrusal listeler
• tek bağlı dairesel listeler
• çift bağlı doğrusal listeler
• çift bağlı dairesel listeler

Veri Yapıları tüm haftalar(M.Ö) 37/68


Yukarıda sıralanmış olan bağlı liste yöntemlerinin detayları aşağıda başlıklar halinde verilmiştir:

18.1. Tek bağlı doğrusal listeler


Her bir düğümde iki veri olur. İlk veri düğümün değeridir, ikinci veri ise sonraki düğümü referans eder.
Aşağıdaki şekilde temsili gösterimi verilmiştir:
Sonraki
Düğüm
düğüm
Sonraki değeri
Düğüm referansı
düğüm
değeri Sonraki
referansı Düğüm
düğüm
değeri
referansı
Sonraki
Düğüm
Sonraki düğüm
Düğüm değeri
düğüm referansı
değeri
referansı

Yukarıdaki şekilde, 5 tane düğüm vardır. Görüldüğü gibi, her bir düğüm bir başka düğümü
göstermektedir. Son düğüm, başka hiç bir düğümü göstermez; benzer şekilde ilk düğümü gösteren bir
düğüm de bulunmamaktadır. Düğümlerin bellekte nerelerde bulunduğu hiç önemli değildir, çünkü her
düğümde bir sonrakinin adresi bulunmaktadır.
Bu şekilde tek bağlı doğrusal bir liste yapabilmek için, nesne tabanlı programlama dillerinde nesneler
kullanılmaktadır. C gibi dillerde ise “nesne” yoktur. Bu işi yapmak için, işaretçi denilen özel bir
referans değişkeni kullanılmaktadır.
Aşağıda bağlı liste düğümleri için oluşturacağımız sınıf-nesne yapısının temsili gösterimi verilmiştir:
Düğüm sınıfı

değer sonraki

Düğüm1 Düğüm2 Düğüm3

değer sonraki değer sonraki değer sonraki

Önce bir sınıf oluşturacağız. Bu sınıfta iki tane veri olacak. Bunların isimleri değer ve sonraki
şeklinde olacak. Bu sınıftan türeteceğimiz tüm nesnelerde de aynı özellikler olacak. Aşağıdaki
uygulamayı inceleyelim:
class DüğümSınıf: # Sınıf tanımı
değer = 0
sonraki = None

düğüm1 = DüğümSınıf() # Bir nesne oluştur


düğüm2 = DüğümSınıf() # Bir nesne daha oluştur

düğüm1.değer=11 # İlk düğümnün değeri


düğüm2.değer=54 # İkinci düğümün değeri

# İlk düğümdeki referans değişkeni (sonraki) ikinci düğümü göstersin


düğüm1.sonraki = düğüm2
Yukarıdaki uygulamada, önce DüğümSınıf isminde sınıf oluşturuluyor. Sonra bu sınıftan düğüm1 ve
düğüm2 adında iki nesne türetiliyor. Yeni nesnelerimizin düğüm1.değer ve düğüm1.sonraki
şeklinde iki tane özelliği olmuş oluyor. düğüm1.değer=11 şeklinde ilk düğümün değerini belirliyoruz.

Veri Yapıları tüm haftalar(M.Ö) 38/68


düğüm1.sonraki=düğüm2 diyerek; ilk düğümün referans gösterdiği düğümü de düğüm2 olarak
belirtiyoruz. Şimdi de programın çıktısına bakalım:

Yukarıdaki görüntüde; program çalıştırıldıktan sonra, konsol ekranında (sağ taraf) bir kaç komut daha
girilmiştir. düğüm1 yazıp enter’a basıldığında, bunun DüğümSınıf sınıfı türünde olduğunu belirtmiş.
düğüm1.değer yazıldığında, değeri 11 olarak doğru bir şekilde vermiş. düğüm1.sonraki
yazıldığında, sonraki düğümün (düğüm2) de aynı sınıfta ama farklı bir adresteki nesne olduğunu
görebiliyoruz. Son olarak; düğüm1.sonraki.değer şeklinde yazıldığında da düğüm2’nin değerini
görebiliyoruz.

Not: Tek bağlı listelerde önceki düğüme erişebilmek için; listenin başından başlayıp ilgili düğüme kadar
tüm elemanları teker teker geçmek gerekecektir.

Veri Yapıları tüm haftalar(M.Ö) 39/68

You might also like