← Kursa Dön
📄 Text · 15 min

STL Algoritmaları

STL algoritmaları, container'lar üzerinde işlem yapan hazır fonksiyonlardır. Sıralama, arama, dönüştürme, filtreleme — hepsini kendin yazmak yerine tek satırda halledebilirsin.

Bir İsviçre çakısı gibi düşün: içinde düzinelerce araç var ve her biri belirli bir iş için tasarlanmış. Hepsini bilmek zorunda değilsin ama temel olanları bilmek hayatını inanılmaz kolaylaştırır.

Tüm STL algoritmaları iterator'lar üzerinden çalışır. Bu sayede herhangi bir container ile kullanabilirsin — aynı sort hem vector hem deque üzerinde çalışır.


std::sort — Sıralama

En sık kullanılan algoritmadır. Varsayılan olarak küçükten büyüğe sıralar. Performansı O(n log n) — pratikte son derece hızlıdır (introsort kullanır).

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {5, 3, 8, 1, 9, 2, 7};

    // Küçükten büyüğe sıralama
    std::sort(nums.begin(), nums.end());

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 1 2 3 5 7 8 9
    std::cout << "\n";

    // Büyükten küçüğe sıralama
    std::sort(nums.begin(), nums.end(), std::greater<int>());

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 9 8 7 5 3 2 1
    std::cout << "\n";

    return 0;
}

Custom Comparator ile Sıralama

Kendi sıralama kuralını tanımlayabilirsin:

#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

struct Student {
    std::string name;
    double gpa;
};

int main() {
    std::vector<Student> students = {
        {"Ali", 3.5}, {"Ayse", 3.8}, {"Mehmet", 2.9}, {"Zeynep", 3.8}
    };

    // GPA'ya göre azalan sırala, eşitse isme göre artan
    std::sort(students.begin(), students.end(),
        [](const Student& a, const Student& b) {
            if (a.gpa != b.gpa) return a.gpa > b.gpa;
            return a.name < b.name;
        });

    for (const auto& s : students) {
        std::cout << s.name << " (" << s.gpa << ")\n";
    }
    // Ayse (3.8)
    // Zeynep (3.8)
    // Ali (3.5)
    // Mehmet (2.9)

    return 0;
}

partial_sort ve stable_sort

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {5, 3, 8, 1, 9, 2, 7};

    // partial_sort — sadece ilk 3'ü sırala
    std::partial_sort(nums.begin(), nums.begin() + 3, nums.end());
    // İlk 3 eleman sıralı: 1 2 3 ... geri kalanı belirsiz
    std::cout << "Top 3: ";
    for (int i = 0; i < 3; ++i) std::cout << nums[i] << " ";
    std::cout << "\n";

    // stable_sort — eşit elemanların orijinal sırasını korur
    std::vector<std::pair<int, std::string>> items = {
        {2, "elma"}, {1, "armut"}, {2, "muz"}, {1, "cilek"}
    };

    std::stable_sort(items.begin(), items.end(),
        [](const auto& a, const auto& b) { return a.first < b.first; });

    for (const auto& [num, name] : items) {
        std::cout << num << ":" << name << " ";
    }
    // 1:armut 1:cilek 2:elma 2:muz — eşit olanların sırası korundu
    std::cout << "\n";

    return 0;
}

std::find ve std::find_if — Arama

find belirli bir değeri, find_if ise bir koşulu sağlayan ilk elemanı arar.

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {10, 25, 30, 45, 50, 65};

    // Değere göre arama
    auto it = std::find(nums.begin(), nums.end(), 30);
    if (it != nums.end()) {
        std::cout << "30 bulundu, indeks: "
                  << std::distance(nums.begin(), it) << "\n";
    }

    // Koşula göre arama — ilk çift sayı
    auto it2 = std::find_if(nums.begin(), nums.end(),
        [](int x) { return x % 2 == 0; });

    if (it2 != nums.end()) {
        std::cout << "Ilk cift sayi: " << *it2 << "\n";  // 10
    }

    // Koşulu sağlamayan ilk eleman
    auto it3 = std::find_if_not(nums.begin(), nums.end(),
        [](int x) { return x < 40; });

    if (it3 != nums.end()) {
        std::cout << "40'tan buyuk/esit ilk: " << *it3 << "\n";  // 45
    }

    return 0;
}

