Kuyruk (Queue)
Veri Yapıları
Kuyruk Yapısı
Kuyruk Yapısı da yığın kadar basit bir veri yapısıdır.
Yığından farkı, kuyruğa ilk giren elemanların ilk önce işlenmesidir.
Yani hizmet istekleri listenin sonuna eklenir. Bu
nedenle, First In First Out (FIFO) saklama olarak da tanımlanır.
Kuyruk aslında bir bekleme dizisidir.
Belirli bir hizmeti almak isteyenler, geliş sırasına göre bir bekleme listesine yerleşirler.
Listeye ilk eklenen eleman, en önce hizmet alır, son
eklenen ise en son hizmet alır.
Kuyruk Yapısı (devam)
Yapıya iki noktadan erişim söz konusudur.
Yeni gelenlerin eklenecekleri konumu işaretleyen bir son işaretçisi (son).
Hizmet alma sırası geleni belirlemek üzere bir baş (bas) işaretçisi.
bas
A1 A2 A3 A4
son
Kuyruk elemanları sadece baş taraftan çıkarılır, ve yeni gelen elemanlar daima sona eklenir.
Uygulama alanları
Bilgisayar sistemlerinde kuyrukların birçok uygulama alanları vardır:
Tek işlemcili bilgisayarlar bir anda sadece tek kullanıcıya hizmet verebilirler. Kullanıcı istekleri bir kuyruk
yapısında tutulur.
İşletim sistemlerinde birçok zamanlama işlemi kuyruk yapısı kullanılarak yapılır.
Bir yazıcıya yazdırma işlemi yine benzer yolla bir kuyruk yapısı kullanılarak yapılır.
. . .
Kuyruk işlemleri
ekleme: Kuyruğun en sonuna yeni bir eleman ekleme işlemidir. Eklenen eleman kuyrukta en sondaki eleman haline gelir.
ekle(...) – enqueue (...)
çıkarma: Kuyrukta en ön sırada yer alan elemanı,
kuyruktan çıkarma işlemidir. Bu elemanın arkasında bekleyen eleman, kuyrukta en ön sıraya yerleşir.
cikar() - dequeue()
boş olma durumunu kontrol etme: kuyruğun boş olup olmadığını kontrol etme işlemidir.
bosmu() – isempty()
Örnek
ekle(‘A’);
ekle(‘B’);
ekle(‘C’);
çıkar();
ekle(‘D’);
çıkar();
çıkar();
A B C
Bir kuyrukta sırasıyla şu işlemleri yapınız:
D
Dizi Kullanarak
Kuyruk Gerçekleme
1. Yöntem :
Dizi elemanlarının kaydırılması
Kuyruk başı işaretçisi her zaman dizinin ilk elemanı olsun.
Yeni eleman eklemek için son işaretçisi bir arttırılır.
Eleman çıkarılırken ise hep ilk eleman çıkarılır.
Ancak bu pahalı bir çözümdür: her eleman
çıkarıldığında, diğer tüm elemanlar birer
kaydırılmalıdırlar.
Örnek:
a c b
son baş
3 2 1 0
son baş
a c b
3 2 1 0
a çıktı
a c b
son baş
3 2 1 0
b çıktı
Dezavantaj : Her defasında dizinin tüm elemanları bir ileri kaydırılmalıdır.
2. Yöntem :
Çok büyük lineer dizi tanımlanması
Eleman eklemek için son işaretçisini bir arttır ve ekle.
Eleman çıkarmak için baş işaretçisini kullanarak elemanı oku ve işaretçiyi arttır.
Eğer çok büyük boyutlu bir dizimiz olsaydı problem şu şekilde çözülebilirdi:
int bas=0; int son=0;
char kuyruk[∞]; //infinite!
bool bosmu(){
return( bas == son );
}
void ekle( char x){
kuyruk[son++] = x ; }
char cikar(){
return( kuyruk[bas++] );
Mümkün değil!
Örnek:
a c b
son baş
3 2 1 0
c b a
son baş
3 2 1 0
a çıktı
a c b
son baş
3 2 1 0
b çıktı
c b a
son baş
3 2 1 0
c çıktı
a d c b
son baş
3 2 1 0
d eklendi
d c b a
baş son
3 2 1 0
d çıktı
Yeni ekleme yapılamaz!
Dizide yer yok görünüyor.
Dezavantaj: Gereksiz bellek kullanımı.
Kuyruk boş
#ifndef KUYRUK_H
#define KUYRUK_H
#define KUYRUKBOYU 10
typedef int KuyrukVeriTipi;
struct Kuyruk{
KuyrukVeriTipi eleman[KUYRUKBOYU];
int bas;
int son;
void olustur();
bool ekle(KuyrukVeriTipi);
KuyrukVeriTipi cikar();
bool bosmu();
};
void Kuyruk::olustur(){
bas=0;son=0;
}
bool Kuyruk::ekle(KuyrukVeriTipi yeni){
if(son<KUYRUKBOYU){
eleman[son++]=yeni;
return true;
}
return false;
}
KuyrukVeriTipi Kuyruk::cikar(){
return eleman[bas++];
}
bool Kuyruk::bosmu(){
return (bas==son);
}
Dizide yer olduğu halde, kuyruk dolu hatası döndürülebilir.
3. Yöntem : Çevrel Dizi
Diziyi doğrusal değil, çevrel olarak gerçekleriz.
Dizinin son elemanını ilk eleman izler.
Süreklilik sağlanır.
Farklı zamanlarda, dizinin farklı bölümlerinde elemanlar bulunacaktır.
Tam dolu olmadıkça “yer yok” mesajı alınmaz.
15
Çevrel Dizi
60 40 70
0 1 2 3 4 5 N-2 N-1 bas son
0
1 2
3
4 N-1
60 40 70
bas
son N-2 5
Çevrel Dizi olarak Gerçekleme
N elemanlı bir dizi çevrel olarak kullanılacak.
Kuyruk[0 … N-1]
N-1 indisinden 0 indisine geçiş isteniyor.
if (i == N-1)
i = 0;
else
i++;
i = (i+1)%N ;
Sınır Durumlar
son: son elemanı gösterir,
son işaretçisini bir arttır, atama yap.
baş: ilk elemanı gösterir,
okuma yap, baş işaretçisini bir arttır.
Eğer kuyrukta tek eleman var ise; baş ve son işaretçileri bu elemana işaret edeceklerdir.
a c x
son baş
i-1 i i+1
Sınır durum1) "Kuyruk boş"
En son eleman çıkarıldığı zaman, baş işaretçisi bir artıp sonu geçecektir.
a c x
son baş
i-1 i i+1
a c x
son baş
i-1 i i+1
Kuyruk boş koşulu:
baş == son+1
Sınır durum2) "Kuyruk dolu"
Eğer kuyrukta tek boş yer var ise, ve yeni bir eleman eklenirse yine baş işaretçisi sondan bir fazla
olacaktır.
0 1
2
baş (i+1) son
(i-1)
i
Dolu kuyruk koşulu: baş işaretçisi son’dan 1 fazla baş == son+1
Sınır durumlar için çözüm seçenekleri
Durum1 ve Durum2 olduğunda kuyruğun Boşmu yoksa Dolumu olduğunu ayırdetmek gerekir.
1. Dizi kapasitesini bir eksik tut. Bir alanı boş bırak.
2. Kuyruğun dolu olduğunu gösterecek bir lojik değişken kullan.
Veya kuyruktaki eleman sayısını içeren bir değişken kullan.
3. Özel indis değerleri kullanarak gerçekleme: Boş olma durumunu göstermek üzere işaretçilere özel değer ata.
İlk durum koşulları: bas=-1, son=-1
Kuyruk boşalınca ilk durum koşullarına geri dön.
Kuyruk doluysa son+1==bas
Özel İndis Değerleri Kullanılması
Başlangıç durumu: baş=0, son= -1
Kuyruk boş duruma geldiğinde, bu değişkenler başlangıç durumuna getirilir (reset)
Eğer kuyruk dolu ise, baş == son+1 and son != -1
BOŞ
Özel İndis Değerleri Kullanılması
0
1
2
3
5 4
0
1 2
3
5 4
DOLU
30 90
70 80
60 20
bas = 0 bas = 3
23
#define N 10 // Kuyruk boyu
typedef char KuyrukVeriTipi;
struct Kuyruk {
KuyrukVeriTipi dizi[N];
int bas;
int son;
void olustur();
bool ekle(KuyrukVeriTipi);
KuyrukVeriTipi cikar();
bool bosmu();
void kuyruk_yaz();
};
typedef struct KUYRUK Kuyruk;
( kuyruk_CevrelListe.cpp )
void Kuyruk::olustur() { bas=0; son=-1;
}
bool Kuyruk::ekle(KuyrukVeriTipi yeni) { if (son != -1 && (bas == (son + 1 ) % N) return false;
}
else {
son = (son+1)%N;
dizi[son] = yeni; // Kuyruğa ekle return true;
KuyrukVeriTipi Kuyruk::cikar() {
KuyrukVeriTipi eleman = dizi[bas];
if (bas == son)
bas = 0; son = -1;
else
bas = (bas + 1) % N;
return eleman;
}
bool Kuyruk::bosmu() {
return (bas==0 && son==-1);
}
void Kuyruk::kuyruk_yaz() { int i;
if ( bosmu() ) return;
cout << "KUYRUK (bas ile son arasi) :“ ; i = bas;
do {
cout << dizi[i] << " ";
if (i == son) break; // Stop loop i = (i+1) % N;
} while (1); // infinite loop }
int main() { Kuyruk k;
int islem;
char veri;
k.olustur();
secenekler();
cout << “Seciminiz : "; cin >> islem;
while ( islem != 3 ) { switch ( islem ) { case 1:
cout << "Bir harf girin : ";
cin >> veri;
k.ekle(veri);
k.kuyruk_yaz();
break;
case 2:
if (k.bosmu())
cout << "Kuyruk bos \n";
else
cout << “Cikarilan: " << k.cikar() ; k.kuyruk_yaz(); break;
default:
cout << "Gecersiz secim\n";
secenekler(); break;
} // end switch secenekler();
cout << “Seciminiz : ";
cin >> islem;
} // end while return 0;
} // end of main
void secenekler() {
cout << "KUYRUK ISLEMLERI (CEVREL DIZI ILE)\n"
<< "1. Ekle \n"
<< "2. Cikar \n"
<< "3. Son \n”;
}
Kuyruk boş
bas 0
29
0 1 2 3 4
son -1
• baş : dequeue işlemi için kullanılır.
• back: enqueue işlemi için kullanılır.
ekle(70)
0
70
• Özel Durum: Kuyruğa ilk defa eleman eklendi.
0 1 2 3 4
0
ekle(60)
70 60
31
0 1 2 3 4
bas 0
son 1
• son göstergesi 1 artırılır, sonra (60) verisi eklenir.
70 60 90
0 1 2 3 4
0 2
ekle(90)
70 60 90 40
33
0 1 2 3 4
bas 0
son 3
ekle(40)
60 90 40
• (70) verisi sıfırlanır, sonra bas göstergesi 1 artırılır.
0 1 2 3 4
1 3
cikar()
90 40
35
0 1 2 3 4
bas 2
son 3
• (60) verisi sıfırlanır, sonra bas göstergesi 1 artırılır.
cikar()
90 40 80
0 1 2 3 4
2 4
ekle(80)
20 90 40 80
37
• son göstergesi 1 artırılır ve wrap edilir.
0 1 2 3 4
bas 2 son
0
ekle(20)
20 50 90 40 80
0 1 2 3 4
2 1
ekle(50)
20 50 90 40 80
39
0 1 2 3 4
bas 2
• Özel Durum:
son + 1 == bas
• Ekleme yapılamaz!
son 1
ekle(100)
Kuyruk
dolu!
20 50 40 80
0 1 2 3 4
3 1
• bas göstergesi ile gösterilen eleman (90) diziden silinir.
cikar()
20 50 80
41
0 1 2 3 4
bas 4 son
1
• bas göstergesi ile gösterilen eleman (40) diziden silinir.
cikar()
20 50
0 1 2 3 4
0 1
• bas göstergesi ile gösterilen eleman (80) diziden silinir, ve bas wrap edilir.
cikar()
50
43
0 1 2 3 4
bas 1
son 1
• bas göstergesi ile gösterilen eleman (20) diziden silinir.
cikar()
0 1 2 3 4
0
• bas göstergesi ile gösterilen eleman (50) diziden silinir.
• Özel Durum : bas == son koşulu sağlandığı için göstergeleri -1 ve 0 yapılır.
-1
cikar()
Kuyruk
tekrar boş
Kuyrukların dizi üzerinde gerçeklenmesinin dezavantajı
Kuyrukların dizi üzerinde gerçeklenmesi de, yığınların dizi üzerinde gerçeklenmesine benzer problemler
oluşturur:
Kuyruk boyunun sabit olması, kuyruğa beklenenden fazla sayıda giriş yapılması durumunda hata ortaya çıkmasına yol açar.
Bu nedenle, kuyrukların liste üzerinde gerçeklenmesi
daha iyi bir çözümdür.
Bağlantılı Liste Kullanarak
Kuyruk Gerçekleme
Bağlantılı Liste kullanılarak gerçekleme
Dizi kullanarak gerçeklemenin getirdiği kısıtlamalar, bağlantılı listeler kullanılarak ortadan kaldırılabilir.
Kuyruğun başı listenin ilk elemanı, kuyruğun sonu da listenin son elemanı olacak şekilde tasarım yapılır.
Kuyruğa ekleme işlemi – listenin sonuna bir eleman ekleme,
Kuyruktan çıkarma işlemi – listenin başından bir eleman çıkarma şeklinde yorumlanır.
bas işaretçisi kuyruğun ilk düğümüne işaret eder. Çıkarma işlemi daima bas işaretçisi ile gösterilen düğümden yapılır. Bu nedenle kuyruktan çıkarma yapmak için listenin taranması ve silinecek düğümün aranması şeklinde bir durum yoktur.
son işaretçisi kuyruğun son düğümüne işaret eder. Ekleme işlemi daima son işaretçisi ile gösterilen düğümün arkasına yapılır. Bu nedenle kuyruğa ekleme yapmak için listenin taranması şeklinde bir durum yoktur.
Kuyruktaki düğümlerin alfabetik, nümerik vb. artan/azalan sırada olması gibi bir durum yoktur.
Kuyruk boş
bas son
• baş : dequeue işlemi için kullanılır.
• back: enqueue işlemi için kullanılır.
ekle(60)
49
• Listeye ilk defa düğüm eklendiği için bas ve son işaretçileri aynı düğüme işaret ediyor.
60
bas son
ekle(40)
• Yeni veri (40) sona eklenir.
bas son
ekle(50)
51
• Yeni veri (50) sona eklenir.
60 bas
40 50
son
cikar()
• Listenin başındaki düğüm (60) silinir.
bas
60 çıkarıldı son
cikar()
53
• Listenin başındaki düğüm (40) silinir.
50 bas
40 çıkarıldı son
cikar()
• Listenin başındaki düğüm (50) silinir.
• Kuyruk tekrar boş duruma geldi.
bas
50 çıkarıldı son
55
#ifndef KUYRUK_H
#define KUYRUK_H
typedef char KuyrukVeriTipi;
struct Dugum{
KuyrukVeriTipi veri;
Dugum *sonraki;
};
struct Kuyruk{
Dugum * bas;
Dugum * son;
void olustur();
void kapat();
bool ekle(KuyrukVeriTipi);
KuyrukVeriTipi cikar();
bool bosmu();
void kuyruk_yaz();
};
typedef struct KUYRUK Kuyruk;
#endif
( kuyruk_BaglantiliListe.cpp )
void Kuyruk::olustur(){
bas=NULL;son=NULL;
}
void Kuyruk::kapat(){
Dugum *p;
while (bas){
p = bas;
bas = bas->sonraki;
delete p;
} }
void Kuyruk::ekle(KuyrukVeriTipi yeni){
Dugum *yenidugum=new Dugum;
yenidugum->veri=yeni;
yenidugum->sonraki=NULL;
if(bas==NULL){
bas=yenidugum;
son=bas;
}
else{
son->sonraki=yenidugum;
son=yenidugum;
} }
KuyrukVeriTipi Kuyruk::cikar(){
Dugum *ustdugum;
KuyrukVeriTipi gecici;
ustdugum=bas;
bas=bas->sonraki;
gecici=ustdugum->veri;
delete ustdugum;
return gecici;
}
bool Kuyruk::bosmu(){
return bas==NULL;
}
void Kuyruk::kuyruk_yaz() { Dugum * tara = bas;
if (bosmu()){
cout << "Kuyruk bos \n";
return;
}
cout << "KUYRUK : ";
while (tara != NULL ) { cout << tara->veri;
tara = tara->sonraki;
} }
main()
int main(){
Kuyruk k;
k.olustur();
k.ekle('A');
k.ekle('B');
k.ekle('C');
k.kuyruk_yaz();
char c=k.cikar();
k.ekle('D');
k.ekle('E');
k.ekle('F');
c=k.cikar();
if(!k.bosmu()) k.cikar();
k.kuyruk_yaz();
k.kapat();
return 0;
}