Turning Learners Into Developers
Codekilla
CODEKILLA
DSA · C

Data Structures & Algorithms in C Interview Questions and Answers (2026 Edition)

Top 30 Data Structures & Algorithms in C interview questions with model answers, code examples and a quick revision table.

← Interview Questions

Fundamentals & C Basics

1. Explain the concept of pointers in C and how they relate to memory addresses.

Pointers are variables that store memory addresses of other variables or functions. They provide a direct way to access and manipulate memory locations. When you declare a pointer, it holds the address of the data type it points to. The & operator retrieves the memory address of a variable, and the * (dereference) operator accesses the value stored at the address a pointer holds. Pointers are fundamental for dynamic memory management, working with arrays efficiently, and implementing complex data structures.

c
int num = 10;
int *ptr = # // ptr stores the address of num
printf("Value: %d, Address: %p\n", *ptr, (void *)ptr); 

2. Differentiate between malloc, calloc, realloc, and free for dynamic memory allocation.

These functions manage memory on the heap.

  • malloc allocates a block of memory of specified size (bytes) and returns a void* pointer to the beginning of the block. The allocated memory is uninitialized (contains garbage values).
  • calloc allocates a block of memory for an array of n elements, each of specified size. It initializes all bytes to zero.
  • realloc changes the size of a previously allocated memory block. It can expand or shrink the block, possibly moving it to a new location if the current block cannot be resized in place.
  • free deallocates memory previously allocated by malloc, calloc, or realloc, returning it to the system. Failing to free memory leads to memory leaks.

3. What are the key differences between arrays and pointers in C?

Arrays and pointers are closely related but distinct.

  • An array is a collection of elements of the same data type stored in contiguous memory locations. The array name itself acts as a constant pointer to its first element and cannot be reassigned.
  • A pointer is a variable that stores a memory address. It can be assigned to point to different memory locations during its lifetime.
  • Arrays have a fixed size determined at compile time (for static arrays) or allocation time (for dynamic arrays), while pointers only store an address and don't inherently carry size information of the block they point to. Pointer arithmetic works similarly on both.
c
int arr[5]; // arr is an array
int *ptr = arr; // ptr points to the first element of arr

4. Explain the concept of a struct in C and its typical use cases in DSA.

A struct (structure) is a user-defined data type that allows combining items of different data types under a single name. It's a way to create complex data types. Each member of a struct occupies its own distinct memory location. In Data Structures and Algorithms, structs are essential for defining nodes of linked lists, trees, graphs, or any composite data type where related pieces of information need to be grouped together logically. They facilitate building abstract data types.

c
struct Node {
    int data;
    struct Node *next; // Self-referential structure for linked lists
};

5. Discuss pass-by-value vs. pass-by-reference in C functions, especially with DSA context.

  • Pass-by-value: A copy of the argument's value is passed to the function. Changes made to the parameter inside the function do not affect the original variable in the caller. This is safe but inefficient for large data structures.
  • Pass-by-reference: The memory address (a pointer) of the argument is passed. The function then uses this address to directly access and modify the original variable. This is crucial in DSA for modifying data structures (like inserting into a linked list, changing a tree root) or avoiding expensive copying of large data (e.g., passing an array).
c
void modifyValue(int *val) {
    (*val)++; // Modifies original variable
}

Linear Data Structures

6. Design and implement a basic singly linked list node structure and an insertion function.

A singly linked list node typically contains two fields: the data and a pointer to the next node. Insertion usually involves allocating a new node, setting its data, and then adjusting the next pointers of the relevant nodes to link it into the list. For insertion at the beginning, the new node's next points to the current head, and the list's head becomes the new node.

c
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

// Function to insert a new node at the beginning of the list
void insertAtBeginning(struct Node **head_ref, int new_data) {
    struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
    new_node->data = new_data;
    new_node->next = *head_ref;
    *head_ref = new_node;
}

7. What are the advantages of a Doubly Linked List over a Singly Linked List?

