C ++ STL neden hiçbir "ağaç" kap sağlamaz?


373

Neden C ++ STL "ağaç" kapları sağlamaz ve bunun yerine kullanılacak en iyi şey nedir?

Performans yükseltmesi olarak bir ağacı kullanmak yerine bir nesne hiyerarşisini ağaç olarak saklamak istiyorum ...


7
Bir hiyerarşinin temsilini saklamak için bir ağaca ihtiyacım var.
Roddy

20
Aşağı gibi görünen "doğru" cevapları oylayan adamla beraberim; "Ağaçlar işe yaramaz". Ağaçların belirsiz kullanımları varsa önemlidir.
Joe Soul-bringer

Bence neden önemsiz - henüz kimse standart kütüphanede uygulamamış. Standart kütüphanenin hiç olmadığı std::unordered_mapve std::unordered_setyakın zamana kadar olduğu gibi . Bundan önce standart kütüphanede hiç STL konteyneri yoktu.
doc

1
Düşüncelerim (yine de ilgili standardı hiç okumadım, dolayısıyla bu bir cevap değil, bir yorumdur) STL'nin belirli veri yapılarını umursamaması, karmaşıklık ve hangi işlemlerin desteklendiğiyle ilgili özellikleri önemsemesidir. Dolayısıyla, kullanılan temel yapı, spesifikasyonları karşılaması koşuluyla uygulamalar ve / veya hedef mimariler arasında değişiklik gösterebilir. Eminim std::mapve std::setorada her uygulamada bir ağaç kullanacak, ancak ağaç olmayan bir yapı da spesifikasyonları karşılıyorsa yapmaları gerekmez.
Mark K Cowan

Yanıtlar:


182

Bir ağacı kullanmak istemenizin iki nedeni vardır:

Sorunu ağaç benzeri bir yapı kullanarak yansıtmak istiyorsunuz: Bunun
için grafik grafik kütüphanemiz var

Veya ağaç gibi erişim özelliklerine sahip bir kap istersiniz

Temel olarak bu iki konteynerin özellikleri, pratik olarak ağaçlar kullanılarak uygulanması gerektiği şekildedir (bu aslında bir gereklilik değildir).

Ayrıca bu soruya bakın: C ağacı Uygulaması


64
En yaygın olsalar bile, bir ağacı kullanmanın birçok, birçok nedeni vardır. En yaygın! Herkese eşittir.
Joe Soul-getirici

3
Bir ağaç istemek için üçüncü bir önemli neden, hızlı ekleme / çıkarma ile her zaman sıralanmış bir listedir, ancak bunun için std: multiset vardır.
VoidStar

1
@Durga: Haritayı sıralanmış bir kap olarak kullanırken derinliğin ne kadar alakalı olduğundan emin değilim. Harita, günlük (n) ekleme / silme / aramayı (ve öğeleri sıralı olarak içerir) garanti eder. Bu, tüm harita için kullanılır (genellikle) kırmızı / siyah ağaç olarak uygulanır. Kırmızı / siyah ağaç, ağacın dengede olmasını sağlar. Böylece ağacın derinliği doğrudan ağaçtaki elemanların sayısıyla ilişkilidir.
Martin York

14
Bu cevaba hem 2008'de hem de şimdi katılmıyorum. Standart kütüphane "desteklemez" ve yükseltilen bir şeyin kullanılabilirliği, onu standarda kabul etmemek için bir neden olmamalıdır (ve olmamalıdır). Ek olarak, BGL geneldir ve ondan bağımsız olarak uzmanlaşmış ağaç sınıflarını hak edecek kadar kapsayıcıdır. Ayrıca, std :: map ve std :: set'in bir ağaç gerektirmesi, IMO, stl::red_black_treevb. İçin başka bir argümandır . Son olarak, std::mapve std::setağaçlar dengelidir, std::treeolmayabilir.
einpoklum

1
@einpoklum: "Artırmadaki bir şeyin mevcudiyeti standardı benimsememek için bir neden olmamalı" - artırma amaçlarından biri, standarda dahil edilmeden önce yararlı kütüphaneler için bir kanıt alanı olarak göz önüne alındığında , sadece "kesinlikle!"
Martin Bonner, Monica

94

