Easy DSA Questions — Strings, Numbers, and Basic Algorithms
Continue with beginner-friendly problems involving strings, digits, comparisons, transformations, and simple mathematical reasoning.
01. Reverse a String
Reverse a given input string.
How It Works & Algorithmic Design
Strings can be processed using either Built-In Language Methods or an In-Place Two-Pointer Character Array Swap:
- Two-Pointer Approach: Convert the string into a mutable character array. Place
left = 0andright = length - 1. Swap characters while incrementingleftand decrementingrightuntil they meet in the middle. - Language Idioms: Languages like Python (
s[::-1]) or Java (StringBuilder.reverse()) offer optimized native operations.
Why This Approach Works Best
Both methods run in linear time $O(n)$. Understanding the manual pointer-swap mechanism is essential for interviews, especially when working with languages that treat strings as immutable.
"hello"'h' <-> 'o' => "oellh" -> 'e' <-> 'l' => "olleh""olleh"Answer: Reverse string implementations
function reverseString(s) {
return s.split('').reverse().join('');
}- String immutability in languages like Java, JS, and Python requires allocating a new string ($O(n)$ space).
- The underlying algorithm relies on the converging two-pointer swap pattern.
- Linear time complexity $O(n)$ is needed to process all $n$ characters.
02. Check Whether a String Is a Palindrome
Determine whether a string reads the same forward and backward.
How It Works & Algorithmic Design
This uses the Symmetric Two-Pointer Check:
- Pointer Positions: Set
left = 0andright = s.length - 1. - Symmetry Test: Compare
s[left]ands[right]:- If
s[left] !== s[right], the string is not symmetrical. Immediately returnfalse. - Otherwise, advance
left++and retreatright--toward the middle.
- If
- Completion: If pointers cross (
left >= right) without a character mismatch, returntrue.
Why This Approach Works Best
Rather than reversing the entire string (which takes extra space and time), this method checks characters in-place and stops early on the first mismatch. It processes at most $n/2$ comparisons, achieving $O(n)$ time and $O(1)$ extra space.
"racecar"'r'=='r' -> 'a'=='a' -> 'c'=='c' -> Center reached ('e')true"hello"'h' != 'o' (Mismatch found instantly)falseAnswer: Two-pointer palindrome check
function isPalindrome(s) {
let left = 0, right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}- Checks symmetry using two converging pointers.
- Early termination avoids unnecessary comparisons when a mismatch is found.
- Operates in $O(n)$ time and $O(1)$ extra space.
03. Count Vowels in a String
Count the number of vowels (a, e, i, o, u - case insensitive) in a given string.
How It Works & Algorithmic Design
This applies Set-Based Lookups within a Linear Character Pass:
- Vowel Set Lookup: Store target vowels (
aeiouAEIOU) in a Hash Set or string lookup table. Checking existence in a Hash Set takes constant time ($O(1)$). - Counting Loop: Iterate over every character
chin the string. - Evaluation: If
vowelSet.has(ch), incrementcount++. - Return: Return the accumulated count.
Why This Approach Works Best
Using a set lookup avoids writing long if-else condition chains. The linear traversal processes the string in $O(n)$ time complexity while requiring only $O(1)$ auxiliary space for the fixed 10-character vowel set.
"Algorithm"'A' (Yes, count=1) -> 'o' (Yes, count=2) -> 'i' (Yes, count=3)3Answer: Count vowels using set lookup
function countVowels(s) {
const vowels = new Set('aeiouAEIOU');
let count = 0;
for (const ch of s) {
if (vowels.has(ch)) count++;
}
return count;
}- Hash Set lookups take $O(1)$ constant time.
- Case sensitivity should be handled by including both uppercase and lowercase vowels in the set.
- Runs in $O(n)$ linear time relative to string length.
04. Find the Frequency of Each Character
Construct a frequency table mapping each character in a string to its total number of occurrences.
How It Works & Algorithmic Design
This relies on Hash Map Frequency Counting:
- Hash Map Setup: Initialize an empty key-value Hash Map
freqMap. - Character Scanning: Iterate through each character
chin the string. - Map Update:
- If
chexists as a key infreqMap, increment its associated value (freqMap[ch] + 1). - If
chis not infreqMap, insert it with an initial value of1(freqMap[ch] = 1).
- If
- Result: Return the populated frequency map.
Why This Approach Works Best
Hash Map insertions and lookups take $O(1)$ average time. Scanning the string of length $n$ takes $O(n)$ time. The space complexity is $O(k)$, where $k$ is the number of unique characters in the alphabet (at most $O(1)$ for fixed character sets like ASCII or lowercase English letters).
"banana"'b': 1 -> 'a': 1 -> 'n': 1 -> 'a': 2 -> 'n': 2 -> 'a': 3{ 'b': 1, 'a': 3, 'n': 2 }Answer: Build character frequency table
function charFrequency(s) {
const freq = {};
for (const ch of s) {
freq[ch] = (freq[ch] || 0) + 1;
}
return freq;
}- Hash Maps provide average $O(1)$ insertion and lookup times for frequency counting.
- Frequency maps serve as building blocks for solving complex string problems like Anagram Detection.
- Time complexity is $O(n)$, and space complexity is bounded by the size of the unique character set ($O(k)$).
05. Check if Two Strings are Anagrams
Given two strings s and t, return true if t is an anagram of s, and false otherwise.
How It Works & Algorithmic Design
An anagram contains the exact same characters with the exact same frequencies, reordered.
- Length Pre-Check: If
s.length !== t.length, they cannot be anagrams. Returnfalseimmediately. - Frequency Map: Traverse string
sand record character counts in a Hash Map (or a fixed 26-element array for lowercase English letters). - Frequency Verification: Traverse string
t. For each character, check if it exists in the map with a count $> 0$. Decrement its count in the map. If a character is missing or its count reaches negative, returnfalse. - Completion: If all characters in
tmatch and exhaust the counts ins, returntrue.
Why This Approach Works Best
Sorting both strings takes $O(n \log n)$ time. Using a frequency map checks anagrams in $O(n)$ time complexity and $O(k)$ space (where $k$ is the alphabet size, $O(1)$ for fixed character sets).
s = "listen", t = "silent"s map: {l:1, i:1, s:1, t:1, e:1, n:1} -> t matches every key count exactly.trueAnswer: Anagram verification using frequency map
function isAnagram(s, t) {
if (s.length !== t.length) return false;
const count = {};
for (const ch of s) count[ch] = (count[ch] || 0) + 1;
for (const ch of t) {
if (!count[ch]) return false;
count[ch]--;
}
return true;
}- Always check string lengths first for instant early termination.
- Frequency map eliminates the need for $O(n \log n)$ sorting operations.
- For lowercase ASCII strings, a fixed 26-element array can replace a dynamic hash table for better performance.
06. First Non-Repeating Character in a String
Find the first non-repeating character in a string and return its index. If it does not exist, return -1.
How It Works & Algorithmic Design
This requires a Two-Pass Algorithm using a Frequency Map:
- First Pass (Counting): Iterate through the string from index $0$ to $n-1$ and build a frequency dictionary mapping each character to its occurrence count.
- Second Pass (Position Lookup): Iterate through the string a second time from left to right. Look up each character's count in the frequency map. The first character with a count equal to
1is our answer; return its current loop index. - Fallback: If all characters have counts $> 1$, return
-1.
Why This Approach Works Best
Checking every character against the rest of the string using nested loops takes $O(n^2)$ time. The two-pass Hash Map pattern reduces the time complexity to optimal linear $O(n)$ while taking $O(1)$ space for fixed alphabets.
"leetcode"{ l:1, e:3, t:1, c:1, o:1, d:1 }Index 0 = 'l', count is 1 -> Found!0Answer: First unique character search
function firstUniqChar(s) {
const count = {};
for (const ch of s) count[ch] = (count[ch] || 0) + 1;
for (let i = 0; i < s.length; i++) {
if (count[s[i]] === 1) return i;
}
return -1;
}- Two-pass linear scans preserve original order while accumulating global frequencies.
- Time complexity is $O(2n) = O(n)$.
- Space complexity remains $O(1)$ when bounded by character set limits (e.g., ASCII/English letters).
07. Two Sum
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
How It Works & Algorithmic Design
This is solved using the Hash Map Complement Pattern:
- Mathematical Relation: For any element $x = \text{nums}[i]$, its required complementary pair is $y = \text{target} - x$.
- Hash Map Storage: Maintain a Hash Map where key =
array element valueand value =array index. - Single Pass: Loop through
numsusing index $i$:- Compute
complement = target - nums[i]. - Check if
complementexists in the Hash Map. - If it exists, return
[hashMap.get(complement), i]. - If not, insert
hashMap.put(nums[i], i)and continue.
- Compute
Why This Approach Works Best
Brute force nested loops take $O(n^2)$ time. Sorting followed by two pointers takes $O(n \log n)$ time and loses original indices. Hash Map lookup gives optimal $O(n)$ runtime and $O(n)$ space complexity.
nums = [2, 7, 11, 15], target = 9Complement = 9 - 2 = 7. Map is empty -> Store {2: 0}Complement = 9 - 7 = 2. Found 2 in map at index 0![0, 1]Answer: Two Sum using complement Hash Map
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
}
map.set(nums[i], i);
}
return [];
}- Store visited elements in a map to turn $O(n^2)$ pair searches into $O(1)$ lookups.
- Calculating
complement = target - currentavoids nested loops. - Optimal single-pass runtime of $O(n)$ time and $O(n)$ space.
08. Find the Missing Number in an Array
Given an array nums containing $n$ distinct numbers in the range $[0, n]$, return the only number in the range that is missing from the array.
How It Works & Algorithmic Design
This problem can be solved mathematically using Gauss's Summation Formula or bit manipulation with XOR:
- Mathematical Sum Method:
- The sum of integers from $0$ to $n$ is calculated using the formula $S_n = \frac{n \cdot (n + 1)}{2}$.
- Calculate the actual sum of elements in
nums. - The missing number is simply $S_n - \text{actualSum}$.
- Bit Manipulation (XOR Method):
- XORing a number with itself yields zero ($a \oplus a = 0$).
- XOR all indices from $0$ to $n$ together with all values in
nums. Duplicate numbers cancel out, leaving only the missing number.
Why This Approach Works Best
Both methods run in $O(n)$ time complexity and $O(1)$ space without requiring array sorting ($O(n \log n)$) or extra hash sets ($O(n)$ space).
[3, 0, 1] (n = 3)(3 * 4) / 2 = 63 + 0 + 1 = 46 - 4 = 2Answer: Find missing number using Gauss's Sum
function missingNumber(nums) {
const n = nums.length;
const expectedSum = (n * (n + 1)) / 2;
const actualSum = nums.reduce((acc, val) => acc + val, 0);
return expectedSum - actualSum;
}- Gauss's summation formula $n(n+1)/2$ finds expected totals in $O(1)$ arithmetic steps.
- XOR method prevents potential integer overflow in languages with fixed integer limits.
- Both strategies achieve optimal $O(n)$ time and $O(1)$ space.
09. Move Zeroes to the End of Array
Given an integer array nums, move all 0s to the end of it while maintaining the relative order of the non-zero elements in-place.
How It Works & Algorithmic Design
This uses a Two-Pointer Write Boundary approach:
- Pointer Definition: Maintain a
insertPospointer initialized to index0. - Pass 1 (Shift Non-Zeroes): Iterate through the array with pointer $i$. Whenever
nums[i] !== 0, placenums[insertPos] = nums[i]and incrementinsertPos++. - Pass 2 (Fill Zeroes): Once all non-zero elements are moved to the front, loop from
insertPosto the end of the array and setnums[i] = 0.
Why This Approach Works Best
This solves the problem in-place without creating a duplicate array ($O(1)$ space). It executes in linear $O(n)$ time complexity.
[0, 1, 0, 3, 12][1, 3, 12, 3, 12] (insertPos = 3)[1, 3, 12, 0, 0][1, 3, 12, 0, 0]Answer: Move zeroes in-place
function moveZeroes(nums) {
let insertPos = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
nums[insertPos++] = nums[i];
}
}
while (insertPos < nums.length) {
nums[insertPos++] = 0;
}
return nums;
}- Maintains relative order of non-zero elements by sequentially writing to
insertPos. - Modifies original array reference directly ($O(1)$ space).
- Linear time complexity $O(n)$ with two sequential simple loops.
10. Find Intersection of Two Arrays
Given two integer arrays nums1 and nums2, return an array of their unique intersection elements.
How It Works & Algorithmic Design
This problem uses Hash Set Intersection Filtering:
- Set Population: Convert
nums1into a Hash Setset1. Hash Sets store only unique elements and provide $O(1)$ membership checks. - Filtering: Initialize a second empty result set
resultSet. - Intersection Pass: Iterate through elements in
nums2. For every number $x$, ifset1.has(x), insert $x$ intoresultSet. - Return: Convert
resultSetback into an array list.
Why This Approach Works Best
Checking every element of nums2 against nums1 using nested loops takes $O(n \times m)$ time. Using a Hash Set reduces total execution time to linear $O(n + m)$ where $n$ and $m$ are the lengths of the two arrays.
nums1 = [1, 2, 2, 1], nums2 = [2, 2]{ 1, 2 }2 in Set 1? Yes -> Add 2 to result.[2]Answer: Array intersection using Hash Sets
function intersection(nums1, nums2) {
const set1 = new Set(nums1);
const result = new Set();
for (const num of nums2) {
if (set1.has(num)) result.add(num);
}
return Array.from(result);
}- Hash Sets eliminate duplicate results automatically.
- Linear time performance $O(n + m)$ using $O(1)$ set membership checks.
- Space complexity is proportional to unique elements stored in memory ($O(n)$).