Bölüm 2: Diziler ve Yapılar

C ile Veri Yapıları ve Algoritmalar
Dizi ADTDizi ve işaretçiDinamik diziÇok boyutlu dizi · adres hesabıÜçgen ve kuşak matrisstruct · unionDizgilerMatris çarpımı
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

Diziyi soyut ve somut görmek

Dizi ADT'sini tanımlayıp C dizisinin bellekteki ardışık yerleşimini açıklamak.

Adres hesaplamak

Satır/sütun öncelikli yerleşimde 2, 3 ve n boyutlu dizilerin öğe adresini bulmak.

Bellek tasarrufu

Üçgen ve kuşak matrisleri tek boyutlu dizide saklayıp O(1) erişim kurmak.

Dinamik bellek

malloc/realloc/free ile büyüyen dizi ve 2B dizi kurmak; amortize maliyeti açıklamak.

Kayıt türleri

struct, union ve dizgileri (char dizisi) doğru ve güvenli kullanmak.

Matris işlemleri

Matris toplama ve çarpımının adım sayısını çıkarıp O(n³) sonucunu göstermek.

Dizi ADT · Horowitz 2.1

Dizi: ⟨indis, değer⟩ çiftlerinden oluşan küme

Soyut bakışta dizi bir eşlemedir (mapping): tanımlı her indise bir değer karşılık gelir. “Ardışık bellek hücreleri” ise bu ADT’nin en yaygın gerçeklemesidir, tanımı değil.

İşlemAnlamı
Create(j, liste)j boyutlu, indis kümesi liste olan boş dizi
Retrieve(A, i)i geçerli indisse A[i] değerini, değilse hata döndür
Store(A, i, x)i geçerliyse A'da i ile x'i eşleştirip yeni diziyi döndür
Neden hızlı?

Başlangıç adresi α ve öğe boyutu s biliniyorsa A[i] adresi α + i·s: tek çarpma + toplama, O(1).

Bedeli

Boyut baştan sabittir; araya ekleme/silme sonraki öğeleri kaydırır: O(n).

Dizi ADT · C gerçeklemesi

C'de dizi bildirimi ve adres

int list[5], *plist[5];
/* list : 5 tamsayı       */
/* plist: 5 tamsayı işaretçisi */

list[i]  /* adresi: α + i * sizeof(int) */
sizeof list   /* 5 * sizeof(int) bayt */

Derleyici list[0]’ın adresini (α) tutar; list[i] için adres çalışma anında hesaplanır. İndis denetlenmez: list[5] yazmak tanımsız davranıştır.

[0]10[1]20[2]30[3]40αα+4α+8α+12

sizeof(int) = 4 varsayımıyla adresler

İndisler 0'dan başlar: n öğeli dizinin geçerli indisleri 0 … n−1.

Dizi ADT · Dizi–işaretçi ilişkisi

Dizi adı ≈ ilk öğenin adresi

İfadeEşdeğeri
list&list[0]
list + i&list[i] (i·sizeof(int) bayt ileri)
*(list + i)list[i]
p = list; p[i]list[i] ile aynı hücre

İşaretçi aritmetiği öğe sayar, bayt değil: p + 1 bir sonraki int’i gösterir.

Fark 1: atanamaz

list = p; derlenmez; dizi adı bir değişken değil, sabit adrestir.

Fark 2: sizeof

Yerel dizide sizeof list tüm diziyi verir; parametrede işaretçi boyutunu.

Parametre

void f(int a[]) ≡ void f(int *a): dizi kopyalanmaz, adresi geçer; boyut ayrı bir parametreyle gönderilir.

Dizi ADT · Kodu adım adım çalıştır

İşaretçi aritmetiği: *(p + i)

[0]10[1]20[2]30[3]40αα+4α+8α+12

dizi yığında 4 ardışık int olarak ayrılır; ilk öğenin adresine α diyelim.

[0]10[1]20[2]30[3]40αα+4α+8α+12p

Çağrıda dizi kopyalanmaz: yalnızca &dizi[0] (α) değeri p'ye geçer.

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 0

0 < 4 → döngü sürer. p + 0 = α + 0·4 = α+0.

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 0

*(p + 0) o adresteki değeri okur: 10 (≡ p[0]).

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 1

1 < 4 → döngü sürer. p + 1 = α + 1·4 = α+4.

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 1

*(p + 1) o adresteki değeri okur: 20 (≡ p[1]).

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 2

2 < 4 → döngü sürer. p + 2 = α + 2·4 = α+8.

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 2

*(p + 2) o adresteki değeri okur: 30 (≡ p[2]).

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 3

3 < 4 → döngü sürer. p + 3 = α + 3·4 = α+12.

[0]10[1]20[2]30[3]40αα+4α+8α+12pp + 3

*(p + 3) o adresteki değeri okur: 40 (≡ p[3]).

[0]10[1]20[2]30[3]40αα+4α+8α+12p

4 < 4 yanlış → döngü biter; yazdir döner.

[0]10[1]20[2]30[3]40αα+4α+8α+12

Her satırda adres 4 bayt artar. (Gerçek %p çıktısı onaltılık bir adrestir; burada α ile gösterildi.)

void yazdir(int *p, int n) {
  int i;
  for (i = 0; i < n; i++)
    printf("%p %d\n", (void *)(p + i), *(p + i));
}

int main(void) {
  int dizi[] = {10, 20, 30, 40};
  yazdir(dizi, 4);
  return 0;
}

Dinamik Diziler · malloc / free

Boyutu çalışma anında belli olan dizi

int n, i, *a;
scanf("%d", &n);
a = malloc(n * sizeof *a);   /* n int */
if (a == NULL) {
  fprintf(stderr, "Yetersiz bellek\n");
  exit(EXIT_FAILURE);
}
for (i = 0; i < n; i++)
  a[i] = i * i;              /* dizi gibi */
free(a);                     /* geri ver */
a = NULL;          /* sarkan işaretçi olmasın */
Yığın (stack) ve heap bellek

