Coding Round Preparation for Freshers: Complete Guide for Campus Placements
By FreePare Team · Mon Jun 22 2026 · 32 min read
Coding rounds can feel difficult for freshers, especially when everyone talks about DSA, competitive programming, algorithms, and coding platforms.
But you do not need to become an expert programmer on the first day.
For campus placements, coding preparation should start with strong basics. You need to understand one programming language properly, solve beginner-level problems, build logic, and slowly move toward data structures and algorithms.
This guide will help you prepare for coding rounds step by step. Every topic below comes with a real problem, complete working code you can run, its time and space complexity, and the one idea that makes the solution work. Read the code, type it out yourself, then change the input and see what breaks. (Throughout this guide, n^2 means n squared and 10^8 means 1 followed by eight zeros.)
Why Coding Rounds are Important
Most technical roles include a coding round.
Companies use coding tests to check whether you can:
- Think logically
- Write working code
- Understand problem statements
- Use basic data structures
- Handle test cases
- Debug errors
- Optimize simple solutions
- Explain your approach
Coding rounds are not only about syntax. They test your problem-solving ability.
For service-based companies, basic and medium-level coding may be enough. For product-based companies, stronger DSA preparation is usually required.
A practical way to read that difference: a service-company round usually gives two or three problems that are solvable with loops, a hash map, and careful edge-case handling. A product-company round expects you to notice that a nested loop will time out on the given input size, and to reach for sorting, two pointers, binary search, or dynamic programming instead.
Choose One Programming Language
Many freshers waste time switching between languages.
They start with C, then move to Python, then Java, then C++, and finally feel confused.
Do not do that.
Choose one language and become comfortable with it.
Good options for placement coding:
- C
- C++
- Java
- Python
If you are preparing for software roles, Java, C++, and Python are commonly used choices.
Pick the language you understand best.
What actually differs between them in a timed round is how much code you have to type and how fast that code runs.
| Language | Strength in a coding round | What to watch out for |
|---|---|---|
| Python | Shortest code; dictionaries, sets, sorting and unbounded integers are built in | Each loop iteration is far slower than in C++, so a tight O(n^2) loop on large input can time out even when the logic is correct |
| C++ | Fastest execution; the STL gives you vector, map, set, sort and priority_queue | You must watch integer overflow and out-of-range indexing yourself |
| Java | Large standard library, and a strict compiler that catches mistakes early | More boilerplate, and slow input reading if you use Scanner on large inputs |
| C | Fast, and simple to reason about | No built-in hash map, string type, or dynamic array, so you write more yourself |
Whichever you pick, learn its sorting function, its hash map, its dynamic array, and its string handling well enough to write them without looking anything up.
Programming Basics Every Fresher Should Know

