Big O notasyonu, bir algoritmanın girdi boyutu büyüdükçe çalışma süresinin veya bellek kullanımının nasıl bir eğilimle arttığını gösteren bir üst sınır ölçüm dilidir. Yani "bu program 3 saniye sürer" gibi kesin bir zaman söylemez; "girdi iki katına çıkarsa işlem sayısı da kabaca iki katına çıkar" gibi bir büyüme örüntüsü anlatır. Mülakatlarda ve gerçek projelerde kod yazarken performansı önceden tahmin edebilmenin temel aracı budur. Bu rehberde Big O'yu matematiksel ispatlara girmeden, gündelik örneklerle ve çalışan kod parçalarıyla sezgisel olarak öğreneceksin.
Big O Notasyonu Nedir? Sezgisel Tanım
Big O notasyonunu anlamanın en kolay yolu, onu bir saniye ölçer gibi değil, bir eğilim ölçer gibi düşünmektir. Bir algoritmanın Big O değeri, girdi (genellikle n harfiyle gösterilir) büyüdüğünde işlem sayısının ne kadar hızlı arttığını gösterir. İki farklı bilgisayarda aynı kod farklı sürelerde çalışabilir çünkü işlemci hızı, bellek durumu ve arka planda çalışan başka programlar süreyi değiştirir. Ama Big O bu ayrıntılardan bağımsızdır; o yalnızca "girdi büyüdükçe iş yükü nasıl büyüyor" sorusuna cevap verir.
Bunu somutlaştırmak için kalabalık bir kütüphaneyi hayal et. Aradığın kitabın rafını ve konumunu tam olarak biliyorsan, kütüphanede kaç bin kitap olduğu önemli değildir; doğrudan gidip alırsın. Ama kitapları hiçbir düzen olmadan, rastgele dizilmiş raflarda aramak zorunda kalırsan, kitap sayısı arttıkça arama süren de artar. İşte Big O, tam olarak bu "kitap sayısı arttıkça arama ne kadar zorlaşıyor" ilişkisini matematiksel bir ispat kurmadan, sadece büyüme mantığı üzerinden ifade eder. Kronometre saniyeyi ölçer, Big O ise "iki katı girdi geldiğinde ne olur" sorusunun cevabını ölçer.
Bu ayrım özellikle mülakatlarda kritik bir noktadır, çünkü mülakatı yapan kişi genelde kodun kaç saniyede çalıştığını değil, girdi milyonlara çıktığında kodun hâlâ mantıklı bir sürede çalışıp çalışmayacağını sorar. Algoritma karmaşıklığı kavramına ne kadar hazır olduğunu görmek istersen ücretsiz algoritma bilgi testi ile kendi seviyeni hızlıca ölçebilirsin. İlerleyen bölümlerde bu büyüme eğilimini O(1), O(n), O(n²) ve O(log n) gibi somut kalıplarla, gerçek kod örnekleri üzerinden adım adım göreceksin.
O(1) Sabit Zaman: Anında Erişim
O(1), Big O dünyasının en cömert notasyonudur çünkü girdi boyutu ne kadar büyürse büyüsün işlem süresi sabit kalır. Bunu numaralı bir dolap sistemine benzetebilirsin: dolabın 15 numaralı çekmecesini açmak istediğinde, binada toplam 10 çekmece de olsa 10 bin çekmece de olsa doğrudan 15 numaraya gidersin; diğer çekmeceleri tek tek kontrol etmene gerek yoktur.
Programlamada bunun en tipik örneği, bir dizide veya listede indeks ile eleman erişimidir. Örneğin Python'da liste[3] ya da Java'da dizi[3] yazdığında, bilgisayar dördüncü elemanın bellekte tam olarak nerede olduğunu doğrudan hesaplar ve oraya gider. Liste 5 elemanlı da olsa 5 milyon elemanlı da olsa bu erişim aynı hızda gerçekleşir, çünkü indeks üzerinden konum hesaplamak, listenin boyutundan bağımsız sabit bir işlemdir.
Bir sözlük (hash map) yapısında anahtar üzerinden değer çekmek de genellikle bu kategoriye girer; anahtarı bilmen, tüm veriyi taramadan doğrudan ilgili kutuya ulaşmanı sağlar. O(1) işlemler, mümkün olduğunda tercih edilmesi gereken en verimli kategoridir; çünkü veri büyüse de performans kaybı yaşanmaz. Ancak her erişim bu kadar şanslı değildir: bazen aradığın şeyin tam olarak nerede olduğunu bilmezsin, o zaman sırayla kontrol etmek gerekir. İşte bu durum bizi bir sonraki büyüme kalıbına, doğrusal zamana taşır.
O(n) Doğrusal Zaman: Listede Eleman Arama

