Problem 2150

Abstract / Summary

Find all numbers in an array that appear exactly once and whose value equals how many times they appear.

Note

The full problem description, including helpful examples, can be found on LeetCode.

Python 2150.py

class Solution:
    def findLonely(self, nums: List[int]) -> List[int]:
        nums.sort()
        for i in range(len(nums)):
            if i > 0:
                if nums[i - 1] >= nums[i] - 1:
                    continue
            if i < len(nums) - 1:
                if nums[i + 1] <= nums[i] + 1:
                    continue

            yield nums[i]
12 lines | 374 bytes | submitted September 28, 2023 | GitHub   tab size 4 | low complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 64% (100k+ submissions).

Typical patterns to solve this problem:
  • Array
  • Counting
  • Hash Table
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.

See this dedicated page for more details.
LeetCode profile | GitHub repo

Code Walkthrough

Overview

The provided implementation defines a method that extracts the “lonely” numbers from an input list. A number is considered lonely when there is no other element in the list that is equal to it, one less than it, or one greater than it. The algorithm follows three logical phases: sorting the input, scanning the sorted sequence while applying neighbor-based conditions, and lazily emitting qualifying values.

Sorting Phase

The first operation arranges the elements in non-decreasing order.

nums.sort()

Sorting guarantees that any two numbers that could affect each other’s loneliness are positioned next to one another. This ordering eliminates the need for nested loops or repeated searches, allowing the subsequent scan to rely solely on immediate neighbors.

Iteration and Conditional Checks

A for loop walks through every index of the sorted list. For each position, two guard clauses examine the surrounding elements when they exist.

for i in range(len(nums)):
    if i > 0:
        if nums[i - 1] >= nums[i] - 1:
            continue
    if i < len(nums) - 1:
        if nums[i + 1] <= nums[i] + 1:
            continue

The first guard activates only when a predecessor is present (i > 0). It discards the current element if the previous value is greater than or equal to one less than the current value, which covers the cases where the predecessor is the same number or exactly one smaller.

The second guard activates only when a successor exists (i < len(nums) - 1). It discards the current element if the next value is less than or equal to one more than the current value, handling the symmetric situation where the successor is the same number or exactly one larger.

By using continue statements, the loop skips further processing for any element that fails either neighbor test, ensuring that only numbers isolated by at least a gap of two on both sides survive to the next stage.

Yielding Lonely Elements

When an index passes both neighbor checks, the method yields the corresponding value.

yield nums[i]

Yielding turns the method into a generator, producing results on demand rather than constructing a full list up front. This approach reduces memory consumption, especially for large inputs where only a small fraction of numbers qualify as lonely.

Edge Cases Handled

The logic naturally accommodates several boundary conditions:

  • Empty Input - The loop body never executes, resulting in an empty generator.
  • Single-Element List - Both neighbor guards are bypassed because the index is neither greater than zero nor less than the last index, allowing the sole element to be yielded as lonely.
  • Duplicate Values - When identical numbers appear consecutively, the predecessor guard detects that the previous element is not less than the current value minus one, causing the duplicate to be skipped.
  • Consecutive Sequences - In a run such as [3,4,5], each interior element fails at least one neighbor guard because the adjacent numbers differ by exactly one, preventing any of them from being yielded.
  • Numbers at the Extremes - The first and last elements are evaluated with only one neighbor guard, correctly handling cases where loneliness depends on a single side.

Performance Optimizations

The design leverages sorting to bring potential conflicts into adjacency, which eliminates the need for costly global searches. After sorting, the algorithm proceeds with a single linear pass, using early continue statements to avoid unnecessary evaluations for elements that are already known to be non-lonely. The generator pattern further optimizes resource usage by emitting results incrementally, allowing callers to consume the output without incurring the overhead of an intermediate collection.

written by openai gpt

ELI5 - Simplified Explanation

Imagine a long line of numbered blocks scattered on the floor. First, the blocks are put in order from the smallest number to the biggest number, just like lining up toys by size. Now, look at each block one by one. To see if a block is lonely, check the neighbors right next to it.

If there is a block with the same number as the current one, or if there is a block with a number that is exactly one higher or one lower, then the current block is not lonely because it has friends close by. Think of this like playing a game where you only pick the blocks that are standing by themselves with nobody nearby. If a block has no friends next to it, it gets to stay.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 7 | μ = 5.93 | σ = 1.88

Complexity Analysis

Time: loglinear
As a rule of thumb, the maximum input size for one second execution time is
  • factorial: up to 10 elements
  • exponential: up to 20 elements
  • polynomial: a few thousand elements
  • loglinear: up to 100,000 elements
  • linear: a few million elements
  • logarithmic: a billion elements
  • constant: practically unlimited
generated automatically

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
Obviously, I did that after I solved the problem myself.

In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.

And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.

(click on a column header to sort the table)
author
author solutions
www AlgoMonster C++ Java Python TS
WalkCCC C++ Java Python
leetcode.ca C++ Java Python Go TS
Progiez C++ Java Python
github black-shadows C++
charles-wangkai Java
everthis JS
fartem Ruby
fsq C++
JoshCrozier JS
lkwq007 Python
martianoff Scala
suxrobGM C#
varunu28 Java
AlgoMonster C++, Python, Java, TypeScript
WalkCCC C++, Python, Java
leetcode.ca C++, Go, Java, Python, TypeScript
Progiez C++, Java, Python
black-shadows C++, C++
charles-wangkai Java
everthis JavaScript
fartem Ruby
fsq C++
JoshCrozier JavaScript
lkwq007 Python
martianoff Scala
suxrobGM C#
varunu28 Java
25 links | 14 authors | 9 languages

Videos

7 videos   did my crawler come up with invalid videos ? drop me a line.

Similar Problems

1 similar problem

Solved On The Same Day

32 solutions   September 28, 2023

Extra

There are a few extra pages giving you some insights into my setup:
  • Tech Stack - my development environment and tools
  • AI - how AI helped me create this website (NOT solving the problems)
  • Progress - progression over time
  • Quality - code quality