HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
20 Topics
Chapter•180 Minutes

Easy DSA Questions — Arrays and Basic Problem Solving

Start with fundamental DSA problems involving arrays, loops, conditions, counting, searching, and simple transformations. These questions build the problem-solving patterns required for more advanced algorithms.

01. Find the Largest Element in an Array

Difficulty:EasyTime:O(n)Space:O(1)

Given an array of numbers, find the largest element.

How It Works & Algorithmic Design

To find the maximum element without sorting, we use a Single-Pass Linear Scan pattern.

  1. Initialization: Assume the first element (arr[0]) is the largest seen so far. Storing an element from the array ensures that negative numbers or zero values do not break the comparison logic (unlike initializing to 0).
  2. Iteration: Loop through each remaining element from left to right.
  3. Comparison & Update: For every element, check if it is strictly greater than our current maximum variable (max). If it is, overwrite max with the new value.
  4. Edge Case Handling: If the input array is empty (arr.length === 0), there is no valid maximum; return null or an equivalent empty-state value.

Why This Approach Works Best

Alternative approaches like sorting the array first take $O(n \log n)$ time complexity. This iterative scan inspects every element exactly once, giving an optimal linear $O(n)$ runtime while using $O(1)$ constant auxiliary memory.

Specifications & Requirements
  • Empty Input:Returns null immediately
  • Comparison Count:Exactly n - 1 comparisons for n elements
Example Walkthrough 1
Input: [4, 8, 2, 10, 3]
Unsorted array with positive integers.
Step-by-Step State: Init: max=4 | Check 8 -> max=8 | Check 2 -> max=8 | Check 10 -> max=10 | Check 3 -> max=10
Tracks the running maximum state across iterations.
Output: 10
10 is the largest value in the array.
Example Walkthrough 2 (Negative Numbers)
Input: [-15, -3, -45, -8]
Array containing only negative values.
Output: -3
Initializing with arr[0] (-15) correctly allows -3 to be chosen as max.

Answer: Find maximum element

function largest(arr) {
  if (!arr || arr.length === 0) return null;
  let max = arr[0];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] > max) {
      max = arr[i];
    }
  }
  return max;
}
💡 Iterate through the array sequentially and maintain a tracking variable initialized to the first element.
Key Takeaways
  • Always initialize the tracking variable with an actual element from the array (like arr[0]), not 0 or arbitrary values.
  • A single pass linear scan inspects every element once, providing $O(n)$ time complexity.
  • No dynamic memory allocation is required, giving $O(1)$ constant space complexity.

02. Find the Smallest Element in an Array

Difficulty:EasyTime:O(n)Space:O(1)

Given an array of numbers, locate and return the smallest element.

How It Works & Algorithmic Design

Finding the minimum value uses the exact inverse logic of finding the maximum value:

  1. Initialization: Set min = arr[0]. This establishes a benchmark value present inside the input dataset.
  2. Linear Pass: Loop through the array from index 1 to arr.length - 1.
  3. Conditional Update: Compare each element with min. If the current value is strictly lower (arr[i] < min), update min = arr[i].
  4. Completion: Once all $n$ elements are evaluated, min holds the smallest value.

Why This Approach Works Best

This algorithm requires no sorting or external memory data structures. It evaluates the array in $O(n)$ time while operating in-place ($O(1)$ space).

Example Walkthrough
Input: [12, 5, 25, 1, 9]
Unsorted numbers including positive integers.
Execution Trace: Init: min=12 | Check 5 -> min=5 | Check 25 -> min=5 | Check 1 -> min=1 | Check 9 -> min=1
Updates 'min' whenever a strictly smaller value is found.
Output: 1
1 is the smallest element in the input list.

Answer: Find minimum element

function smallest(arr) {
  if (!arr || arr.length === 0) return null;
  let min = arr[0];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < min) min = arr[i];
  }
  return min;
}
💡 Traverse the array sequentially and retain the lowest value encountered.
Key Takeaways
  • Reversing the comparison operator (< instead of >) turns maximum-finding into minimum-finding.
  • Linear traversal inspects all items in $O(n)$ time.
  • Always account for empty inputs before referencing index 0 to prevent runtime exceptions.

03. Calculate the Sum of Array Elements

Difficulty:EasyPattern:AccumulatorTime:O(n)Space:O(1)

