0Pricing
DSA Interview Prep · Ders

Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme

DP ve en düşük ayarlı bit yöntemini kullanarak 0..n için bit sayılarını hesaplayın, XOR ile eksik sayıyı bulun ve 32 bitlik bir tamsayının bitlerini ters çevirin.

Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme, CoddyKit'te ücretsiz bir DSA Interview Prep dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, DSA Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DSA Interview Prep kursu toplamda 4 dersten oluşur.

Bit Sayma Probleminin Genel Bakışı

Bit Sayma problemi (LeetCode 338) şunu ister: n verildiğinde, ans boyutu n+1 olan bir dizi döndürün; burada ans[i], i içindeki 1 bitlerinin sayısıdır. Naif yaklaşım O(n log n) zaman alır; her sayıdaki bitleri tek tek sayar. DP yaklaşımı, i ile yarısı veya en düşük 1 biti arasındaki ilişkiden yararlanarak O(n) zamanda çalışır.

DP'nin temelinde iki önemli gözlem vardır: (1) i >> 1 en düşük anlamlı biti kaldırır; bu nedenle bits[i] = bits[i >> 1] + (i & 1). (2) En düşük 1 bitini temizleme: bits[i] = bits[i & (i-1)] + 1. Her iki yöntem de O(n) zaman ve O(n) alan kullanır (çıktı dizisi için).

def count_bits_v1(n):
    # O(n log n): naive individual count
    return [bin(i).count('1') for i in range(n + 1)]

def count_bits_dp(n):
    # O(n): DP using right shift
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        dp[i] = dp[i >> 1] + (i & 1)   # i >> 1 drops last bit
    return dp

def count_bits_dp2(n):
    # O(n): DP using lowest-set-bit trick
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        dp[i] = dp[i & (i - 1)] + 1   # i & (i-1) clears lowest set bit
    return dp

n = 10
print('Naive:', count_bits_v1(n))
print('DP v1:', count_bits_dp(n))
print('DP v2:', count_bits_dp2(n))

DP Yineleme Bağıntıları Neden Çalışır

Sağa kaydırma yineleme bağıntısı dp[i] = dp[i >> 1] + (i & 1) için: 2'ye bölme (sağa kaydırma) son biti kaldırır. Son bit 1 ise sayıma 1 eklenir; 0 ise değişiklik olmaz. Böylece bits[i] = bits[i // 2] + (i mod 2) elde edilir.

En düşük 1 biti yineleme bağıntısı dp[i] = dp[i & (i-1)] + 1 için: i & (i-1) en sağdaki 1 bitini temizler; dolayısıyla i'den bir eksik 1 bite sahiptir. Bu nedenle sonuç, azaltılmış değerin sayımına 1 eklenerek bulunur. Her iki yineleme bağıntısı da i değerlerini artan sırada işler; böylece küçük alt problemler her zaman önce çözülür.

# Trace both recurrences for i = 0..8
print('i | i>>1 | i&1 | dp[i>>1]+(i&1) | i&(i-1) | 1+dp[i&(i-1)]')
print('-' * 60)
dp = [0] * 9
for i in range(1, 9):
    # Right shift method
    v1 = dp[i >> 1] + (i & 1)
    # Lowest set bit method
    v2 = dp[i & (i - 1)] + 1
    dp[i] = v1   # either works
    print(f'{i:2d} ({bin(i)[2:]:4s}) | {i>>1:2d} | {i&1} | {v1}               | {i&(i-1):2d}      | {v2}')
print('\nFinal dp:', dp)

Eksik Sayı: XOR ve Toplam Yaklaşımları

Eksik Sayı problemi (LeetCode 268), [0, n] aralığında yer alan n farklı sayıdan oluşan ve tam olarak bir sayısı eksik olan bir dizi verir. XOR yaklaşımı: 0..n arasındaki tüm indisleri, dizideki tüm değerlerle XOR işlemine tabi tutun. Eşleşen çiftler birbirini götürür ve geriye eksik sayı kalır. Toplam yaklaşımı: expected = n*(n+1)//2 değerini hesaplayın ve expected - sum(nums) değerini döndürün.

