Preparing for placements at top product-based companies like Microsoft, Amazon, D.E. Shaw, Directi, Intel, Nutanix, and KPIT requires a strong grasp of Data Structures and Algorithms (DSA). These companies frequently test candidates on their problem-solving abilities, making DSA a crucial subject for aspirants.
In this guide, we cover essential DSA topics with problem sets ranging from basic to advanced levels, ensuring you’re ready to ace coding interviews.
Each section includes practical problems, making it easier to build problem-solving intuition.
Sorting is a crucial topic in coding interviews. Understanding various sorting techniques and their time complexities helps in optimizing solutions.
Dynamic programming is widely tested in coding interviews. Understanding memoization and tabulation techniques is key to solving these problems efficiently.
These problems require a combination of graph theory, recursion, and dynamic programming.
Write a program to reverse a singly linked list.
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void reverse(struct Node** head) {
struct Node* prev = NULL;
struct Node* current = *head;
struct Node* next = NULL;
while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head = prev;
}
// Helper functions omitted for brevity
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
void reverse(Node*& head) {
Node *prev = nullptr, *curr = head, *next = nullptr;
while (curr) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
head = prev;
}
class Node {
int data;
Node next;
Node(int data) { this.data = data; this.next = null; }
}
public class ReverseLinkedList {
static Node reverse(Node head) {
Node prev = null, current = head, next = null;
while (current != null) {
next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
}
class Node:
def __init__(self, data):
self.data = data
self.next = None
def reverse(head):
prev = None
current = head
while current:
next = current.next
current.next = prev
prev = current
current = next
return prev
Design a stack using an array with push, pop, and display operations.
#include <stdio.h>
#define MAX 100
int stack[MAX], top = -1;
void push(int x) {
if (top == MAX - 1) { printf("Stack Overflow\n"); return; }
stack[++top] = x;
}
int pop() {
if (top == -1) { printf("Stack Underflow\n"); return -1; }
return stack[top--];
}
#include <iostream>
#define MAX 100
using namespace std;
class Stack {
int top, arr[MAX];
public:
Stack() { top = -1; }
void push(int x) {
if (top == MAX - 1) { cout << "Overflow\n"; return; }
arr[++top] = x;
}
int pop() {
if (top == -1) { cout << "Underflow\n"; return -1; }
return arr[top--];
}
};
class Stack {
int top = -1;
int[] arr = new int[100];
void push(int x) {
if (top == 99) { System.out.println("Overflow"); return; }
arr[++top] = x;
}
int pop() {
if (top == -1) { System.out.println("Underflow"); return -1; }
return arr[top--];
}
}
class Stack:
def __init__(self):
self.stack = []
def push(self, x):
self.stack.append(x)
def pop(self):
if not self.stack:
print("Underflow")
return -1
return self.stack.pop()
Implement a queue using two stacks with enqueue and dequeue operations.
#include <stack>
#include <iostream>
using namespace std;
class Queue {
stack<int> s1, s2;
public:
void enqueue(int x) { s1.push(x); }
int dequeue() {
if (s1.empty() && s2.empty()) { cout << "Queue is Empty\n"; return -1; }
if (s2.empty()) {
while (!s1.empty()) { s2.push(s1.top()); s1.pop(); }
}
int x = s2.top(); s2.pop();
return x;
}
};
import java.util.Stack;
class Queue {
Stack<Integer> s1 = new Stack<>();
Stack<Integer> s2 = new Stack<>();
void enqueue(int x) { s1.push(x); }
int dequeue() {
if (s1.isEmpty() && s2.isEmpty()) { System.out.println("Queue is Empty"); return -1; }
if (s2.isEmpty()) { while (!s1.isEmpty()) { s2.push(s1.pop()); } }
return s2.pop();
}
}
class Queue:
def __init__(self):
self.s1 = []
self.s2 = []
def enqueue(self, x):
self.s1.append(x)
def dequeue(self):
if not self.s2:
while self.s1:
self.s2.append(self.s1.pop())
if not self.s2:
print("Queue is Empty")
return -1
return self.s2.pop()
Find the middle element of a singly linked list in O(n) time.
struct Node {
int data;
struct Node* next;
};
void findMiddle(Node* head) {
Node *slow = head, *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
printf("Middle Element: %d\n", slow->data);
}
void findMiddle(Node* head) {
Node *slow = head, *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
cout << "Middle Element: " << slow->data << endl;
}
void findMiddle(Node head) {
Node slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
System.out.println("Middle Element: " + slow.data);
}
def find_middle(head):
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
print("Middle Element:", slow.data)
Mastering Data Structures and Algorithms is essential for cracking placement tests at top tech companies. By practicing these problems and understanding core concepts, you’ll be well-prepared for coding interviews.