Compute the sum of all numerical values in a given array.

How It Works & Algorithmic Design

This utilizes the Accumulator Pattern:

  1. State Initialization: Declare an accumulator variable total = 0. Zero acts as the additive identity element ($x + 0 = x$).
  2. Iteration: Walk through the elements one by one.
  3. Accumulation: Add each element's value to total using the addition assignment operator (total += element).
  4. Return: Once the loop completes, return total.

Why This Approach Works Best

It performs a single pass over the array with constant extra space ($O(1)$ memory). For large arrays, integer overflow should be considered depending on the language (e.g., using 64-bit integers like long in Java when working with large numbers).

Example Walkthrough
Input: [1, 2, 3, 4, 5]
Sequential numbers from 1 to 5.
Step-by-Step Calculation: 0 + 1 = 1 | 1 + 2 = 3 | 3 + 3 = 6 | 6 + 4 = 10 | 10 + 5 = 15
Accumulates values step by step.
Output: 15
Total sum of all elements.

Answer: Calculate total sum

function sum(arr) {
  let total = 0;
  for (const value of arr) {
    total += value;
  }
  return total;
}
💡 Iterate through the array and accumulate values into a tracking variable.
Key Takeaways
  • Initialize accumulators with the identity element (0 for sum, 1 for product).
  • Time complexity is $O(n)$ as every element is visited once.
  • In languages like Java or C++, use long to prevent overflow when dealing with large numbers.

04. Count Even Numbers in an Array

Difficulty:EasyPattern:CountingTime:O(n)Space:O(1)

Determine the count of even numbers contained in an array.

How It Works & Algorithmic Design

This combines Conditional Filtering with Counter Incrementation:

  1. Divisibility Condition: A integer $x$ is defined as even if $x \pmod 2 = 0$.
  2. Counter Setup: Initialize count = 0.
  3. Evaluation Loop: Traverse through each number in the array. Apply the modulo operator (value % 2). If the remainder equals 0, increment count by 1.
  4. Negative Numbers: The condition value % 2 === 0 holds true for negative even integers as well (e.g., -4 % 2 === 0), making this approach robust across integer domains.

Why This Approach Works Best

It processes the input in $O(n)$ runtime and $O(1)$ extra space without creating intermediate filtered arrays in memory.

Example Walkthrough
Input: [1, 4, 7, 8, 10, 13]
Mixed odd and even integers.
Evaluation Trace: 1 (Odd) -> 4 (Even, count=1) -> 7 (Odd) -> 8 (Even, count=2) -> 10 (Even, count=3) -> 13 (Odd)
Only increments counter when number % 2 == 0.
Output: 3
There are three even numbers (4, 8, 10).

Answer: Count even values

function countEven(arr) {
  let count = 0;
  for (const value of arr) {
    if (value % 2 === 0) count++;
  }
  return count;
}
💡 Filter numbers using modulo 2 and increment a counter variable.
Key Takeaways
  • Modulo operator (%) accurately checks for divisibility.
  • Avoid instantiating auxiliary filtered arrays to keep space complexity at $O(1)$.
  • Modulo operation works consistently for both positive and negative even integers.

05. Reverse an Array In-Place

Difficulty:EasyPattern:Two PointersTime:O(n)Space:O(1)

Reverse the order of elements in an array without using extra memory allocations.

How It Works & Algorithmic Design

This demonstrates the Two Pointers Technique:

  1. Pointer Setup: Position a left pointer at index 0 and a right pointer at index arr.length - 1.
  2. Symmetric Swapping: While left < right:
    • Swap the elements at arr[left] and arr[right].
    • Increment left (left++).
    • Decrement right (right--).
  3. Termination: When left >= right, the pointers meet or cross in the middle, confirming all elements have been mirrored.

Why This Approach Works Best

Creating a duplicate reversed array requires $O(n)$ auxiliary space. The two-pointer in-place swap modifies the original array directly, achieving $O(n/2) = O(n)$ time complexity and true $O(1)$ space complexity.

Example Walkthrough
Input: [10, 20, 30, 40, 50]
Original array.
Step 1: Swap index 0 (10) & 4 (50) -> [50, 20, 30, 40, 10]
left=1, right=3.
Step 2: Swap index 1 (20) & 3 (40) -> [50, 40, 30, 20, 10]
left=2, right=2. Loop terminates.
Output: [50, 40, 30, 20, 10]
Array reversed in place.