Muhtemelen aynı nedenden ötürü, güçlendirilmiş bir ağaç kabı yoktur. Böyle bir kabı uygulamanın birçok yolu vardır ve onu kullanacak herkesi tatmin etmenin iyi bir yolu yoktur.

Dikkate alınması gereken bazı konular:

  • Bir düğümün çocuk sayısı sabit mi yoksa değişken mi?
  • Düğüm başına ne kadar ek yük? - yani, ana işaretçiler, kardeş işaretçiler vb.
  • Hangi algoritmalar sağlanmalı? - farklı yineleyiciler, arama algoritmaları vb.

Sonunda sorun, herkes için yeterince yararlı olacak bir ağaç kabının, onu kullanan insanların çoğunu tatmin etmek için çok ağır olacağıdır. Güçlü bir şey arıyorsanız, Grafik Kitaplığını Artırın aslında bir ağaç kütüphanesinin ne için kullanılabileceğinin bir üst kümesidir.

İşte diğer genel ağaç uygulamaları:


5
"... herkesi tatmin etmenin iyi bir yolu yok ..." stl :: map, stl :: multimap ve stl :: set'in stl'nin rb_tree'sine dayalı olması dışında, bu temel türlerin yaptığı gibi birçok durumu karşılaması gerekir .
Catskul

44
Bir düğümün çocuklarını geri getirmenin bir yolu olmadığından std::map, bu ağaç kaplarını çağırmam. Bunlar yaygın olarak ağaç olarak uygulanan birleştirici kaplardır. Büyük fark.
Mooing Duck

2
Mooing Duck'a katılıyorum, bir std :: haritasında bir genişlik ilk aramasını nasıl uygularsınız? Çok pahalı olacak
Marco A.

1
Kasper Peeters 'tree.hh kullanmaya başladım, ancak GPLv3 veya başka bir GPL sürümü için lisansı inceledikten sonra ticari yazılımımızı kirletecekti. Ticari amaçlar için bir yapıya ihtiyacınız varsa @hplbsh tarafından yorumda sağlanan treetree bakarak tavsiye ederim.
Jake88

3
Ağaçlardaki çeşitliliğe özgü gereklilikler, hiç ağaç türüne sahip olmamak için farklı ağaç türlerine sahip olmak için bir argüman.
André

50

STL'nin felsefesi, konteynerin nasıl uygulandığına göre değil, garantilere dayalı bir kap seçmenizdir. Örneğin, kapsayıcı seçiminiz hızlı arama ihtiyacına dayalı olabilir. İlgilendiğiniz her şey için, konteyner tek yönlü bir liste olarak uygulanabilir - arama çok hızlı olduğu sürece mutlu olursunuz. Çünkü iç kısma dokunmuyorsunuz, erişim için yineleyicileri veya üye işlevlerini kullanıyorsunuz. Kodunuz, kabın nasıl uygulandığına değil, ne kadar hızlı olduğuna veya sabit ve tanımlanmış bir sıralamaya sahip olup olmadığına veya alan üzerinde etkili olup olmadığına ve benzerlerine bağlıdır.


12
Konteyner uygulamaları hakkında konuştuğunu sanmıyorum, gerçek bir ağaç kabının kendisinden bahsediyor.
Mooing Duck

3
@MooingDuck Ben wilhelmtell ne anlama geliyor C ++ standart kütüphane temel veri yapılarına göre kap tanımlamıyor olduğunu; konteynerleri sadece arayüzlerine ve asimptotik performans gibi gözlemlenebilir özelliklere göre tanımlar. Bunu düşündüğünüzde, bir ağaç aslında (onları bildiğimiz gibi) bir kap değildir. Düz bir ileriye sahip değiller end()ve begin()tüm elemanlar, vb.Ile yineleyebilirsiniz
Jordan Melo

7
@JordanMelo: Her noktada saçmalık. Nesneler içeren bir şey. Yineleme yapmak için bir start () ve end () ve çift yönlü yineleyicilere sahip olacak şekilde tasarlanması çok önemsizdir. Her kap farklı özelliklere sahiptir. Bir kişinin ağaç özelliklerine sahip olması yararlı olacaktır. Çok kolay olmalı.
Mooing Duck

Bu nedenle, çocuk ve ebeveyn düğümleri için hızlı aramalar ve makul bellek gereksinimleri sağlayan bir kapsayıcıya sahip olmak ister.
doc

