Algorithms and Data Structures in C++: From Theory to Practice
\n\nMastering algorithms and data structures is a key stage in becoming a professional developer. Without a deep understanding of how to efficiently organize and process data, it is impossible to create performant and scalable applications. C++ provides unique capabilities for low-level memory management and high performance, making it an ideal language for studying this topic.
\n\nIn this article, we will cover fundamental algorithms and data structures in C++ with practical code examples that you can immediately apply in your projects. We will not delve into abstract mathematical calculations — our focus is on real implementation and understanding the principles of operation.
\n\n1. Dynamic Arrays and Memory Management
\n\nLet's start with the basics — dynamic arrays. Unlike static arrays, whose size is fixed at compile time, dynamic arrays allow you to change size during program execution. In C++, the new[] and delete[] operators are used for this.
#include <iostream>\n\nint main() {\n int size = 5;\n int* arr = new int[size]; // memory allocation\n \n // Initialization\n for(int i = 0; i < size; i++) {\n arr[i] = i * 10;\n }\n \n // Output\n for(int i = 0; i < size; i++) {\n std::cout << arr[i] << " ";\n }\n \n delete[] arr; // memory deallocation\n return 0;\n}\n\nIt is important to remember that when working with dynamic memory, you are responsible for freeing it. Memory leaks are one of the most common problems in C++. It is recommended to use smart pointers (std::unique_ptr, std::shared_ptr) from the standard library for automatic memory management.
2. Stack and Queue: Classic Data Structures
\n\nStack and queue are two fundamental data structures that implement the LIFO (Last In, First Out) and FIFO (First In, First Out) principles, respectively. Let's look at their implementation in C++ using a dynamic array.
\n\nStack Implementation
\n\n#include <iostream>\n#include <stdexcept>\n\nclass Stack {\nprivate:\n int* data;\n int capacity;\n int top;\n \npublic:\n Stack(int size) : capacity(size), top(-1) {\n data = new int[capacity];\n }\n \n ~Stack() {\n delete[] data;\n }\n \n void push(int value) {\n if(top >= capacity - 1) {\n throw std::overflow_error("Stack overflow");\n }\n data[++top] = value;\n }\n \n int pop() {\n if(isEmpty()) {\n throw std::underflow_error("Stack underflow");\n }\n return data[top--];\n }\n \n bool isEmpty() const {\n return top == -1;\n }\n \n int peek() const {\n if(isEmpty()) {\n throw std::underflow_error("Stack is empty");\n }\n return data[top];\n }\n};\n\nint main() {\n Stack stack(5);\n stack.push(10);\n stack.push(20);\n stack.push(30);\n \n std::cout << "Top element: " << stack.peek() << std::endl;\n std::cout << "Popping: " << stack.pop() << std::endl;\n std::cout << "Popping: " << stack.pop() << std::endl;\n \n return 0;\n}\n\nQueue Implementation
\n\n#include <iostream>\n\nclass Queue {\nprivate:\n int* data;\n int capacity;\n int front;\n int rear;\n int count;\n \npublic:\n Queue(int size) : capacity(size), front(0), rear(-1), count(0) {\n data = new int[capacity];\n }\n \n ~Queue() {\n delete[] data;\n }\n \n void enqueue(int value) {\n if(isFull()) {\n throw std::overflow_error("Queue overflow");\n }\n rear = (rear + 1) %