Provide self-contained code
You are missing headers, and namespaces:
#include <utility>
#include <vector>
void SortNums(std::vector<int>& nums) {
...
}
Compile with warnings
On Clang and GCC, this means -Wall -Wextra -Werror
.
Ensure correct integer types
The size of a std::vector
is std::size_t
, an unsigned integer, not int
.
The indexes which you use to index over std::vector
should also be std::size_t
.
Note: comparisons between signed and unsigned integers can give weird results, the compiler would have warned about this.
Switch to a better type (C++20)
In the end, you never manipulate the vector size, or capacity. In fact, your function would work on any slice of int
, regardless of whether they're from an array, a vector
, or something else.
From C++20 onwards, you should use std::span<int>
as an argument, instead.
Cache the vector size
The compiler may or may not be able to optimize nums.size()
. It'll depend whether it manages to prove that the writes performed through nums[...]
may or may not alter the size.
It's thus best practice to cache the size (or end iterator) when doing such iteration.
for (std::size_t i = 0, max = nums.size(); i < max && left < right; ++i) {
}
Use pre-increment
Use pre-increment when post-increment is unnecessary. While in practice for integral types the generated code will be the same, for complex iterators this is not the case, thus it's a good habit to get into.
Always wrap blocks with brackets
Always use brackets around blocks after a while
, if
, etc...
Apple's GOTO FAIL bug would have been easier to spot with the proper use of brackets, for example.
Use a better algorithm
Having a limited number of values is the text-book usecase for Counting Sort.
Assuming that we are specializing for [0, 2]
, this means:
#include <cassert>
#include <algorithm>
#include <span>
#include <utility>
void SortNums(std::span<int> nums) {
// The best way to document pre-conditions, is to enforce them.
assert(std::all_of(nums.begin(), nums.end(),
[](int i) { return 0 <= i && i <= 2; });
std::size_t counts[3] = { 0, 0, 0 };
for (auto i : nums) { ++counts[i]; }
auto it = nums.begin();
for (int i = 0; i < 3; ++i) {
it = std::fill_n(it, counts[i], i);
}
}
This algorithm has O(N) time complexity (two linear passes over the data) and O(1) extra space complexity.
It also likely triggers auto-vectorization of the write pass, and may trigger auto-vectorization of the read pass.
std::sort()
(orstd::ranges::sort()
) from the<algorithm>
library? Or are you intentionally reinventing-the-wheel? \$\endgroup\$vector
here an alias ofstd::vector
, or something else? You'll get better reviews if you provide code that's complete, with the necessary headers or other definitions for it to compile. \$\endgroup\$switch
or if/else if/else. (I'd recommend against a std::unordered_map hash table for only 3 elements.) See Micro Optimization of a 4-bucket histogram of a large array or list for how to count very fast with SIMD for 32-bit elements. \$\endgroup\$std::vector
. And I am "reinventing the wheel" if you want to put it that way. \$\endgroup\$