A doubly linked list differs from a singly linked list by having each node contain a pointer to both the next node and the previous node. Advantages:

  • Bidirectional Traversal: Allows traversal in both forward and backward directions, which can simplify certain operations.
  • Efficient Deletion: Deleting a node is more efficient as you can easily access the previous node without traversing from the head.
  • Easier Insertion: Insertion before a given node is straightforward. However, it consumes more memory due to the extra pointer per node and makes insertion/deletion operations slightly more complex as two pointers need to be updated.

8. Describe a Circular Linked List and provide a real-world use case.

A circular linked list is a linked list where the last node points back to the first node, forming a cycle. There is no NULL termination. This structure allows continuous traversal of the list, as if it were a loop. A common real-world use case is implementing a round-robin scheduling algorithm in operating systems. Processes are added to a circular list, and the scheduler iterates through the list, giving each process a turn. When it reaches the end, it cycles back to the first process. Another use is managing buffers or queues where data needs to be continuously accessed in a loop.

9. Explain the Stack data structure. How can it be implemented in C, and what are its core operations?

A Stack is a Last-In, First-Out (LIFO) data structure. Elements are added and removed only from one end, called the top. Core operations:

  • push: Adds an element to the top of the stack.
  • pop: Removes and returns the element from the top.
  • peek / top: Returns the top element without removing it.
  • isEmpty: Checks if the stack is empty.
  • isFull: (For array implementation) Checks if the stack has reached its capacity. In C, a stack can be implemented using either an array (fixed size, potential overflow) or a linked list (dynamic size, potentially higher memory overhead per element).
c
// Array-based Stack implementation snippet
#define MAX_SIZE 100
int stack[MAX_SIZE];
int top = -1;

void push(int item) {
    if (top >= MAX_SIZE - 1) {
        printf("Stack Overflow!\n");
        return;
    }
    stack[++top] = item;
}

10. Describe the Queue data structure. How can it be implemented, and where is it typically used?

A Queue is a First-In, First-Out (FIFO) data structure. Elements are added at one end (the rear or tail) and removed from the other end (the front or head). Core operations:

  • enqueue: Adds an element to the rear of the queue.
  • dequeue: Removes and returns the element from the front.
  • front: Returns the front element without removing it.
  • isEmpty, isFull. Queues are commonly implemented using arrays (circular arrays to manage space efficiently) or linked lists. Typical use cases include managing tasks in an operating system (CPU scheduling), handling requests in web servers, BFS algorithm, and printer spooling.

Non-Linear Data Structures

11. Define key terminology used in trees: Root, Node, Parent, Child, Leaf, Depth, Height.

  • Root: The topmost node in a tree. It has no parent.
  • Node: A fundamental unit in a tree, containing data and links to its children.
  • Parent: A node that has one or more child nodes directly below it.
  • Child: A node directly connected to another node (its parent) one level below it.
  • Leaf: A node with no children.
  • Depth: The length of the path from the root to a specific node (number of edges). The root has depth 0.
  • Height: The length of the path from a specific node to its deepest leaf (number of edges). The height of a tree is the height of its root.

12. Explain the properties of a Binary Search Tree (BST) and demonstrate how to insert an element.

A Binary Search Tree (BST) is a binary tree where for every node:

  1. All nodes in its left subtree have values less than the node's value.
  2. All nodes in its right subtree have values greater than the node's value.
  3. Both the left and right subtrees are also BSTs. This property allows for efficient searching, insertion, and deletion operations with an average time complexity of O(log N). To insert, start at the root, compare the new value. If smaller, go left; if larger, go right. Repeat until an empty spot (NULL) is found, and insert the new node there.
c
struct TreeNode {
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
};

struct TreeNode* insert(struct TreeNode* node, int data) {
    if (node == NULL) {
        struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
        newNode->data = data;
        newNode->left = newNode->right = NULL;
        return newNode;
    }
    if (data < node->data)
        node->left = insert(node->left, data);
    else if (data > node->data)
        node->right = insert(node->right, data);
    return node;
}