Her iki yaklaşım da O(n) zaman ve O(1) alan kullanır. XOR yaklaşımı, sabit genişlikli tamsayılar kullanan dillerde taşma olasılığını önlediği için daha güvenilirdir. Python'da tamsayılar sınırsız duyarlığa sahip olduğundan iki yaklaşım da sorunsuz çalışır.

def missing_xor(nums):
    n = len(nums)
    result = n
    for i, val in enumerate(nums):
        result ^= i ^ val   # each index i cancels its matching value
    return result

def missing_sum(nums):
    n = len(nums)
    return n * (n + 1) // 2 - sum(nums)

test_cases = [
    [3, 0, 1],           # missing 2
    [0, 1],              # missing 2
    [9,6,4,2,3,5,7,0,1], # missing 8
    [0],                 # missing 1
]
for nums in test_cases:
    print(f'{nums} => XOR={missing_xor(nums)}, Sum={missing_sum(nums)}')

32 Bitlik Tamsayıda Bitleri Tersine Çevirme

Bitleri Tersine Çevirme problemi (LeetCode 190), 32 bitlik işaretsiz bir tamsayının ikili gösterimini tersine çevirmenizi ister. Yinelemeli yaklaşımda: girdideki 32 bitin her birini sağdan sola işleyip çıktıdaki bitleri soldan sağa yerleştirin. Her yinelemede en sağdaki biti n & 1 ile çıkarın, yer açmak için çıktıyı sola kaydırın, bit üzerinde OR işlemi yapın ve ardından n'yi sağa kaydırın.

32 yinelemeden sonra çıktı tamsayısı, n'nin 32 bitinin tamamını ters sırada içerir. Bu işlem çağrı başına O(32) = O(1) zaman alır; 8 bitlik parçalar için önbellekleme kullanıldığında tekrarlanan çağrılarda itfa edilmiş karmaşıklık O(1) olur.

def reverse_bits(n):
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1)  # shift result left, OR in rightmost bit
        n >>= 1                            # move to next bit
    return result

# Test with known values
print(reverse_bits(0b00000010100101000001111010011100))  # 964176192
print(reverse_bits(0b11111111111111111111111111111101))  # 3221225471
print(reverse_bits(0))   # 0
print(reverse_bits(1))   # 2147483648 (bit 0 goes to bit 31)
print(reverse_bits(0b10000000000000000000000000000000))  # 1

Bitleri Tersine Çevirme: Böl ve Yönet

Daha hızlı olan O(log 32) = O(1) yaklaşımı, bitleri böl ve yönet takası kullanarak tersine çevirir. Önce komşu bitleri, ardından komşu 2 bitlik grupları, sonra 4 bitlik grupları ve bu şekilde devam ederek takas edin. Her takas düzeyi, dönüşümlü grupları ayırmak ve bunları iç içe geçirmek için maskeler ve kaydırma kullanır. Beş takastan sonra 32 bitin tamamı tersine çevrilmiş olur.

Bu yaklaşım, girdiden bağımsız olarak O(1) sabit sayıda işlem kullanır ve donanım uygulamalarında tercih edilir. Maskeler sabitlerdir: 0x55555555 (dönüşümlü 01 deseni), 0x33333333 (dönüşümlü 0011), 0x0f0f0f0f (dönüşümlü 00001111) ve diğerleri.

def reverse_bits_dc(n):
    # Treat n as 32-bit unsigned
    n &= 0xFFFFFFFF
    # Swap adjacent bits
    n = ((n & 0x55555555) << 1)  | ((n >> 1)  & 0x55555555)
    # Swap adjacent 2-bit groups
    n = ((n & 0x33333333) << 2)  | ((n >> 2)  & 0x33333333)
    # Swap adjacent 4-bit groups
    n = ((n & 0x0f0f0f0f) << 4)  | ((n >> 4)  & 0x0f0f0f0f)
    # Swap adjacent bytes
    n = ((n & 0x00ff00ff) << 8)  | ((n >> 8)  & 0x00ff00ff)
    # Swap adjacent 16-bit halves
    n = ((n & 0x0000ffff) << 16) | ((n >> 16) & 0x0000ffff)
    return n & 0xFFFFFFFF

# Verify against iterative version
def reverse_bits_iter(n):
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1); n >>= 1
    return result

