Group Anagrams

Given an array of strings strs, group the anagrams together. You can return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example 1:

Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Approach1:

We know that two string are anagrams if their sorted strings are equal.

  • Iterate through the Strings of array

  • We sort each String that we encounter in the loop and use it as Key

  • Since the Keys consist of sorted Strings therefore all anagrams will share the same key.

  • Essentially, when collision occurs (When two strings are anagram), we add that String to that it's respective place in the map.

  • Finally, we return the values of the HahsMap as it contained all the anagrams grouped in groups.

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        
        for(String s : strs){
            char[]a = s.toCharArray();
            Arrays.sort(a);
            String key = String.valueOf(a);
            map.putIfAbsent(key, new ArrayList<>());
            map.get(key).add(s);
        }
        return new ArrayList<>(map.values());
    }
}

Time: O(mklogk) Space: O(mk)

Approach2: Optimal

After completing our first-approach, we notice that we might be able to optimize our algorithm. Primarily due to the fact that we know that our Strings will only contain lower-case english letters. This subsequenly allows us to use Counting Sort which is an O(N) sorting algorithm in lieu of Arrays.sort() which is a nlogn algorithm (quickSort).

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        
        for(String s : strs){
            int[]freq = new int[26];
            for(int i = 0; i < s.length(); i++)
                freq[s.charAt(i) - 'a']++;
            String key = Arrays.toString(freq);
            map.putIfAbsent(key, new ArrayList<>());
            map.get(key).add(s);
        }
        return new ArrayList<>(map.values());
    }
}

Time: O(mk) Space: O(mk)

Approach3: Brute-force

When first presented with this problem, the below implementation is also an intuitive approach however the time complexity is exponential.

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Set<List<String>> set = new HashSet<>();
        List<String> list = new ArrayList<>();
        
        for(int i = 0; i < strs.length; i++){
            char[]ch = strs[i].toCharArray();
            Arrays.sort(ch);
            for(int j = 0; j < strs.length; j++){
                char[]a = strs[j].toCharArray();
                Arrays.sort(a);
                if(Arrays.equals(ch, a)){
                    list.add(strs[j]);
                }
            }
            set.add(new ArrayList<>(list));
            list.clear();
        }
        return new ArrayList<>(set);
    }
}

Time: O(mklogk2) Space: O(mk)

Last updated

Was this helpful?