💡 Sorted container'larda (set, map) `find` üye fonksiyonunu kullan. STL'in std::find algoritması O(n) lineer arama yapar. Ama set::find() O(log n) — ağaç yapısından faydalanır.


std::count ve std::count_if — Sayma

Bir değerin veya koşulu sağlayan elemanların kaç tane olduğunu sayar.

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 2, 4, 2, 5, 2};

    // Değer sayma
    int twos = std::count(nums.begin(), nums.end(), 2);
    std::cout << "2'lerin sayisi: " << twos << "\n";  // 4

    // Koşula göre sayma — kaç tanesi 3'ten büyük?
    int bigOnes = std::count_if(nums.begin(), nums.end(),
        [](int x) { return x > 3; });
    std::cout << "3'ten buyuk: " << bigOnes << "\n";  // 2

    // Pratik: çift sayıları say
    int evens = std::count_if(nums.begin(), nums.end(),
        [](int x) { return x % 2 == 0; });
    std::cout << "Cift sayilar: " << evens << "\n";  // 5

    return 0;
}

std::transform — Dönüştürme

Her elemanı bir fonksiyondan geçirip sonucu başka bir yere (veya aynı yere) yazar. Fonksiyonel programlamadaki map operasyonuna karşılık gelir.

#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    std::vector<int> squares(nums.size());

    // Her elemanın karesini al
    std::transform(nums.begin(), nums.end(), squares.begin(),
        [](int x) { return x * x; });

    for (int x : squares) std::cout << x << " ";
    // Çıktı: 1 4 9 16 25
    std::cout << "\n";

    // Yerinde dönüştürme — aynı container'a yaz
    std::transform(nums.begin(), nums.end(), nums.begin(),
        [](int x) { return x * 2; });

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 2 4 6 8 10
    std::cout << "\n";

    // String'i büyük harfe çevir
    std::string text = "merhaba dunya";
    std::transform(text.begin(), text.end(), text.begin(),
        [](char c) { return std::toupper(c); });
    std::cout << text << "\n";  // MERHABA DUNYA

    return 0;
}

İki Container ile Transform

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> a = {1, 2, 3, 4, 5};
    std::vector<int> b = {10, 20, 30, 40, 50};
    std::vector<int> result(a.size());

    // İki container'ın elemanlarını topla
    std::transform(a.begin(), a.end(), b.begin(), result.begin(),
        [](int x, int y) { return x + y; });

    for (int x : result) std::cout << x << " ";
    // Çıktı: 11 22 33 44 55
    std::cout << "\n";

    return 0;
}

std::accumulate — Biriktirme/Toplama

Tüm elemanları bir başlangıç değerinden başlayarak birleştirir. Varsayılan olarak toplama yapar ama herhangi bir işlem tanımlayabilirsin.

#include <vector>
#include <numeric>
#include <string>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // Toplam
    int sum = std::accumulate(nums.begin(), nums.end(), 0);
    std::cout << "Toplam: " << sum << "\n";  // 15

    // Çarpım
    int product = std::accumulate(nums.begin(), nums.end(), 1,
        [](int acc, int x) { return acc * x; });
    std::cout << "Carpim: " << product << "\n";  // 120

    // String birleştirme
    std::vector<std::string> words = {"Merhaba", " ", "Dunya", "!"};
    std::string sentence = std::accumulate(
        words.begin(), words.end(), std::string(""));
    std::cout << sentence << "\n";  // Merhaba Dunya!

    return 0;
}

⚠️ `accumulate`'in başlangıç değerinin tipi önemli! std::accumulate(v.begin(), v.end(), 0) ile 0 (int) verirsen sonuç int olur. Ondalıklı sonuç istiyorsan 0.0 (double) ver.

#include <vector>
#include <numeric>
#include <iostream>