Before solving placement coding questions, revise the basics.
Important programming concepts:
- Input and output
- Variables
- Data types
- Operators
- Conditions
- Loops
- Functions
- Arrays
- Strings
- Recursion basics
- Object-oriented programming basics
If these basics are weak, even easy coding questions will feel difficult.
One basic that quietly fails candidates is reading input. Most online judges hand you the entire input on standard input, and a slow reader can make a correct solution exceed the time limit. This is the input pattern worth memorising in Python:
import sys
def main():
data = sys.stdin.read().split()
n = int(data[0])
nums = [int(x) for x in data[1:1 + n]]
print(sum(nums))
main()
Reading everything once and splitting it is much faster than calling input() inside a loop. The Java equivalent uses BufferedReader rather than Scanner, which is one of the most common reasons a correct Java solution runs out of time:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
StringTokenizer st = new StringTokenizer(br.readLine());
long sum = 0;
for (int i = 0; i < n; i++) {
sum += Long.parseLong(st.nextToken());
}
System.out.println(sum);
}
}
Notice the long for the running total. If n is 100000 and each value can reach 10^9, the sum reaches 10^14, which overflows a 32-bit int silently and produces a wrong answer with no error message at all.
Read the Constraints Before You Write Code
The constraints printed under the problem statement are not decoration. They tell you which approach the setter expects, before you have written a single line.
The rule of thumb is that a judge running compiled code gets through roughly 10^8 simple operations per second. For interpreted Python, plan for closer to 10^7 per second in a plain loop. Divide that budget by the largest n in the constraints and you get the complexity you are allowed.
| Largest n | Complexity you can afford | Technique that usually fits |
|---|---|---|
| up to 10 | O(n!) | Generate every permutation with recursion |
| up to 20 | O(2^n) | Try every subset, with backtracking or a bitmask |
| up to 500 | O(n^3) | Triple loop, or dynamic programming over pairs of indices |
| up to 5,000 | O(n^2) | Nested loop, or a two-dimensional DP table |
| up to 100,000 | O(n log n) | Sorting, binary search, a heap, or sort-then-two-pointers |
| up to 10,000,000 | O(n) | One pass with a hash map, prefix sums, or a sliding window |
| tiny n but values up to 10^9 | O(log n) or O(1) | Binary search on the answer, or a direct formula |
Two examples of reading this correctly. If a problem says n is at most 100,000 and asks for a pair of numbers that sums to a target, checking every pair is about 10^10 operations and will time out, while a single pass with a hash map is 10^5 operations and passes comfortably. If a problem says n is at most 1,000, checking every pair is only 10^6 operations and is completely fine, so do not spend ten minutes inventing a clever solution you might get wrong.
Read the value range too, not only the count. Values up to 10^9 mean sums can overflow 32-bit integers. Values that may be negative break the assumption that prefix sums keep increasing. A line saying the string contains both uppercase and lowercase letters means a frequency array of size 26 is not enough.
If Big O still feels vague, work through Time Complexity for Beginners: Understanding Big O Without the Confusion before you continue, because every section below states a complexity and assumes you know why it matters.
Important Coding Topics for Placements
Here are the most important coding topics freshers should prepare. Each one below is a real problem with a complete solution, not a list of topic names. Try to write the code yourself before reading the solution.
1. Arrays
Arrays are one of the most important topics in coding rounds.
Practice questions like:
- Find largest element
- Find second largest element
- Reverse an array
- Find missing number
- Remove duplicates
- Find frequency of elements
- Rotate an array
- Find pair with given sum
Arrays are the foundation of many coding problems.
Worked problem: find the second largest element. Given an array of integers, return the second largest distinct value, or nothing if it does not exist. The lazy answer is to sort and read the second-last element, which costs O(n log n). You can do it in one pass.
def second_largest(nums):
largest = float('-inf')
second = float('-inf')
for x in nums:
if x > largest:
second = largest
largest = x
elif x > second and x < largest:
second = x
return None if second == float('-inf') else second
print(second_largest([12, 35, 1, 10, 34, 1])) # 34
print(second_largest([5, 5, 5])) # None
Key insight: keep two running maximums, and when a new value beats the largest, the old largest becomes the second largest. The condition x < largest in the elif is what keeps duplicates of the maximum from being reported as a second largest, which is the edge case most candidates miss. Time O(n), space O(1).
Worked problem: rotate an array left by k positions, in place. For [1, 2, 3, 4, 5] with k = 2 the answer is [3, 4, 5, 1, 2]. Rotating one step at a time k times costs O(n * k). The reversal trick costs O(n).
def rotate_left(nums, k):
n = len(nums)
if n == 0:
return nums
k %= n
def reverse(lo, hi):
while lo < hi:
nums[lo], nums[hi] = nums[hi], nums[lo]
lo += 1
hi -= 1
reverse(0, k - 1) # reverse the first k
reverse(k, n - 1) # reverse the rest
reverse(0, n - 1) # reverse the whole array
return nums
print(rotate_left([1, 2, 3, 4, 5], 2)) # [3, 4, 5, 1, 2]
The same algorithm in Java, because array rotation is asked in both languages:
static void reverse(int[] a, int lo, int hi) {
while (lo < hi) {
int temp = a[lo];
a[lo] = a[hi];
a[hi] = temp;
lo++;
hi--;
}
}
static void rotateLeft(int[] a, int k) {
int n = a.length;
if (n == 0) return;
k %= n;
reverse(a, 0, k - 1);
reverse(a, k, n - 1);
reverse(a, 0, n - 1);
}
Key insight: reversing the two blocks separately puts each block's elements in the wrong order but the right region; reversing the whole array then fixes the order inside both blocks. The k %= n line handles k larger than the array, and it is the line interviewers check for. Time O(n), space O(1).
2. Strings
String questions are common in beginner and intermediate coding rounds.
Practice:
- Reverse a string
- Check palindrome
- Count vowels and consonants
- Find character frequency
- Remove duplicates
- Check anagram
- Find first non-repeating character
String problems improve logic and implementation.
Worked problem: first non-repeating character. Return the index of the first character that appears exactly once, or -1 if every character repeats.
def first_unique_char(s):
counts = {}
for ch in s:
counts[ch] = counts.get(ch, 0) + 1
for i, ch in enumerate(s):
if counts[ch] == 1:
return i
return -1
print(first_unique_char("leetcode")) # 0
print(first_unique_char("aabbcdd")) # 4
print(first_unique_char("aabb")) # -1
Key insight: two passes beat one clever pass. The first pass counts, the second pass walks the string in original order and returns the first count of one. Trying to do it in a single pass is where people lose the "first" part of the requirement. Time O(n), space O(k) where k is the number of distinct characters, so O(1) for a fixed alphabet.
Worked problem: are two strings anagrams? Two strings are anagrams if one is a rearrangement of the other.
def is_anagram(a, b):
if len(a) != len(b):
return False
counts = {}
for ch in a:
counts[ch] = counts.get(ch, 0) + 1
for ch in b:
if counts.get(ch, 0) == 0:
return False
counts[ch] -= 1
return True
print(is_anagram("listen", "silent")) # True
print(is_anagram("aab", "abb")) # False
Key insight: the length check first is not a nicety, it is what makes the count-down logic correct. Once the lengths match, if every character of b can be cancelled against a count from a, nothing can be left over. Sorting both strings and comparing also works and is easier to remember, but costs O(n log n) instead of O(n). Space is O(k) for the counts.
3. Two Pointers
Two pointers is the pattern hiding behind a large share of array and string questions, so it deserves its own place beside the named data structures. The idea is to keep two indices moving through the data, using a property of the input, usually sortedness, to decide which one to move.
Worked problem: find a pair in a sorted array that sums to a target.
def pair_with_sum(sorted_nums, target):
lo = 0
hi = len(sorted_nums) - 1
while lo < hi:
total = sorted_nums[lo] + sorted_nums[hi]
if total == target:
return (lo, hi)
if total < target:
lo += 1 # need a bigger sum
else:
hi -= 1 # need a smaller sum
return None
print(pair_with_sum([1, 3, 4, 6, 8, 10], 10)) # (2, 3)
print(pair_with_sum([1, 3, 4], 100)) # None
Key insight: because the array is sorted, if the current sum is too small the only way to increase it is to move the left pointer right, and if it is too big the only way to decrease it is to move the right pointer left. Each move eliminates one candidate permanently, so no pair is ever missed. Time O(n), space O(1). If the array is not sorted, sorting first makes the whole thing O(n log n), which is still far better than checking every pair.
4. Searching
Important searching topics:
- Linear search
- Binary search
- Search in sorted array
- Find first and last occurrence
- Search insert position
Binary search is especially important for placement coding.
Worked problem: first and last position of a target in a sorted array. For [5, 7, 7, 8, 8, 10] and target 8, the answer is (3, 4). Rather than writing two subtly different binary searches, write one boundary-finding helper and use it twice.
def lower_bound(nums, target):
"""Index of the first element that is not less than target."""
lo, hi = 0, len(nums)
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] < target:
lo = mid + 1
else:
hi = mid
return lo
def upper_bound(nums, target):
"""Index of the first element strictly greater than target."""
lo, hi = 0, len(nums)
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] <= target:
lo = mid + 1
else:
hi = mid
return lo
def first_and_last(nums, target):
start = lower_bound(nums, target)
if start == len(nums) or nums[start] != target:
return (-1, -1)
return (start, upper_bound(nums, target) - 1)
print(first_and_last([5, 7, 7, 8, 8, 10], 8)) # (3, 4)
print(first_and_last([5, 7, 7, 8, 8, 10], 6)) # (-1, -1)
Key insight: the half-open range [lo, hi) with hi starting at len(nums) is what makes this version safe. The loop never reads nums[hi], mid is always a valid index because lo < hi, and the range shrinks on every iteration, so the loop always terminates. lower_bound doubles as the answer to "search insert position". Time O(log n), space O(1).
Binary search only works on data that is sorted, or on an answer space where a yes/no test flips exactly once from false to true. Checking that condition is the actual skill; the code is short and always the same.
5. Sorting
Understand basic sorting algorithms:
- Bubble sort
- Selection sort
- Insertion sort
- Merge sort basics
- Quick sort basics
Also learn how to use built-in sorting functions in your chosen language.
| Algorithm | Average time | Worst time | Extra space | Stable |
|---|---|---|---|---|
| Bubble sort | O(n^2) | O(n^2) | O(1) | Yes |
| Selection sort | O(n^2) | O(n^2) | O(1) | No |
| Insertion sort | O(n^2) | O(n^2) | O(1) | Yes |
| Merge sort | O(n log n) | O(n log n) | O(n) | Yes |
| Quick sort | O(n log n) | O(n^2) | O(log n) stack | No |
Insertion sort is O(n) on data that is already sorted, which is why real library sorts fall back to it on small pieces. Quick sort hits its O(n^2) worst case when the pivot is repeatedly the smallest or largest element, for example when a naive first-element pivot meets already-sorted input.
Worked problem: implement merge sort.
def merge_sort(nums):
if len(nums) <= 1:
return nums[:]
mid = len(nums) // 2
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:])
merged = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merged.append(left[i])
i += 1
else:
merged.append(right[j])
j += 1
merged.extend(left[i:])
merged.extend(right[j:])
return merged
print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
# [3, 9, 10, 27, 38, 43, 82]
Key insight: merging two already-sorted lists is a two-pointer walk. The <= rather than < is what makes merge sort stable, meaning equal elements keep their original relative order, which matters when you sort records by one field and expect the previous ordering to survive. Time O(n log n) in every case, space O(n).
In a real round you will usually call the built-in sort. What interviewers test there is whether you can sort by a custom rule. Sorting numbers by descending frequency, and by value when frequencies tie, looks like this:
from collections import Counter
def sort_by_frequency(nums):
counts = Counter(nums)
return sorted(nums, key=lambda x: (-counts[x], x))
print(sort_by_frequency([1, 1, 2, 2, 2, 3]))
# [2, 2, 2, 1, 1, 3]
The key returns a tuple, and tuples compare element by element, so the negative count sorts higher frequencies first and the value breaks ties. Time O(n log n), space O(n).
6. Recursion
Recursion helps in many coding problems.
Start with:
- Factorial
- Fibonacci
- Sum of numbers
- Reverse string
- Power calculation
- Basic backtracking idea
Do not jump into difficult recursion problems immediately.
Worked problem: compute base raised to exp in O(log exp) time. The loop version multiplies exp times. Fast exponentiation halves the exponent each step.
def power(base, exp):
if exp == 0:
return 1
half = power(base, exp // 2)
if exp % 2 == 0:
return half * half
return half * half * base
print(power(2, 10)) # 1024
print(power(3, 5)) # 243
Key insight: every recursive function needs a base case that stops it and a recursive step that moves strictly closer to that base case. Here exp == 0 stops it and exp // 2 shrinks the problem by half, so the depth is about log2(exp), roughly 30 calls even for an exponent of a billion. Computing half once and squaring it, instead of calling power twice, is the difference between O(log exp) and O(exp). Space is O(log exp) for the call stack.
Worked problem: generate all subsets of a list. This is the smallest honest example of backtracking, and it explains why the constraints table gives O(2^n) only up to about n = 20.
def subsets(nums):
result = []
current = []
def backtrack(index):
if index == len(nums):
result.append(current[:]) # copy, not reference
return
backtrack(index + 1) # branch 1: skip nums[index]
current.append(nums[index])
backtrack(index + 1) # branch 2: take nums[index]
current.pop() # undo before returning
backtrack(0)
return result
print(subsets([1, 2]))
# [[], [2], [1], [1, 2]]
Key insight: at each index there are exactly two choices, take it or skip it, so the recursion tree has 2^n leaves and each leaf is one subset. The current.pop() is the backtracking step: it restores the state so the next branch starts clean. Appending current[:] rather than current matters, because current is one shared list that keeps changing. Time O(n * 2^n) because each of the 2^n subsets costs up to O(n) to copy; recursion depth O(n).
7. Hashing
Hashing helps solve frequency and lookup-based problems faster.
Practice:
- Frequency count
- Duplicate elements
- Two-sum problem
- Common elements
- First repeating element
Hashing is useful for improving time complexity.
Worked problem: two sum. Given an unsorted array and a target, return the indices of two numbers that add up to the target. Checking every pair is O(n^2); a hash map does it in one pass.
def two_sum(nums, target):
seen = {} # value -> index where we saw it
for i, x in enumerate(nums):
need = target - x
if need in seen:
return [seen[need], i]
seen[x] = i
return []
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]
print(two_sum([3, 2, 4], 6)) # [1, 2]
The same idea in Java:
import java.util.HashMap;
import java.util.Map;
static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int need = target - nums[i];
if (seen.containsKey(need)) {
return new int[] { seen.get(need), i };
}
seen.put(nums[i], i);
}
return new int[0];
}
Key insight: instead of searching for the partner, remember every number you have already passed, so that when you reach its partner the lookup is a single hash-table probe. Checking seen before inserting the current number is what stops an element from pairing with itself when the target is exactly twice that element. Time O(n) on average, space O(n). Hash-map lookups are average O(1), not guaranteed O(1): with many collisions a bucket degrades toward a list.
8. Stack and Queue
Basic stack and queue questions are useful for placement rounds.
Practice:
- Stack implementation
- Queue implementation
- Valid parentheses
- Reverse a queue
- Next greater element basics
Worked problem: valid parentheses. Given a string of brackets, decide whether every bracket is closed by the matching type in the correct order.
def is_valid(s):
pairs = {')': '(', ']': '[', '}': '{'}
stack = []
for ch in s:
if ch in '([{':
stack.append(ch)
elif ch in pairs:
if not stack or stack.pop() != pairs[ch]:
return False
return not stack # anything left open means invalid
print(is_valid("{[]}")) # True
print(is_valid("([)]")) # False
print(is_valid("((")) # False
Key insight: a stack is the right structure whenever the most recent unfinished thing is the one you must finish first. The two failure modes are a closing bracket with an empty stack, and a leftover open bracket at the end, and a solution that forgets the second one passes most sample tests and fails the hidden ones. Time O(n), space O(n).
Worked problem: next greater element. For each element, report the first element to its right that is larger, or -1. The nested loop is O(n^2). A monotonic stack does it in O(n).
def next_greater(nums):
result = [-1] * len(nums)
stack = [] # indices whose answer is still unknown
for i, x in enumerate(nums):
while stack and nums[stack[-1]] < x:
result[stack.pop()] = x
stack.append(i)
return result
print(next_greater([4, 5, 2, 25])) # [5, 25, 25, -1]
print(next_greater([13, 7, 6, 12])) # [-1, 12, 12, -1]
Key insight: the stack holds the indices still waiting for an answer, and their values are always in decreasing order from bottom to top. A new value resolves everything smaller than itself, all at once. Each index is pushed once and popped at most once, so despite the inner while loop the total work is O(n) amortised, with O(n) space. Being able to explain that amortised argument is worth more in an interview than the code.
9. Linked List Basics
Some companies ask linked list questions.
Prepare:
- Create linked list
- Insert node
- Delete node
- Reverse linked list
- Find middle element
- Detect loop basics
Worked problem: reverse a singly linked list, iteratively.
class Node:
def __init__(self, value, next_node=None):
self.value = value
self.next = next_node
def reverse_list(head):
previous = None
current = head
while current is not None:
next_node = current.next # save the rest of the list
current.next = previous # flip this link
previous = current # step both markers forward
current = next_node
return previous # new head
def to_list(head):
out = []
while head is not None:
out.append(head.value)
head = head.next
return out
head = Node(1, Node(2, Node(3, Node(4))))
print(to_list(reverse_list(head))) # [4, 3, 2, 1]
Key insight: you need three references at once. Saving next_node before overwriting current.next is the whole trick, because the moment you flip the link you have lost the rest of the list. The loop ends with current at None and previous at the last node, which is the new head. Time O(n), space O(1).
Worked problem: middle node and cycle detection with slow and fast pointers.
def middle_node(head):
slow = fast = head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
return slow
def has_cycle(head):
slow = fast = head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
Key insight: the fast pointer moves twice as quickly, so when it reaches the end the slow pointer is at the middle. On a list with an even number of nodes this returns the second of the two middles; say that out loud in an interview rather than hoping nobody asks. For cycle detection, if a loop exists the fast pointer keeps circling and closes the gap on the slow pointer by one node per step, so they must eventually land on the same node. Checking both fast and fast.next before advancing is what prevents an attribute error on an even-length list. Time O(n), space O(1).
10. Basic Dynamic Programming
For beginner-level placement preparation, start with simple DP only.
Practice:
- Fibonacci using DP
- Climbing stairs
- Minimum cost basics
- 0/1 knapsack concept
Do not start advanced DP too early.
Worked problem: climbing stairs. You can climb 1 or 2 steps at a time. In how many distinct ways can you reach step n? For n = 5 the answer is 8.
def climb_stairs(n):
if n <= 2:
return max(n, 1)
prev2, prev1 = 1, 2 # ways to reach step 1 and step 2
for _ in range(3, n + 1):
prev2, prev1 = prev1, prev1 + prev2
return prev1
print(climb_stairs(5)) # 8
print(climb_stairs(10)) # 89
Key insight: the last move onto step n came either from step n-1 or from step n-2, and those two groups of paths cannot overlap, so ways(n) = ways(n-1) + ways(n-2). That is the whole of dynamic programming in one sentence: find a recurrence, then compute each state once instead of re-deriving it. Because the recurrence looks back only two states, you do not need an array at all. Time O(n), space O(1). The plain recursive version without memoisation is O(2^n) and dies around n = 40, which is a good thing to be able to explain.
Worked problem: minimum coins to make an amount. Given coin denominations and a target amount, return the fewest coins needed, or -1 if it is impossible. Greedily taking the largest coin is wrong for many coin sets, for example coins 1, 3 and 4 with amount 6, where greedy gives 4+1+1 = three coins but the answer is 3+3 = two.
def min_coins(coins, amount):
INF = float('inf')
best = [0] + [INF] * amount # best[v] = fewest coins for value v
for value in range(1, amount + 1):
for coin in coins:
if coin <= value and best[value - coin] + 1 < best[value]:
best[value] = best[value - coin] + 1
return -1 if best[amount] == INF else best[amount]
print(min_coins([1, 3, 4], 6)) # 2
print(min_coins([2], 3)) # -1
Key insight: to make value v, the last coin used was some coin c, and the rest of the value was made optimally, so best[v] = 1 + min(best[v - c]) over every usable coin c. Solving smaller values first means every value the inner loop needs is already final. Time O(amount * number of coins), space O(amount). This is the same shape as 0/1 knapsack, which is why it is worth understanding thoroughly before you touch harder DP.
Once these ten topics feel comfortable, the natural next step is trees, heaps and graphs, laid out in Data Structures and Algorithms for Placements: A Beginner-Friendly Roadmap.
Dry Run a Solution by Hand
Dry running means executing your own code on paper, one line at a time, writing down every variable after every step. It is the fastest way to find an off-by-one error, and in an interview it is how you prove your code is correct without running it.
Here is a full dry run of lower_bound from the searching section, on nums = [5, 7, 7, 8, 8, 10] with target = 8. Remember that hi starts at 6, which is the length of the array, not the last index.
| Step | lo | hi | mid | nums[mid] | Test: nums[mid] is less than 8? | Action |
|---|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 8 | No | hi = mid = 3 |
| 2 | 0 | 3 | 1 | 7 | Yes | lo = mid + 1 = 2 |
| 3 | 2 | 3 | 2 | 7 | Yes | lo = mid + 1 = 3 |
| 4 | 3 | 3 | - | - | - | lo equals hi, loop ends, return 3 |
Three observations from those four rows. First, the range shrinks at every step, which is the proof that the loop terminates. Second, hi is set to mid and not mid - 1, because index mid is still a candidate answer when its value is not less than the target. Third, the returned 3 is exactly the index of the first 8, which is what first_and_last needs.
Do this for every solution you are unsure about, and always include the awkward inputs: an empty array, a single element, all elements equal, the target smaller than everything, and the target larger than everything.
Best Coding Preparation Roadmap
Follow this roadmap as a fresher.
Week 1: Programming Basics
Revise syntax, loops, conditions, functions, arrays, and strings.
Goal:
Write simple programs without looking at solutions.
Finish the week able to read the whole input, print formatted output, and write a function that returns a value rather than printing it, because every problem after this assumes those.
Week 2: Arrays and Strings
Solve at least 30 to 40 problems from arrays and strings.
Goal:
Improve logic building.
Include the two-pointer and prefix-sum patterns here; they turn many O(n^2) array solutions into O(n) ones.
Week 3: Searching, Sorting, and Hashing
Practice binary search, sorting questions, and frequency-based problems.
Goal:
Improve efficiency.
Write lower_bound from memory at the end of this week. If you cannot, you have not finished the week.
Week 4: Stack, Queue, Linked List, and Recursion
Cover basic data structures and recursion.
Goal:
Become ready for common placement coding questions.
Implement a stack and a queue yourself once, then use the library versions afterwards. Doing it once is what makes the follow-up questions answerable.
Week 5 and Beyond: Mixed Practice
Start solving mixed problems under time limits.
Goal:
Prepare for real coding rounds.
Mixed practice matters because in a real round nobody tells you the topic. Half the difficulty is recognising that a question is a hashing question at all.
How to Practice Coding Properly
Do not only watch videos.
Coding improves when you write code yourself.
Use this method:
- Understand the problem
- Write the logic in simple words
- Think of sample input and output
- Write code
- Run test cases
- Fix errors
- Optimize if possible
- Read better solutions after trying
If you directly copy solutions, you may understand the answer but you will not build problem-solving skill.
Before you submit, run this edge-case checklist against your code. Most hidden-test failures come from this short list, not from a wrong algorithm:
- Empty input, and input of exactly one element
- All elements identical, and already-sorted or reverse-sorted input
- Negative numbers and zero, when the constraints allow them
- The largest allowed value, to check for integer overflow
- The target or answer not existing at all, so the function must return the "not found" value
- Duplicates, when the question says "distinct" or asks for the second largest
How to Improve Logic Building
To improve coding logic:
- Solve easy problems first
- Dry run with examples
- Use pen and paper
- Break the problem into steps
- Write pseudo-code
- Practice arrays and strings daily
- Review different solutions
- Explain your approach aloud
Logic building takes time. Do not compare your progress with others.
One habit accelerates all of this: after solving a problem, write one line naming the pattern you used, such as "sorted array plus target means two pointers" or "count things then scan in order means hash map plus second pass". After thirty problems you will have a personal pattern list, and recognising the pattern is most of the work in a timed round. The framework in How to Think Before You Code is a good structure for those notes.
Coding Round Practice Plan
If you have 60 minutes daily:
- 10 minutes concept revision
- 35 minutes coding practice
- 10 minutes debugging and improvement
- 5 minutes mistake notes
If you have 90 minutes daily:
- 15 minutes concept revision
- 50 minutes problem solving
- 15 minutes solution analysis
- 10 minutes revision of old problems
Consistency is more important than solving many questions in one day.
Once a week, replace the session with a timed set: three problems in 60 minutes, no hints, no looking anything up. That is the only way to find out whether your knowledge survives a clock.
How to Explain Code in Interviews
Sometimes interviewers ask you to explain your code.
Explain in this order:
- What the problem asks
- What approach you used
- Why you used it
- Step-by-step logic
- Time complexity
- Space complexity
- Edge cases
Even if your code is not perfect, a clear explanation can create a good impression.
Here is that structure applied to the two-sum solution above, at roughly the length you should aim for:
The problem gives an unsorted array and a target, and asks for the indices of two numbers that add to the target. My approach is one pass with a hash map. I used it because the brute-force version compares every pair, which isO(n^2)and too slow for an array of a hundred thousand elements. Walking left to right, for each number I compute the partner I would need, which is target minus the current number. If that partner is already in the map I return its stored index together with the current index. Otherwise I store the current number and its index and continue. Time isO(n)because each element is handled once and a hash lookup is average constant time. Space isO(n)for the map. For edge cases: I check the map before inserting the current number, so an element cannot pair with itself; if no pair exists I return an empty result; and duplicate values are fine because the earlier index is the one stored and returned.
Notice that it names the slower approach and says why it was rejected. That single sentence is often what separates a pass from a fail, because it shows the choice was deliberate.
Common Coding Mistakes

