DEV Community

Cover image for Modern C++ for LeetCode πŸ§‘β€πŸ’»πŸš€

Modern C++ for LeetCode πŸ§‘β€πŸ’»πŸš€

C++ has come a long way since its early days, and the "modern" C++ (starting with C++11 and beyond) offers a host of features that make solving problems on platforms like LeetCode way more efficient and enjoyable. Forget manual memory management, verbose loops, and cryptic pointer arithmetic! Modern C++ gives us tools like std::unordered_map, range-based loops, and lambda functions that let us focus more on solving problems and less on boilerplate code. Let's dive into some common algorithmic patterns and see how modern C++ makes them awesome! πŸ’‘

1. Hashmaps (using std::unordered_map) πŸ—ΊοΈ

In old-school C, implementing a hash map meant writing your own data structure with hashing functions, collision handling, and all that jazz. Not anymore! Modern C++ gives us std::unordered_map, which is fast and easy to use.

Example: Two Sum (LeetCode #1)

#include <unordered_map>
#include <vector>

std::vector<int> twoSum(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> map; // Key: number, Value: index
    for (int i = 0; i < nums.size(); ++i) {
        int complement = target - nums[i];
        if (map.count(complement)) { // Check if complement exists in map
            return {map[complement], i};
        }
        map[nums[i]] = i;
    }
    return {};
}
Enter fullscreen mode Exit fullscreen mode

Why it’s awesome:

Fast lookups: Average O(1) for key-value lookups.
No need to write custom hash functions or deal with memory allocation.

2. Binary Search (using std::binary_search) πŸ”

Binary search is a classic algorithm, but in C++, we get helper functions from the header that make life easier.

Example: Search Insert Position (LeetCode #35)

#include <vector>
#include <algorithm>

int searchInsert(const std::vector<int>& nums, int target) {
    return std::lower_bound(nums.begin(), nums.end(), target) - nums.begin();
}
Enter fullscreen mode Exit fullscreen mode

Why it’s awesome:

std::lower_bound: Finds the first element not less than the target.
Clean and concise: No more manually tracking low, high, and mid pointers.

3. Quicksort (using std::sort) ⚑

While implementing quicksort manually is a fun exercise, it's not something you want to do in every LeetCode problem. Modern C++ has std::sort, which is highly optimized.

Example: Sort an Array (LeetCode #912)

#include <vector>
#include <algorithm>

std::vector<int> sortArray(std::vector<int>& nums) {
    std::sort(nums.begin(), nums.end());
    return nums;
}
Enter fullscreen mode Exit fullscreen mode

Why it’s awesome:

Under the hood, std::sort uses a hybrid of quicksort, heapsort, and insertion sort.
Efficient and portable: Optimized for real-world performance.

4. Two Pointer Technique (Range-based Loops + std::string Utilities) πŸƒβ€β™‚οΈπŸƒβ€β™€οΈ

The two-pointer technique is perfect for problems involving arrays or strings. Modern C++ makes it easier with range-based loops and helper functions for strings.

Example: Valid Palindrome (LeetCode #125)

#include <string>
#include <cctype>

bool isPalindrome(const std::string& s) {
    int left = 0, right = s.size() - 1;
    while (left < right) {
        while (left < right && !std::isalnum(s[left])) ++left;
        while (left < right && !std::isalnum(s[right])) --right;
        if (std::tolower(s[left]) != std::tolower(s[right])) return false;
        ++left; --right;
    }
    return true;
}
Enter fullscreen mode Exit fullscreen mode

Why it’s awesome:

std::isalnum and std::tolower: Built-in utilities to handle character checks.
No need for manual ASCII checks!

5. Graphs (using std::queue and std::vector) πŸŒ‰

Graph problems often involve BFS or DFS. Modern C++ shines here with containers like std::vector for adjacency lists and std::queue for BFS.

Example: Number of Islands (LeetCode #200)

#include <vector>
#include <queue>

void bfs(std::vector<std::vector<char>>& grid, int r, int c) {
    std::queue<std::pair<int, int>> q;
    q.push({r, c});
    grid[r][c] = '0'; // Mark visited
    std::vector<std::pair<int, int>> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    while (!q.empty()) {
        auto [row, col] = q.front(); q.pop();
        for (const auto& [dr, dc] : directions) {
            int nr = row + dr, nc = col + dc;
            if (nr >= 0 && nr < grid.size() && nc >= 0 && nc < grid[0].size() && grid[nr][nc] == '1') {
                q.push({nr, nc});
                grid[nr][nc] = '0';
            }
        }
    }
}

int numIslands(std::vector<std::vector<char>>& grid) {
    int count = 0;
    for (int r = 0; r < grid.size(); ++r) {
        for (int c = 0; c < grid[0].size(); ++c) {
            if (grid[r][c] == '1') {
                ++count;
                bfs(grid, r, c);
            }
        }
    }
    return count;
}
Enter fullscreen mode Exit fullscreen mode

Why it’s awesome:

std::queue: Great for BFS, abstracts away all the pointer/memory headache.

Pair destructuring with [row, col]: Clean and readable code.

Conclusion πŸŽ‰

Modern C++ takes away much of the boilerplate coding and error-prone manual work that was once necessary. Whether it's hash maps, sorting, or handling strings and graphs, these new features let you focus on problem-solving instead of fighting the language.

TL;DR of Modern C++ Features:

  • std::unordered_map: O(1) lookups!
  • std::lower_bound and std::sort: Simplifies search and sorting.
  • Range-based loops, lambdas, and std::string utilities: Cleaner, safer, and faster code.
  • std::vector and std::queue: Perfect for graphs, dynamic arrays, and more.

Now grab your favorite cup of coffee β˜• and start smashing those LeetCode problems!

Top comments (3)

Collapse
 
dyfet profile image
David Sugar

Classic C++ gave us virtual methods and required monkey-patching methods by having to derive classes. Templating provided a potentially orthogonal way to create derived types and even to produce traits, but thru much added complexity, but made generic containers easy. Modern C++ gave us lambdas to attach functionality and modify an existing class behavior without need of either inheritance or templates, much like objective C and ruby blocks / closures. I would have put this in such a list all by itself.

Collapse
 
brunociccarino profile image
Bruno Ciccarino Ξ»

I didn't know about this, I've been studying C++ for a short time, but it's cool to know, thanks for telling me

Collapse
 
dyfet profile image
David Sugar

Lambdas were in some ways also poorly introduced, as almost nothing in stdlib++ initially made use of them when C++11 was introduced. It's much easier to appreciate what could be done with it if you had worked with ruby, which has file and containers libraries that do make use of anonymous function blocks and closures extensively. I use it in somewhat similar ways in moderncli:

gitlab.com/tychosoft/moderncli

Some comments may only be visible to logged-in visitors. Sign in to view all comments.