387. First Unique Character in a String (Easy)
Problem
Given a string s, find the first non-repeating character and return its index. Return -1 if no such character exists.
Example
s = "leetcode"→0(character'l')s = "loveleetcode"→2(character'v')s = "aabb"→-1
LeetCode 387 · Link · Easy
Try it yourself
Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).
Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.
Click Run TS to execute. First run downloads Babel (~400 KB, cached after that).
Approach: Two-pass frequency count
Pass 1: build a frequency map (count of each character). Pass 2: scan left to right and return the first index whose count is exactly 1.
from collections import Counter
def first_uniq_char(s: str) -> int: counts = Counter(s) # L1: O(n) scan, build frequency map for i, ch in enumerate(s): # L2: O(n) second scan if counts[ch] == 1: # L3: O(1) lookup return i # L4: O(1) return return -1 # L5: O(1)function firstUniqChar(s: string): number { const counts = new Map<string, number>(); for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1); // L1: O(n) build for (let i = 0; i < s.length; i++) { // L2: O(n) second scan if (counts.get(s[i]) === 1) return i; // L3-L4: O(1) lookup } return -1; // L5: O(1)}Where the time goes, line by line
Variables: n = len(s), k = number of distinct characters (at most 26 for lowercase letters).
| Line | Per-call cost | Times executed | Contribution |
|---|---|---|---|
| L1 (Counter) | 1 | ||
| L2 (second scan) | body | n | ← ties L1 |
| L3 (lookup) | n | ||
| L4 (return) | 1 | ||
| L5 (return -1) | 1 |
Both passes are . The total is even though we traverse the string twice.
Complexity
- Time: , driven by L1 (Counter) and L2 (second scan). Two linear passes.
- Space: . The
Counterholds at most 26 entries for lowercase Latin letters regardless ofn.
The space bound is because the problem constrains s to lowercase English letters: the alphabet is fixed at 26 characters. No matter how long the string is, the frequency map never exceeds 26 entries.
Try this approach:
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).
Click Run TS to execute. First run downloads Babel (~400 KB, cached after that).
Click Run Go to execute. Runs via the Go Playground API.
final class Solution { func firstUniqChar(_ s: String) -> Int { var counts: [Character: Int] = [:]; for char in s { counts[char, default: 0] += 1 } for (index, char) in s.enumerated() where counts[char] == 1 { return index } return -1 }}Summary
| Step | Cost |
|---|---|
| Build Counter | |
| Scan for first count == 1 | |
| Total | time, space |
Test cases
# Quick smoke tests, paste into a REPL or save as test_387.py and run.
from collections import Counter
def first_uniq_char(s: str) -> int: counts = Counter(s) for i, ch in enumerate(s): if counts[ch] == 1: return i return -1
def _run_tests(): assert first_uniq_char("leetcode") == 0 assert first_uniq_char("loveleetcode") == 2 assert first_uniq_char("aabb") == -1 assert first_uniq_char("z") == 0 assert first_uniq_char("aab") == 2 assert first_uniq_char("cc") == -1 print("all tests pass")
if __name__ == "__main__": _run_tests()function assert(condition: boolean, msg: string = ''): void { if (!condition) throw new Error(msg || 'Assertion failed');}
function firstUniqChar(s: string): number { const counts = new Map<string, number>(); for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1); for (let i = 0; i < s.length; i++) { if (counts.get(s[i]) === 1) return i; } return -1;}
assert(firstUniqChar("leetcode") === 0);assert(firstUniqChar("loveleetcode") === 2);assert(firstUniqChar("aabb") === -1);assert(firstUniqChar("z") === 0);assert(firstUniqChar("aab") === 2);assert(firstUniqChar("cc") === -1);console.log("all tests pass");Related topics
- Group Anagrams, frequency maps over character counts
- Valid Anagram, comparing character frequency maps
- Two Sum, hash-map lookup pattern
Related concepts
- Hash Map Counting, the lookup table pattern for complements, frequencies, and seen items.
- Array Scans, the linear pass habit of carrying just enough state while reading each item once.