Freshers should avoid these mistakes:
- Learning too many languages
- Ignoring basics
- Copying code without understanding
- Not practicing daily
- Skipping dry run
- Not testing edge cases
- Ignoring time complexity
- Starting advanced DSA too early
- Giving up after errors
- Not revising old problems
Errors are part of coding. Debugging makes you better.
Four more mistakes are specific enough to be worth naming, because they produce wrong answers that look correct on the sample input:
- Integer overflow. Summing 100,000 values of up to 10^9 in a 32-bit
intwraps around silently in C, C++ and Java. Uselong. Python integers grow automatically, so this bug does not exist there. - Modifying a list while looping over it. Removing elements during iteration skips elements. Build a new list, or iterate backwards.
- Off-by-one in loop bounds. Mixing up "last index" and "length" is the single most common binary-search bug. Pick one convention and use it everywhere.
- Assuming the input is sorted. Two pointers and binary search are only valid on sorted data. If the statement does not promise it, sort first and pay the
O(n log n).
Conclusion
Coding round preparation for freshers should be practical and consistent.
Do not start with the hardest DSA problems. Start with basics, build logic, and solve problems daily.
Choose one language. Learn arrays, strings, searching, sorting, recursion, and basic data structures. Practice with test cases and review mistakes.
If you keep solving and reviewing problems, coding will stop feeling scary and become a skill you can trust during placements.
The fastest way to use this guide is to close it and reopen it as a checklist: pick one worked problem above, write it from memory, dry run it on a small input, then check your version against the one here. When you want timed practice under exam conditions, browse the practice tests by subject and topic and take one on the topic you just revised, because a topic you can pass under a clock is the only kind that counts.
FAQs
1. Which language is best for placement coding?
C++, Java, and Python are popular choices. Choose one language and become comfortable with it. Python needs the least typing but runs slowest, C++ runs fastest, and Java sits between the two; none of them will cost you an offer as long as you know its sorting function, hash map and string handling well.
2. Is DSA important for campus placements?
Yes, especially for technical roles. Start with arrays, strings, searching, sorting, and basic data structures. In practice, arrays, strings, hashing, two pointers and binary search cover the majority of fresher-level coding questions, and they are also the fastest topics to become reliable at.
3. How should freshers start coding preparation?
Start with programming basics, then practice arrays, strings, searching, sorting, and basic DSA. Concretely: spend the first week able to read input and write functions, then solve easy array and string problems until you can write them without hints, and only then move to the topics that need a data structure.
4. How many coding questions should I solve daily?
Freshers can start with 2 to 3 coding questions daily and increase gradually. Two problems you dry run, debug and can re-solve a week later are worth far more than eight problems you read the solution to.
5. Can I clear coding rounds without advanced DSA?
For some service-based companies, strong basics may be enough. For product-based companies, advanced DSA is more important. The dividing line is usually whether the input size forces you past a nested loop: if constraints stay near a few thousand, careful basic code passes, and once they reach a hundred thousand you need the O(n log n) and O(n) techniques covered above.
Tags: campus-placement, freshers-placement-guide, coding-round