for test in [0b10110100, 0b11111111, 0, 1, 0xDEADBEEF]:
    assert reverse_bits_dc(test) == reverse_bits_iter(test)
    print(f'{test:#010x} reversed: {reverse_bits_dc(test):#010x}')

1 Bitlerinin Sayısı (Hamming Ağırlığı)

1 Bitlerinin Sayısı problemi (LeetCode 191), işaretsiz bir tamsayının Hamming ağırlığını (popcount) bulmanızı ister. Farklı ödünleşimlere sahip üç yaklaşım vardır: naif döngü (O(32)), Brian Kernighan yöntemi (k = 1 bitlerinin sayısı olmak üzere O(k)) ve Python'un yerleşik n.bit_count() işlevi (3.10+).

Brian Kernighan yöntemi, n & (n-1) tekniğini anladığınızı gösterdiği için mülakatlarda tercih edilir. Her yineleme en düşük 1 bitini kaldırır; bu nedenle döngü, tam 32 bitlik taramadan çok daha hızlı olacak şekilde, tam olarak 1 bitlerinin sayısı kadar çalışır.

def hamming_weight_naive(n):
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count

def hamming_weight_kernighan(n):
    count = 0
    while n:
        n &= n - 1   # clear lowest set bit
        count += 1
    return count

# Python 3.10+
# def hamming_weight_builtin(n): return n.bit_count()

for n in [0, 1, 11, 128, 255, 0xDEADBEEF]:
    naive = hamming_weight_naive(n)
    kern  = hamming_weight_kernighan(n)
    bits  = bin(n).count('1')
    print(f'{n:#012b} ({n:10d}): naive={naive}, kern={kern}, bin={bits}')

Ardışık Bitlerin Toplamı: Önek Yaklaşımı

Bazen [l, r] aralığındaki 1 bitlerini hızlıca saymanız gerekir. 0..n aralığı için 1 bitlerinin önek toplamını oluşturun: prefix[i] = prefix[i-1] + bin(i).count('1'). Ardından [l, r] aralığındaki sayım prefix[r] - prefix[l-1] olur. Böylece O(n) ön işleme sonrasında O(1) aralık sorguları yapılabilir.

Bu yöntem, bir aralık üzerindeki bit tabanlı tüm toplulaştırmalara genellenebilir. Örneğin, [l, r] aralığında çift sayıda 1 biti bulunan sayıları saymak için aynı önek tekniği, ancak farklı bir biriktirme işlevi kullanılır.

def build_bit_prefix(n):
    prefix = [0] * (n + 2)
    for i in range(1, n + 1):
        prefix[i] = prefix[i - 1] + bin(i).count('1')
    return prefix

def count_bits_range(prefix, l, r):
    return prefix[r] - prefix[l - 1]

# Build prefix for 0..15
prefix = build_bit_prefix(15)
print('Prefix sums (set bit counts up to i):')
for i in range(16):
    print(f'  i={i:2d} ({bin(i)[2:]:4s}): bits={bin(i).count("1")}, prefix={prefix[i]}')

# Range queries
print(f'\nSet bits in [5, 10]: {count_bits_range(prefix, 5, 10)}')
print(f'Set bits in [1, 15]: {count_bits_range(prefix, 1, 15)}')

Negatif Sayılar için Bitleri Tersine Çevirme

Python'da tamsayılar işaretlidir ve genişlikleri sınırsızdır. LeetCode probleminde bitleri tersine çevirirken girdiyi 32 bitlik işaretsiz bir tamsayı olarak ele almalıyız. Yalnızca 32 bitin dikkate alınmasını sağlamak için işlemeden önce girdiye & 0xFFFFFFFF uygulayın. Çıktı da işaretsiz 32 bitlik bir tamsayı, yani negatif olmayan bir değer olmalıdır.

İkinin tümleyeni anlamında negatif olabilecek bir Python tamsayısı verilirse önce işaretsiz 32 bitlik gösterimi elde etmek için & 0xFFFFFFFF uygulayın, ardından bitleri tersine çevirin. Sonuç her zaman 0 ile 2^32 - 1 arasında, negatif olmayan bir tamsayıdır.

