BIL222 Veri Yapıları ve Algoritmalar 1. ĠKĠLĠ AĞAÇLAR (BIARY TREES) Bütün düğümlerinin derecesi en fazla iki olan ağaca ikili ağaç denir. Yani bir düğüme en fazla iki tane düğüm bağlanabilir ( çocuk sayısı veya alt ağaç sayısı en fazla iki olur). V V L sol ağaç V R sağ ağaç Şekil 1 Basit bir ikili ağaç İkili ağacın önemli özelliklerinden birisi de ortalama bir ikili ağacın derinliğinin den küçük olmasıdır. Bazen derinlik -1 gibi çok büyük bir değer de olabilir. A B C E Şekil 2 En kötü durumlu ikili ağaç Şekil 3 te, 3 tane ikili ağacın olası durumu verilmiştir. a da ağacın oluşması istenen ideal durumdur denilebilir. Ancak b ve c deki ikili ağaçlar oluşması istenmeyen durumlardır. Çünkü bir ikili ağacın b ve c deki gibi serpilmesi bağlantılı liste anlamına gelir ve ağaç modelinden uzaklaşılmış olur. 1
9 9 9 5 14 14 5 2 7 13 38 38 7 25 39 39 6 a) engeli b) engesiz c) engesiz Şekil 3 İkili Ağaçların Olası urumları Şekil 3.a daki gibi dengeli bir ağacın oluşması için sayılar olası olarak 9,5,14,2,7,13,38,25 ve 39 sırasıyla gelmiş olabilir. b deki gibi oluşması için 9,14,38,39, c deki gibi oluşması için 9,5,7,6 gibi gelmesi gerekir. b ve c deki ağaç şekli istenmeyen durumlardır. c deki durum yaprak düğümün bir sağına bir soluna eklenecek biçimde gelirse oluşur. a da gösterilen ağaç şekli ideal duruma yakın olup dengeli ağaç olarak adlandırılır. engeli ağaçlarda yaprakların düzeyleri arasında en kötü durumda 1 fark olur. engeli ikili ağaç üzerine arama yapma zamanı O( log 2 ) iken, b ve c deki gibi bir ağacın üzerinde ise O( ) e doğru kayar. İkili ağaçlar, daha çok ikili arama ağacı, bağıntı ağacı, kümeleme ağacı gibi uygulamalarda karşımıza çıkmaktadır. Elemanları rasgele yerleştirilmiş, yani sıralı olmayan ikili ağaçlar pek uygulanmamaktadır. 2
1.1 Tanımlamalar İkili ağaçtaki bir düğüme ait veri yapısında bilgiye ek olarak iki tane işaretçi tutulur. Biri sol diğeri sağ olarak adlandırılan bu işaretçiler düğümlerin çocuklarını/altağaçlarını bağlamak içindir. Böyle bir veri yapısının olası tanımı aşağıdaki gibi yapılır. struct Treeode { int data ; struct Treeode * lchild ; struct Treeode * rchild ; } ; Program 1 İkili ağaç için düğüm tanımlamaları Treeode adında bir yapı tanımlanır. Bu yapının data, birer işaretçi olan lchild ve rchild olmak üzere üç tane üyesi vardır. Eğer bir düğümün sol tarafına bir başka düğüm veya bir altağaç bağlıysa lchild adlı işaretçide bağlı olan düğümün adresi bulunur; aksi durumda boş/bağlantı yok anlamında ULL değeri bulunur. Benzer durum rchild için de geçerlidir. 2. ĠKĠLĠ ARAMA AĞAÇLARI (BIARY SEARCH TREES) Bir ağacın kurulmasında/düğüm eklenmesinde, anahtar veriye bağlı olarak küçük olan sola, büyük olan sağa şeklinde bir kurala uyulursa ağaç, ikili arama ağacı (binary search tree) olarak adlandırılır. Aralarında büyüklük küçüklük gibi bir ilişki olan veriler ikili arama ağaçlarında kullanılabilir. İkili arama ağaçlarında bir değerden iki defa kullanılmasına izin verilmez ve her düğümün en fazla iki çocuğu vardır. İkili arama ağacı üzerinde dolaşma birçok şekilde yapılabilir. Ancak, rastgele dolaşmak yerine, önceden belirlenmiş bir yöntem/kurala uyulması ikili arama ağçlarında tanımlanacak algoritmik ifadeleri kolaylaştırır. Tekrarlamalı (recursive) fonksiyon yapısı kullanılırsa ağaç üzerinde işlem yapan algoritmaların tasarımı kolaylaşır. Önce-kök (preorder), ortada-kök (inorder), sonra-kök (postorder) olarak adlandırılan üç değişik dolaşma şekli çeşitli uygulamalara çözüm olmaktadır. Önce-kök (Preorder) : Kök, Sol, Sağ Ortada-kök (Inorder) : Sol, Kök, Sağ Sonra-kök (Postorder) : Sol, Sağ, Kök 3
Önce-kök yaklaşımında önce ağacın kökü, sonra sol altağaç ve ardından sağ altağaç dolaşılır. Ortada-kökte, önce sol altağaç, kök ve sağ altağaç, sonra-kökte ise, önce sol altağaç, sağ altağaç ve kök dolaşılır. Aşağıda her üç yaklaşım bir ikili arama ağacında gösterilmiştir. Şekil 4 te görüldüğü gibi önce-kökte çıkan bilgi diğerlerinde farklı olmaktadır. A B E C F G Şekil 4 İkili ağaçta çeşitli dolaşma şekilleri Önce-kök : ABCEFG Sonra-kök : CBFGEA Ortada-kök : CBFGEA 2.1 Ağaç erinliği İkili arama ağaçları üzerinde yapılan bütün işlemler ağacın derinliğiyle alakalıdır ve kurulabilecek bütün ikili arama ağaçlarında herhangi bir düğümün derinliği ortalama O(log ) olur. Ġspat: Internal Path Line (IPL) : Ağaç üzerindeki bütün düğümlerin köke olan uzaklıkları toplamıdır. Şekil 5 elemanlı bir ağaç 4
Şekil 5 teki elemanlı ağacın toplam derinliği () olur. ortalama IPL IPL 1 den e kadar olan sayıları içeren bir ikili arama ağacımız olsun. 1... Bu sayılardan bir tanesini k yı düğüm olarak seçelim: 1 den k ya kadar k dan e kadar (k-1) eleman var (-k) eleman var Şekil 6 İki altağaçlı bir ikili ağaç Şekil 6 dan k k 1 + k 1 denklemini elde ederiz. k-1 tane eleman için k ya olan yolları da sayarız Her bir elemanın kök düğüm olma ihtimalini eşit varsayarsak; 1 Pr2......... Pr Pr 1 2 k1 1 k O zaman Şekil 6 dan elde ettiğimiz denklemi k de yerine koyarsak k 1 k 1 k 1 k1 1 2 1 0, 2,... olarak bulunur. O log i e bölersek olur. Buradan da ortalama derinlik = O(log ) olarak bulunur. 5
k1 k 1 k k1 k 1 0 1 k1 k 1 2.... 0 1 O halde elimizde iki tane denklem olur: 1. 2. 1 2 k 0 k 1 2 1 1 2 k 0 k 2 enklemlerin sol taraflarını pay ve paydadaki ve -1 değerleriyle çarpalım: 1. 2. 1 2 k 1 k 1 2 k0 1 1 2 k 2 1 1. denklemden 2. yi çıkaralım: 1 1 2 1 2 1 1 1 2 1 => bunu +1 e bölersek 1 2 1 1 1 ; S 1 olsun. S S 1 2 1 1 S i toplam ifadesi biçiminde yazalım: S 1 k 1 2k k 1 k 2 S 0 0 0) ( S 6
S 1 k1 S 1 2k 1 k 1 k 2 k 1 k 2 O log Ortalama derinlik = 1 O log k O log Olog olarak bulunur. Eğer ikili ağaç sıralı değilse, ağaç üzerinde koşacak olan ekleme, listeleme, arama, silme gibi işlemler hep ağaç üzerinde dolaşmayı gerektirir. Çünkü, ağaç üzerindeki tüm düğümlere bakılması gerekebilir. Örneğin bir düğümün silinmesi için önce ağaç üzerinde varlığı ve yeri araştırılmalıdır. Bu nedenle sıralı olmayan ikili ağaçlar üzerinde işlem yapacak algoritmaların zaman karmaşıklığı, genel olarak, toplam düğüm sayısı olmak üzere, O() olur. Zaman karmaşıklığını azaltmak için sıralı özelliğinden dolayı ikili arama ağaçlarının kullanılması yaygındır. Bu durumda ağaç üzerinde arama işleminin ve silme, ekleme gibi, temelde, aramaya dayanan işlemlerin maliyetleri O(log 2 ) mertebesine iner. üğümleri listeleme, ağacı dosyaya yazma gibi işlemleri maliyeti ise, yine O() de kalır. Ağaç uygulamasında daha çok arama, ekleme ve silme işlemlerinin yapıldığı göz önüne alınırsa, uygulamalarda sıralı ikili ağaç kullanılmasının kaçınılmaz olduğu aşikardır. 2.2 ĠKĠLĠ ARAMA AĞAÇLARI ĠÇĠ ALGORĠTMALAR 2.2.1 Ġkili Arama Ağacı Ġçin Tanımlamalar struct Treeode; typedef struct Treeode *Position; typedef struct Treeode *SearchTree; SearchTree MakeEmpty( SearchTree T ); Position Find( ElementType X, SearchTree T ); Position FindMin( SearchTree T ); Position FindMax( SearchTree T ); SearchTree Insert( ElementType X, SearchTree T ); SearchTree elete( ElementType X, SearchTree T ); ElementType Retrieve( Position P ); struct Treeode { ElementType Element; SearchTree Left; SearchTree Right; }; Program 2 İkili Arama Ağacı İçin Tanımlamalar 7
Tanımlanan Treeode yapısının tipinde Position ve SearchTree adlarında iki tane işaretçi tanımlanmıştır. Her fonksiyonun hangi tipte argüman alıp hangi tipte değer döndürecekleri deklarasyonlarında belirtilmiştir. 2.2.2 MakeEmpty SearchTree MakeEmpty( SearchTree T ) { if( T!= ULL ) { MakeEmpty( T->Left ); MakeEmpty( T->Right ); free( T ); } return ULL; } Program 3 MakeEmpty fonksiyonunun tanımı MakeEmpty fonksiyonu bir T ağacı alır ve T ağacı boş değilse T yi free ile boşaltır ve sürekli tekrarlamalı (recursive) olarak aynı fonksiyon çağırılarak bütün T ağacı silinmiş olur ve boş ağacı döndürür. Eğer T ağacı boşsa hiçbir şey yapmaz ve ULL döndürür. O(1) zamanda çalışır. 2.2.3 Find Position Find( ElementType X, SearchTree T ) { if( T == ULL ) return ULL; if( X < T->Element ) return Find( X, T->Left ); else if( X > T->Element ) return Find( X, T->Right ); else return T; } Program 4 Find Fonksiyonunun Tanımı 8
Find fonksiyonunda bir T ağacında X değeri aranmaktadır. Eğer T ağacı boşsa işlem yapılmaz ve fonksiyon ULL değerini döndürür. Eğer aranan X değeri T ağacının ilk elemanından küçükse sol altağaca geçilir ve tekrarlamalı (recursive) olarak X değeri bulunana kadar sol altağaçta arama yapılır. Eğer aranan X değeri T ağacının ilk elemanından büyükse sağ altağaca geçilir ve tekrarlamalı (recursive) olarak X değeri bulunana kadar sağ altağaçta arama yapılır. Tworst = O(), Tbest = O(1), Taverage = O(log) süre alır. 2.2.4 FindMin Position FindMin( SearchTree T ) { if( T == ULL ) return ULL; else if( T->Left == ULL ) return T; else return FindMin( T->Left ); } Program 5 FindMin Fonksiyonunun Tanımı FindMin fonksiyonu ağaçtaki en küçük elemanı bulur ve pozisyonunu döndürür. Eğer T ağacı boşsa ULL döndürür, boş değilse ve sol düğümü boşsa T ağacını aynen döndürür, çünkü sol altağaç olmayacağı için küçük değerler de olmayacaktır. Eğer sol altağaç mevcutsa FindMin fonksiyonu rekürsif olarak en küçük elemanı bulana kadar devam eder. FindMin : Tworst = O(), Tbest = O(1), Taverage = O(log) süre alır. 9
2.2.4 FindMax Position FindMax( SearchTree T ) { if( T!= ULL ) while( T->Right!= ULL ) T = T->Right; return T; } Program 6 FindMax Fonksiyonunun Tanımı FindMax fonksiyonu ağaçtaki en büyük elemanı bulur ve pozisyonunu döndürür. Eğer T ağacı boş değilse ve sağ düğüm boş değilken T ağacı T nin sağ altağacına eşitlenir. Yani, fonksiyon artık sağ altağaç için çalışır. FindMax : Tworst = O(), Tbest = O(1), Taverage = O(log) süre alır. 10