O(n), girdi boyutu ile işlem sayısının birebir orantılı büyüdüğü durumu ifade eder. Listede belirli bir konumu değil, belirli bir değeri arıyorsan ve o değerin nerede olduğunu bilmiyorsan, en güvenli yöntem sırayla her elemanı kontrol etmektir. 10 elemanlı bir listede en fazla 10 kontrol yaparsın, 10 milyon elemanlı bir listede ise en fazla 10 milyon kontrol yapman gerekebilir; kontrol sayısı, liste büyüdükçe aynı oranda büyür.
Bu mantığı doğrusal arama (linear search) fonksiyonuyla somutlaştıralım:
def dogrusal_arama(liste, aranan):
for index in range(len(liste)):
if liste[index] == aranan:
return index
return -1
sayilar = [4, 8, 15, 16, 23, 42]
sonuc = dogrusal_arama(sayilar, 23)
print(sonuc) # 4
Bu fonksiyon, aranan değeri bulana kadar listeyi baştan sona tek tek gezer. En kötü senaryoda (değer listede yoksa veya en sonda ise) tüm elemanları kontrol etmiş olursun; bu yüzden karmaşıklığı O(n) olarak adlandırılır. Küçük listelerde bu fark hissedilmez, ama liste binlerce, milyonlarca elemana çıktığında doğrusal aramanın maliyeti gözle görülür şekilde artar. Bu tür temel algoritma mantıklarını sıfırdan, canlı uygulamalı biçimde adım adım öğrenmek istersen birebir Python eğitimi bu konuda sağlam bir başlangıç noktası sunar. Doğrusal aramanın sınırlarını görmek, sıralı veriler üzerinde çok daha hızlı çalışan ikili arama gibi yöntemlere neden ihtiyaç duyduğumuzu anlamanın da ilk adımıdır.
O(n²) Karesel Zaman: İç İçe Döngüler
Bir listede tekrar eden eleman olup olmadığını bulmak istediğinde en doğal (ama en yavaş) yöntem, her elemanı diğer tüm elemanlarla karşılaştırmaktır. Bunu yapmak için bir döngünün içine başka bir döngü koyman gerekir — işte bu "iç içe döngü" yapısı, çoğu zaman O(n²) karmaşıklığının habercisidir. Listedeki her eleman için, listenin tamamını yeniden tarıyorsun demektir.
Aşağıdaki örnekte bir listede tekrar eden sayıları bu şekilde buluyoruz:
def tekrar_edenleri_bul(sayilar):
tekrarlar = []
n = len(sayilar)
for i in range(n):
for j in range(i + 1, n):
if sayilar[i] == sayilar[j] and sayilar[i] not in tekrarlar:
tekrarlar.append(sayilar[i])
return tekrarlar
liste = [3, 5, 2, 5, 8, 3, 9]
print(tekrar_edenleri_bul(liste))
Burada dış döngü listeyi bir kez baştan sona geziyor, iç döngü ise her adımda listenin kalan kısmını yeniden tarıyor. 7 elemanlı bir listede bu, yaklaşık 21 karşılaştırma yapmak anlamına gelir. Listeyi 70 elemana çıkarırsan karşılaştırma sayısı 21'in katı kadar değil, kabaca 100 katı kadar artar — çünkü hem dış döngü hem iç döngü büyür ve ikisi birbiriyle çarpılır. Eleman sayısını 10 kat artırdığında işlem sayısı 10 kat değil, 10x10 kat, yani 100 kat artıyor. İşte "karesel" kelimesi tam olarak bunu anlatıyor: girdi büyüklüğü ikiye katlandığında iş yükü dörde katlanıyor, üçe katlandığında dokuza katlanıyor. Bu tür iç içe döngü mantığı, gündelik kod yazarken sürekli karşına çıkar ve bir noktada "bunu daha hızlı yapabilir miyim?" sorusunu sormaya iter. Bu sorgulama alışkanlığını kazanmak, özellikle Java gibi güçlü tip sistemine sahip bir dilde veri yapılarını doğru seçmeyi öğrenmekle de doğrudan ilgilidir; birebir Java özel ders sürecinde bu tür karmaşıklık farkındalığı, öğrencinin kod yazma refleksinin bir parçası haline getirilir. Mülakatlarda "bu çözümü daha optimize edebilir misin?" sorusunun arkasında genellikle tam olarak bu iç içe döngü problemi yatar.
O(log n) Logaritmik Zaman: İkili Arama Mantığı

