<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://sonsuzus.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sonsuzus.github.io/" rel="alternate" type="text/html" /><updated>2026-04-03T12:17:02+00:00</updated><id>https://sonsuzus.github.io/feed.xml</id><title type="html">SonsuzUs</title><subtitle>Programlama ve Yazılım</subtitle><author><name>Sonsuz Us</name></author><entry><title type="html">Rekabetçi Programcı Çizgenin Temelleri</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-cizgenin-temelleri" rel="alternate" type="text/html" title="Rekabetçi Programcı Çizgenin Temelleri" /><published>2026-04-03T00:00:00+00:00</published><updated>2026-04-03T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-cizgenin-temelleri</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-cizgenin-temelleri"><![CDATA[<p>Çoğu kodlama sorusu bir çizge problemi olarak modellenip uygun bir çizge algoritmasıyla çözülebilir. Tipik bir örnek, bir ülkedeki yolları ve şehirleri temsil eden ağdır. Bazen çizge sorunun içinde gizli olduğundan fark edilmesi güçleşir. Bu bölümde çizgelerle ilgili temel kavramları ele alıp algoritmalarda çizgeleri göstermenin farklı yollarını inceleyeceğiz.</p>

<h2 id="111-çizge-terminolojisi">11.1 Çizge Terminolojisi</h2>

<p>Bir <strong>çizge</strong> (graph), <strong>düğümlerden</strong> (nodes) ve <strong>kenarlardan</strong> (edges) oluşur. Bu yazıda $n$ çizgedeki toplam düğüm sayısını, $m$ ise toplam kenar sayısını belirtecektir. Düğümler $1, 2, \ldots, n$ tamsayılarıyla numaralandırılır.</p>

<h3 id="yol-ve-döngü">Yol ve Döngü</h3>

<p><strong>Yol</strong> (path), $a$ düğümünden $b$ düğümüne kenarlar kullanılarak ulaşılmasını sağlar. Yolun uzunluğu, yolda geçilen kenar sayısına eşittir. Örneğin $1 \to 3 \to 4 \to 5$ yolu, 1. düğümden 5. düğüme 3 uzunluğunda bir yoldur.</p>

<p>Başlangıç ve son düğümün aynı olduğu yola <strong>döngü</strong> (cycle) denir. Bir yolda her düğüm en fazla bir kez geçiyorsa bu yol <strong>basittir</strong> (simple).</p>

<h3 id="bağlılık">Bağlılık</h3>

<p>Her iki düğümü arasında bir yol bulunan çizge <strong>bağlıdır</strong> (connected). Bir çizgenin bağlı alt gruplarına <strong>parça</strong> (component) denir. Örneğin ${1,2,3}$, ${4,5,6,7}$ ve ${8}$ olmak üzere üç parçalı bir çizge bağlı değildir.</p>

<p>Bir çizge bağlıysa ve $n$ düğüm ile $n-1$ kenardan oluşuyorsa bu çizge bir <strong>ağaçtır</strong> (tree). Ağaçta her iki düğüm arasında tam olarak bir yol vardır.</p>

<h3 id="kenar-yönleri">Kenar Yönleri</h3>

<p>Kenarların tek yönlü olduğu çizge <strong>yönlüdür</strong> (directed). Yönlü bir çizgede $3 \to 1 \to 2 \to 5$ yolu olabilirken $5$’ten $3$’e giden herhangi bir yol olmayabilir.</p>

<h3 id="kenar-ağırlıkları">Kenar Ağırlıkları</h3>

<p><strong>Ağırlıklı</strong> (weighted) bir çizgede her kenarın bir ağırlığı vardır; bu ağırlık genellikle uzunluk olarak yorumlanır. Ağırlıklı bir çizgedeki yolun uzunluğu, yoldaki kenarların ağırlıklarının toplamıdır. Örneğin $1 \to 2 \to 5$ yolunun uzunluğu 12, $1 \to 3 \to 4 \to 5$ yolunun uzunluğu 11 olabilir; burada ikinci yol daha kısadır.</p>

<h3 id="komşular-ve-dereceler">Komşular ve Dereceler</h3>

<p>İki düğüm arasında kenar varsa bunlar <strong>komşu</strong> (neighbor) düğümlerdir. Bir düğümün <strong>derecesi</strong> (degree), komşu sayısına eşittir.</p>

<p>$m$ kenarlı bir çizgenin toplam derece sayısı her zaman $2m$’dir; çünkü her kenar iki düğümün derecesini birer artırır. Bu nedenle derece toplamı daima çifttir.</p>

<p>Yönlü çizgelerde bir düğümün <strong>iç derecesi</strong> (indegree) o düğüme gelen kenar sayısını, <strong>dış derecesi</strong> (outdegree) ise o düğümden çıkan kenar sayısını verir.</p>

<p>Her düğümün derecesi $d$ ise çizge <strong>sıradan</strong> (regular), her düğüm birbirine bağlıysa (derece $n-1$) çizge <strong>tam</strong> (complete) olarak adlandırılır.</p>

<h3 id="boyamalar">Boyamalar</h3>

<p>Çizgeyi boyarken komşu düğümlerin farklı renk almasına dikkat edilir. Yalnızca iki renkle boyanabilen çizge <strong>iki parçalıdır</strong> (bipartite). Bir çizgenin iki parçalı olabilmesi için tek sayıda kenarlı herhangi bir döngü içermemesi gerekir.</p>

<p>Örneğin altı düğümlü bir çizge iki parçalıysa düğümler iki gruba ayrılıp her kenar gruplar arasında geçer; iki renkle boyanabilir. Ama tek sayıda kenarlı bir döngü içeriyorsa boyama mümkün olmaz.</p>

<p>Genel durumda bir çizgenin $k$ renkle boyanıp boyanamayacağını bulmak zordur. $k = 3$ için dahi bilinen verimli bir algoritma yoktur — bu problem <strong>NP-hard</strong>‘dır.</p>

<h3 id="basitlik">Basitlik</h3>

<p>Aynı düğümde başlayıp biten kenar (öz-döngü) veya iki düğüm arasında birden fazla kenar içeren çizge <strong>basit değildir</strong> (not simple). Genellikle çizgelerin basit olduğu kabul edilir.</p>

<h2 id="112-çizge-gösterimi">11.2 Çizge Gösterimi</h2>

<p>Algoritmalarda çizgeleri göstermenin birkaç yaygın yolu vardır. Veri yapısının seçimi çizgenin büyüklüğüne ve algoritmanın çizgeyi işleme biçimine göre değişir.</p>

<h3 id="komşuluk-listesi-gösterimi">Komşuluk Listesi Gösterimi</h3>

<p><strong>Komşuluk listesi</strong> (adjacency list) gösteriminde her $x$ düğümüne bir liste atanır; bu liste $x$’ten çıkan kenarların ulaştığı düğümleri içerir. Komşuluk listeleri çizgeleri göstermenin en popüler yoludur ve çoğu algoritma bu yöntemle verimli biçimde kodlanabilir.</p>

<p>Komşuluk listesini oluşturmanın pratik yolu vektörlerden oluşan bir dizi kullanmaktır:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">adj</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>
</code></pre></div></div>

<p>Örneğin aşağıdaki çizge için:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">adj</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</code></pre></div></div>

<p>Yönsüz (undirected) çizgelerde her kenar her iki yöne de eklenir. Ağırlıklı bir çizge için yapı şöyle güncellenir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">adj</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>
</code></pre></div></div>

