C++ 0528.cpp
class Solution {
vector<int64_t> sum;
public:
Solution(vector<int>& w) {
sum.reserve(w.size());
int64_t total = 0;
for (auto x : w)
{
total += x;
sum.push_back(total);
}
}
int pickIndex() {
int maxx = sum.back();
std::random_device r;
std::default_random_engine engine(r());
std::uniform_int_distribution<int> uniform_dist(1, maxx);
int x = uniform_dist(engine);
auto p = lower_bound(sum.begin(), sum.end(), x);
return distance(sum.begin(), p);
}
};
Difficulty
and has an acceptance rate of about 49% (1+ million submissions).
Typical patterns to solve this problem:
- Array
- Binary Search
- Math
- Prefix Sum
- Randomized
AI Disclaimer
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.
Code Walkthrough
Overview
The presented class implements a weighted random selection mechanism. During construction it builds a prefix-sum array from the supplied weight vector. The pickIndex method draws a random integer in the range [1, totalWeight] and uses binary search to locate the first prefix sum that meets or exceeds the drawn value. The index of that prefix sum is returned, giving each element a selection probability proportional to its weight.
Construction of the Prefix-Sum Array
Solution(vector<int>& w) {
sum.reserve(w.size());
int64_t total = 0;
for (auto x : w)
{
total += x;
sum.push_back(total);
}
}
The constructor receives a vector of integer weights. It first reserves space in the internal sum vector equal to the number of weights, preventing repeated reallocations as the prefix sums are appended. An int64_t accumulator named total aggregates the running sum. For each weight x the accumulator is increased and the new total is stored in sum. The resulting sum vector holds cumulative totals, where sum[i] equals the sum of the first i+1 weights. This structure enables O(log n) lookup later.
Edge Cases Addressed
When the input contains a single weight, the loop runs once, sum ends with a single element equal to that weight, and pickIndex will always return index 0, correctly reflecting a 100 % selection chance. The use of a 64-bit accumulator safeguards against overflow when the sum of many 32-bit weights exceeds the range of int. The reserve call also gracefully handles the case where the weight vector is empty; the subsequent loop simply does not execute, leaving sum empty. (The later pickIndex call would then operate on an empty sum, which is outside the intended usage scenario.)
Random Number Generation
int maxx = sum.back();
std::random_device r;
std::default_random_engine engine(r());
std::uniform_int_distribution<int> uniform_dist(1, maxx);
int x = uniform_dist(engine);
maxx captures the total weight, the last element of the prefix-sum array. A random_device supplies entropy to seed a default_random_engine. The engine is constructed anew on each invocation of pickIndex, ensuring that the sequence of generated numbers does not depend on previous calls. The uniform integer distribution is configured with inclusive bounds [1, maxx], guaranteeing that every integer within the total weight range has an equal chance of being drawn.
Edge Cases Addressed
If the total weight is exactly 1, the distribution range collapses to a single value, and the generated x will always be 1. The binary search that follows will therefore always locate the first prefix sum that is at least 1, which is the correct behavior for a single-unit weight scenario. The use of int for maxx and the distribution is safe because maxx originates from a 64-bit sum but is cast to int; this implicitly assumes that the total weight fits within the 32-bit signed integer range, which aligns with typical problem constraints.
Binary Search for the Target Index
auto p = lower_bound(sum.begin(), sum.end(), x);
return distance(sum.begin(), p);
std::lower_bound performs a binary search over the sorted prefix-sum vector to find the first element that is not less than the random value x. Because the prefix sums are monotonically increasing, the position returned corresponds to the weight bucket that contains x. The distance from the beginning of the vector to this iterator yields the zero-based index to be returned.
Edge Cases Addressed
When x equals a prefix sum exactly, lower_bound returns an iterator to that element, ensuring that the corresponding index is selected. If x is larger than all but the last prefix sum, the iterator points to the final element, correctly mapping the random draw to the last weight bucket. The algorithm also works when multiple weights share the same value, as the cumulative nature of the prefix sums preserves the correct boundaries.
Performance Optimizations
The constructor’s reserve call eliminates the need for dynamic resizing during the accumulation loop, reducing allocation overhead and improving cache locality. Storing cumulative totals in a contiguous vector<int64_t> enables the binary search to operate on a compact memory layout, which benefits branch prediction and prefetching. The use of std::lower_bound leverages a highly tuned implementation of binary search, providing logarithmic lookup time without manual loop management. By constructing the random engine locally for each call, the code avoids shared mutable state, which can be a source of contention in multithreaded contexts.
The combination of these choices results in a solution that builds its internal data structure in linear time relative to the number of weights and answers each selection request in logarithmic time, while keeping memory usage proportional to the input size. The reliance on standard library components ensures that the implementation benefits from compiler optimizations and platform-specific enhancements.
ELI5 - Simplified Explanation
Imagine a row of treasure chests. Some chests are very big, and some are tiny. The big chests have more chances to be picked, just like a game where the biggest pile of toys gets chosen most often.
The first part of the code goes through all the chests one by one. It keeps a running total of how much space they take up. Think of it like walking along a line and marking the distance as you pass each chest. The tiny chests give you a tiny step, while the big chests give you a giant step. By the end, you have a long ruler that shows exactly where each chest starts and ends.
When it is time to pick a chest, the code uses a magical spinning wheel to pick a random number. This number is used to find the right spot on that long ruler. Because the big chests cover a much larger part of the ruler than the tiny ones, the random number is much more likely to land in a space belonging to a big chest.
After picking the spot, the code looks at the ruler to see which chest holds that spot. It quickly checks the marks made earlier to decide which chest was chosen. This makes sure that the bigger the chest, the more often it gets picked, making it a very fair way to play the game.
Code Quality
Complexity Analysis
Other Solutions
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++ | Python | Java | TS | ||||
| WalkCCC | C++ | Python | Java | ||||||
| leetcode.ca | C++ | Python | JS | Java | Rust | Go | |||
| LeetcodeHelp | C++ | Python | Java | ||||||
| Progiez | C++ | Python | Java | ||||||
| github | BigEggStudy | C# | |||||||
| black-shadows | C++ | ||||||||
| charles-wangkai | Java | ||||||||
| everthis | JS | ||||||||
| fpdjsns | C++ | ||||||||
| halfrost | Go | ||||||||
| JoshCrozier | JS | ||||||||
| keineahnung2345 | C++ | ||||||||
| lkwq007 | Python | ||||||||
| lzl124631x | C++ | ||||||||
| ruoru | Rust | ||||||||
| sonugiri1043 | C++ | ||||||||
| suxrobGM | C# | ||||||||
| trung-hn | Python | ||||||||
| varunu28 | Java | ||||||||
| wisdompeak | C++ | ||||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||||
| WalkCCC | C++, Python, Java | ||||||||
| leetcode.ca | C++, Go, Java, JavaScript, Python, Rust | ||||||||
| LeetcodeHelp | C++, Java, Python | ||||||||
| Progiez | C++, Java, Python | ||||||||
| BigEggStudy | C# | ||||||||
| black-shadows | C++, C++ | ||||||||
| charles-wangkai | Java | ||||||||
| everthis | JavaScript | ||||||||
| fpdjsns | C++ | ||||||||
| halfrost | Go | ||||||||
| JoshCrozier | JavaScript | ||||||||
| keineahnung2345 | C++ | ||||||||
| lkwq007 | Python | ||||||||
| lzl124631x | C++ | ||||||||
| ruoru | Rust | ||||||||
| sonugiri1043 | C++ | ||||||||
| suxrobGM | C# | ||||||||
| trung-hn | Python | ||||||||
| varunu28 | Java | ||||||||
| wisdompeak | C++ |
Videos
Similar Problems
- problem 497: Random Point in Non-overlapping Rectangles
- problem 710: Random Pick with Blacklist
Solved On The Same Day
- problem 13: Roman to Integer
- problem 21: Merge Two Sorted Lists
- problem 28: Find the Index of the First Occurrence in a String
- problem 35: Search Insert Position
- problem 58: Length of Last Word
- problem 66: Plus One
- problem 67: Add Binary
- problem 69: Sqrt(x)
- problem 70: Climbing Stairs
- problem 206: Reverse Linked List
- problem 242: Valid Anagram
- problem 278: First Bad Version
- problem 283: Move Zeroes
- problem 367: Valid Perfect Square
- problem 374: Guess Number Higher or Lower
- problem 389: Find the Difference
- problem 441: Arranging Coins
- problem 459: Repeated Substring Pattern
- problem 509: Fibonacci Number
- problem 657: Robot Return to Origin
- problem 682: Baseball Game
- problem 704: Binary Search
- problem 709: To Lower Case
- problem 744: Find Smallest Letter Greater Than Target
- problem 746: Min Cost Climbing Stairs
- problem 860: Lemonade Change
- problem 896: Monotonic Array
- problem 976: Largest Perimeter Triangle
- problem 1137: N-th Tribonacci Number
- problem 1232: Check If It Is a Straight Line
- problem 1275: Find Winner on a Tic Tac Toe Game
- problem 1351: Count Negative Numbers in a Sorted Matrix
- problem 1491: Average Salary Excluding the Minimum and Maximum Salary
- problem 1502: Can Make Arithmetic Progression From Sequence
- problem 1523: Count Odd Numbers in an Interval Range
- problem 1539: Kth Missing Positive Number
- problem 1572: Matrix Diagonal Sum
- problem 1672: Richest Customer Wealth
- problem 1768: Merge Strings Alternately
- problem 1822: Sign of the Product of an Array
- problem 2: Add Two Numbers
- problem 5: Longest Palindromic Substring
- problem 33: Search in Rotated Sorted Array
- problem 34: Find First and Last Position of Element in Sorted Array
- problem 43: Multiply Strings
- problem 50: Pow(x, n)
- problem 54: Spiral Matrix
- problem 62: Unique Paths
- problem 63: Unique Paths II
- problem 64: Minimum Path Sum
- problem 73: Set Matrix Zeroes
- problem 74: Search a 2D Matrix
- problem 120: Triangle
- problem 139: Word Break
- problem 153: Find Minimum in Rotated Sorted Array
- problem 198: House Robber
- problem 221: Maximal Square
- problem 275: H-Index II
- problem 279: Perfect Squares
- problem 300: Longest Increasing Subsequence
- problem 436: Find Right Interval
- problem 445: Add Two Numbers II
- problem 540: Single Element in a Sorted Array
- problem 611: Valid Triangle Number
- problem 658: Find K Closest Elements
- problem 852: Peak Index in a Mountain Array
- problem 981: Time Based Key-Value Store
- problem 1041: Robot Bounded In Circle
- problem 1146: Snapshot Array
- problem 2300: Successful Pairs of Spells and Potions
- problem 154: Find Minimum in Rotated Sorted Array II
Extra
- 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