13. Briefly compare AVL Trees and Red-Black Trees. When would you choose one over the other?

Both AVL Trees and Red-Black Trees are self-balancing binary search trees, meaning they automatically maintain a balanced height to ensure O(log N) time complexity for operations even in worst-case scenarios.

  • AVL Trees are stricter in balancing, ensuring that for every node, the height difference between its left and right subtrees (balance factor) is at most 1. This makes lookups slightly faster due to stricter balancing.
  • Red-Black Trees are looser in balancing, allowing a height difference of up to 2:1. They achieve balance by coloring nodes red or black and enforcing rules on color. This makes insertion and deletion operations generally faster with fewer rotations than AVL trees. Choose AVL for applications where read operations are much more frequent than writes, like databases with heavy query loads. Choose Red-Black for applications with a mix of reads and writes or where writes are more frequent, such as in maps/sets in standard libraries (e.g., C++ std::map, std::set).

14. Describe the Heap data structure (Min-Heap and Max-Heap) and the heapify operation.

A Heap is a specialized tree-based data structure that satisfies the heap property:

  • Min-Heap: For every node i other than the root, the value of i is greater than or equal to the value of its parent p. The smallest element is always at the root.
  • Max-Heap: For every node i other than the root, the value of i is less than or equal to the value of its parent p. The largest element is always at the root. Heaps are typically implemented using arrays because of their specific structure (complete binary tree). The heapify operation is used to maintain the heap property. When an element is inserted or deleted, or when building a heap from an arbitrary array, heapify ensures the heap property is restored by sifting an element up or down the tree. For deletion of the root, the last element replaces the root, then heapify moves it down to its correct position.

15. Explain how a Hash Table works, including collision resolution strategies like chaining and open addressing.

A Hash Table stores data in an associative array (key-value pairs) by using a hash function to map keys to indices in an array (buckets).

  1. Hash Function: Takes a key and converts it into an array index. A good hash function distributes keys uniformly to minimize collisions.
  2. Collision Resolution: When two different keys map to the same index.
    • Chaining: Each bucket stores a linked list of all key-value pairs that hash to that index. When a collision occurs, the new item is added to the linked list at that bucket.
    • Open Addressing: All elements are stored directly within the hash table array. When a collision occurs, the algorithm probes for an alternative empty slot. Common probing techniques include linear probing (next available slot), quadratic probing, and double hashing. Hash tables offer average O(1) time complexity for insertion, deletion, and search, but worst-case O(N) if collisions are frequent.

Graph Data Structures

16. Compare adjacency matrix and adjacency list representations for graphs. When would you prefer one over the other?

FeatureAdjacency MatrixAdjacency List
StorageO(V²) for V verticesO(V + E) for V vertices, E edges
Adding EdgeO(1)O(1) (for singly linked list) or O(degree)
Checking EdgeO(1) (direct lookup)O(degree(V)) (traverse list)
Iterating NeighborsO(V) (iterate row)O(degree(V))
Space EfficiencyGood for dense graphs (E ≈ V²), bad for sparseGood for sparse graphs (E << V²), bad for dense

Prefer Adjacency Matrix: For dense graphs (many edges), when quick checking of edge existence between any two nodes is frequent, or when the number of vertices is small. Prefer Adjacency List: For sparse graphs (few edges), when iterating over a node's neighbors is more common than checking specific edge existence, or when the number of vertices is large.

17. Explain Breadth-First Search (BFS) for graph traversal and its typical applications.

Breadth-First Search (BFS) is a graph traversal algorithm that explores all the neighbor nodes at the present depth level before moving on to nodes at the next depth level. It uses a queue to manage the order of nodes to visit.

  1. Start at a source node, mark it visited, and enqueue it.
  2. While the queue is not empty:
    • Dequeue a node.
    • Visit/process the node.
    • Enqueue all its unvisited neighbors and mark them visited. Typical Applications: Finding the shortest path in an unweighted graph, network broadcasting, finding connected components, detecting cycles in undirected graphs.
