Algorithms and Data Structures in C++: From Theory to Real Projects
Mastering algorithms and data structures is the foundation that distinguishes a novice programmer from a professional engineer. In C++, one of the most powerful and performant languages, understanding these concepts allows you to write not just working code, but fast, scalable, and reliable code. In this article, we will break down key data structures and algorithms in practice, with ready-made examples that you can immediately use in your projects.
We will not delve into abstract theory — each section contains specific C++ code and explanations of how and why a particular construct works. The material is aimed at developers who are already familiar with C++ syntax but want to systematize their knowledge of algorithms.
1. Basic Data Structures: Arrays, Lists, and Dynamic Structures
Let's start with the essentials. In C++, the Standard Library (STL) provides powerful containers, but understanding their internal workings is critical for optimization.
Dynamic Array (std::vector)
std::vector is the most commonly used container. It represents a dynamic array that automatically expands when elements are added. However, it's important to remember amortized complexity: insertion at the end is O(1) on average, but insertion in the middle is O(n).
#include <iostream>#include <vector>
int main() { std::vector<int> numbers = {10, 20, 30, 40}; // Adding to the end numbers.push_back(50); // Insertion at the beginning (expensive operation) numbers.insert(numbers.begin(), 5); // Removing the last element numbers.pop_back(); for (int num : numbers) { std::cout << num << " "; } // Output: 5 10 20 30 40 return 0;}Linked List (std::list)
Unlike a vector, std::list is implemented as a doubly linked list. It is ideal when you frequently insert or delete elements in the middle of the container — these operations are performed in O(1). The trade-off is the lack of direct index-based access.
#include <list>
std::list<std::string> tasks = {"Write code", "Run tests"};tasks.push_front("Plan architecture");tasks.push_back("Document");
// Insertion in the middleauto it = tasks.begin();std::advance(it, 2); // Move iterator to the 2nd elementtasks.insert(it, "Refactoring");Stack and Queue (std::stack, std::queue)
These are container adapters that limit the functionality of the underlying container. A stack operates on the LIFO (Last In, First Out) principle, a queue on FIFO (First In, First Out).
#include <stack>#include <queue>
// Stackstd::stack<int> st;st.push(1);st.push(2);st.pop(); // Removes 2
// Queuestd::queue<int> q;q.push(10);q.push(20);q.pop(); // Removes 10 (first)2. Sorting and Searching Algorithms: From Bubble Sort to Binary Search
Sorting is a classic problem, but in practice it's important to know when to use built-in algorithms and when to write your own.
QuickSort — Implementation in C++
The STL uses introsort (a hybrid of QuickSort, HeapSort, and InsertionSort). But for understanding, let's write a classic QuickSort:
#include <iostream>#include <vector>
int partition(std::vector<int>& arr, int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] < pivot) { i++; std::swap(arr[i], arr[j]); } } std::swap(arr[i + 1], arr[high]); return i + 1;}
void quickSort(std::vector<int>& arr, int low, int high) { if (low < hi