Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Return the index of the first character that appears exactly once, or -1 if none. Two passes: pass one counts every character's frequency; pass two scans left to right and returns the first index whose count is 1. O(n) time, O(1) space for a fixed alphabet. The order check must scan the original string — iterating the map loses positional order.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you split the problem into a frequency tally plus an ordered scan, and whether you preserve positional order by re-scanning the string rather than the map.
Where candidates slip
Trying to answer in a single pass, or iterating the hash map for the 'first' index (map order isn't input order).
How to stand out
State the fixed-array O(1)-space claim, handle the all-repeats → -1 case explicitly, and mention the streaming follow-up that needs a queue.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewPass 1: count frequenciescount[c] == 1?YesNoExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Brute Force — Recount Per Character
1Hint 1Explore hintHide hint
For each character, how would you check whether it appears anywhere else?
2Hint 2Explore hintHide hint
For index i, scan the whole string counting occurrences of s[i]; the first with count 1 is the answer.
3Hint 3Explore hintHide hint
That's O(n²) — replace the per-character recount with a single precomputed frequency table.
A · Core idea
For each index i, count how many times s[i] occurs in the whole string; return i the first time that count is 1. If no index qualifies, return -1.
Why mention it. It's the direct reading of 'non-repeating', and it's a clean baseline. But recounting the string for every character is quadratic — with n up to 10⁵ that's 10¹⁰ operations. Precomputing all frequencies once removes the repeated scans.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "aab"i=0 'a'
count('a') = 2
not unique2C · Code with teaching comments
Reference implementation previewpublic int firstUniqChar(String s) { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); int occurrences = 0; for (int j = 0; j < s.length(); j++) {Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
The wasted work is recomputing the same frequencies n times. Tallying once into a table and then querying it is the standard count-then-use upgrade.
ComplexityO(n²) time, O(1) space — For each of n positions we scan up to n characters → O(n²). No extra structures beyond counters.
E · Edge cases and pitfalls2Calling s.count / inner scan repeatedly — O(n²) and slow on large inputs.Explore edge casesHide edge cases
Pitfalls
- Calling s.count / inner scan repeatedly — O(n²) and slow on large inputs.
- Returning the character instead of the index.
Approach 2 of 2 · Optimal
Frequency Count — Two Passes (Optimal)
1Hint 1Explore hintHide hint
What if you knew every character's total count before looking for the answer?
2Hint 2Explore hintHide hint
Pass 1: build a frequency table. Pass 2: scan the string and return the first index whose char has count 1.
3Hint 3Explore hintHide hint
For lowercase English the table is an int[26], so space is O(1).
A · Core idea
First pass: tally each character's frequency into a table (an int[26] for lowercase English, or a hash map for Unicode). Second pass: walk the string left to right and return the index of the first character whose tally is exactly 1. If none qualifies, return -1.
Why two passes and why re-scan the string. Frequencies are only fully known after seeing the whole string, so 'is this character unique?' can't be answered on the first pass. The second pass must iterate the string (not the map) because we need the leftmost position, and a hash map doesn't preserve input order. A 26-slot array does have a fixed order, but the index into it isn't the character's position in s — so we still scan s.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "loveleetcode"Hash-map state
after pass 1
Continue scanning
frequency table built
2C · Code with teaching comments
D · Why it works and complexityO(n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
This cleanly separates two questions: 'how many of each?' (the table) and 'where is the first qualifying one?' (the ordered scan). Conflating them into one pass is what trips people up.
ComplexityO(n) time, O(1) space — Two linear passes over the string plus O(26) table work = O(n). The fixed-size table makes extra space O(1).
E · Edge cases and pitfalls5s = "aabb" — Every count is 2; pass 2 finds nothing → return -1.Explore edge casesHide edge cases
Edge cases handled
s = "aabb"Every count is 2; pass 2 finds nothing → return -1.s = "z"Single character has count 1 → return 0.
Pitfalls
- Iterating the map in pass 2 — its order isn't the string's order, so 'first' is wrong.
- Trying to answer in one pass before all counts are known.
- Using int[26] for Unicode input — switch to a hash map keyed by code point.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int firstUniqChar(String s) { int[] count = new int[26]; for (int i = 0; i < s.length(); i++) { count[s.charAt(i) - 'a']++; }Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Scanning the map for the 'first' index
Hash maps don't preserve insertion/positional order reliably, so the first count-1 entry in the map may not be the first in the string. Re-scan the string.
Explore the code comparisonHide the code comparison
Scanning the map for the 'first' index
Hash maps don't preserve insertion/positional order reliably, so the first count-1 entry in the map may not be the first in the string. Re-scan the string.
Don't write this
for c, n in count.items():
if n == 1:
return s.index(c) # also O(n) per call, and order is map orderWrite this instead
for i, c in enumerate(s):
if count[c] == 1:
return i2One-pass attempt
On the first pass you can't know a character is unique because a later duplicate may still appear. You need the full tally before locating the answer.
Explore the code comparisonHide the code comparison
One-pass attempt
On the first pass you can't know a character is unique because a later duplicate may still appear. You need the full tally before locating the answer.
Don't write this
// returns too early: 'l' looks unique before its second occurrence
for (int i = 0; i < s.length(); i++)
if (seenOnce(s.charAt(i))) return i;Write this instead
// pass 1 counts everything, pass 2 finds the first count==1Revise
Carry the pattern into the interview
Pattern to remember
Frequency count + ordered re-scan
Code pattern to memorize
count = Counter(s)
for i, c in enumerate(s):
if count[c] == 1: return i
return -1Rules to remember
- 1Two passes: first count frequencies, then re-scan the string in order.
- 2Re-scan the string, not the map — only the string preserves left-to-right position.
- 3A fixed 26-slot array gives
O(1)space for lowercase English; use a map for Unicode. - 4Return the index, not the character; return
-1when every character repeats. - 5Counting then querying separates 'how many' from 'where' — don't try to do both in one pass.
Use this pattern when
- 'First/only element with frequency exactly 1' over a sequence
- Need positional answer after a global frequency tally
- Any two-phase count-then-locate query on characters or values
Choose another pattern when
- Need the unique element's value, order irrelevant → single XOR (Single Number)
- Streaming with queries over time → a queue of candidates + counts (Design phase)
- Need all unique characters, not the first → collect every count-1 entry
Interview takeaway
Count-then-rescan is the clean separation of 'tally frequencies' and 'find a position'. The frequency table is the same histogram used by Valid Anagram and Group Anagrams.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternTwo-phase frequency-count then ordered scan. Reach for it whenever a positional answer depends on a global tally. The histogram is the same one behind Valid Anagram and Group Anagrams.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Valid AnagramSame character-frequency table, compared between two strings.
- Sort Characters By FrequencyComing soonCount, then order characters by their frequency.
- First Missing PositiveComing soonDifferent problem, same 'tally then scan for the first qualifying index' shape.