Counting Bits
Dla danej liczby całkowitej n, która jest równa 0 lub większa, zwróć tablicę zawierającą n+1 elementów, w której element i jest liczbą jedynek w binarnym zapisie liczby i, dla każdej wartości i od 0 do n.
Funkcja
- ninteger
- ostatnia liczba do zliczenia, 0 lub więcej
- Zwracainteger-array
- tablica n+1 liczników, gdzie element i jest liczbą bitów o wartości 1 w i
Ograniczenia
0 ≤ n ≤ 2 × 104
Przykłady
- Wejście
- n = 2
- Wyjście
- [0, 1, 1]
- Wyjaśnienie
- W systemie binarnym 0 to
0, 1 to1, a 2 to10: brak jedynek, potem jedna, a następnie jedna.
- Wejście
- n = 5
- Wyjście
- [0, 1, 1, 2, 1, 2]
- Wyjaśnienie
- 3 to
11, a 5 to101— po dwie jedynki; 4 to100— jedna. Dla 0, 1 i 2 z pierwszego przykładu liczby jedynek wynoszą 0, 1, 1, 2, 1, 2.
Pytanie dodatkowe
Czy potrafisz wypełnić całą tablicę w czasie O(n), bez użycia wbudowanej funkcji zliczającej bity i bez zliczania bitów każdej liczby od początku?
Podpowiedzi
Otwieraj je po kolei. Każda zdradza trochę więcej.
Zapisz liczby od 0 do 8 w systemie binarnym i porównaj liczbę z liczbą otrzymaną po usunięciu jej ostatniej cyfry. 6 to
110, a 3 to11. Jak porównują się liczby jedynek w tych zapisach?Przesunięcie w prawo o jeden bit,
i >> 1, usuwa ostatnią cyfrę binarnąi. Liczba dlaito liczba dlai >> 1powiększona o tę ostatnią cyfrę, czylii & 1.Wypełnij tablicę, zaczynając od 0 i idąc w górę. Gdy dojdziesz do
i, wpis dlai >> 1jest już wypełniony, ponieważ jest mniejszy, więc każdy wpis wymaga jednego wyszukania i jednego dodawania.
Rozwiązanie
Liczenie każdej liczby od zera powtarza pracę: 13 to 1101, bity liczby 6 (110) z jedną dodatkową cyfrą na końcu. Wpisuj odpowiedzi w kolejności rosnącej, a każdy kolejny wpis wykorzystuje mniejszą wartość i jedno dodawanie.
Policz bity każdej liczby
Intuicja
Policz bezpośrednio jedynki w zapisie binarnym każdej liczby: dodaj x & 1 do licznika, przesuń x w prawo o jeden bit i zakończ na 0. Dla 13 (1101) bity to kolejno 1, 0, 1, 1, więc ich liczba wynosi 3.
Liczba nie większa niż n ma około log2 n cyfr binarnych, więc działanie ma złożoność O(n log n): około 20 000 × 15 = 300 000 kroków dla n = 2 × 10^4.
Algorytm
- Utwórz pustą listę wyników.
- Dla każdego
iod 0 donustawcountna 0, axnai. - Gdy
xjest większe od 0, dodajx & 1docounti przesuńxw prawo o jeden bit. - Dodaj
countdo wyniku. - Zwróć wynik.
def countBits(n):
bits = []
for i in range(n + 1):
count = 0
x = i
while x > 0:
count += x & 1 # the lowest bit
x >>= 1 # shift it out
bits.append(count)
return bitsZbuduj na połowie liczby
Intuicja
Przesunięcie i w prawo o jeden usuwa jego ostatnią cyfrę binarną, a i & 1 to ta cyfra. Zatem i ma jedynki z i >> 1 plus swoją ostatnią cyfrę: bits[i] = bits[i >> 1] + (i & 1).
Dla każdego i większego lub równego 1 wartość i >> 1 jest mniejsza niż i, więc wypełnianie tablicy od lewej do prawej, zaczynając od bits[0] = 0, oznacza, że odczytywany wpis jest zawsze gotowy. Dla n = 5: bits[3] = bits[1] + 1 = 2, bits[4] = bits[2] + 0 = 1, bits[5] = bits[2] + 1 = 2.
Każdy wpis wymaga jednego przesunięcia, jednego AND i jednego dodawania: czas O(n) i brak dodatkowej pamięci poza wynikiem.
Algorytm
- Utwórz tablicę
bitszłożoną zn+1zer.bits[0]pozostaje równe 0. - Dla
iod 1 donustawbits[i]nabits[i >> 1] + (i & 1). - Zwróć
bits.
def countBits(n):
bits = [0] * (n + 1)
for i in range(1, n + 1):
# i >> 1 is i without its last bit, and i & 1 is that last bit
bits[i] = bits[i >> 1] + (i & 1)
return bits
Pułapki i przypadki brzegowe
- Zwrócenie
nelementów zamiastn+1. Dlan= 0 odpowiedzią jest[0]. - Kolejność operatorów. W Pythonie, C, Javie i JavaScript operator
+ma wyższy priorytet niż&, więcbits[i >> 1] + i & 1oznacza(bits[i >> 1] + i) & 1. Zachowaj nawiasy wokół(i & 1). - Odczytywanie
bits[i-1]zamiastbits[i >> 1]. Sąsiednie liczby nie mają prostej wspólnej reguły: 7 to111z trzema jedynkami, a 8 to1000z jedną. - W Lua i R tablice zaczynają się od indeksu 1, więc liczba jedynek dla
iznajduje się pod indeksemi+1, a odczyt dlai >> 1pod indeksemfloor(i/2) + 1. Lua 5.1 nie ma operatora przesunięcia, więc dziel przez 2, używającmath.floor(i / 2).
Najczęstsze pytania3
Jaka jest złożoność czasowa algorytmu zliczania bitów?
Najlepsze rozwiązanie działa w czasie O(n): każdy z n+1 wpisów powstaje z jednego wcześniejszego wpisu przez dodanie jedynki. Zliczanie bitów każdej liczby osobno zajmuje O(n log n). Oba rozwiązania wykorzystują O(1) pamięci poza tablicą wynikową.
Czy istnieje inna rekurencja O(n) dla zliczania bitów?
Tak. i & (i-1) zeruje najniższy bit 1 w i, więc bits[i] = bits[i & (i-1)] + 1 dla każdego i większego lub równego 1. Dla 12 (1100) 12 & 11 wynosi 8 (1000), które zawiera jedną jedynkę, więc 12 zawiera dwie.
Czy mogę użyć wbudowanej funkcji popcount?
Wywołanie jej dla każdej liczby, na przykład Integer.bitCount w Javie lub __builtin_popcount w C i C++, daje poprawną odpowiedź. Osoby prowadzące rozmowę kwalifikacyjną zwykle proszą o wersję bez niej, ponieważ istotą problemu jest ponowne wykorzystanie już obliczonych odpowiedzi.
Podobne zadania
Zadania oparte na tych samych pomysłach. Rozwiązanie dwóch lub trzech utrwala schemat.
def countBits(n):
# Napisz kod tutajTest #1
Test #2
Argument
n = 2
Oczekiwany wynik
[0, 1, 1]