Veri Yapıları - WordPress.com

advertisement
27.12.2013
Veri Yapıları
Ağaçlar
Dr. Sinan TUNCEL
Ağaçlar genel bilgi
•
•
•
Ağaçlar, fizikçi Gustava Kirşof tarafından 1847’de kablo
ağlarındaki elektrik akışını formülize etmek için
kullanılmıştır.
Kirşof yasaları olarak adlandırılan denklemlerin tamamı
bağımsız olmadığından Kirşof ağaçları kullanarak bu
denklemlerin hangilerinin bağımsız olduğunu belirlemiştir.
Ağaç terimi, bu çalışmalardan on yıl sonra İngiliz
matematikçi Arthur Cayley tarafından verilmiştir. Cayley
matematik içerisindeki bir problemi incelemek için ağaçlar
üzerine odaklanarak çalışmalar gerçekleştirmiştir. Cayley
kimyadaki izomerleri ağaçları kullanarak incelemiş ve teoriyi
uygulamaya taşımına becerisini göstermiştir.
1
27.12.2013
Ağaçlar genel bilgi
•
•
•
•
Ağaçlar, verilerin birbirine sanki bir ağaç yapısı
oluşturuyormuş gibi sanal olarak bağlanmasıyla elde
edilen bir veri modelidir. Ağaç veri modeli daha fazla belek
alanına gereksinim duyar.
Bağlı listeler, yığınlar ve kuyruklar doğrusal (linear) veri
yapılarıdır. Ağaçlar ise doğrusal olmayan belirli niteliklere
sahip iki boyutlu veri yapılarıdır.
İçerisinde çevrim (cycle) bulundurmayan bağlantılı grafların
özel bir türüne ağaç adı verilir.
Ağaçlar bir çok nedenden dolayı graf teorisi içerisinde
önemli bir yere sahiptir. Ayrıca, ağaçlar graf teorisinin birçok
uygulamasında ön plana çıkmaktadır.
Dr.Sinan TUNCEL
3
Nerelerde Kullanılır
Ağaç veri yapısı, günlük yaşamda da karşılaştığımız bir
yapıdır.
• Bir insanın/bitkinin soy ağacı
• hiyerarşik bir yönetimdeki ilişkiler
• şirketlerdeki organizasyon şeması
ağaç veri yapısı kullanılarak kolayca grafiksel olarak
tanımlanabilir.
2
27.12.2013
Nerelerde Kullanılır
Ağaç veri yapısı, bilgisayar bilimlerinde önemli yer tutar. Yazılım
dünyasında birçok yerde programcının karşısına çıkar.
•
•
•
•
•
•
•
•
•
•
Arama
Sıralama
Söz dizim
Veri sıkıştırma
Çözümleme (syntax analysis)
Kod optimizasyonu (code optimization)
Derleyici gerçekleştirimindeki ara süreçler
Hiyerarşik veritabanı
İşletim sistemlerinin dosya sistemi.
Oyunların olası hamleleri.
Nerelerde Kullanılır
3
27.12.2013
Nerelerde Kullanılır
Dosya Sistemi
/ymt
kodlar
kitaplar
ymt219 ymt112
1.pdf
2.pdf
1.pdf
...
dersler
2010-2011 eski
a.java b.java
ymt219
ymt217
1.ppt
1.doc
ymt215
...
1.Pdf
7
Faydaları
Esneklik : Tek bir amaç için en iyi çözüm ağaç yapısı olmasa da,
çoklu çözümler için ağaç yapısının esnekliği bu yapının avantajı
olarak sayılabilir.
Etkili Arama: Arama sırasında ağacın bazı dalları budanarak arama
yapılması performans artışı sağlar ve bu da verimliliği arttırır.
Ağaç yapısı genellikle bilginin istenilen bölümüne konumlanmak
için genellikle filtreleme aracı olarak kullanılır.
Doğal Temsil: Bilgiyi temsil etmenin doğal bir yoludur.
Ağaç yapısı bir uygulamayı bazen daha kolay uygulanabilir bir hale
dönüştürür.
4
27.12.2013
Ağaç Üzerinde Bazı Tanımlar
2
Dügüm (node) : Agacın her
bir elemanına dügüm adı
verilir. Örnekler : A, B, C.
Kök
Derinlik
1
A
B
C
Ara
Düğüm
Yaprak
Düğüm
3
D
4
E
Yaprak
Düğüm
F
G
7 düğümlü ağaç
Kök (root) :Agacın en üstteki
dügümüne kök (root) adı
verilir Düzey 0'daki tek
dügüm.
Çocuk (child) : : Bir düğüme
doğrudan bağlı (sol ve sag
bagı aracılıgı ile baglandıgı
dügümler) olan düğümlere o
düğümün çocukları denir. B
ve C, A'nın çocuklarıdır.
9
Ağaç Üzerinde Bazı Tanımlar
Kardeş Düğüm (sibling, brother) : Aynı düğüme bağlı (Aynı parent'a sahip) düğümlere denir.
Örnekler : B ile C kardestir. D ile E kardestir. H ile I kardestir.
Aile (Parent) : Düğümlerin doğrudan bağlı olduğu düğüme denir (Bir dügüm, sag ve sol bagları ile
baglandıgı dügümlerin parent'ıdır) A dügümü, B ve C dügümlerinin parent'ıdır.
Ata: Aile düğümünün üstündeki düğüme ata denir.
Orman: Ağaçlar kümesi
Yol: Bir düğümden başka bir düğüme gidebilmek için üzerinden geçilmesi gereken düğümlerin
listesi.
Derece: Bir düğümden alt hiyerarşiye yapılan bağlantıların sayısıdır.
Düzey / Derinlik (depth) : Kök ile düğüm arasındaki yolun üzerinde bulunan düğümlerin sayısıdır.
Bir düğümün kök dügümden olan uzaklıgıdır. Örnek : D düğümünün düzeyi veya derinliği 2'dir.
Ağacın derinligi (depth of tree) : En derindeki yaprağın derinliği veya yüksekligi (height).
Yükseklik: Bir düğümün kendi silsilesindeki en uzak mesafedeki yaprak düğüme olan düzey sayısı
5
27.12.2013
Ağaç Üzerinde Bazı Tanımlar
Altağaç: Ağacın herhangi bir dalı
Yaprak (leaf) : Sol ve sag bagı bos olan düğümlere yaprak adı verilir. Örnekler : D,G,H,I.
Ancestor (üst dügüm) : Bir düğümün parent'ı birinci ancestor'ıdır. Parent'ın parent'ı
(recursion) ikinci ancestor'ıdır. Kök, kendi hariç tüm dügümlerin ancestor'ıdır.
Descendant (alt dügüm) : Bir düğümün iki çocuğu birinci alt düğümüdür. Onların
çocukları da ikinci alt düğümüdür.
N tane düğümden oluşan bir ağacın kenar sayısı N-1 tanedir.
Ağaçtaki iki düğüm arasında en fazla 1 yol olabilir.
Dr.Sinan TUNCEL
11
Ağaç Üzerinde Bazı Tanımlar
Kök
A
B
C
D
E
F
G
Tanım
kök
B
D
Çocuk/Derece
2
0
0
Kardeş
1
2
3
Düzey
1
2
3
Aile
yok
kök
C
Ata
yok
yok
Kök
Yol
A
A, B
A,C,D
Derinlik
1
2
3
Yükseklik
4
3
2
12
6
27.12.2013
Ağaç Üzerinde Bazı Tanımlar
Dr.Sinan TUNCEL
13
Ağaçlar
•
Ağaç yapıları ikili veya çoklu bağlı listeler ile gerçekleştirilebilir.
•
Ağaçlardaki düğümlerden iki veya daha fazla bağ çıkabilir. İkili
ağaçlar (binary trees), düğümlerinde en fazla iki bağ içeren (0,1
veya 2) ağaçlardır.
Ağaç veri modelinde,
bir kök işaretçisi,
sonlu sayıda düğümleri ve onları birbirine bağlayan dalları vardır.
Veri ağacın düğümlerinde tutulur.
Dallarda ise geçiş koşulları vardır.
Her ağacın bir kök işaretçisi vardır.
Ağaca henüz bir düğüm eklenmemiş ise ağaç boştur ve kök
işaretçisi NULL değerini gösterir. Ağaç bu kök etrafında dallanır
ve genişler.
•
Dr.Sinan TUNCEL
14
7
27.12.2013
Ağaç veri modelinin uygulanması
•
•
•
Sekil de görülen ağacın düğümlerindeki bilgiler sayılardan oluşmuştur.
Her düğümdeki sol ve sağ bağlar yardımı ile diğer düğümlere ulaşılır. Sol
(leftptr) ve sag (rightptr) baglar bos ("NULL" = "/" = "\") da olabilir.
Düğüm yapıları değişik türlerde bilgiler içeren veya birden fazla bilgi içeren
ağaçlar da olabilir. veri yapılarındaki ağaçlar kökü yukarıda yaprakları aşağıda
olacak şekilde çizilirler.
Level
Kök Node
1
1
Kök
2
4
5
Yaprak
8
2
3
Yaprak
Node
6
9
Yaprak
3
7
10
4
11
12
13
Yaprak
Yaprak
Yaprak
5
(Binary Tree)
Dr.Sinan TUNCEL
15
Ağaç (Tree)
Arama ve sıralama işlemleri için kullanılan İkili Arama Ağacı
(BST: Binary Search Tree) gibi özel ağaç türleri de vardır.
Ağaç yapıları ikili veya çoklu bağlı listeler ile gerçekleştirilebilir.
bağ1
veri
veri
bağ2
veri
8
27.12.2013
Ağaçlar
Ağaç tanımı özyinelemelidir:
Bir ağaç iki şekilde olabilir:
a. Boş düğüm kümesi, veya
b. Kök ismi verilen bir düğüm ve 0 veya daha fazla alt-ağacı olan
yapı.
Ağaç veri yapısını gerçekleştirmek için 2 yol vardır.
Bağlantılı liste kullanmak
Dizi kullanmak
17
Ağaç Gerçekleştirimi
Her bir bağlantı için birer bağlantı bilgisi tutulur.
A
B
C
E
•
D
F
Problem: Bir sonraki elemanın çocuk sayısını bilmiyoruz.
18
9
27.12.2013
Ağaç Gerçekleştirimi
Daha iyisi: 1. Çocuk/Kardeş Gösterimi
Her düğümde iki bağlantı bilgisi tutularak hem çocuk hem de yandaki
kardeş tutulabilir.
İstenildiği kadar çocuk/kardeş olabilir.
JAVA Declaration
class AgacDugumu {
int eleman;
AgacDugumu ilkCocuk;
AgacDugumu kardes;
}
A
B
C
E
D
F
19
İkili Ağaç
İkili Agaç (Binary Tree) :
Kök olarak adlandırılan özel bir düğüm vardır.
– Her düğüm en fazla iki düğüme bağlıdır.
– Kök hariç her düğüm bir daldan gelmektedir.
– Tüm düğümlerden yukarı doğru çıkıldıkça sonuçta köke ulaşılır
İkili Arama Agacı (Binary Search Tree) : Bos olan veya her dügümü
asagıdaki sartlara uyan anahtara sahip bir ikili agaçtır :
• Kökün solundaki alt ağaçlardaki (eger varsa) tüm anahtarlar kökteki
anahtardan küçüktür.
• Kökün sağındaki alt ağaçlardaki (eğer varsa) tüm anahtarlar kökteki
anahtardan büyüktür.
• Sol ve sag alt agaçlar da ikili arama agaçlarıdır.
Dr.Sinan TUNCEL
20
10
27.12.2013
İkili Ağaç
Full binary tree :
i)
Her yapragı aynı derinlikte olan
ii)
ii) Yaprak olmayan dügümlerin tümünün iki çocugu olan agaç Full
(Strictly) Binary Tree'dir.
iii)
Bir full binary tree'de n tane yaprak varsa bu agaçta toplam 2n-1
dügüm vardır.
Complete binary tree : Full binary tree'de yeni bir derinlige soldan saga
dogru dügümler eklendiginde olusan agaçlara Complete Binary Tree
denilir. Böyle bir agaçta bazı yapraklar digerlerinden daha derindir. Bu
nedenle full binary tree olmayabilirler. En derin düzeyde dügümler
olabildigince soldadır.
General Tree (Agaç) : Her düğümün en fazla iki çocugu olabilme sınırı
olmayan ağaçlardır.
Dr.Sinan TUNCEL
21
İkili Ağaç
İkili ağac bir düğümün en fazla 2 tane çocuğa sahip olabildiği
ağaçtır
Her düğüm en fazla 2 çocuğa sahip olabilir.
Bilgisayar bilimlerinde en yaygın ağaçtır.
Kök
A
A
A
B
B
D
C
İki farklı ikili ağaç
Z
P
I
K
M
Sağ alt ağaç
22
11
27.12.2013
İkili Ağaç (devam)
N tane düğüm veriliyor, İkili ağacın minimum derinliği nedir.
Derinlik 1: N = 1 = 20 düğüm
Derinlik 2: N = 2 ve 3 düğüm = 21 ve 21+1 -1 düğüm
Herhangi bir d derinliğinde, N = ?
23
İkili Ağaç (devam)
•
•
•
•
Derinlik 0: N = 1 = 20 düğüm
Derinlik 1: N = 2 ve 3 düğüm = 21 ve 21+1 -1
düğüm
D derinliğinde , N = 2d ve 2d+1-1 düğüm (tam
bir ikili ağaç)
En küçük derinlik:
log N ≤ d ≤ log(N+1)-1 or Θ(log N)
24
12
27.12.2013
İkili Ağaç (devam)
N düğümlü ikili ağacın minimum derinliği: Θ(log N)
İkili ağacın maksimum derinliği ne kadardır?
Dengesiz ağaç: Ağaç bir bağlantılı liste olursa!
Maksimum derinlik = N
Amaç: Arama gibi operasyonlarda bağlantılı listeden daha iyi
performans sağlamak için derinliğin log N de tutulması
gerekmektedir.
–
–
Bağlantılı liste
Derinlik = N
25
İkili Ağaç Gerçekleştirimi
d
sol
veri
sag
kök
public class İkiliAgacDugumu {
public İkiliAgacDugumu sol;
public int veri;
public İkiliAgacDugumu sag;
}
4
12
6
45
7
26
13
27.12.2013
İkili Ağaç Gerçekleştirimi
/* İkili ağaç düğümü oluşturur.
*/
İkiliAgacDugumu DugumOlustur(int veri){
İkiliAgacDugumu dugum = new İkiliAgacDugumu();
dugum.veri = veri;
dugum.sol = null;
dugum.sag = null;
dugum
veri
null
null
return dugum;
}
•
Bu yordam ikili ağaç düğümü oluşturur ve bunu geri
döndürür.
27
İkili Ağaç Gerçekleştirimi
İkiliAgacDugumu dugum = null;
public static void main main(){
kok = DugumOlustur(4);
kök
4
kok.sol = DugumOlustur(6);
kok.sag = DugumOlustur(12);
12
6
kok.sol.sol = DugumOlustur(45);
45
7
1
kok.sag.sol = DugumOlustur(7);
kok.sag.sag = DugumOlustur(1);
} /* main */
•
Kök verilmiş olsun tüm ağaç üzerinde dolaşıp elemanları
ekrana nasıl yazdırırız.?
−
Ağaç dolaşma algoritmaları
28
14
27.12.2013
Kullanılan Verimli Arama Ağaçları
Fikir: Verileri arama ağacı yapısına göre düzenlersek arama
işlemi daha verimli olacaktır.
1. İkili Arama Ağacı (Binary search tree (BST))
2. AVL Ağacı
3. Splay Ağacı
4. Red-Black Ağacı
5. B Ağacı ve B+ Ağacı
29
İkili Arama Ağaçı
A İkili Arama Ağacı her bir düğümdeki değerlere göre düzenlenir:
Sol alt ağaçtaki tüm değerler kök düğümünden küçüktür.
Sağ alt ağaçtaki tüm değerler kök düğümünden büyüktür.
Kök
Kök
2
5
SolAA
7
3
SağAA
3
SağAA
7
4
2
<5
>2
8
>5
5
8
4
30
15
27.12.2013
İkili Arama Ağacı - Tanımlama
BST Dugum
x
sol
deger
sag
/* İKİLİ ARAMA AĞACI */
public class BST {
Private BSTDugum kok;
public
public
public
public
public
public
BST(){kok=null;}
void Ekle(int deger);
void Sil(int deger);
BSTNode Bul(int key);
BSTNode Min();
BSTNode Max();
public class BSTDugum {
public BSTDugum sol;
public int deger;
public BSTDugum sag;
}
3
4
2
9
};
7
31
BST Operasyonları - Bul
•
Değeri içeren düğümü bul ve bu düğümü geri
döndür.
15
K
1.
2.
3.
4.
Aranan Sayı=13
kök
kök
SolA
SagA
<K
>K
Arama işlemine kökten başla
if (aranaDeger == kok.deger)
if (aranaDeger < kok.deger)
else
<15
>15
return kok;
Ara SolAA
Ara SagAA
32
16
27.12.2013
BST Operasyonları - Bul
Kök
Aranan
15
sayı=13
18
6
2
13
4
9
•
•
30
7
3
public BSTDugum Bul(int deger){
return Bul2(kok, deger);
}
public BSTDugum Bul2(BSTDugum kok,
int deger){
if (kok == null) return null;
if (deger == kok.deger)
return kok;
else if (deger < kok.deger)
return Bul2(kok.sol, deger);
else /* deger > kok.deger */
return Bul2(kok.sag, deger);
}
Mavi renkli düğümler arama sırasında ziyaret edilen
düğümlerdir.
Algoritmanın çalışma karmasıklığı O(d) dir. (d = ağaçın
derinliği)
33
BST Operasyonları - Bul
Aynı algoritma while döngüsü yardımıyla yinelemeli şekilde
yazılabilir
public BSTDugum Bul(int deger){
BSTDugum p = kok;
while (p){
if (deger == p.deger)
return p;
else if (deger < p.deger) p = p.sol;
else /* deger > p.deger */ p = p.sag;
} /* while-bitti */
return null;
} //bul-Bitti
•
Yinelemeli versiyon özyinelemeli versiyona göre
daha verimli çalışır.
34
17
27.12.2013
BST Operasyonları - Min
Ağaçtaki en küçük elemanı içeren düğümü bulur ve geri
döndürür.
En küçük elemanı içeren düğüm en soldaki düğümde bulunur.
Kökten başlayarak devamlı sola gidilerek bulunur.
public BSTDugum Min(){
if (kok == null)
return null;
Kök
15
BSTDugum p = kok;
while (p.sol != null){
p = p.sol;
}
return p;
}
18
6
2
30
7
3
13
4
9
35
BST Operasyonları - Max
Ağaçtaki en büyük elemanı içeren düğümü bulur ve geri
döndürür.
En büyük elemanı içeren düğüm en sağdaki düğümde bulunur.
Kökten başlayarak devamlı sağa gidilerek bulunur.
public BSTDugum Max(){
if (kok == null)
return null;
Kök
15
BSTDugum p = kok;
while (p.sag != null){
p = p.sag;
}
return p;
}
18
6
2
30
7
3
13
4
9
36
18
27.12.2013
BST Operasyonları – Ekle(int deger)
•
•
•
Eklenecek değeri içeren “z”
isimli yeni bir düğüm
oluştur.
• Ö.g.: Ekle 14
z
14
NULL
NULL
Eklenecek “z” düğümü.
z.deger = 14
Kökten başlayarak ağaç
üzerinde eklenecek sayıyı
arıyormuş gibi aşağıya
doğru ilerle.
z
Kok
14
15
18
6
Yeni düğüm aramanın bittiği
düğümün çocuğu olmalıdır.
2
30
7
3
13
4
9
Eklemeden
Eklemedensonra
önce
14
37
BST Operasyonları – Ekle(int deger)
public void Ekle(int deger){
BSTDugum pp = null; /* pp p’nin ailesi */
BSTDugum p = kok;
/* Kökten başla ve aşağıya doğru ilerle*/
while (p){
pp = p;
if (deger == p.deger)
return; /* Zaten var */
else if (deger < p.deger) p = p.sol;
else /* deger > p.deger */ p = p.sag;
}
/* Yeni değeri kaydedeceğimiz düğüm */
BSTDugum z = new BSTDugum();
z.deger = deger; z.sol = z.sag = null;
if (kok == null) kok = z; /* Boş ağaca ekleme */
else if (deger < pp.deger) pp.sag = z;
else
pp.sol = z;
} // ekleme işlemi bitti.
38
19
27.12.2013
BST Operasyonları – Sil(int deger)
Silme işlemi biraz karmaşıktır.
3 durum var:
1. Silinecek düğümün hiç
çocuğu yoksa (yaprak
düğüm)
–
2.
Kök
15
Sil 9
Silinecek düğümün 1
çocuğu varsa
–
18
6
Sil 7
2
30
7
3
13
4
14
9
3.
Silinecek düğümün 2
çocuğu varsa
1.
Sil 6
39
Silme: Durum 1 – Yaprak Düğümü Silme
Kök
Kök
15
15
18
6
2
30
7
3
13
4
18
6
2
30
7
3
4
13
9
Sil 9: Düğümü kaldırın ve
bağlantı kısmını güncelleyin
9 silindikten sonra
40
20
27.12.2013
Silme: Durum 2 – 1 Çocuklu Düğüm
Kök
Kök
15
15
18
6
2
30
7
3
13
4
18
6
13
3
2
4
30
9
9
7 silindikten sonra
Sil 7: Silinecek düğümün
ailesi ve çocuğu
arasında bağ kurulur
41
Silme: Durum 3 – 2 Çocuklu Düğüm
Kök
Kök
17
17
18
6
2
30
14
3
16
10
4
7
18
7
2
13
30
14
3
16
10
4
8
13
8
1)
2)
3)
4)
Sil 6:
Sağ alt ağaçtaki en küçük eleman bulunur.(7)
Bu elemanın sol çocuğu olmayacaktır.
6 ve 7 içeren düğümlerin içeriklerini değiştirin
6 nolu eleman 1 çocuğu varmış gibi silinir.
6 silindikten sonra
Not: Sağ alt ağaçtaki en
küçük eleman yerine sol alt
ağaçtaki en büyük eleman
bulunarak aynı işlemler
yapılabilir.
42
21
27.12.2013
İkili Ağaç Üzerinde Dolaşma
İkili ağaç üzerinde dolaşma birçok şekilde yapılabilir. Ancak
belirli bir yönteme uyulması algoritmik ifadeyi kolaylaştırır.
İkili ağaç üzerinde dolaşmak için 3 temel yol vardır. Bunlar:
Önce-kök (Preorder): Kök, Sol, Sağ
Önce ağacın kökü, sonra sol alt ağaç ve ardından sağ alt ağaç
Ortada-kök (Inorder): Sol, Kök, Sağ
Önce sol alt ağaç, kök ve sağ alt ağaç
Sonra-kök (Postorder): Sol, Sağ, Kök
Önce sol alt ağaç, sağ alt ağaç ve kök.
43
Örnek
Kök
Önce-kök
ACPDZMIK
A
Ortada-kök
PCAMZDIK
D
C
Z
P
M
I
Sonra-kök
PCMZKIDA
K
44
22
27.12.2013
Algoritma
OnceKok(IkiliAgacDugumu kok){
if (kok == null) return;
System.out.print(kok.veri+" ");
OnceKok(kok.sol);
OnceKok(kok.sag);
}
OrtadaKok(IkiliAgacDugumu kok){
if (kok == null) return;
OrtadaKok(kok.sol);
System.out.print(kok.veri+" ");
OrtadaKok(kok.sag);
}
SonraKok(IkiliAgacDugumu kok){
if (kok == null) return;
SonraKok(kok.sol);
SonraKok(kok.sag);
System.out.print(kok.veri+" ");
}
45
Aynı Sayılarla Başa Çıkma
Ağaç içerisindeki aynı sayılarla aşağıda verilen iki şeklide
başa çıkılabilir:
Düğümde saklanan bir sayaç değişkeni ile
Veya
Düğümde kullanılan bağlantılı liste ile
Kök
5
3
2 3
Kök
2
5
7 4
1
4
2
3
7
8 6
46
23
27.12.2013
İkili Arama ağacı Uygulamaları
İkili arama ağacı harita, sözlük gibi birçok uygulamada
kullanılır.
İkili arama ağacı (anahtar, değer) çifti şeklinde kullanılacak sistemler
için uygundur.
Ö.g.: Şehir Bilgi Sistemi
Posta kodu veriliyor , şehir ismi döndürülüyor. (posta kodu/ Şehir
ismi)
Ö.g.: telefon rehberi
İsim veriliyor telefon numarası veya adres döndürülüyor. (isim,
Adres/Telefon)
Ö.g.: Sözlük
Kelime veriliyor anlamı döndürülüyor. (kelime, anlam)
47
İyi çalışmalar…
Dr.Sinan TUNCEL
48
24
Download