Struktury danych
Baza pytań rekrutacyjnych i wiedzy. Filtruj, szukaj i sprawdzaj swoją wiedzę.
easyarraylinked-listcomparison+1
Odpowiedź
Tablica (lub dynamiczna tablica/ArrayList) trzyma elementy obok siebie w pamięci, więc dostęp losowy to O(1). Wstawianie/usuwanie w środku to O(n), bo trzeba przesuwać elementy (a przy powiększaniu często kopiować). Lista wiązana ma węzły połączone wskaźnikami: wstawienie/usunięcie w znanym miejscu to O(1), ale dostęp losowy to O(n) i jest większy narzut pamięci.
easyjsonformatexample
Odpowiedź
Poprawny JSON to tekstowy format obiektów i tablic używający {} i [], gdzie klucze i wartości tekstowe są w podwójnych cudzysłowach, a wartości mogą być tylko typu: string, number, boolean, null, tablica lub obiekt. Przykład: { "name": "Jan", "age": 30, "roles": ["dev"], "active": true }.
{
"name": "John Doe",
"age": 30,
"isEmployed": true,
"roles": ["developer", "admin"],
"address": {
"city": "New York",
"zip": "10001"
}
}easystackqueuedata-structure+1
Odpowiedź
Stos to struktura LIFO, gdzie odkładasz i zdejmujesz elementy z wierzchołka (push/pop); używana np. w stosie wywołań, undo czy DFS. Kolejka to FIFO, gdzie dodajesz na końcu (enqueue) i zdejmujesz z początku (dequeue); używana do kolejkowania zadań, buforowania czy BFS.
mediumhashmaphashingcollision+1
Odpowiedź
HashMap trzyma pary klucz–wartość w kubełkach. Używa hashCode() klucza, aby wybrać kubełek, a equals() żeby znaleźć właściwy klucz w kubełku. Kolizje trzyma w liście/drzewku; po przekroczeniu progu load factor powiększa się i przelicza hashe, aby średnio utrzymać dostęp blisko O(1).
hardtreebinary-search-treebalancing+1
Odpowiedź
Drzewa zrównoważone (np. AVL, Red‑Black) to samo‑równoważące drzewa BST, które po wstawieniach i usunięciach wykonują rotacje, aby wysokość była rzędu log n. Gwarantuje to operacje wyszukiwania, wstawiania i usuwania w O(log n) nawet w pesymistycznym przypadku.
easysetdeduplicationmembership
Odpowiedź
Set przechowuje unikalne wartości (bez duplikatów). Użyj go do szybkiego sprawdzania „czy element istnieje” i do usuwania duplikatów (np. unikalne ID użytkowników).
const ids = new Set<number>();
ids.add(42);
console.log(ids.has(42)); // trueeasypriority-queueheapbig-o
Odpowiedź
Kolejka priorytetowa wyjmuje element o najwyższym (lub najniższym) priorytecie, a nie najstarszy. Zwykle jest zaimplementowana kopcem, więc wstawienie/usunięcie to O(log n).
import java.util.PriorityQueue;
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(5);
pq.add(1);
System.out.println(pq.poll()); // 1easydequequeuering-buffer
Odpowiedź
Deque pozwala dodawać i usuwać elementy zarówno z przodu, jak i z tyłu. Przy implementacji jako bufor cykliczny lub lista te operacje są O(1).
mediumgraphadjacency-listadjacency-matrix+1
Odpowiedź
Macierz zajmuje O(V^2) pamięci i daje O(1) sprawdzenie krawędzi, więc jest dobra dla gęstych grafów. Lista zajmuje O(V+E) pamięci i lepiej pasuje do rzadkich grafów oraz iterowania po sąsiadach.
mediumtrieprefixautocomplete
Odpowiedź
Trie przechowuje napisy po prefiksach: każdy węzeł reprezentuje znak. Umożliwia szybkie wyszukiwanie po prefiksie/autouzupełnianie; wyszukiwanie to O(L), gdzie L to długość słowa.
mediumhash-tablecollisionchaining+1
Odpowiedź
Przy chaining każdy kubełek trzyma listę/drzewo wpisów z tym samym hashem. Przy open addressing kolizje rozwiązuje się przez próbkowanie innych pól (linear/quadratic/double hashing). Chaining jest prostszy przy większym obciążeniu; open addressing jest cache-friendly, ale wymaga niższego load factor.
harddynamic-arrayamortizedresizing+1
Odpowiedź
Większość dopisań trafia w wolne miejsce i kosztuje O(1). Czasem następuje powiększenie i kopiowanie O(n) elementów; rozłożone na wiele dopisań daje średnio O(1) na operację (amortyzacja).
let capacity = 4;
let size = 0;
function append(x: number) {
if (size === capacity) {
capacity *= 2; // resize + copy O(n)
}
size++;
}hardavlred-blackbalancing+1
Odpowiedź
AVL utrzymuje bardziej rygorystyczne zrównoważenie, więc wyszukiwanie bywa szybsze, ale wstawienia/usunięcia częściej wymagają równoważenia. Red-Black ma luźniejsze reguły, dzięki czemu aktualizacje są tańsze, a wysokość nadal jest O(log n).
hardbloom-filterprobabilistichashing+1
Odpowiedź
Bloom filter to probabilistyczny zbiór do testów przynależności. Może zwracać fałszywie dodatnie wyniki (powie „jest”, choć nie ma), ale nie ma fałszywych negatywów. Jest bardzo oszczędny pamięciowo i szybki, ale nie pozwala odtworzyć elementów.
hardunion-finddsukruskal+1
Odpowiedź
Union-Find (DSU) utrzymuje rozłączne zbiory operacjami find(x) (reprezentant) i union(a,b) (scalenie). Używa się go do spójności i w algorytmie Kruskala; z path compression + union by rank operacje są prawie O(1) amortyzowane.
class DSU {
private parent: number[];
constructor(n: number) {
this.parent = Array.from({ length: n }, (_, i) => i);
}
find(x: number): number {
if (this.parent[x] === x) return x;
this.parent[x] = this.find(this.parent[x]);
return this.parent[x];
}
union(a: number, b: number) {
const ra = this.find(a);
const rb = this.find(b);
if (ra !== rb) this.parent[rb] = ra;
}
}easymapdictionaryhashmap+1
Odpowiedź
Map przechowuje wartości pod kluczem (klucz → wartość). Użyj go, gdy chcesz szybko wyszukiwać po identyfikatorze (np. email → użytkownik), a nie po indeksie liczbowym; hash map zwykle ma O(1) średnio dla get/put.
mediumlrucachehashmap+2
Odpowiedź
LRU (Least Recently Used) usuwa element, którego najdłużej nie używano. Typowa implementacja w O(1): HashMap do lookupu key→węzeł + lista dwukierunkowa, aby po użyciu przesuwać element na początek i usuwać z końca.
type Node = { key: string; prev?: Node; next?: Node }
// idea:
// map: key -> node
// list: head <-> ... <-> tail
// get(key): move node to head (O(1))
// put(key): insert/move to head; if over capacity, evict tail (O(1))mediumring-bufferqueueproducer-consumer
Odpowiedź
Ring buffer to tablica o stałym rozmiarze używana jak kolejka z wskaźnikami head/tail, które zawijają się (mod pojemność). Przydaje się do strumieni i kolejek producer/consumer, bo unika alokacji i daje O(1) enqueue/dequeue.
hardfenwickbitprefix-sum+1
Odpowiedź
Drzewo Fenwicka wspiera sumy prefiksowe i aktualizacje punktowe w O(log n) przy pamięci O(n). Użyj go, gdy masz dużo update’ów i zapytań typu „suma [0..i]” (np. problemy z częstotliwościami/licznikami).
class Fenwick {
private bit: number[];
constructor(n: number) {
this.bit = Array(n + 1).fill(0);
}
add(i: number, delta: number) {
for (let x = i + 1; x < this.bit.length; x += x & -x) this.bit[x] += delta;
}
sum(i: number) {
let res = 0;
for (let x = i + 1; x > 0; x -= x & -x) res += this.bit[x];
return res;
}
}hardskip-listlinked-listprobabilistic+1
Odpowiedź
Skip list to wielopoziomowa lista, gdzie część węzłów jest losowo „promowana” do wyższych poziomów. Daje oczekiwane O(log n) dla search/insert/delete (jak zbalansowane drzewa), jest prostsza w implementacji, ale ma probabilistyczną wydajność.
easyheapbinary-heappriority-queue+1
Odpowiedź
W min-heap każdy węzeł jest ≤ swoich dzieci (najmniejszy element jest w korzeniu). W max-heap każdy węzeł jest ≥ swoich dzieci. Dzięki temu peek jest O(1), a insert/remove O(log n).
mediumsegment-treerange-querybig-o
Odpowiedź
Segment tree przechowuje agregaty na przedziałach (sum/min/max) i wspiera zapytania o zakresy oraz aktualizacje punktowe w O(log n). Przydaje się, gdy masz dużo zapytań typu „suma na [l..r]” i jednocześnie update’y.
mediumhash-tableload-factorrehash+1
Odpowiedź
Load factor to w przybliżeniu `liczba elementów / liczba kubełków`. Gdy jest zbyt wysoki, rośnie liczba kolizji i operacje zwalniają. Resize zwiększa liczbę kubełków i robi rehash, żeby utrzymać średnio O(1).
hardb-treebstdisk-io+1
Odpowiedź
B-tree ma duży współczynnik rozgałęzienia, więc jest płytkie i robi mniej IO dyskowego (mniej odczytów stron). BST jest „wskaźnikowe” i głębsze; w pamięci działa OK, ale na dysku przegrywa z B-tree dopasowanym do stron.
hardstringssuffix-arraysuffix-tree+1
Odpowiedź
To struktury do pracy ze stringami, używane do szybkich zapytań o podciągi i wzorce (np. „czy wzorzec P występuje w tekście T?”). Stosuje się je w indeksowaniu tekstu, wyszukiwaniu i bioinformatyce.
easybitsetbitmapmemory+1
Odpowiedź
Bitset (bitmapa) przechowuje wartości true/false jako bity, więc jest bardzo oszczędny pamięciowo. Sprawdza się do szybkiej przynależności w małym, gęstym zakresie liczb (np. ID 0..N) oraz do szybkich operacji bitowych (AND/OR).
mediumropestringdata-structure+1
Odpowiedź
Rope reprezentuje długi napis jako drzewo mniejszych fragmentów. Może przyspieszać konkatenację oraz wstawianie/usuwanie w środku (bez kopiowania całego stringa), kosztem większej złożoności i zwykle wolniejszego dostępu po indeksie.
mediumimmutablepersistentstructural-sharing+1
Odpowiedź
Persistent (niezmienna) struktura danych zwraca nową wersję przy “modyfikacji”, zamiast zmieniać się w miejscu. Structural sharing oznacza, że nowa wersja współdzieli większość węzłów ze starą, więc “kopiowanie” jest tanie (przydatne do historii/undo i bezpieczniejsze przy współbieżności).
hardsparse-tablermqpreprocessing+1
Odpowiedź
Sparse table prelicza wyniki dla przedziałów o długości 2^k, dzięki czemu można szybko odpowiadać na statyczne zapytania o przedziały. Dla operacji idempotentnych (np. min/max/gcd) zapytanie jest O(1) po preprocessingu O(n log n), ale nie wspiera wydajnych aktualizacji.
hardhashingcuckoo-hashinghash-table+1
Odpowiedź
Cuckoo hashing używa dwóch (lub więcej) funkcji hashujących, więc każdy klucz może leżeć w jednym z kilku miejsc. Odczyt jest prosty i O(1), ale wstawianie może wywołać “wypychanie” elementów łańcuchem; czasem trzeba zrobić rehash/resize, gdy pojawi się cykl.
easylinked-listsinglydoubly+1
Odpowiedź
Lista jednokierunkowa ma tylko wskaźnik `next`, więc jest prostsza i zużywa mniej pamięci. Lista dwukierunkowa ma `prev` i `next`, co ułatwia usuwanie znanego węzła oraz chodzenie wstecz, ale kosztuje więcej pamięci i aktualizacji wskaźników. Jednokierunkowa: gdy wystarczy przejście w przód. Dwukierunkowa: gdy często usuwasz w środku albo potrzebujesz przejścia wstecz.
mediumhash-tablerehashlatency+1
Odpowiedź
Resize często oznacza alokację większej tablicy i przeliczenie (rehash) oraz przeniesienie wszystkich wpisów, czyli O(n) pracy naraz. To może zrobić pauzę i skok latencji. Jak temu zapobiegać: rozsądny load factor, pre-size gdy znasz rozmiar, inkrementalny rehash (przenoszenie stopniowo) albo użycie implementacji współbieżnej, która rozkłada pracę w czasie.
mediumheapheapifycomplexity+1
Odpowiedź
Jeśli budujesz kopiec metodą bottom-up (heapify), to większość węzłów jest blisko liści i przesuwa się o małą liczbę poziomów. Suma pracy dla wszystkich węzłów tworzy malejącą serię, która daje O(n). Wstawianie elementów po kolei to O(n log n), ale heapify bottom-up to O(n).
hardb-treeb-plus-treeindexes+1
Odpowiedź
W B+ tree wszystkie rekordy (albo wskaźniki do rekordów) są w liściach, a węzły wewnętrzne trzymają tylko klucze do nawigacji. Liście są zwykle połączone, więc skany zakresowe są bardzo szybkie. To pasuje do dysku: duża liczba dzieci daje płytkie drzewo, a połączone liście ułatwiają szybkie odczyty po kolei.
hardradix-treepatriciatrie+1
Odpowiedź
Radix tree to “skompresowane” trie: ciągi węzłów z jednym dzieckiem są łączone w jedną krawędź opisaną fragmentem napisu. Oszczędza pamięć i zmniejsza liczbę skoków po wskaźnikach, zachowując szybkie wyszukiwanie po prefiksie. Używa się go np. w tablicach routingu i wyszukiwaniu po prefiksach.
easydequequeuestack+1
Odpowiedź
Deque (double-ended queue) pozwala dodawać i usuwać elementy zarówno z przodu, jak i z tyłu. Przydaje się, gdy potrzebujesz jednocześnie zachowań stosu i kolejki lub w algorytmach typu sliding window/monotonic queue.
mediumhash-tablecollisionschaining+1
Odpowiedź
Separate chaining trzyma kolidujące klucze w liście (lub drzewie) w kubełku. Open addressing przechowuje wszystkie wpisy w tablicy i szuka alternatywnych slotów (np. linear/quadratic probing, double hashing). Chaining jest prostsze przy usuwaniu; open addressing bywa bardziej cache‑friendly, ale jest wrażliwe na load factor.
mediumtrieprefixautocomplete+1
Odpowiedź
Trie przechowuje napisy jako ścieżki znaków; każda krawędź to znak. Zapytania po prefiksie są szybkie, bo wszystkie słowa z tym samym prefiksem współdzielą ścieżkę, więc lookup to O(L), gdzie L to długość klucza. Minusem jest wyższe zużycie pamięci niż przy hashowaniu.
easypriority-queueheapordering+1
Odpowiedź
Kolejka priorytetowa wspiera insert, podgląd (min/max) i usuwanie (min/max). Najczęściej jest implementowana kopcem binarnym, co daje O(log n) dla insert/extract i O(1) dla peek.
mediumgraphadjacency-listadjacency-matrix+1
Odpowiedź
Listę sąsiedztwa wybierasz dla grafów rzadkich, bo ma pamięć O(V + E) i szybko iteruje sąsiadów. Macierz sąsiedztwa jest dobra dla grafów gęstych lub gdy potrzebujesz O(1) sprawdzenia, czy krawędź istnieje; kosztuje O(V^2) pamięci.
hardsegment-treerange-queryupdates+1
Odpowiedź
Segment tree to drzewo binarne nad przedziałami tablicy. Umożliwia zapytania zakresowe (suma/min/max) i aktualizacje punktowe w O(log n) po zbudowaniu w O(n). Z lazy propagation potrafi też obsługiwać aktualizacje zakresowe.
class SegTree {
private tree: number[];
private n: number;
constructor(n: number) {
this.n = n;
this.tree = Array(4 * n).fill(0);
}
update(node: number, l: number, r: number, idx: number, val: number) {
if (l === r) {
this.tree[node] = val;
return;
}
const mid = Math.floor((l + r) / 2);
if (idx <= mid) this.update(node * 2, l, mid, idx, val);
else this.update(node * 2 + 1, mid + 1, r, idx, val);
this.tree[node] = this.tree[node * 2] + this.tree[node * 2 + 1];
}
}mediumheapbstpriority-queue+1
Odpowiedź
Kopiec binarny świetnie nadaje się do min/max: peek to O(1), a insert/extract to O(log n). (Zbalansowane) BST wspiera porządkowy traversal i wyszukiwanie po kluczu w O(log n). Kopce nie nadają się do szybkiego wyszukiwania ani zapytań zakresowych.
easymaptreemaphashmap+1
Odpowiedź
Mapę uporządkowaną wybierasz, gdy potrzebujesz kluczy w kolejności lub zapytań zakresowych (np. klucze między A i B). TreeMap daje O(log n) i porządek; HashMap ma średnio O(1), ale nie zachowuje porządku.
mediumsparse-matrixcsrcoo+1
Odpowiedź
Formaty rzadkie (CSR/COO) używa się, gdy większość wpisów to zera. Przechowują tylko wartości niezerowe i ich pozycje, co oszczędza pamięć i przyspiesza operacje typu mnożenie macierz‑wektor. Minusem jest wolniejszy losowy dostęp i trudniejsze aktualizacje.