Yerel dizi fonksiyon bitince yok olur; malloc bloğu free edilene dek yaşar.

sizeof *a kalıbı

Tür değişse de doğru boyutu verir; sizeof(int*) ile karıştırma hatasını önler.

calloc(n, s)

Bloğu sıfırlayarak ayırır; realloc bloğu büyütür/küçültür.

Dinamik Diziler · Kodu adım adım çalıştır

Büyüyen dizi: kapasiteyi ikiye katla

veriNULLboyut = 0 kap = 0

Boş dinamik dizi: {NULL, 0, 0}. Sırayla 7, 3, 9, 4, 6 eklenecek.

veriNULLboyut = 0 kap = 0

0 == 0 → dolu, büyütülmeli.

veri0boyut = 0 kap = 1+0 kopya

yeni = 1. realloc 1 hücrelik blok verir; eski 0 öğe kopyalanır (NULL'dan realloc = malloc). Önce t'ye alınır: başarısızsa eski blok kaybolmaz.

veri0boyut = 0 kap = 1

t != NULL: d->veri = t, kap = 1.

veri07boyut = 1 kap = 1

veri[0] = 7, sonra boyut++ → 1. (Son ek: önce kullanılır, sonra artırılır.)

veri07boyut = 1 kap = 1

1 == 1 → dolu, büyütülmeli.

veri071boyut = 1 kap = 2+1 kopya

yeni = 2. realloc 2 hücrelik blok verir; eski 1 öğe kopyalanır (toplam kopya 1). Önce t'ye alınır: başarısızsa eski blok kaybolmaz.

veri071boyut = 1 kap = 2

t != NULL: d->veri = t, kap = 2.

veri0713boyut = 2 kap = 2

veri[1] = 3, sonra boyut++ → 2. (Son ek: önce kullanılır, sonra artırılır.)

veri0713boyut = 2 kap = 2

2 == 2 → dolu, büyütülmeli.

veri071323boyut = 2 kap = 4+2 kopya

yeni = 4. realloc 4 hücrelik blok verir; eski 2 öğe kopyalanır (toplam kopya 3). Önce t'ye alınır: başarısızsa eski blok kaybolmaz.

veri071323boyut = 2 kap = 4

t != NULL: d->veri = t, kap = 4.

veri0713293boyut = 3 kap = 4

veri[2] = 9, sonra boyut++ → 3. (Son ek: önce kullanılır, sonra artırılır.)

veri0713293boyut = 3 kap = 4

3 == 4 → yer var, doğrudan yazılır.

veri07132934boyut = 4 kap = 4

veri[3] = 4, sonra boyut++ → 4. (Son ek: önce kullanılır, sonra artırılır.)

veri07132934boyut = 4 kap = 4

4 == 4 → dolu, büyütülmeli.

veri071329344567boyut = 4 kap = 8+4 kopya

yeni = 8. realloc 8 hücrelik blok verir; eski 4 öğe kopyalanır (toplam kopya 7). Önce t'ye alınır: başarısızsa eski blok kaybolmaz.

veri071329344567boyut = 4 kap = 8

t != NULL: d->veri = t, kap = 8.

veri0713293446567boyut = 5 kap = 8

veri[4] = 6, sonra boyut++ → 5. 5 eklemede toplam yalnız 7 kopya yapıldı.

typedef struct { int *veri; int boyut, kap; } DinDizi;

int ekle(DinDizi *d, int x) {
  if (d->boyut == d->kap) {              /* dolu */
    int yeni = d->kap ? 2 * d->kap : 1;
    int *t = realloc(d->veri, yeni * sizeof *t);
    if (t == NULL) return 0;    /* eski blok sağlam */
    d->veri = t;
    d->kap = yeni;
  }
  d->veri[d->boyut++] = x;
  return 1;
}

Dinamik Diziler · Analiz

İkiye katlama: ekleme başına amortize O(1)

n eklemekatlama: kopyakopya / n+1 büyütme: kopyakopya / n
870.88283.5
64630.98201631.5
102410231.00523776511.5

Katlamada kopyalar 1 + 2 + 4 + … + 2k < 2n → toplam O(n) (n yazmayla birlikte toplam maliyet < 3n), ekleme başına O(1) (amortize). Her seferinde 1 (ya da sabit c) büyütmek toplam n(n−1)/2 = O(n²) kopya demektir.

Tek ekleme yine O(n) olabilir

Büyütme anındaki ekleme pahalıdır; amortize analiz uzun bir dizi işlemin ortalamasını garanti eder.

Bellek payı

Kapasite en çok 2·boyut olur; en fazla %50 boş hücre bedelidir.

realloc tuzağı

a = realloc(a, …) başarısız olursa NULL döner ve eski blok kaybolur (sızıntı). Önce geçici değişkene alınmalı.

Dinamik Diziler · Kodu adım adım çalıştır

2B dinamik dizi: işaretçi dizisi + satırlar

xx[0]?x[1]?x[2]?satır = 3sütun = 4

Önce 3 hücrelik işaretçi dizisi ayrılır (sizeof *x = sizeof(int *)); içerikler henüz belirsiz.

xx[0]?x[1]?x[2]?satır = 3sütun = 4

x != NULL: ayırma başarılı, devam.

xx[0]?x[1]?x[2]?satır = 3sütun = 4

i = 0 < 3 → 0. satır ayrılacak.

xx[0]0?1?2?3?x[1]?x[2]?satır = 3sütun = 4

x[0] 4 int'lik yeni bloğu gösterir (sizeof **x = sizeof(int)). NULL değil.

xx[0]0?1?2?3?x[1]?x[2]?satır = 3sütun = 4

i = 1 < 3 → 1. satır ayrılacak.

xx[0]0?1?2?3?x[1]????x[2]?satır = 3sütun = 4

x[1] 4 int'lik yeni bloğu gösterir (sizeof **x = sizeof(int)). NULL değil.

xx[0]0?1?2?3?x[1]????x[2]?satır = 3sütun = 4

i = 2 < 3 → 2. satır ayrılacak.

xx[0]0?1?2?3?x[1]????x[2]????satır = 3sütun = 4

x[2] 4 int'lik yeni bloğu gösterir (sizeof **x = sizeof(int)). NULL değil.

xx[0]0?1?2?3?x[1]????x[2]????satır = 3sütun = 4

3 < 3 yanlış → döngü biter.

xx[0]0?1?2?3?x[1]????x[2]????satır = 3sütun = 4

x döner; x[i][j] ≡ *(*(x + i) + j). Satırlar ardışık olmak zorunda değil. Bırakırken önce her x[i], sonra x free edilir.

int **dizi2b(int satir, int sutun) {
  int i, **x = malloc(satir * sizeof *x);
  if (x == NULL) return NULL;
  for (i = 0; i < satir; i++) {
    x[i] = malloc(sutun * sizeof **x);
    if (x[i] == NULL) {                /* geri al */
      while (--i >= 0) free(x[i]);
      free(x); return NULL;
    }
  }
  return x;
}

Çok Boyutlu Diziler

Bellek tek boyutlu, dizi çok boyutlu

A[upper0][upper1]…[uppern−1] olarak tanımlı n boyutlu dizide ∏ upperi öğe vardır. Ör. A[10][10][10] → 1000 öğe.

Bellek ise tek boyutlu bir bayt dizisidir; derleyici öğeleri bir sıraya dizmek zorundadır:

  • Satır öncelikli (row major): önce 0. satırın tümü, sonra 1. satır… (C, C++, Python/NumPy varsayılanı)
  • Sütun öncelikli (column major): önce 0. sütun… (Fortran, MATLAB)
Soru hep aynı: A[i][j] öğesinden önce kaç öğe var? Adres = α + (öncekilerin sayısı) · öğe boyutu.
C'de

int x[3][4]; aslında 4'lük dizilerden oluşan 3 öğeli bir dizidir; x[1] 1. satırın başını gösterir.

Çok Boyutlu Diziler

int x[3][4] belleğe iki biçimde dizilebilir

00x0001x0112x0223x0331x104x115x126x1372x208x219x2210x23110x001x012x023x034x105x116x127x138x209x2110x2211x23αα + 11·ssatır öncelikli: satır satır

Satır öncelikli: x[i][j]'den önce i tam satır (i·upper1 öğe) ve j öğe vardır → &x[i][j] = α + (i·upper1 + j)·s. C bunu kullanır.

00x0001x0132x0263x0391x101x114x127x13102x202x215x228x23110x001x102x203x014x115x216x027x128x229x0310x1311x23αα + 11·ssütun öncelikli: sütun sütun

Sütun öncelikli: önce j tam sütun (j·upper0 öğe), sonra i öğe → &x[i][j] = α + (j·upper0 + i)·s. Aynı öğe farklı konumda: x12 → satırda 6, sütunda 7.

α + i·upper1 + j formülü öğe (hücre) birimindedir; bayt adresi için s = sizeof(öğe) ile çarpılır.

Çok Boyutlu Diziler · Kodu adım adım çalıştır

Satır öncelikli adres hesabı

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres

x[1][2]'nin adresi istenir: α = 100, satır uzunluğu upper1 = 4, s = 4 bayt.

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 6

Önce 1 tam satır (1·4 = 4 öğe) ve satırında 2 öğe var → k = 6 (mavi hücreler).

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 6

100 + 6·4 = 124 döner.

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 6

printf 124 yazar. Sütun öncelikli olsaydı k = j·3 + i = 7 → 128 olurdu.

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres

x[2][3]'nin adresi istenir: α = 100, satır uzunluğu upper1 = 4, s = 4 bayt.

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 11

Önce 2 tam satır (2·4 = 8 öğe) ve satırında 3 öğe var → k = 11 (mavi hücreler).

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 11

100 + 11·4 = 144 döner.

000010120230311011121322021222300010120230341051161271382092110221123100104108112116120124128132136140144adres▲ k = 11

printf 144 yazar. Son öğe: 100 + 11·4 = 144.

/* int x[3][4]: satır öncelikli bayt adresi */
long adres(long alfa, int i, int j,
           int ust1, int s) {
  long k = (long)i * ust1 + j;   /* öncekiler */
  return alfa + k * s;
}

int main(void) {
  printf("%ld\n", adres(100, 1, 2, 4, 4));
  printf("%ld\n", adres(100, 2, 3, 4, 4));
  return 0;
}

Çok Boyutlu Diziler

G[2][2][3]: dilim · satır · sütun

dilim 0[0][0][0]+0[0][0][1]+1[0][0][2]+2[0][1][0]+3[0][1][1]+4[0][1][2]+5dilim 1[1][0][0]+6[1][0][1]+7[1][0][2]+8[1][1][0]+9[1][1][1]+10[1][1][2]+11
öğeadres (öğe birimi)
[0][0][0]α
[i][0][0]α + i·u1·u2
[i][j][0]α + i·u1·u2 + j·u2
[i][j][k]α + i·u1·u2 + j·u2 + k

Son ifadede 3 çarpma + 3 toplama var. Çarpma toplamadan pahalı olduğundan derleyici paranteze alır:

α + (i·u1 + j)·u2 + k

→ 2 çarpma + 3 toplama. Örnek: [1][0][2] → (1·2 + 0)·3 + 2 = 8.

Çok Boyutlu Diziler

n boyutlu dizide adres formülü

A[u0][u1]…[un−1] için:

öğeadres
A[0][0]…[0]α
A[i0][0]…[0]α + i0·u1u2…un−1
A[i0][i1]…[0]… + i1·u2…un−1
A[i0]…[in−1]… + in−1

&A[i0]…[in−1] = α + Σj=0n−1 ij·aj

aj = ∏k=j+1n−1 uk (0 ≤ j < n−1), an−1 = 1

aj katsayıları yalnız boyutlara bağlıdır: derleyici onları bir kez hesaplar. Horner biçimiyle n−1 çarpma yeter: t = i0; t = t·uj + ij.

Çok Boyutlu Diziler · Kodu adım adım çalıştır

n boyutlu bağıl konum: Horner yöntemi

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

G[1][0][2] aranıyor. t = ind[0] = 1: 1 numaralı dilim.

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

j = 1 < 3 → bir boyut daha içeri.

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

t = 1·2 + 0 = 2: bu, dilimler boyunca sayılan “genel satır” numarası (dilim 1, satır 0).

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

j = 2 < 3 → bir boyut daha içeri.

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

t = 2·3 + 2 = 8: 8 öğe önde → hücre 8.

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

3 < 3 yanlış → döngü biter.

dilim 0000001002010011012dilim 110010110211011111200001001200230104011501261007101810291101011111112

8 döner. Toplam 2 çarpma (n−1). Kontrol: 1·2·3 + 0·3 + 2 = 8. ust[0] hiç kullanılmadı!

/* ind: indisler, ust: boyutlar, n: boyut sayısı */
int ofset(const int ind[], const int ust[], int n) {
  int j, t = ind[0];
  for (j = 1; j < n; j++)
    t = t * ust[j] + ind[j];            /* Horner */
  return t;               /* adres = alfa + t*s */
}

Üçgen ve Kuşak Matrisler

Matrisin yarısını tek boyutlu dizide tutmak

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

Kare matris köşegenle ikiye bölünür; bazen bir yarısı yeter (şehirler arası uzaklıklar, periyodik tablodaki atom ağırlıkları…). Veri tek boyutlu ALT dizisinde tutulur; kullanıcıya iki boyutlu bir matrisle çalışıyormuş arayüzü sunulur.

i. satırda i+1 öğe var → toplam n(n+1)/2. aij, önceki satırların öğeleri + satırdaki sırası:

k = i(i+1)/2 + j  (j ≤ i)

Üçgen ve Kuşak Matrisler · Kodu adım adım çalıştır

Alt üçgeni okumak: satır başı k

001020301002030123456789ALT dizisi (n(n+1)/2 = 10 öğe)

4·5/2 = 10 ≤ 100: ALT dizisine sığar. Girdi: 3 1 4 1 5 9 2 6 5 3.

001020301002030123456789ALT dizisi (n(n+1)/2 = 10 öğe)

k = 1·0/2 = 0: 0. satırdan önce 0 öğe yerleşti.

00310203010020303123456789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[0+0] = alt[0] ← 3 (a00).

00310203010020303123456789ALT dizisi (n(n+1)/2 = 10 öğe)

k = 2·1/2 = 1: 1. satırdan önce 1 öğe yerleşti.

0031020301100203031123456789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[1+0] = alt[1] ← 1 (a10).

003102030114002030311243456789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[1+1] = alt[2] ← 4 (a11).

003102030114002030311243456789ALT dizisi (n(n+1)/2 = 10 öğe)

k = 3·2/2 = 3: 2. satırdan önce 3 öğe yerleşti.

00310203011400210303112431456789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[3+0] = alt[3] ← 1 (a20).

0031020301140021503031124314556789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[3+1] = alt[4] ← 5 (a21).

003102030114002159030311243145596789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[3+2] = alt[5] ← 9 (a22).

003102030114002159030311243145596789ALT dizisi (n(n+1)/2 = 10 öğe)

k = 4·3/2 = 6: 3. satırdan önce 6 öğe yerleşti.

00310203011400215903203112431455962789ALT dizisi (n(n+1)/2 = 10 öğe)

alt[6+0] = alt[6] ← 2 (a30).

0031020301140021590326031124314559627689ALT dizisi (n(n+1)/2 = 10 öğe)

alt[6+1] = alt[7] ← 6 (a31).

003102030114002159032650311243145596276859ALT dizisi (n(n+1)/2 = 10 öğe)

alt[6+2] = alt[8] ← 5 (a32).

00310203011400215903265303112431455962768593ALT dizisi (n(n+1)/2 = 10 öğe)

alt[6+3] = alt[9] ← 3 (a33).

00310203011400215903265303112431455962768593ALT dizisi (n(n+1)/2 = 10 öğe)

10 değer okundu; üst üçgendeki 6 sıfır hiç saklanmadı.

#define MAX_SIZE 100
void AltUcgenOku(int alt[], int n) {
  int i, j, k;
  if (n * (n + 1) / 2 > MAX_SIZE) {
    printf("Alt dizi boyutu yetersiz!\n"); exit(1);
  }
  for (i = 0; i < n; i++) {
    k = (i + 1) * i / 2;      /* öncekiler */
    for (j = 0; j <= i; j++)  /* j <= i */
      scanf("%d", &alt[k + j]);
  }
}

Üçgen ve Kuşak Matrisler · Kodu adım adım çalıştır

Erişim: indis mi, hata kodu mu?

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

a21 isteniyor.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

Sınır denetimi: indisler 0…3 aralığında.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

2 < 1? Hayır → alt üçgende.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

k = 3·2/2 + 1 = 4 → ALT[4] = a21.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

printf 4 yazar.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

a13 isteniyor.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

Sınır denetimi: indisler 0…3 aralığında.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

1 < 3? Evet → üst üçgen (sıfırlı bölge): −1.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

printf -1 yazar. Hata kodları {−1, −2} ile geçerli dönüşler {0, 1, 2, …} kesişmez: çağıran durumu ayırt edebilir.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

a40 isteniyor.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

Sınır denetimi: i = 4 ≥ n → geçersiz!

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

Hata iletisi basılır ve −2 döner.

00a001020301a10a11002a20a21a2203a30a31a32a330a001a102a113a204a215a226a307a318a329a33ALT dizisi (n(n+1)/2 = 10 öğe)

printf -2 yazar.

int AltUcgenEris(int i, int j, int n) {
  if (i < 0 || i >= n || j < 0 || j >= n) {
    printf("Gecersiz indis\n");
    return -2;
  } else if (i < j) return -1;    /* üst üçgen: 0 */
  else return (i + 1) * i / 2 + j;   /* geçerli */
}
/* main: */
printf("%d\n", AltUcgenEris(2, 1, 4));
printf("%d\n", AltUcgenEris(1, 3, 4));
printf("%d\n", AltUcgenEris(4, 0, 4));

Üçgen ve Kuşak Matrisler · ÇALIŞMA

ÇALIŞMA çözümü: dört üçgen türü ve simetrik matris

saklanan bölgesatır i'deki öğek (satır öncelikli)
j ≤ i (alt sol)i + 1i(i+1)/2 + j
j ≥ i (üst sağ)n − ii·n − i(i−1)/2 + (j − i)
i + j ≤ n−1 (sol üst)n − ii·n − i(i−1)/2 + j
i + j ≥ n−1 (sağ alt)i + 1i(i+1)/2 + j − (n−1−i)
Gerçek simetrik matris (aij = aji): üst üçgene erişimde −1 döndürmek yerine indisler yer değiştirilir: i < j ise k = j(j+1)/2 + i. Formüller n = 1…8 için betikle doğrulandı.

Üçgen ve Kuşak Matrisler

Kuşak (band) matris: yalnız köşegen şeridi saklanır

00d001d012c3c1d10d11d12c2d20d21d22d233cd31d32d330d201d312d103d214d325d006d117d228d339d0110d1211d23kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

Kuşak dışındaki öğeler kullanılmaz ya da hepsi aynı sabit c değerindedir. n: kenar (4), a: alt kuşak sayısı köşegen dâhil (3), b: üst kuşak sayısı köşegen dâhil (2).

öğe sayısı = n(a+b−1) − a(a−1)/2 − b(b−1)/2 = 12

Hocadan “Saklama sorun değil, ama hızlı erişilebiliyor mu?”

Üçgen ve Kuşak Matrisler

Kuşak numarası j − i ve ARA dizisi

  • Aynı kuşaktaki öğelerde j − i sabittir: d20 ve d31 → 0−2 = 1−3 = −2. Ana köşegen 0; negatifler alt, pozitifler üst kuşak.
  • Geçerli kuşaklar [1 − a, b − 1] kapalı aralığında; dışı sabit bölge.
  • ARA her kuşağın ilk öğesinin kusak[] içindeki konumunu tutar: a + b − 1 öğe. C'de negatif indis olmadığından kuşak no + a − 1 ile kaydırılır.
  • Kuşak içindeki sıra: alt kuşakta j, üst kuşakta i (kuşağın ilk öğesi 0. satır/0. sütundadır).
Hocadan “Her şey kısıtlı gibi tahlil ederek mekanizma belirliyorum.”

Üçgen ve Kuşak Matrisler · Kodu adım adım çalıştır

Kuşak matrisi kurmak (alt kuşaklar)

0012c3c1c23c01234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-2-101ara[](indis = kuşak no + a − 1)

4·4 − 3 − 1 = 12 ≤ MAX_SIZE. Girdi kuşak kuşak gelir: 6 10 · 3 7 11 · 1 4 8 12 · 2 5 9.

0012c3c1c23c01234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-2-101ara[](indis = kuşak no + a − 1)

Kuşak i = -2 (alt): 2 öğe.

0012c3c1c23c01234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-101ara[](indis = kuşak no + a − 1)

ara[-2+2] = ara[0] = 0: kuşak kusak[0]'den başlar.

0012c3c1c263c061234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-101ara[](indis = kuşak no + a − 1)

kusak[0+0] ← 6 (d20).

0012c3c1c263c1006110234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-101ara[](indis = kuşak no + a − 1)

kusak[0+1] ← 10 (d31).

0012c3c1c263c1006110234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-101ara[](indis = kuşak no + a − 1)

toplamoge += 2 → 2: sonraki kuşağın başı.

0012c3c1c263c1006110234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-101ara[](indis = kuşak no + a − 1)

Kuşak i = -1 (alt): 3 öğe.

0012c3c1c263c1006110234567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

ara[-1+2] = ara[1] = 2: kuşak kusak[2]'den başlar.

0012c3c13c263c10061102334567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

kusak[2+0] ← 3 (d10).

0012c3c13c2673c100611023374567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

kusak[2+1] ← 7 (d21).

0012c3c13c2673c1011061102337411567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

kusak[2+2] ← 11 (d32).

0012c3c13c2673c1011061102337411567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

toplamoge += 3 → 5: sonraki kuşağın başı.

void KusakMatrisKur(int kusak[], int ara[],
                    int n, int a, int b) {
  int i, k, toplamoge = 0;   /* önceki kuşaklar */
  if (n*(a+b-1) - a*(a-1)/2 - b*(b-1)/2 > MAX_SIZE) {
    printf("Bellek yetersiz!\n"); exit(-1);
  }
  for (i = 1 - a; i <= b - 1; i++) {  /* kuşak kuşak */
    ara[i + a - 1] = toplamoge;     /* kuşak başı */
    for (k = 0; k <= n - abs(i) - 1; k++)
      scanf("%d", &kusak[ara[i + a - 1] + k]);
    toplamoge += n - abs(i);
  }
}

Üçgen ve Kuşak Matrisler · Kodu adım adım çalıştır

Kuşak matrisi kurmak (devam: köşegen ve üst)

0012c3c13c2673c1011061102337411567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-1201ara[](indis = kuşak no + a − 1)

Kuşak i = 0 (ana köşegen): 4 öğe.

0012c3c13c2673c1011061102337411567891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

ara[0+2] = ara[2] = 5: kuşak kusak[5]'den başlar.

00112c3c13c2673c10110611023374115167891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

kusak[5+0] ← 1 (d00).

00112c3c134c2673c101106110233741151647891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

kusak[5+1] ← 4 (d11).

00112c3c134c26783c1011061102337411516478891011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

kusak[5+2] ← 8 (d22).

00112c3c134c26783c10111206110233741151647881291011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

kusak[5+3] ← 12 (d33).

00112c3c134c26783c10111206110233741151647881291011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

toplamoge += 4 → 9: sonraki kuşağın başı.

00112c3c134c26783c10111206110233741151647881291011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-12051ara[](indis = kuşak no + a − 1)

Kuşak i = 1 (üst): 3 öğe.

00112c3c134c26783c10111206110233741151647881291011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

ara[1+2] = ara[3] = 9: kuşak kusak[9]'den başlar.

001122c3c134c26783c101112061102337411516478812921011kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

kusak[9+0] ← 2 (d01).

001122c3c1345c26783c1011120611023374115164788129210511kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

kusak[9+1] ← 5 (d12).

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

kusak[9+2] ← 9 (d23).

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

toplamoge += 3 → 12: sonraki kuşağın başı.

void KusakMatrisKur(int kusak[], int ara[],
                    int n, int a, int b) {
  int i, k, toplamoge = 0;   /* önceki kuşaklar */
  if (n*(a+b-1) - a*(a-1)/2 - b*(b-1)/2 > MAX_SIZE) {
    printf("Bellek yetersiz!\n"); exit(-1);
  }
  for (i = 1 - a; i <= b - 1; i++) {  /* kuşak kuşak */
    ara[i + a - 1] = toplamoge;     /* kuşak başı */
    for (k = 0; k <= n - abs(i) - 1; k++)
      scanf("%d", &kusak[ara[i + a - 1] + k]);
    toplamoge += n - abs(i);
  }
}

Üçgen ve Kuşak Matrisler · Kodu adım adım çalıştır

Kuşak matriste O(1) erişim

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

d31 isteniyor; indisler geçerli. Kuşak no j − i = -2.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

i − j = 2 < a = 3? Evet → alt kuşak.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

ara[-2+2] + j = 0 + 1 = 1 → kusak[1] = 10.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

d12 isteniyor; indisler geçerli. Kuşak no j − i = 1.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

1 ≥ 1 → üst üçgen.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

1 < b = 2 → geçerli üst kuşak: ara[1+2] + i = 9 + 1 = 10.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

d03 isteniyor; indisler geçerli. Kuşak no j − i = 3.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

3 ≥ 1 → üst üçgen.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

3 ≥ b = 2 → kuşak dışı, üst sabit bölge: −1 (değer c).

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

d30 isteniyor; indisler geçerli. Kuşak no j − i = -3.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

i − j = 3 < a = 3? Hayır → kuşak dışı.

001122c3c1345c267893c10111206110233741151647881292105119kusak[]kuşak -2kuşak -1kuşak 0kuşak 1-20-120519ara[](indis = kuşak no + a − 1)

Alt sabit bölge: −2. Her durumda sabit sayıda işlem → O(1).

int KusakMatrisEris(int i, int j, int n, int a,
                    int b, int ara[]) {
  if (i < 0 || j < 0 || i >= n || j >= n) {
    printf("Indis tasmasi\n"); return -3;
  } else if (j - i >= 1) {         /* üst üçgen */
    if (j - i < b)               /* geçerli kuşak */
      return ara[j - i + a - 1] + i;
    else return -1;              /* üst sabit bölge */
  } else if (i - j < a)          /* köşegen / alt */
    return ara[j - i + a - 1] + j;
  else
    return -2;                   /* alt sabit bölge */
}

Üçgen ve Kuşak Matrisler

ARA dizisi mi, formül mü? (ÖDEV 1)

KusakMatrisEris girdiye bağlı hiçbir döngü içermez → O(1). Kuşak başlarını döngüyle saymak karmaşıklığı artırırdı; ARA bunu önler ama bellek harcar ve programı “dinamik olmaktan uzaklaştırır”.

Hocadan “Eğer performansı artıracaksa makul ölçüde olmak kaydıyla fazladan bellek kullanmakta sakınca yok.”

ÖDEV 1: ARA dizisinden yardım almadan kuşak başını hesaplayın. (Örnek çözüm yanda.)

/* t: kuşak no (1-a..b-1); ara[t+a-1] ile aynı */
int kusak_basi(int t, int n, int a) {
  if (t <= 0)                     /* alt kuşaklar */
    return (t + a - 1) * n
           - (a * (a - 1) + t * (1 - t)) / 2;
  return (a - 1) * n - a * (a - 1) / 2  /* t=0 başı */
         + t * n - t * (t - 1) / 2;
}

n = 4, a = 3: t = −2, −1, 0, 1 → 0, 2, 5, 9 (ARA ile aynı). Betikle n ≤ 11 için tüm a, b denendi. Erişim: kusak_basi(j−i, n, a) + (j > i ? i : j).

Üçgen ve Kuşak Matrisler

if (ifade1 && ifade2) güvenli mi?

Sık duyulan bir kanı: “Derleyicinin sağdan sola mı, soldan sağa mı taradığını bilemeyiz; bu yüzden şu iki eşdeğer ifadeden ikincisi daha güvenlidir.”

/* 1 */ if (i < n && a[i] != 0) ...

/* 2 */ if (i < n)
          if (a[i] != 0) ...
Doğrusu: C standardı && ve || için soldan sağa değerlendirmeyi ve kısa devreyi garanti eder (C99 §6.5.13–14): sol taraf yanlışsa sağ taraf hiç hesaplanmaz. Yani iki biçim aynı derecede güvenlidir; 1. biçim yaygın ve doğru bir kalıptır.
Asıl tuzak

& (bit düzeyi VE) kısa devre yapmaz; f(x) + g(x) gibi işlenenlerin sırası ise belirtilmemiştir.

Yapılar · Horowitz 2.2

struct: farklı türde alanları tek kayıtta toplamak

typedef struct {
  char ad[10];
  int yas;
  float maas;
} insan;

insan k1, k2;
strcpy(k1.ad, "Ali");
k1.yas = 21;
k1.maas = 3500.0f;
k2 = k1;          /* alan alan kopyalanır */
/* if (k1 == k2) -> DERLENMEZ */
Nokta ve ok

k1.yas değişkenin alanı; p->yas ≡ (*p).yas (p: insan *).

Atama var, == yok

Yapılar bütün olarak atanabilir ama karşılaştırılamaz; alan alan karşılaştıran bir fonksiyon yazılır.

typedef

typedef struct {…} insan; ile her seferinde struct yazmaktan kurtuluruz.

Yapılar · Horowitz 2.2

Yapı içinde yapı · kendine başvuran yapı

typedef struct {
  int gun, ay, yil;
} tarih;

typedef struct {
  char ad[10];
  tarih dogum;          /* iç içe yapı */
} kisi;
/* k.dogum.yil = 2004; */

typedef struct liste {
  char veri;
  struct liste *bag;    /* kendine başvuru */
} liste;
abcNULLveribag

Kendine başvuran yapılar bağlı listelerin temelidir.

Bir yapı kendi türünden alan içeremez (sonsuz boyut), ama kendi türüne işaretçi içerebilir.

Yapılar · Horowitz 2.2

union: alanlar aynı belleği paylaşır

typedef struct {
  enum { KADIN, ERKEK } tur;   /* etiket */
  union {
    int cocuk_sayisi;          /* KADIN ise */
    int sakal;                 /* ERKEK ise */
  } u;
} cinsiyet;

cinsiyet c;
c.tur = ERKEK;
c.u.sakal = 0;   /* yalnız etiketle uyumlu alanı oku */

union boyutu en büyük alan kadardır (+ hizalama). Hangi alanın geçerli olduğunu etiket alanıyla izlemek programcının işidir.

{char c; int x; char d;} → 12cxxxxd{int x; char c; char d;} → 8xxxxcdboş kutu = dolgu (padding) baytı

Alan sırası bellek düzenini değiştirir (derleyiciye bağlı; tipik 32/64 bit değerler)

Yapılar · Kodu adım adım çalıştır

Yapı dizisi üzerinde gezinme

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

Yapı dizisi bellekte kayıt kayıt ardışık durur; her kayıt sizeof(Ogrenci) bayt.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

0 < 3 → 0. kayıt.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

s[0].ort = 3.2 eklenir → top = 3.2.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

1 < 3 → 1. kayıt.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

s[1].ort = 2.6 eklenir → top = 5.8.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

2 < 3 → 2. kayıt.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

s[2].ort = 3.5 eklenir → top = 9.3.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

3 < 3 yanlış → döngü biter.

s[0]101"Ada"3.2noadorts[1]102"Can"2.6noadorts[2]103"Ece"3.5noadort

n > 0 → 9.3 / 3 = 3.10 döner. n = 0 olsaydı sıfıra bölme yerine 0.0.

typedef struct {
  int no;
  char ad[16];
  double ort;
} Ogrenci;

double sinif_ort(const Ogrenci s[], int n) {
  double top = 0.0;
  int i;
  for (i = 0; i < n; i++)
    top += s[i].ort;               /* . ile alan */
  return n > 0 ? top / n : 0.0;    /* boş sınıf */
}

const Ogrenci s[]: dizi kopyalanmaz; const fonksiyonun kayıtları değiştirmeyeceğini belgeler.

Dizgiler · Horowitz 2.6 · <string.h>

C'de dizgi: '\0' ile biten char dizisi

char s[] = "dog";      /* 4 bayt: d o g \0 */
char t[10];
char *p = "dog";       /* salt okunur sabit */

strlen(s)   /* 3 : \0'a kadar */
sizeof s    /* 4 : dizinin boyutu */

strcpy(t, s);                 /* t yeterince büyük mü? */
snprintf(t, sizeof t, "%s!", s);   /* güvenli */
if (strcmp(s, "dog") == 0) ...     /* == değil! */
0'd'1'o'2'g'3'\0's
fonksiyoniş
strlenuzunluk (\0 hariç)
strcpy / strncpykopyala
strcatsona ekle
strcmpsözlük sırası karşılaştır
strchr / strstrkarakter / alt dizgi ara

Dizgiler · Kodu adım adım çalıştır

strlen nasıl çalışır?

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 0

p, s ile aynı yeri (ilk karakteri) gösterir.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 0

*p = 'v' ≠ '\0' → devam.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 1

p++: bir karakter (1 bayt) ileri.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 1

*p = 'e' ≠ '\0' → devam.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 2

p++: bir karakter (1 bayt) ileri.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 2

*p = 'r' ≠ '\0' → devam.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 3

p++: bir karakter (1 bayt) ileri.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 3

*p = 'i' ≠ '\0' → devam.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 4

p++: bir karakter (1 bayt) ileri.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 4

*p = '\0' = '\0' → döngü biter.

0'v'1'e'2'r'3'i'4'\0's▲ pp − s = 4

p − s = 4 döner. Uzunluk için dizgi baştan sona taranır: O(n) — döngü koşulunda strlen çağırmak O(n²) yapar!

size_t uzunluk(const char *s) {
  const char *p = s;
  while (*p != '\0')     /* sonlandırıcıya dek */
    p++;
  return (size_t)(p - s);  /* işaretçi farkı */
}

/* uzunluk("veri") */

Matris İşlemleri · Kodu adım adım çalıştır

Matris çarpımı: üç iç içe döngü

a1234×b5678=c0

c[0][0] için a'nın 0. satırı ile b'nin 0. sütunu kullanılacak; önce 0.

a1234×b5678=c5

k = 0: += a[0][0]·b[0][0] = 1·5 → 5.

a1234×b5678=c19

k = 1: += a[0][1]·b[1][0] = 2·7 → 19.

a1234×b5678=c190

c[0][1] için a'nın 0. satırı ile b'nin 1. sütunu kullanılacak; önce 0.

a1234×b5678=c196

k = 0: += a[0][0]·b[0][1] = 1·6 → 6.

a1234×b5678=c1922

k = 1: += a[0][1]·b[1][1] = 2·8 → 22.

a1234×b5678=c19220

c[1][0] için a'nın 1. satırı ile b'nin 0. sütunu kullanılacak; önce 0.

a1234×b5678=c192215

k = 0: += a[1][0]·b[0][0] = 3·5 → 15.

a1234×b5678=c192243

k = 1: += a[1][1]·b[1][0] = 4·7 → 43.

a1234×b5678=c1922430

c[1][1] için a'nın 1. satırı ile b'nin 1. sütunu kullanılacak; önce 0.

a1234×b5678=c19224318

k = 0: += a[1][0]·b[0][1] = 3·6 → 18.

a1234×b5678=c19224350

k = 1: += a[1][1]·b[1][1] = 4·8 → 50.

a1234×b5678=c19224350

Bitti: [[19, 22], [43, 50]]. En içteki satır N³ = 8 kez çalıştı → O(n³).

#define N 2
void mult(int a[][N], int b[][N], int c[][N]) {
  int i, j, k;
  for (i = 0; i < N; i++)
    for (j = 0; j < N; j++) {
      c[i][j] = 0;
      for (k = 0; k < N; k++)
        c[i][j] += a[i][k] * b[k][j];
    }
}

Matris İşlemleri · ödev

Ödevler: matris çarpımı ve transpoz

  1. mult (n×n) için adım sayısı tablosunu çıkarın; kaç çarpma yapılır?
  2. m×n ile n×p matrisin çarpımının karmaşıklığı nedir? Sonuç matrisi kaç hücre?
  3. n×n bir matrisin transpozunu ek dizi kullanmadan (yerinde) alan yordamı yazın.
Çözümler
  1. for i: n+1 · for j: n(n+1) · c=0: n² · for k: n²(n+1) · +=: n³ → 2n³ + 3n² + 2n + 1 = O(n³); n³ çarpma. (n = 1…9 için betikle doğrulandı.)
  2. Her c[i][j] için n çarpım: O(m·n·p); m·p hücre.
  3. for (i = 0; i < n; i++) for (j = i + 1; j < n; j++) { t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t; } — yalnız üst üçgen gezilir (j = 0'dan başlasaydı her çift iki kez takas edilir, matris değişmezdi). n(n−1)/2 takas → O(n²).

Laboratuvar · C kodu adım adım

Laboratuvar: Dinamik Dizi

Kapanış

Karmaşıklık Özeti

İşlemZamanEk bellek
A[i] erişimi; n boyutlu adres (n−1 çarpma)O(1)O(1)
Dinamik diziye sona ekleme (katlama)amortize O(1), en kötü O(n)≤ 2·boyut
Araya ekleme / silmeO(n)O(1)
Alt üçgen / kuşak matris erişimiO(1)n(n+1)/2 · kuşak öğeleri
KusakMatrisKur, AltUcgenOkuO(saklanan öğe)ARA: a+b−1
strlen / uzunlukO(n)O(1)
Matris toplama (m×n)O(mn)O(1)
Matris çarpımı (n×n)O(n³)O(1)

Kapanış

Sık Yapılan Hatalar

1Dizi sınırı dışına yazmak (a[n]); C bunu denetlemez.
2a = realloc(a, …): başarısızlıkta eski bloğu kaybetmek.
3sizeof(int *) ile sizeof(int)’i karıştırmak; parametrede sizeof a kullanmak.
42B dinamik dizide satırları free etmeden yalnız işaretçi dizisini bırakmak.
5Satır/sütun öncelikli formülleri karıştırmak; öğe boyutunu (s) unutmak.
6Dizgiler için == kullanmak ya da '\0' için yer ayırmamak.

Kapanış

Alıştırmalar

  1. double d[5][8] satır öncelikli, α = 2000: &d[3][5] kaçtır? Sütun öncelikli olsaydı?
  2. int A[4][5][6] için A[2][3][4]'ün bağıl konumunu Horner ile hesaplayın.
  3. 5×5 alt üçgen matriste a43 ALT dizisinin kaçıncı konumunda?
  4. n = 6, a = 2, b = 3 kuşak matrisi kaç öğe tutar? ARA dizisi nedir?
  5. Kapasitesi 1'den başlayıp ikiye katlanan dinamik diziye 100 öğe eklenirse toplam kaç kopya yapılır?
  6. struct { char a; double b; char c; } için alanlar yeniden sıralanarak boyut nasıl küçültülür?
Cevaplar
  1. Satır: 2000 + (3·8 + 5)·8 = 2232. Sütun: 2000 + (5·5 + 3)·8 = 2224.
  2. (2·5 + 3)·6 + 4 = 82.
  3. k = 4·5/2 + 3 = 13.
  4. 6·4 − 1 − 3 = 20; kuşaklar −1, 0, 1, 2 → uzunluk 5, 6, 5, 4 → ARA = [0, 5, 11, 16].
  5. 1 + 2 + 4 + … + 64 = 127 (< 2·100).
  6. double başa alınırsa: { double b; char a, c; } → tipik 16 bayt (24 yerine).

Kapanış

Bölüm Özeti

Dizi = eşleme

ADT: ⟨indis, değer⟩; C gerçeklemesi ardışık bellek ve α + i·s adresi.

Adres hesabı

Satır öncelikli: α + (i·u1 + j)·s; n boyutta Horner ile n−1 çarpma.

Yer kazanmak

Üçgen: n(n+1)/2 öğe, k = i(i+1)/2 + j. Kuşak: j − i numaralı kuşaklar + ARA.

Dinamik bellek

malloc/realloc/free; katlayarak büyüme amortize O(1); 2B dizi = işaretçi dizisi.

Kayıtlar

struct alanları toplar, union paylaştırır; dizgi '\0' ile biten char dizisidir.

Matrisler

Toplama O(mn), çarpma O(n³).

Kapanış

Kaynaklar ve Devam

  • E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. bs., Bölüm 2 (diziler, yapılar, çok boyutlu diziler, dizgiler) ve 1.4.
  • B. Kernighan, D. Ritchie, The C Programming Language, 2. bs., Bölüm 5–6 (işaretçiler, diziler, yapılar).
  • T. Cormen ve ark., Introduction to Algorithms, 17.4 (dinamik tablolar ve amortize analiz).

Son

Bölüm 2: Diziler ve Yapılar
Sonraki bölüm: Bölüm 3: Özyineleme, İkili Arama ve Geri İzleme · Ders sayfası
1 / 1 C ile Veri Yapıları