@JordanMelo: Bu perspektiften, ayrıca adaptörleri kuyruklar, yığınlar veya öncelikli kuyruklar gibi STL aittir (onlar da yok olmaz begin()ve end()). Ve bir öncelik kuyruğunun tipik olarak en azından teoride bir ağaç (gerçek uygulamalara rağmen) bir yığın olduğunu unutmayın. Dolayısıyla, bir ağacı farklı bir temel veri yapısı kullanarak adaptör olarak uygulasanız bile, STL'ye dahil edilmeye uygun olacaktır.
andreee

48

"Nesneler hiyerarşisini ağaç olarak saklamak istiyorum"

C ++ 11 geldi ve gitti ve hala bir std::treefikir verme ihtiyacı görmediler, ancak fikir ortaya çıktı ( buraya bakın ). Belki de bunu eklememelerinin nedeni, mevcut kapların üzerine kendi yapınızı oluşturmanın çok kolay olmasıdır. Örneğin...

template< typename T >
struct tree_node
   {
   T t;
   std::vector<tree_node> children;
   };

Basit bir geçiş özyineleme kullanır ...

template< typename T >
void tree_node<T>::walk_depth_first() const
   {
   cout<<t;
   for ( auto & n: children ) n.walk_depth_first();
   }

Bir hiyerarşiyi korumak istiyorsanız ve bunun STL algoritmalarıyla çalışmasını istiyorsanız , işler karmaşıklaşabilir. Kendi yineleyicilerinizi oluşturabilir ve bazı uyumluluk elde edebilirsiniz, ancak algoritmaların çoğu hiyerarşi için bir anlam ifade etmez (örneğin, bir aralığın sırasını değiştiren herhangi bir şey). Bir hiyerarşi içinde bir aralığı tanımlamak bile dağınık bir iş olabilir.


2
Proje, bir tree_node öğesinin alt öğelerinin sıralanmasına izin verebilirse, std :: vector <> yerine bir std :: set <> kullanmak ve daha sonra tree_node nesnesine <() işleci eklemek büyük ölçüde iyileşir 'T' benzeri bir nesnenin 'arama' performansı.
J Jorgenson

4
Tembel oldukları ve aslında ilk örneğinizi Tanımsız Davranış yaptıkları ortaya çıkıyor.
user541686

2
@Mehrdad: Sonunda buradaki yorumunuzun arkasındaki ayrıntıyı sormaya karar verdim .
nobar

many of the algorithms simply don't make any sense for a hierarchy. Bir yorum meselesi. Stackoverflow kullanıcılarının bir yapısını hayal edin ve her yıl daha fazla itibar puanına sahip olanların daha düşük itibar puanına sahip olanlara patron olmasını istiyorsunuz. Böylece her yıl BFS yineleyici ve uygun karşılaştırma, sadece çalıştırın std::sort(tree.begin(), tree.end()).
doc

Aynı şekilde, kolaylıkla yerine (örneğin JSON gibi, yapısal olmayan anahtar değeri kayıtlarını model) bir birleştirici ağacı oluşturmak olabilir vectorile mapyukarıdaki örnekte. JSON benzeri bir yapının tam desteği variantiçin, düğümleri tanımlamak için kullanabilirsiniz .
nobar

43

Bir RB ağacı uygulaması arıyorsanız, stl_tree.h sizin için de uygun olabilir.


14
Garip bir şekilde, asıl soruyu cevaplayan tek cevap budur.
Catskul

12
"Heiarchy" istediği düşünüldüğünde, "dengeleme" olan herhangi bir şeyin yanlış cevap olduğunu varsaymak güvenlidir.
Mooing Duck

11
"Bu, diğer kütüphane başlıkları tarafından dahil edilen bir iç başlık dosyasıdır. Doğrudan kullanmaya çalışmamalısınız."
Dan

3
@ Dan: Kopyalamak doğrudan kullanmak anlamına gelmez.
einpoklum

12

std :: map kırmızı siyah bir ağacı temel alır . Kendi ağaç türlerinizi uygulamanıza yardımcı olması için başka kaplar da kullanabilirsiniz .


13
Genellikle kırmızı-siyah ağaçlar kullanır (Bunu yapmak gerekmez).
Martin York