c
// BFS traversal snippet (requires queue implementation)
void BFS(int graph[V][V], int startNode) {
    int visited[V] = {0};
    // Queue q; // Assume queue implementation
    // enqueue(&q, startNode);
    // visited[startNode] = 1;

    // while (!isEmpty(&q)) {
    //     int currentNode = dequeue(&q);
    //     printf("%d ", currentNode);

    //     for (int i = 0; i < V; i++) {
    //         if (graph[currentNode][i] == 1 && !visited[i]) {
    //             enqueue(&q, i);
    //             visited[i] = 1;
    //         }
    //     }
    // }
}

18. Explain Depth-First Search (DFS) for graph traversal and its typical applications.

Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. It uses a stack implicitly (via recursion) or explicitly.

  1. Start at a source node, mark it visited, and push it onto a stack (or call recursively).
  2. While the stack is not empty (or recursive calls continue):
    • Pop a node (or process current recursive node).
    • Visit/process the node.
    • For each unvisited neighbor, recursively call DFS on it (or push it onto the stack). Typical Applications: Finding connected components, detecting cycles in directed graphs, topological sorting, pathfinding, solving mazes (backtracking).
c
// DFS traversal snippet (adjacency matrix)
void DFSUtil(int graph[V][V], int v, int visited[]) {
    visited[v] = 1;
    printf("%d ", v);

    for (int i = 0; i < V; i++) {
        if (graph[v][i] == 1 && !visited[i]) {
            DFSUtil(graph, i, visited);
        }
    }
}

void DFS(int graph[V][V], int startNode) {
    int visited[V] = {0};
    DFSUtil(graph, startNode, visited);
}

19. Outline the main idea behind Dijkstra's Algorithm for shortest paths.

Dijkstra's Algorithm finds the shortest paths from a single source node to all other nodes in a graph with non-negative edge weights. It works using a greedy approach.

  1. Initialize distances to all nodes as infinity, except the source node (0).
  2. Maintain a set of visited nodes and a min-priority queue (often implemented with a min-heap) containing (distance, node) pairs. Initially, it contains (0, source).
  3. While the priority queue is not empty:
    • Extract the node u with the smallest distance from the priority queue.
    • Mark u as visited.
    • For each unvisited neighbor v of u:
      • If dist[u] + weight(u, v) < dist[v], update dist[v] and add/update (dist[v], v) in the priority queue. Dijkstra's is efficient for its specific use case (non-negative weights).

20. Briefly describe Prim's or Kruskal's Algorithm for finding a Minimum Spanning Tree (MST).

Prim's Algorithm and Kruskal's Algorithm both find a Minimum Spanning Tree (MST) for a connected, undirected, weighted graph. An MST is a subgraph that connects all vertices with the minimum possible total edge weight, without forming any cycles.

  • Prim's Algorithm: Starts from an arbitrary vertex and grows the MST by iteratively adding the cheapest edge that connects a vertex already in the MST to a vertex not yet in the MST. It uses a min-priority queue to efficiently select the next edge. It's often compared to Dijkstra's.
  • Kruskal's Algorithm: Sorts all edges by weight in non-decreasing order. It then iteratively adds the next cheapest edge to the MST if it does not form a cycle with the edges already added. It uses a Disjoint Set Union (DSU) data structure to efficiently detect cycles.

Algorithms & Techniques

21. Briefly describe the core idea behind Bubble Sort, Selection Sort, and Insertion Sort.

These are comparison-based sorting algorithms, generally suitable for small datasets due to their O(N²) average and worst-case time complexity.

  • Bubble Sort: Repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. Passes through the list are repeated until no swaps are needed, indicating the list is sorted. Larger elements "bubble" to the end.
  • Selection Sort: Divides the list into a sorted and an unsorted part. It repeatedly finds the minimum element from the unsorted part and swaps it with the first element of the unsorted part, thus extending the sorted part.
  • Insertion Sort: Builds the final sorted array (or list) one item at a time. It iterates through the input elements and, for each element, finds its correct position within the already sorted portion of the array and inserts it there.