int main() {
    std::vector<double> grades = {3.5, 3.8, 2.9, 3.2};

    // YANLIŞ — int başlangıç değeri, kesirler kaybolur
    // double avg = std::accumulate(grades.begin(), grades.end(), 0) / grades.size();

    // DOĞRU — double başlangıç değeri
    double avg = std::accumulate(grades.begin(), grades.end(), 0.0)
                 / grades.size();
    std::cout << "Ortalama: " << avg << "\n";  // 3.35

    return 0;
}

std::for_each — Her Eleman İçin

Her eleman üzerinde bir fonksiyon çalıştırır. Modern C++'ta range-based for döngüsü genellikle daha okunabilir, ama for_each'in bazı avantajları var.

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // Her elemanı yazdır
    std::for_each(nums.begin(), nums.end(),
        [](int x) { std::cout << x * x << " "; });
    // Çıktı: 1 4 9 16 25
    std::cout << "\n";

    // Her elemanı değiştir
    std::for_each(nums.begin(), nums.end(),
        [](int& x) { x += 10; });

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 11 12 13 14 15
    std::cout << "\n";

    return 0;
}

std::min_element ve std::max_element

Container'daki en küçük veya en büyük elemana iterator döner.

#include <vector>
#include <algorithm>
#include <iostream>
#include <string>

int main() {
    std::vector<int> nums = {5, 3, 8, 1, 9, 2};

    auto minIt = std::min_element(nums.begin(), nums.end());
    auto maxIt = std::max_element(nums.begin(), nums.end());

    std::cout << "Min: " << *minIt << " (indeks "
              << std::distance(nums.begin(), minIt) << ")\n";
    std::cout << "Max: " << *maxIt << " (indeks "
              << std::distance(nums.begin(), maxIt) << ")\n";

    // İkisini birden — minmax_element
    auto [lo, hi] = std::minmax_element(nums.begin(), nums.end());
    std::cout << "Min: " << *lo << ", Max: " << *hi << "\n";

    // Custom comparator — en uzun string
    std::vector<std::string> words = {"elma", "portakal", "muz", "cilek"};
    auto longest = std::max_element(words.begin(), words.end(),
        [](const std::string& a, const std::string& b) {
            return a.length() < b.length();
        });
    std::cout << "En uzun: " << *longest << "\n";  // portakal

    return 0;
}

Remove-Erase Idiom

STL'in remove algoritması aslında elemanları silmez — sadece silinmeyecek elemanları öne taşır ve "yeni son"a iterator döner. Gerçek silme işlemi container'ın erase fonksiyonu ile yapılır.

Bu ikili kullanıma remove-erase idiom denir.

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 2, 4, 2, 5};

    // Adım 1: remove — 2'leri "sona taşı"
    auto newEnd = std::remove(nums.begin(), nums.end(), 2);
    // nums şimdi: {1, 3, 4, 5, ?, ?, ?}
    //                            ^ newEnd

    // Adım 2: erase — sondaki çöpleri sil
    nums.erase(newEnd, nums.end());
    // nums şimdi: {1, 3, 4, 5}

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 1 3 4 5
    std::cout << "\n";

    return 0;
}

Tek Satırda (Klasik Pattern)

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 2, 4, 2, 5};

    // Tek satır remove-erase
    nums.erase(std::remove(nums.begin(), nums.end(), 2), nums.end());

    for (int x : nums) std::cout << x << " ";
    std::cout << "\n";

    // Koşula göre — çift sayıları sil
    nums = {1, 2, 3, 4, 5, 6, 7, 8};
    nums.erase(
        std::remove_if(nums.begin(), nums.end(),
            [](int x) { return x % 2 == 0; }),
        nums.end());

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 1 3 5 7
    std::cout << "\n";

    return 0;
}

💡 C++20'de `std::erase` ve `std::erase_if` geldi — artık remove-erase idiom'una gerek yok: ``cpp std::erase(nums, 2); // Tüm 2'leri sil std::erase_if(nums, [](int x) { return x > 5; }); // Koşula göre sil ``


Diğer Faydalı Algoritmalar

copy ve copy_if

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<int> dst;

    // Sadece çift olanları kopyala
    std::copy_if(src.begin(), src.end(), std::back_inserter(dst),
        [](int x) { return x % 2 == 0; });

    for (int x : dst) std::cout << x << " ";
    // Çıktı: 2 4 6 8
    std::cout << "\n";

    return 0;
}