1
GCC, haritayı uygulamak için bir ağaç kullanır. Herkes kendi VC içerir microsoft ne kullandığını görmek için dizin içerir?
JJ

// Kırmızı-siyah ağaç sınıfı, STL'nin uygulanmasında kullanılmak üzere tasarlanmıştır // ilişkilendirilebilir kaplar (set, çoklu set, harita ve çoklu harita). Bunu stl_tree.h dosyamdan yakaladım.
JJ

@JJ En azından Studio 2010'da, ordered red-black tree of {key, mapped} values, unique keysiçinde tanımlanmış bir iç sınıf kullanır <xtree>. Şu anda daha modern bir sürüme erişiminiz yok.
Justin Time - Monica

8

Bir bakıma, std :: map bir ağaçtır (dengeli bir ikili ağaçla aynı performans özelliklerine sahip olmak gerekir), ancak diğer ağaç işlevlerini göstermez. Gerçek bir ağaç veri yapısı içermemesinin arkasındaki akıl yürütme, muhtemelen sadece stl'deki her şeyi dahil etmeme meselesiydi. Stl, kendi algoritmalarınızı ve veri yapılarınızı uygularken kullanabileceğiniz bir çerçeve olarak görülebilir.

Stl değil İstediğiniz temel kütüphane işlevi, varsa Genelde, düzeltme bakmak için BOOST .

Aksi takdirde, bir var demet ait kütüphaneler dışarı orada senin ağacının ihtiyaçlarına bağlı olarak,.


6

Tüm STL konteyneri harici olarak bir iterasyon mekanizması ile "sekanslar" olarak temsil edilir. Ağaçlar bu deyimi takip etmez.


7
Bir ağaç veri yapısı, yineleyiciler aracılığıyla ön sipariş, düzensiz veya postorder geçişi sağlayabilir. Aslında std :: map bunu yapar.
Andrew Tomazos

3
Evet ve hayır ... "ağaç" ile ne demek istediğine bağlı. std::mapdahili olarak btree olarak uygulanır, ancak harici olarak sıralanmış PAIRS SEQUENCE olarak görünür. Hangi unsur olursa olsun, evrensel olarak kimden önce kimin peşinde olduğunu sorabilirsiniz. Her biri diğerini içeren elemanlar içeren genel ağaç yapıları herhangi bir sıralama veya yön empoze etmez. Bir ağaç yapısını birçok yönden (tekrarlayan | önce derin | son ...) yürüyen yineleyicileri tanımlayabilirsiniz, ancak bir kez yaptıktan sonra, bir std::treekap bunlardan birini bir beginişlevden döndürmelidir . Ve bir veya daha fazla geri dönmek için açık bir neden yoktur.
Emilio Garavaglia

4
Bir std :: haritası genellikle bir B-ağacı değil, dengeli bir ikili arama ağacı ile gösterilir. Yaptığınız aynı argüman std :: unordered_set için geçerli olabilir, doğal bir sırası yoktur, ancak başlangıç ​​ve bitiş yineleyicileri sunar. Başlangıç ​​ve bitiş şartı, tüm unsurları belirli bir sırayla yinelemesidir, doğal olanı değil. ön sipariş, bir ağaç için tamamen geçerli bir yineleme sırasıdır.
Andrew Tomazos

4
Cevabınızın anlamı, bir "dizi" arayüzü olmadığı için stl n-tree veri yapısının olmamasıdır. Bu sadece yanlış.
Andrew Tomazos

3
@EmiloGaravaglia: Tarafından kanıtlandığı gibi std::unordered_set, üyelerini yinelemenin "benzersiz bir yolu" yoktur (aslında yineleme sırası sahte rastgele ve uygulama tanımlıdır), ancak yine de bir stl kapsayıcısıdır - bu, amacınızı çürütür. Bir kaptaki her öğenin üzerine yineleme, sipariş tanımlanmamış olsa bile yine de yararlı bir işlemdir.
Andrew Tomazos

4

Çünkü STL bir "her şey" kütüphanesi değildir. Esasen, bir şeyler inşa etmek için gereken minimum yapıları içerir.


13
İkili ağaçlar son derece basit bir işlevselliktir ve aslında, std :: map, std :: multimap ve stl :: set gibi türler olduğundan diğer kaplardan daha temeldir. Bu türler bunlara dayandığı için altta yatan türün açığa çıkmasını beklersiniz.
Catskul