22. Explain Merge Sort and its time/space complexity.

Merge Sort is a divide-and-conquer sorting algorithm.

  1. Divide: It recursively divides the unsorted list into N sublists, each containing one element (a list of one element is considered sorted).
  2. Conquer (Merge): Repeatedly merges sublists to produce new sorted sublists until there is only one sorted list remaining. The merge operation is critical: it takes two sorted sublists and combines them into one sorted list. Time Complexity: O(N log N) in all cases (best, average, worst) because it always divides the list into two halves and performs linear-time merging. Space Complexity: O(N) due to the temporary array needed during the merge operation.
c
// Merge function snippet (core of Merge Sort)
void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;

    int L[n1], R[n2]; // Temp arrays

    for (i = 0; i < n1; i++) L[i] = arr[l + i];
    for (j = 0; j < n2; j++) R[j] = arr[m + 1 + j];

    i = 0; j = 0; k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) arr[k++] = L[i++];
        else arr[k++] = R[j++];
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

23. Explain Quick Sort, its partitioning strategy, and potential worst-case scenario.

Quick Sort is another divide-and-conquer sorting algorithm, generally faster in practice than Merge Sort due to better cache performance, though it has a worst-case scenario.

  1. Pivot Selection: Choose an element from the array, called the pivot.
  2. Partitioning: Rearrange the array such that all elements smaller than the pivot come before it, and all elements greater come after it. The pivot is now in its final sorted position.
  3. Recurse: Recursively apply Quick Sort to the sub-arrays of smaller and greater elements. Worst-case scenario: Occurs when the pivot selection consistently results in one sub-array being empty (e.g., always picking the smallest or largest element as pivot on an already sorted array). This leads to O(N²) time complexity. To mitigate this, common strategies include picking a random pivot or median-of-three pivot. Average time complexity is O(N log N). Space complexity is O(log N) due to recursion stack.

24. Describe Binary Search and its requirements.

Binary Search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing the search interval in half.

  1. Start with an interval covering the whole array.
  2. If the value of the search key is less than the item in the middle of the interval, narrow the interval to the lower half.
  3. Otherwise, narrow it to the upper half.
  4. Repeat until the value is found or the interval is empty. Requirements: The input collection must be sorted for Binary Search to work correctly. It cannot be used on unsorted data. Time Complexity: O(log N). Space Complexity: O(1) for iterative, O(log N) for recursive due to call stack.

25. Compare recursion and iteration in C programming. When is recursion preferred for DSA problems?

FeatureRecursionIteration (Loops)
ConceptFunction calling itselfRepeated execution of a block of code
MemoryUses call stack (can lead to stack overflow)Uses fixed memory (generally more efficient)
ReadabilityOften more concise for tree/graph problemsCan be verbose for complex recursive logic
PerformanceOverhead of function callsGenerally faster, less overhead
DebuggingCan be harder to trace call stackEasier to inspect variables at each step

Recursion is preferred for problems that naturally fit a recursive definition, such as:

  • Tree and Graph traversals (DFS, tree traversals like inorder, preorder, postorder).
  • Divide-and-conquer algorithms (Merge Sort, Quick Sort).
  • Problems involving backtracking.
  • When the recursive solution is significantly simpler and more elegant than an iterative one, and the depth of recursion is manageable to avoid stack overflow.

26. Explain the concept of Dynamic Programming and differentiate between Memoization and Tabulation.

