Correct answers and detailed explanations for 75 CS Fundamentals quiz questions
#1·Complexity
Beginner
What is the average-case and worst-case complexity of a hash table lookup?
A
Average O(log n), worst case O(n)
B
Average O(n), worst case O(n log n)
C
Average O(1), worst case O(1)
D
Average O(1), worst case O(n)
Correct answer
Explanation & correct answer: D
Average O(1), worst case O(n). A good hash function spreads keys evenly so each bucket holds only a few elements. When many keys collide into the same bucket, the operation degenerates into a linear scan of that bucket's list.
A hash table trades memory for speed: the hash function maps a key straight to a bucket index, so no ordered key comparison is needed. The O(1) average holds only under the assumptions that the hash distributes keys evenly and the load factor stays low — most implementations rehash once the element count crosses a threshold (0.75 of the bucket count, typically).
The O(n) worst case happens when every key lands in one bucket. That is not purely theoretical: hash flooding is an attack that deliberately submits colliding keys to force O(n) per request. Runtimes defend by seeding the hash randomly per process, or by converting an overloaded bucket into a balanced tree — Java's HashMap switches to a red-black tree past 8 entries in a bucket, bounding the worst case at O(log n).
The point to state in an interview is that O(1) means "average, under conditions", not a guarantee. When you need a hard upper bound or ordered iteration, a tree-based structure with a steady O(log n) is usually the better call.
What is the fundamental cost difference between an array and a linked list?
A
An array indexes in O(1) but inserts in the middle in O(n); a linked list is the other way round
Correct answer
B
An array uses less memory because it stores only pointers rather than the data itself
C
Both are O(1) for access; they differ only in how memory is allocated
D
A linked list beats an array at every operation because it never has to reallocate memory
Explanation & correct answer: A
An array stores elements contiguously, so indexed access is O(1), but inserting/deleting in the middle requires shifting elements, so O(n). A linked list only needs to relink pointers for insert/delete — O(1) once you hold the pointer to the position — but accessing the k-th element must traverse from the head, so O(n).
The difference comes from memory layout. An array occupies one contiguous block, so the address of element i is computed directly as base + i × sizeof(T) — one multiply-add, independent of n. A linked list scatters nodes across the heap and joins them with pointers, so reaching element k means following k links.
What the complexity table hides is cache behaviour. The CPU reads memory in 64-byte cache lines, so walking an array pulls in several neighbouring elements per fetch; walking a linked list jumps to an unpredictable address per node and misses the cache almost every time. In practice, sequential traversal of an array is often an order of magnitude faster than a list even though both are O(n). This is why std::vector is the recommended default in C++ even under moderate insert/delete load.
A linked list still earns its place in specific cases: repeated insert/remove at a position you already hold a pointer to (LRU lists, allocator free lists), large elements where shifting an array is expensive to copy, or when you need pointers and references to stay valid, since an array can reallocate and invalidate them.
What is the difference between stack and heap memory?
A
The stack is for read-only data and the heap is for writable data
B
The stack lives in RAM while the heap is allocated on disk
C
The stack holds global variables while the heap holds a function's local variables
D
The stack is reclaimed automatically when a function returns; the heap is not
Correct answer
Explanation & correct answer: D
The stack allocates by function call frame: entering a function pushes a frame, returning reclaims it automatically, and the cost is just moving a pointer. The heap allocates scattered blocks whose lifetime is decided by the programmer or the garbage collector, is slower, and can fragment. Local variables usually live on the stack; data that outlives the call lives on the heap.
The stack is a per-thread LIFO structure: each call pushes a frame holding parameters, locals and the return address; when the function returns, the stack pointer moves back and the whole frame disappears. Allocation therefore costs one pointer subtraction, and the data stays hot in cache. The trade-offs are a fixed size (typically 1–8 MB per thread) and a lifetime locked to the call — returning a pointer to a local is the classic bug.
The heap serves data whose size is unknown at compile time or that must outlive the function that created it. The allocator has to find a suitable free block, update bookkeeping, and over time memory fragments. Garbage-collected languages move the freeing burden to the runtime but do not remove the cost: something still has to scan and, in a compacting collector, relocate objects.
The boundary is not always the programmer's to choose. The Go compiler runs escape analysis and places a local on the stack when it does not escape the function, even if you wrote new. The JVM's scalar replacement has a similar effect. In JavaScript and Python every object lives on the heap and only primitives and references sit on the stack — a detail that explains why assigning an object to another variable shares a reference rather than copying.
What is the fundamental difference between a process and a thread?
A
A process runs in true parallel while a thread only takes turns on one core
B
A process has its own address space; threads within one process share it
Correct answer
C
Threads are managed by the OS while processes are managed by the language
D
A process is lighter than a thread, so creating one is much faster
Explanation & correct answer: B
A process owns its own address space and is isolated from others by the OS. A thread is a scheduling unit inside a process: threads share the heap, code, and file descriptors, with only their own stack and registers. Consequence: threads exchange data directly but need synchronization, while processes must go through IPC, but a fault in one process doesn't corrupt another.
The address space is the boundary from which every other difference follows. Because threads share a heap, passing data between them is just passing a pointer — fast, but it opens the door to race conditions and forces locks or immutable structures. Processes are isolated by the OS and must use pipes, sockets or shared memory; slower, since it costs copying and a trip into the kernel.
The costs differ sharply too. Creating a process requires building new page tables (Linux uses copy-on-write to defer most of that work); creating a thread only needs a stack, typically 1–8 MB of virtual address space. Context switching between threads of one process is cheaper than between processes because the TLB need not be flushed.
The trade-off drives architecture: Chrome runs each tab in its own process so one hung tab cannot take the browser down, accepting the RAM cost. PostgreSQL uses a process per connection for robustness, while MySQL uses threads for lightness. At the application layer, modern runtimes add another level — Go's goroutines, Java 21's virtual threads — runtime-scheduled threads that are orders of magnitude cheaper than OS threads, and usually the interviewer's next question.
When the connection crosses many firewalls, because UDP is always allowed through
B
When latency matters more than integrity — voice calls or real-time games, for instance
Correct answer
C
When transferring large files, because UDP has higher throughput
D
When encryption is needed, because UDP supports TLS and TCP does not
Explanation & correct answer: B
When latency matters more than absolute integrity. TCP guarantees reliability and ordering by retransmitting lost packets, but that means one lost packet blocks the entire stream behind it. For voice, live video or games, data that arrives late has lost its value, so skipping a lost frame is better than waiting for it.
TCP provides a reliable, ordered byte stream with congestion and flow control. The price is a three-way handshake before the first byte moves, plus head-of-line blocking: if packet 5 is lost, packets 6, 7 and 8 sit in the buffer already received but undeliverable until 5 is retransmitted.
UDP adds only source/destination ports and a checksum on top of IP — no handshake, no retransmission, no ordering. The application receives datagram boundaries intact and decides for itself what loss means. In a live voice call, losing 20 ms of audio is a barely perceptible blip, while waiting for a retransmission produces audible lag.
QUIC is worth raising in an interview: it runs over UDP but reimplements reliability in userspace per independent stream, so loss on one stream does not stall the others — exactly the head-of-line problem HTTP/2 over TCP could not solve. HTTP/3 is simply HTTP over QUIC. The general lesson: choosing UDP does not mean giving up reliability, it means taking control of which reliability you need and where.
Why must passwords be hashed rather than encrypted when stored in a database?
A
Because hashing is faster than encryption, so logging in responds more quickly
B
Because data-protection law forbids encrypting personal information
C
Because encryption is reversible — anyone who obtains the key recovers every password
Correct answer
D
Because a hash has a fixed length, which saves storage space
Explanation & correct answer: C
Because encryption is reversible. Anyone who gets the key — an attacker or an internal employee — can recover every password in plaintext. Hashing is one-way: the system only needs to hash what the user types and compare, never knowing the original value. You must use a password-specific hash function (bcrypt, scrypt, Argon2) with salt.
The principle is to store the minimum needed for authentication. Authentication only requires answering "does this string match", never reading the password back. Keeping it in a reversible form creates unnecessary risk: a leaked key turns the whole database into a list of working passwords, and because users reuse passwords, the damage spreads to other services.
Hashing with SHA-256 is still not enough. SHA is designed to be fast, and modern GPUs compute billions of hashes per second, making dictionary attacks very effective. Proper password hashes are deliberately expensive: bcrypt has a cost parameter controlling the round count, and Argon2 adds a memory parameter that neutralises the parallelism advantage of GPUs and ASICs. OWASP currently recommends Argon2id as the first choice, with bcrypt as an acceptable alternative.
A salt is mandatory: a unique random string per user, stored alongside the hash. Without it, two users with the same password produce the same hash and a precomputed rainbow table breaks many accounts in one pass. bcrypt and Argon2 generate the salt automatically and embed it in the output string, so no separate column is needed. Finally, hash comparison must use a constant-time function to avoid leaking information through a timing attack.
What happens first when a browser needs to open a domain it has never visited?
A
Sending an HTTP GET request to fetch the HTML document
B
A DNS lookup to get the IP address behind the domain name
Correct answer
C
A TLS handshake to establish an encrypted channel with the target server
D
Checking the server's certificate against the OS trust store
Explanation & correct answer: B
DNS resolution. Networks route by IP address, not by domain name, so the browser must ask a resolver to turn the domain into an IP first. Only then do the TCP handshake, TLS handshake, and the HTTP request follow.
The full chain when opening a URL: check the browser and OS DNS caches, and on a miss ask a resolver (usually the ISP's, or 1.1.1.1/8.8.8.8), which walks from the root to the TLD to the domain's authoritative name server; once an IP is known, the TCP three-way handshake runs; for HTTPS the TLS handshake follows; only then does the HTTP request go out.
Each step is a network round trip, which makes this a productive place to cut latency. dns-prefetch gets the browser resolving third-party hostnames early; preconnect goes further and completes TCP and TLS too. The TTL on a DNS record governs how long caches hold it — lower it before an infrastructure change for a fast cutover, keep it high otherwise to reduce lookups.
A common follow-up: why do some users still reach the old server after a DNS change. The answer is layered caching — browser, OS, ISP resolver — and not every resolver honours TTL. It is also why serious migrations put a load balancer in front rather than relying on a DNS record swap.
What trade-off do you make when you add an index to a database column?
A
It reduces storage by compressing the indexed column's data
B
Writes get faster, but complex queries become markedly slower
C
Reads get faster at no cost, so every column should be indexed
D
Reads get faster, but writes get slower and it costs extra storage
Correct answer
Explanation & correct answer: D
Faster reads, slower writes, more storage. An index is a pre-sorted structure (usually a B-tree) that lets a query find rows without scanning the whole table. In exchange, every insert, update, or delete has to update both the table and every related index, and indexes take up extra disk and cache.
Without an index, finding a row means scanning the whole table sequentially — O(n) in row count. A B-tree index brings that to O(log n), so on a multi-million-row table the difference is hundreds of milliseconds versus a few. That is why every foreign key and every column used in WHERE, JOIN or ORDER BY deserves consideration.
The write cost is routinely underestimated. A table with five indexes means each INSERT is really six writes. On write-heavy systems (logs, events, queues) this is a very common bottleneck. An unused index is worse still, all cost and no benefit — audit pg_stat_user_indexes for indexes that have never been scanned.
Three details worth raising in an interview. Column order in a composite index matters: an index on (a, b) serves queries filtering on a, or on a and b, but not one filtering only on b. Wrapping a column in a function skips the index — WHERE lower(email) = ... needs an index on the expression lower(email). And a low-selectivity index, say a two-valued column, is usually ignored by the planner because a sequential scan is cheaper.
What does the Single Responsibility Principle actually state?
A
A class should have only one reason to change
Correct answer
B
A class should have exactly one public method
C
A class should not depend on more than one other class
D
A function should not exceed twenty lines
Explanation & correct answer: A
A class should have only one reason to change. Robert C. Martin later phrased it more precisely: a module should be responsible to only one group of users or one stakeholder. This is not the "a class does only one thing" rule in the sense of counting methods.
The common but wrong reading is "each class does exactly one thing", which leads to over-splitting into dozens of one-method classes that are hard to follow. The correct statement is about reasons to change: if accounting asks to change how pay is calculated while HR asks to change how the report is formatted, and both force edits to the same class, that class carries two responsibilities.
The textbook example is an Employee class that calculates pay, formats a report and persists itself. Those three change for three different reasons at the request of three different parties; merging them means each edit risks breaking the other two. Splitting along reasons to change yields three components with independent lifecycles.
The downside to watch for is over-application producing fragmentation, where understanding one business flow means opening seven files. Split only once you have actually observed two distinct reasons to change; do not split on prediction. This is also where the principle meets YAGNI — both push back on designing for an imagined future.
In testing, what is the difference between a mock and a stub?
A
A stub is for unit tests while a mock only works in integration tests
B
A stub is generated by a library while a mock must be written entirely by hand
C
A stub replaces the database while a mock replaces network calls
D
A stub returns canned data; a mock also verifies it was called correctly
Correct answer
Explanation & correct answer: D
A stub returns canned answers so the test can run — it serves as input. A mock does more than answer: it records how it was called, and the test asserts on those interactions. In short: stubs verify state, mocks verify behavior.
Gerard Meszaros's full taxonomy has five test doubles. A dummy just fills a parameter and is never used. A stub returns canned values. A spy is a stub that records calls for later inspection. A mock is preprogrammed with expectations and fails the test itself when they are not met. A fake is a working but simplified implementation, such as an in-memory repository.
Which one you choose determines whether tests are brittle or durable. Asserting on state — call the function, check the result — leaves you free to refactor internals. Asserting on interactions — verifying repository.save was called once with these arguments — couples the test to the implementation, so changing how the work is done turns the test red even when the outcome is still correct.
A pragmatic rule: default to stubs or fakes and assert on results. Reach for a mock only when the interaction itself is the guarantee — proving an email is sent exactly once, or that an audit record is written. For repositories, an in-memory fake usually yields tests that are both faster and far less brittle than mocking each method.
In REST, which of these endpoints follows the naming convention?
A
GET /users/42/delete
B
POST /deleteUser?id=42
C
DELETE /users/42
Correct answer
D
DELETE /getUserAndRemove/42
Explanation & correct answer: C
DELETE /users/42. The core REST principle is that the path describes resources with nouns (usually plural), while the action is expressed by the HTTP method. Putting verbs in the path turns the API into a REST-flavoured RPC and loses the method's semantic guarantees.
The baseline conventions: GET /users lists, POST /users creates, GET /users/42 reads one, PUT /users/42 replaces, PATCH /users/42 partially updates, DELETE /users/42 removes. Nested relations follow the same rule: GET /users/42/orders. Plural nouns are preferred because they read consistently for both the collection and its members.
The most consequential mistake is using GET for a state-changing operation. The spec defines GET as safe, so browsers prefetch it, proxies cache it and crawlers follow it freely. Systems have lost data because a crawler followed every /delete link. Losing safety also means losing cacheability and safe retries.
Be honest about REST's limits: some operations do not map naturally onto resources. Activating an account, resending a confirmation email, kicking off a background job — forcing these into nouns gets contrived. The widely accepted practice is to model the action as a resource when that reads well (POST /users/42/activation), and when it does not, use an action-style endpoint and document it plainly. Consistency across the API matters more than strict conformance.
Compressing data to reduce its size before sending it over the network
B
Producing a fixed-length hash to check a file's integrity
C
Representing binary data with text characters that are safe to transmit
Correct answer
D
Encrypting data so a third party cannot read its contents
Explanation & correct answer: C
To represent binary data using text characters, for channels that can only carry text such as email, JSON, or URLs. Base64 is not encryption: there is no key, anyone can decode it, and it also inflates the data by about 33%.
How it works: take each 3 bytes (24 bits), split into four 6-bit groups, and map each group to one of 64 characters — A–Z, a–z, 0–9, plus + and /. Since 3 bytes become 4 characters, size grows by exactly 4/3, about 33%. Trailing = characters are padding when the byte count is not divisible by 3.
The dangerous misconception is treating Base64 as protection. The string dXNlcjpwYXNz looks encoded but decodes to user:pass with one base64 -d. That is precisely how HTTP Basic Authentication works, and why Basic Auth must be paired with HTTPS. Likewise a JWT payload is Base64url, not encrypted — never put sensitive data there.
The Base64url variant swaps + and / for - and _ so the output is safe in URLs and filenames, and usually drops padding. On practical cost: embedding an image as a data URI in CSS or HTML saves a request but adds 33% to the size and gives up separate caching for that image — worth it only for very small assets like icons.
Two nested loops, each running n times, give what time complexity?
A
O(n²)
Correct answer
B
O(2n)
C
O(n)
D
O(n log n)
Explanation & correct answer: A
O(n²). The outer loop runs n times, each time performing all n steps of the inner loop, so the total number of operations is n × n. If the two loops are placed sequentially instead of nested, the cost is only n + n, i.e. O(n).
The rule of thumb is compact: nested means multiply, sequential means add. Multiplying gives O(n²); adding reduces to O(n) since constants are dropped. By the same logic, three nested loops give O(n³).
Watch out for nested loops with different bounds. An outer loop of n and an inner loop of m is O(n × m), not O(n²) — very different when m is small and fixed. Conversely, an inner loop running from i to n totals n(n−1)/2 operations, still O(n²) because the 1/2 is dropped.
The commonly missed part is hidden cost inside the loop body. A single loop that calls arr.includes(x) is really two nested loops, O(n²), even though only one for is visible. The same applies to string concatenation or array slicing inside a loop. When reading code to estimate cost, you must account for calls that look like a single operation.
What is the difference between a stack and a queue?
A
A stack only holds integers while a queue holds any data type
B
A stack lives in stack memory while a queue lives on the heap
C
A stack removes the most recently added element; a queue removes the oldest
Correct answer
D
A stack has a fixed size while a queue grows as more room is needed
Explanation & correct answer: C
A stack is LIFO — the last element in is the first out, with operations at the same end. A queue is FIFO — first in first out, added at one end and removed at the other. Both have O(1) push and pop; the difference is in the removal order, not the cost.
The choice follows the nature of the problem. A stack suits anything that backtracks: browser Back history, undo, balanced-bracket checking, depth-first traversal, and the program call stack itself. A queue suits anything that must respect arrival order: job queues, request buffers, breadth-first traversal, queueing simulations.
One observation worth raising in an interview: depth-first and breadth-first graph traversal are the same algorithm, differing only in whether pending vertices are held in a stack or a queue. Swapping the structure changes the traversal order entirely while the rest of the code stays put.
On implementation, both are usually built on a dynamic array or a doubly linked list. For an array-backed queue, the naive use of shift() to dequeue is O(n) because the whole array shifts; the correct approach is a circular buffer or a deque. In JavaScript a plain array makes a fine stack (push/pop are both O(1)) but avoid shift() for queues once the data grows.
How does calling an asynchronous function differ from calling a synchronous one?
A
An async function returns a promise immediately, with the result arriving later through a callback
Correct answer
B
An async function cannot throw, so the return value must be checked instead
C
An async function runs faster because it gets higher scheduling priority
D
An async function always runs on a separate thread allocated by the runtime
Explanation & correct answer: A
A synchronous function blocks the calling thread until there's a result. An asynchronous function immediately returns an object representing the future result (promise, future, task), the calling thread continues, and the actual value is delivered once the task finishes. Asynchrony doesn't make the task faster, it just avoids wasting time waiting.
The motivation for asynchrony is a speed gap. A network call takes around 50 milliseconds, during which the CPU could execute hundreds of millions of instructions. Blocking a thread to wait wastes exactly that gap. Asynchrony lets the thread pick up other work and return when the data is ready.
Keep asynchrony and multithreading apart — this is where people get confused. Asynchrony is about when a result arrives; multithreading is about having several execution streams at once. JavaScript is asynchronous but single-threaded. Conversely, a multithreaded program can consist entirely of blocking synchronous calls.
The common beginner mistake is awaiting independent tasks sequentially. Three unrelated API calls awaited one after another cost the sum of all three; grouping them with Promise.all costs only the slowest. The second mistake is using asynchrony for CPU-bound work — it helps nothing, because there is no waiting to reclaim; that needs a separate thread or process.