Interview Questions - Easy

Easy-level algorithm interview questions with detailed approach explanations and solutions.

How to Approach Interview Problems

  1. Clarify requirements: Ask about edge cases, constraints, input/output format
  2. Think out loud: Explain your thought process
  3. Start with brute force: Then optimize
  4. Consider trade-offs: Time vs. space complexity
  5. Test with examples: Walk through your solution
  6. Handle edge cases: Empty input, single element, duplicates

Problem 1: Two Sum

Problem: Given an array of integers and a target, return indices of two numbers that add up to target.

Example:

1Input: nums = [2,7,11,15], target = 9
2Output: [0,1]
3Explanation: nums[0] + nums[1] = 2 + 7 = 9

Approach

Brute Force ($O(n^2)$): Check all pairs

1for i := 0; i < len(nums); i++ {
2    for j := i+1; j < len(nums); j++ {
3        if nums[i] + nums[j] == target {
4            return []int{i, j}
5        }
6    }
7}

Optimized ($O(n)$): Use hash map to store complements

Key Insight: For each number, check if its complement (target - num) exists in map.

Solution

 1func twoSum(nums []int, target int) []int {
 2    seen := make(map[int]int) // value -> index
 3    
 4    for i, num := range nums {
 5        complement := target - num
 6        
 7        if j, exists := seen[complement]; exists {
 8            return []int{j, i}
 9        }
10        
11        seen[num] = i
12    }
13    
14    return nil
15}

Time: $O(n)$, Space: $O(n)$

Why this works: We build the map as we go, so when we find a complement, we know its index.


Problem 2: Valid Parentheses

Problem: Determine if string of brackets is valid (every opening has matching closing in correct order).

Example:

1Input: s = "()[]{}"
2Output: true
3
4Input: s = "([)]"
5Output: false

Approach

Key Insight: Use stack - opening brackets push, closing brackets must match top of stack.

Steps:

  1. For each character:
    • If opening bracket: push to stack
    • If closing bracket: check if matches stack top, pop if yes
  2. Stack should be empty at end

Solution

 1func isValid(s string) bool {
 2    stack := []rune{}
 3    pairs := map[rune]rune{')': '(', '}': '{', ']': '['}
 4    
 5    for _, char := range s {
 6        // Opening bracket
 7        if char == '(' || char == '{' || char == '[' {
 8            stack = append(stack, char)
 9        } else {
10            // Closing bracket
11            if len(stack) == 0 || stack[len(stack)-1] != pairs[char] {
12                return false
13            }
14            stack = stack[:len(stack)-1] // Pop
15        }
16    }
17    
18    return len(stack) == 0
19}

Time: $O(n)$, Space: $O(n)$

Edge cases: Empty string (valid), unmatched opening, unmatched closing


Problem 3: Merge Two Sorted Lists

Problem: Merge two sorted linked lists into one sorted list.

Example:

1Input: list1 = [1,2,4], list2 = [1,3,4]
2Output: [1,1,2,3,4,4]

Approach

Key Insight: Use two pointers, always pick smaller value.

Technique: Dummy node to simplify edge cases.

Solution

 1func mergeTwoLists(l1, l2 *ListNode) *ListNode {
 2    dummy := &ListNode{}
 3    curr := dummy
 4    
 5    for l1 != nil && l2 != nil {
 6        if l1.Val <= l2.Val {
 7            curr.Next = l1
 8            l1 = l1.Next
 9        } else {
10            curr.Next = l2
11            l2 = l2.Next
12        }
13        curr = curr.Next
14    }
15    
16    // Attach remaining nodes
17    if l1 != nil {
18        curr.Next = l1
19    } else {
20        curr.Next = l2
21    }
22    
23    return dummy.Next
24}

Time: $O(m + n)$, Space: $O(1)$

Why dummy node: Avoids special case for first node.


Problem 4: Best Time to Buy and Sell Stock

Problem: Find maximum profit from one buy and one sell (buy before sell).

Example:

1Input: prices = [7,1,5,3,6,4]
2Output: 5
3Explanation: Buy at 1, sell at 6, profit = 5

Approach

Key Insight: Track minimum price seen so far, calculate profit at each step.

Pattern: Single pass, keep running minimum.

Solution

 1func maxProfit(prices []int) int {
 2    if len(prices) == 0 {
 3        return 0
 4    }
 5    
 6    minPrice := prices[0]
 7    maxProfit := 0
 8    
 9    for _, price := range prices {
10        if price < minPrice {
11            minPrice = price
12        } else if price - minPrice > maxProfit {
13            maxProfit = price - minPrice
14        }
15    }
16    
17    return maxProfit
18}

Time: $O(n)$, Space: $O(1)$

Why this works: At each point, we know the best buy price before this point.


Problem 5: Valid Palindrome

Problem: Check if string is palindrome (ignoring non-alphanumeric, case-insensitive).

Example:

1Input: s = "A man, a plan, a canal: Panama"
2Output: true

Approach

Key Insight: Two pointers from both ends, skip non-alphanumeric.