any_of, all_of, none_of

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {2, 4, 6, 8, 10};

    bool allEven = std::all_of(nums.begin(), nums.end(),
        [](int x) { return x % 2 == 0; });
    std::cout << "Hepsi cift mi? " << (allEven ? "Evet" : "Hayir") << "\n";

    bool anyNegative = std::any_of(nums.begin(), nums.end(),
        [](int x) { return x < 0; });
    std::cout << "Negatif var mi? " << (anyNegative ? "Evet" : "Hayir") << "\n";

    bool noneZero = std::none_of(nums.begin(), nums.end(),
        [](int x) { return x == 0; });
    std::cout << "Sifir yok mu? " << (noneZero ? "Evet" : "Hayir") << "\n";

    return 0;
}

fill ve iota

#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>

int main() {
    std::vector<int> v(10);

    // Hepsini aynı değerle doldur
    std::fill(v.begin(), v.end(), 42);
    // v: {42, 42, 42, ...}

    // Artan değerlerle doldur
    std::iota(v.begin(), v.end(), 1);
    // v: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    for (int x : v) std::cout << x << " ";
    std::cout << "\n";

    return 0;
}

unique

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 1, 2, 2, 3, 3, 3, 4, 5, 5};

    // unique — ardışık tekrarları kaldırır (önce sıralı olmalı!)
    auto newEnd = std::unique(nums.begin(), nums.end());
    nums.erase(newEnd, nums.end());

    for (int x : nums) std::cout << x << " ";
    // Çıktı: 1 2 3 4 5
    std::cout << "\n";

    return 0;
}

reverse ve rotate

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    // Ters çevir
    std::reverse(v.begin(), v.end());
    for (int x : v) std::cout << x << " ";
    // Çıktı: 5 4 3 2 1
    std::cout << "\n";

    // Rotate — 3. eleman başa gelsin
    v = {1, 2, 3, 4, 5};
    std::rotate(v.begin(), v.begin() + 2, v.end());
    for (int x : v) std::cout << x << " ";
    // Çıktı: 3 4 5 1 2
    std::cout << "\n";

    return 0;
}

Algoritma Hızlı Referans Tablosu

AlgoritmaNe YaparKarmaşıklık
sortSıralarO(n log n)
stable_sortSıralar (eşit elemanların sırası korunur)O(n log n)
partial_sortİlk k elemanı sıralarO(n log k)
find / find_ifİlk eşleşeni bulurO(n)
count / count_ifEşleşen sayısını dönerO(n)
transformHer elemanı dönüştürürO(n)
accumulateElemanları birleştirirO(n)
for_eachHer elemana fonksiyon uygularO(n)
min_element / max_elementEn küçük/büyükO(n)
remove / remove_ifElemanları "sona taşır"O(n)
copy / copy_ifElemanları kopyalarO(n)
all_of / any_of / none_ofKoşul kontrolüO(n)
fillAynı değerle doldururO(n)
iotaArtan değerlerle doldururO(n)
reverseTers çevirirO(n)
uniqueArdışık tekrarları kaldırırO(n)

Özet

  • `std::sort` O(n log n) sıralama yapar. Custom comparator ile kendi sıralama kuralını tanımlayabilirsin. stable_sort eşit elemanların orijinal sırasını korur.

  • `std::find` ve `std::find_if` lineer arama yapar (O(n)). Sorted container'larda üye fonksiyonlarını (set::find gibi) tercih et.

  • `std::transform` her elemanı bir fonksiyondan geçirir — fonksiyonel programlamadaki map karşılığı.

  • `std::accumulate` elemanları bir başlangıç değerinden itibaren birleştirir — toplama, çarpma, string birleştirme. Başlangıç değerinin tipi sonucu belirler.

  • Remove-erase idiom iki adımlı bir silme tekniğidir: remove taşır, erase siler. C++20'de std::erase/std::erase_if bunu tek satıra indirir.

  • STL algoritmaları iterator tabanlıdır — container'dan bağımsız çalışırlar. <algorithm> ve <numeric> header'larını dahil etmeyi unutma.