Bir ders için zamanda geriye gitme zamanı. Bugün bu şeyleri fantezi yönetilen dillerimizde fazla düşünmese de, bunlar aynı temel üzerine inşa edildi, bu yüzden hafızanın C'de nasıl yönetildiğine bakalım.
Dalış yapmadan önce, " işaretçi " teriminin ne anlama geldiğine dair hızlı bir açıklama . İşaretçi, basitçe bellekteki bir konuma "işaret eden" bir değişkendir. Belleğin bu alanındaki gerçek değeri içermez, belleğin bellek adresini içerir. Bir bellek bloğunu posta kutusu olarak düşünün. İşaretçi, bu posta kutusunun adresi olacaktır.
C de, bir dizi sadece ofseti olan bir göstericidir, ofset bellekte ne kadar görüneceğini belirler. Bu O (1) erişim süresi sağlar.
MyArray [5]
^ ^
Pointer Offset
Diğer tüm veri yapıları bunun üzerine kurulur veya depolama için bitişik belleği kullanmaz, bu da rastgele rasgele erişim arama süresine neden olur (Rağmen sıralı bellek kullanmamanın başka yararları vardır).
Örneğin, içinde 6 rakamlı (6,4,2,3,1,5) bir dizimiz olduğunu varsayalım, bellekte şöyle görünecektir:
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
Bir dizide, her öğenin bellekte yan yana olduğunu biliyoruz. AC dizisi ( MyArrayburada çağrılır ) sadece ilk öğenin bir göstergesidir:
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^
MyArray
Eğer bakmak isteseydik MyArray[4], dahili olarak şu şekilde erişilebilirdi:
0 1 2 3 4
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^
MyArray + 4 ---------------/
(Pointer + Offset)
İşaretçiye ofseti ekleyerek dizideki herhangi bir öğeye doğrudan erişebildiğimiz için, dizinin boyutuna bakılmaksızın herhangi bir öğeyi aynı süre içinde arayabiliriz. Bu, elde etmenin, almakla MyArray[1000]aynı zaman alacağı anlamına gelir MyArray[5].
Alternatif bir veri yapısı bağlantılı bir listedir. Bu, her biri bir sonraki düğüme işaret eden doğrusal bir işaretçiler listesidir
======== ======== ======== ======== ========
| Data | | Data | | Data | | Data | | Data |
| | -> | | -> | | -> | | -> | |
| P1 | | P2 | | P3 | | P4 | | P5 |
======== ======== ======== ======== ========
P(X) stands for Pointer to next node.
Her "düğümü" kendi bloğuna yaptığımı unutmayın. Bunun nedeni, bellekte bitişik olmaları garanti edilmemesidir (ve büyük olasılıkla olmayacaklardır).
P3'e erişmek istiyorsam, doğrudan erişemiyorum, çünkü bellekte nerede olduğunu bilmiyorum. Tüm bildiğim kök (P1) nerede, bu yüzden bunun yerine P1 başlamak ve istenen düğüme her işaretçiyi izlemek zorunda.
Bu bir O (N) arama süresidir (Her öğe eklendikçe arama maliyeti artar). P1000'e ulaşmak P4'e kıyasla çok daha pahalı.
Karma tablolar, yığınlar ve kuyruklar gibi daha üst düzey veri yapılarının tümü dahili olarak bir dizi (veya birden çok dizi) kullanabilirken, Bağlantılı Listeler ve İkili Ağaçlar genellikle düğümleri ve işaretçileri kullanır.
Neden herkesin sadece bir dizi kullanmak yerine bir değeri aramak için doğrusal geçiş gerektiren bir veri yapısı kullanacağını merak edebilirsiniz, ancak kullanımları vardır.
Dizimizi tekrar ele alalım. Bu kez, '5' değerini tutan dizi öğesini bulmak istiyorum.
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^ ^ ^ ^ ^ FOUND!
Bu durumda, işaretçiyi bulmak için hangi ofseti ekleyeceğimi bilmiyorum, bu yüzden 0'dan başlamam ve bulana kadar yukarı doğru çalışmam gerekiyor. Bu 6 kontrol yapmam gerektiği anlamına geliyor.
Bu nedenle, bir dizideki bir değeri aramak O (N) olarak kabul edilir. Dizi büyüdükçe arama maliyeti de artar.
Bazen sıralı olmayan bir veri yapısı kullanmanın avantajları olabileceğini söylediğim yeri hatırlıyor musunuz? Veri aramak bu avantajlardan biridir ve en iyi örneklerden biri de İkili Ağaç'tır.
İkili Ağaç, bağlantılı listeye benzer bir veri yapısıdır, ancak tek bir düğüme bağlanmak yerine her düğüm iki alt düğüme bağlanabilir.
==========
| Root |
==========
/ \
========= =========
| Child | | Child |
========= =========
/ \
========= =========
| Child | | Child |
========= =========
Assume that each connector is really a Pointer
Bir ikili ağaca veri eklendiğinde, yeni düğümün nereye yerleştirileceğine karar vermek için birkaç kural kullanır. Temel kavram şudur: eğer yeni değer ebeveynlerden büyükse, onu sola, daha düşükse sağa sokar.
Bu, bir ikili ağaçtaki değerlerin şöyle görünebileceği anlamına gelir:
==========
| 100 |
==========
/ \
========= =========
| 200 | | 50 |
========= =========
/ \
========= =========
| 75 | | 25 |
========= =========
75 değeri için bir ikili ağaç ararken, bu yapı nedeniyle sadece 3 düğümü (O (log N)) ziyaret etmemiz gerekir:
- 75 100'den az mı? Sağ Düğüme Bakın
- 75 50'den büyük mü? Sol Düğüme Bakın
- 75 var!
Ağacımızda 5 düğüm olmasına rağmen, kalan ikisine bakmamız gerekmiyordu, çünkü onların (ve çocuklarının) aradığımız değeri içeremeyeceğini biliyorduk. Bu bize en kötü durumda her düğümü ziyaret etmemiz gerektiği anlamına gelen bir arama süresi verir, ancak en iyi durumda düğümlerin sadece küçük bir bölümünü ziyaret etmemiz gerekir.
Dizilerin dövüldüğü yer, O (1) erişim süresine rağmen doğrusal bir O (N) arama süresi sağlar.
Bu, bellekteki veri yapılarına inanılmaz derecede yüksek bir genel bakış, birçok ayrıntıyı atlıyor, ancak umarım bir dizinin diğer veri yapılarına kıyasla gücünü ve zayıflığını gösterir.