Dynamic Programming (DP) is an algorithmic technique used to solve complex problems by breaking them down into simpler subproblems. It solves each subproblem only once and stores their solutions to avoid redundant computations, especially for problems with overlapping subproblems and optimal substructure.

  • Memoization (Top-down DP): A recursive approach where solutions to subproblems are stored (memoized) in a table (e.g., an array or hash map) as they are computed. When a subproblem is encountered again, its stored solution is retrieved instead of recomputing. It starts from the main problem and recursively solves subproblems.
  • Tabulation (Bottom-up DP): An iterative approach where the solutions to subproblems are computed and stored in a table (e.g., an array) from the smallest subproblems up to the main problem. It builds up the solution iteratively, ensuring all needed subproblem solutions are available when a larger problem is tackled. Both achieve the same goal of avoiding re-computation, but through different execution flows.

Advanced Topics & System Design Considerations

27. What are memory leaks and dangling pointers in C? How can they be prevented?

  • Memory Leak: Occurs when dynamically allocated memory is no longer reachable by the program but has not been deallocated using free(). The program loses its reference to the memory block, making it impossible to free, leading to a gradual consumption of available memory.
  • Dangling Pointer: A pointer that points to a memory location that has been freed or deallocated. If the program attempts to dereference a dangling pointer, it can lead to undefined behavior, crashes, or security vulnerabilities (use-after-free). Prevention:
  1. Always free() allocated memory: For every malloc, calloc, or realloc, ensure a corresponding free call when the memory is no longer needed.
  2. Nullify pointers after free(): After free(ptr), set ptr = NULL; to prevent it from becoming a dangling pointer. Check for NULL before dereferencing.
  3. Track memory: Use debugging tools (e.g., Valgrind) to detect memory leaks and invalid memory accesses.
  4. Ownership rules: Clearly define which part of the code is responsible for allocating and freeing specific memory blocks.
c
int *arr = (int *)malloc(10 * sizeof(int));
// Use arr
free(arr); // Deallocate
arr = NULL; // Prevent dangling pointer

28. Discuss performance considerations in C, particularly regarding cache locality and data alignment.

In C, fine-grained control allows significant performance optimization, especially crucial for large-scale DSA.

  • Cache Locality: CPU caches are fast but small. Accessing data that is spatially or temporally close to recently accessed data improves performance because it's likely already in cache.
    • Spatial Locality: Arranging data contiguously in memory (e.g., arrays, structs) enables the CPU to fetch multiple elements in a single cache line. Loop through arrays sequentially, not randomly.
    • Temporal Locality: Reusing the same data items within a short period.
  • Data Alignment: Modern CPUs operate most efficiently when data is aligned on memory addresses that are multiples of their size (e.g., an int on a 4-byte boundary). Misaligned access can incur performance penalties or even crashes on some architectures. Compilers often handle this, but manual malloc or struct packing can cause issues. _Alignof operator and aligned_alloc in C11 help ensure proper alignment. Optimized data structures should consider memory layout.

29. How do threads and mutexes relate to Data Structures & Algorithms in a C context?

In a multi-threaded C environment, multiple threads might access and modify the same data structures concurrently.

  • Threads: Allow a program to execute multiple parts concurrently, improving responsiveness and utilizing multi-core processors.
  • Mutexes (Mutual Exclusion): Are synchronization primitives used to protect shared data structures from race conditions. A mutex ensures that only one thread can access a critical section of code (e.g., modifying a linked list, updating a shared counter) at any given time. Without proper synchronization using mutexes, operations on shared data structures can lead to data corruption, inconsistent states, and unpredictable behavior. For example, if two threads try to insert into a linked list simultaneously without a mutex, they might overwrite next pointers, corrupting the list. Using a pthread_mutex_t for locking/unlocking before/after critical sections is essential for thread-safe DSA.

30. Given a scenario where you need to store key-value pairs with frequent lookups and occasional insertions/deletions, which data structure in C would you choose and why?

For frequent lookups and occasional insertions/deletions of key-value pairs, a Hash Table (implemented with chaining) is generally the best choice in C.

