C Programming with DSA Interview Questions for 10 Years Experience
40 questions with answers for candidates with 10+ years of experience — 1 easy, 9 medium, 30 hard.
What interviewers expect: 10 years experience
At ten years, roles are architect, principal engineer or engineering manager. Interviews cover platform strategy, hardware and software co-design, long term maintainability of large C codebases, hiring and mentoring and working with product and hardware teams. Deep C knowledge is still tested, but influence, communication and strategic thinking decide the final outcome.
This set is weighted towards Trees and Graphs (8), Sorting, Searching and Dynamic Programming (7), Stacks, Queues and Hashing (6), Pointers and Memory Management (6).
Platform and product architecture Hardware software co-design Stakeholder management Hiring and mentoring
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
An adjacency matrix is a V by V array where cell [u][v] marks an edge; it gives O(1) edge checks but uses O(V squared) memory, suiting dense graphs. An adjacency list stores for each vertex a list or dynamic array of neighbours, using O(V + E) memory, which suits sparse real world graphs like road or social networks. Weighted graphs store the weight with each edge.
02
Count the number of islands in a grid of land and water cells. What approach do you take?
Treat each land cell as a graph node connected to its four neighbours. Scan the grid, and when you find unvisited land, increment the count and flood fill with DFS or BFS to mark the whole island visited. Time and space are O(rows times cols). For very large grids, use iterative BFS to avoid recursion depth issues, or union-find.
03
Merge two sorted linked lists into one sorted list.
A buffer overflow writes beyond the end of an array, corrupting nearby data, return addresses or heap metadata, and it is a classic security hole. Prevent it with bounds checked functions like fgets and snprintf, always passing buffer sizes, validating input lengths and using sizeof correctly. Compiler protections such as stack canaries, FORTIFY_SOURCE and sanitizers help catch and limit damage.
05
What is the difference between open addressing and separate chaining in terms of performance?
Separate chaining stores colliding keys in linked lists, so it handles high load factors gracefully and deletes easily, but each node is a separate allocation with poor cache locality. Open addressing keeps all entries in one array, giving excellent cache performance and no per-entry allocation, but it degrades sharply above about 70 percent load and needs tombstones for deletion. High performance tables usually use open addressing.
Copy byte by byte through unsigned char pointers.
void *my_memcpy(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
while (n--) {
*d++ = *s++;
}
return dest;
}
memcpy must not be used for overlapping regions; memmove handles overlap by copying backwards when needed. Real libraries copy whole words for speed.
07
A Dijkstra implementation gives wrong distances on a graph with a few negative edges. Why?
Dijkstra assumes that once a vertex is taken from the heap its distance is final, which holds only when all weights are non-negative. A negative edge found later can make a shorter path, so results are wrong. Use Bellman-Ford, which relaxes all edges V-1 times in O(VE) and can also detect negative cycles, or reweight the graph with Johnson's algorithm.
08
A DP solution for a large grid uses too much memory. How can you reduce it?
Many grid DPs only need the previous row to compute the current one, so replace the 2D table with two 1D arrays or even one array updated in place, cutting memory from O(rows times cols) to O(cols). For knapsack, iterate capacity backwards in a single array. If you also need to reconstruct the path, store compact choice bits or use Hirschberg's divide and conquer technique.
Strict aliasing says an object may be accessed only through an lvalue of its own type, a compatible type or a character type. Reading a float through an int pointer breaks the rule and is undefined behaviour, and optimisers at -O2 may reorder or drop such accesses. To reinterpret bytes safely, use memcpy into a variable of the other type, or a union, which GCC supports for type punning.
10
Why is it risky to write structs directly to a file with fwrite?
A raw struct includes padding bytes, platform specific sizes and byte order, so a file written on one compiler or CPU may not read correctly on another. Pointers inside the struct are meaningless after reload. For portable formats, serialise each field explicitly with fixed-width types and a defined byte order, or use a format like JSON or Protocol Buffers.
11
How do you handle a situation where requirements change late in a project?
Situation: two weeks before release, the client asked to add a new field to our device's binary log format. Task: add it without breaking older files. Action: I explained the risk, added a version byte to the header, wrote a reader that handled both versions and tested with old and new samples. Result: we shipped on time and older devices kept working without a firmware update.
12
Describe a time you had to make a trade-off between memory and speed.
Situation: a C routing service on a small server ran out of memory with an adjacency matrix for 50,000 nodes. Task: fit within 512 MB without slowing queries much. Action: I switched to compact adjacency lists stored in flat arrays, known as CSR format, and kept a small cache for frequent routes. Result: memory fell from 10 GB worth of matrix to 60 MB and queries stayed under 20 ms.
13
Quicksort in your code becomes very slow on already sorted input. Why and how do you fix it?
Picking the first or last element as pivot on sorted input makes every partition split into sizes n-1 and 0, so quicksort degrades to O(n squared) and recursion depth reaches n. Choose a random pivot or median of three, switch to insertion sort for small parts and recurse on the smaller side first to bound stack depth. Introsort switches to heap sort if recursion gets too deep.
14
A hash table becomes slow as more data is inserted. What is wrong?
The load factor has grown, so chains get long or open addressing probes become long, turning O(1) into nearly O(n). Another cause is a poor hash function clustering keys into few buckets. Fix it by resizing to roughly double capacity and rehashing when the load factor crosses a threshold, and by using a better hash function. Measure the longest chain to confirm.
15
Implement a simple hash table with separate chaining for string keys.
C11 provides _Static_assert, and C23 adds static_assert as a keyword.
#include <stdint.h>
typedef struct {
uint32_t timestamp;
uint16_t reading;
uint8_t sensorId;
uint8_t flags;
} Record;
_Static_assert(sizeof(Record) == 8, "Record must be 8 bytes");
The build fails if someone adds a field or padding changes, which protects file formats and protocols.
17
After calling realloc, the program sometimes crashes. What could be wrong?
realloc may move the block and returns the new address, so old pointers to the block become invalid. Writing ptr = realloc(ptr, n) also leaks the original memory if realloc fails and returns NULL. Store the result in a temporary pointer, check for NULL, then assign. Make sure no other pointers still refer to the old location after a successful move.
The master theorem gives the time complexity of divide and conquer recurrences of the form T(n) = aT(n/b) + f(n). Compare f(n) with n to the power log base b of a. For merge sort, a = 2, b = 2 and f(n) = n, which match, giving O(n log n). For binary search, a = 1 and b = 2 with constant work, giving O(log n). It does not cover every recurrence.
19
Use qsort with a function pointer to sort an array of integers in descending order.
Write a comparator that returns negative, zero or positive.
int cmpDesc(const void *a, const void *b) {
int x = *(const int *)a;
int y = *(const int *)b;
return (y > x) - (y < x);
}
int arr[] = {4, 1, 9, 3};
qsort(arr, 4, sizeof arr[0], cmpDesc);
Returning y - x can overflow, so the comparison form is safer.
20
You need the maximum of every sliding window of size k in an array. How would you solve it efficiently?
Use a deque storing indexes in decreasing order of value. For each element, remove indexes outside the window from the front, pop smaller values from the back, then push the current index. The front is always the maximum of the current window. Each index enters and leaves once, so the total time is O(n) instead of O(nk).
Undefined behaviour means the C standard places no requirement on what happens, so the program may crash, give wrong results or appear to work. Examples are signed integer overflow, dereferencing a null or freed pointer, reading an uninitialised variable, accessing an array out of bounds and modifying a variable twice without a sequence point, such as i = i++. Compilers optimise assuming it never happens.
22
What is a ring buffer and why is it common in embedded systems?
A ring buffer is a fixed-size circular queue in a static array. It needs no dynamic allocation, has O(1) operations and predictable memory, which suits microcontrollers. A single producer, such as a UART interrupt, writes at head while a single consumer, such as the main loop, reads at tail. With power of two sizes, index masking is cheap, and with one writer and one reader it can be lock free.
23
How do you handle working with a teammate who writes buggy code?
Situation: a teammate's stack and queue module in our project kept failing tests. Task: fix the quality issue without blaming. Action: I offered to pair on one bug, showed how drawing the array and indexes revealed the off-by-one error and suggested we both add edge case tests before pushing. Result: his code quality improved quickly and our module passed all tests at the review.
Union-find, or disjoint set union, tracks which elements are in the same group. find returns a group's representative and union merges two groups. With path compression and union by rank, both run in nearly constant amortised time. It is used for Kruskal's minimum spanning tree, detecting cycles in undirected graphs and counting connected components in dynamic connectivity problems.
25
Find the length of the longest common subsequence of two strings.
Each node is allocated separately and may sit anywhere in memory, so traversing a list causes frequent cache misses and the CPU cannot prefetch well. Arrays store elements contiguously, so a cache line brings several elements at once. Even with O(1) insertion, linked lists are often slower in practice. Use them when frequent insertions in the middle with stable node addresses really matter.
A flexible array member is a last struct member declared with empty brackets, such as struct Packet { size_t len; char data[]; };. You allocate the struct and its data in one malloc call: malloc(sizeof(struct Packet) + n). It keeps the header and payload together, improving cache use and needing only one free. It was standardised in C99.
28
How would you find the kth smallest element in an unsorted array efficiently?
Sorting takes O(n log n). A max heap of size k gives O(n log k), good when k is small or data streams in. Quickselect partitions like quicksort but recurses into one side only, giving O(n) average and O(n squared) worst case, which random pivots make unlikely. Median of medians guarantees O(n) worst case but is slower in practice.
29
Implement union-find with path compression and union by rank.
Two small functions cover most uses.
int parent[MAXN], rnk[MAXN];
void makeSet(int n) {
for (int i = 0; i < n; i++) { parent[i] = i; rnk[i] = 0; }
}
int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
int unite(int a, int b) {
a = find(a); b = find(b);
if (a == b) return 0;
if (rnk[a] < rnk[b]) { int t = a; a = b; b = t; }
parent[b] = a;
if (rnk[a] == rnk[b]) rnk[a]++;
return 1;
}
30
You must design an LRU cache in C with O(1) get and put. How would you build it?
Combine a hash table that maps keys to nodes with a doubly linked list ordered by recent use. On get, find the node through the hash table and move it to the front. On put, update or insert at the front, and if capacity is exceeded remove the tail node and delete its key from the hash table. Both operations are O(1) on average.
31
A social network feature must suggest friends of friends. How would you find them efficiently?
Represent users and friendships as an undirected graph with adjacency lists. Run BFS from the user to depth two, collect nodes at distance exactly two and rank them by the number of mutual friends counted during traversal. Exclude existing friends. For huge graphs, precompute suggestions offline in batches and cap the number of neighbours explored per user.
32
Memory use of a long running C service grows every hour. How do you investigate?
Run it under Valgrind or with AddressSanitizer and LeakSanitizer in a test environment and reproduce typical traffic, then read the leak report with allocation stack traces. In production, compare heap usage over time with tools like heaptrack. Check error paths that return early without free, lost pointers after realloc, and caches without size limits. Fix ownership and add a leak check to CI.
33
Solve the 0/1 knapsack problem in C with a 1D DP array.
Iterate capacity backwards so each item is used once.
int knapsack(const int *wt, const int *val, int n, int W) {
int dp[W + 1];
for (int c = 0; c <= W; c++) dp[c] = 0;
for (int i = 0; i < n; i++) {
for (int c = W; c >= wt[i]; c--) {
int take = dp[c - wt[i]] + val[i];
if (take > dp[c]) dp[c] = take;
}
}
return dp[W];
}
Time O(nW), space O(W).
34
Detect a cycle in a linked list and return the node where it starts.
Situation: a C tool comparing two inventory files of 5 lakh rows took 25 minutes. Task: make it run in a nightly window of 2 minutes. Action: I found nested loops doing O(n squared) matching, sorted both files with qsort and merged them with two pointers, which is O(n log n). Result: runtime fell to 40 seconds, and the approach became our standard for file reconciliation jobs.
36
Your embedded device must store 10,000 sensor records in limited RAM. How would you design the struct?
Use fixed-width types sized to the real range, such as uint16_t for readings and uint32_t for timestamps, order members from largest to smallest to cut padding and pack boolean flags into bit fields or a single bitmask byte. Consider storing time as an offset from a base to save bytes. Check sizeof in a static assertion so the size stays as designed.
37
A recursive tree traversal crashes on a very large, skewed tree. How do you fix it?
A skewed tree has height close to n, so recursion depth becomes huge and overflows the call stack. Convert the traversal to an iterative version using an explicit stack allocated on the heap, or use Morris traversal for in-order with O(1) extra space. Long term, use a self-balancing tree so height stays around log n.
In expressions, types smaller than int, like char and short, are promoted to int before arithmetic. In mixed signed and unsigned comparisons, the signed value may convert to unsigned. So if (-1 < sizeof(int)) is false, because -1 becomes a huge unsigned number. These rules cause subtle bugs in embedded and systems code, so compile with -Wall -Wextra and use explicit casts carefully.
39
A linked list used in a multithreaded C server sometimes corrupts. How would you fix it?
Concurrent inserts and deletes race on next pointers. The simplest fix is a pthread mutex around every list operation, keeping critical sections short. For read-heavy workloads, a read-write lock lets readers run in parallel. Lock-free lists with atomic compare-and-swap are possible but very hard to get right because of the ABA problem and safe memory reclamation, so use them only with proven libraries.
40
Valgrind reports invalid read of size 4 after free. How do you fix it?
This is a use-after-free: code reads memory that was already released. Valgrind shows where it was freed and where it was read. Fix the ownership logic so nothing uses the object after free, and set pointers to NULL right after freeing so accidental use fails fast. If multiple parts share the object, use reference counting or free only at one clear owner.
Frequently asked questions
What is asked in C Programming with DSA interviews for 10+ years of experience?
At ten years, roles are architect, principal engineer or engineering manager. Interviews cover platform strategy, hardware and software co-design, long term maintainability of large C codebases, hiring and mentoring and working with product and hardware teams. Deep C knowledge is still tested, but influence, communication and strategic thinking decide the final outcome.
How many questions should I practise before a C Programming with DSA interview?
Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.
Which topics matter most for 10+ years of experience?
Focus first on Platform and product architecture, Hardware software co-design, Stakeholder management, Hiring and mentoring.