2
OP'nin ikili bir ağaç istediğini düşünmüyorum, bir hiyerarşi depolamak için bir ağaç istiyor.
Mooing Duck

Sadece bu da değil, STL'ye bir ağaç "kapsayıcısı" eklemek, birçok yeni kavram, örneğin bir ağaç gezgini (genelleştirici yineleme) eklemek anlamına gelecektir.
alfC

5
"Şeyleri inşa etmek için minimum yapılar" çok öznel bir ifadedir. Ham C ++ kavramları ile bir şeyler inşa edebilirsiniz, bu yüzden gerçek minimum hiç STL olmayacağını tahmin ediyorum.
doc


3

IMO, bir ihmal. Ama bence STL'ye bir ağaç yapısı dahil etmemek için iyi bir neden var. En iyi temel TreeNodenesneye üye işlevleri olarak yazılan bir ağacın korunmasında çok fazla mantık vardır . Bir TreeNodeSTL başlığına sarıldığında, daha da karmaşıklaşır.

Örneğin:

template <typename T>
struct TreeNode
{
  T* DATA ; // data of type T to be stored at this TreeNode

  vector< TreeNode<T>* > children ;

  // insertion logic for if an insert is asked of me.
  // may append to children, or may pass off to one of the child nodes
  void insert( T* newData ) ;

} ;

template <typename T>
struct Tree
{
  TreeNode<T>* root;

  // TREE LEVEL functions
  void clear() { delete root ; root=0; }

  void insert( T* data ) { if(root)root->insert(data); } 
} ;

7
Bu, birçoğunda işaretçi olmaya ihtiyaç duymayan, sahip olduğunuz ham işaretçiler sahibi olmaktır.
Mooing Ördek

Bu cevabı geri çekmenizi öneririz. Bir TreeNode sınıfı, bir ağaç uygulamasının parçasıdır.
einpoklum

3

Bence STL ağacı olmamasının birkaç nedeni var. Öncelikle Ağaçlar, bir kap (liste, vektör, set) gibi, doğru seçimleri zorlaştıran çok farklı ince yapıya sahip bir özyinelemeli veri yapısı biçimidir. Ayrıca STL kullanarak temel formda inşa etmek çok kolaydır.

Sonlu bir köklü ağaç, değeri veya yükü olan bir kap, örneğin bir A sınıfı örneği ve muhtemelen boş köklü (alt) ağaçlardan oluşan bir koleksiyon olarak düşünülebilir; ağaçların boş koleksiyonuna sahip ağaçlar yaprak olarak düşünülür.

template<class A>
struct unordered_tree : std::set<unordered_tree>, A
{};

template<class A>
struct b_tree : std::vector<b_tree>, A
{};

template<class A>
struct planar_tree : std::list<planar_tree>, A
{};

Yineleyici tasarım vb. Hakkında biraz düşünmek gerekir ve ağaçlar arasında hangi ürün ve yardımcı ürün işlemlerini tanımlamaya ve verimli olmaya izin verir - ve orijinal STL iyi yazılmalıdır - böylece boş set, vektör veya liste kabı varsayılan durumda herhangi bir yükü gerçekten boş.

Ağaçlar pek çok matematiksel yapıda önemli bir rol oynar (Kasap, Grossman ve Larsen'in klasik belgelerine bakın; ayrıca birleştirilebilecekleri örnekleri ve nasıl numaralandırıldıklarına dair Connes ve Kriemer makaleleri). Rollerinin sadece diğer bazı operasyonları kolaylaştırmak olduğunu düşünmek doğru değildir. Aksine, veri yapısı olarak temel rolleri nedeniyle bu görevleri kolaylaştırırlar.

Ancak, ağaçlara ek olarak "ortak ağaçlar" da vardır; her şeyden önce ağaçların kökleri silerseniz her şeyi sildiğiniz özelliği vardır.

Ağaçtaki yineleyicileri düşünün, muhtemelen basit bir yineleyici yığını, bir düğüme ve ebeveynine, köküne kadar gerçekleştirileceklerdi.

template<class TREE>
struct node_iterator : std::stack<TREE::iterator>{
operator*() {return *back();}
...};

Ancak, istediğiniz kadarına sahip olabilirsiniz; topluca bir "ağaç" oluştururlar, ancak tüm okların köke doğru aktığı yerde, bu ortak ağaç yineleyiciler aracılığıyla önemsiz yineleyici ve köke doğru tekrarlanabilir; ancak, diğer tüm yineleyiciler tarafından bilinemez) ve tüm örneklerin izlenmesi dışında yineleyiciler topluluğu silinemez.

