Round one is pass/fail and it is not a Gen-AI round — it's LeetCode-medium plus the fundamentals rapid-fire a semiconductor company still asks. The Qualcomm-specific pattern weighting, a ranked 45-problem hit list, the bit-manipulation set with pseudocode, and tiered model answers for C, OS, Linux, and Python — with an explicit line for where to stop.
Round one is the round you can only lose. Nobody has ever been levelled up for an elegant two-pointer solution, and plenty of strong applied-AI candidates have been filtered out in 45 minutes because they hadn't touched a linked list in three years.
Two things make Qualcomm's version different from a product company's:
The calibration that matters, and the reason this page has tiers: for a datacenter cloud/applied Gen AI role, you need recall-level answers on the low-level material, not depth. One focused evening, not one week. Section 5 marks exactly where to stop — everything past that line is for firmware candidates, and studying it is how you spend a week signalling you misread the role.
This page is the Qualcomm-shaped overlay on the DSA track — that page has the sixteen patterns, the daily protocol, and the full 80-problem curriculum. Don't re-read it here. This page tells you what to reweight, gives you a ranked hit list, and covers the fundamentals half that no DSA curriculum contains.
Next: Round 2, classic system design The 80-problem curriculum
| Segment | Shape | Time | How to treat it |
|---|---|---|---|
| Online assessment (sometimes) | ~20 MCQs: C/C++ output prediction, complexity, OOP, OS basics + 1–2 coding problems | 60–90 min | Pure recall. §5 is the entire prep. |
| Coding screen | 1–2 LeetCode-medium problems, shared editor | 45 min | §3–4. This is the gate. |
| Fundamentals tail | 5–15 min of rapid-fire at the end of the coding round | — | §5. Crisp answers, no rambling. |
Be aware the loop is team-dependent — some candidates report a straightforward two-problem screen, others a full-day final stage that folds coding, design, and project deep-dives together. Prepare for the union; you'll be over-prepared for whichever you get.
Take the sixteen patterns from the DSA track and change the time allocation. Relative to a generic FAANG-style prep:
| Pattern | Weight here | Why |
|---|---|---|
| Bit manipulation | 🔴 Way up | The house specialty. Expect at least one. §4 is dedicated to it. |
| Linked lists | 🔴 Up | Pointer manipulation is the thing they actually want to watch. Reverse, cycle detect, merge, LRU. |
| Arrays + two pointers + sliding window | 🟢 Standard | Still the most common single category. |
| Hashing | 🟢 Standard | The default tool; know when it's the wrong one (ordering, memory). |
| Heaps / top-k | 🟡 Slightly up | Streaming and scheduling flavors. |
| Intervals | 🟡 Slightly up | Scheduling and resource-allocation framing fits the org. |
| Trees / BST | 🟢 Standard | Traversals, LCA, validate, level order. |
| Graphs (BFS/DFS/topo/union-find) | 🟢 Standard | Dependency and connectivity framing. |
| Matrix | 🟡 Slightly up | Rotate, spiral, set-zeroes — in-place variants especially. |
| Binary search | 🟢 Standard | Including "search on the answer." |
| Stacks / monotonic stack | 🟢 Standard | Parsing, next-greater, histogram. |
| Backtracking | 🟡 Down | Appears, rarely decisive. |
| DP | 🔵 Down | Classic 1-D and knapsack only. Deep DP is not where this loop lives. |
| Design (LRU/LFU/iterator) | 🟡 Up | Bridges into rounds 2–3 nicely. |
The reallocation in one line: take the hours you'd have spent on hard DP and spend them on bits, linked lists, and the §5 fundamentals.
If you have a week, do them in this order. If you have three days, do the ★ ones.
Bits (do all — this is the differentiator)
★ Single Number · ★ Single Number II · ★ Single Number III (two uniques) · ★ Number of 1 Bits · Counting Bits · ★ Reverse Bits · Missing Number · Power of Two · Sum of Two Integers (no +) · ★ Bitwise AND of Numbers Range · Gray Code · Maximum XOR of Two Numbers (trie)
Linked lists ★ Reverse Linked List · ★ Merge Two Sorted Lists · ★ Linked List Cycle II (find entry) · Remove Nth From End · ★ Reorder List · Copy List with Random Pointer · Merge k Sorted Lists · Add Two Numbers
Arrays / two pointers / window ★ Two Sum · ★ Best Time to Buy and Sell Stock · Container With Most Water · 3Sum · ★ Longest Substring Without Repeating · Minimum Window Substring · Product of Array Except Self · ★ Rotate Array (in place) · Sort Colors
Heaps / intervals / design ★ Top K Frequent Elements · Kth Largest in a Stream · ★ Merge Intervals · ★ Meeting Rooms II · Insert Interval · ★ LRU Cache · Min Stack · Implement Trie
Trees / graphs / search ★ Validate BST · Lowest Common Ancestor · Level Order Traversal · Serialize/Deserialize Tree · ★ Number of Islands · Course Schedule · Clone Graph · ★ Search in Rotated Sorted Array · Koko Eating Bananas
Matrix / DP (light) Rotate Image · Spiral Matrix · Set Matrix Zeroes · ★ Climbing Stairs · Coin Change · House Robber
Pseudocode below is C-flavored where the C matters — because for these, it does. Say the invariant, then write.
You will be asked some version of "set bits 4..7 of a register to v without disturbing anything else."
// single-bit ops on an unsigned word
set: x |= (1u << n);
clear: x &= ~(1u << n);
toggle: x ^= (1u << n);
test: ((x >> n) & 1u)
// multi-bit FIELD write: [lo, lo+len) := v <-- the one they actually ask
mask = ((len == 32) ? ~0u : ((1u << len) - 1u)) << lo; // len==32 UB guard
x = (x & ~mask) | ((v << lo) & mask); // clear, then OR in
// field read
field = (x >> lo) & ((1u << len) - 1u);Three details that separate a pass from a strong pass. Use unsigned — shifting into or past the sign bit of a signed int is undefined behavior. 1u << 31 is fine; 1 << 31 is not. And (1u << len) - 1 breaks when len == 32, because a shift by the full width is undefined in C and typically wraps to a shift by 0 — say this unprompted and you have effectively passed the bits question.
def popcount_kernighan(n): # O(number of set bits), not O(32)
c = 0
while n:
n &= n - 1 # clears the LOWEST set bit
c += 1
return c
def popcount_swar(n): # O(1), no loop — divide and conquer
n = n - ((n >> 1) & 0x55555555) # pairs
n = (n & 0x33333333) + ((n >> 2) & 0x33333333) # nibbles
n = (n + (n >> 4)) & 0x0F0F0F0F # bytes
return (n * 0x01010101) >> 24 # sum the bytesThen say: "in production I'd use __builtin_popcount / POPCNT, which is one instruction." Knowing the trick and knowing you'd never ship it is the right answer.
def single_number(nums): # every element twice except one
r = 0
for x in nums: r ^= x # XOR is associative, commutative, x^x==0
return r
def two_singles(nums): # every element twice except TWO
xor = 0
for x in nums: xor ^= x # == a ^ b
low = xor & -xor # lowest bit where a and b DIFFER
a = b = 0
for x in nums:
if x & low: a ^= x # partition into two groups...
else: b ^= x # ...each with exactly one unique
return a, bxor & -xor isolating the lowest set bit is the single most reusable trick in this section — it's two's complement doing the work, and it shows up again in Fenwick trees and in the allocator from the Gen-AI coding page.
is_power_of_two(n): return n > 0 and (n & (n - 1)) == 0
lowest_set_bit(n): return n & -n
clear_lowest(n): return n & (n - 1)
next_power_of_two(n): # smear the highest set bit down, then +1
n -= 1; n |= n>>1; n |= n>>2; n |= n>>4; n |= n>>8; n |= n>>16
return n + 1
def reverse_bits(n): # divide and conquer, 5 swaps
n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16)
n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
return n
def add_without_plus(a, b): # carry propagation by hand
while b:
carry = (a & b) << 1 # bits where BOTH are 1 carry left
a = a ^ b # sum without carry
b = carry
return aThe swap-without-temp trap. a ^= b; b ^= a; a ^= b; is a classic — and it is wrong when a and b alias the same object, because the first XOR zeroes it. Say that. Also say you'd never ship it: a compiler generates better code from a plain temp.
Answer in two or three sentences and stop. Rambling here reads as uncertainty.
volatile. Tells the compiler the value can change outside this program's control — a memory-mapped register, a flag written by an ISR or another thread — so it must re-read from memory on every access instead of caching it in a register. Without it, while (!flag) {} gets hoisted into if (!flag) for(;;); and hangs forever. What it does not give you: atomicity or ordering. It is not a synchronization primitive and not a substitute for std::atomic or a memory barrier — that's the follow-up, and it's the half most candidates miss.
Wild vs dangling pointer. Wild = never initialized; points at whatever garbage was on the stack. Dangling = pointed at valid memory that has since been freed or gone out of scope. Prevention: initialize to NULL at declaration, and set to NULL immediately after free() — free(NULL) is a defined no-op, so that single habit makes double-free impossible.
Memory layout of a process. Low to high: text (code, read-only) → rodata → data (initialized globals/statics) → bss (zero-initialized globals; occupies no space in the binary) → heap (grows up) → mmap regions (shared libs, large allocations) → stack (grows down). The follow-up: "where does a static local variable live?" — data or bss, not the stack, which is why it survives across calls.
Struct padding. Each member is aligned to its natural alignment, and the struct's total size is rounded up to the alignment of its widest member so arrays stay aligned.
struct A { char a; int b; char c; }; // 1 + 3pad + 4 + 1 + 3pad = 12 bytes
struct B { int b; char a; char c; }; // 4 + 1 + 1 + 2pad = 8 bytesThe rule: order members largest to smallest. Bonus: #pragma pack removes padding at the cost of unaligned access, which is slower on x86 and a fault on some architectures.
Endianness, and how to detect it.
int is_little_endian(void) { int x = 1; return *(char *)&x == 1; }Little-endian stores the least-significant byte at the lowest address. It matters at exactly two boundaries: the network (hence htonl/ntohl) and binary file formats.
Mutex vs semaphore. A mutex has ownership — only the thread that locked it may unlock it — and exists for mutual exclusion. A semaphore is a counter with no ownership; any thread may post it, which makes it the right tool for signalling and for counting a resource pool. Consequences: only a mutex can implement priority inheritance, and a binary semaphore used as a lock can be released by a thread that never held it.
The four conditions for deadlock. Mutual exclusion, hold-and-wait, no preemption, circular wait. Break any one. A global lock-ordering convention breaks circular wait — that's the specific answer; "we use lock ordering" without naming which condition it kills is the vague one.
Priority inversion. A low-priority thread holds a lock that a high-priority thread needs; a medium-priority thread preempts the low one, so the high-priority thread waits on the medium one indefinitely. Fix: priority inheritance (the holder temporarily inherits the waiter's priority). This is the Mars Pathfinder bug, and naming it lands.
Process vs thread. Threads share an address space, file descriptors, and heap; each has its own stack and registers. The practical consequence: a thread switch doesn't flush the TLB, a process switch does — which is why thread switches are meaningfully cheaper.
Virtual memory. Each process gets its own virtual address space; the MMU translates virtual → physical through multi-level page tables, and the TLB caches recent translations. A miss on the page table is a page fault, which the OS services by demand-paging. fork() uses copy-on-write so the child shares pages until one writes.
Why huge pages matter to you. A 4 KB page maps 4 KB per TLB entry; a 50 GB weight buffer needs ~13 million entries, and the TLB has maybe a few thousand. So you thrash — every weight fetch pays a page-table walk. 2 MB huge pages cut the entry count 512×, and the working set fits. This is the answer that connects the fundamentals round to the actual job, so have it ready.
Cache hierarchy and false sharing. L1 ~1 ns, L2 ~4 ns, L3 ~30 ns, main memory ~100 ns — a factor of ~100 from top to bottom, which is why locality beats cleverness. False sharing: two threads writing two different variables that happen to share one 64-byte cache line. Under MESI (Modified/Exclusive/Shared/Invalid) each write invalidates the other core's copy, and the line ping-pongs between cores. Throughput collapses with no logical contention at all. Fix: pad or alignas(64) each per-thread counter to its own line. This is a real bug in per-core metrics counters in serving hot paths — worth saying so.
Spinlock vs mutex. Spin when the expected critical section is shorter than a context switch (~1–2 µs) and you have more cores than runnable threads. Otherwise sleep — spinning on a single core with the holder descheduled is pathological.
Memory barriers / atomics. Both compilers and CPUs reorder memory operations. Acquire semantics prevent later reads/writes from moving before the acquire; release semantics prevent earlier ones from moving after the release. Together they give you the happens-before edge that makes lock-free code correct. And back to Tier 1: volatile provides none of this.
Linux, when latency regresses. Reach in this order: top/htop (is it CPU at all?) → pidstat/vmstat/iostat (CPU vs memory vs I/O) → perf top (where in the code) → strace -c (syscall storm) → ss/netstat (connection state, retransmits) → /proc/<pid>/status (memory growth, context switches) → numactl --hardware (cross-socket memory access). And the framing: flat CPU with rising p99 usually means queueing, locking, or GC — not compute.
Python, for a cloud role. The GIL means only one thread executes Python bytecode at a time, so threads help for I/O-bound work (the GIL is released during I/O and inside many C extensions) and do nothing for CPU-bound work — use multiprocessing or push the compute into a native extension for that. asyncio is single-threaded cooperative concurrency: excellent for thousands of concurrent connections, useless if any coroutine blocks. Know that reference counting frees most objects immediately and the cycle collector handles the rest.
Everything below here is for firmware, DSP, and embedded reqs. For a cloud/applied Gen AI role, be able to say the name and one sentence — nothing more. MESI state transitions in detail · pipeline hazards and forwarding · RTOS task scheduling and tickless idle · interrupt latency and ISR design · memory-mapped I/O and DMA · UART/I2C/SPI · linker scripts and boot sequences · power domains and clock gating · cache line eviction policies.
If an interviewer goes deep here, the honest, high-signal move is: "I know the concept and where it matters, but I haven't worked at that layer — my depth is on the serving and platform side. Happy to reason through it if it's relevant to the role." Bluffing at this depth in front of a Qualcomm systems engineer is far worse than a clean boundary.
The three ways people fail this round, in order of frequency: silence while thinking; jumping to code before the constraints are pinned; and getting the algorithm right but fumbling the language (off-by-one in a while loop, signed shift, iterator invalidation). The third is a fluency problem — the only fix is writing code by hand, not reading solutions.
x & -x isolates the lowest set bit; x & (x-1) clears it; x && !(x & (x-1)) tests power-of-two.(1u << k) - 1 is undefined at k == 32. Guard it.unsigned. Signed shifts into the sign bit are UB.x^x == 0, x^0 == x, associative and commutative — that's the whole family of tricks.volatile = re-read from memory. Not atomicity, not ordering.free(p); p = NULL; — makes double-free impossible, costs nothing.alignas(64).The full 80-problem DSA curriculum · Round 2: classic system design · Gen-AI systems coding · The 4-week track
Reported question banks — Qualcomm coding questions: OA, technical, onsite · Qualcomm SWE interview guide · Interview process overview · Embedded-role guide (the Tier 3 boundary) · Candidate experiences