def reverse_bits_signed_safe(n):
    n &= 0xFFFFFFFF   # treat as 32-bit unsigned
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1)
        n >>= 1
    return result & 0xFFFFFFFF

# Python treats -1 as all 1s in two's complement
print(f'-1 as 32-bit unsigned: {-1 & 0xFFFFFFFF:#010x}')  # 0xffffffff
print(f'Reversed: {reverse_bits_signed_safe(-1):#010x}')   # 0xffffffff (all 1s reversed = all 1s)

# -2 in 32-bit = 0xFFFFFFFE = 11...10
print(f'-2 as 32-bit unsigned: {-2 & 0xFFFFFFFF:#010x}')  # 0xfffffffe
print(f'Reversed: {reverse_bits_signed_safe(-2):#010x}')   # 0x7fffffff

Bit İşleme DP'si: Bit Sayma Örüntüleri

Bit sayma problemi, bit DP'si için genel bir örüntüyü ortaya çıkarır: i'nin daha küçük bir sürümü için yanıtı biliyorsanız, sabit zamanda çalışan bir bit işlemiyle i için yanıtı hesaplayabilirsiniz. Bu örüntü, [0, n] aralığında tam olarak k adet 1 biti bulunan sayıları saymak (ikili listeleme kullanarak) veya her sayıyı bölen en büyük iki kuvvetini bulmak gibi diğer bit sayma problemlerine de genellenebilir.

Bir başka yararlı gözlem şudur: i için 1 biti sayısı, her iki kuvveti aralığında tekrarlanan bir örüntü izler. [2^k, 2^(k+1) - 1] aralığındaki örüntü, [0, 2^k - 1] aralığındakiyle aynıdır; tek fark, her değere 1 eklenmesidir, çünkü bu aralıkta k biti her zaman 1'dir.

# Visualise the repeating pattern
def show_bit_pattern(n):
    bits = [bin(i).count('1') for i in range(n + 1)]
    print('i  | bits | pattern')
    for i, b in enumerate(bits):
        block = i.bit_length() - 1 if i > 0 else 0
        print(f'{i:2d} ({bin(i)[2:]:4s}) | {b} | block {block}')
    return bits

bits = show_bit_pattern(15)
# Verify the pattern: bits[i] = bits[i - highest_power] + 1 for i >= 2^k
print('\nVerify pattern:')
for i in range(1, 16):
    highest_pow = 1 << (i.bit_length() - 1)
    if highest_pow < i:
        prev_i = i - highest_pow
        print(f'bits[{i}] = bits[{prev_i}] + 1 = {bits[prev_i]} + 1 = {bits[i]}')

Üçünü Birleştirme: Bütünleşik Bir Alıştırma

Birçok mülakat problemi, bit saymayı, eksik sayı mantığını ve bitleri tersine çevirmeyi tek bir soruda birleştirir. Örneğin: öğeleri n bitlik tamsayılar olan ve bir öğesi eksik bulunan bir dizi verildiğinde eksik değeri bulun. Ya da bit sayımlarından oluşan bir akış verildiğinde eksik tamsayıyı yeniden oluşturun. Bunlar, hangi alt tekniğin uygulanacağını tanımanızı gerektirir.

Zihinsel bir harita oluşturma alıştırması yapın: bir problem eksik öğeleri bulmaktan söz ediyorsa XOR veya toplamı düşünün. Verimli biçimde 1'leri saymayı söylüyorsa Kernighan yöntemini veya DP'yi düşünün. Bitleri tersine çevirmeyi söylüyorsa yinelemeli yaklaşımı veya böl ve yönet yaklaşımını düşünün. Bunlar, mülakatlarda bit işlemenin üç temel aracıdır.

# Integrated exercise: given bit-count array, find the missing number
# arr[i] = number of 1 bits in i, for all i in 0..n except one
# Reconstruct the missing number

def find_missing_from_bit_counts(bit_counts, n):
    # Rebuild full count array
    full = [bin(i).count('1') for i in range(n + 1)]
    # Find which index is missing by comparing
    for i, count in enumerate(bit_counts):
        if full[i] != count:
            return i - 1  # the entry before the mismatch is missing
    return n  # last element missing