Answer: Reverse in-place using two pointers

function reverse(arr) {
  let left = 0, right = arr.length - 1;
  while (left < right) {
    let temp = arr[left];
    arr[left] = arr[right];
    arr[right] = temp;
    left++;
    right--;
  }
  return arr;
}
💡 Swap symmetric elements from opposite ends using converging pointers.
Key Takeaways
  • Two-pointer technique avoids extra memory overhead.
  • Only $n/2$ iterations are needed to reverse an array of size $n$.
  • In-place algorithm design modifies the input reference directly.

06. Check Whether an Array Is Sorted

Difficulty:EasyPattern:Linear ScanTime:O(n)Space:O(1)

Verify if an array's elements are ordered in non-decreasing sequence ($arr[i] \le arr[i+1]$).

How It Works & Algorithmic Design

This uses an Adjacent Pair Comparison strategy with Early Termination:

  1. Invariant: An array is sorted if every consecutive pair satisfied $arr[i - 1] \le arr[i]$.
  2. Iteration: Start looping from index 1 up to arr.length - 1.
  3. Violation Check: Compare arr[i] against its preceding neighbor arr[i - 1]. If arr[i] < arr[i - 1], a sorting violation has occurred. Instantly return false.
  4. Success Return: If the loop finishes without finding any out-of-order pair, return true.

Why This Approach Works Best

It avoids checking $O(n^2)$ pairs by relying on transitivity: if $a \le b$ and $b \le c$, then $a \le c$. Comparing adjacent items in a single pass ($O(n)$ time, $O(1)$ space) is sufficient.

Example Walkthrough 1 (Sorted)
Input: [2, 5, 8, 8, 12]
Non-decreasing array containing a duplicate.
Comparisons: 5 >= 2 (OK) -> 8 >= 5 (OK) -> 8 >= 8 (OK) -> 12 >= 8 (OK)
No violation found.
Output: true
Array is sorted.
Example Walkthrough 2 (Unsorted)
Input: [1, 3, 2, 4]
Unsorted array.
Comparisons: 3 >= 1 (OK) -> 2 < 3 (VIOLATION!)
Returns false immediately at index 2.
Output: false
Array is not sorted.

Answer: Check array order

function isSorted(arr) {
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < arr[i - 1]) return false;
  }
  return true;
}
💡 Scan adjacent elements and return false upon detecting any out-of-order pair.
Key Takeaways
  • Transitivity property allows order verification through adjacent comparisons alone.
  • Early return optimizes average runtime when input arrays are unsorted.
  • Single-pass linear scan yields optimal $O(n)$ time performance.

08. Count Occurrences of a Target

Difficulty:EasyPattern:Counting PassTime:O(n)Space:O(1)

Find how many times a given target value appears within an array.

How It Works & Algorithmic Design

Unlike Linear Search (which returns early upon finding the first match), counting occurrences requires inspecting every element in the array:

  1. Counter Initialization: Set count = 0.
  2. Exhaustive Pass: Iterate through every position from index $0$ to $n-1$.
  3. Match Accumulation: Increment count by $1$ every time arr[i] === target.
  4. Final Result: Return the accumulated total after the loop finishes.

Why This Approach Works Best

Because target matches may occur multiple times across the array, early termination cannot be used. Inspecting every element gives a time complexity of $O(n)$ and $O(1)$ space.

Example Walkthrough
Input: [5, 2, 5, 8, 5, 1], Target: 5
Target number 5 appears multiple times.
Trace: 5 (count=1) -> 2 -> 5 (count=2) -> 8 -> 5 (count=3) -> 1
Evaluates all elements sequentially.
Output: 3
Number 5 appears 3 times.

Answer: Count target frequencies

function countOccurrences(arr, target) {
  let count = 0;
  for (const value of arr) {
    if (value === target) count++;
  }
  return count;
}
💡 Scan the entire collection and increment a counter whenever the value matches.
Key Takeaways
  • Requires an exhaustive scan over the entire collection.
  • Runs in $O(n)$ time while keeping memory overhead at $O(1)$.
  • Can be extended to count frequencies across multiple targets using a Hash Map.