Solution

 1import "unicode"
 2
 3func isPalindrome(s string) bool {
 4    left, right := 0, len(s)-1
 5    
 6    for left < right {
 7        // Skip non-alphanumeric from left
 8        for left < right && !isAlphaNumeric(rune(s[left])) {
 9            left++
10        }
11        
12        // Skip non-alphanumeric from right
13        for left < right && !isAlphaNumeric(rune(s[right])) {
14            right--
15        }
16        
17        // Compare (case-insensitive)
18        if unicode.ToLower(rune(s[left])) != unicode.ToLower(rune(s[right])) {
19            return false
20        }
21        
22        left++
23        right--
24    }
25    
26    return true
27}
28
29func isAlphaNumeric(ch rune) bool {
30    return unicode.IsLetter(ch) || unicode.IsDigit(ch)
31}

Time: $O(n)$, Space: $O(1)$


Problem 6: Reverse Linked List

Problem: Reverse a singly linked list.

Example:

1Input: 1 -> 2 -> 3 -> 4 -> 5
2Output: 5 -> 4 -> 3 -> 2 -> 1

Approach

Key Insight: Three pointers - prev, curr, next.

Steps:

  1. Save next node
  2. Reverse current node's pointer
  3. Move all pointers forward

Solution

 1func reverseList(head *ListNode) *ListNode {
 2    var prev *ListNode
 3    curr := head
 4    
 5    for curr != nil {
 6        next := curr.Next    // Save next
 7        curr.Next = prev     // Reverse pointer
 8        prev = curr          // Move prev forward
 9        curr = next          // Move curr forward
10    }
11    
12    return prev // New head
13}

Time: $O(n)$, Space: $O(1)$

Common mistake: Forgetting to save next before changing curr.Next.


Problem 7: Contains Duplicate

Problem: Check if array contains any duplicate values.

Example:

1Input: nums = [1,2,3,1]
2Output: true

Approach

Key Insight: Use hash set to track seen values.

Solution

 1func containsDuplicate(nums []int) bool {
 2    seen := make(map[int]bool)
 3    
 4    for _, num := range nums {
 5        if seen[num] {
 6            return true
 7        }
 8        seen[num] = true
 9    }
10    
11    return false
12}

Time: $O(n)$, Space: $O(n)$

Alternative: Sort first ($O(n \log n)$ time, $O(1)$ space), then check adjacent elements.


Problem 8: Maximum Subarray (Kadane's Algorithm)

Problem: Find contiguous subarray with largest sum.

Example:

1Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
2Output: 6
3Explanation: [4,-1,2,1] has sum 6

Approach

Key Insight: At each position, decide: extend current subarray or start new one.

Kadane's Algorithm: Keep running sum, reset if it goes negative.

Solution

 1func maxSubArray(nums []int) int {
 2    maxSum := nums[0]
 3    currentSum := nums[0]
 4    
 5    for i := 1; i < len(nums); i++ {
 6        // Either extend current or start new
 7        currentSum = max(nums[i], currentSum + nums[i])
 8        maxSum = max(maxSum, currentSum)
 9    }
10    
11    return maxSum
12}
13
14func max(a, b int) int {
15    if a > b {
16        return a
17    }
18    return b
19}

Time: $O(n)$, Space: $O(1)$

Why this works: If current sum becomes negative, it can't help future sums.


Problem 9: Climbing Stairs

Problem: How many distinct ways to climb n stairs (1 or 2 steps at a time)?

Example:

1Input: n = 3
2Output: 3
3Explanation: 1+1+1, 1+2, 2+1

Approach

Key Insight: To reach step n, you came from step n-1 or n-2.

Recurrence: $f(n) = f(n-1) + f(n-2)$ (Fibonacci!)

Solution

 1func climbStairs(n int) int {
 2    if n <= 2 {
 3        return n
 4    }
 5    
 6    prev2, prev1 := 1, 2
 7    
 8    for i := 3; i <= n; i++ {
 9        curr := prev1 + prev2
10        prev2 = prev1
11        prev1 = curr
12    }
13    
14    return prev1
15}

Time: $O(n)$, Space: $O(1)$

Common mistake: Using recursion without memoization ($O(2^n)$ time).


Problem 10: Binary Tree Maximum Depth

Problem: Find maximum depth of binary tree.

Example:

1Input: root = [3,9,20,null,null,15,7]
2Output: 3

Approach

Key Insight: Depth = 1 + max(left depth, right depth).

Pattern: Recursive tree traversal.

Solution

 1func maxDepth(root *TreeNode) int {
 2    if root == nil {
 3        return 0
 4    }
 5    
 6    leftDepth := maxDepth(root.Left)
 7    rightDepth := maxDepth(root.Right)
 8    
 9    return 1 + max(leftDepth, rightDepth)
10}

Time: $O(n)$, Space: $O(h)$ where h is height (recursion stack)

Iterative version: Use level-order traversal (BFS) and count levels.


Common Patterns in Easy Problems

  1. Hash Map/Set: Two sum, contains duplicate
  2. Two Pointers: Valid palindrome, merge lists
  3. Stack: Valid parentheses
  4. Sliding Window: Maximum subarray
  5. Dynamic Programming: Climbing stairs
  6. Tree Recursion: Maximum depth
  7. Linked List: Reverse, merge

Interview Tips

Do:

  • Ask clarifying questions
  • Start with brute force
  • Explain your thinking
  • Test with examples
  • Consider edge cases

Don't:

  • Jump to code immediately
  • Assume constraints
  • Stay silent
  • Ignore edge cases
  • Give up if stuck

Time Management

  • 5 min: Understand problem, ask questions
  • 10 min: Discuss approach, consider alternatives
  • 20 min: Code solution
  • 5 min: Test and debug

Total: ~40 minutes per problem

Related Snippets