Çok Boyutlu Diziler ve Matris İşlemleri
Şimdiye kadar tek boyutlu diziler gördük — bir sıra halinde elemanlar. Ama ya bir tablo gerekiyorsa? Satırlar ve sütunlardan oluşan bir not çizelgesi, bir oyun tahtası, bir piksel ekranı? İşte burada çok boyutlu diziler devreye giriyor.
Bu derste 2 boyutlu (2D) dizileri hem C-style hem de modern vector<vector<int>> yaklaşımıyla öğreneceğiz. Matris toplama ve çarpma gibi pratik örnekler yapacağız.
2D Dizi Nedir?
2D diziyi bir Excel tablosu gibi düşün. Satırlar ve sütunlar var. Her hücreye ulaşmak için iki indeks lazım: biri satır, biri sütun.
Mesela 3 öğrencinin 4 dersten notlarını düşün:
| Ders 0 | Ders 1 | Ders 2 | Ders 3 | |
|---|---|---|---|---|
| Öğr. 0 | 90 | 85 | 78 | 92 |
| Öğr. 1 | 75 | 88 | 95 | 70 |
| Öğr. 2 | 60 | 72 | 80 | 85 |
Bu tabloyu bir 2D dizi ile temsil edebilirsin: notlar[satir][sutun].
2D C-Style Dizi
Tanımlama ve Başlatma
#include <iostream>
using namespace std;
int main() {
// 3 satır, 4 sütun
int notlar[3][4] = {
{90, 85, 78, 92}, // 0. öğrenci
{75, 88, 95, 70}, // 1. öğrenci
{60, 72, 80, 85} // 2. öğrenci
};
// Erişim: notlar[satir][sutun]
cout << "1. öğrenci, 2. ders: " << notlar[1][2] << endl; // 95
// Değer değiştirme
notlar[0][0] = 95;
cout << "Güncel: " << notlar[0][0] << endl; // 95
return 0;
}Döngü ile Gezinme
#include <iostream>
using namespace std;
int main() {
int notlar[3][4] = {
{90, 85, 78, 92},
{75, 88, 95, 70},
{60, 72, 80, 85}
};
// Klasik iç içe for döngüsü
for (int i = 0; i < 3; i++) {
cout << "Öğrenci " << i << ": ";
for (int j = 0; j < 4; j++) {
cout << notlar[i][j] << "\t";
}
cout << endl;
}
return 0;
}Her Öğrencinin Ortalamasını Hesaplama
#include <iostream>
using namespace std;
int main() {
const int OGRENCI = 3;
const int DERS = 4;
int notlar[OGRENCI][DERS] = {
{90, 85, 78, 92},
{75, 88, 95, 70},
{60, 72, 80, 85}
};
for (int i = 0; i < OGRENCI; i++) {
int toplam = 0;
for (int j = 0; j < DERS; j++) {
toplam += notlar[i][j];
}
double ort = static_cast<double>(toplam) / DERS;
cout << "Öğrenci " << i << " ortalama: " << ort << endl;
}
return 0;
}2D C-Style Diziyi Fonksiyona Geçirme
C-style 2D diziyi fonksiyona geçirirken sütun sayısını belirtmek zorundasın:
#include <iostream>
using namespace std;
// Sütun sayısı sabit olmalı
void yazdir(int dizi[][4], int satir_sayisi) {
for (int i = 0; i < satir_sayisi; i++) {
for (int j = 0; j < 4; j++) {
cout << dizi[i][j] << "\t";
}
cout << endl;
}
}
int main() {
int notlar[3][4] = {
{90, 85, 78, 92},
{75, 88, 95, 70},
{60, 72, 80, 85}
};
yazdir(notlar, 3);
return 0;
}⚠️ Dikkat: C-style 2D dizilerde fonksiyona geçerken ilk boyutu (satır) boş bırakabilirsin ama ikinci boyutu (sütun) mutlaka belirtmelisin. Bu, derleyicinin bellek düzenini hesaplaması için gerekli.
vector\<vector\<int\>\> ile 2D Yapı
Modern C++'ta 2D yapılar için vector<vector<int>> kullanılır. Avantajları: boyutlar dinamik, fonksiyona geçirmek kolay, boyut bilgisi kaybolmaz.
Tanımlama ve Kullanım
#include <iostream>
#include <vector>
using namespace std;
int main() {
// Başlangıç değerleriyle
vector<vector<int>> notlar = {
{90, 85, 78, 92},
{75, 88, 95, 70},
{60, 72, 80, 85}
};
// Erişim
cout << "notlar[1][2] = " << notlar[1][2] << endl; // 95
// Boyut bilgisi
cout << "Satır sayısı: " << notlar.size() << endl; // 3
cout << "Sütun sayısı: " << notlar[0].size() << endl; // 4
// Gezinme
for (int i = 0; i < notlar.size(); i++) {
for (int j = 0; j < notlar[i].size(); j++) {
cout << notlar[i][j] << "\t";
}
cout << endl;
}
return 0;
}Dinamik Boyut: Satır ve Sütun Ekleme
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<int>> matris;
// Satır ekleme
matris.push_back({1, 2, 3});
matris.push_back({4, 5, 6});
matris.push_back({7, 8, 9});
// Bir satıra sütun ekleme
matris[0].push_back(99); // ilk satırın sonuna 99 ekle
// Dikkat: artık satırlar farklı uzunlukta olabilir (jagged array)
for (auto& satir : matris) {
for (int x : satir) {
cout << x << "\t";
}
cout << endl;
}
return 0;
}Belirli Boyutta Boş Matris Oluşturma
#include <iostream>
#include <vector>
using namespace std;
int main() {
int satir = 3, sutun = 4;
// satir x sutun boyutunda, hepsi 0
vector<vector<int>> matris(satir, vector<int>(sutun, 0));
// Kontrol
cout << "Boyut: " << matris.size() << "x" << matris[0].size() << endl;
for (auto& s : matris) {
for (int x : s) cout << x << " ";
cout << endl;
}
return 0;
}Bu kalıp çok kullanışlı: vector<vector<int>>(satir, vector<int>(sutun, varsayilan_deger)).
Nested Range-Based For
C++11'den itibaren iç içe range-based for döngüsü ile 2D yapıları temiz bir şekilde gezebilirsin:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<int>> matris = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Sadece okuma
for (const auto& satir : matris) {
for (int eleman : satir) {
cout << eleman << "\t";
}
cout << endl;
}
// Değer değiştirme
for (auto& satir : matris) {
for (int& eleman : satir) {
eleman *= 2;
}
}
cout << "İkiye çarpıldıktan sonra:" << endl;
for (const auto& satir : matris) {
for (int eleman : satir) {
cout << eleman << "\t";
}
cout << endl;
}
return 0;
}💡 İpucu: Dış döngüdeki
satirher zaman referans (auto&veyaconst auto&) olmalı. Eğer referans yapmazsan her satır vektörü kopyalanır — hem yavaş hem de değişiklik yapmak istersen orijinali etkilemez.
Matris Toplama
İki matrisin toplanması: aynı boyuttaki matrislerin karşılıklı elemanlarını topla.
A = |1 2 3| B = |9 8 7| C = A + B = |10 10 10|
|4 5 6| |6 5 4| |10 10 10|#include <iostream>
#include <vector>
using namespace std;
using Matris = vector<vector<int>>;
Matris matris_topla(const Matris& A, const Matris& B) {
int satir = A.size();
int sutun = A[0].size();
Matris C(satir, vector<int>(sutun, 0));
for (int i = 0; i < satir; i++) {
for (int j = 0; j < sutun; j++) {
C[i][j] = A[i][j] + B[i][j];
}
}
return C;
}
void matris_yazdir(const Matris& M) {
for (const auto& satir : M) {
for (int x : satir) {
cout << x << "\t";
}
cout << endl;
}
}
int main() {
Matris A = {{1, 2, 3}, {4, 5, 6}};
Matris B = {{9, 8, 7}, {6, 5, 4}};
Matris C = matris_topla(A, B);
cout << "A + B =" << endl;
matris_yazdir(C);
return 0;
}Çıktı:
A + B =
10 10 10
10 10 10Matris Çarpma
Matris çarpma, toplama kadar basit değil. A (m×n) ve B (n×p) matrislerini çarpmak için:
Sonuç C matrisi m×p boyutunda olur
C[i][j] = A'nın i. satırı ile B'nin j. sütununun iç çarpımı
A = |1 2| B = |5 6| C = A × B = |1*5+2*7 1*6+2*8| = |19 22|
|3 4| |7 8| |3*5+4*7 3*6+4*8| |43 50|#include <iostream>
#include <vector>
using namespace std;
using Matris = vector<vector<int>>;
Matris matris_carp(const Matris& A, const Matris& B) {
int m = A.size();
int n = A[0].size();
int p = B[0].size();
Matris C(m, vector<int>(p, 0));
for (int i = 0; i < m; i++) {
for (int j = 0; j < p; j++) {
for (int k = 0; k < n; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
return C;
}
void matris_yazdir(const Matris& M) {
for (const auto& satir : M) {
for (int x : satir) cout << x << "\t";
cout << endl;
}
}
int main() {
Matris A = {{1, 2}, {3, 4}};
Matris B = {{5, 6}, {7, 8}};
Matris C = matris_carp(A, B);
cout << "A x B =" << endl;
matris_yazdir(C);
// 19 22
// 43 50
return 0;
}⚠️ Dikkat: Matris çarpması için A'nın sütun sayısı ile B'nin satır sayısı eşit olmalı. Gerçek kodda bu kontrolü eklemeyi unutma.
Pratik Örnek: Matris Transpozu
Transpoze almak = satırları sütun, sütunları satır yapmak:
#include <iostream>
#include <vector>
using namespace std;
using Matris = vector<vector<int>>;
Matris transpoze(const Matris& M) {
int satir = M.size();
int sutun = M[0].size();
Matris T(sutun, vector<int>(satir));
for (int i = 0; i < satir; i++) {
for (int j = 0; j < sutun; j++) {
T[j][i] = M[i][j];
}
}
return T;
}
void matris_yazdir(const Matris& M) {
for (const auto& satir : M) {
for (int x : satir) cout << x << "\t";
cout << endl;
}
}
int main() {
Matris M = {
{1, 2, 3},
{4, 5, 6}
};
cout << "Orijinal (2x3):" << endl;
matris_yazdir(M);
Matris T = transpoze(M);
cout << "Transpoze (3x2):" << endl;
matris_yazdir(T);
return 0;
}Pratik Örnek: Tic-Tac-Toe Tahtası
2D dizilerin güzel bir kullanım alanı: oyun tahtaları.
#include <iostream>
#include <vector>
using namespace std;
void tahta_yazdir(const vector<vector<char>>& tahta) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << " " << tahta[i][j] << " ";
if (j < 2) cout << "|";
}
cout << endl;
if (i < 2) cout << "-----------" << endl;
}
}
int main() {
vector<vector<char>> tahta = {
{'X', 'O', 'X'},
{'.', 'X', 'O'},
{'O', '.', 'X'}
};
tahta_yazdir(tahta);
// Bir hamle yap
tahta[1][0] = 'O';
cout << "\nHamle sonrası:" << endl;
tahta_yazdir(tahta);
return 0;
}Çıktı:
X | O | X
-----------
. | X | O
-----------
O | . | X
Hamle sonrası:
X | O | X
-----------
O | X | O
-----------
O | . | XBellek Düzeni: Row-Major vs Column-Major
C++ 2D dizileri row-major (satır öncelikli) düzende saklar. Yani dizi[0][0], dizi[0][1], dizi[0][2], dizi[1][0], ... şeklinde bellekte yan yana durur.
Bu neden önemli? Cache dostu erişim için iç döngüyü sütunlar üzerinde yapmalısın:
#include <iostream>
#include <vector>
using namespace std;
int main() {
const int N = 1000;
vector<vector<int>> matris(N, vector<int>(N, 1));
long long toplam = 0;
// İYİ — satır satır git (cache dostu)
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
toplam += matris[i][j];
}
}
// KÖTÜ — sütun sütun git (cache düşmanı)
toplam = 0;
for (int j = 0; j < N; j++) {
for (int i = 0; i < N; i++) {
toplam += matris[i][j]; // her adımda farklı satıra atla
}
}
cout << "Toplam: " << toplam << endl;
return 0;
}💡 İpucu: Büyük matrislerde iç döngünün sütun indeksini (j) gezmesi, dış döngünün satır indeksini (i) gezmesi daha performanslıdır. Bu küçük detay büyük veri setlerinde 5-10x fark yaratabilir.
3D ve Daha Fazla Boyut
İhtiyaç olursa 3D ve daha fazla boyut da kullanılabilir ama pratikte nadirdir:
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 3D: 2 sayfa, 3 satır, 4 sütun
vector<vector<vector<int>>> kup(
2, vector<vector<int>>(3, vector<int>(4, 0))
);
kup[0][1][2] = 42;
cout << kup[0][1][2] << endl; // 42
// Gezinme
for (auto& sayfa : kup) {
for (auto& satir : sayfa) {
for (int x : satir) {
cout << x << " ";
}
cout << endl;
}
cout << "---" << endl;
}
return 0;
}3D'den sonrası genellikle kafa karıştırıcı olur. O noktada düz bir 1D vector ile indeks = i * sutun * derinlik + j * derinlik + k formülü kullanmak daha pratik.
Özet
2D dizi satır ve sütunlardan oluşan tablo yapısıdır. C-style'da
int dizi[satir][sutun], modern C++'tavector<vector<int>>ile tanımlanır.C-style 2D dizileri fonksiyona geçirirken sütun sayısı sabit olarak belirtilmelidir;
vector<vector<int>>bu sorunu ortadan kaldırır.Nested range-based for ile 2D yapılar temiz şekilde gezilir; dış döngüde
const auto&kullanmak şarttır (kopyalamayı önlemek için).Matris toplama karşılıklı elemanları toplar; matris çarpma üçlü iç içe döngü gerektirir ve A'nın sütun sayısı B'nin satır sayısına eşit olmalıdır.
C++ dizileri row-major düzende saklanır; büyük matrislerde iç döngünün sütunları gezmesi cache performansı açısından kritiktir.
Pratikte
vector<vector<int>>tercih et — dinamik boyut, kolay fonksiyon geçirme ve boyut bilgisi avantajları sunar.
AI Asistan
Sorularını yanıtlamaya hazır