Kalın bir sözlükte "kelebek" kelimesini ararken sözlüğü baştan sona sayfa sayfa çevirmezsin, değil mi? Ortadan bir yerden açar, aradığın kelime alfabetik olarak öncesindeyse kitabın sol yarısına, sonrasındaysa sağ yarısına geçersin. Her açışta arama yapacağın alan yarıya iner. İşte O(log n) tam olarak bu mantığı ifade eder: her adımda elindeki olası seçenekleri ikiye bölerek elersin. Bu yöntemin adı ikili arama (binary search) ve sıralı bir listede çalışır. Sıralı bir listede ortadaki elemana bakarsın; aradığın değer ondan büyükse listenin sağ yarısıyla, küçükse sol yarısıyla ilgilenirsin, diğer yarıyı tamamen göz ardı edersin. Sonraki adımda kalan yarıyı yine ikiye bölersin ve bu böyle sürer.
Sayılarla düşününce fark çok çarpıcı hale gelir. 1.000 elemanlı sıralı bir listede doğrusal arama en kötü durumda 1.000 karşılaştırma gerektirebilir. İkili arama ise aynı listede yaklaşık 10 adımda sonuca ulaşır — çünkü 1.000 sayısını art arda ikiye bölmeye başladığında 10 adım sonra pratik olarak tek bir eleman kalır. Liste 1 milyon elemana çıksa bile ikili aramanın adım sayısı yalnızca yaklaşık 20'ye çıkar, doğrusal arama ise en kötü durumda 1 milyon adıma kadar dayanabilir. Girdi büyüklüğü ne kadar patlarsa patlasın, logaritmik büyüme adım sayısını neredeyse yassı bir çizgi gibi tutar. Bu yüzden ikili arama, sıralı verilerle çalışırken en sık başvurulan tekniklerden biridir ve mülakatlarda "bu listeyi nasıl daha hızlı ararsın?" sorusunun klasik cevabıdır. Tek şartı listenin önceden sıralanmış olmasıdır; sıralı olmayan bir listede bu yarıya bölme mantığı işe yaramaz, çünkü ortadaki elemanın hangi tarafta arama yapman gerektiğini garanti edecek bir düzen yoktur.
Neden Önemli? Ölçeklenebilirlik ve Gerçek Dünya Performansı
10 elemanlı bir listede O(n) ile O(n²) arasındaki fark neredeyse hiç hissedilmez; her ikisi de göz açıp kapayıncaya kadar biter. Ama aynı kodu 1 milyon kayıtlı bir müşteri veritabanına, bir e-ticaret sitesinin ürün listesine ya da bir sosyal medya uygulamasının kullanıcı akışına uyguladığında tablo tamamen değişir. Küçük veri setinde fark etmeyen bir yavaşlık, ölçek büyüdüğünde uygulamayı donduran, sunucuyu zorlayan, kullanıcıyı bekletmeye başlayan gerçek bir soruna dönüşür. Bu yüzden ölçeklenebilirlik kavramı, "çalışıyor" ile "gerçekten kullanılabilir" arasındaki farkı belirler. Aşağıdaki tablo bu hissi somutlaştırıyor:
| Karmaşıklık | Örnek Senaryo | 10 elemanda hissedilen fark | 1 milyon elemanda hissedilen fark |
|---|---|---|---|
| O(1) | Diziden index ile eleman çekme | Anlık, fark yok | Anlık, hâlâ fark yok |
| O(log n) | Sıralı listede ikili arama | Anlık, fark yok | Milisaniyeler içinde, pratikte fark edilmez |
| O(n) | Listede doğrusal arama | Anlık, fark yok | Gözle görülür bir bekleme oluşabilir |
| O(n²) | İç içe döngüyle tekrar eden eleman bulma | Anlık, fark yok | Uygulama kilitlenecek kadar yavaşlayabilir |
Tablodaki en çarpıcı satır O(n²) satırıdır: küçük veride tamamen görünmez olan bu maliyet, veri büyüdüğünde en yıkıcı hâle gelen maliyettir. Tam da bu yüzden mülakatlarda "bu kod küçük veride çalışıyor ama üretimde neden yavaşlar?" sorusu sürekli sorulur; doğru cevap genellikle gizli bir O(n²) döngüsünü fark edip fark etmediğinle ilgilidir. Kendi kodlama seviyeni ve algoritmik sezgini objektif biçimde görmek istiyorsan, ücretsiz kariyer testi ile hangi alanlarda daha çok pratiğe ihtiyacın olduğunu görebilirsin. Ölçeklenebilirlik farkındalığı, sadece teorik bir bilgi değil; gerçek projelerde performans sorunlarını önceden görebilme yeteneğidir ve bu yetenek zamanla, bilinçli pratikle gelişir.
Sık Karıştırılan Noktalar: En Kötü Durum, Sabit Katsayılar ve Alan Karmaşıklığı
Big O konusuna yeni başlayanların en çok kafasını karıştıran nokta, "en kötü durum" (worst case) ile "ortalama durum" (average case) arasındaki farktır. Örneğin bir listede belirli bir sayıyı doğrusal arama ile ararken, aradığın sayı listenin ilk elemanıysa tek adımda bulursun; bu en iyi durumdur. Ama sayı listenin son elemanıysa veya listede hiç yoksa, tüm elemanları tek tek kontrol etmen gerekir — bu da en kötü durumdur. Big O notasyonu genellikle en kötü durumu baz alır, çünkü bir algoritmanın "şanslı" senaryoda ne kadar hızlı olduğu değil, "şanssız" senaryoda ne kadar dayanıklı olduğu asıl garantiyi verir. Mülakatlarda da sorulan soru genellikle "bu algoritmanın en kötü durumda karmaşıklığı nedir?" şeklindedir.
Sabit katsayıların ve küçük terimlerin göz ardı edilmesi de sık kafa karıştıran bir başka konudur. Bir algoritma her elemanı üç kez işliyorsa bunun karmaşıklığı O(3n) değil, O(n) olarak yazılır. Benzer şekilde bir algoritmanın karmaşıklığı n² + n + 5 gibi bir formülle ifade ediliyorsa, veri boyutu (n) büyüdükçe n² terimi diğerlerini anlamsız hale getirir; bu yüzden sadece O(n²) yazılır. Big O, sabit bir işlem sayısıyla ilgilenmez, veri büyüdükçe işlem sayısının nasıl büyüdüğüyle ilgilenir.
Sıkça düşülen bir tuzak da "daha az kod satırı yazmak = daha hızlı kod" yanılgısıdır. Tek satırlık bir liste içi işlem, arka planda gizlice tüm listeyi tarayan bir döngü çalıştırıyorsa, görünüşte kısa olan bu kod aslında O(n) karmaşıklığındadır ve iki satırlık açık bir döngüden daha hızlı değildir. Kodun kısalığı okunabilirlik açısından değerlidir ama performans açısından hiçbir garanti sunmaz; asıl belirleyici olan içeride kaç işlem yapıldığıdır.
Son olarak, zaman karmaşıklığı ile alan (space) karmaşıklığının birbirinden tamamen ayrı kavramlar olduğunu unutmamak gerekir. Zaman karmaşıklığı bir algoritmanın kaç adım attığını ölçerken, alan karmaşıklığı algoritmanın çalışırken ek olarak ne kadar bellek kullandığını ölçer. Bir algoritma daha az adımla çalışsın diye ekstra bir liste veya harita tutabilir; bu durumda zamandan kazanırken bellekten harcama yapmış olursun. Bu değiş tokuş, bir sonraki bölümde göreceğin karşılaştırmalı örnekte açıkça görülecek.
Aynı Problemi İki Yöntemle Çözmek: Karşılaştırmalı Kod Örneği
Teoriyi somutlaştırmanın en iyi yolu, aynı problemi iki farklı yaklaşımla çözüp karmaşıklıklarını karşılaştırmaktır. Mülakatlarda sık sorulan klasik bir örnek üzerinden gidelim: bir listede tekrar eden (birden fazla kez geçen) bir eleman var mı, varsa hangisi? İlk aklına gelen çözüm genellikle iç içe döngüdür: her elemanı listedeki diğer tüm elemanlarla karşılaştırırsın. İkinci ve daha verimli çözüm ise listeyi tek geçişte tarayıp gördüğün elemanları bir hashmap (Python'da sözlük) içinde tutmaktır.
def has_duplicate_nested(nums):
# O(n^2) - her eleman diger tum elemanlarla karsilastirilir
n = len(nums)
for i in range(n):
for j in range(i + 1, n):
if nums[i] == nums[j]:
return True
return False
def has_duplicate_hashmap(nums):
# O(n) - liste tek gecişte taranir, gorulenler kaydedilir
seen = set()
for num in nums:
if num in seen:
return True
seen.add(num)
return False
İlk fonksiyon her eleman için listenin kalanını tekrar tekrar tarar; bu yüzden işlem sayısı veri büyüklüğünün karesiyle orantılı büyür ve O(n²) karmaşıklığına sahiptir. İkinci fonksiyon ise listeyi sadece bir kez baştan sona gezer ve her elemanı bir kümede (set) kontrol eder; bu kontrol işlemi ortalama olarak sabit zamanda çalıştığı için toplam karmaşıklık O(n) olur. Buradaki değiş tokuş tam da önceki bölümde bahsedilen konudur: ikinci yöntem daha hızlı çalışır ama ek bir set/hashmap tutarak biraz daha fazla bellek kullanır — yani zamandan kazanıp alandan harcama yapar.
Mülakatlarda sadece "çalışan" bir çözüm sunmak genellikle yeterli görülmez; görüşmeci beklediği asıl şey, adayın önce basit çözümü verip ardından "bunu daha verimli hale getirebilir miyim?" sorusunu kendine sorabilmesidir. Bu tür sorularda pratik yapmak, algoritmik düşünme alışkanlığını kalıcı hale getirir. Bu noktada bire bir ilerlemek isteyenler için Python ve Java özel ders programı, bu tarz karşılaştırmalı problem çözme pratiğini öğrencinin kendi hızında ve mülakat senaryolarına göre işleyebiliyor.
Sık Sorulan Sorular
Big O notasyonu ile gerçek çalışma süresi (saniye) arasındaki fark nedir?
Big O, bir algoritmanın veri büyüdükçe işlem sayısının nasıl büyüdüğünü gösterir; saniye cinsinden bir süre vermez. Aynı O(n) karmaşıklığındaki iki kod, çalıştığı bilgisayara, dile veya o anki sistem yüküne göre farklı gerçek sürelerde tamamlanabilir. Big O, donanımdan bağımsız bir büyüme eğilimi tarif eder.
En kötü durum (worst case) ile ortalama durum (average case) karmaşıklığı neden farklı değerlendirilir?
Ortalama durum, girdilerin "tipik" dağılımına göre beklenen performansı tarif ederken en kötü durum, algoritmanın karşılaşabileceği en zorlu senaryoda ne yapacağını garanti eder. Mülakatlarda ve sistem tasarımında genellikle en kötü durum tercih edilir, çünkü bir sistemin şanssız anlarda çökmemesi gerekir.
Zaman karmaşıklığı ile alan (space) karmaşıklığı arasındaki fark nedir?
Zaman karmaşıklığı algoritmanın kaç adım attığını, alan karmaşıklığı ise çalışma sırasında ek olarak ne kadar bellek kullandığını ölçer. Bir algoritma daha hızlı çalışmak için ekstra veri yapıları tutabilir; bu durumda zaman karmaşıklığı düşerken alan karmaşıklığı yükselir.
Daha az kod satırı yazmak bir algoritmayı otomatik olarak hızlandırır mı?
Hayır. Tek satırlık bir ifade arka planda gizlice bir döngü veya tarama çalıştırabilir ve bu yüzden açık şekilde yazılmış birkaç satırlık koddan daha hızlı olmayabilir. Performansı belirleyen, satır sayısı değil, kodun arka planda kaç işlem yaptığıdır.
Mülakatlarda Big O ile ilgili sorulara nasıl hazırlanmalıyım?
Önce basit ve çalışan bir çözüm bulmayı, ardından bu çözümün karmaşıklığını yüksek sesle ifade etmeyi ve daha verimli bir alternatif önerebilmeyi pratik etmelisin. Tekrar eden eleman bulma, ikili arama gibi klasik problemleri iki farklı yöntemle çözmeyi denemek bu alışkanlığı hızlandırır.
O(log n) neden O(n)'den daha hızlı kabul edilir?
O(log n) karmaşıklığında veri büyüklüğü iki katına çıktığında işlem sayısı sadece bir adım artar, çünkü her adımda arama alanı yarıya iner. O(n) karmaşıklığında ise veri iki katına çıktığında işlem sayısı da iki katına çıkar; bu yüzden büyük veri setlerinde aradaki fark giderek büyür.
Küçük veri setlerinde karmaşıklık farkı neden fark edilmez?
Veri sayısı az olduğunda (örneğin on veya yirmi eleman) hem O(n) hem O(n²) karmaşıklığındaki bir algoritma gözle görülür bir zaman farkı yaratmayacak kadar hızlı çalışır. Karmaşıklık farkının etkisi, veri büyüklüğü yüzlerce, binlerce veya milyonlarca elemana ulaştığında belirginleşir.
Big O notasyonunu bir kez sezgisel olarak kavradığında, kod yazarken "bu daha hızlı çalışır mı" sorusunu otomatik olarak sormaya başlarsın; bu da hem mülakatlarda hem gerçek projelerde seni bir adım öne taşır. Öğrendiklerini pratiğe dökmek ve nerede eksik olduğunu görmek istersen algoritma bilgi testi ile kendi seviyeni kısa sürede ölçebilirsin.