2150. Find All Lonely Numbers in the Array
Problem Description
You are given an integer array nums
. A number x
is considered lonely when it meets two conditions:
- It appears exactly once in the array (frequency of 1)
- Its adjacent numbers (
x - 1
andx + 1
) do not appear in the array at all
Your task is to find and return all lonely numbers in nums
. The numbers can be returned in any order.
For example, if nums = [10, 6, 5, 8]
, then:
10
is lonely because it appears once, and neither9
nor11
appear in the array6
is NOT lonely because although it appears once,5
(which is6 - 1
) also appears in the array5
is NOT lonely because6
(which is5 + 1
) appears in the array8
is lonely because it appears once, and neither7
nor9
appear in the array
So the answer would be [10, 8]
.
The solution uses a hash table (Counter) to track the frequency of each number, then filters for numbers that appear exactly once and have no adjacent numbers present in the array.
Intuition
To identify lonely numbers, we need to check two conditions for each number: whether it appears exactly once, and whether its adjacent numbers (x - 1
and x + 1
) are absent from the array.
The key insight is that we need to know the frequency of every number in the array before we can determine if any number is lonely. This naturally leads us to use a frequency counting approach.
Why a hash table? Consider checking if a number x
is lonely:
- We need to know if
x
appears exactly once - We need to know if
x - 1
exists in the array - We need to know if
x + 1
exists in the array
Without preprocessing, we'd have to scan the entire array three times for each number we check, resulting in O(n²)
time complexity.
By using a hash table (Counter) to store frequencies first, we can answer all three questions in O(1)
time for each number. The Counter gives us both the frequency information and acts as a set to check existence - if a number doesn't exist in the Counter, its count is implicitly 0
.
Once we have the frequency map, we simply iterate through it and check each number against our lonely criteria: count[x] == 1
and count[x-1] == 0
and count[x+1] == 0
. This gives us an efficient O(n)
solution overall.
Solution Approach
The solution uses a hash table to efficiently track the occurrence count of each number and check for adjacent numbers.
Step 1: Count Frequencies
We use Python's Counter
from the collections module to create a hash table cnt
that stores the frequency of each number in the array:
cnt = Counter(nums)
This gives us a dictionary where keys are the numbers from nums
and values are their occurrence counts.
Step 2: Filter Lonely Numbers We iterate through the hash table using a list comprehension to find all lonely numbers:
return [ x for x, v in cnt.items() if v == 1 and cnt[x - 1] == 0 and cnt[x + 1] == 0 ]
For each number x
with frequency v
in the hash table, we check three conditions:
v == 1
: The number appears exactly oncecnt[x - 1] == 0
: The numberx - 1
doesn't exist in the array (Counter returns 0 for non-existent keys)cnt[x + 1] == 0
: The numberx + 1
doesn't exist in the array
Only numbers that satisfy all three conditions are included in the result.
Time Complexity: O(n)
where n
is the length of the input array. We iterate through the array once to build the Counter, and then iterate through the unique numbers once to check the lonely conditions.
Space Complexity: O(n)
for storing the hash table, which in the worst case contains all unique numbers from the input array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [1, 3, 5, 3]
.
Step 1: Build the frequency map
First, we create a Counter to track how many times each number appears:
cnt = Counter([1, 3, 5, 3]) cnt = {1: 1, 3: 2, 5: 1}
Step 2: Check each number for loneliness
Now we examine each unique number in our frequency map:
For x = 1
(frequency = 1):
- Does
1
appear exactly once? ✓ Yes (frequency = 1) - Is
x - 1 = 0
absent from the array? ✓ Yes (cnt[0] = 0) - Is
x + 1 = 2
absent from the array? ✓ Yes (cnt[2] = 0) - Result:
1
is lonely ✓
For x = 3
(frequency = 2):
- Does
3
appear exactly once? ✗ No (frequency = 2) - Result:
3
is not lonely (fails first condition)
For x = 5
(frequency = 1):
- Does
5
appear exactly once? ✓ Yes (frequency = 1) - Is
x - 1 = 4
absent from the array? ✓ Yes (cnt[4] = 0) - Is
x + 1 = 6
absent from the array? ✓ Yes (cnt[6] = 0) - Result:
5
is lonely ✓
Final Answer: [1, 5]
Both 1
and 5
appear exactly once and have no adjacent numbers in the array, making them lonely numbers.
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def findLonely(self, nums: List[int]) -> List[int]:
6 # Count the frequency of each number in the array
7 frequency_map = Counter(nums)
8
9 # Find lonely numbers:
10 # A number is lonely if it appears exactly once (frequency == 1)
11 # AND neither (number - 1) nor (number + 1) exist in the array
12 lonely_numbers = []
13 for number, frequency in frequency_map.items():
14 if (frequency == 1 and
15 frequency_map[number - 1] == 0 and
16 frequency_map[number + 1] == 0):
17 lonely_numbers.append(number)
18
19 return lonely_numbers
20
1class Solution {
2 public List<Integer> findLonely(int[] nums) {
3 // Create a frequency map to count occurrences of each number
4 Map<Integer, Integer> frequencyMap = new HashMap<>();
5
6 // Count the frequency of each number in the array
7 for (int number : nums) {
8 frequencyMap.merge(number, 1, Integer::sum);
9 }
10
11 // List to store lonely numbers
12 List<Integer> lonelyNumbers = new ArrayList<>();
13
14 // Iterate through each entry in the frequency map
15 for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
16 int number = entry.getKey();
17 int frequency = entry.getValue();
18
19 // A number is lonely if:
20 // 1. It appears exactly once (frequency == 1)
21 // 2. Neither (number - 1) nor (number + 1) exists in the array
22 if (frequency == 1 &&
23 !frequencyMap.containsKey(number - 1) &&
24 !frequencyMap.containsKey(number + 1)) {
25 lonelyNumbers.add(number);
26 }
27 }
28
29 return lonelyNumbers;
30 }
31}
32
1class Solution {
2public:
3 vector<int> findLonely(vector<int>& nums) {
4 // Create a frequency map to count occurrences of each number
5 unordered_map<int, int> frequencyMap;
6
7 // Count the frequency of each number in the array
8 for (int num : nums) {
9 frequencyMap[num]++;
10 }
11
12 // Store the result - numbers that are "lonely"
13 vector<int> result;
14
15 // Iterate through the frequency map to find lonely numbers
16 for (const auto& [number, frequency] : frequencyMap) {
17 // A number is lonely if:
18 // 1. It appears exactly once (frequency == 1)
19 // 2. Neither (number - 1) nor (number + 1) exist in the array
20 if (frequency == 1 &&
21 !frequencyMap.contains(number - 1) &&
22 !frequencyMap.contains(number + 1)) {
23 result.push_back(number);
24 }
25 }
26
27 return result;
28 }
29};
30
1/**
2 * Finds all lonely numbers in an array.
3 * A lonely number is one that appears exactly once and has no adjacent numbers (n-1 or n+1) in the array.
4 *
5 * @param nums - The input array of numbers
6 * @returns An array containing all lonely numbers
7 */
8function findLonely(nums: number[]): number[] {
9 // Create a frequency map to count occurrences of each number
10 const frequencyMap: Map<number, number> = new Map<number, number>();
11
12 // Count the frequency of each number in the array
13 for (const num of nums) {
14 frequencyMap.set(num, (frequencyMap.get(num) || 0) + 1);
15 }
16
17 // Array to store the lonely numbers
18 const lonelyNumbers: number[] = [];
19
20 // Check each unique number to see if it's lonely
21 for (const [num, frequency] of frequencyMap) {
22 // A number is lonely if:
23 // 1. It appears exactly once (frequency === 1)
24 // 2. Neither (num - 1) nor (num + 1) exists in the array
25 if (frequency === 1 && !frequencyMap.has(num - 1) && !frequencyMap.has(num + 1)) {
26 lonelyNumbers.push(num);
27 }
28 }
29
30 return lonelyNumbers;
31}
32
Time and Space Complexity
Time Complexity: O(n)
The algorithm involves:
- Creating a Counter from the input list
nums
, which requires iterating through alln
elements once -O(n)
- Iterating through the Counter's items (at most
n
unique elements) to check conditions -O(n)
- For each element
x
, checkingcnt[x-1]
andcnt[x+1]
are bothO(1)
operations due to hash table lookups
Therefore, the overall time complexity is O(n) + O(n) = O(n)
.
Space Complexity: O(n)
The space is used for:
- The Counter object
cnt
, which stores at mostn
key-value pairs (in the worst case where all elements are unique) -O(n)
- The output list, which in the worst case could contain all
n
elements if they are all lonely numbers -O(n)
Therefore, the overall space complexity is O(n)
.
Here, n
is the length of the array nums
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Using Standard Dictionary Instead of Counter
A common mistake is using a regular dictionary and checking for adjacent numbers with the in
operator or get()
method:
# Incorrect approach that may cause errors frequency_map = {} for num in nums: frequency_map[num] = frequency_map.get(num, 0) + 1 lonely_numbers = [] for number, frequency in frequency_map.items(): if (frequency == 1 and (number - 1) not in frequency_map and (number + 1) not in frequency_map): lonely_numbers.append(number)
Why Counter is better: Counter automatically returns 0 for non-existent keys, making the code cleaner and avoiding potential KeyError exceptions. With Counter, frequency_map[number - 1] == 0
safely checks for absence, while with a regular dict you need explicit existence checks.
Pitfall 2: Modifying the Original Array During Iteration
Some might try to check conditions while iterating through the original array:
# Incorrect: This doesn't handle duplicates properly lonely = [] for num in nums: if (nums.count(num) == 1 and (num - 1) not in nums and (num + 1) not in nums): lonely.append(num) return lonely
Problems with this approach:
- Time complexity becomes O(n²) due to repeated
count()
andin
operations - Duplicate lonely numbers would be added to the result
- Inefficient for large arrays
Pitfall 3: Off-by-One Errors in Adjacent Number Checks
A subtle mistake is incorrectly checking adjacent numbers:
# Incorrect: Checking wrong adjacent values if (frequency == 1 and frequency_map[number - 1] == 1 and # Wrong condition! frequency_map[number + 1] == 1): # Wrong condition!
The correct logic: We want adjacent numbers to NOT exist (frequency == 0), not to exist exactly once.
Pitfall 4: Integer Overflow Concerns
While not typically an issue in Python, in other languages checking number + 1
or number - 1
for very large integers could cause overflow:
# Better practice for extreme edge cases MAX_INT = 2**31 - 1 MIN_INT = -2**31 for number, frequency in frequency_map.items(): if frequency == 1: # Check boundaries to avoid potential issues left_absent = (number == MIN_INT or frequency_map[number - 1] == 0) right_absent = (number == MAX_INT or frequency_map[number + 1] == 0) if left_absent and right_absent: lonely_numbers.append(number)
Solution: In Python, integers have arbitrary precision so overflow isn't a concern, but being aware of this issue is important when implementing in other languages like Java or C++.
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!