🚀 Phases 1–4 are live — Days 1–13 + Day 17 are fully written. See the roadmap →

First Unique Character in a String easy

Description

Given a string s, find the first non-repeating character and return its index. If there is no such character, return -1.

Examples

> Case 1:
    Input:  s = "leetcode"
    Output: 0       # 'l' is the first character to appear only once
 
> Case 2:
    Input:  s = "loveleetcode"
    Output: 2       # 'v' is the first non-repeating; 'l' and 'o' appear twice
 
> Case 3:
    Input:  s = "aabb"
    Output: -1

Constraints

  • 1 <= s.length <= 10^5
  • s consists of only lowercase English letters.

Intuition

Two passes:

  1. First pass — count frequencies. Build {char: count} by walking the whole string. O(n).
  2. Second pass — find the first single. Walk the string again, return the index of the first character whose count is 1. O(n).

Total: O(n) time, O(1) space (the count map has at most 26 entries because we’re told it’s lowercase ASCII).

You can’t do it in one pass because “the first character with count 1” depends on the final count — and you don’t know that until you’ve seen the whole string.

Code

Array vs HashMap for fixed character sets. When the keys come from a small, known universe (26 lowercase letters, ASCII, etc.), use a plain array indexed by c - 'a' instead of a HashMap<Character, Integer>. Same big-O — but array access is faster than hash, the array is cache-friendlier, and there’s no hashing overhead. Switch to a hash map only when the key space is unbounded (full unicode, arbitrary strings).

Why two passes is the right call

You might think: “what if I track the first occurrence and decrement as we see duplicates?” That gets messy fast — you need to remember which index was the candidate, then push it forward when duplicates appear, then handle the no-unique case. Two clean passes is simpler and just as fast.

Variant: streaming version

What if the input is a stream and you have to answer “first non-repeating character so far” after every character? That’s harder — you need to maintain an ordered set of single-occurrence characters as the stream advances. The standard solution uses a LinkedHashMap (Java), OrderedDict (Python), or a doubly linked list keyed by the character (C++).

Analysis

  • Time: O(n) — two passes through the string.
  • Space: O(1) when the alphabet is bounded; O(k) where k is the number of distinct characters in the general case.