Why:

  1. Average O(1) Time Complexity: Hash tables provide average O(1) time for lookup, insertion, and deletion operations, assuming a good hash function and reasonable load factor. This is significantly faster than O(log N) for balanced trees or O(N) for unsorted lists/arrays.
  2. Flexibility with Chaining: Using chaining for collision resolution handles collisions gracefully by storing multiple entries in linked lists within buckets. This prevents worst-case scenarios of open addressing (like primary clustering) from severely degrading performance, especially as the load factor increases.
  3. Dynamic Sizing: While resizing requires re-hashing, it allows the table to grow, maintaining performance even with increasing data.

A Binary Search Tree (specifically a self-balancing one like AVL or Red-Black Tree) would also work, offering O(log N) for all operations. However, for "frequent lookups" where average performance is key, hash tables often outperform trees due to their constant-time access nature, provided memory layout (cache locality) and hash function quality are good. If range queries or sorted order iteration were critical, a balanced BST would be preferred.


Quick revision table

TopicOne-line answerCommon follow-up
PointersVariables holding memory addresses.How to handle pointer arithmetic with different data types?
Dynamic Memory Allocationmalloc, calloc, realloc, free for heap management.What causes memory leaks and how to prevent them?
StructsUser-defined data types combining different types.How are structs used to build self-referential structures?
Singly Linked ListNodes with data and a pointer to the next node.How do you delete a node from a specific position?
Doubly Linked ListNodes with pointers to both next and previous nodes.What are the memory overhead implications?
Stack (LIFO)Last-In, First-Out collection (e.g., function call stack).Implement push and pop using an array and handle overflow.
Queue (FIFO)First-In, First-Out collection (e.g., task scheduling).How do you implement a circular queue to optimize space?
Binary Search TreeLeft < Root < Right for all nodes.What is the worst-case scenario for BST operations and why?
Heap (Min/Max)Tree-based structure satisfying heap property (parent-child relation).How is a heap used in Heap Sort or a priority queue?
Hash TableMaps keys to array indices using a hash function.Explain the trade-offs between chaining and open addressing.
Graph RepresentationAdjacency matrix (O(V²)) or list (O(V+E)).When is a weighted graph represented differently?
BFSExplores layer by layer using a queue (shortest path unweighted).How would you find all connected components using BFS?
DFSExplores depth-first using a stack/recursion (topological sort).How can DFS detect cycles in a directed graph?
Dijkstra's AlgorithmShortest paths from source in weighted, non-negative graphs.What data structure is crucial for its efficiency?
Merge SortDivide and conquer, O(N log N) time, O(N) space.Is Merge Sort stable? Why is stability important?
Quick SortDivide and conquer, O(N log N) avg., O(N²) worst-case.How can pivot selection impact Quick Sort's performance?
Binary SearchEfficiently finds element in a sorted array (O(log N)).What happens if the array is not sorted?
Dynamic ProgrammingSolves subproblems once, stores results (overlapping, optimal substructure).Provide an example where DP (memoization) is better than pure recursion.
Memory LeaksUnfreed dynamically allocated memory.Name a tool used to detect memory leaks in C.
Cache LocalityOrganizing data for efficient CPU cache usage.How does iterating over a 2D array by row vs. column affect cache?

How to prepare

  • Master C Fundamentals: Pointers, memory allocation, structs, and basic I/O are the bedrock. Practice them until they are second nature.
  • Implement from Scratch: Don't just understand, implement core data structures like linked lists, stacks, queues, and basic trees using plain C. This reinforces memory management and pointer handling.
  • Practice Algorithm Variations: Understand the core algorithms (sorting, searching, graph traversals), then try solving common variations or applying them to different scenarios.
  • Focus on Time and Space Complexity (Big O): Be able to analyze and articulate the performance implications of your chosen data structures and algorithms, especially in C where direct memory access matters.
  • Debug and Optimize: Use debugging tools (like GDB or Valgrind) to find memory errors. Think about how to optimize your C code for performance, considering aspects like cache locality and system calls.