Coding Round Preparation for Freshers: Complete Guide for Campus Placements

By FreePare Team · Mon Jun 22 2026 · 32 min read

Coding Round Preparation for Freshers: Complete Guide for Campus Placements

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:

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:

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.

LanguageStrength in a coding roundWhat to watch out for
PythonShortest code; dictionaries, sets, sorting and unbounded integers are built inEach 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_queueYou must watch integer overflow and out-of-range indexing yourself
JavaLarge standard library, and a strict compiler that catches mistakes earlyMore boilerplate, and slow input reading if you use Scanner on large inputs
CFast, and simple to reason aboutNo 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

Five-step coding preparation cycle: revise fundamental programming concepts, understand the essence of each concept, apply that knowledge to coding questions, identify weaknesses, then strengthen the weak areas.

Before solving placement coding questions, revise the basics.

Important programming concepts:

  1. Input and output
  2. Variables
  3. Data types
  4. Operators
  5. Conditions
  6. Loops
  7. Functions
  8. Arrays
  9. Strings
  10. Recursion basics
  11. 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 nComplexity you can affordTechnique that usually fits
up to 10O(n!)Generate every permutation with recursion
up to 20O(2^n)Try every subset, with backtracking or a bitmask
up to 500O(n^3)Triple loop, or dynamic programming over pairs of indices
up to 5,000O(n^2)Nested loop, or a two-dimensional DP table
up to 100,000O(n log n)Sorting, binary search, a heap, or sort-then-two-pointers
up to 10,000,000O(n)One pass with a hash map, prefix sums, or a sliding window
tiny n but values up to 10^9O(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:

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:

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:

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:

Also learn how to use built-in sorting functions in your chosen language.

AlgorithmAverage timeWorst timeExtra spaceStable
Bubble sortO(n^2)O(n^2)O(1)Yes
Selection sortO(n^2)O(n^2)O(1)No
Insertion sortO(n^2)O(n^2)O(1)Yes
Merge sortO(n log n)O(n log n)O(n)Yes
Quick sortO(n log n)O(n^2)O(log n) stackNo

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:

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:

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:

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:

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:

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.

Steplohimidnums[mid]Test: nums[mid] is less than 8?Action
10638Nohi = mid = 3
20317Yeslo = mid + 1 = 2
32327Yeslo = mid + 1 = 3
433---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:

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:

How to Improve Logic Building

To improve coding logic:

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:

If you have 90 minutes daily:

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:

  1. What the problem asks
  2. What approach you used
  3. Why you used it
  4. Step-by-step logic
  5. Time complexity
  6. Space complexity
  7. 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 is O(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 is O(n) because each element is handled once and a hash lookup is average constant time. Space is O(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

Tree diagram of ten coding preparation mistakes: not revising old problems, giving up after errors, starting advanced DSA too early, ignoring time complexity, ignoring edge cases, skipping dry runs, lack of daily practice, copying code without understanding, neglecting basics, and learning too many languages at once.

Freshers should avoid these mistakes:

  1. Learning too many languages
  2. Ignoring basics
  3. Copying code without understanding
  4. Not practicing daily
  5. Skipping dry run
  6. Not testing edge cases
  7. Ignoring time complexity
  8. Starting advanced DSA too early
  9. Giving up after errors
  10. 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:

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