└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # CPP-STL-beginners-guide 2 | 3 | This is my first attempt at writing a tutorial on the C++ STL. I created this with love ❤️, however as a beginner, there may be some inaccuracies. I welcome any feedback to improve it! 4 | 5 | You can also download the PDF version here: [STL Tutorial PDF](https://drive.google.com/file/d/15Z97li6MgD8I_1dlT4S1e0AaSjox85op/view?usp=sharing) 6 | 7 | --- 8 | 9 | The C++ Standard Template Library (STL) is a powerful set of containers, algorithms, and iterators. This repository aims to provide a gentle introduction to the key components of the STL for C++ beginners, with hands-on examples and explanations. We'll cover vectors, pairs, queues, stacks, and more. While not comprehensive, it serves as a starting point to leverage templates and generic programming in your C++ projects. 10 | 11 | The tutorials cover: 12 | 13 | - Vectors 14 | - Pairs 15 | - Queues 16 | - Stacks 17 | - Deques 18 | - Iterators 19 | - Structs 20 | - Set 21 | - Map 22 | - Priority_queue 23 | 24 | # C++ STL (Standard Template Library) 25 | 26 | The C++ STL is a collection of template classes and functions that provide a set of generic and efficient algorithms, containers, and iterators widely used in C++ programming. 27 | 28 | ## Components of STL 29 | 30 | STL has four major components: 31 | 32 | - Containers 33 | - Iterators 34 | - Algorithms 35 | - Function objects 36 | 37 | So, start with containers. 38 | 39 | ### Containers 40 | 41 | If you are dealing with many elements, then you need to use some sort of container. The container can be described as objects that are used to store the collection of data. It helps in recreating and implementing complex data structures efficiently. 42 | 43 | Now, containers are further classified into three categories: 44 | 45 | ![cpp_STL_Example1](https://www.simplilearn.com/ice9/free_resources_article_thumb/C%2B%2B_STL_Example1.PNG) 46 | 47 | ## STL Containers 48 | 49 | ### Sequence Containers 50 | 51 | Sequence containers implement data structures that can be accessed in a sequential manner. The following sequence containers are available: 52 | 53 | - [vector](https://www.geeksforgeeks.org/vector-in-cpp-stl/) 54 | - [list](https://www.geeksforgeeks.org/list-cpp-stl/) 55 | - [deque](https://www.geeksforgeeks.org/deque-cpp-stl/) 56 | 57 | ### Container Adaptors 58 | 59 | Container adaptors provide a different interface for sequential containers. The following container adaptors are available: 60 | 61 | - [queue](https://www.geeksforgeeks.org/queue-cpp-stl/) 62 | - [priority_queue](https://www.geeksforgeeks.org/priority-queue-in-cpp-stl/) 63 | - [stack](https://www.geeksforgeeks.org/stack-in-cpp-stl/) 64 | 65 | ### Associative Containers 66 | 67 | Associative containers implement sorted data structures that can be quickly searched with O(log n) complexity. The following associative containers are available: 68 | 69 | - [set](https://www.geeksforgeeks.org/set-in-cpp-stl/) 70 | - [multiset](https://www.geeksforgeeks.org/multiset-in-cpp-stl/) 71 | - [map](https://www.geeksforgeeks.org/map-associative-containers-the-c-standard-template-library-stl/) 72 | - [multimap](https://www.geeksforgeeks.org/multimap-associative-containers-the-c-standard-template-library-stl/) 73 | 74 | ### Unordered Associative Containers 75 | 76 | Unordered associative containers implement unordered data structures that can be quickly searched. The following unordered associative containers are available (Introduced in C++11): 77 | 78 | - [unordered_set](https://www.geeksforgeeks.org/unordered_set-in-cpp-stl/) 79 | - [unordered_multiset](https://www.geeksforgeeks.org/unordered_multiset-and-its-uses/) 80 | - [unordered_map](https://www.geeksforgeeks.org/unordered_map-in-cpp-stl/) 81 | - [unordered_multimap](https://www.geeksforgeeks.org/unordered_multimap-and-its-application/) 82 | 83 | --- 84 | 85 | ## This tutorial covers the following topics: 86 | 87 | The Linear Container STL includes various data structures that store elements in a linear manner. Some commonly used linear containers are: 88 | 89 | - **Vector**: A dynamic array that can resize itself. 90 | 91 | - **Queue**: A container that follows the First-In-First-Out (FIFO) principle. 92 | - **Deque**: A double-ended queue that allows insertion and deletion at both ends. 93 | - **Stack**: A container that follows the Last-In-First-Out (LIFO) principle. 94 | - **Pair**: A simple container that holds two values togethe, can be used with other STL components. 95 | - **Struct**: A user-defined data type that can hold multiple values. Not an STL container. 96 | 97 | --- 98 | 99 | ## Vector : 100 | 101 | In C++, vectors are **dynamically-sized arrays**. They offer a convenient and efficient way to store collections of elements of the same type. Imagine a flexible container that automatically adjusts its size as you add or remove elements, that's essentially what a vector is. 102 | 103 | ### Declaring: There are several ways to declare a vector: 104 | 105 | ```c++ 106 | vector v = {1, 2, 3}; // initializer list 107 | vector vect1{10, 20, 30}; // brace initialization 108 | vector v; // empty vector 109 | vector vect(n, 10); // size n, all values 10 110 | vector vect(10); // size 10 111 | ``` 112 | 113 | You can also create a vector of vectors: 114 | 115 | ```c++ 116 | vector> vec; 117 | v2 = {1, 2, 3}; 118 | vec.push_back(v2); 119 | // This creates a vector containing a single vector with the elements 1, 2, and 3. 120 | 121 | // To create a 5x5 matrix with all elements set to 0, you can use: 122 | vector> v(5, vector(5, 0)); 123 | ``` 124 | 125 | ### Accessing elements 126 | 127 | 1. Using the `at()` method: 128 | 129 | ```c++ 130 | vector num {1, 2, 3, 4, 5}; 131 | cout << "Element at Index 0: " << num.at(0) << endl; 132 | ``` 133 | 134 | 2. Using the `[]` operator: 135 | 136 | ```c++ 137 | cout << num[1]; 138 | ``` 139 | 140 | The `at()` method is preferred over the `[]` operator because it throws an exception if you try to access an element that is out of bounds. The `[]` operator, on the other hand, will simply give you a garbage value. 141 | 142 | ```c++ 143 | vector num {1, 2, 3}; 144 | 145 | // gives garbage value 146 | cout << num[4]; 147 | 148 | // throws an exception 149 | cout << num.at(4); 150 | ``` 151 | 152 | - **front()**: Returns the first element of the vector. 153 | 154 | ```c++ 155 | vector v = {1, 2, 3}; 156 | int first_element = v.front(); // first_element = 1 157 | ``` 158 | 159 | - **back()**: Returns the last element of the vector. 160 | 161 | ```c++ 162 | vector v = {"apple", "banana"}; 163 | string last_element = v.back(); // last_element = "banana" 164 | ``` 165 | 166 | **Note:** `front()` and `back()` throw an exception if the vector is empty. 167 | 168 | --- 169 | 170 | ### Auto keyword 171 | 172 | #### The `auto` keyword in C++ automatically detects and assigns a data type to the variable with which it is used. This can be helpful for making your code more concise and readable. 173 | 174 | Here are some examples of how to use the `auto` keyword: 175 | 176 | ```c++ 177 | auto IntegerVar = 25; // creates integer type variable 178 | auto FloatVar = 26.77; // creates float type variable 179 | auto StringVar = "Hello"; // creates string type variable 180 | auto CharVar = "C"; // creates char type variable 181 | ``` 182 | 183 | You can also use `auto` with references: 184 | 185 | ```c++ 186 | auto number1 = 10; 187 | 188 | auto& number2 = number1; // number2 is a reference to number1 189 | number2 = 20; // Modifies the original value of number1 190 | ``` 191 | 192 | ### Foreach loop 193 | 194 | The `for` loop is a powerful tool in C++ for iterating over a sequence of elements. However, it can be a bit verbose. The `foreach` loop is a more concise way to iterate over a vector: 195 | 196 | ```c++ 197 | for (data_type variable_name : container_name) { 198 | // operations using variable_name 199 | } 200 | ``` 201 | 202 | Here's an example of how to use the `foreach` loop to iterate over a vector of numbers: 203 | 204 | ```c++ 205 | vector numbers = {1, 2, 3, 4, 5}; 206 | 207 | // Example 1 - modifies original vector 208 | for (auto& num : numbers) { 209 | num *= 2; 210 | } 211 | 212 | // Example 2 - doesn't modify original vector 213 | for (auto num : numbers) { 214 | // do something with num 215 | } 216 | ``` 217 | 218 | The `foreach` loop is a great way to make your code more concise and readable. It's especially useful for working with vectors. 219 | 220 | ### Iterators 221 | 222 | #### C++ Vector Iterators : Vector iterators are powerful tools for accessing and manipulating elements within a vector. They act like pointers, but with additional functionalities specific to vector operations. 223 | 224 | ### Declaring Iterators 225 | 226 | The syntax for declaring a vector iterator is: 227 | 228 | ```c++ 229 | vector::iterator iteratorName; 230 | ``` 231 | 232 | Here, `T` represents the type of elements stored in the vector. 233 | 234 | **Example:** 235 | 236 | ```c++ 237 | // Iterator for an int vector 238 | vector::iterator iter1; 239 | 240 | // Iterator for a double vector 241 | vector::iterator iter2; 242 | ``` 243 | 244 | ### Initializing Iterators 245 | 246 | There are two main ways to initialize vector iterators: 247 | 248 | **1. Using `begin()` and `end()`:** 249 | 250 | - `begin()`: Returns an iterator pointing to the first element. 251 | - `end()`: Points to the theoretical element after the last element. 252 | 253 | **Example:** 254 | 255 | ```c++ 256 | vector num = {1, 2, 3}; 257 | vector::iterator iter; 258 | 259 | // iter points to num[0] 260 | iter = num.begin(); 261 | cout << "num[0] = " << *iter << endl; 262 | 263 | // iter points to num[2] 264 | iter = num.end() - 1; 265 | ``` 266 | 267 | **2. Using Reverse Iterators:** 268 | 269 | - `rbegin()`: Returns a reverse iterator pointing to the last element. 270 | - `rend()`: Points to the theoretical element before the first element. 271 | 272 | **Example:** 273 | 274 | ```c++ 275 | vector num = {1, 2, 3}; 276 | auto iter = num.rbegin(); // iter points to num[2] 277 | 278 | iter++; // iter points to num[1] 279 | iter++; // iter points to num[0] 280 | 281 | // Loop through the vector in reverse order 282 | for (auto i = num.rbegin(); i != num.rend(); ++i) { 283 | cout << *i << " "; // prints 3 2 1 284 | } 285 | ``` 286 | 287 | ### Looping through Vectors with Iterators 288 | 289 | Iterators are commonly used in `for` loops to iterate through each element in a vector. 290 | 291 | **Example:** 292 | 293 | ```c++ 294 | vector v = {1, 2, 3, 4, 5}; 295 | 296 | // Forward loop 297 | for (auto i = v.begin(); i != v.end(); ++i) { 298 | cout << *i << " "; // prints 1 2 3 4 5 299 | } 300 | 301 | // Reverse loop 302 | for (auto ir = v.rbegin(); ir != v.rend(); ++ir) { 303 | cout << *ir << " "; // prints 5 4 3 2 1 304 | } 305 | ``` 306 | 307 | Using iterators provides more control and flexibility compared to traditional indexing. They offer functionalities like element insertion, deletion, and distance calculation, making them essential tools for working with vectors in C++. 308 | 309 | --- 310 | 311 | ## Vector Functions 312 | 313 | This section explores some essential functions used to manipulate elements within a vector 314 | 315 | ### Adding Elements: 316 | 317 | - **push_back()**: This function adds new elements to the **back** of the vector. This is the most common way to add elements as it maintains the original order and is efficient for large datasets. 318 | 319 | ```c++ 320 | vector v = {1, 2, 3}; 321 | v.push_back(4); 322 | v.push_back(5); 323 | // v now contains: 1, 2, 3, 4, 5 324 | ``` 325 | 326 | - **emplace_back()**: This function **constructs and adds** a new element to the back of the vector. It avoids unnecessary copies compared to `push_back()` and can be more efficient for large objects. 327 | 328 | ```c++ 329 | vector v = {"apple", "banana"}; 330 | v.emplace_back("orange"); 331 | // v now contains: "apple", "banana", "orange" 332 | ``` 333 | 334 | Let's break down the differences between `insert`, `push_back`, and `emplace_back` functions in a beginner-friendly way. 335 | 336 | ### `push_back`: 337 | 338 | - Usage: 339 | 340 | ```c++ 341 | vector numbers = {1, 2, 3}; 342 | numbers.push_back(4); 343 | ``` 344 | 345 | - **Explanation:** You use `push_back` when you want to add an element to the back (end) of the vector. It's like adding a new item to the end of a line. 346 | 347 | ### `emplace_back`: 348 | 349 | - Usage: 350 | 351 | ```c++ 352 | vector names = {"Alice", "Bob"}; 353 | names.emplace_back("Charlie"); 354 | ``` 355 | 356 | - **Explanation:** `emplace_back` is similar to `push_back`, but it directly constructs the element in place. It's like welcoming a new person into a group by constructing them right there instead of preparing them separately and then adding. 357 | 358 | ### `insert`: 359 | 360 | - **Purpose:** Inserts elements at a specified position in the vector. 361 | 362 | - Usage: 363 | 364 | ```c++ 365 | vector numbers = {1, 2, 3}; 366 | auto it = numbers.begin() + 1; 367 | numbers.insert(it, 4); 368 | ``` 369 | 370 | - **Explanation:** `insert` is like adding elements at a specific point in the vector. It's like squeezing a new person into a group but not necessarily at the end – you decide where they fit. 371 | 372 | **Inserting books (Example):** 373 | 374 | - **Push:** This is like adding a book to the **end** of a specific section. It's quick and easy, like adding a new book to the "Science" section at the end of the other science books. 375 | - **Emplace:** This is like building a book directly on the shelf. It's more efficient for large or complex books, like building a custom scrapbook directly on the shelf instead of bringing it from another location. 376 | 377 | In summary, `push_back` is for adding to the end, `emplace_back` is for constructing and adding to the end. 378 | 379 | - **insert(position, value)**: This function inserts a new element at a specified **position** within the vector. The existing elements at and after the position are shifted to make room for the new element. 380 | 381 | ```c++ 382 | vector v = {1, 2, 3}; 383 | v.insert(v.begin() + 1, 10); // Insert 10 at index 1 384 | // v now contains: 1, 10, 2, 3 385 | ``` 386 | 387 | - **insert(position, n, value)**: This function inserts **n copies** of a specific value at a specified position. 388 | 389 | ```c++ 390 | vector v = {'a', 'b', 'c'}; 391 | v.insert(v.begin() + 1, 2, 'x'); // Insert 2 'x' at index 1 392 | // v now contains: 'a', 'x', 'x', 'b', 'c' 393 | ``` 394 | 395 | - **insert(position, begin, end)**: This function inserts a **range of elements** from another container (begin and end iterators) at a specified position. 396 | 397 | ```c++ 398 | vector v1 = {1, 2, 3}; 399 | vector v2 = {4, 5, 6}; 400 | v1.insert(v1.begin() + 2, v2.begin(), v2.end()); 401 | // v1 now contains: 1, 2, 4, 5, 6, 3 402 | ``` 403 | 404 | ### Removing Elements: 405 | 406 | - **pop_back()**: This function removes the element from the **back** of the vector. This is efficient for large datasets as it doesn't need to shift elements. 407 | 408 | ```c++ 409 | vector v = {"apple", "banana", "orange"}; 410 | v.pop_back(); 411 | // v now contains: "apple", "banana" 412 | ``` 413 | 414 | - **erase(position)**: This function removes the element at a specified **position** from the vector. The remaining elements are shifted down to fill the gap. 415 | 416 | ```c++ 417 | vector v = {1, 2, 3, 4}; 418 | v.erase(v.begin() + 2); // Erase element at index 2 419 | // v now contains: 1, 2, 4 420 | 421 | vector fruits = {"apple", "banana", "orange"}; 422 | fruits.erase(fruits.end() - 1); // Remove the last element ("orange") 423 | 424 | // Fruits now contains: {"apple", "banana"} 425 | ``` 426 | 427 | - **erase(begin, end)**: This function removes a **range of elements** from a specified position (begin and end iterators) within the vector. 428 | 429 | ```c++ 430 | vector v = {'a', 'b', 'c', 'd', 'e'}; 431 | v.erase(v.begin() + 1, v.begin() + 3); // Erase elements from index 1 to 2 (exclusive) 432 | // v now contains: 'a', 'd', 'e' 433 | ``` 434 | 435 | ### Complexity of `erase` 436 | 437 | The time complexity of `erase` depends on the operation: 438 | 439 | - **Single element:** O(n), where n is the number of elements after the erased element. This is because remaining elements need to be shifted to fill the gap. 440 | - **Range of elements:** O(n + m), where n is the number of elements after the erased range and m is the number of erased elements. 441 | - **Erasing the last element:** O(1), as no shifting is necessary. 442 | 443 | ### Other Operations: 444 | 445 | - **swap()**: This function exchanges the contents of two vectors of the same type. This is useful when swapping large datasets as it avoids copying elements. 446 | 447 | ```c++ 448 | vector v1 = {1, 2, 3}; 449 | vector v2 = {4, 5, 6}; 450 | v1.swap(v2); 451 | ``` 452 | 453 | --- 454 | 455 | ## Vector Operations: Size, Capacity, and More 456 | 457 | This section explores additional functions for managing vector size and accessing elements. 458 | 459 | ### Size and Capacity: 460 | 461 | - **size()**: Returns the number of elements currently present in the vector. 462 | 463 | ```c++ 464 | vector v = {1, 2, 3}; 465 | int size = v.size(); // size = 3 466 | ``` 467 | 468 | - **capacity()**: Returns the size of the memory currently allocated to the vector. This may be larger than the actual number of elements. 469 | - allocated : **مخصص** 470 | 471 | ```c++ 472 | vector v; 473 | v.push_back(1); // capacity might be increased 474 | size_t capacity = v.capacity(); // capacity might be > 1 475 | ``` 476 | 477 | ### Resizing and Shrinking: 478 | 479 | - **resize(n)**: Changes the size of the vector to n. If n is smaller than the current size, elements are removed. If it's larger, new elements are added with their default values. 480 | 481 | ```c++ 482 | vector v = {1, 2, 3}; 483 | v.resize(5); // v becomes {1, 2, 3, 0, 0} 484 | ``` 485 | 486 | - **resize(n, value)**: Similar to `resize(n)`, but assigns the specified value to all new elements added. 487 | 488 | ```c++ 489 | vector v = {"apple", "banana"}; 490 | v.resize(5, "orange"); // v becomes {"apple", "banana", "orange", "orange", "orange"} 491 | ``` 492 | 493 | - **shrink_to_fit()**: Reduces the capacity of the vector to match its size. 494 | 495 | ```c++ 496 | vector v(100); // allocated space for 100 elements 497 | v.push_back(1); // only 1 element added 498 | v.shrink_to_fit(); // capacity reduced to 1 499 | ``` 500 | 501 | - **reserve(n)**: Requests that the vector capacity be at least enough to hold n elements. This avoids reallocation when adding elements. 502 | 503 | ```c++ 504 | vector v; 505 | v.reserve(100); // ensures space for at least 100 elements 506 | ``` 507 | 508 | - **empty()**: Checks if the vector contains any elements. 509 | 510 | ```c++ 511 | vector v; 512 | bool is_empty = v.empty(); // is_empty is true 513 | ``` 514 | 515 | ### Modifying Elements: 516 | 517 | - **assign(n, value)**: Replaces all elements in the vector with the specified value. 518 | 519 | ```c++ 520 | vector v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 521 | 522 | v.assign(10, 0); 523 | deb(v); // v = {0,0,0,0,0,0,0,0,0,0} 524 | deb(v.capacity()); 525 | 526 | v.assign(20, 0); // creates a vector of size 20 and assigns 0 to all elements 527 | deb(v); // v = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 528 | deb(v.capacity()); 529 | 530 | v.assign(5, 10); // creates a vector of size 5 and assigns 10 to all elements 531 | deb(v); // v = {10,10,10,10,10} 532 | deb(v.capacity()); // capacity remains same (not lowered) 533 | ``` 534 | 535 | ### Additional Points: 536 | 537 | - `clear()` removes all elements from the vector, but **doesn't change its capacity**. 538 | - `front()` and `back()` are not iterators and **should not be used for loop iterations**. 539 | 540 | By understanding these functions, you can effectively manage the size and content of your vectors, leading to efficient and optimized code. 541 | 542 | ### Advantages of Vector 543 | 544 | - Vectors have contiguous memory allocation, which means that elements are stored in contiguous memory locations. This makes accessing elements faster compared to other containers like lists and deques. (Note that accessing elements in a vector using the [] operator is faster than using the at() function, as the at() function performs a range check.) (list and deque are not contiguous, they are linked list) 545 | - Vectors have a dynamic size, which means that they can be resized during runtime. 546 | - Vectors support random access, which means that elements can be accessed in constant time using an index. 547 | 548 | ### Disadvantages of Vector 549 | 550 | - Inserting or deleting elements in the middle of a vector can be expensive, as all the elements after the insertion or deletion point have to be shifted. 551 | - Vectors have a fixed capacity, which means that if the number of elements exceeds the capacity, the vector has to be reallocated and all the elements have to be copied to the new memory location. This can be expensive in terms of time and memory. 552 | for example, if the capacity of a vector is 10 and we try to insert the 11th element, the vector will be reallocated to a new memory location with a capacity of 20 and all the 10 elements will be copied to the new memory location. 553 | 554 | --- 555 | 556 | --- 557 | 558 | # Pair 559 | 560 | A pair in C++ is a container that holds two heterogeneous values (can be different data types). Pairs provide a convenient way to store a simple key-value pair or return two values from a function. 561 | 562 | The pair is a simple container that can be used with other STL components, such as associative containers and container adaptors, but it is not part of the STL itself. 563 | 564 | ## Declaring Pairs 565 | 566 | To use pairs, include the utility header: 567 | 568 | ```c++ 569 | #include 570 | ``` 571 | 572 | Pairs can be declared in several ways: 573 | 574 | ```c++ 575 | pair myPair; 576 | 577 | pair p1; 578 | 579 | pair p2(‘a’, 5); 580 | 581 | auto p3 = make_pair(true, 3.14); 582 | 583 | pair> p4; 584 | p4.first = 1; 585 | p4.second = { 1, 2, 3 }; 586 | ``` 587 | 588 | ## Accessing Elements 589 | 590 | The elements can be accessed using `.first` and `.second`: 591 | 592 | ```c++ 593 | pair p = {1, "abc"}; 594 | 595 | int x = p.first; // x = 1 596 | string y = p.second; // y = "abc" 597 | ``` 598 | 599 | ## Modifying Elements 600 | 601 | Elements can be modified using `.first` and `.second`: 602 | 603 | ```c++ 604 | p.first = 2; 605 | p.second = "def"; 606 | ``` 607 | 608 | ## Relational Operators 609 | 610 | Pairs support comparison operators like `==`, `!=`, `<=`, `>=` to compare the elements lexicographically: 611 | 612 | ```c++ 613 | pairp1 = {1, 2}; 614 | pairp2 = {2, 1}; 615 | 616 | p1 == p2 // false 617 | p1 != p2 // true 618 | p1 >= p2 // false 619 | ``` 620 | 621 | ## swap() 622 | 623 | The `swap()` function can be used to swap the contents of two pairs efficiently: 624 | 625 | ```c++ 626 | p1.swap(p2); 627 | // p1 = {2, 1} 628 | // p2 = {1, 2} 629 | ``` 630 | 631 | ## Usage in Containers 632 | 633 | Using STL containers, inside each other allows you to create complex data structures that provide flexibility **and** convenience. 634 | 635 | Pairs can be used as elements in other STL containers like vector: 636 | 637 | ```cpp 638 | vector> v; 639 | v.push_back({1, "abc"}); 640 | v.push_back({2, "def"}); 641 | 642 | vector> students; 643 | students.push_back({ 1, "Ahmed" }); 644 | students.push_back({ 2, "Mohamed" }); 645 | students.push_back({ 3, "Ali" }); 646 | students.push_back({ 4, "Mahmoud" }); 647 | for (auto& x : students) { 648 | cout << x.first << " " << x.second << el; 649 | } 650 | ``` 651 | 652 | This allows creating complex data structures easily. 653 | 654 | Overall, pairs provide a simple way to handle two elements together in situations where using a custom class would be overhead. 655 | 656 | --- 657 | 658 | --- 659 | 660 | # Queue 661 | 662 | The queue is a linear data structure that follows the **First-In-First-Out (FIFO)** principle. 663 | 664 | Elements are inserted at the back (enqueue operation) and removed from the front (dequeue operation). 665 | 666 | ## Declaration 667 | 668 | To use queues, include the queue header: 669 | 670 | ```c++ 671 | #include 672 | ``` 673 | 674 | Queues can be declared as: 675 | 676 | ```c++ 677 | queue q; // empty queue of ints 678 | 679 | queue q2({"apple", "banana", "orange"}); 680 | 681 | // WRONG 682 | queue q2 = {1,2,3,4,5}; // wrong way 683 | ``` 684 | 685 | ## Enqueue (Inserting Elements) 686 | 687 | New elements are added using `push()` or `emplace()`: 688 | 689 | ```c++ 690 | q.push(1); // Insert 1 to back 691 | 692 | q.emplace(2); // Construct and insert element 693 | ``` 694 | 695 | - `push()` copies the element to the back. 696 | - `emplace()` constructs the element in-place at the back. 697 | 698 | ## Dequeue (Removing Elements) 699 | 700 | Front elements can be removed using `pop()`: 701 | 702 | ```c++ 703 | q.pop(); // Remove front element 704 | ``` 705 | 706 | ## Front and Back 707 | 708 | - `front()` - Access first element. 709 | - `back()` - Access last element. 710 | 711 | ```c++ 712 | q.front(); 713 | q.back(); 714 | ``` 715 | 716 | **Note:** Calling `front()` or `back()` on an empty queue leads to undefined behavior. 717 | 718 | ## Size and Empty 719 | 720 | - `size()` - Get current size. 721 | - `empty()` - Check if queue is empty. 722 | 723 | ```c++ 724 | int num = q.size(); 725 | 726 | if(q.empty()) { 727 | // queue is empty 728 | } 729 | ``` 730 | 731 | ## Iterating Over Queue 732 | 733 | ```c++ 734 | while(!q.empty()) { 735 | auto x = q.front(); 736 | q.pop(); 737 | 738 | // process element x 739 | } 740 | 741 | // you can't access the second element of the queue directly, you have to pop the first element to access the second element 742 | 743 | ``` 744 | 745 | ## Copy and compare 746 | 747 | ```c++ 748 | queue q; 749 | q.push(1); 750 | q.push(2); 751 | q.push(3); 752 | 753 | queueq2; 754 | // copy the queue 755 | q2 = q; 756 | 757 | // check if two queues are equal or not : 758 | cout << (q == q2) << el; 759 | ``` 760 | 761 | This allows processing elements while removing them. 762 | 763 | The queue provides a convenient FIFO data structure useful in many algorithms and applications. It saves effort over manually handling insertion and removal of elements. 764 | 765 | --- 766 | 767 | --- 768 | 769 | # Deque 770 | 771 | ![Queues Implement Stack Queue Using Deque | Prepbytes Blog](https://prepbytes-misc-images.s3.ap-south-1.amazonaws.com/assets/1658222272577-Image-02.png) 772 | 773 | The deque (double-ended queue) is a linear container that allows fast insertion and deletion at both the beginning and the end. It overcomes limitations of vectors and queues in this aspect. 774 | 775 | ## Declaration 776 | 777 | ```c++ 778 | #include 779 | 780 | deque dq; // Empty deque of ints 781 | deque dq2 = { 1, 2, 3, 4, 5 }; 782 | ``` 783 | 784 | ## Inserting Elements 785 | 786 | - **push_front(x)** - Insert x at the beginning 787 | - **push_back(x)** - Insert x at the end 788 | - **emplace_front(x)** - Construct and insert x at the beginning 789 | - **emplace_back(x)** - Construct and insert x at the end 790 | 791 | ```c++ 792 | dq.push_front(5); 793 | dq.emplace_back(10); 794 | ``` 795 | 796 | ## Removing Elements 797 | 798 | - **pop_front()** - Remove first element 799 | - **pop_back()** - Remove last element 800 | 801 | ```c++ 802 | dq.pop_front(); 803 | dq.pop_back(); 804 | ``` 805 | 806 | ## Access 807 | 808 | - **front()** - Access first element 809 | - **back()** - Access last element 810 | 811 | ```c++ 812 | dq.front(); 813 | dq.back(); 814 | cout << dq[2] << el; 815 | ``` 816 | 817 | ## Size, Empty 818 | 819 | - **size()** - Number of elements 820 | - **empty()** - Check if deque is empty 821 | 822 | ```c++ 823 | int num = dq.size(); 824 | 825 | if(dq.empty()) { 826 | // deque is empty 827 | } 828 | ``` 829 | 830 | ## Iterating Over Deque 831 | 832 | ```c++ 833 | for(auto x : dq) { 834 | // process element x 835 | } 836 | for (int i = 0; i < dq.size(); ++i) { 837 | cout << dq[i] << " "; 838 | } 839 | ``` 840 | 841 | --- 842 | 843 | ## Difference between Deque and Vector 844 | 845 | - **Insertion and Deletion**: Deque allows fast insertion and deletion at both the beginning and the end, while vector only allows efficient insertion and deletion at the end. This makes deque more suitable for scenarios where elements need to be added or removed frequently from both ends. 846 | 847 | - **Memory Allocation**: Deque uses a sequence of individually allocated fixed-size arrays, while vector uses a single dynamically allocated array. This means that deque can grow more efficiently than vector when elements are added or removed frequently. 848 | 849 | - **Random Access**: Both deque and vector support random access to elements using the `[]` operator. However, deque may have slightly slower random access compared to vector due to its internal data structure. 850 | 851 | ## Additional Functions 852 | 853 | In addition to the functions mentioned in the code snippet, here are some additional functions available in the deque container: 854 | 855 | - **insert(pos, x)**: Inserts element x at the specified position pos. 856 | - **erase(pos)**: Removes the element at the specified position pos. 857 | - **clear()**: Removes all elements from the deque. 858 | - **resize(n)**: Resizes the deque to contain n elements. 859 | - **swap(dq2)**: Swaps the contents of two deques. 860 | 861 | These functions provide more flexibility and control over the deque container. 862 | 863 | The deque provides flexibility of vectors and queues together while enabling fast inserts and deletes at both ends. This makes it useful in algorithms requiring dynamic collections. 864 | 865 | --- 866 | 867 | --- 868 | 869 | # Stack 870 | 871 | The stack is a linear data structure that follows the **Last-In-First-Out (LIFO)** principle. The last element inserted is the first one to be removed. 872 | 873 | ![](https://prepinsta.com/wp-content/uploads/2021/01/stack-1.webp) 874 | 875 | ## Declaration 876 | 877 | Include the stack header to use stacks: 878 | 879 | ```c++ 880 | #include 881 | ``` 882 | 883 | Stacks can be declared as: 884 | 885 | ```c++ 886 | stack s; // Empty stack of ints 887 | ``` 888 | 889 | ## Push (Inserting Elements) 890 | 891 | New elements are added using `push()`: 892 | 893 | ```c++ 894 | s.push(5); // Insert 5 to stack top 895 | ``` 896 | 897 | `emplace()` can also be used to construct and insert: 898 | 899 | ```c++ 900 | s.emplace(10); // Construct 10 and push 901 | ``` 902 | 903 | ## Pop (Removing Elements) 904 | 905 | Top elements can be removed using `pop()`: 906 | 907 | ```c++ 908 | s.pop(); // Remove element from top 909 | ``` 910 | 911 | ## Top 912 | 913 | `top()` allows accessing the top element without removing: 914 | 915 | ```c++ 916 | int x = s.top(); // Access top element 917 | ``` 918 | 919 | ## Size and Empty 920 | 921 | - `size()` - Current number of elements 922 | - `empty()` - Check if stack is empty 923 | 924 | ```c++ 925 | int num = s.size(); 926 | 927 | if(s.empty()) { 928 | // stack is empty 929 | } 930 | ``` 931 | 932 | The stack provides a convenient LIFO implementation for algorithms that require element access in reverse order. It handles insertion and removal from one end automatically 933 | 934 | --- 935 | 936 | **Reversing a String** 937 | 938 | ```c++ 939 | string reverseString(string str) { 940 | 941 | stack letters; 942 | 943 | for(char ch : str) { 944 | letters.push(ch); 945 | } 946 | 947 | string result; 948 | 949 | while(!letters.empty()) { 950 | result += letters.top(); 951 | letters.pop(); 952 | } 953 | 954 | return result; 955 | } 956 | 957 | string name = "John"; 958 | string rev = reverseString(name); // rev = "nhoJ" 959 | ``` 960 | 961 | Pushes characters onto stack, then pops them back off to build reversed string. 962 | 963 | --- 964 | 965 | **Checking Balanced Parentheses** 966 | 967 | ```c++ 968 | bool isBalanced(string exp) { 969 | 970 | stack parentheses; 971 | 972 | for(char ch : exp) { 973 | if(ch == '(' || ch == '[') { 974 | parentheses.push(ch); 975 | } 976 | else if(ch == ')' || ch == ']') { 977 | if(parentheses.empty()) { 978 | return false; 979 | } 980 | 981 | if(ch == ')' && parentheses.top() != '(') { 982 | return false; 983 | } 984 | 985 | if(ch == ']' && parentheses.top() != '[') { 986 | return false; 987 | } 988 | 989 | parentheses.pop(); 990 | } 991 | } 992 | 993 | return parentheses.empty(); 994 | } 995 | 996 | string statement = "[code](inside)parenthesis"; 997 | 998 | if(isBalanced(statement)) { 999 | // balanced 1000 | } 1001 | else { 1002 | // not balanced 1003 | } 1004 | ``` 1005 | 1006 | --- 1007 | 1008 | --- 1009 | 1010 | # Struct 1011 | 1012 | A struct in C++ is a user-defined data type that allows grouping together data items of different types. It can contain members that can be accessed through dot notation. 1013 | 1014 | ## Declaring Structs 1015 | 1016 | ```c++ 1017 | struct Student { 1018 | string name; 1019 | int age; 1020 | int id; 1021 | 1022 | // Default Constructor 1023 | Student() { 1024 | name = ""; 1025 | age = 0; 1026 | id = 0; 1027 | } 1028 | 1029 | // Parameterized Constructor 1030 | Student(string n, int a, int i) { 1031 | name = n; 1032 | age = a; 1033 | id = i; 1034 | } 1035 | void print() { 1036 | cout << "Name : " << name << endl; 1037 | cout << "Age : " << age << endl; 1038 | cout << "ID : " << id << endl; 1039 | } 1040 | }; 1041 | 1042 | // Usage 1043 | Student s1; // invokes default constructor, members initialized 1044 | 1045 | Student s2("Ahmed", 20, 1); // invokes parameterized constructor 1046 | ``` 1047 | 1048 | This declares a Student struct. 1049 | 1050 | ## Creating Struct Objects 1051 | 1052 | ```c++ 1053 | Student s1("Ahmed", 20, 1); 1054 | 1055 | Student s2; // Default constructor 1056 | s2.name = "Sarah"; 1057 | ``` 1058 | 1059 | Access members using dot `.` operator. 1060 | 1061 | ## Structs in Containers 1062 | 1063 | Structs can also be used inside other containers: 1064 | 1065 | ```c++ 1066 | vector database; 1067 | 1068 | database.push_back(Student("Ahmed", 20, 1)); 1069 | 1070 | for(auto s : database) { 1071 | // access struct members 1072 | } 1073 | 1074 | pair p; 1075 | Student s2; 1076 | s2.name = "Mohamed"; 1077 | s2.age = 21; 1078 | s2.id = 456; 1079 | p.first = s2; 1080 | p.second = 100; 1081 | cout << p.first.name << endl; 1082 | cout << p.second << endl; 1083 | ``` 1084 | 1085 | ## Struct vs Class 1086 | 1087 | - Members and base classes in structs are public by default while private in classes. 1088 | - Structs can't inherit other structs/classes (no hierarchy) while classes can. 1089 | 1090 | Good for simple data holders without encapsulation needs. 1091 | 1092 | Overall, structs allow customizing and reusing complex data types conveniently. 1093 | 1094 | 1095 | # Set 1096 | Ordered unique elements (Red-Black Tree) 1097 | 1098 | ## Key Characteristics 1099 | - Automatic Sorting: 1100 | - Unique Elements 1101 | ``` cpp 1102 | set s {3,1,2} // Stored as {1,2,3} 1103 | s.insert(3); // No effect becouse '3' exists 1104 | ``` 1105 | ## Declaration & Initialization 1106 | ```cpp 1107 | #include 1108 | using namespace std; 1109 | 1110 | set s1; // Empty set 1111 | set vowels{'a','e','i'}; // Initializer list 1112 | set names = {"Alice", "Bob"}; 1113 | ``` 1114 | ## Iteration 1115 | 1116 | - Forward Iteration 1117 | ``` cpp 1118 | for(auto it = vowels.begin(); it != vowels.end(); ++it) { 1119 | cout << *it << " "; 1120 | } 1121 | 1122 | // Range-based loop 1123 | for(const auto& ch : vowels) { 1124 | cout << ch << " "; 1125 | } 1126 | ``` 1127 | - Reverse Iteration 1128 | ```cpp 1129 | for(auto rit = vowels.rbegin(); rit != vowels.rend(); ++rit) { 1130 | cout << *rit << " "; // Reverse order 1131 | } 1132 | ``` 1133 | ## Set Functions 1134 | ### - Insertion Functions 1135 | 1136 | ```cpp 1137 | insert() // log(n) 1138 | ``` 1139 | 1140 | Adds elements while maintaining order 1141 | 1142 | --- 1143 | 1144 | ```cpp 1145 | emplace() // log(n) 1146 | ``` 1147 | More efficient for complex objects 1148 | 1149 | ```cpp 1150 | set> people; 1151 | people.emplace(25, "Alice"); // Constructs element in-place 1152 | ``` 1153 | --- 1154 | ### - Deletion Functions 1155 | ```cpp 1156 | erase() 1157 | ``` 1158 | Remove elements in different ways: 1159 | ```cpp 1160 | vowels.erase('a'); // By value (returns 1 if removed) 1161 | auto it = vowels.find('e'); 1162 | vowels.erase(it); // By iterator 1163 | vowels.erase(vowels.begin(), vowels.find('i')); // Range 1164 | ``` 1165 | --- 1166 | 1167 | ```cpp 1168 | clear() 1169 | ``` 1170 | Empty the set 1171 | ```cpp 1172 | vowels.clear(); // Size becomes 0 1173 | ``` 1174 | ### - Search Functions 1175 | ```cpp 1176 | find() // log(n) 1177 | ``` 1178 | Returns iterator to element 1179 | ```cpp 1180 | auto it = vowels.find('e'); 1181 | if(it != vowels.end()) { 1182 | cout << "Found: " << *it; 1183 | } 1184 | ``` 1185 |
1186 | 1187 | ```cpp 1188 | lower_bound(); 1189 | upper_bound(); 1190 | ``` 1191 | Range queries 1192 | 1193 | ```cpp 1194 | set nums{10,20,30,40,50}; 1195 | auto lb = nums.lower_bound(25); // First element >= 25 (30) 1196 | auto ub = nums.upper_bound(45); // First element > 45 (50) 1197 | ``` 1198 | --- 1199 | ### Capacity & Status 1200 | ```cpp 1201 | empty(); // true or false 1202 | ``` 1203 | Check if set is empty 1204 | ```cpp 1205 | size() 1206 | ``` 1207 | Get size of the set 1208 | 1209 | ### Performance Table 1210 | | Operation | Time Complexity | 1211 | |-----------|-----------------| 1212 | |insert()| O(log n)| 1213 | |erase() | O(log n)| 1214 | |find() | O(log n)| 1215 | |lower_bound()| O(log n)| 1216 | |iteration| O(n)| 1217 | 1218 | 1219 | # Map 1220 | Key-value pairs with sorted keys: 1221 | ```cpp 1222 | map population { 1223 | {"Tokyo", 37400000}, 1224 | {"Delhi", 29400000} 1225 | }; 1226 | cout << population["Tokyo"]; // 37400000 1227 | ``` 1228 | ## Key Characteristics 1229 | - **Automatic Sorting** : Keys are automatically sorted 1230 | - **Unique keys**: Each key maps to exactly one value 1231 | - **Complexity**: O(log n) for insert/delete/search 1232 | 1233 | ## Declaration & Initialization 1234 | ```cpp 1235 | // Empty map 1236 | map age_map; 1237 | 1238 | // Initialization list 1239 | map prices { 1240 | {"Apple", 2.99}, 1241 | {"Milk", 3.49}, 1242 | {"Bread", 1.99} 1243 | }; 1244 | 1245 | // Complex key type 1246 | map, string> coordinate_labels; 1247 | ``` 1248 | 1249 | ## Iteration Techniques 1250 | **Range-Based Loop** 1251 | ```cpp 1252 | for(const auto& [item, price] : prices) { 1253 | cout << item << ": $" << price << endl; 1254 | } 1255 | ``` 1256 | --- 1257 | Revarse Iteration 1258 | ```cpp 1259 | for(auto rit = prices.rbegin(); rit != prices.rend(); ++rit) { 1260 | cout << rit->first << " costs " << rit->second << endl; 1261 | } 1262 | ``` 1263 | ## Map Methods 1264 | ### Insertion Methods 1265 | ```insert()``` (Returns pair) 1266 | ```cpp 1267 | auto result = prices.insert({"Eggs", 2.49}); 1268 | if(result.second) { 1269 | cout << "Insertion successful"; 1270 | } 1271 | ``` 1272 | ```emplace()``` (In-place construction) 1273 | ```cpp 1274 | prices.emplace("Cheese", 4.99); 1275 | ``` 1276 | ```operator[]``` (Auto-insert if missing) 1277 | ```cpp 1278 | prices["Butter"] = 3.79; // Creates entry if not exists 1279 | ``` 1280 | 1281 | ### Accessing Elements 1282 | Safe Access with `at()` 1283 | ```cpp 1284 | try { 1285 | cout << prices.at("Milk"); // Throws if key missing 1286 | } 1287 | catch(const out_of_range& e) { 1288 | cerr << "Key not found!"; 1289 | } 1290 | ``` 1291 | 1292 | Unsafe Access with `operator[]` 1293 | ```cpp 1294 | double milk_price = prices["Milk"]; // Returns 0.0 if missing! 1295 | // it creates new pair using this missing key with defult value 1296 | ``` 1297 | Check Existence 1298 | ```cpp 1299 | if(prices.find("Pizza") != prices.end()) { 1300 | cout << "Pizza price available"; 1301 | } 1302 | ``` 1303 | 1304 | ### Deletion Methods 1305 | - **Erase by Key** 1306 | ```cpp 1307 | prices.erase("Bread"); // Returns 1 if erased 1308 | 1309 | // Erase multiple keys 1310 | prices.erase("Apple"); 1311 | prices.erase("Milk"); 1312 | ``` 1313 | 1314 | - **Erase by iterator** 1315 | ```cpp 1316 | auto it = prices.find("ExpiredItem"); 1317 | if(it != prices.end()) { 1318 | prices.erase(it); 1319 | } 1320 | ``` 1321 | 1322 | - `clear()` 1323 | ```cpp 1324 | prices.clear(); // Size becomes 0 1325 | ``` 1326 | 1327 | ### Capacity & Status 1328 | ```cpp 1329 | empty(); // true or false 1330 | ``` 1331 | Check if map is empty 1332 | ```cpp 1333 | size() 1334 | ``` 1335 | Get size of the map 1336 | 1337 | ### Performance Table 1338 | | Operation | Time Complexity | 1339 | |-----------|-----------------| 1340 | |insert()| O(log n)| 1341 | |delete() | O(log n)| 1342 | |Search() | O(log n)| 1343 | 1344 | # Priority Queue 1345 | ## Basic Characteristics 1346 | - **Container Adaptor**: Built on top of vectors/deques (default: vector) 1347 | - **Heap Structure**: Max-heap by default (largest element on top) 1348 | - **Key Operations**: 1349 | - `push()` : O(log n) 1350 | - `pop()` : O(log n) 1351 | - `top()` : O(1) 1352 | - **No Iteration**: Only access top element 1353 | 1354 | ## Declaration & Initialization 1355 | Default (Max-Heap) 1356 | ```cpp 1357 | priority_queue max_pq; // empty heap 1358 | vector nums {3,1,4,1,5};// Initialize with values 1359 | priority_queue pq(nums.begin(), nums.end()); 1360 | ``` 1361 | 1362 | **Min-Heap** 1363 | ```cpp 1364 | priority_queue, greater> min_pq; 1365 | ``` 1366 | 1367 | ## Key Operations 1368 | - `push()` 1369 | - `top()` 1370 | - `pop()` 1371 | - `empty()` 1372 | - `size()` 1373 | 1374 | ```cpp 1375 | max_pq.push(5); 1376 | max_pq.push(2); 1377 | max_pq.push(8); // Top becomes 8 1378 | 1379 | cout << "Highest priority: " << max_pq.top(); // 8 1380 | 1381 | max_pq.pop(); // Remove 8, new top is 5 1382 | 1383 | if(!max_pq.empty()) { 1384 | cout << "Elements count: " << max_pq.size(); 1385 | } 1386 | ``` 1387 | 1388 | 1389 | --------------------------------------------------------------------------------