<p>Bu durumda $a$ düğümünden $b$ düğümüne $w$ ağırlığında kenar varsa $a$’nın listesi $(b, w)$ çiftini içerir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">adj</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">push_back</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">});</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">push_back</span><span class="p">({</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">});</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">push_back</span><span class="p">({</span><span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">});</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="n">push_back</span><span class="p">({</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">});</span>
<span class="n">adj</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="n">push_back</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">});</span>
</code></pre></div></div>

<p>Komşuluk listesinin en büyük avantajı, bir düğümden ulaşılabilecek düğümlerin hızlıca dolaşılabilmesidir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">u</span> <span class="o">:</span> <span class="n">adj</span><span class="p">[</span><span class="n">s</span><span class="p">])</span> <span class="p">{</span>
    <span class="c1">// u düğümünü işle</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="komşuluk-matrisi-gösterimi">Komşuluk Matrisi Gösterimi</h3>

<p><strong>Komşuluk matrisi</strong> (adjacency matrix) gösterimi, çizgedeki kenarları iki boyutlu bir dizide tutar ve iki düğüm arasında kenar olup olmadığını $O(1)$ zamanda sorgular:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">adj</span><span class="p">[</span><span class="n">N</span><span class="p">][</span><span class="n">N</span><span class="p">];</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">adj[a][b] = 1</code> ise $a$’dan $b$’ye kenar vardır, <code class="language-plaintext highlighter-rouge">adj[a][b] = 0</code> ise yoktur. Ağırlıklı çizgelerde kenar ağırlığı da bu matriste tutulabilir.</p>

<p>Dezavantajı, $n^2$ elemanlık bir dizi gerektirmesidir. Büyük çizgeler için bellek açısından pratik değildir; özellikle matrisin büyük bölümü sıfırdan oluştuğunda (seyrek çizgeler).</p>

<h3 id="kenar-listesi-gösterimi">Kenar Listesi Gösterimi</h3>

<p><strong>Kenar listesi</strong> (edge list) gösterimi, çizgenin tüm kenarlarını rastgele bir sırada bir vektörde tutar. Algoritma tüm kenarları işliyorsa ve belirli bir düğümden başlayan kenarları bulmaya gerek yoksa bu gösterim uygundur.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">edges</span><span class="p">;</span>
</code></pre></div></div>

<p>Her $(a, b)$ çifti, $a$ düğümünden $b$ düğümüne bir kenar olduğunu gösterir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">});</span>
</code></pre></div></div>

<p>Ağırlıklı çizgeler için yapı üçlü demet olarak genişletilir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="n">tuple</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">edges</span><span class="p">;</span>
</code></pre></div></div>

<p>Her $(a, b, w)$ demeti, $a$’dan $b$’ye $w$ ağırlığında kenar olduğunu belirtir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">});</span>
<span class="n">edges</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">});</span>
</code></pre></div></div>

<h3 id="gösterimler-arasında-karşılaştırma">Gösterimler Arasında Karşılaştırma</h3>

<table>
  <thead>
    <tr>
      <th>Gösterim</th>
      <th>Bellek</th>
      <th>Kenar var mı?</th>
      <th>Düğümün komşuları</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Komşuluk listesi</td>
      <td>$O(n + m)$</td>
      <td>$O(\text{derece})$</td>
      <td>$O(\text{derece})$</td>
    </tr>
    <tr>
      <td>Komşuluk matrisi</td>
      <td>$O(n^2)$</td>
      <td>$O(1)$</td>
      <td>$O(n)$</td>
    </tr>
    <tr>
      <td>Kenar listesi</td>
      <td>$O(m)$</td>
      <td>$O(m)$</td>
      <td>$O(m)$</td>
    </tr>
  </tbody>
</table>

<p>Seyrek çizgelerde ($m \ll n^2$) komşuluk listesi tercih edilir. Yoğun çizgelerde ($m \approx n^2$) komşuluk matrisi pratik olabilir. Bellman-Ford gibi tüm kenarları tek tek işleyen algoritmalar için kenar listesi doğal bir seçimdir.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bazı eski derleyicilerde süslü parantez yerine <code class="language-plaintext highlighter-rouge">make_tuple</code> fonksiyonu kullanılmalıdır. Örneğin <code class="language-plaintext highlighter-rouge">{1, 2, 5}</code> yerine <code class="language-plaintext highlighter-rouge">make_tuple(1, 2, 5)</code> yazılır. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="çizge" /><category term="graph" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="yarışma" /><category term="veri-yapisi" /><category term="komşuluk-listesi" /><category term="komşuluk-matrisi" /><category term="kenar-listesi" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Çoğu kodlama sorusu bir çizge problemi olarak modellenip uygun bir çizge algoritmasıyla çözülebilir. Tipik bir örnek, bir ülkedeki yolları ve şehirleri temsil eden ağdır. Bazen çizge sorunun içinde gizli olduğundan fark edilmesi güçleşir. Bu bölümde çizgelerle ilgili temel kavramları ele alıp algoritmalarda çizgeleri göstermenin farklı yollarını inceleyeceğiz.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Aralık Sorguları</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-aralik-sorgulari" rel="alternate" type="text/html" title="Rekabetçi Programcı Aralık Sorguları" /><published>2026-04-02T00:00:00+00:00</published><updated>2026-04-02T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-aralik-sorgulari</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-aralik-sorgulari"><![CDATA[<p>Bir dizinin alt aralıklarında hızlıca sorgu yapmak rekabetçi programlamada sık karşılaşılan bir ihtiyaçtır. Bir <strong>aralık sorgusunda</strong> görev, bir dizinin belirli bir alt aralığında bir değeri hesaplamaktır. Tipik aralık sorguları şunlardır:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">sumq(a, b)</code>: $[a, b]$ aralığındaki sayıların toplamını bul</li>
  <li><code class="language-plaintext highlighter-rouge">minq(a, b)</code>: $[a, b]$ aralığındaki minimum sayıyı bul</li>
  <li><code class="language-plaintext highlighter-rouge">maxq(a, b)</code>: $[a, b]$ aralığındaki maksimum sayıyı bul</li>
</ul>

<p>Örneğin <code class="language-plaintext highlighter-rouge">[1, 3, 8, 4, 6, 1, 3, 4]</code> dizisinde $[3,6]$ aralığı için <code class="language-plaintext highlighter-rouge">sumq(3,6) = 14</code>, <code class="language-plaintext highlighter-rouge">minq(3,6) = 1</code>, <code class="language-plaintext highlighter-rouge">maxq(3,6) = 6</code> olur.</p>

<p>En basit yaklaşım aralık içindeki tüm elemanlara tek tek bakmaktır; bu $O(n)$ sürer. $q$ sorgu için toplam $O(nq)$ zaman gerekir. Hem $n$ hem de $q$ büyük olduğunda bu yavaş kalır. Neyse ki aralık sorgularını çok daha verimli yapmanın yolları vardır.</p>

<h2 id="91-statik-dizi-sorguları">9.1 Statik Dizi Sorguları</h2>

<p>Dizinin <strong>statik</strong> olduğu (sorgular sırasında değerlerin değişmediği) duruma ilk bakacağız. Bu durumda sorguları sabit zamanda yanıtlayan bir veri yapısı oluşturmak yeterlidir.</p>

<h3 id="toplam-sorguları">Toplam Sorguları</h3>

<p>Statik bir dizideki toplam sorgularını <strong>prefix toplam dizisi</strong> ile kolayca çözebiliriz. Prefix toplam dizisindeki $k$. konum, orijinal dizinin $[0, k]$ aralığının toplamına eşittir; yani <code class="language-plaintext highlighter-rouge">sumq(0, k)</code> değerini tutar. Bu dizi $O(n)$ zamanda oluşturulabilir.</p>

<p>Örneğin <code class="language-plaintext highlighter-rouge">[1, 3, 4, 8, 6, 1, 4, 2]</code> dizisine karşılık gelen prefix toplam dizisi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1  4  8  16  22  23  27  29
0  1  2   3   4   5   6   7
</code></pre></div></div>

<p>Herhangi bir <code class="language-plaintext highlighter-rouge">sumq(a, b)</code> değerini $O(1)$ zamanda şu formülle bulabiliriz:</p>

\[\text{sumq}(a, b) = \text{sumq}(0, b) - \text{sumq}(0, a-1)\]

<p>$\text{sumq}(0, -1) = 0$ tanımlaması sayesinde formül $a = 0$ için de geçerlidir. Örneğin:</p>

\[\text{sumq}(3, 6) = \text{sumq}(0, 6) - \text{sumq}(0, 2) = 27 - 8 = 19\]

<p>Bu fikir daha fazla boyuta da genelleşir. İki boyutlu bir prefix toplam dizisi oluşturularak herhangi bir dikdörtgen alt dizi toplamı $O(1)$ zamanda hesaplanabilir. Gri alt dizinin toplamı, $S(X)$’in sol üst köşeden $X$ pozisyonuna gelen dikdörtgen alt dizi toplamı olduğu varsayılarak şu formülle bulunur:</p>

\[S(A) - S(B) - S(C) + S(D)\]

<h3 id="minimum-sorgular">Minimum Sorgular</h3>

<p>Minimum sorgular, toplam sorgularına göre daha zordur. Yine de $O(n \log n)$’lik bir ön işleme ile minimum sorguları $O(1)$ zamanda yanıtlanabilir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. Minimum ve maksimum sorgular benzer şekilde ele alındığından yalnızca minimuma bakacağız.</p>

<p><strong>Fikir:</strong> Uzunluğu ikinin kuvveti olan tüm <code class="language-plaintext highlighter-rouge">minq(a, b)</code> değerleri önceden hesaplanır. Örneğin <code class="language-plaintext highlighter-rouge">[1, 3, 4, 8, 6, 1, 4, 2]</code> dizisi için bu değerler uzunluk 1, 2 ve 4’lük aralıklar için aşağıdaki gibidir:</p>

<table>
  <thead>
    <tr>
      <th>a</th>
      <th>b</th>
      <th>minq(a,b)</th>
      <th> </th>
      <th>a</th>
      <th>b</th>
      <th>minq(a,b)</th>
      <th> </th>
      <th>a</th>
      <th>b</th>
      <th>minq(a,b)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>0</td>
      <td>1</td>
      <td> </td>
      <td>0</td>
      <td>1</td>
      <td>1</td>
      <td> </td>
      <td>0</td>
      <td>3</td>
      <td>1</td>
    </tr>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>3</td>
      <td> </td>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td> </td>
      <td>1</td>
      <td>4</td>
      <td>3</td>
    </tr>
    <tr>
      <td>2</td>
      <td>2</td>
      <td>4</td>
      <td> </td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td> </td>
      <td>2</td>
      <td>5</td>
      <td>1</td>
    </tr>
    <tr>
      <td>3</td>
      <td>3</td>
      <td>8</td>
      <td> </td>
      <td>3</td>
      <td>4</td>
      <td>6</td>
      <td> </td>
      <td>3</td>
      <td>6</td>
      <td>1</td>
    </tr>
    <tr>
      <td>4</td>
      <td>4</td>
      <td>6</td>
      <td> </td>
      <td>4</td>
      <td>5</td>
      <td>1</td>
      <td> </td>
      <td>4</td>
      <td>7</td>
      <td>1</td>
    </tr>
    <tr>
      <td>5</td>
      <td>5</td>
      <td>1</td>
      <td> </td>
      <td>5</td>
      <td>6</td>
      <td>1</td>
      <td> </td>
      <td>0</td>
      <td>7</td>
      <td>1</td>
    </tr>
    <tr>
      <td>6</td>
      <td>6</td>
      <td>4</td>
      <td> </td>
      <td>6</td>
      <td>7</td>
      <td>2</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>7</td>
      <td>7</td>
      <td>2</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>Toplam $O(n \log n)$ değer, aşağıdaki özyinelemeli formülle hesaplanır ($w = (b - a + 1)/2$):</p>

\[\text{minq}(a, b) = \min(\text{minq}(a,\ a+w-1),\ \text{minq}(a+w,\ b))\]

<p>Sorgu anında $b - a + 1$ değerini geçmeyen en büyük ikinin kuvveti $k$ alınır ve:</p>

\[\text{minq}(a, b) = \min(\text{minq}(a,\ a+k-1),\ \text{minq}(b-k+1,\ b))\]

<p>formülü $O(1)$ zamanda sonucu verir. Örneğin $[1, 6]$ aralığında uzunluk 6 için $k = 4$ seçilir; $[1,4]$ ve $[3,6]$ aralıklarının birleşimi olarak $\text{minq}(1,6) = \min(3, 1) = 1$ bulunur.</p>

<h2 id="92-i̇kili-i̇ndisli-ağaç-binary-indexed-tree">9.2 İkili İndisli Ağaç (Binary Indexed Tree)</h2>

<p><strong>İkili indisli ağaç</strong> ya da <strong>Fenwick ağacı</strong><sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, prefix toplam dizisinin dinamik versiyonu olarak düşünülebilir. $O(\log n)$ zamanda iki operasyonu destekler: aralık toplamı sorgusu ve tek eleman güncellemesi. Prefix toplam dizisinden farkı, her güncellemeden sonra diziyi baştan oluşturmak yerine yalnızca ilgili konumları güncellemesidir.</p>

<h3 id="yapı">Yapı</h3>

<p>Bu bölümde diziler 1’den indislenir. $p(k)$, $k$’yı tam bölen en büyük 2’nin kuvveti olsun. İkili indisli ağaç <code class="language-plaintext highlighter-rouge">tree</code> dizisinde şu şekilde tutulur:</p>

\[\text{tree}[k] = \text{sumq}(k - p(k) + 1,\ k)\]

<p>Yani her $k$ konumu, uzunluğu $p(k)$ olan ve $k$’da biten aralığın toplamını tutar. Örneğin $p(6) = 2$ olduğundan <code class="language-plaintext highlighter-rouge">tree[6]</code>, <code class="language-plaintext highlighter-rouge">sumq(5, 6)</code> değerini saklar.</p>

<p><code class="language-plaintext highlighter-rouge">[1, 3, 4, 8, 6, 1, 4, 2]</code> dizisine karşılık gelen ikili indisli ağaç:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1  4  4  16  6  7  4  29
1  2  3   4  5  6  7   8
</code></pre></div></div>

<p>$[1, k]$ aralığı her zaman ağaçta toplamları tutulan $O(\log n)$ alt aralığa bölünebildiğinden <code class="language-plaintext highlighter-rouge">sumq(1, k)</code> değeri $O(\log n)$ zamanda hesaplanabilir. Örneğin:</p>

\[\text{sumq}(1, 7) = \text{sumq}(1,4) + \text{sumq}(5,6) + \text{sumq}(7,7) = 16 + 7 + 4 = 27\]

<p>$a &gt; 1$ durumunda ise:</p>

\[\text{sumq}(a, b) = \text{sumq}(1, b) - \text{sumq}(1, a-1)\]

<h3 id="i̇mplementasyon">İmplementasyon</h3>

<p>İkili indisli ağaç, bit operasyonları kullanılarak verimli biçimde kodlanabilir. $p(k)$ değeri şöyle hesaplanır:</p>

\[p(k) = k\ \&amp;\ {-k}\]

<p><code class="language-plaintext highlighter-rouge">sumq(1, k)</code> değerini hesaplayan fonksiyon:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">sum</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">k</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">s</span> <span class="o">+=</span> <span class="n">tree</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
        <span class="n">k</span> <span class="o">-=</span> <span class="n">k</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">k</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">s</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>$k$. pozisyondaki elemanı $x$ kadar artıran fonksiyon:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">add</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">k</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tree</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">+=</span> <span class="n">x</span><span class="p">;</span>
        <span class="n">k</span> <span class="o">+=</span> <span class="n">k</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">k</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Her iki fonksiyon da $O(\log n)$ zamanda çalışır; her adımda $O(\log n)$ farklı konuma erişilir ve her hareket $O(1)$ zaman alır.</p>

<h2 id="93-segment-ağacı">9.3 Segment Ağacı</h2>

<p><strong>Segment ağacı</strong><sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> iki operasyonu destekleyen genel amaçlı bir veri yapısıdır: aralık sorgusu ve tek eleman güncellemesi. İkili indisli ağaca kıyasla daha geneldir; yalnızca toplam değil minimum, maksimum, EBOB, bit operasyonları (AND, OR, XOR) gibi pek çok sorguyu da $O(\log n)$ zamanda destekler. Bununla birlikte daha fazla bellek kullanır ve implementasyonu biraz daha karmaşıktır<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>.</p>

<h3 id="yapı-1">Yapı</h3>

<p>Segment ağacı bir ikili ağaçtır; en alt seviyedeki yapraklar dizi elemanlarına, iç düğümler ise aralık sorgularına gereken bilgilere karşılık gelir. Dizinin boyutunun ikinin kuvveti olduğu ve sıfır tabanlı indis kullanıldığı varsayılır; gerekirse fazladan eleman eklenebilir.</p>

<p>Toplam sorgularını destekleyen <code class="language-plaintext highlighter-rouge">[5, 8, 6, 3, 2, 7, 2, 6]</code> dizisine karşılık gelen segment ağacı:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>           39
       22      17
    13    9   9   8
   5 8  6 3  2 7 2 6
</code></pre></div></div>

<p>Her iç düğüm, kendisine karşılık gelen aralığın elemanlarının toplamını tutar ve bu değer sol ile sağ çocuklarının toplamına eşittir.</p>

<p>Herhangi bir $[a, b]$ aralığı, değerleri ağaç düğümlerinde bulunan $O(\log n)$ alt aralığa bölünebilir. Örneğin $[2, 7]$ aralığı için <code class="language-plaintext highlighter-rouge">sumq(2,7) = 9 + 17 = 26</code>. Her seviyeden en fazla iki düğüm kullanıldığından toplam düğüm sayısı $O(\log n)$ olur.</p>

<p>Bir dizi güncellemesinden sonra değişen düğümdeki yapraktan köke giden yoldaki tüm iç düğümler güncellenir; bu yol $O(\log n)$ düğüm içerir.</p>

<h3 id="i̇mplementasyon-1">İmplementasyon</h3>

<p>Segment ağacı, $n$ elemanlı bir dizi için $2n$ elemanlı bir dizi içinde tutulur. <code class="language-plaintext highlighter-rouge">tree[1]</code> köke, <code class="language-plaintext highlighter-rouge">tree[2]</code> ve <code class="language-plaintext highlighter-rouge">tree[3]</code> köke bağlı çocuklara karşılık gelir. <code class="language-plaintext highlighter-rouge">tree[n]</code>‘den <code class="language-plaintext highlighter-rouge">tree[2n-1]</code>‘e kadar olan değerler yaprak düğümlerdir.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>39 22 17 13  9  9  8  5  8  6  3  2  7  2  6
 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
</code></pre></div></div>

<p>Bu gösterimde <code class="language-plaintext highlighter-rouge">tree[k]</code>‘nin ebeveyni <code class="language-plaintext highlighter-rouge">tree[⌊k/2⌋]</code>, çocukları <code class="language-plaintext highlighter-rouge">tree[2k]</code> ve <code class="language-plaintext highlighter-rouge">tree[2k+1]</code> olur.</p>

<p><code class="language-plaintext highlighter-rouge">sumq(a, b)</code> değerini hesaplayan fonksiyon:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">sum</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">a</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span> <span class="n">b</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">a</span> <span class="o">&lt;=</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="n">s</span> <span class="o">+=</span> <span class="n">tree</span><span class="p">[</span><span class="n">a</span><span class="o">++</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">b</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">s</span> <span class="o">+=</span> <span class="n">tree</span><span class="p">[</span><span class="n">b</span><span class="o">--</span><span class="p">];</span>
        <span class="n">a</span> <span class="o">/=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">b</span> <span class="o">/=</span> <span class="mi">2</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">s</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>$k$. pozisyondaki elemanı $x$ kadar artıran fonksiyon:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">add</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">k</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span>
    <span class="n">tree</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">+=</span> <span class="n">x</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">k</span> <span class="o">/=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">k</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">k</span> <span class="o">/=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tree</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">tree</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">k</span><span class="p">]</span> <span class="o">+</span> <span class="n">tree</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">k</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Her iki fonksiyon da $O(\log n)$ zamanda çalışır.</p>

<h3 id="diğer-sorgular">Diğer Sorgular</h3>

<p>Segment ağaçları, bir aralığı iki parçaya bölmenin mümkün olduğu tüm sorgular için kullanılabilir: minimum/maksimum, EBOB, AND/OR/XOR gibi bit operasyonları. Örneğin minimum sorgularını destekleyen ağaçta her düğüm, ilgili aralığın minimumunu tutar.</p>

<p>Segment ağacının ikili ağaç yapısı, ikili arama uygulamasına da olanak tanır. Örneğin minimum sorgularını destekleyen bir ağaçta en küçük değere sahip eleman $O(\log n)$ zamanda bulunabilir; kökten yapraklara doğru inen yolda minimum değer izlenerek bu eleman tespit edilir.</p>

<h2 id="94-ek-teknikler">9.4 Ek Teknikler</h2>

<h3 id="i̇ndis-sıkıştırması">İndis Sıkıştırması</h3>

<p>Bu bölümdeki veri yapıları ardışık sayılardan oluşan diziler üzerine kuruludur. Büyük indisler gerektiğinde (örneğin $10^9$ indisi) bellek yetersizliği sorunu ortaya çıkar. Bu sınır <strong>indis sıkıştırması</strong> ile aşılabilir.</p>

<p>Buradaki fikir, orijinal indisleri $1, 2, 3, \ldots$ gibi ardışık indislerle değiştirmektir. Bunun için algoritma çalışmaya başlamadan önce tüm indislerin bilinmesi gerekir. Sıkıştırma fonksiyonu $c$ sıralamayı korumalıdır: eğer $a &lt; b$ ise $c(a) &lt; c(b)$ olmalıdır. Bu sayede indisler sıkıştırılmış olsa bile sorgular doğru çalışır.</p>

<p>Örneğin orijinal indisler $8$, $555$ ve $10^9$ ise yeni indisler:</p>

\[c(8) = 1, \quad c(555) = 2, \quad c(10^9) = 3\]

<h3 id="aralık-güncellemeleri">Aralık Güncellemeleri</h3>

<p>Şimdiye kadar aralık sorguları yapıp tek eleman güncelleyebilen yapılar incelendi. Tam tersi durumda — aralık güncelleyip tek eleman okumada — <strong>fark dizisi</strong> kullanılır. Fark dizisindeki her değer, mevcut değer ile orijinal değer arasındaki farkı tutar; dolayısıyla orijinal dizi, fark dizisinin prefix toplam dizisidir.</p>

<p>Örneğin <code class="language-plaintext highlighter-rouge">[3, 3, 1, 1, 1, 5, 2, 2]</code> dizisinin fark dizisi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3  0  -2  0  0  4  -3  0
0  1   2  3  4  5   6  7
</code></pre></div></div>

<p>Fark dizisinin avantajı, orijinal dizideki bir aralığı yalnızca iki elemanı değiştirerek güncelleyebilmesidir. Örneğin $[1, 4]$ aralığındaki tüm değerleri 5 artırmak için fark dizisindeki 1. konumu 5 artırıp 5. konumu 5 azaltmak yeterlidir:</p>

\[\text{Sonuç:}\quad 3 \; 5 \; {-2} \; 0 \; 0 \; {-1} \; {-3} \; 0\]

<p>Genel olarak $[a, b]$ aralığını $x$ kadar artırmak için $a$. konumu $x$ artırıp $b+1$. konumu $x$ azaltırız. Tek eleman güncellemesi ve toplam sorgusunun birlikte gerektiği bu yapı için ikili indisli ağaç veya segment ağacı kullanılır.</p>

<p>Hem aralık sorgularını hem de aralık güncellemelerini bir arada destekleyen daha gelişmiş yapılar Bölüm 28’de ele alınacaktır.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bu teknik sparse table metodu olarak da bilinir. Daha gelişmiş teknikler mevcuttur; ön işleme $O(n)$ zamanda tamamlanabilir, fakat bu yöntemler rekabetçi programlamada nadiren gereklidir. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>İkili indisli ağaç yapısı P. M. Fenwick tarafından 1994’te tanıtılmıştır. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>Bu bölümdeki aşağıdan yukarıya implementasyon, 1970’lerin sonlarında geometri problemleri için kullanılan benzer yapılardan esinlenilmiştir. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>Aslında iki tane ikili indisli ağaç kullanılarak minimum sorguları da yapılabilir; ancak bu yöntem segment ağacına kıyasla daha karmaşıktır. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="aralik-sorgusu" /><category term="range-query" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="segment-agaci" /><category term="fenwick" /><category term="prefix-toplam" /><category term="veri-yapisi" /><category term="kitap" /><summary type="html"><![CDATA[Bir dizinin alt aralıklarında hızlıca sorgu yapmak rekabetçi programlamada sık karşılaşılan bir ihtiyaçtır. Bir aralık sorgusunda görev, bir dizinin belirli bir alt aralığında bir değeri hesaplamaktır. Tipik aralık sorguları şunlardır:]]></summary></entry><entry><title type="html">Rekabetçi Programcı Bit Manipülasyonu</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-bit-manipulasyonu" rel="alternate" type="text/html" title="Rekabetçi Programcı Bit Manipülasyonu" /><published>2026-04-02T00:00:00+00:00</published><updated>2026-04-02T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-bit-manipulasyonu</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-bit-manipulasyonu"><![CDATA[<p>Bilgisayar programlarındaki tüm veriler bit olarak yani 0 ve 1 sayıları biçiminde tutulur. Bu bölüm tam sayıların bit gösterimlerini açıklayıp bit operasyonlarının kullanıldığı örneklere değinecektir. Algoritma programlamasında bit manipülasyonunu kullanmanın pek çok farklı yolu vardır.</p>

<h2 id="101-bit-gösterimi">10.1 Bit Gösterimi</h2>

<p>Programlamada bir $n$ bitlik tamsayı, $n$ bitten oluşan bir binary sayısı olarak tutulur. Örneğin C++’da <code class="language-plaintext highlighter-rouge">int</code> 32-bit olup her <code class="language-plaintext highlighter-rouge">int</code> sayısı 32 bitten oluşur.</p>

<p><code class="language-plaintext highlighter-rouge">int 43</code> sayısının bit gösterimi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000000000000000000000101011
</code></pre></div></div>

<p>Gösterimdeki bitler sağdan sola sıralanır. Bit gösterimi $b_k \cdots b_2 b_1 b_0$ olan bir sayıya dönüştürmek için:</p>

\[b_k 2^k + \ldots + b_2 2^2 + b_1 2^1 + b_0 2^0\]

<p>Örneğin $1 \cdot 2^5 + 1 \cdot 2^3 + 1 \cdot 2^1 + 1 \cdot 2^0 = 43$.</p>

<h3 id="signed-ve-unsigned-gösterim">Signed ve Unsigned Gösterim</h3>

<p>Bir sayının bit gösterimi ya <strong>signed</strong> ya da <strong>unsigned</strong> olur.</p>

<p><strong>Signed</strong> gösterimde hem negatif hem pozitif sayılar tutulabilir. $n$ bitlik bir signed değişken $-2^{n-1}$ ile $2^{n-1}-1$ arasındaki herhangi bir tamsayıyı tutabilir. C++’da <code class="language-plaintext highlighter-rouge">int</code> signed’dır; $-2^{31}$ ile $2^{31}-1$ arasındaki değerleri alabilir. Signed gösterimde ilk bit işaret bitidir (negatif olmayan sayılar için 0, negatifler için 1). Negatifler <strong>ikinin tümleyeni</strong> (Two’s Complement) ile gösterilir: tüm bitler tersine çevrilir ve 1 eklenir. Örneğin <code class="language-plaintext highlighter-rouge">int -43</code> sayısının bit gösterimi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>11111111111111111111111111010101
</code></pre></div></div>

<p><strong>Unsigned</strong> gösterimde yalnızca negatif olmayan sayılar kullanılır; buna karşın üst sınır daha yüksektir. $n$ bitlik bir unsigned değişken $0$ ile $2^n - 1$ arasındaki değerleri tutabilir. C++’da <code class="language-plaintext highlighter-rouge">unsigned int</code> değişkeni $0$ ile $2^{32}-1$ arasında olabilir.</p>

<p>İki gösterim arasındaki ilişki: bir signed sayı $-x$, unsigned sayı $2^n - x$’e eşittir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="mi">43</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// -43</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 4294967253</span>
</code></pre></div></div>

<h3 id="overflow">Overflow</h3>

<p>Bir sayı bit gösteriminin üst sınırını aşarsa <strong>overflow</strong> oluşur. Signed gösterimde $2^{n-1}-1$’den sonraki sayı $-2^{n-1}$ olur; unsigned gösterimde $2^n - 1$’den sonraki sayı $0$ olur.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2147483647</span><span class="p">;</span> <span class="c1">// 2^31 - 1</span>
<span class="n">x</span><span class="o">++</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// -2147483648</span>
</code></pre></div></div>

<h2 id="102-bit-operasyonları">10.2 Bit Operasyonları</h2>

<h3 id="ve-and-operasyonu">Ve (AND) Operasyonu</h3>

<p><code class="language-plaintext highlighter-rouge">x &amp; y</code>, hem $x$’te hem $y$’de 1 olan bitlerde 1, diğerlerinde 0 içerir. Örneğin $22\ \&amp;\ 26 = 18$:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  10110  (22)
&amp; 11010  (26)
= 10010  (18)
</code></pre></div></div>

<p>AND operasyonu ile bir $x$ sayısının çift olup olmadığını kontrol edebiliriz: $x$ çiftse <code class="language-plaintext highlighter-rouge">x &amp; 1 = 0</code>, tekse <code class="language-plaintext highlighter-rouge">x &amp; 1 = 1</code>. Genel olarak <code class="language-plaintext highlighter-rouge">x &amp; (2^k - 1) = 0</code> ise $x$, $2^k$’ya bölünebiliyordur.</p>

<h3 id="veya-or-operasyonu">Veya (OR) Operasyonu</h3>

<table>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">x | y</code>, $x$ veya $y$’den en az birinde 1 biti olan pozisyonlarda 1 içerir. Örneğin $22\</td>
      <td>\ 26 = 30$:</td>
    </tr>
  </tbody>
</table>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  10110  (22)
| 11010  (26)
= 11110  (30)
</code></pre></div></div>

<h3 id="xor-operasyonu">XOR Operasyonu</h3>

<p><code class="language-plaintext highlighter-rouge">x ^ y</code>, $x$ ya da $y$’den yalnızca birinde 1 bulunan pozisyonlarda 1 içerir. Örneğin $22\ \hat{}\ 26 = 12$:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  10110  (22)
^ 11010  (26)
= 01100  (12)
</code></pre></div></div>

<h3 id="ters-not-operasyonu">Ters (NOT) Operasyonu</h3>

<p><code class="language-plaintext highlighter-rouge">~x</code>, $x$’in tüm bitlerini tersine çevirir. $\sim x = -x - 1$ formülü geçerlidir. Örneğin <code class="language-plaintext highlighter-rouge">~29 = -30</code>. 32-bit <code class="language-plaintext highlighter-rouge">int</code> için:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> x =  29   00000000000000000000000000011101
~x = -30   11111111111111111111111111100010
</code></pre></div></div>

<h3 id="bit-kaydırması">Bit Kaydırması</h3>

<p>Sol kaydırma <code class="language-plaintext highlighter-rouge">x &lt;&lt; k</code>, sayıya $k$ tane 0 biti ekler ($x \cdot 2^k$). Sağ kaydırma <code class="language-plaintext highlighter-rouge">x &gt;&gt; k</code>, sayıdan son $k$ biti çıkarır ($\lfloor x / 2^k \rfloor$). Örneğin $14 \ll 2 = 56$ (<code class="language-plaintext highlighter-rouge">1110</code> → <code class="language-plaintext highlighter-rouge">111000</code>) ve $49 \gg 3 = 6$ (<code class="language-plaintext highlighter-rouge">110001</code> → <code class="language-plaintext highlighter-rouge">110</code>).</p>

<h3 id="uygulamalar">Uygulamalar</h3>

<p><code class="language-plaintext highlighter-rouge">1 &lt;&lt; k</code> formundaki sayı, yalnızca $k$. pozisyonda 1 biti içerir; bu sayede tek bitlere erişebiliriz. $x$’in $k$. biti, <code class="language-plaintext highlighter-rouge">x &amp; (1 &lt;&lt; k)</code> sıfır olmadığında 1’dir. Aşağıdaki kod bir <code class="language-plaintext highlighter-rouge">int x</code> sayısının bit gösterimini yazdırır:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">31</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">))</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1"</span><span class="p">;</span>
    <span class="k">else</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"0"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Benzer fikirlerle bitleri değiştirmek de mümkündür:</p>

<table>
  <thead>
    <tr>
      <th>İşlem</th>
      <th>Formül</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$k$. biti 1’e çevir</td>
      <td><code class="language-plaintext highlighter-rouge">x \| (1 &lt;&lt; k)</code></td>
    </tr>
    <tr>
      <td>$k$. biti 0’a çevir</td>
      <td><code class="language-plaintext highlighter-rouge">x &amp; ~(1 &lt;&lt; k)</code></td>
    </tr>
    <tr>
      <td>$k$. biti tersine çevir</td>
      <td><code class="language-plaintext highlighter-rouge">x ^ (1 &lt;&lt; k)</code></td>
    </tr>
    <tr>
      <td>Son 1 bitini sıfırla</td>
      <td><code class="language-plaintext highlighter-rouge">x &amp; (x-1)</code></td>
    </tr>
    <tr>
      <td>Son 1 biti hariç tümünü sıfırla</td>
      <td><code class="language-plaintext highlighter-rouge">x &amp; -x</code></td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">x &amp; (x-1) = 0</code> ise $x$ pozitif sayısı ikinin kuvvetidir.</p>

<h3 id="g-yerleşik-fonksiyonları">g++ Yerleşik Fonksiyonları</h3>

<p>g++ derleyicisi bit sayımı için hazır fonksiyonlar sunar:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5328</span><span class="p">;</span> <span class="c1">// 00000000000000000000010100110100000</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_clz</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>      <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 19  (baştaki 0 sayısı)</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_ctz</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>      <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 4   (sondaki 0 sayısı)</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 5   (1 bit sayısı)</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_parity</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>   <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 1   (1 bitlerinin tek/çift paritesi)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">long long</code> sayılar için aynı fonksiyonların <code class="language-plaintext highlighter-rouge">ll</code> ön ekli versiyonları kullanılır: <code class="language-plaintext highlighter-rouge">__builtin_popcountll</code> vb.</p>

<h2 id="103-kümeleri-göstermek">10.3 Kümeleri Göstermek</h2>

<p>${0, 1, 2, \ldots, n-1}$ kümesinin her alt kümesi, $n$ bitten oluşan bir tamsayıyla gösterilebilir. 1 bitleri alt kümeye ait elemanları belirtir. Bu yöntem son derece verimlidir: her eleman yalnızca bir bit hafıza gerektirir ve küme operasyonları doğrudan bit operasyonlarına dönüşür.</p>

<p>Örneğin <code class="language-plaintext highlighter-rouge">int</code> 32-bit olduğundan bir <code class="language-plaintext highlighter-rouge">int</code> sayısı, ${0, 1, \ldots, 31}$ kümesinin herhangi bir alt kümesini gösterebilir. ${1, 3, 4, 8}$ kümesinin bit gösterimi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000000000000000000100011010
</code></pre></div></div>

<p>Bu $2^8 + 2^4 + 2^3 + 2^1 = 282$ sayısına karşılık gelir.</p>

<h3 id="küme-i̇mplementasyonu">Küme İmplementasyonu</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">x</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// {1}</span>
<span class="n">x</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">);</span>  <span class="c1">// {1, 3}</span>
<span class="n">x</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">);</span>  <span class="c1">// {1, 3, 4}</span>
<span class="n">x</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">);</span>  <span class="c1">// {1, 3, 4, 8}</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 4</span>
</code></pre></div></div>

<p>Kümeye ait elemanları yazdırmak:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">))</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// çıktı: 1 3 4 8</span>
</code></pre></div></div>

<h3 id="küme-operasyonları">Küme Operasyonları</h3>

<table>
  <thead>
    <tr>
      <th>Küme işlemi</th>
      <th>Bit işlemi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kesişim $a \cap b$</td>
      <td><code class="language-plaintext highlighter-rouge">a &amp; b</code></td>
    </tr>
    <tr>
      <td>Birleşim $a \cup b$</td>
      <td><code class="language-plaintext highlighter-rouge">a \| b</code></td>
    </tr>
    <tr>
      <td>Tümlayan $\bar{a}$</td>
      <td><code class="language-plaintext highlighter-rouge">~a</code></td>
    </tr>
    <tr>
      <td>Fark $a \setminus b$</td>
      <td><code class="language-plaintext highlighter-rouge">a &amp; (~b)</code></td>
    </tr>
  </tbody>
</table>

<p>Örneğin $x = {1,3,4,8}$ ve $y = {3,6,8,9}$ için birleşim $z = x \cup y = {1,3,4,6,8,9}$:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">1</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">4</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">8</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">6</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">8</span><span class="p">)</span><span class="o">|</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">9</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">|</span> <span class="n">y</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">z</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 6</span>
</code></pre></div></div>

<h3 id="altkümelerden-geçmek">Altkümelerden Geçmek</h3>

<p>${0, 1, \ldots, n-1}$ kümesinin tüm alt kümelerinden geçmek:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// b alt kümesini işle</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Tam $k$ elemanlı alt kümelerden geçmek:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="n">k</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// b alt kümesini işle</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bir $x$ kümesinin tüm alt kümelerinden geçmek:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">do</span> <span class="p">{</span>
    <span class="c1">// b alt kümesini işle</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="n">b</span> <span class="o">-</span> <span class="n">x</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">x</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="104-bit-optimizasyonu">10.4 Bit Optimizasyonu</h2>

<p>Çoğu algoritma bit operasyonları ile optimize edilebilir. Bu optimizasyonlar zaman karmaşıklığını değiştirmez; ancak gerçek çalışma süresinde büyük fark yaratabilir.</p>

<h3 id="hamming-mesafeleri">Hamming Mesafeleri</h3>

<p><strong>Hamming mesafesi</strong> $\text{hamming}(a, b)$, eşit uzunluktaki $a$ ve $b$ yazılarının farklı olduğu konum sayısıdır. Örneğin $\text{hamming}(01101,\ 11001) = 2$.</p>

<p>Her biri $k$ uzunluğunda $n$ adet bit yazısından oluşan bir listede minimum Hamming mesafesini bulmak istersek naif çözüm $O(n^2 k)$ sürer:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">hamming</span><span class="p">(</span><span class="n">string</span> <span class="n">a</span><span class="p">,</span> <span class="n">string</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">k</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="n">d</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">d</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>$k \leq 32$ ise yazıları <code class="language-plaintext highlighter-rouge">int</code> olarak tutup XOR + popcount ile çok daha hızlı hesaplayabiliriz:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">hamming</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">a</span> <span class="o">^</span> <span class="n">b</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>XOR operasyonu, $a$ ve $b$’nin farklı olduğu pozisyonlarda 1 içeren bir sayı üretir; <code class="language-plaintext highlighter-rouge">__builtin_popcount</code> ise bu 1 bitlerini sayar.</p>

<p>30 uzunluğundaki 10.000 rastgele bit yazısından oluşan bir listede bu iki yaklaşım karşılaştırılmış; naif yöntem 13.5 saniye, bit optimize versiyonu ise yalnızca <strong>0.5 saniye</strong> sürmüştür. Yaklaşık <strong>27 kat</strong> hızlanma.</p>

<h3 id="alt-izgaraları-saymak">Alt Izgaraları Saymak</h3>

<p>$n \times n$’lik bir ızgarada her kare ya siyah (1) ya da beyaz (0) olsun. Tüm köşeleri siyah olan alt ızgara sayısını bulmak istiyoruz.</p>

<p>Naif $O(n^3)$ çözümde tüm $O(n^2)$ satır çifti $(a, b)$ için her iki satırda aynı anda siyah olan sütunları sayıp $\binom{\text{count}}{2}$ ile çarpıyoruz:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">color</span><span class="p">[</span><span class="n">a</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">color</span><span class="p">[</span><span class="n">b</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="n">count</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bit optimizasyonunda ızgarayı $N$ sütunluk bloklara bölüp her satırı $N$-bitlik tamsayı listesi olarak tutarız. Böylece $N$ sütunu aynı anda AND + popcount ile işleyebiliriz:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">/</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">count</span> <span class="o">+=</span> <span class="n">__builtin_popcount</span><span class="p">(</span><span class="n">color</span><span class="p">[</span><span class="n">a</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">color</span><span class="p">[</span><span class="n">b</span><span class="p">][</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu sayede algoritma $O(n^3/N)$ zamana düşer. $2500 \times 2500$ rastgele ızgarada yapılan karşılaştırmada: orijinal kod 29.6 saniye, $N=32$ (int) ile 3.1 saniye, $N=64$ (long long) ile <strong>1.7 saniye</strong> sürmüştür.</p>

<h2 id="105-dinamik-programlama">10.5 Dinamik Programlama</h2>

<p>Bit operasyonları, elemanların alt kümelerini içeren dinamik programlama algoritmalarını hem verimli hem de temiz biçimde yazmamızı sağlar; çünkü durumları birer sayı olarak tutabiliriz.</p>

<h3 id="optimal-seçim">Optimal Seçim</h3>

<p>$k$ ürünün $n$ gündeki fiyatları verildiğinde her ürünü tam bir kez ve günde en fazla bir ürün alarak minimum toplam ücret nedir?</p>

<p>$\text{price}[x][d]$, $x$ ürününün $d$. gündeki fiyatı olsun. $\text{total}(S, d)$ ise $S$ alt kümesindeki ürünleri $d$. güne kadar almayı sağlayan minimum toplam ücret olsun. Hedef $\text{total}({0 \ldots k-1},\ n-1)$ değeridir.</p>

<p>Temel durumlar: $\text{total}(\emptyset, d) = 0$ ve $\text{total}({x}, 0) = \text{price}[x][0]$. Genel özyineleme:</p>

\[\text{total}(S, d) = \min\!\Bigl(\text{total}(S, d-1),\ \min_{x \in S}\bigl(\text{total}(S \setminus \{x\}, d-1) + \text{price}[x][d]\bigr)\Bigr)\]

<p>Dinamik programlama ile implementasyon:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">total</span><span class="p">[</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">K</span><span class="p">][</span><span class="n">N</span><span class="p">];</span>

<span class="c1">// d = 0 başlangıç durumları</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">k</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">total</span><span class="p">[</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">x</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">price</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span>
<span class="p">}</span>

<span class="c1">// DP geçişi</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">d</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">d</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">s</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">k</span><span class="p">);</span> <span class="n">s</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">total</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="n">d</span><span class="p">]</span> <span class="o">=</span> <span class="n">total</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="n">d</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">k</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">x</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">total</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="n">d</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">total</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="n">d</span><span class="p">],</span>
                    <span class="n">total</span><span class="p">[</span><span class="n">s</span> <span class="o">^</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">x</span><span class="p">)][</span><span class="n">d</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">price</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">d</span><span class="p">]);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Algoritmanın zaman karmaşıklığı $O(n \cdot 2^k \cdot k)$ olur.</p>

<h3 id="permütasyonlardan-altkümelere">Permütasyonlardan Altkümelere</h3>

<p>Permütasyonlar yerine altkümelere bakmak büyük avantaj sağlayabilir: $n = 20$ için $n! \approx 2.4 \times 10^{18}$ iken $2^n \approx 10^6$’dır<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p>Örnek problem: Maksimum kapasitesi $x$ olan bir asansörle $n$ insanı en az kaç turda taşıyabiliriz?</p>

<p>$\text{rides}(S)$ = $S$ alt kümesini taşımak için gereken minimum tur sayısı, $\text{last}(S)$ = son turdaki minimum toplam ağırlık olsun. Hedef $\text{rides}({0 \ldots n-1})$.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">best</span><span class="p">[</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">N</span><span class="p">];</span>
<span class="n">best</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">s</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">s</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">best</span><span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">auto</span> <span class="n">option</span> <span class="o">=</span> <span class="n">best</span><span class="p">[</span><span class="n">s</span> <span class="o">^</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">)];</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">option</span><span class="p">.</span><span class="n">second</span> <span class="o">+</span> <span class="n">weight</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
                <span class="c1">// p'yi mevcut tura ekle</span>
                <span class="n">option</span><span class="p">.</span><span class="n">second</span> <span class="o">+=</span> <span class="n">weight</span><span class="p">[</span><span class="n">p</span><span class="p">];</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="c1">// p için yeni tur oluştur</span>
                <span class="n">option</span><span class="p">.</span><span class="n">first</span><span class="o">++</span><span class="p">;</span>
                <span class="n">option</span><span class="p">.</span><span class="n">second</span> <span class="o">=</span> <span class="n">weight</span><span class="p">[</span><span class="n">p</span><span class="p">];</span>
            <span class="p">}</span>
            <span class="n">best</span><span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">best</span><span class="p">[</span><span class="n">s</span><span class="p">],</span> <span class="n">option</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Döngü, $S_1 \subseteq S_2$ olduğunda $S_1$’i her zaman $S_2$’den önce işler; bu sayede DP değerleri doğru sırada hesaplanır. Zaman karmaşıklığı $O(2^n \cdot n)$.</p>

<h3 id="altkümeleri-saymak">Altkümeleri Saymak</h3>

<p>Her $S \subseteq X$ alt kümesi için bir $\text{value}[S]$ tamsayısı tanımlansın. Her $S$ için alt kümelerinin değer toplamını hesaplamak istiyoruz:</p>

\[\text{sum}(S) = \sum_{A \subseteq S} \text{value}[A]\]

<p>Naif çözüm tüm $O(2^{2n})$ alt küme çiftlerini dener. Daha akıllı yaklaşım için $\text{partial}(S, k)$, $S$’ten yalnızca $0 \ldots k$ elemanlarının çıkarılabildiği durumdaki toplamı versin:</p>

\[\text{partial}(S, k) = \begin{cases} \text{value}[S] &amp; k \notin S \text{ veya } k = -1 \\ \text{partial}(S, k-1) + \text{partial}(S \setminus \{k\}, k-1) &amp; k \in S \end{cases}\]

<p>$\text{sum}(S) = \text{partial}(S, n-1)$ olduğundan dinamik programlama ile aşağıdaki $O(2^n \cdot n)$ implementasyon elde edilir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">sum</span><span class="p">[</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">N</span><span class="p">];</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">s</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">s</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">sum</span><span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">[</span><span class="n">s</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">s</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">s</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">k</span><span class="p">))</span> <span class="n">sum</span><span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">+=</span> <span class="n">sum</span><span class="p">[</span><span class="n">s</span> <span class="o">^</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">k</span><span class="p">)];</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Her $k$ adımında <code class="language-plaintext highlighter-rouge">sum</code> dizisi yeniden kullanılır; böyle verimli ve sade bir implementasyon elde edilir.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bu teknik 1962 yılında M. Held ve R. M. Karp tarafından bulunmuştur. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="bit" /><category term="bit-manipulasyonu" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="dinamik-programlama" /><category term="altküme" /><category term="optimizasyon" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Bilgisayar programlarındaki tüm veriler bit olarak yani 0 ve 1 sayıları biçiminde tutulur. Bu bölüm tam sayıların bit gösterimlerini açıklayıp bit operasyonlarının kullanıldığı örneklere değinecektir. Algoritma programlamasında bit manipülasyonunu kullanmanın pek çok farklı yolu vardır.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Amortize Analizi</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-amortize-analizi" rel="alternate" type="text/html" title="Rekabetçi Programcı Amortize Analizi" /><published>2025-09-27T00:00:00+00:00</published><updated>2025-09-27T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-amortize-analizi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-amortize-analizi"><![CDATA[<p>Bir algoritmanın sadece yapısını inceleyerek (örneğin döngü sayılarına bakarak) zaman karmaşıklığını hesaplamak kolaydır. Fakat bazen bu üstünkörü analiz, algoritmanın gerçek verimliliğini doğru yansıtmaz.</p>

<p><strong>Amortize Analizi</strong>, zaman karmaşıklığı farklı olan operasyonları içeren algoritmaları inceler. Buradaki fikir, tek tek operasyonların en kötü durumuna bakmak yerine, algoritmanın çalışması sürecinde bütün operasyonlar için harcanan toplam zamanı tahmin etmektir. Bazen bazı operasyonlar yavaş olsa da, toplamda bu yavaş operasyonlar sık gerçekleşmediği için ortalama performans verimli olabilir.</p>

<h2 id="81-i̇ki-i̇şaretçi-methodu-two-pointers-method">8.1 İki İşaretçi Methodu (Two Pointers Method)</h2>

<p>İki işaretçi methodunda, dizinin elemanları üzerinden geçmek için iki işaretçi (pointer) kullanılır. Bu işaretçiler genellikle tek bir yöne doğru hareket ederler, bu da algoritmanın verimli çalışmasını sağlar.</p>

<h3 id="altdizi-subarray-toplamı">Altdizi (Subarray) Toplamı</h3>

<p><strong>Problem:</strong> <code class="language-plaintext highlighter-rouge">n</code> tane pozitif sayıdan oluşan bir dizide, toplamı <code class="language-plaintext highlighter-rouge">x</code> olan ardışık bir altdizi bulmak.</p>

<p><strong>Fikir:</strong> Altdizinin başlangıcını ve sonunu gösteren iki işaretçi (<code class="language-plaintext highlighter-rouge">sol</code> ve <code class="language-plaintext highlighter-rouge">sağ</code>) tutulur. <code class="language-plaintext highlighter-rouge">sağ</code> işaretçi, toplam <code class="language-plaintext highlighter-rouge">x</code>‘i geçmediği sürece ilerletilir. Toplam <code class="language-plaintext highlighter-rouge">x</code>‘i geçerse, <code class="language-plaintext highlighter-rouge">sol</code> işaretçi ilerletilerek altdizi küçültülür. Eğer toplam tam olarak <code class="language-plaintext highlighter-rouge">x</code> olursa, bir çözüm bulunmuş olur.</p>

<p><code class="language-plaintext highlighter-rouge">[1, 3, 2, 5, 1, 1, 2, 3]</code> dizisinde toplamı 8 olan altdiziyi bulmak için iki işaretçi yönteminin adımları</p>

<p>Her iki işaretçi de dizi boyunca sadece ileri doğru hareket ettiği için toplamda en fazla <code class="language-plaintext highlighter-rouge">2n</code> adım atarlar. Bu yüzden algoritma $O(n)$ zamanda çalışır.</p>

<h3 id="2sum-problemi">2SUM Problemi</h3>

<p><strong>Problem:</strong> <code class="language-plaintext highlighter-rouge">n</code> sayıdan oluşan bir dizide, toplamı <code class="language-plaintext highlighter-rouge">x</code> olan iki eleman bulmak.</p>

<p><strong>Fikir:</strong></p>

<ol>
  <li>Diziyi artan sırada sıralayın.</li>
  <li>Bir işaretçiyi (<code class="language-plaintext highlighter-rouge">sol</code>) dizinin başına, diğerini (<code class="language-plaintext highlighter-rouge">sağ</code>) dizinin sonuna yerleştirin.</li>
  <li><code class="language-plaintext highlighter-rouge">array[sol] + array[sağ]</code> toplamını kontrol edin:
    <ul>
      <li>Toplam <code class="language-plaintext highlighter-rouge">x</code>‘ten küçükse, daha büyük bir değere ihtiyacımız var demektir, bu yüzden <code class="language-plaintext highlighter-rouge">sol</code> işaretçisini bir sağa kaydırın.</li>
      <li>Toplam <code class="language-plaintext highlighter-rouge">x</code>‘ten büyükse, daha küçük bir değere ihtiyacımız var demektir, bu yüzden <code class="language-plaintext highlighter-rouge">sağ</code> işaretçisini bir sola kaydırın.</li>
      <li>Toplam <code class="language-plaintext highlighter-rouge">x</code>‘e eşitse, bir çözüm bulunmuştur.</li>
    </ul>
  </li>
  <li>İşaretçiler karşılaşana kadar devam edin.</li>
</ol>

<p>Sıralama $O(n \log n)$, iki işaretçi ile arama ise $O(n)$ sürdüğü için toplam zaman karmaşıklığı $O(n \log n)$ olur. Daha zor bir problem olan <strong>3SUM problemi</strong> (toplamı <code class="language-plaintext highlighter-rouge">x</code> olan üç eleman bulmak), bu fikir genişletilerek $O(n^2)$ zamanda çözülebilir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<h2 id="82-en-yakın-küçük-elemanlar-nearest-smaller-elements">8.2 En Yakın Küçük Elemanlar (Nearest Smaller Elements)</h2>

<p><strong>Problem:</strong> Bir dizideki her eleman için, o elemanın solunda bulunan ve kendisinden küçük olan en yakın elemanı bulmak.</p>

<p><strong>Fikir:</strong> Dizinin solundan sağına doğru ilerlerken bir yığın (stack) kullanılır. Her eleman için:</p>
<ol>
  <li>Yığının tepesindeki eleman mevcut elemandan küçük olana kadar veya yığın boşalana kadar yığından eleman çıkarılır.</li>
  <li>Eğer yığın boş değilse, tepedeki eleman aranan en yakın küçük elemandır.</li>
  <li>Mevcut eleman yığına eklenir.</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">[1, 3, 4, 2, 5, 3, 4, 2]</code> dizisi için yığının adım adım değişimi</p>

<p>Bir eleman yığına en fazla bir kez eklenip bir kez çıkarıldığı için, her eleman amortize olarak $O(1)$ yığın operasyonu gerektirir. Bu yüzden algoritmanın toplam zaman karmaşıklığı $O(n)$’dir.</p>

<h2 id="83-sürgülü-pencere-minimumu-sliding-window-minimum">8.3 Sürgülü Pencere Minimumu (Sliding Window Minimum)</h2>

<p><strong>Problem:</strong> Sabit <code class="language-plaintext highlighter-rouge">k</code> boyutundaki bir altdizinin (sürgülü pencere), dizi boyunca soldan sağa hareket ederken her pozisyondaki minimum elemanı bulmak.</p>

<p><strong>Fikir:</strong> Bir <code class="language-plaintext highlighter-rouge">deque</code> (çift yönlü kuyruk) veri yapısı kullanılır. Bu <code class="language-plaintext highlighter-rouge">deque</code> her zaman pencere içindeki elemanların indislerini artan değer sırasında tutar. <code class="language-plaintext highlighter-rouge">deque</code>‘in başındaki eleman her zaman o anki pencerenin minimumudur.
Her adımda pencere bir sağa kaydığında:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">deque</code>‘in sonundan, yeni eklenecek elemandan daha büyük olan elemanlar çıkarılır.</li>
  <li>Yeni elemanın indisi <code class="language-plaintext highlighter-rouge">deque</code>‘in sonuna eklenir.</li>
  <li><code class="language-plaintext highlighter-rouge">deque</code>‘in başındaki elemanın indisi pencerenin dışına çıkmışsa, baştan çıkarılır.</li>
</ol>

<p>Her eleman <code class="language-plaintext highlighter-rouge">deque</code>‘e en fazla bir kez eklenip bir kez çıkarıldığı için, bu algoritma da amortize olarak $O(n)$ zamanda çalışır.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Uzun bir zaman boyunca 3SUM problemini $O(n^2)$ zamandan daha verimli bir şekilde çözmenin mümkün olmayacağı kabul edilmiştir. Fakat 2014’te bu durumun böyle olmadığı anlaşılmıştır. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="search" /><category term="arama" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="değişken" /><category term="analiz" /><category term="bellek" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><category term="amortize" /><summary type="html"><![CDATA[Bir algoritmanın sadece yapısını inceleyerek (örneğin döngü sayılarına bakarak) zaman karmaşıklığını hesaplamak kolaydır. Fakat bazen bu üstünkörü analiz, algoritmanın gerçek verimliliğini doğru yansıtmaz.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Dinamik Programlama (Dynamic Programming)</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-dinamik-programlama" rel="alternate" type="text/html" title="Rekabetçi Programcı Dinamik Programlama (Dynamic Programming)" /><published>2025-09-22T00:00:00+00:00</published><updated>2025-09-22T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-dinamik-programlama</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-dinamik-programlama"><![CDATA[<p>Dinamik programlama, tam bir aramanın doğruluğu ile açgözlü algoritmaların verimliliğini birleştiren bir tekniktir. Eğer bir problemde aynı alt problemler birkaç defa çözülüyorsa ve bu alt problemler bağımsız bir şekilde çözülebiliyorsa dinamik programlama kullanabiliriz.</p>

<p>Dinamik programlamanın iki temel kullanımı vardır:</p>

<ul>
  <li><strong>Optimal bir çözüm bulmak:</strong> Olabildiğince büyük veya küçük bir sonuç aradığımız durumlar.</li>
  <li><strong>Olası çözüm sayısını hesaplamak:</strong> Toplam olası çözüm sayısını bulmak.</li>
</ul>

<p>Bu bölüm, dinamik programlamanın temellerini ve klasik problemler üzerindeki uygulamalarını gösterecektir.</p>

<h2 id="71-para-problemi">7.1 Para Problemi</h2>

<p>Bölüm 6’da gördüğümüz para problemini tekrar ele alalım: <code class="language-plaintext highlighter-rouge">coins = {c_1, c_2, ..., c_k}</code> değerlerinden oluşan bir para kümesiyle, <code class="language-plaintext highlighter-rouge">n</code> toplamını oluşturan en az sayıda parayı bulmak. Açgözlü yaklaşımın her zaman çalışmadığını görmüştük. Şimdi bu problemi her para kümesi için çalışan dinamik programlama ile çözeceğiz.</p>

<h3 id="özyinelemeli-formülleştirme">Özyinelemeli Formülleştirme</h3>

<p>Problemin çözümünü daha küçük alt problemlerin çözümlerinden bulabiliriz. <code class="language-plaintext highlighter-rouge">solve(x)</code>, <code class="language-plaintext highlighter-rouge">x</code> toplamı için gereken minimum para sayısını ifade etsin.</p>

<p>Eğer <code class="language-plaintext highlighter-rouge">coins = {1, 3, 4}</code> ise, <code class="language-plaintext highlighter-rouge">x</code> toplamına ulaşmak için ilk seçtiğimiz para ya 1, ya 3, ya da 4 olabilir.</p>

<ul>
  <li>Eğer 1 seçersek, geri kalan <code class="language-plaintext highlighter-rouge">x-1</code> toplamı için <code class="language-plaintext highlighter-rouge">solve(x-1)</code> kadar paraya ihtiyacımız olur.</li>
  <li>Eğer 3 seçersek, geri kalan <code class="language-plaintext highlighter-rouge">x-3</code> toplamı için <code class="language-plaintext highlighter-rouge">solve(x-3)</code> kadar paraya ihtiyacımız olur.</li>
  <li>Eğer 4 seçersek, geri kalan <code class="language-plaintext highlighter-rouge">x-4</code> toplamı için <code class="language-plaintext highlighter-rouge">solve(x-4)</code> kadar paraya ihtiyacımız olur.</li>
</ul>

<p>Bu durumda özyineleme formülü şu şekilde olur:
<code class="language-plaintext highlighter-rouge">solve(x) = min(solve(x-1)+1, solve(x-3)+1, solve(x-4)+1)</code>
Temel durum <code class="language-plaintext highlighter-rouge">solve(0) = 0</code>‘dır.</p>

<h3 id="memoization">Memoization</h3>

<p>Yukarıdaki özyinelemeli fonksiyon, aynı <code class="language-plaintext highlighter-rouge">solve(x)</code> değerini tekrar tekrar hesapladığı için verimsizdir. <strong>Memoization</strong> tekniği ile, hesaplanan her <code class="language-plaintext highlighter-rouge">solve(x)</code> değerini bir dizide saklarız. Fonksiyon tekrar aynı <code class="language-plaintext highlighter-rouge">x</code> değeri için çağrıldığında, sonucu yeniden hesaplamak yerine doğrudan diziden alırız.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// value[x], solve(x)'in hesaplanmış değerini tutar</span>
<span class="c1">// ready[x], solve(x)'in hesaplanıp hesaplanmadığını belirtir</span>
<span class="kt">int</span> <span class="n">value</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>
<span class="kt">bool</span> <span class="n">ready</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>

<span class="kt">int</span> <span class="nf">solve</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">INF</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">ready</span><span class="p">[</span><span class="n">x</span><span class="p">])</span> <span class="k">return</span> <span class="n">value</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
  
  <span class="kt">int</span> <span class="n">best</span> <span class="o">=</span> <span class="n">INF</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">c</span> <span class="o">:</span> <span class="n">coins</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">best</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="n">solve</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">c</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
  <span class="p">}</span>
  
  <span class="n">ready</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
  <span class="n">value</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">best</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">best</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu algoritmanın zaman karmaşıklığı $O(nk)$ olur (<code class="language-plaintext highlighter-rouge">n</code> hedef toplam, <code class="language-plaintext highlighter-rouge">k</code> para sayısı).</p>

<h3 id="döngüsel-iterative-yaklaşım">Döngüsel (Iterative) Yaklaşım</h3>

<p>Aynı çözümü özyineleme yerine döngü kullanarak da yazabiliriz. Bu genellikle daha kısa ve biraz daha verimlidir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">value</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">value</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">INF</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">c</span> <span class="o">:</span> <span class="n">coins</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">c</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">value</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">x</span><span class="p">],</span> <span class="n">value</span><span class="p">[</span><span class="n">x</span> <span class="o">-</span> <span class="n">c</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="çözüm-sayısını-hesaplamak">Çözüm Sayısını Hesaplamak</h3>

<p>Minimum para sayısı yerine, bir toplamı oluşturmanın kaç farklı yolu olduğunu saymak istiyorsak, özyineleme formülündeki <code class="language-plaintext highlighter-rouge">min</code> işlemini toplama ile değiştiririz:
<code class="language-plaintext highlighter-rouge">solve(x) = solve(x-1) + solve(x-3) + solve(x-4)</code>
Temel durum <code class="language-plaintext highlighter-rouge">solve(0) = 1</code>‘dir (boş küme bir yoldur).</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">count</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">c</span> <span class="o">:</span> <span class="n">coins</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">c</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">count</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">+=</span> <span class="n">count</span><span class="p">[</span><span class="n">x</span> <span class="o">-</span> <span class="n">c</span><span class="p">];</span>
      <span class="n">count</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">%=</span> <span class="n">m</span><span class="p">;</span> <span class="c1">// Genellikle sonuç modulo m istenir</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="72-en-uzun-artan-altdizi-longest-increasing-subsequence">7.2 En Uzun Artan Altdizi (Longest Increasing Subsequence)</h2>

<p>n elemanlı bir dizide, elemanları soldan sağa doğru artan sırada olan en uzun altdiziyi bulma problemidir. Elemanların ardışık olması gerekmez.</p>

<p><code class="language-plaintext highlighter-rouge">length(k)</code>, <code class="language-plaintext highlighter-rouge">k</code> pozisyonunda biten en uzun artan altdizinin uzunluğunu belirtsin. <code class="language-plaintext highlighter-rouge">length(k)</code>‘yı hesaplamak için, <code class="language-plaintext highlighter-rouge">array[i] &lt; array[k]</code> koşulunu sağlayan tüm <code class="language-plaintext highlighter-rouge">i &lt; k</code> pozisyonları arasındaki en büyük <code class="language-plaintext highlighter-rouge">length(i)</code> değerini buluruz ve buna 1 ekleriz.</p>

<p>[Görsel: 6,2,5,1,7,4,8,3 dizisinde en uzun artan altdizinin (2,5,7,8) gösterimi]</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">length</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">k</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">])</span> <span class="p">{</span>
      <span class="n">length</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">length</span><span class="p">[</span><span class="n">k</span><span class="p">],</span> <span class="n">length</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu çözümün zaman karmaşıklığı $O(n^2)$’dir. Bu problem $O(n \log n)$ zamanda da çözülebilir.</p>

<h2 id="73-düzlemde-yollar">7.3 Düzlemde Yollar</h2>

<p><code class="language-plaintext highlighter-rouge">n x n</code> bir ızgarada, her hücrede pozitif bir sayı varken, sol üst köşeden sağ alt köşeye sadece sağa ve aşağı hareket ederek gidilebilen ve hücrelerdeki sayıların toplamını maksimize eden yolu bulma problemidir.</p>

<p>[Görsel: 5x5’lik bir düzlemde optimal yolu gösteren şema]</p>

<p><code class="language-plaintext highlighter-rouge">sum(y, x)</code>, <code class="language-plaintext highlighter-rouge">(y, x)</code> hücresine ulaşan en yüksek puanlı yolun toplamını versin. Özyineleme formülü:
<code class="language-plaintext highlighter-rouge">sum(y, x) = max(sum(y, x-1), sum(y-1, x)) + value[y][x]</code></p>

<p>Bu problem, $O(n^2)$’lik bir dinamik programlama çözümü ile çözülebilir.</p>

<h2 id="74-knapsack-problemleri">7.4 Knapsack Problemleri</h2>

<p>Knapsack (sırt çantası) problemleri, bir obje kümesinden belirli özelliklere sahip bir alt küme seçmeyi içerir. Klasik bir versiyonu, verilen ağırlıklarla oluşturulabilecek tüm olası toplam ağırlıkları bulmaktır.</p>

<p>Eğer <code class="language-plaintext highlighter-rouge">possible(x, k)</code> ilk <code class="language-plaintext highlighter-rouge">k</code> ağırlıkla <code class="language-plaintext highlighter-rouge">x</code> toplamını oluşturmanın mümkün olup olmadığını belirtirse, özyineleme formülü şu şekildedir:
<code class="language-plaintext highlighter-rouge">possible(x, k) = possible(x - w_k, k-1) OR possible(x, k-1)</code></p>

<p>Bu, <code class="language-plaintext highlighter-rouge">k</code>. ağırlığı (<code class="language-plaintext highlighter-rouge">w_k</code>) ya kullandığımız ya da kullanmadığımız anlamına gelir. Bu da $O(nW)$’lik bir dinamik programlama çözümü ile çözülebilir (<code class="language-plaintext highlighter-rouge">W</code> toplam ağırlık).</p>

<h2 id="75-değişiklik-farkı-edit-distance">7.5 Değişiklik Farkı (Edit Distance)</h2>

<p>Değişiklik farkı (veya Levenshtein farkı<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>), bir kelimeyi diğerine dönüştürmek için gereken minimum karakter ekleme, silme veya değiştirme sayısını verir. Örneğin, “LOVE” ve “MOVIE” arasındaki fark 2’dir (L-&gt;M, sonra V’den sonra I ekle).</p>

<p><code class="language-plaintext highlighter-rouge">distance(a, b)</code>, <code class="language-plaintext highlighter-rouge">x</code> kelimesinin ilk <code class="language-plaintext highlighter-rouge">a</code> karakteri ile <code class="language-plaintext highlighter-rouge">y</code> kelimesinin ilk <code class="language-plaintext highlighter-rouge">b</code> karakteri arasındaki farkı versin. Formül şu şekildedir:
<code class="language-plaintext highlighter-rouge">distance(a, b) = min(distance(a, b-1)+1, distance(a-1, b)+1, distance(a-1, b-1)+cost)</code>
Burada <code class="language-plaintext highlighter-rouge">cost</code>, eğer <code class="language-plaintext highlighter-rouge">x[a]</code> ve <code class="language-plaintext highlighter-rouge">y[b]</code> aynı ise 0, değilse 1’dir. Bu problem $O(nm)$’lik 2 boyutlu bir DP tablosu ile çözülebilir.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bu kavram V. I. Levenshtein’dan ismini almıştır. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="search" /><category term="arama" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="değişken" /><category term="dinamik" /><category term="bellek" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><category term="açgözlü" /><summary type="html"><![CDATA[Dinamik programlama, tam bir aramanın doğruluğu ile açgözlü algoritmaların verimliliğini birleştiren bir tekniktir. Eğer bir problemde aynı alt problemler birkaç defa çözülüyorsa ve bu alt problemler bağımsız bir şekilde çözülebiliyorsa dinamik programlama kullanabiliriz.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Açgözlü Algoritmalar (Greedy Algorithms)</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-ac-gozlu-algoritmalar" rel="alternate" type="text/html" title="Rekabetçi Programcı Açgözlü Algoritmalar (Greedy Algorithms)" /><published>2025-09-21T00:00:00+00:00</published><updated>2025-09-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-ac-gozlu-algoritmalar</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-ac-gozlu-algoritmalar"><![CDATA[<p>Açgözlü algoritma, her zaman o anki en iyi gözüken kararı vererek çözüme ulaşan bir yaklaşımdır. Açgözlü bir algoritma asla daha önce verdiği kararları geri almaz ve doğrudan son sonucu oluşturur. Bu yüzden açgözlü algoritmalar genellikle verimlidir. Açgözlü bir algoritma oluşturmanın zorluğu, her zaman en iyi (optimal) cevabı verecek bir açgözlü strateji bulmaktır. Yapılan küçük optimal kararların, genel olarak da optimal olması gerekir. Genellikle bir açgözlü algoritmanın doğru çalıştığını kanıtlamak zordur.</p>

<h2 id="61-para-problemi-coin-problem">6.1 Para Problemi (Coin problem)</h2>

<p>Elimizdeki madeni paraları kullanarak <code class="language-plaintext highlighter-rouge">n</code> miktarında bir para üstü vermemiz isteniyor. Elimizdeki paraların değerleri <code class="language-plaintext highlighter-rouge">coins = {c_1, c_2, ..., c_k}</code>‘dir ve her parayı istediğimiz kadar kullanabiliriz. Amaç, toplam için gereken minimum sayıda madeni para kullanmaktır.</p>

<p>Örneğin, <code class="language-plaintext highlighter-rouge">{1, 2, 5, 10, 20, 50, 100, 200}</code> paralarıyla <code class="language-plaintext highlighter-rouge">n = 520</code> oluşturmak için en az 4 para gerekir: <code class="language-plaintext highlighter-rouge">200 + 200 + 100 + 20</code>.</p>

<h3 id="açgözlü-algoritma">Açgözlü Algoritma</h3>

<p>Basit bir açgözlü algoritma, gereken miktar toplanana kadar her zaman seçilebilecek en büyük değerli parayı seçmektir. Bu strateji, standart Euro madeni paraları gibi sistemlerde işe yarar.</p>

<h3 id="genel-durum">Genel Durum</h3>

<p>Ancak genel durumda, sahip olduğumuz paralar rastgele değerlerde olabilir ve bu açgözlü algoritma her zaman optimal çözümü vermeyebilir. Örneğin, elimizdeki paralar <code class="language-plaintext highlighter-rouge">{1, 3, 4}</code> ise ve istenen toplam 6 ise, açgözlü algoritma <code class="language-plaintext highlighter-rouge">4 + 1 + 1</code> (3 adet para) çözümünü verirken, en iyi çözüm <code class="language-plaintext highlighter-rouge">3 + 3</code>‘tür (2 adet para).</p>

<p>Para problemi için her zaman çalışan genel bir açgözlü algoritma bilinmemektedir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<h2 id="62-zaman-planlaması-scheduling">6.2 Zaman Planlaması (Scheduling)</h2>

<p>Çoğu zaman planlama sorusu açgözlü algoritmalar ile çözülebilir. Klasik bir problem, başlangıç ve bitiş zamanları bilinen <code class="language-plaintext highlighter-rouge">n</code> tane etkinlikten, birbiriyle çakışmayacak şekilde en fazla sayıda etkinliği seçmektir.</p>

<table>
  <thead>
    <tr>
      <th>Etkinlik</th>
      <th>Başlama Zamanı</th>
      <th>Bitiş Zamanı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>A</td>
      <td>1</td>
      <td>3</td>
    </tr>
    <tr>
      <td>B</td>
      <td>2</td>
      <td>5</td>
    </tr>
    <tr>
      <td>C</td>
      <td>3</td>
      <td>9</td>
    </tr>
    <tr>
      <td>D</td>
      <td>6</td>
      <td>8</td>
    </tr>
  </tbody>
</table>

<p>Bu durumda en fazla iki etkinlik seçilebilir, örneğin B ve D.</p>

<p>Farklı açgözlü stratejiler denenebilir:</p>

<ol>
  <li><strong>En kısa etkinliği seç:</strong> Bu strateji her zaman çalışmaz. Kısa bir etkinlik, daha uzun süren iki etkinliğin seçilmesini engelleyebilir.</li>
  <li><strong>En erken başlayan etkinliği seç:</strong> Bu da her zaman çalışmaz. Erken başlayan uzun bir etkinlik, daha sonraki birçok etkinliği engelleyebilir.</li>
  <li><strong>En erken biten etkinliği seç:</strong> <strong>Bu strateji her zaman doğru çalışır.</strong> Her adımda, mevcut etkinliklerle çakışmayan ve bitiş zamanı en erken olan etkinliği seçmek, kalan zamanı maksimize ettiği için optimal bir çözüm üretir.</li>
</ol>

<h2 id="63-görevler-ve-son-teslimler-tasks-and-deadlines">6.3 Görevler ve Son Teslimler (Tasks and Deadlines)</h2>

<p>Süresi ve son teslim tarihi bilinen <code class="language-plaintext highlighter-rouge">n</code> tane görevimiz olduğunu düşünelim. Amacımız görevleri yapmak için bir sıra oluşturmak. Her görev için $d - x$ puan kazanıyoruz, burada <code class="language-plaintext highlighter-rouge">d</code> görevin son teslim tarihi ve <code class="language-plaintext highlighter-rouge">x</code> görevi bitirdiğimiz zamandır. En fazla alabileceğimiz toplam puan kaçtır?</p>

<p>İlginç bir şekilde, bu sorunun optimal çözümü son teslim tarihlerine bağlı değildir. Doğru açgözlü strateji, görevleri <strong>sürelerine göre artan bir şekilde sıralamaktır</strong>. Bunun nedeni, eğer daha uzun süren bir görevi daha kısa süren bir görevden önce yaparsak, bu iki görevin yerini değiştirdiğimizde toplam puanın her zaman artması veya aynı kalmasıdır. Daha kısa görevleri önce bitirmek, sonraki tüm görevlerin bitiş zamanını öne çeker ve toplam puanı iyileştirir.</p>

<h2 id="64-toplamları-küçültmek-minimizing-sums">6.4 Toplamları Küçültmek (Minimizing Sums)</h2>

<p>Bize <code class="language-plaintext highlighter-rouge">n</code> tane $a_1, a_2, …, a_n$ sayısı verildiğinde, $\sum_{i=1}^{n} \lvert a_i - x\rvert ^c$ toplamını en küçük yapacak <code class="language-plaintext highlighter-rouge">x</code> değerini bulma problemi.</p>

<h3 id="c--1-durumu">c = 1 Durumu</h3>

<p>$\sum \lvert a_i - x\rvert $ toplamını minimize etmek için en iyi <code class="language-plaintext highlighter-rouge">x</code> değeri, sayıların <strong>medyanı</strong>dır. Sayılar sıralandıktan sonra ortadaki eleman medyandır.</p>

<h3 id="c--2-durumu">c = 2 Durumu</h3>

<p>$\sum (a_i - x)^2$ toplamını minimize etmek için en iyi <code class="language-plaintext highlighter-rouge">x</code> değeri, sayıların <strong>aritmetik ortalaması</strong>dır ($(\sum a_i) / n$).</p>

<h2 id="65-veri-sıkıştırma-data-compression">6.5 Veri Sıkıştırma (Data Compression)</h2>

<p>Bir metni sıkıştırmak için her karaktere bir bit dizisi (kod) atanabilir. Daha sık geçen karakterlere daha kısa kodlar, daha az geçenlere daha uzun kodlar atayarak metnin toplam uzunluğu azaltılabilir.</p>

<h3 id="huffman-kodlaması">Huffman Kodlaması</h3>

<p>Huffman Kodlaması, bir metni sıkıştırmak için en optimal kodu (prefix code) üreten bir açgözlü algoritmadır<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>.</p>

<p>Algoritma, karakterlerin metindeki frekanslarına (geçme sıklıklarına) dayalı bir ikili ağaç (binary tree) oluşturur.</p>

<ol>
  <li>Her karakteri, frekansı kadar ağırlığa sahip bir düğüm olarak başlat.</li>
  <li>Her adımda, en düşük ağırlığa sahip iki düğümü seç ve bunları yeni bir ebeveyn düğüm altında birleştir. Yeni düğümün ağırlığı, birleştirilen iki düğümün ağırlıkları toplamıdır.</li>
  <li>Tek bir kök düğüm kalana kadar bu işleme devam et.</li>
</ol>

<p>Oluşturulan ağaçta, kökten bir karakterin yaprağına giden yol o karakterin kodunu verir (sola gitmek ‘0’, sağa gitmek ‘1’).</p>

<p>Bu yöntemle, sık geçen ‘A’ karakteri gibi karakterler kısa kodlar alırken, az geçen ‘B’ ve ‘D’ gibi karakterler daha uzun kodlar alır, bu da optimal sıkıştırmayı sağlar.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bu bölümde yapılan açgözlü algoritmanın elimizdeki paralar için doğru olup olmadığını polinom zamanda (polynomial time) kontrol etmek mümkündür. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>D. A. Huffman bu metodu üniversite dersinde bir soruyu çözerken bulmuştur ve bu algoritmayı 1952’de yayınlamıştır. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="search" /><category term="arama" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="değişken" /><category term="açgözlü" /><category term="greedy" /><category term="bellek" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Açgözlü algoritma, her zaman o anki en iyi gözüken kararı vererek çözüme ulaşan bir yaklaşımdır. Açgözlü bir algoritma asla daha önce verdiği kararları geri almaz ve doğrudan son sonucu oluşturur. Bu yüzden açgözlü algoritmalar genellikle verimlidir. Açgözlü bir algoritma oluşturmanın zorluğu, her zaman en iyi (optimal) cevabı verecek bir açgözlü strateji bulmaktır. Yapılan küçük optimal kararların, genel olarak da optimal olması gerekir. Genellikle bir açgözlü algoritmanın doğru çalıştığını kanıtlamak zordur.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Tam Arama (Complete Search)</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-tam-arama-complete-search" rel="alternate" type="text/html" title="Rekabetçi Programcı Tam Arama (Complete Search)" /><published>2025-09-20T00:00:00+00:00</published><updated>2025-09-20T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-tam-arama-complete-search</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-tam-arama-complete-search"><![CDATA[<p>Tam arama, neredeyse her algoritma probleminde kullanılan bir metottur. Buradaki fikir, kaba kuvvet (brute force) kullanarak bütün olası çözümleri oluşturup sonrasında probleme göre aralarından en iyi çözümü bulmak veya bütün çözümleri saymaktır. Tam arama, bütün çözümler denenebiliyorsa işe yarar bir tekniktir çünkü bu aramayı koda dökmek kolaydır ve aynı zamanda her zaman doğru çözümü bulur. Eğer tam arama soru için çok yavaş kalıyorsa, açgözlü (greedy) algoritmalar veya dinamik programlama gibi başka yöntemler gerekebilir.</p>

<h2 id="51-alt-küme-oluşturmak-generating-subsets">5.1 Alt küme Oluşturmak (Generating Subsets)</h2>

<p><code class="language-plaintext highlighter-rouge">n</code> elemanlı bir kümenin bütün alt kümelerini oluşturmak için iki yaygın yöntem vardır: özyineleme (recursion) veya bit maskeleme (bitmasking).</p>

<h3 id="yöntem-1-özyineleme-recursion">Yöntem 1: Özyineleme (Recursion)</h3>
<p>Bütün alt kümeleri oluşturmanın şık yollarından biri özyinelemedir. Aşağıdaki <code class="language-plaintext highlighter-rouge">search</code> fonksiyonu <code class="language-plaintext highlighter-rouge">{0, 1, ..., n-1}</code> kümesinin alt kümelerini oluşturur.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">subset</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">search</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">k</span> <span class="o">==</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// alt kümeyi işle</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="c1">// k elemanını alt kümeye dahil etme</span>
    <span class="n">search</span><span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="c1">// k elemanını alt kümeye dahil et</span>
    <span class="n">subset</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">k</span><span class="p">);</span>
    <span class="n">search</span><span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">subset</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><img src="/img/arama-agaci.jpg" alt="" /></p>

<h3 id="yöntem-2-bit-maskeleme-bitmasking">Yöntem 2: Bit Maskeleme (Bitmasking)</h3>
<p><code class="language-plaintext highlighter-rouge">n</code> elemanlı bir kümenin her alt kümesi, <code class="language-plaintext highlighter-rouge">n</code> bitten oluşan bir tam sayı (bitmask) ile temsil edilebilir. Sayıdaki <code class="language-plaintext highlighter-rouge">1</code> olan bitler, o pozisyondaki elemanın alt kümede olduğunu gösterir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">subset</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">b</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">subset</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="c1">// alt kümeyi işle</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="52-permütasyon-oluşturmak-generating-permutations">5.2 Permütasyon Oluşturmak (Generating Permutations)</h2>

<h3 id="yöntem-1-özyineleme-recursion-1">Yöntem 1: Özyineleme (Recursion)</h3>

<p>Permütasyonlar da özyineleme kullanılarak oluşturulabilir. Aşağıdaki kod, <code class="language-plaintext highlighter-rouge">{0, 1, ..., n-1}</code> kümesinin tüm permütasyonlarını bulur.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">permutation</span><span class="p">;</span>
<span class="kt">bool</span> <span class="n">chosen</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
<span class="kt">void</span> <span class="nf">search</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">permutation</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">==</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// permütasyonu işle</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">chosen</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="k">continue</span><span class="p">;</span>
      <span class="n">chosen</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
      <span class="n">permutation</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
      <span class="n">search</span><span class="p">();</span>
      <span class="n">chosen</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
      <span class="n">permutation</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="yöntem-2-c-next_permutation">Yöntem 2: C++ <code class="language-plaintext highlighter-rouge">next_permutation</code></h3>

<p>C++ standart kütüphanesi, bir dizinin bir sonraki leksikografik permütasyonunu oluşturan <code class="language-plaintext highlighter-rouge">next_permutation</code> fonksiyonunu içerir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">p</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">do</span> <span class="p">{</span>
  <span class="c1">// permütasyonu işle</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">next_permutation</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">p</span><span class="p">.</span><span class="n">end</span><span class="p">()));</span>
</code></pre></div></div>

<h2 id="53-geri-i̇zleme-backtracking">5.3 Geri İzleme (Backtracking)</h2>

<p>Geri izleme, boş bir çözümle başlayıp çözümü adım adım büyüten bir tam arama tekniğidir. Arama, çözüme ulaşmayan yolları terk ederek (geri izleyerek) ilerler.</p>

<p>Klasik bir örnek <strong>n-Vezir Problemi</strong>‘dir: <code class="language-plaintext highlighter-rouge">n x n</code> bir satranç tahtasına <code class="language-plaintext highlighter-rouge">n</code> veziri, birbirlerini tehdit etmeyecek şekilde yerleştirmek.</p>

<p><img src="/img/4x4-vezir.jpg" alt="" /></p>

<p>Bu problem, her satıra sırayla bir vezir koyarak ve her yeni vezirin öncekileri tehdit etmediğinden emin olarak geri izleme ile çözülebilir.</p>

<p><img src="/img/vezir-yerlestirme.jpg" alt="" /></p>

<h2 id="54-aramayı-budama-pruning-the-search">5.4 Aramayı Budama (Pruning The Search)</h2>

<p>Arama ağacını budayarak geri izleme optimize edilebilir. Buradaki fikir, yarı-çözümün tam bir çözüme ulaşamayacağı anlaşıldığı anda o arama dalını terk etmektir. Bu optimizasyonlar, aramanın verimliliğini ciddi derecede artırabilir.</p>

<p>Örneğin, <code class="language-plaintext highlighter-rouge">n x n</code> bir tahtada sol üst köşeden sağ alt köşeye her kareden tam bir kez geçerek yol bulma probleminde, aşağıdaki gibi optimizasyonlar yapılabilir:</p>

<ol>
  <li><strong>Simetri:</strong> Sadece aşağı veya sağa giderek başlayıp sonucu ikiyle çarpmak.</li>
  <li><strong>Erken Varış:</strong> Tüm kareler gezilmeden hedefe varıldıysa yolu geçersiz saymak.</li>
  <li><strong>Bölünme:</strong> Yol bir duvara ulaştığında, eğer sola ve sağa gidebiliyorsa tahtayı ikiye böler ve tüm kareleri gezmek imkansız hale gelir. Bu durumda arama dalı budanır.</li>
</ol>

<p>Bu tür küçük optimizasyonlar, arama süresini yüzlerce kat hızlandırabilir.</p>

<h2 id="55-ortada-buluşmak-meet-in-the-middle">5.5 Ortada Buluşmak (Meet in the middle)</h2>

<p>Ortada buluşmak (meet-in-the-middle), arama uzayını iki eşit parçaya bölerek çalışan bir tekniktir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. Her parça için ayrı bir arama yapılır ve sonda bu aramaların sonuçları birleştirilir. Genelde, $O(2^n)$ olan bir zaman karmaşıklığını $O(2^{n/2})$’ye indirebilir.</p>

<p>Örnek problem: <code class="language-plaintext highlighter-rouge">n</code> elemanlı bir kümede, toplamları <code class="language-plaintext highlighter-rouge">x</code> olan bir alt küme bulmak.</p>

<ol>
  <li>Kümeyi <code class="language-plaintext highlighter-rouge">n/2</code> elemanlı A ve B olmak üzere ikiye ayır.</li>
  <li>A kümesinin tüm alt küme toplamlarını hesapla ve bir <code class="language-plaintext highlighter-rouge">S_A</code> listesine kaydet.</li>
  <li>B kümesinin tüm alt küme toplamlarını hesapla ve bir <code class="language-plaintext highlighter-rouge">S_B</code> listesine kaydet.</li>
  <li><code class="language-plaintext highlighter-rouge">S_A</code>‘dan her <code class="language-plaintext highlighter-rouge">s_a</code> toplamı için, <code class="language-plaintext highlighter-rouge">S_B</code>‘de <code class="language-plaintext highlighter-rouge">x - s_a</code> değerinin olup olmadığını kontrol et.</li>
</ol>

<p>Bu yaklaşım, $O(2^n)$ yerine $O(2^{n/2})$ zamanda çalışır çünkü her iki yarım için de arama uzayı önemli ölçüde küçülür.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bu algoritma, E. Horowitz ve S. Sahni tarafından 1974’te bulunmuştur. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="search" /><category term="arama" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="değişken" /><category term="brute force" /><category term="bellek" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Tam arama, neredeyse her algoritma probleminde kullanılan bir metottur. Buradaki fikir, kaba kuvvet (brute force) kullanarak bütün olası çözümleri oluşturup sonrasında probleme göre aralarından en iyi çözümü bulmak veya bütün çözümleri saymaktır. Tam arama, bütün çözümler denenebiliyorsa işe yarar bir tekniktir çünkü bu aramayı koda dökmek kolaydır ve aynı zamanda her zaman doğru çözümü bulur. Eğer tam arama soru için çok yavaş kalıyorsa, açgözlü (greedy) algoritmalar veya dinamik programlama gibi başka yöntemler gerekebilir.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Veri Yapıları</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-veri-yapilari" rel="alternate" type="text/html" title="Rekabetçi Programcı Veri Yapıları" /><published>2025-09-18T00:00:00+00:00</published><updated>2025-09-18T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-veri-yapilari</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-veri-yapilari"><![CDATA[<p>Veri yapısı, bilgisayarın hafızasında veri saklamak için bir yoldur. Ele alınan problem için uygun bir veri yapısı seçimi yapmak önemlidir çünkü her veri yapısının kendine göre avantajları ve dezavantajları bulunmaktadır. Bu noktada cevaplanması gereken ana soru, hangi işlemlerin seçtiğimiz veri yapısında verimli olacağıdır.</p>

<p>Bu bölümde C++ standart kütüphanesindeki en önemli veri yapıları tanıtılacaktır. Standart kütüphane zamandan tasarruf sağlayacağı için mümkün olduğunca onu kullanmaya özen gösterilmelidir.</p>

<h2 id="41-dinamik-diziler">4.1 Dinamik Diziler</h2>

<p>Dinamik dizi, programın çalışması sırasında boyutu değiştirilebilen bir dizidir. C++’daki en popüler dinamik dizi <code class="language-plaintext highlighter-rouge">vector</code> yapısıdır.</p>

<p>Aşağıdaki kod, boş bir vektör oluşturur ve bu vektöre üç eleman ekler:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">v</span><span class="p">;</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="c1">// [3]</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// [3,2]</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// [3,2,5]</span>
</code></pre></div></div>

<p>Elemanlara sıradan bir dizideki gibi erişilebilir:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 3</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">size()</code> fonksiyonu vektördeki eleman sayısını döndürür. Vektördeki elemanları yazdırmak için:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Veya daha kısa bir yol:</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">x</span> <span class="o">:</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">back()</code> fonksiyonu son elemanı döndürürken, <code class="language-plaintext highlighter-rouge">pop_back()</code> son elemanı siler.</p>

<p>Vektörler başlangıç değerleriyle de oluşturulabilir:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 5 elemanlı bir vektör</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">v1</span> <span class="o">=</span> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">};</span>

<span class="c1">// 10 elemanlı ve tüm değerleri 0 olan bir vektör</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">v2</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>

<span class="c1">// 10 elemanlı ve tüm değerleri 5 olan bir vektör</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">v3</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">push_back</code> operasyonunun ortalama zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(1)</code>‘dir<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p><code class="language-plaintext highlighter-rouge">string</code> veri yapısı da <code class="language-plaintext highlighter-rouge">vector</code> gibi kullanılabilen bir dinamik dizidir ve ek olarak <code class="language-plaintext highlighter-rouge">+</code> ile birleştirme, <code class="language-plaintext highlighter-rouge">substr</code> ile alt string alma gibi özelliklere sahiptir.</p>

<h2 id="42-küme-set-yapıları">4.2 Küme (Set) Yapıları</h2>

<p>Küme, elemanlar topluluğu barındıran bir veri yapısıdır. Eleman ekleme, arama ve silme işlemlerini destekler.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">set</code>: Dengeli bir ikili ağaç (balanced binary tree) üzerine kuruludur ve işlemleri $O(\log n)$ sürede çalışır. Elemanları sıralı tutar.</li>
  <li><code class="language-plaintext highlighter-rouge">unordered_set</code>: Kıyım (hashing) kullanır ve işlemleri ortalama olarak $O(1)$ sürede çalışır. Elemanlar sırasızdır.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">s</span><span class="p">;</span>
<span class="n">s</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="n">s</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
<span class="n">s</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">s</span><span class="p">.</span><span class="n">count</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 1 (var)</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">s</span><span class="p">.</span><span class="n">count</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 0 (yok)</span>
<span class="n">s</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">s</span><span class="p">.</span><span class="n">count</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 0</span>
</code></pre></div></div>
<p>Kümelerin önemli bir özelliği, bütün elemanlarının farklı olmasıdır. Bir eleman birden fazla kez eklenemez.</p>

<p><code class="language-plaintext highlighter-rouge">multiset</code> ve <code class="language-plaintext highlighter-rouge">unordered_multiset</code> ise bir elemanın birden fazla kopyasını barındırabilen küme türleridir.</p>

<p><code class="language-plaintext highlighter-rouge">erase</code> fonksiyonu bir <code class="language-plaintext highlighter-rouge">multiset</code>‘ten bir elemanın tüm kopyalarını siler. Sadece bir kopyayı silmek için <code class="language-plaintext highlighter-rouge">s.erase(s.find(5));</code> gibi bir kullanım gerekir.</p>

<h2 id="43-map-yapıları">4.3 Map Yapıları</h2>

<p>Bir <code class="language-plaintext highlighter-rouge">map</code>, anahtar-değer çiftlerinden (key-value pairs) oluşan genelleştirilmiş bir dizidir.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">map</code>: Dengeli ikili ağaç yapısındadır, anahtarları sıralı tutar ve erişim $O(\log n)$’dir.</li>
  <li><code class="language-plaintext highlighter-rouge">unordered_map</code>: Hashleme kullanır, anahtarlar sırasızdır ve erişim ortalama $O(1)$’dir.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">&lt;</span><span class="n">string</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
<span class="n">m</span><span class="p">[</span><span class="s">"monkey"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="n">m</span><span class="p">[</span><span class="s">"banana"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">m</span><span class="p">[</span><span class="s">"banana"</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 3</span>
</code></pre></div></div>
<p>Eğer istenen anahtar map’te bulunmuyorsa, otomatik olarak varsayılan bir değerle (sayılar için 0) eklenir. <code class="language-plaintext highlighter-rouge">count</code> fonksiyonu bir anahtarın map’te olup olmadığını kontrol etmek için kullanılabilir.</p>

<h2 id="44-i̇teratörler-ve-aralıklar-iterators-and-ranges">4.4 İteratörler ve Aralıklar (Iterators and Ranges)</h2>

<p>İteratör, bir veri yapısındaki bir elemanı gösteren bir değişkendir. <code class="language-plaintext highlighter-rouge">begin()</code> iteratörü ilk elemanı, <code class="language-plaintext highlighter-rouge">end()</code> iteratörü ise son elemandan <em>sonraki</em> pozisyonu gösterir.</p>

<p>Bu iteratörler <code class="language-plaintext highlighter-rouge">sort</code>, <code class="language-plaintext highlighter-rouge">reverse</code>, <code class="language-plaintext highlighter-rouge">random_shuffle</code> gibi birçok standart kütüphane fonksiyonunda kullanılır:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sort</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">set</code> gibi veri yapılarının elemanlarına erişmek için iteratörler kullanılır:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">s</span> <span class="o">=</span> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">};</span>
<span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">it</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 2 (en küçük eleman)</span>

<span class="n">it</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
<span class="n">it</span><span class="o">--</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">it</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 8 (en büyük eleman)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">set</code> için <code class="language-plaintext highlighter-rouge">find(x)</code>, <code class="language-plaintext highlighter-rouge">lower_bound(x)</code> ve <code class="language-plaintext highlighter-rouge">upper_bound(x)</code> gibi fonksiyonlar da iteratör döndürür.</p>

<h2 id="45-diğer-yapılar">4.5 Diğer Yapılar</h2>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">bitset</code></strong>: Her değeri 0 ya da 1 olan sabit boyutlu bir dizidir. Hafıza açısından verimlidir ve bit operasyonları (<code class="language-plaintext highlighter-rouge">&amp;</code>, <code class="language-plaintext highlighter-rouge">|</code>, <code class="language-plaintext highlighter-rouge">^</code>) ile hızlıca manipüle edilebilir.
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">bitset</span><span class="o">&lt;</span><span class="mi">10</span><span class="o">&gt;</span> <span class="n">s</span><span class="p">;</span>
  <span class="n">s</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">s</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">s</span><span class="p">.</span><span class="n">count</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 1 olan bit sayısı</span>
</code></pre></div>    </div>
  </li>
  <li><strong><code class="language-plaintext highlighter-rouge">deque</code></strong>: Hem başından hem de sonundan eleman eklenip çıkarılabilen (<code class="language-plaintext highlighter-rouge">push_front</code>, <code class="language-plaintext highlighter-rouge">pop_front</code>, <code class="language-plaintext highlighter-rouge">push_back</code>, <code class="language-plaintext highlighter-rouge">pop_back</code>) dinamik bir dizidir. Ortalama <code class="language-plaintext highlighter-rouge">O(1)</code> zamanda çalışır.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">stack</code></strong>: Sadece en üstten eleman eklenen (push) ve çıkarılan (pop) bir “LIFO” (Last-In, First-Out) yapısıdır.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">queue</code></strong>: Sonuna eleman eklenen (push) ve başından eleman çıkarılan (pop) bir “FIFO” (First-In, First-Out) yapısıdır.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">priority_queue</code></strong>: Her zaman en büyük (veya en küçük) elemana hızlı erişim sağlayan bir yapıdır. Eleman ekleme ve çıkarma $O(\log n)$ sürer.
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">priority_queue</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">q</span><span class="p">;</span>
  <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
  <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
  <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">q</span><span class="p">.</span><span class="n">top</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 5</span>
  <span class="n">q</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">q</span><span class="p">.</span><span class="n">top</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="c1">// 3</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="46-sıralama-ile-karşılaştırma">4.6 Sıralama ile Karşılaştırma</h2>

<p>Bir problemi hem sıralama hem de veri yapıları ile çözmek mümkün olabilir. Zaman karmaşıklıkları aynı olsa bile (<code class="language-plaintext highlighter-rouge">O(n log n)</code> gibi), pratik performansları farklılık gösterebilir. Örneğin, iki listedeki ortak eleman sayısını bulma probleminde, sıralama tabanlı bir çözüm genellikle <code class="language-plaintext highlighter-rouge">set</code> veya <code class="language-plaintext highlighter-rouge">map</code> tabanlı bir çözümden daha hızlı çalışır çünkü sıralama basit bir işlemdir ve sadece bir kez yapılırken, <code class="language-plaintext highlighter-rouge">set</code> gibi yapılar her işlemde daha karmaşık bir ağaç yapısını yönetir.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>(Ç.N.) İnternet sistemleri dizayn edilirken kullanılan standart kütüphanelerdeki dinamik dizilerin boyutunun hangi değerde artacağı kamuya açık bir bilgi olduğundan bazı hacker’ların bu duruma hoş olmayan bir istisna oluşturduğunu söyleyebiliriz. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="küme" /><category term="vektör" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="dizi" /><category term="yarışma" /><category term="değişken" /><category term="veri" /><category term="bellek" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Veri yapısı, bilgisayarın hafızasında veri saklamak için bir yoldur. Ele alınan problem için uygun bir veri yapısı seçimi yapmak önemlidir çünkü her veri yapısının kendine göre avantajları ve dezavantajları bulunmaktadır. Bu noktada cevaplanması gereken ana soru, hangi işlemlerin seçtiğimiz veri yapısında verimli olacağıdır.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Sıralama</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-siralama" rel="alternate" type="text/html" title="Rekabetçi Programcı Sıralama" /><published>2025-09-17T00:00:00+00:00</published><updated>2025-09-17T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-siralama</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-siralama"><![CDATA[<p>Sıralama, temel algoritma sorularından biridir. Çoğu algoritmanın içeriğinde, veriyi sıralı halde işlemek daha kolay olduğu için sıralama bulunur. Örneğin “Bu dizide birbiriyle aynı iki eleman var mı?” sorusu sıralamayla çok kolay bir şekilde çözülebilir. Eğer dizi birbiriyle aynı iki eleman içeriyorsa, dizi sıralandıktan sonra bu elemanlar ardışık olacaktır.</p>

<p>Verimli çalışan sıralama algoritmaları $O(n \log n)$ zamanda çalışır ve genelde içeriğinde sıralama bulunan algoritmalar da bu zaman karmaşıklığına sahiptir.</p>

<h2 id="31-sıralama-teorisi">3.1 Sıralama Teorisi</h2>

<p>Temel sıralama problemi şöyledir: n elemana sahip bir diziyi artan sırada sıralayın.
Örneğin <code class="language-plaintext highlighter-rouge">[1, 3, 8, 2, 9, 2, 5, 6]</code> dizisi sıralandıktan sonra <code class="language-plaintext highlighter-rouge">[1, 2, 2, 3, 5, 6, 8, 9]</code> haline dönüşür.</p>

<h3 id="on2-algoritmalar">$O(n^2)$ Algoritmalar</h3>

<p>Basit dizi sıralama algoritmaları $O(n^2)$ zamanda çalışır. Bu algoritmalar kısa olup genelde iki <code class="language-plaintext highlighter-rouge">for</code> döngüsüyle çalışır. Çok bilinen $O(n^2)$ algoritmalarından biri olan <strong>kabarcık sıralaması (bubble sort)</strong>, dizideki elemanların değerlerine göre “kabarcık” gibi yer değiştirmesiyle çalışır.</p>

<p><img src="/img/kabarcik-siralama.jpg" alt="" /></p>

<p>Kabarcık sıralaması <code class="language-plaintext highlighter-rouge">n</code> defa tur atar. Her turda, algoritma sıraya uymayan iki ardışık eleman bulduğunda yerlerini değiştirir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="p">{</span>
      <span class="n">swap</span><span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">]);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="ters-elemanlar-inversions">Ters Elemanlar (Inversions)</h3>

<p>Bir dizide <code class="language-plaintext highlighter-rouge">a &lt; b</code> iken <code class="language-plaintext highlighter-rouge">array[a] &gt; array[b]</code> olması durumuna <strong>ters eleman (inversion)</strong> denir. Dizide herhangi bir ters eleman durumu kalmayınca dizi sıralanmış olur. En kötü durumda (dizi tersten sıralıysa) ters eleman sayısı $\frac{n(n-1)}{2} = O(n^2)$ olur. Sadece ardışık elemanları değiştiren algoritmalar her adımda en fazla bir ters eleman durumunu düzeltebildiği için zaman karmaşıklıkları en az $O(n^2)$’dir.</p>

<h3 id="on-log-n-algoritmalar">$O(n \log n)$ Algoritmalar</h3>

<p>Bir diziyi, sadece ardışık eleman değiştirmeye bağlı kalmayarak $O(n \log n)$’lık algoritmalarla sıralayabiliriz. Bu tip algoritmalardan biri özyinelemeye (recursion) dayalı <strong>birleştirme sıralamasıdır (merge sort)</strong><sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p>Birleştirme sıralaması <code class="language-plaintext highlighter-rouge">array[a...b]</code> alt dizisini aşağıdaki gibi sıralar:</p>

<ol>
  <li>Eğer <code class="language-plaintext highlighter-rouge">a == b</code> ise, alt dizi zaten sıralıdır.</li>
  <li>Orta elemanın pozisyonunu hesapla: $k = \lfloor(a+b)/2\rfloor$.</li>
  <li>Özyinelemeli bir şekilde <code class="language-plaintext highlighter-rouge">array[a...k]</code> alt dizisini sırala.</li>
  <li>Özyinelemeli bir şekilde <code class="language-plaintext highlighter-rouge">array[k+1...b]</code> alt dizisini sırala.</li>
  <li>Sıralı alt dizileri birleştirerek <code class="language-plaintext highlighter-rouge">array[a...b]</code>‘yi oluştur.</li>
</ol>

<h3 id="sıralama-alt-sınırı-lower-bound">Sıralama Alt Sınırı (Lower Bound)</h3>

<p>Elemanları sadece birbiriyle karşılaştırarak çalışan sıralama algoritmaları için zaman karmaşıklığı alt sınırı $O(n \log n)$’dir. Bunun nedeni, <code class="language-plaintext highlighter-rouge">n</code> elemanın <code class="language-plaintext highlighter-rouge">n!</code> tane olası permütasyonu olması ve her karşılaştırmanın bu olasılıkları en fazla yarıya indirmesidir. Bu durumu ayırt etmek için en az $\log_2(n!) \approx O(n \log n)$ karşılaştırma gerekir.</p>

<h3 id="sayarak-sıralama-counting-sort">Sayarak Sıralama (Counting Sort)</h3>

<p>Eğer dizideki her elemanın değerinin $0…c$ arasında olduğu biliniyorsa ve $c=O(n)$ ise, <strong>sayarak sıralama (counting sort)</strong> ile diziyi $O(n)$ zamanda sıralayabiliriz. Bu algoritma, her elemanın dizide kaç kez geçtiğini sayan bir frekans dizisi oluşturur ve bu diziyi kullanarak sıralı diziyi oluşturur.</p>

<h2 id="32-c-dilinde-sıralama">3.2 C++ Dilinde Sıralama</h2>

<p>Yarışmalarda kendi sıralama algoritmanızı yazmak yerine standart kütüphanelerdeki hazır fonksiyonları kullanmak daha pratiktir. C++’daki <code class="language-plaintext highlighter-rouge">sort</code> fonksiyonu verimli bir şekilde çalışır.</p>

<p><strong>Vector sıralama:</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">v</span> <span class="o">=</span> <span class="p">{</span><span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span>
<span class="n">sort</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> <span class="c1">// v becomes {2, 3, 3, 4, 5, 5, 8}</span>
</code></pre></div></div>

<p><strong>Dizi (array) sıralama:</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span>
<span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="n">sort</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="n">n</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>String sıralama:</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"monkey"</span><span class="p">;</span>
<span class="n">sort</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">s</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> <span class="c1">// s becomes "ekmnoy"</span>
</code></pre></div></div>

<h3 id="karşılaştırma-i̇şlemleri">Karşılaştırma İşlemleri</h3>

<p><code class="language-plaintext highlighter-rouge">sort</code> fonksiyonu, <code class="language-plaintext highlighter-rouge">pair</code> ve <code class="language-plaintext highlighter-rouge">tuple</code> gibi yapıları da sıralayabilir. Sıralama önce ilk elemana göre, eşitlik durumunda ikinci elemana göre vb. yapılır.</p>

<p>Kendi tanımladığınız yapılar (<code class="language-plaintext highlighter-rouge">struct</code>) için <code class="language-plaintext highlighter-rouge">operator&lt;</code> fonksiyonunu tanımlamanız gerekir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">P</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">;</span>
  <span class="kt">bool</span> <span class="k">operator</span><span class="o">&lt;</span><span class="p">(</span><span class="k">const</span> <span class="n">P</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">!=</span> <span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">)</span> <span class="k">return</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">return</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Alternatif olarak <code class="language-plaintext highlighter-rouge">sort</code> fonksiyonuna üçüncü bir argüman olarak bir karşılaştırma fonksiyonu da verebilirsiniz.</p>

<h2 id="33-i̇kili-arama-algoritması-binary-search">3.3 İkili Arama Algoritması (Binary Search)</h2>

<p>Eğer bir dizi <strong>sıralıysa</strong>, içinde bir elemanı aramak için <code class="language-plaintext highlighter-rouge">O(n)</code>‘lik lineer arama yerine <code class="language-plaintext highlighter-rouge">O(log n)</code>‘de çalışan <strong>ikili arama (binary search)</strong> kullanılabilir. İkili arama, arama aralığını her adımda ikiye bölerek çalışır.</p>

<h3 id="yöntem-1-aralık-küçültme">Yöntem 1: Aralık Küçültme</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">a</span> <span class="o">&lt;=</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// bulundu</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">x</span><span class="p">)</span> <span class="n">b</span> <span class="o">=</span> <span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">else</span> <span class="n">a</span> <span class="o">=</span> <span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="yöntem-2-zıplamalı-arama">Yöntem 2: Zıplamalı Arama</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="n">b</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">b</span> <span class="o">/=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">while</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">array</span><span class="p">[</span><span class="n">k</span> <span class="o">+</span> <span class="n">b</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">k</span> <span class="o">+=</span> <span class="n">b</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// bulundu</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="c-fonksiyonları">C++ Fonksiyonları</h3>

<p>C++ standart kütüphanesi, sıralı diziler üzerinde logaritmik zamanda çalışan hazır ikili arama fonksiyonları sunar:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">lower_bound</code>: Değeri en az <code class="language-plaintext highlighter-rouge">x</code> olan ilk elemanın işaretçisini (pointer/iterator) döner.</li>
  <li><code class="language-plaintext highlighter-rouge">upper_bound</code>: Değeri <code class="language-plaintext highlighter-rouge">x</code>‘ten büyük olan ilk elemanın işaretçisini döner.</li>
  <li><code class="language-plaintext highlighter-rouge">equal_range</code>: Yukarıdaki iki işaretçiyi bir çift olarak döner.</li>
</ul>

<p>Bu fonksiyonları kullanarak bir elemanın dizide olup olmadığını kontrol edebilir veya kaç adet bulunduğunu sayabilirsiniz.</p>

<h3 id="en-küçük-çözümü-bulmak">En Küçük Çözümü Bulmak</h3>

<p>İkili arama, belirli bir <code class="language-plaintext highlighter-rouge">k</code> değerinden sonra <code class="language-plaintext highlighter-rouge">true</code> olan bir <code class="language-plaintext highlighter-rouge">ok(x)</code> fonksiyonu için bu en küçük <code class="language-plaintext highlighter-rouge">k</code> değerini bulmak amacıyla da kullanılabilir. Bu, “cevaba ikili arama” (binary search on the answer) olarak bilinen yaygın bir tekniktir.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Birleştirme sıralaması J. von Neumann tarafından 1945 tarihinde icat edilmiştir. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="sıralama" /><category term="c" /><category term="programlama" /><category term="algoritma" /><category term="olimpiyat" /><category term="karmaşıklık" /><category term="yarışma" /><category term="kabarcık sıralaması" /><category term="ters elemanlar" /><category term="ikili arama" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Sıralama, temel algoritma sorularından biridir. Çoğu algoritmanın içeriğinde, veriyi sıralı halde işlemek daha kolay olduğu için sıralama bulunur. Örneğin “Bu dizide birbiriyle aynı iki eleman var mı?” sorusu sıralamayla çok kolay bir şekilde çözülebilir. Eğer dizi birbiriyle aynı iki eleman içeriyorsa, dizi sıralandıktan sonra bu elemanlar ardışık olacaktır.]]></summary></entry><entry><title type="html">Rekabetçi Programcı Zaman Karmaşıklığı</title><link href="https://sonsuzus.github.io/posts/rekabetci-programci-zaman-karmasikligi" rel="alternate" type="text/html" title="Rekabetçi Programcı Zaman Karmaşıklığı" /><published>2025-09-16T00:00:00+00:00</published><updated>2025-09-16T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/rekabetci-programci-zaman-karmasikligi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/rekabetci-programci-zaman-karmasikligi"><![CDATA[<p>Rekabetçi programlamada algoritmaların verimliliği önemlidir. Genelde soruyu çözen yavaş bir algoritma oluşturmak kolaydır ama asıl zorluk hızlı bir algoritma oluşturmaktır. Eğer algoritma çok yavaşsa ya sorudan kısmi puan alacaktır ya da hiç almayacaktır.</p>

<p>Zaman karmaşıklığı, bir algoritmanın bir girdi için tahmini olarak ne kadar süre gerektireceğini belirtir. Bunun amacı, girdinin boyutuna göre değişen ve algoritmanın verimliliğini gösteren bir fonksiyon oluşturmaktır. Zaman karmaşıklığını hesaplayarak, algoritmayı koda dökmeden önce yeterince hızlı olup olmadığını anlayabiliriz.</p>

<h2 id="21-hesaplama-kuralları">2.1 Hesaplama Kuralları</h2>

<p>Bir algoritmanın zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(...)</code> ile gösterilir. Genelde, girdi büyüklüğü olarak <code class="language-plaintext highlighter-rouge">n</code> değişkeni kullanılır. Örneğin sayılardan oluşan bir dizide <code class="language-plaintext highlighter-rouge">n</code> dizinin büyüklüğünü, eğer girdi bir yazı ise <code class="language-plaintext highlighter-rouge">n</code> yazının uzunluğunu verir.</p>

<h3 id="döngüler">Döngüler</h3>

<p>Algoritmanın yavaşlamasına neden olan genel sebeplerden biri, girdi üzerinde çalışan çok fazla döngü bulundurmasıdır. Bir algoritma ne kadar çok iç içe geçmiş döngü içerirse o kadar yavaşlar. Eğer <code class="language-plaintext highlighter-rouge">k</code> tane iç içe geçmiş döngü varsa zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(n^k)</code> olur.</p>

<p>Örneğin aşağıdaki kodun zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(n)</code>‘dir:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// kod</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Ve aşağıdaki kodun zaman karmaşıklığı ise <code class="language-plaintext highlighter-rouge">O(n^2)</code>‘dir:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// kod</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="büyüklük-sırası-order-of-magnitude">Büyüklük Sırası (Order of Magnitude)</h3>

<p>Zaman karmaşıklığı bize döngü içindeki kodun tam olarak kaç defa çalışacağını vermez. Örneğin aşağıdaki kodların hepsinin zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(n)</code>‘dir, ancak döngü sayıları farklıdır.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="o">*</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// kod</span>
<span class="p">}</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="o">+</span><span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// kod</span>
<span class="p">}</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// kod</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="aşamalar-phases">Aşamalar (Phases)</h3>

<p>Eğer algoritma birden çok aşamadan oluşuyorsa, toplam zaman karmaşıklığı en yavaş aşamanın karmaşıklığına eşittir. Çünkü genelde en yavaş çalışan aşama, toplam süreyi domine eder.</p>

<h3 id="çoklu-değişkenler">Çoklu Değişkenler</h3>

<p>Bazen zaman karmaşıklığı birden çok faktöre bağlıdır. Bu durumda formül birden çok değişken içerir. Örneğin aşağıdaki kodun zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(nm)</code>‘dir:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">m</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// kod</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="özyineleme-recursion">Özyineleme (Recursion)</h3>

<p>Özyinelemeli bir fonksiyonun zaman karmaşıklığı, fonksiyonun kaç kez çağrıldığı ile bir çağrının zaman karmaşıklığının çarpımına eşittir.</p>

<p>Örneğin, aşağıdaki <code class="language-plaintext highlighter-rouge">f(n)</code> fonksiyonu <code class="language-plaintext highlighter-rouge">n</code> kez çağrılır ve her çağrı <code class="language-plaintext highlighter-rouge">O(1)</code> sürdüğü için toplam karmaşıklık <code class="language-plaintext highlighter-rouge">O(n)</code>‘dir.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">f</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
  <span class="n">f</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Aşağıdaki <code class="language-plaintext highlighter-rouge">g(n)</code> fonksiyonunda ise her çağrı iki yeni çağrı oluşturur. Bu da toplamda <code class="language-plaintext highlighter-rouge">1 + 2 + 4 + ... + 2^(n-1) = 2^n - 1</code> çağrıya yol açar. Dolayısıyla zaman karmaşıklığı <code class="language-plaintext highlighter-rouge">O(2^n)</code>‘dir.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">g</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
  <span class="n">g</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
  <span class="n">g</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="22-karmaşıklık-sınıfları-complexity-classes">2.2 Karmaşıklık Sınıfları (Complexity Classes)</h2>

<table>
  <thead>
    <tr>
      <th>Karmaşıklık</th>
      <th>Açıklama</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(1)</code></td>
      <td><strong>Sabit Zaman:</strong> Girdi büyüklüğünden bağımsızdır.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(log n)</code></td>
      <td><strong>Logaritmik:</strong> Girdiyi her adımda bölen algoritmalardır (örn. ikili arama).</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(sqrt(n))</code></td>
      <td><strong>Karekök:</strong> <code class="language-plaintext highlighter-rouge">O(log n)</code>‘den yavaş, <code class="language-plaintext highlighter-rouge">O(n)</code>‘den hızlıdır.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(n)</code></td>
      <td><strong>Lineer:</strong> Girdi üzerinden sabit sayıda geçiş yapar. Genelde en iyi karmaşıklıktır.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(n log n)</code></td>
      <td>Genellikle verimli sıralama algoritmalarının karmaşıklığıdır.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(n^2)</code></td>
      <td><strong>Karesel:</strong> Genellikle iki iç içe döngü içerir. Girdideki tüm eleman çiftlerini gezer.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(n^3)</code></td>
      <td><strong>Kübik:</strong> Genellikle üç iç içe döngü içerir. Girdideki tüm üçlüleri gezer.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(2^n)</code></td>
      <td>Genellikle girdinin tüm alt kümelerini gezen algoritmalardır.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">O(n!)</code></td>
      <td>Genellikle girdinin tüm permütasyonlarını gezen algoritmalardır.</td>
    </tr>
  </tbody>
</table>

<p>Zaman karmaşıklığı en fazla <code class="language-plaintext highlighter-rouge">O(n^k)</code> (k sabit) olan bir algoritma <strong>polinom</strong>dur. <code class="language-plaintext highlighter-rouge">O(2^n)</code> ve <code class="language-plaintext highlighter-rouge">O(n!)</code> dışındaki yukarıdaki karmaşıklıklar polinomdur.</p>

<h2 id="23-verimliliği-tahmin-etme">2.3 Verimliliği Tahmin Etme</h2>

<p>Girdinin büyüklüğüne göre, 1 saniyelik zaman limitinde beklenen zaman karmaşıklığı aşağıdaki gibidir:</p>

<table>
  <thead>
    <tr>
      <th>Girdi Büyüklüğü <code class="language-plaintext highlighter-rouge">n</code></th>
      <th>Gerekli Zaman Karmaşıklığı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$n \le 10$</td>
      <td><code class="language-plaintext highlighter-rouge">O(n!)</code></td>
    </tr>
    <tr>
      <td>$n \le 20$</td>
      <td><code class="language-plaintext highlighter-rouge">O(2^n)</code></td>
    </tr>
    <tr>
      <td>$n \le 500$</td>
      <td><code class="language-plaintext highlighter-rouge">O(n^3)</code></td>
    </tr>
    <tr>
      <td>$n \le 5000$</td>
      <td><code class="language-plaintext highlighter-rouge">O(n^2)</code></td>
    </tr>
    <tr>
      <td>$n \le 10^6$</td>
      <td><code class="language-plaintext highlighter-rouge">O(n log n)</code> veya <code class="language-plaintext highlighter-rouge">O(n)</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">n</code> çok büyük</td>
      <td><code class="language-plaintext highlighter-rouge">O(1)</code> veya <code class="language-plaintext highlighter-rouge">O(log n)</code></td>
    </tr>
  </tbody>
</table>

<h2 id="24-en-büyük-alt-dizi-toplamı">2.4 En Büyük Alt Dizi Toplamı</h2>

<p>Problem: n elemanlı bir dizide, elemanları ardışık olan ve toplamı en büyük olan alt dizinin toplamını bulmak.</p>

<p>Örneğin, <code class="language-plaintext highlighter-rouge">[-1, 2, 4, -3, 5, 2, -5, 2]</code> dizisi için en büyük alt dizi toplamı <code class="language-plaintext highlighter-rouge">[2, 4, -3, 5, 2]</code> alt dizisinden gelen 10’dur.</p>

<h3 id="1-algoritma-on3">1. Algoritma: $O(n^3)$</h3>

<p>Tüm olası alt dizileri üç iç içe döngü ile deneyen kaba kuvvet çözümü.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">best</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">a</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;=</span> <span class="n">b</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">sum</span> <span class="o">+=</span> <span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="n">best</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">best</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="2-algoritma-on2">2. Algoritma: $O(n^2)$</h3>

<p>İçteki döngülerden birini kaldırarak iyileştirilmiş çözüm.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">best</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">a</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">sum</span> <span class="o">+=</span> <span class="n">array</span><span class="p">[</span><span class="n">b</span><span class="p">];</span>
    <span class="n">best</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">best</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="3-algoritma-on-kadanein-algoritması">3. Algoritma: $O(n)$ (Kadane’in Algoritması)</h3>

<p>Her pozisyonda biten en büyük alt dizi toplamını bularak ilerleyen verimli çözüm.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">best</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">sum</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">],</span> <span class="n">sum</span> <span class="o">+</span> <span class="n">array</span><span class="p">[</span><span class="n">k</span><span class="p">]);</span>
  <span class="n">best</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">best</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
</code></pre></div></div>
<p>Bu, problem için mümkün olan en iyi zaman karmaşıklığıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="bilim" /><category term="c" /><category term="programlama" /><category term="giriş" /><category term="algoritma" /><category term="olimpiyat" /><category term="zaman" /><category term="karmaşıklık" /><category term="sınıf" /><category term="rekürsif" /><category term="yarışma" /><category term="rekabet" /><category term="kodlama" /><category term="matematik" /><category term="kitap" /><summary type="html"><![CDATA[Rekabetçi programlamada algoritmaların verimliliği önemlidir. Genelde soruyu çözen yavaş bir algoritma oluşturmak kolaydır ama asıl zorluk hızlı bir algoritma oluşturmaktır. Eğer algoritma çok yavaşsa ya sorudan kısmi puan alacaktır ya da hiç almayacaktır.]]></summary></entry></feed>