# Simpler: use XOR on indices matching bit counts
# (This is simplified for illustration)
bits = [0,1,1,2,1,2,2,3,0,1]  # bit counts for 0..9 with 8 missing
# Normal: [0,1,1,2,1,2,2,3,1,2]
# Missing is index 8
full = [bin(i).count('1') for i in range(10)]
missing_idx = None
for i in range(10):
    if i >= len(bits) or bits[i] != full[i]:
        missing_idx = i
        break
print(f'Missing number: {missing_idx}')

Bitleri Tersine Çevirmek için Bit Önbellekleme

Bitleri tekrarlı olarak tersine çevirmeniz gerekiyorsa (örneğin bir donanım benzetiminde) sonuçları 8 bitlik parçalar için önbelleğe alın. Her bayt yalnızca 256 farklı değer alabileceğinden 0-255 arasındaki her değer için tersine çevrilmiş baytı önceden hesaplayın. 32 bitlik bir tamsayıyı tersine çevirmek için onu dört adet 8 bitlik parçaya bölün, her parçayı tersine çevirin ve ters sırada yeniden birleştirin.

Bu yöntem, her çağrıyı dört tablo araması ve bit işlemine indirger; toplu işlemede 32 yinelemeli bir döngüden çok daha hızlıdır. Önbellek O(256 × 8) zamanda bir kez oluşturulur ve sonraki tüm çağrılarda O(1) zamanda yeniden kullanılır.

# Build 8-bit reverse cache
def build_reverse_byte_cache():
    cache = [0] * 256
    for i in range(256):
        n, result = i, 0
        for _ in range(8):
            result = (result << 1) | (n & 1)
            n >>= 1
        cache[i] = result
    return cache

cache = build_reverse_byte_cache()

def reverse_bits_cached(n):
    return (cache[n & 0xFF] << 24 |
            cache[(n >> 8) & 0xFF] << 16 |
            cache[(n >> 16) & 0xFF] << 8 |
            cache[(n >> 24) & 0xFF])

# Test
for test in [0b10110100, 0b11111111, 0x12345678]:
    cached  = reverse_bits_cached(test)
    # Reference: iterative
    n, result = test, 0
    for _ in range(32): result = (result << 1) | (n & 1); n >>= 1
    assert cached == result
    print(f'{test:#010x} => {cached:#010x}')

Kısa Kontrol

Bu dersteki Veri Yapıları & Algoritmalar — Kodlama Mülakatına Hazırlık kavramlarını anlayıp anlamadığınızı sınayın.

Ders Özeti

Bu derste şunları öğrendiniz: bit sayma, O(n) zaman için dp[i] = dp[i >> 1] + (i & 1) veya dp[i] = dp[i & (i-1)] + 1 bağıntılarını kullanan DP ile yapılır; eksik sayı, tüm indislerle tüm değerleri XOR işlemine tabi tutarak veya aritmetik toplam formülünü kullanarak O(n)/O(1) içinde bulunur; ayrıca 32 bitin tersine çevrilmesi O(32) zamanda yinelemeli olarak ya da böl ve yönet maskeleme tekniğiyle yapılır. Sırada, artan ve azalan değişmezle başlayarak monoton yığınları ve bir sonraki daha büyük öğe sorgularını inceleyeceğiz.

Sıkça Sorulan Sorular

“Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme” dersi ücretsiz mi?

Evet — “Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve DSA Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DSA Interview Prep kursu toplamda 4 dersten oluşur.

“Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme” dersinde ne öğreneceğim?

DP ve en düşük ayarlı bit yöntemini kullanarak 0..n için bit sayılarını hesaplayın, XOR ile eksik sayıyı bulun ve 32 bitlik bir tamsayının bitlerini ters çevirin. DSA Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

DSA Interview Prep öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te DSA Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.

“Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu DSA Interview Prep dersinde kod yazıp çalıştırabilir miyim?

Evet. Her DSA Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Bit Düzeyi İşleçler: AND, OR, XOR, NOT ve Kaydırmalar
  2. Tek Sayı ve XOR Özellikleri
  3. Bit Maskeleri: Ayarla, Temizle, Değiştir, Denetle
  4. Bitleri Sayma, Eksik Sayı ve Bitleri Ters Çevirme
← DSA Interview Prep Sayfasına Dön