Bölüm 8: Yığın ve Kuyruk Uygulamaları
Giriş
Öğrenme Çıktıları
Labirentte yığınla yol arayıp çıkmazdan geri dönmeyi izlemek.
Bir algoritma için en kötü veri grubunu tasarlayıp analiz etmek.
Infix, prefix ve postfix arasında elle ve yığınla çeviri yapmak.
eval yordamını işlenen/işleç yığınıyla adım adım çalıştırmak.
isp/icp tablosuyla infix → postfix çeviren yordamı yazmak.
Parantez denetiminde, geri al/yinele gibi problemlerde ve özyinelemesiz gezinmede yığını kullanmak.
Labirent
Labirent (Maze) Problemi
n×p boyutlu bir matriste 0’lar açık yolu, 1’ler duvarı simgeler. Giriş [1][1] (G), çıkış [6][6] (Ç): girişten çıkışa hangi yoldan gidilebileceği bulunmak istenir.
- Yatay, dikey ve çapraz: 8 yönde hareket edilebilir.
- Her adımda “matris dışına çıkıyor muyum?” denetiminden kurtulmak için matrisin çevresi 1 ile çevrilir.
- Gidilen hücreler
gezildimatrisinde işaretlenir: aynı yere iki kez girilmez. - Çıkmaza girilince son yol ayrımına geri dönülür: son giren ilk çıkar → yığın.
Labirent
Sekiz Yön ve hareket[] Tablosu
| yön | hareket[yön].dikey | hareket[yön].yatay |
|---|---|---|
| 0 | −1 | 0 |
| 1 | −1 | 1 |
| 2 | 0 | 1 |
| 3 | 1 | 1 |
| 4 | 1 | 0 |
| 5 | 1 | −1 |
| 6 | 0 | −1 |
| 7 | −1 | −1 |
Yön 0 kuzey; saat yönünde 1…7. Yeni konum: [i + dikey][j + yatay].
if yazmaktan kurtarır: yön bir döngü değişkeni olur.Labirent
Labirent İçin Tanımlar
#define MAX_STACK_SIZE 100 #define CIKIS_SATIR 6 #define CIKIS_SUTUN 6 typedef struct { short int dikey, yatay; } yonler; yonler hareket[8] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}}; typedef struct { short int satir, sutun, yon; } element; element yigit[MAX_STACK_SIZE]; int top = -1; short int gezildi[8][8]; short int labirent[8][8] = {{1,1,1,1,1,1,1,1}, {1,0,1,0,0,0,1,1}, {1,0,1,0,1,1,1,1}, {1,1,0,1,1,1,1,1}, {1,0,1,1,0,1,1,1}, {1,1,0,0,1,0,1,1}, {1,1,1,1,1,1,0,1}, {1,1,1,1,1,1,1,1}};
element: yığına konan kayıt, hangi hücreden (satir, sutun) ve geri dönülürse hangi yönden devam edileceği (yon).gezildi: ziyaret edilen hücreler (küresel olduğu için başta sıfırdır).labirent: çevresi 1 ile çevrili 8×8 matris; iç kısım 6×6.- Yığının en fazla boyutu: açık hücre sayısı kadar (burada 13).
Labirent
izbul Yordamı
void izbul(void) { short int satir, sutun, y_satir, y_sutun; int i, yon, bulundu = FALSE; element konum; yigit[0].satir = yigit[0].sutun = 1; yigit[0].yon = 1; /* ilk konum yığına */ gezildi[1][1] = 1; top = 0; while (top > -1 && !bulundu) { konum = yigit_al(&top); /* yeni konum */ satir = konum.satir; sutun = konum.sutun; yon = konum.yon; /* hangi yöne */ while (yon < 8 && !bulundu) { y_satir = satir + hareket[yon].dikey; y_sutun = sutun + hareket[yon].yatay;
if (y_satir == CIKIS_SATIR && y_sutun == CIKIS_SUTUN) bulundu = TRUE; /* çıkış mı? */ else if (!labirent[y_satir][y_sutun] && !gezildi[y_satir][y_sutun]) { gezildi[y_satir][y_sutun] = 1; konum.satir = satir; /* nereden */ konum.sutun = sutun; konum.yon = ++yon; /* dönüşte yön */ yigit_ekle(&top, konum); satir = y_satir; sutun = y_sutun; yon = 0; } else ++yon; /* başka yön */ } /* geri adım */ }
Sonunda bulundu ise yığındaki konumlar (0…top), son konum ve çıkış sırayla yazdırılır; değilse “Cikis yolu bulunamadi!”. (Kodu okunur kılmak için satırlar bölündü; mantık aynen korundu.)
Labirent · Kodu adım adım çalıştır
Kod İzleme: Yığınla Geri İzleme
Başlangıç: yığında (1,1) yön 1. yigit: satir,sutun,yon kayıtları (alttan üste).
İlk pop: (1,1), yön 1'den başlanır (yön 0 zaten duvar).
(1,1)'den yön 4 (G) açık → (2,1)'e geç; (1,1, yön 5) yığına itildi.
(2,1)'den yön 3 (GD) açık → (3,2)'e geç; (2,1, yön 4) yığına itildi.
(3,2)'den yön 1 (KD) açık → (2,3)'e geç; (3,2, yön 2) yığına itildi.
(2,3)'den yön 0 (K) açık → (1,3)'e geç; (2,3, yön 1) yığına itildi.
(1,3)'den yön 2 (D) açık → (1,4)'e geç; (1,3, yön 3) yığına itildi.
(1,4)'den yön 2 (D) açık → (1,5)'e geç; (1,4, yön 3) yığına itildi.
Geri adım / pop: (1,4) konumuna dönülür, yön 3'ten (GD) denemeye devam edilir.
Geri adım / pop: (1,3) konumuna dönülür, yön 3'ten (GD) denemeye devam edilir.
Geri adım / pop: (2,3) konumuna dönülür, yön 1'den (KD) denemeye devam edilir.
Geri adım / pop: (3,2) konumuna dönülür, yön 2'den (D) denemeye devam edilir.
(3,2)'den yön 5 (GB) açık → (4,1)'e geç; (3,2, yön 6) yığına itildi.
(4,1)'den yön 3 (GD) açık → (5,2)'e geç; (4,1, yön 4) yığına itildi.
(5,2)'den yön 2 (D) açık → (5,3)'e geç; (5,2, yön 3) yığına itildi.
(5,3)'den yön 1 (KD) açık → (4,4)'e geç; (5,3, yön 2) yığına itildi.
(4,4)'den yön 3 (GD) açık → (5,5)'e geç; (4,4, yön 4) yığına itildi.
Yön 3 (GD) çıkış! Yol: yığın + (5,5) + (6,6).
while (top > -1 && !bulundu) { konum = yigit_al(&top); satir = konum.satir; sutun = konum.sutun; yon = konum.yon; while (yon < 8 && !bulundu) { ys = satir + hareket[yon].dikey; yy = sutun + hareket[yon].yatay; if (ys == CIKIS_SATIR && yy == CIKIS_SUTUN) bulundu = TRUE; else if (!labirent[ys][yy] && !gezildi[ys][yy]) { gezildi[ys][yy] = 1; konum = (element){satir, sutun, ++yon}; yigit_ekle(&top, konum); satir = ys; sutun = yy; yon = 0; } else ++yon; } }
Labirent
Değerlendirme ve En Kötü Durum
- Sekizlik look-up table kodu kısalttı ve işi kolaylaştırdı.
- Program labirentte yığın yapısıyla kaba kuvvet (brute force) araması yapar.
- En kötü durum analizi için en kötü veri grubu bulunmalıdır; yönlerin tarama sırası belirleyicidir.
- Girdi 1: ilk denenen yol uzun ama doğru → yalnız ekleme, silme yok.
- Girdi 2: ilk denenen yol uzun ve yanlış → sonuna kadar gidilir, yığın bir bir boşaltılır; doğru yol dışındaki her yer gezildikten sonra çözüme ulaşılır. En kötü veri grubu budur.
Labirent
Karmaşıklık ve Kuyrukla Karşılaştırma
Her açık hücre en çok bir kez itilir (gezildi), her hücrede en çok 8 yön denenir: m×p labirentte O(m·p).
Yığın en fazla açık hücre sayısı kadar büyür: O(m·p); gezildi da O(m·p).
Bulunan yol en kısa olmak zorunda değil. Bu labirentte (2,3)→(1,3)→(1,4)→(1,5) çıkmazına girip döndü; bulduğu yol 9 hücre.
Hücreleri uzaklık sırasıyla açar: en kısa yolu garanti eder. Burada o da 9 hücrelik aynı yolu bulur; ama genelde DFS'ninki daha uzun olabilir.
İfade Gösterimleri
İfade Değerlendirme (Evaluation of Expressions)
Mantıksal ve aritmetik ifadelerin derleyiciler tarafından değerlendirilmesinde de yığın kullanılır. Derleyici şu ifadelerin üstesinden nasıl geliyor?
x = a / b − c + d * e − a * c
((4 / 2) − 2) + (3 * 3) − (4 * 2) = 1
a / (b − c) + d * (e − a) * c
- Soldan sağa okurken bir işleç hemen uygulanamaz: sağında daha öncelikli bir işleç olabilir.
- Çözüm iki aşamalı: önce ifade önceliğe göre postfix’e çevrilir, sonra postfix tek geçişte hesaplanır. İkisi de yığınla yapılır.
İfade Gösterimleri
C'de İşlem Önceliği (Özet)
| Öncelik | İşleçler | Birleşme |
|---|---|---|
| en yüksek | () [] -> . | soldan sağa |
tekli − ! ++ -- * & | sağdan sola | |
* / % | soldan sağa | |
+ − | soldan sağa | |
< <= > >= | soldan sağa | |
== != | soldan sağa | |
&& | soldan sağa | |
| en düşük | || | soldan sağa |
- Önce öncelik, eşitlikte birleşme yönü karar verir:
a − b − c=(a − b) − c. - Parantez her şeyi geçersiz kılar.
- Örnek:
a / b − c + d * e − a * c=(((a / b) − c) + (d * e)) − (a * c). - Bu bölümün algoritmaları
+ − * / %ve parantezle sınırlıdır (Horowitz–Sahni, Şekil 3.12).
İfade Gösterimleri
Infix, Prefix, Postfix
- Infix (ara-işleç): işleç işlenenlerinin arasında,
a + b. Hem parantezleri hem öncelikleri dikkate almak gerekir. - Prefix (ön-işleç, Polonya gösterimi): işleç önde,
+ a b. - Postfix (son-işleç, ters Polonya): işleç sonda,
a b +. Prefix ve postfix’te parantez gerekmez, öncelik düşünmeden işlem yapılır.
| No | Prefix | Infix | Postfix |
|---|---|---|---|
| 1 | *34 | 3*4 | 34* |
| 2 | +2*34 | 2+3*4 | 234*+ |
| 3 | -*+abc/da | (a+b)*c-d/a | ab+c*da/- |
| 4 | **/a+-bcd-eac | (a/(b-c+d))*(e-a)*c | abc-d+/ea-*c* |
İfade Gösterimleri
Elle Çevirme: Parantezleme, Taşıma, Silme
a / b − c + d * e − a * cBaşlangıç ifadesi.
((((a / b) − c) + (d * e)) − (a * c))1. Öncelik ve birleşmeye göre tam parantezlenir.
((((a b /) c −) (d e *) +) (a c *) −)2. Her işleç kendi parantezinin sağ kapanışına taşınır.
a b / c − d e * + a c * −3. Parantezler silinir: postfix. (Prefix için işleç sol açılışa taşınır: − + − / a b c * d e * a c.)
İfade Gösterimleri
Postfix → Infix: 42/2−33*+42*−
4 işlenen → yığına.
2 işlenen → yığına.
/ işleç → iki öğe alınır (4, 2), (4/2) olarak geri konur.
2 işlenen → yığına.
- işleç → iki öğe alınır ((4/2), 2), ((4/2)-2) olarak geri konur.
3 işlenen → yığına.
3 işlenen → yığına.
* işleç → iki öğe alınır (3, 3), (3*3) olarak geri konur.
+ işleç → iki öğe alınır (((4/2)-2), (3*3)), (((4/2)-2)+(3*3)) olarak geri konur.
4 işlenen → yığına.
2 işlenen → yığına.
* işleç → iki öğe alınır (4, 2), (4*2) olarak geri konur.
- işleç → iki öğe alınır ((((4/2)-2)+(3*3)), (4*2)), ((((4/2)-2)+(3*3))-(4*2)) olarak geri konur. Sonuç: ((((4/2)−2)+(3*3))−(4*2)).
* işlendikten sonra [1] hücresi boş kalmaz; orada (3*3) ve (4*2) durur (top = 1).
Postfix Hesaplama
Söz Dizim Birimi: token_al
typedef enum { sol_paran, sag_paran, arti, eksi, carpma, bolme, mod, eos, operand } oncelik; oncelik token_al(const char *expr, char *sembol, int *n) { *sembol = expr[(*n)++]; switch (*sembol) { case '(': return sol_paran; case ')': return sag_paran; case '+': return arti; case '-': return eksi; case '/': return bolme; case '*': return carpma; case '%': return mod; case '\0': return eos; default: return operand; } }
- İfade karakter karakter okunur; her karakter bir token (damga, söz dizim birimi).
- İşlenenler tek basamaklı varsayılır. Rakamı sayıya çevirmek:
sembol − '0'. '2'nin ASCII kodu 50, '0'ınki 48: 50 − 48 = 2. - İfade
'\0'ile biter →eos. exprküresel bir dizi olarak da tutulabilirdi; burada parametre olarak geçirilir.
Postfix Hesaplama
Postfix Hesaplama: eval (Küresel Sürüm)
float eval(void) { char sembol; oncelik token; float op1, op2; int n = 0, top = -1; token = token_al(&sembol, &n); while (token != eos) { if (token == operand) yigit_ekle(&top, sembol - '0'); else { op2 = yigit_al(&top); /* önce sağ işlenen */ op1 = yigit_al(&top); switch (token) { /* +, -, *, /, % */ } } token = token_al(&sembol, &n); } return yigit_al(&top); /* kalan tek öğe sonuç */ }
- İşlenen gelirse yığına konur; işleç gelirse iki işlenen alınır, işlem yapılır, sonuç yığına konur.
- İlk alınan op2 (sağ işlenen):
6 2 −için 6 − 2, 2 − 6 değil. - Sonda yığında tek öğe kalmalı; kalmıyorsa ifade hatalıdır.
case mod: (int)op1 % (int)op2:%float ile kullanılamaz.
Postfix Hesaplama
Küresel Değişkenden Parametreye
/* kuresel surum: disa bagimli */ char expr[] = "42/2-33*+42*-"; float yigit[MAX_SIZE]; float eval(void);
İfade ve yığın küresel: aynı anda iki ifade hesaplanamaz, yordam başka bir programa taşınamaz.
/* bileşen: her şey parametre/yerel */ float eval(const char *expr); /* yığın eval'in içinde yerel dizi */ /* main içinde: */ float a = eval("23*54*+9-"); float b = eval("42/2-33*+42*-");
Aynı yordam her yerde, her ifadeyle, yan etkisiz çalışır. Kod izlemelerinde bu sürüm kullanılır.
Postfix Hesaplama · Kodu adım adım çalıştır
Kod İzleme: eval("23*54*+9-")
İfade 23*54*+9-; yığın boş.
'2' işlenen: '2' − '0' = 2 yığına.
'3' işlenen: '3' − '0' = 3 yığına.
'*' işleç: op2 = 3, op1 = 2 → 2 * 3 = 6 yığına.
'5' işlenen: '5' − '0' = 5 yığına.
'4' işlenen: '4' − '0' = 4 yığına.
'*' işleç: op2 = 4, op1 = 5 → 5 * 4 = 20 yığına.
'+' işleç: op2 = 20, op1 = 6 → 6 + 20 = 26 yığına.
'9' işlenen: '9' − '0' = 9 yığına.
'-' işleç: op2 = 9, op1 = 26 → 26 - 9 = 17 yığına.
'\0': döngü biter; yığındaki tek öğe 17 döner. Her token bir kez işlendi → O(n).
float eval(const char *expr) { float y[MAX_SIZE], op1, op2; int top = -1; for (int n = 0; expr[n] != '\0'; n++) { char c = expr[n]; if (isdigit(c)) y[++top] = c - '0'; else { op2 = y[top--]; op1 = y[top--]; switch (c) { case '+': y[++top] = op1 + op2; break; case '-': y[++top] = op1 - op2; break; case '*': y[++top] = op1 * op2; break; case '/': y[++top] = op1 / op2; break; } } } return y[top]; }
Postfix Hesaplama · Kodu adım adım çalıştır
Kod İzleme: Örnek İfade
İfade 42/2-33*+42*-; yığın boş.
'4' işlenen: '4' − '0' = 4 yığına.
'2' işlenen: '2' − '0' = 2 yığına.
'/' işleç: op2 = 2, op1 = 4 → 4 / 2 = 2 yığına.
'2' işlenen: '2' − '0' = 2 yığına.
'-' işleç: op2 = 2, op1 = 2 → 2 - 2 = 0 yığına.
'3' işlenen: '3' − '0' = 3 yığına.
'3' işlenen: '3' − '0' = 3 yığına.
'*' işleç: op2 = 3, op1 = 3 → 3 * 3 = 9 yığına.
'+' işleç: op2 = 9, op1 = 0 → 0 + 9 = 9 yığına.
'4' işlenen: '4' − '0' = 4 yığına.
'2' işlenen: '2' − '0' = 2 yığına.
'*' işleç: op2 = 2, op1 = 4 → 4 * 2 = 8 yığına.
'-' işleç: op2 = 8, op1 = 9 → 9 - 8 = 1 yığına.
'\0': döngü biter; yığındaki tek öğe 1 döner. Her token bir kez işlendi → O(n).
float eval(const char *expr) { float y[MAX_SIZE], op1, op2; int top = -1; for (int n = 0; expr[n] != '\0'; n++) { char c = expr[n]; if (isdigit(c)) y[++top] = c - '0'; else { op2 = y[top--]; op1 = y[top--]; switch (c) { case '+': y[++top] = op1 + op2; break; case '-': y[++top] = op1 - op2; break; case '*': y[++top] = op1 * op2; break; case '/': y[++top] = op1 / op2; break; } } } return y[top]; }
Infix → Postfix
Infix → Postfix: Algoritma
- İşlenen: doğrudan çıktıya yazılır.
- İşleç: yığındaki işlecin önceliği gelenin önceliğinden büyük ya da eşit oldukça o işleç çıktıya alınır; sonra gelen yığına konur (eşitlikte almak = soldan birleşme).
- ( : her zaman yığına girer; içeride en düşük öncelikli davranır.
- ): ( görülene kadar yığından çıktıya alınır; ( atılır.
- Sonda: yığında kalanların hepsi çıktıya alınır.
| Token | isp (yığında) | icp (gelen) |
|---|---|---|
( | 0 | 20 |
* / % | 13 | 13 |
+ − | 12 | 12 |
# (dip işareti) | −1 | — |
( iki farklı öncelik taşır: gelirken en yüksek (hiçbir şeyi yığından çıkarmadan girer), yığındayken en düşük (üstüne her işleç binebilir). Yığının dibindeki # döngüyü durdurur.Infix → Postfix · Kodu adım adım çalıştır
Kod İzleme: a*(b+c)-d → Postfix
İfade a*(b+c)-d. Yığının dibinde # (isp = −1) var; şekilde gösterilmiyor.
a işlenen → çıktıya.
*: isp(#) = -1 < icp(*) = 13 → * yığına.
(: isp(*) = 13 < icp(() = 20 → ( yığına.
b işlenen → çıktıya.
+: isp(() = 0 < icp(+) = 12 → + yığına.
c işlenen → çıktıya.
): ( görülene kadar al → + çıktıya; ( atıldı.
-: isp(*) = 13 ≥ icp(-) = 12 → * çıktıya; - yığına.
d işlenen → çıktıya.
Girdi bitti: kalan - çıktıya. Sonuç abc+*d-. Her token bir kez yığına girip bir kez çıkar → O(n).
void postfix(const char *e, char *cikti) { char y[MAX] = {'#'}; int top = 0, k = 0; for (; *e; e++) { if (isalnum(*e)) cikti[k++] = *e; else if (*e == ')') { while (y[top] != '(') cikti[k++] = y[top--]; top--; /* '(' at */ } else { while (isp(y[top]) >= icp(*e)) cikti[k++] = y[top--]; y[++top] = *e; } } while (top > 0) cikti[k++] = y[top--]; cikti[k] = '\0'; }
Infix → Postfix · Kodu adım adım çalıştır
Kod İzleme: a*(b+c)*d Örneği
İfade a*(b+c)*d. Yığının dibinde # (isp = −1) var; şekilde gösterilmiyor.
a işlenen → çıktıya.
*: isp(#) = -1 < icp(*) = 13 → * yığına.
(: isp(*) = 13 < icp(() = 20 → ( yığına.
b işlenen → çıktıya.
+: isp(() = 0 < icp(+) = 12 → + yığına.
c işlenen → çıktıya.
): ( görülene kadar al → + çıktıya; ( atıldı.
*: isp(*) = 13 ≥ icp(*) = 13 → * çıktıya; * yığına.
d işlenen → çıktıya.
Girdi bitti: kalan * çıktıya. Sonuç abc+*d*. Her token bir kez yığına girip bir kez çıkar → O(n).
void postfix(const char *e, char *cikti) { char y[MAX] = {'#'}; int top = 0, k = 0; for (; *e; e++) { if (isalnum(*e)) cikti[k++] = *e; else if (*e == ')') { while (y[top] != '(') cikti[k++] = y[top--]; top--; /* '(' at */ } else { while (isp(y[top]) >= icp(*e)) cikti[k++] = y[top--]; y[++top] = *e; } } while (top > 0) cikti[k++] = y[top--]; cikti[k] = '\0'; }
Infix → Postfix
İzleme Tablosu: a+b*c → abc*+
| token | [0] | [1] | top | Çıktı |
|---|---|---|---|---|
| a | −1 | a | ||
| + | + | 0 | a | |
| b | + | 0 | ab | |
| * | + | * | 1 | ab |
| c | + | * | 1 | abc |
| eos | −1 | abc*+ |
*gelince üstte+var: isp(+) = 12 < icp(*) = 13 →*üstüne biner.- Sonda önce
*, sonra+çıkar: daha öncelikli olan önce uygulanır.
postfix yordamı işlenenleri anında çıktıya yazdığı ve sonda yığını boşalttığı için son işlenen de basılır.Infix → Postfix
Infix → Prefix: Ters Çevirme, Postfix, Ters Çevirme
(a+b)*c-d/aInfix → prefix için bir yol: ifade ters çevrilir.
a/d-c*)b+a(Ters çevrildi. Şimdi ( ile ) yer değiştirir.
a/d-c*(b+a)Parantezler düzeltildi. Bu ifade postfix’e çevrilir; ancak eşit öncelikte yığından alınmaz (> kullanılır), çünkü ters çevrilmiş ifadede birleşme yönü de ters döner.
ad/cba+*-Postfix sonucu.
-*+abc/daTekrar ters çevrilir: prefix. Tablodaki 3. satırla aynı.
Doğrudan yol: ifade sağdan sola taranır; işlenenler çıktının başına eklenir, işleçler aynı yığın kuralıyla yönetilir. İki yol da O(n).
Infix → Postfix · Kodu adım adım çalıştır
Kod İzleme: Prefix Hesaplama (-+*23*549)
Prefix -+*23*549 (infix: 2*3+5*4−9). Sağdan sola taranır.
'9' işlenen → yığına.
'4' işlenen → yığına.
'5' işlenen → yığına.
'*': ilk alınan op1 = 5 (sol işlenen!), op2 = 4 → 5 * 4 = 20.
'3' işlenen → yığına.
'2' işlenen → yığına.
'*': ilk alınan op1 = 2 (sol işlenen!), op2 = 3 → 2 * 3 = 6.
'+': ilk alınan op1 = 6 (sol işlenen!), op2 = 20 → 6 + 20 = 26.
'-': ilk alınan op1 = 26 (sol işlenen!), op2 = 9 → 26 - 9 = 17.
Sonuç 17: postfix örneğiyle (23*54*+9−) aynı ifade. Postfix’te ilk alınan sağ işlenendi; prefix’te sol işlenen.
float prefix_eval(const char *e) { float y[MAX], op1, op2; int top = -1; for (int i = strlen(e) - 1; i >= 0; i--) { if (isdigit(e[i])) y[++top] = e[i] - '0'; else { op1 = y[top--]; op2 = y[top--]; y[++top] = uygula(e[i], op1, op2); } } return y[top]; }
Infix → Postfix
Çalışma: Diğer Çeviriler
✓: bu bölümde yapıldı · ?: tasarlanacak çeviriler.
ÇALIŞMA: İfade çözümleme ile ilgili diğer çevirme ve hesaplama algoritmalarını tasarlayınız.
- postfix → infix (bu bölümde elle yapıldı; C ile yazın)
- postfix → prefix ve prefix → postfix
- infix → prefix (doğrudan, sağdan sola tarayarak)
- prefix → infix
- infix'i doğrudan hesaplama (iki yığın: işlenen ve işleç)
Diğer Uygulamalar · Kodu adım adım çalıştır
Kod İzleme: Dengeli Parantez {a*(b+c)-[d]}
Girdi {a*(b+c)-[d]}.
{ açan → yığına.
'a' parantez değil, atlanır.
'*' parantez değil, atlanır.
( açan → yığına.
'b' parantez değil, atlanır.
'+' parantez değil, atlanır.
'c' parantez değil, atlanır.
) kapanan; üstteki ( ile eşleşti → pop.
'-' parantez değil, atlanır.
[ açan → yığına.
'd' parantez değil, atlanır.
] kapanan; üstteki [ ile eşleşti → pop.
} kapanan; üstteki { ile eşleşti → pop.
Girdi bitti ve yığın boş → true: dengeli.
bool dengeli(const char *e) { char y[MAX]; int top = -1; for (; *e; e++) { if (*e=='(' || *e=='[' || *e=='{') y[++top] = *e; else if (*e==')' || *e==']' || *e=='}') { if (top < 0 || !eslesir(y[top], *e)) return false; /* fazla/yanlış */ top--; } } return top == -1; /* açık kalmadı mı */ }
Diğer Uygulamalar
Parantez Denetimi: Üç Hata Türü
| Girdi | Ne olur? | Hata |
|---|---|---|
[(a+b]*c) | ] gelince üstte ( var | Yanlış eşleşme |
(a+b)) | İkinci ) geldiğinde yığın boş | Fazla kapanan |
((a+b) | Girdi bitince yığında ( kaldı | Kapanmamış açan |
- Yalnız sayaç tutmak tek tür parantez için yeterlidir; birden çok türde “en son açılan” bilgisi gerekir → yığın.
- Derleyiciler, HTML/XML denetleyicileri ve metin düzenleyicilerin eşleşen parantez vurgusu bu algoritmayı kullanır.
- Zaman O(n), bellek en kötü O(n) (hepsi açan).
Diğer Uygulamalar
Yığın ve Kuyruğun Başka Uygulamaları
İki yığın: yapılan işlem “geri al” yığınına, geri alınan “yinele” yığınına gider; tarayıcının geri/ileri düğmeleri de böyle çalışır.
Kök ortada gezinme, düğüm adreslerini tutan açık bir yığınla döngüye çevrilir: O(n) zaman, O(h) yığın.
Tek geçişte her öğenin sağındaki ilk büyük öğe: azalan yığın, O(n).
Deque ile her pencerenin en büyüğü O(n)'de bulunur.
Labirentte kuyruk kullanılırsa ilk bulunan yol en kısa yoldur.
Özyinelemeli geri izleme; açık yığınla da yazılabilir.
Özet
Karmaşıklık Özeti
| Algoritma | Yapı | Zaman | Ek bellek |
|---|---|---|---|
| Labirent (izbul) | yığın | O(m·p) | O(m·p) |
| Labirent en kısa yol | kuyruk | O(m·p) | O(m·p) |
| Postfix / prefix hesaplama | yığın | O(n) | O(n) |
| Infix → postfix / prefix | yığın | O(n) | O(n) |
| Parantez denetimi | yığın | O(n) | O(n) |
| Özyinelemesiz kök ortada | yığın | O(n) | O(h) |
| Seviye sırası gezinme | kuyruk | O(n) | O(n) |
n: ifade uzunluğu ya da düğüm sayısı · m×p: labirent boyutu · h: ağaç yüksekliği.
Özet
Sık Yapılan Hatalar
op1 ile op2'yi ters almak: 6 2 − −4 olur.a−b−c yanlış çevrilir.) gelince ('ı yığında unutmak ya da çıktıya yazmak.gezildi işaretini unutup sonsuz döngüye girmek.Özet
Alıştırmalar
a/(b-c)+d*(e-a)*cifadesini postfix ve prefix'e çevir.6 2 3 + − 3 8 2 / + *postfix ifadesini hesapla.- Postfix → infix çeviren C yordamını yaz.
izbulbulunan yolu labirentte işaretlesin.- Labirentte en kısa yolu kuyrukla bul.
- Özyinelemesiz kök başta ve kök sonda gezinmeyi yaz.
İpuçları / cevaplar
- Postfix:
abc-/dea-*c*+; prefix:+/a-bc**d-eac. - 2+3=5, 6−5=1, 8/2=4, 3+4=7, 1*7 = 7.
- İşlenen: tek karakterli dizge it; işleç: b = pop, a = pop, "(" a op b ")" it. malloc/free'ye dikkat.
- Bulunca yığındaki (satir, sutun) çiftlerini labirent[s][t] = 2 yap.
- Kuyruğa (satir, sutun) koy; her hücre için geldiği hücreyi sakla; çıkışa ulaşınca geriye izle.
- Kök başta: kökü it; döngü: al, bas, önce sağı sonra solu it. Kök sonda: iki yığın ya da ziyaret bayrağı.
Özet
Bölüm Özeti
Labirentte her yol ayrımı yığına; çıkmazda pop ile son ayrıma dönülür.
Uzun ve yanlış ilk yol: tüm yanlış bölge gezilir.
Infix öncelik ister; postfix/prefix parantezsiz ve önceliksiz hesaplanır.
İşlenen itilir; işleçte op2, op1 alınır; sonuç itilir. O(n).
isp ≥ icp oldukça alınır; ( gelirken yüksek, yığında düşük öncelik.
“En son açılan ilk kapanır” görülen her yerde yığın; “sırayla” görülen yerde kuyruk.
Özet
Kaynaklar ve Devam
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. bs., Bölüm 3.5 (labirent), 3.6 (ifade değerlendirme).
- R. Sedgewick, Algorithms in C, Kısım 1–4, Bölüm 4.3 (postfix), 5 (özyineleme ve yığın).