Ağaçlar inanılmaz derecede faydalı, çok fazla yapıya sahipler, bu kesinlikle doğru yaklaşımı elde etmek için ciddi bir zorluk haline getiriyor. Bence bu yüzden STL'de uygulanmadılar. Dahası, geçmişte, insanların dindar olduklarını gördüm ve kendi türünün zorlayıcı örneklerini içeren bir tür kap fikrini buldum - ama yüzleşmek zorundalar - bu bir ağaç türünün temsil ettiği şeydir - bir muhtemelen (küçük) ağaçların boş koleksiyonu. Geçerli dil, varsayılan container<B>kurucunun öbek (veya başka bir yerde) için bir alan Bvb.

Bu, iyi bir biçimde, standarda doğru yolunu bulsaydı, memnun olurum.


0

Buradaki cevapları okuyarak, ortak adlandırılmış nedenler, bir kişinin ağaç üzerinde yinelenememesi veya ağacın diğer STL kaplarıyla benzer arayüzü almaması ve bu ağaç yapısına sahip STL algoritmalarını kullanamamasıdır.

Bunu göz önünde bulundurarak, STL benzeri bir arayüz sağlayacak ve mevcut STL algorthims ile mümkün olduğunca kullanılabilecek kendi ağaç veri yapımı tasarlamaya çalıştım.

Benim fikrim, ağacın mevcut STL kaplarına dayanması ve kapın gizlenmemesi gerektiğiydi, böylece STL algoritmalarıyla kullanılabilecekti.

Ağacın sağlaması gereken diğer önemli özellik, çapraz yineleyicilerdir.

İşte ben geldim ne: https://github.com/igagis/utki/blob/master/src/utki/tree.hpp

İşte testler: https://github.com/igagis/utki/blob/master/tests/tree/tests.cpp


-9

Tüm STL kapları yineleyicilerle kullanılabilir. Bir yineleyiciye ve bir ağaca sahip olamazsınız, çünkü `` doğru '' bir yolunuz yoktur.


3
Ancak BFS veya DFS'nin doğru yol olduğunu söyleyebilirsiniz. Veya her ikisini de destekleyin. Ya da hayal edebileceğiniz herhangi bir şey. Jut kullanıcıya ne olduğunu söyle.
tomas789

2
std :: map'de ağaç yineleyici var.
Jai

1
Bir ağaç, bir "aşırı" değerden diğerine sırayla tüm düğümleri geçen kendi özel yineleyici türünü tanımlayabilir (yani 0 ve 1 yollarına sahip herhangi bir ikili ağaç için "0" lardan "all" e kadar olan bir yineleyici sunabilir 1s "ve tersini yapan bir ters yineleyici, bir 3 derinliği ve başlangıç düğümü ile bir ağaç s, örneğin, ağının üzerine yineleme olarak s000, s00, s001, s0, s010, s01, s011, s, s100, s10, s101, s1, s110, s11, s111(" en sağdaki ')" ile' en soldaki, aynı zamanda (bir derinlik geçişi deseni kullanabilirsiniz s, s0, s1, s00, s01, s10, s11,
Justin Time - Monica

, vb.) veya her bir düğümü, her birinin yalnızca bir kez geçirileceği şekilde yinelediği sürece.
Justin Time - Monica

1
@doc, çok iyi bir nokta. Sanırım std::unordered_setbir dizi "yapıldı" çünkü bazı keyfi yollardan (hash fonksiyonu tarafından dahili olarak verilen) başka öğeler üzerinde tekrarlamanın daha iyi bir yolunu bilmiyoruz. Bence ağacın tam tersi bir durum söz konusudur: yinelemenin unordered_setgereğinden az belirtildiği, teoride belki "rastgele" dışında bir yinelemeyi tanımlamanın "yolu" yoktur. Ağaç söz konusu olduğunda birçok "iyi" (rastgele olmayan) yol vardır. Fakat yine de, amacınız geçerlidir.
alfC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.