09. Find the Second Largest Element

Difficulty:EasyPattern:Single Pass TrackingTime:O(n)Space:O(1)

Find the second largest distinct element in an array without sorting.

How It Works & Algorithmic Design

This uses a Dual-Variable Tracking approach in a single pass:

  1. Variables: Maintain first (largest) and second (second largest), initialized to negative infinity (-Infinity).
  2. Iteration & Rules: Loop through each number $x$:
    • Case 1 ($x > first$): A new maximum is found. Demote the old first to second (second = first), then update first = x.
    • Case 2 ($x > second$ and $x < first$): $x$ is not the largest, but is strictly greater than second. Update second = x.
    • Case 3 ($x \le second$): Ignore $x$.
  3. Distinct Constraint: The check x < first in Case 2 ensures duplicates of the maximum value do not improperly overwrite second.
  4. Result: If second remains -Infinity, no distinct second largest element exists; return null.

Why This Approach Works Best

Sorting takes $O(n \log n)$ time. Scanning twice (once for max, once for second max) takes $O(2n)$ time. This dual-variable method finds the answer in a single $O(n)$ pass using $O(1)$ memory.

Example Walkthrough
Input: [12, 35, 1, 10, 34, 1]
Unsorted array with distinct elements.
Execution Trace: Check 12 (first=12) -> Check 35 (second=12, first=35) -> Check 1 -> Check 10 -> Check 34 (second=34) -> Check 1
Tracks both top distinct values simultaneously.
Output: 34
34 is the second largest distinct element.

Answer: Find second largest distinct element

function secondLargest(arr) {
  let first = -Infinity;
  let second = -Infinity;
  for (const x of arr) {
    if (x > first) {
      second = first;
      first = x;
    } else if (x > second && x < first) {
      second = x;
    }
  }
  return second === -Infinity ? null : second;
}
💡 Track the top two distinct maximum values simultaneously in one traversal.
Key Takeaways
  • Demote first to second whenever a new maximum is found.
  • Ensure duplicates of the max value do not overwrite second by checking x < first.
  • Solves the problem in a single pass ($O(n)$ time) without extra space ($O(1)$ memory).

10. Remove Duplicates from a Sorted Array

Difficulty:EasyPattern:Read/Write Two PointersTime:O(n)Space:O(1)

Given a sorted array, remove duplicate values in place such that each unique element appears only once. Return the number of unique elements.

How It Works & Algorithmic Design

This problem uses a Read/Write Two-Pointer Pattern:

  1. Precondition: Because the array is sorted, all duplicate elements are contiguous.
  2. Pointer Definitions:
    • write: Index where the next unique value should be placed (starts at 1).
    • read: Index used to scan through the array (starts at 1).
  3. Scanning Logic: Compare arr[read] with the element right before it (arr[read - 1]):
    • If arr[read] !== arr[read - 1], a new unique element has been found.
    • Write this unique element to arr[write], then increment write (write++).
  4. Result: The value of write equals the total count of unique elements, and the sub-array arr[0 ... write - 1] contains those unique values in order.

Why This Approach Works Best

It modifies the array in-place without creating a new set or duplicate array, running in $O(n)$ time and $O(1)$ extra space.

Example Walkthrough
Input Array: [0, 0, 1, 1, 1, 2, 2, 3]
Sorted array with duplicate groups.
Write Operations: arr[1]=1 -> arr[2]=2 -> arr[3]=3
Overwrites duplicate positions with newly discovered unique values.
Modified Sub-Array: [0, 1, 2, 3, ...]
Unique elements shifted to the front.
Return Count: 4
4 unique elements found.

Answer: In-place deduplication

function removeDuplicates(arr) {
  if (!arr || arr.length === 0) return 0;
  let write = 1;
  for (let read = 1; read < arr.length; read++) {
    if (arr[read] !== arr[read - 1]) {
      arr[write] = arr[read];
      write++;
    }
  }
  return write;
}
💡 Use read and write pointers to overwrite duplicate entries in a single pass.
Key Takeaways
  • Contiguous grouping in sorted arrays makes duplicate detection straightforward.
  • The read pointer scans elements while the write pointer places unique values.
  • Achieves $O(n)$ time complexity and $O(1)$ space complexity by modifying the array in-place.