Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
21 views82 pages

C++ Notes

This comprehensive guide covers C++ programming from basics to advanced concepts, focusing on Data Structures and Algorithms (DSA) and competitive programming. It includes topics such as control flow, functions, object-oriented programming, the Standard Template Library (STL), and various data structures and algorithms. Each section is accompanied by clear code examples to enhance understanding and application.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views82 pages

C++ Notes

This comprehensive guide covers C++ programming from basics to advanced concepts, focusing on Data Structures and Algorithms (DSA) and competitive programming. It includes topics such as control flow, functions, object-oriented programming, the Standard Template Library (STL), and various data structures and algorithms. Each section is accompanied by clear code examples to enhance understanding and application.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 82

‭C++ for DSA and Placements: A Comprehensive Guide‬

‭ elcome to your journey into C++! This guide is designed to take you from the very‬
W
‭basics to advanced concepts, with a strong emphasis on what you need for Data‬
‭Structures and Algorithms (DSA) and competitive programming/placement‬
‭preparation. Each concept is explained with clear, concise code examples to help‬
‭solidify your understanding.‬

‭Table of Contents‬
‭1.‬ ‭Introduction to C++‬
‭2.‬ ‭Basic Building Blocks‬
‭○‬ ‭Variables and Data Types‬
‭○‬ ‭Operators‬
‭○‬ ‭Input and Output‬
‭3.‬ ‭Control Flow‬
‭○‬ ‭Conditional Statements (if-else, switch)‬
‭○‬ ‭Loops (for, while, do-while)‬
‭4.‬ ‭Functions‬
‭○‬ ‭Function Declaration and Definition‬
‭○‬ ‭Function Overloading‬
‭5.‬ ‭Arrays and Pointers‬
‭○‬ ‭Arrays‬
‭○‬ ‭Pointers‬
‭○‬ ‭Pointers and Arrays‬
‭○‬ ‭Dynamic Memory Allocation‬
‭6.‬ ‭Strings‬
‭○‬ ‭C-style Strings‬
‭○‬ ‭std::string‬
‭7.‬ ‭Object-Oriented Programming (OOP) in C++‬
‭○‬ ‭Classes and Objects‬
‭○‬ ‭Constructors and Destructors‬
‭○‬ ‭Encapsulation (Access Specifiers)‬
‭○‬ ‭Inheritance‬
‭○‬ ‭Polymorphism (Function Overriding, Virtual Functions)‬
‭○‬ ‭Abstraction (Abstract Classes and Pure Virtual Functions)‬
‭8.‬ ‭Standard Template Library (STL) - Your DSA Powerhouse‬
‭○‬ ‭Containers‬
‭■‬ ‭std::vector‬
‭■‬ ‭std::list‬
‭■‬ ‭std::deque‬
‭■‬ ‭std::map and std::unordered_map‬
‭■‬ ‭std::set and std::unordered_set‬
‭■‬ ‭std::stack‬
‭■‬ ‭std::queue‬
‭■‬ ‭std::priority_queue‬
‭○‬ ‭Iterators‬
‭○‬ ‭Algorithms‬
‭9.‬ ‭Recursion‬
‭10.‬‭Basic Data Structures‬
‭○‬ ‭Linked Lists‬
‭○‬ ‭Stacks (Manual Implementation)‬
‭○‬ ‭Queues (Manual Implementation)‬
‭○‬ ‭Trees (Binary Tree, Binary Search Tree)‬
‭○‬ ‭Graphs (Adjacency List/Matrix)‬
‭11.‬ ‭Basic Algorithms‬
‭○‬ ‭Searching Algorithms (Linear, Binary)‬
‭○‬ ‭Sorting Algorithms (Bubble, Selection, Insertion, Merge, Quick)‬
‭○‬ ‭Hashing‬
‭12.‬‭File I/O‬

‭1. Introduction to C++‬


‭ ++ is a powerful, general-purpose programming language that supports procedural,‬
C
‭object-oriented, and generic programming. It's an extension of the C language.‬

‭Why C++ for DSA?‬


‭●‬ ‭Performance:‬‭C++ offers high performance due to low-level‬‭memory‬
‭ anagement and direct hardware access.‬
m
‭ ‬ ‭STL:‬‭The Standard Template Library provides ready-to-use‬‭data structures and‬

‭algorithms, making development faster and more efficient.‬
‭●‬ ‭Object-Oriented:‬‭Helps in organizing complex code‬‭into modular, reusable‬
‭components.‬
‭●‬ ‭Widely Used:‬‭A popular choice in competitive programming‬‭and for‬
‭systems-level development.‬
‭Basic Structure of a C++ Program:‬

‭#include <iostream> // Header for input/output operations‬

‭// The main function where program execution begins‬


‭int main() {‬
‭// Print "Hello, World!" to the console‬
‭std::cout << "Hello, World!" << std::endl;‬

/‭ / Return 0 to indicate successful execution‬


‭return 0;‬
‭}‬

‭●‬ ‭#include <iostream>: Includes the iostream library, which provides functionalities‬
f‭ or input and output operations (like std::cout).‬
‭ ‬ ‭int main(): The entry point of every C++ program. Execution starts here.‬

‭●‬ ‭std::cout: Used to print output to the console. std::endl inserts a new line and‬
‭flushes the output buffer.‬
‭●‬ ‭return 0;: Indicates that the program executed successfully.‬

‭2. Basic Building Blocks‬


‭2.1. Variables and Data Types‬
‭ ariables are named storage locations that can hold data. Data types specify the type‬
V
‭of data a variable can hold.‬

‭ include <iostream>‬
#
‭#include <string> // Required for std::string‬

‭int main() {‬
‭// Integer type: Stores whole numbers‬
‭int age = 25;‬
‭std::cout << "Age: " << age << std::endl;‬

/‭ / Floating-point types: Stores numbers with decimal points‬


‭float temperature = 98.6f; // 'f' suffix for float literal‬
‭double pi = 3.1415926535;‬
‭std::cout << "Temperature: " << temperature << std::endl;‬
‭std::cout << "Pi: " << pi << std::endl;‬

/‭ / Character type: Stores single characters‬


‭char grade = 'A';‬
‭std::cout << "Grade: " << grade << std::endl;‬
/‭ / Boolean type: Stores true or false‬
‭bool isStudent = true;‬
‭std::cout << "Is Student: " << isStudent << std::endl; // Prints 1 for true, 0 for false‬

/‭ / String type: Stores sequences of characters (text)‬


‭std::string name = "Alice";‬
‭std::cout << "Name: " << name << std::endl;‬

/‭ / Long long: For very large integers‬


‭long long bigNumber = 123456789012345LL; // 'LL' suffix for long long literal‬
‭std::cout << "Big Number: " << bigNumber << std::endl;‬

/‭ / Sizeof operator: Get the size of a data type in bytes‬


‭std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;‬
‭std::cout << "Size of double: " << sizeof(double) << " bytes" << std::endl;‬

‭return 0;‬
‭}‬

‭2.2. Operators‬
‭Operators perform operations on variables and values.‬

‭#include <iostream>‬

‭int main() {‬
‭int a = 10, b = 3;‬

/‭ / Arithmetic Operators‬
‭std::cout << "--- Arithmetic Operators ---" << std::endl;‬
‭std::cout << "a + b = " << (a + b) << std::endl; // Addition‬
‭std::cout << "a - b = " << (a - b) << std::endl; // Subtraction‬
‭std::cout << "a * b = " << (a * b) << std::endl; // Multiplication‬
‭std::cout << "a / b = " << (a / b) << std::endl; // Division (integer division)‬
‭std::cout << "a % b = " << (a % b) << std::endl; // Modulo (remainder)‬

/‭ / Relational Operators (return true/false)‬


‭std::cout << "\n--- Relational Operators ---" << std::endl;‬
‭std::cout << "a == b: " << (a == b) << std::endl; // Equal to‬
s‭ td::cout << "a != b: " << (a != b) << std::endl; // Not equal to‬
‭std::cout << "a > b: " << (a > b) << std::endl; // Greater than‬
‭std::cout << "a < b: " << (a < b) << std::endl; // Less than‬
‭std::cout << "a >= b: " << (a >= b) << std::endl; // Greater than or equal to‬
‭std::cout << "a <= b: " << (a <= b) << std::endl; // Less than or equal to‬

/‭ / Logical Operators (work with boolean values)‬


‭bool x = true, y = false;‬
‭std::cout << "\n--- Logical Operators ---" << std::endl;‬
‭std::cout << "x && y: " << (x && y) << std::endl; // Logical AND‬
‭std::cout << "x || y: " << (x || y) << std::endl; // Logical OR‬
‭std::cout << "!x: " << (!x) << std::endl; // Logical NOT‬

/‭ / Assignment Operators‬
‭std::cout << "\n--- Assignment Operators ---" << std::endl;‬
‭int c = 5;‬
‭std::cout << "Initial c: " << c << std::endl;‬
‭c += 2; // c = c + 2;‬
‭std::cout << "c after c += 2: " << c << std::endl;‬
‭c *= 3; // c = c * 3;‬
‭std::cout << "c after c *= 3: " << c << std::endl;‬

/‭ / Increment/Decrement Operators‬
‭std::cout << "\n--- Increment/Decrement Operators ---" << std::endl;‬
‭int i = 5;‬
‭std::cout << "Initial i: " << i << std::endl;‬
‭std::cout << "i++ (post-increment): " << i++ << std::endl; // Uses current value, then‬
‭increments‬
‭std::cout << "i after i++: " << i << std::endl;‬
‭std::cout << "++i (pre-increment): " << ++i << std::endl; // Increments, then uses new‬
‭value‬
‭std::cout << "i after ++i: " << i << std::endl;‬

‭return 0;‬
‭}‬

‭2.3. Input and Output‬


‭std::cin is used to get input from the user.‬

‭ include <iostream>‬
#
‭#include <string>‬

‭int main() {‬
‭std::string name;‬
‭int age;‬

/‭ / Prompt the user for input‬


‭std::cout << "Enter your name: ";‬
‭// Read a single word (stops at whitespace)‬
‭std::cin >> name;‬

s‭ td::cout << "Enter your age: ";‬


‭// Read an integer‬
‭std::cin >> age;‬

‭std::cout << "Hello, " << name << "! You are " << age << " years old." << std::endl;‬

/‭ / To read a full line with spaces, use std::getline‬


‭std::cin.ignore(); // Clear the buffer after previous std::cin >> age;‬
‭std::cout << "Tell me something about yourself (press Enter when done): ";‬
‭std::string about;‬
‭std::getline(std::cin, about); // Reads until newline character‬
‭std::cout << "You said: " << about << std::endl;‬

‭return 0;‬
‭}‬

‭3. Control Flow‬


‭Control flow statements determine the order in which instructions are executed.‬

‭3.1. Conditional Statements (if-else, switch)‬


‭#include <iostream>‬

‭int main() {‬
‭int score = 75;‬
/‭ / if-else if-else ladder‬
‭std::cout << "--- if-else if-else ---" << std::endl;‬
‭if (score >= 90) {‬
‭std::cout << "Grade: A" << std::endl;‬
‭} else if (score >= 80) {‬
‭std::cout << "Grade: B" << std::endl;‬
‭} else if (score >= 70) {‬
‭std::cout << "Grade: C" << std::endl;‬
‭} else {‬
‭std::cout << "Grade: F" << std::endl;‬
‭}‬

/‭ / switch statement (for discrete values)‬


‭std::cout << "\n--- switch statement ---" << std::endl;‬
‭char choice = 'B';‬
‭switch (choice) {‬
‭case 'A':‬
‭std::cout << "Excellent choice!" << std::endl;‬
‭break; // Exit switch after this case‬
‭case 'B':‬
‭std::cout << "Good choice!" << std::endl;‬
‭break;‬
‭case 'C':‬
‭std::cout << "Okay choice." << std::endl;‬
‭break;‬
‭default: // Executed if no other case matches‬
‭std::cout << "Invalid choice." << std::endl;‬
‭break;‬
‭}‬

‭return 0;‬
‭}‬

‭3.2. Loops (for, while, do-while)‬


‭#include <iostream>‬

‭int main() {‬
/‭ / for loop: When you know the number of iterations‬
‭std::cout << "--- for loop (counting 1 to 5) ---" << std::endl;‬
‭for (int i = 1; i <= 5; ++i) {‬
‭std::cout << i << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / while loop: When the number of iterations is unknown, based on a condition‬


‭std::cout << "\n--- while loop (countdown from 3) ---" << std::endl;‬
‭int count = 3;‬
‭while (count > 0) {‬
‭std::cout << count << " ";‬
‭count--;‬
‭}‬
‭std::cout << std::endl;‬

/‭ / do-while loop: Executes at least once, then checks the condition‬


‭std::cout << "\n--- do-while loop (always runs once) ---" << std::endl;‬
‭int num = 0;‬
‭do {‬
‭std::cout << "Enter a positive number: ";‬
‭std::cin >> num;‬
‭} while (num <= 0);‬
‭std::cout << "You entered: " << num << std::endl;‬

/‭ / break and continue‬


‭std::cout << "\n--- break and continue ---" << std::endl;‬
‭for (int i = 1; i <= 10; ++i) {‬
‭if (i == 5) {‬
‭std::cout << "Skipping 5 (continue)" << std::endl;‬
‭continue; // Skip the rest of the current iteration‬
‭}‬
‭if (i == 8) {‬
‭std::cout << "Breaking loop at 8" << std::endl;‬
‭break; // Exit the loop entirely‬
‭}‬
‭std::cout << i << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭return 0;‬
‭}‬

‭4. Functions‬
‭ unctions are blocks of code that perform a specific task. They promote code‬
F
‭reusability and modularity.‬

‭4.1. Function Declaration and Definition‬


‭#include <iostream>‬

/‭ / Function Declaration (Prototype): Tells the compiler about the function‬


‭int add(int a, int b); // Function signature: return_type name(parameters);‬

/‭ / Function Definition: Contains the actual code of the function‬


‭void greet(std::string name) { // void means no return value‬
‭std::cout << "Hello, " << name << "!" << std::endl;‬
‭}‬

‭int main() {‬
‭greet("Alice"); // Calling the greet function‬

i‭nt sum_result = add(5, 7); // Calling the add function‬


‭std::cout << "Sum: " << sum_result << std::endl;‬

‭return 0;‬
‭}‬

/‭ / Definition of the add function (can be after main if declared before)‬


‭int add(int a, int b) {‬
‭return a + b;‬
‭}‬

‭4.2. Function Overloading‬


‭ llows multiple functions with the same name but different parameters (different‬
A
‭number or different types of parameters).‬
‭ include <iostream>‬
#
‭#include <string>‬

/‭ / Function to add two integers‬


‭int add(int a, int b) {‬
‭std::cout << "Adding two integers: ";‬
‭return a + b;‬
‭}‬

/‭ / Overloaded function to add three integers‬


‭int add(int a, int b, int c) {‬
‭std::cout << "Adding three integers: ";‬
‭return a + b + c;‬
‭}‬

/‭ / Overloaded function to concatenate two strings‬


‭std::string add(std::string s1, std::string s2) {‬
‭std::cout << "Concatenating two strings: ";‬
‭return s1 + s2;‬
‭}‬

‭int main() {‬
‭std::cout << add(10, 20) << std::endl;‬
‭std::cout << add(1, 2, 3) << std::endl;‬
‭std::cout << add("Hello, ", "World!") << std::endl;‬

‭return 0;‬
‭}‬

‭5. Arrays and Pointers‬


‭5.1. Arrays‬
‭ rrays are collections of elements of the same data type stored in contiguous memory‬
A
‭locations.‬

‭ include <iostream>‬
#
‭#include <array> // For std::array (fixed-size, safer)‬
‭#include <vector> // For std::vector (dynamic-size, more flexible)‬
‭int main() {‬
‭// C-style array: Fixed size at compile time‬
‭int numbers[5]; // Declares an array of 5 integers (indices 0 to 4)‬

/‭ / Initialize elements‬
‭numbers[0] = 10;‬
‭numbers[1] = 20;‬
‭numbers[2] = 30;‬
‭numbers[3] = 40;‬
‭numbers[4] = 50;‬

/‭ / Access elements using index‬


‭std::cout << "First element: " << numbers[0] << std::endl;‬
‭std::cout << "Third element: " << numbers[2] << std::endl;‬

/‭ / Iterate through the array‬


‭std::cout << "All elements of C-style array: ";‬
‭for (int i = 0; i < 5; ++i) {‬
‭std::cout << numbers[i] << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Initialization during declaration‬


‭int scores[] = {100, 95, 88, 72}; // Size is automatically determined (4 elements)‬
‭std::cout << "Scores: ";‬
‭for (int score : scores) { // Range-based for loop (C++11 and later)‬
‭std::cout << score << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / std::array (C++11): Fixed size, but behaves like a container (safer)‬


‭std::array<int, 3> arr_std = {1, 2, 3};‬
‭std::cout << "std::array elements: ";‬
‭for (int val : arr_std) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭std::cout << "Size of std::array: " << arr_std.size() << std::endl;‬
/‭ / 2D Arrays (Matrices)‬
‭int matrix[2][3] = {‬
‭{1, 2, 3},‬
‭{4, 5, 6}‬
‭};‬
‭std::cout << "\n2D Matrix:" << std::endl;‬
‭for (int i = 0; i < 2; ++i) {‬
‭for (int j = 0; j < 3; ++j) {‬
‭std::cout << matrix[i][j] << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭}‬

‭return 0;‬
‭}‬

‭5.2. Pointers‬
‭Pointers are variables that store memory addresses of other variables.‬

‭#include <iostream>‬

‭int main() {‬
‭int var = 10; // Declare an integer variable‬

/‭ / Declare a pointer variable 'ptr' that can store the address of an integer‬
‭int* ptr;‬

/‭ / Store the address of 'var' into 'ptr' using the address-of operator (&)‬
‭ptr = &var;‬

s‭ td::cout << "Value of var: " << var << std::endl;‬


‭std::cout << "Address of var: " << &var << std::endl; // Address in memory‬
‭std::cout << "Value of ptr (address stored): " << ptr << std::endl; // Same as &var‬

/‭ / Dereference operator (*): Access the value at the address stored in the pointer‬
‭std::cout << "Value at address stored in ptr (*ptr): " << *ptr << std::endl;‬
/‭ / Changing value using pointer‬
‭*ptr = 20; // Change the value at the address pointed to by ptr‬
‭std::cout << "New value of var: " << var << std::endl; // var is now 20‬

/‭ / Null pointer: A pointer that doesn't point to any valid memory location‬
‭int* nullPtr = nullptr; // C++11 way (preferred over NULL)‬
‭// int* nullPtr = NULL; // Old C-style way‬
‭std::cout << "Value of nullPtr: " << nullPtr << std::endl;‬

/‭ / Pointer to a pointer‬
‭int** ptrToPtr = &ptr;‬
‭std::cout << "Value of ptrToPtr (address of ptr): " << ptrToPtr << std::endl;‬
‭std::cout << "Value at *ptrToPtr (value of ptr): " << *ptrToPtr << std::endl;‬
‭std::cout << "Value at **ptrToPtr (value of var): " << **ptrToPtr << std::endl;‬

‭return 0;‬
‭}‬

‭5.3. Pointers and Arrays‬


‭An array name can be treated as a pointer to its first element.‬

‭#include <iostream>‬

‭int main() {‬
‭int arr[] = {10, 20, 30, 40, 50};‬
‭int size = sizeof(arr) / sizeof(arr[0]);‬

/‭ / Array name 'arr' itself acts as a pointer to the first element‬


‭std::cout << "Address of first element (arr): " << arr << std::endl;‬
‭std::cout << "Address of first element (&arr[0]): " << &arr[0] << std::endl;‬

/‭ / Accessing elements using pointer arithmetic‬


‭std::cout << "Elements using pointer arithmetic:" << std::endl;‬
‭for (int i = 0; i < size; ++i) {‬
‭// *(arr + i) means "value at (address of arr[0] + i * sizeof(int))"‬
‭std::cout << *(arr + i) << " ";‬
‭}‬
‭std::cout << std::endl;‬
/‭ / A pointer variable can also point to the array‬
‭int* p = arr; // p now points to arr[0]‬

s‭ td::cout << "Elements using pointer variable 'p':" << std::endl;‬


‭for (int i = 0; i < size; ++i) {‬
‭std::cout << p[i] << " "; // Array-like access using pointer‬
‭}‬
‭std::cout << std::endl;‬

s‭ td::cout << "Elements using incrementing pointer 'p':" << std::endl;‬


‭for (int i = 0; i < size; ++i) {‬
‭std::cout << *p << " "; // Dereference current position‬
‭p++; // Move pointer to next element‬
‭}‬
‭std::cout << std::endl;‬
‭// Note: 'p' now points past the end of the array. 'arr' still points to the beginning.‬

‭return 0;‬
‭}‬

‭5.4. Dynamic Memory Allocation‬


‭ llocating memory during program execution using new and delete. This is crucial for‬
A
‭DSA when array sizes are not known at compile time.‬

‭#include <iostream>‬

‭int main() {‬
‭int size;‬
‭std::cout << "Enter the number of elements: ";‬
‭std::cin >> size;‬

/‭ / Dynamically allocate an array of integers‬


‭// 'new int[size]' requests memory for 'size' integers and returns a pointer to the first‬
‭element.‬
‭int* dynamicArray = new int[size];‬

‭// Check if allocation was successful (optional but good practice)‬


‭if (dynamicArray == nullptr) {‬
‭std::cout << "Memory allocation failed!" << std::endl;‬
‭return 1; // Indicate error‬
‭}‬

/‭ / Initialize and print elements‬


‭std::cout << "Enter " << size << " integers:" << std::endl;‬
‭for (int i = 0; i < size; ++i) {‬
‭std::cout << "Element " << i + 1 << ": ";‬
‭std::cin >> dynamicArray[i]; // Access like a normal array‬
‭}‬

s‭ td::cout << "Dynamically allocated array elements: ";‬


‭for (int i = 0; i < size; ++i) {‬
‭std::cout << dynamicArray[i] << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Deallocate the memory to prevent memory leaks‬


‭// 'delete[]' is used for arrays, 'delete' for single objects.‬
‭delete[] dynamicArray;‬
‭dynamicArray = nullptr; // Set pointer to nullptr after deallocation (good practice)‬

/‭ / Dynamically allocate a single integer‬


‭int* singleInt = new int;‬
‭*singleInt = 100;‬
‭std::cout << "Dynamically allocated single int: " << *singleInt << std::endl;‬
‭delete singleInt;‬
‭singleInt = nullptr;‬

‭return 0;‬
‭}‬

‭6. Strings‬
‭6.1. C-style Strings (Character Arrays)‬
‭Null-terminated character arrays. Less safe and flexible than std::string.‬
‭ include <iostream>‬
#
‭#include <cstring> // For C-style string functions like strlen, strcpy‬

‭int main() {‬
‭// Declaring and initializing a C-style string‬
‭char greeting[20] = "Hello"; // Size 20, includes space for null terminator '\0'‬
‭char name[] = "World"; // Size automatically determined (6 including '\0')‬

s‭ td::cout << "Greeting: " << greeting << std::endl;‬


‭std::cout << "Name: " << name << std::endl;‬

/‭ / Concatenating strings: strcat_s (safer version) or strcat (less safe)‬


‭// For competitive programming, often strcat is used, but for general code, use safer‬
‭versions‬
‭char combined[50];‬
‭strcpy(combined, greeting); // Copy "Hello" to combined‬
‭strcat(combined, " "); // Append space‬
‭strcat(combined, name); // Append "World"‬
‭strcat(combined, "!"); // Append "!"‬
‭std::cout << "Combined: " << combined << std::endl;‬

/‭ / Length of string: strlen‬


‭std::cout << "Length of combined: " << strlen(combined) << std::endl;‬

/‭ / Comparing strings: strcmp (returns 0 if equal)‬


‭char str1[] = "apple";‬
‭char str2[] = "banana";‬
‭char str3[] = "apple";‬

s‭ td::cout << "Comparing 'apple' and 'banana': " << strcmp(str1, str2) << std::endl; //‬
‭Non-zero‬
‭std::cout << "Comparing 'apple' and 'apple': " << strcmp(str1, str3) << std::endl; //‬
‭Zero‬

‭return 0;‬
‭}‬

‭6.2. std::string‬
‭ he preferred way to handle strings in modern C++. Safer, more flexible, and easier to‬
T
‭use.‬

‭ include <iostream>‬
#
‭#include <string> // Required for std::string‬

‭int main() {‬
‭// Declaring and initializing std::string‬
‭std::string s1 = "Hello";‬
‭std::string s2("World");‬
‭std::string s3; // Empty string‬

s‭ td::cout << "s1: " << s1 << std::endl;‬


‭std::cout << "s2: " << s2 << std::endl;‬

/‭ / Concatenation using + operator‬


‭s3 = s1 + " " + s2 + "!";‬
‭std::cout << "s3 (concatenated): " << s3 << std::endl;‬

/‭ / Appending using += operator‬


‭std::string message = "Good";‬
‭message += " Morning";‬
‭std::cout << "Message: " << message << std::endl;‬

/‭ / Length of string‬
‭std::cout << "Length of s3: " << s3.length() << std::endl; // or s3.size()‬

/‭ / Accessing characters (like an array)‬


‭std::cout << "First character of s3: " << s3[0] << std::endl;‬
‭std::cout << "Last character of s3: " << s3[s3.length() - 1] << std::endl;‬

/‭ / Substring‬
‭std::string sub = s3.substr(7, 5); // Start index 7, length 5‬
‭std::cout << "Substring (World): " << sub << std::endl;‬

/‭ / Finding a substring‬
‭size_t pos = s3.find("World"); // Returns position or std::string::npos if not found‬
‭if (pos != std::string::npos) {‬
‭std::cout << "'World' found at position: " << pos << std::endl;‬
‭} else {‬
‭std::cout << "'World' not found." << std::endl;‬
‭}‬

/‭ / Comparison‬
‭std::string strA = "apple";‬
‭std::string strB = "banana";‬
‭std::string strC = "apple";‬

‭if (strA == strC) {‬


‭std::cout << "strA and strC are equal." << std::endl;‬
‭}‬
‭if (strA != strB) {‬
‭std::cout << "strA and strB are not equal." << std::endl;‬
‭}‬

/‭ / Input with spaces (using getline)‬


‭std::string full_line;‬
‭std::cout << "Enter a full line of text: ";‬
‭std::getline(std::cin >> std::ws, full_line); // std::ws consumes leading whitespace‬
‭std::cout << "You entered: " << full_line << std::endl;‬

‭return 0;‬
‭}‬

‭7. Object-Oriented Programming (OOP) in C++‬


‭ OP is a programming paradigm based on the concept of "objects", which can‬
O
‭contain data and code. The four pillars are Encapsulation, Inheritance, Polymorphism,‬
‭and Abstraction.‬

‭7.1. Classes and Objects‬


‭●‬ ‭Class:‬‭A blueprint or template for creating objects.‬‭It defines the properties (data‬
‭ embers) and behaviors (member functions) that objects of its type will have.‬
m
‭ ‬ ‭Object:‬‭An instance of a class.‬

‭ include <iostream>‬
#
‭#include <string>‬
/‭ / Define a class named 'Car'‬
‭class Car {‬
‭public: // Access specifier: members are accessible from outside the class‬
‭// Data members (attributes)‬
‭std::string brand;‬
‭std::string model;‬
‭int year;‬

/‭ / Member functions (behaviors)‬


‭void displayInfo() {‬
‭std::cout << "Brand: " << brand << ", Model: " << model << ", Year: " << year <<‬
‭std::endl;‬
‭}‬

‭void startEngine() {‬
‭std::cout << model << " engine started!" << std::endl;‬
‭}‬
‭}; // Don't forget the semicolon after class definition!‬

‭int main() {‬
‭// Create objects (instances) of the Car class‬
‭Car car1; // Object 1‬
‭Car car2; // Object 2‬

/‭ / Access data members and assign values using the dot operator (.)‬
‭car1.brand = "Toyota";‬
‭car1.model = "Camry";‬
‭car1.year = 2020;‬

‭ ar2.brand = "Honda";‬
c
‭car2.model = "Civic";‬
‭car2.year = 2022;‬

/‭ / Call member functions using the dot operator‬


‭std::cout << "Car 1 Info: ";‬
‭car1.displayInfo();‬
‭car1.startEngine();‬

‭std::cout << "Car 2 Info: ";‬


‭ ar2.displayInfo();‬
c
‭car2.startEngine();‬

‭return 0;‬
‭}‬

‭7.2. Constructors and Destructors‬


‭●‬ ‭Constructor:‬‭A special member function that is automatically‬‭called when an‬
‭ bject is created. Used for initializing object's data members. Has the same name‬
o
‭as the class and no return type.‬
‭ ‬ ‭Destructor:‬‭A special member function that is automatically‬‭called when an‬

‭object is destroyed (goes out of scope). Used for cleaning up resources (e.g.,‬
‭freeing dynamically allocated memory). Has the same name as the class, prefixed‬
‭with a tilde (~), and no return type or parameters.‬
‭ include <iostream>‬
#
‭#include <string>‬

‭ lass Dog {‬
c
‭public:‬
‭std::string name;‬
‭int age;‬

/‭ / Default Constructor (no parameters)‬


‭Dog() {‬
‭name = "Unknown";‬
‭age = 0;‬
‭std::cout << "Default constructor called for " << name << std::endl;‬
‭}‬

/‭ / Parameterized Constructor‬
‭Dog(std::string n, int a) {‬
‭name = n;‬
‭age = a;‬
‭std::cout << "Parameterized constructor called for " << name << std::endl;‬
‭}‬

‭// Copy Constructor (creates a new object as a copy of an existing one)‬


‭Dog(const Dog& other) {‬
‭name = "Copy of " + other.name;‬
‭age = other.age;‬
‭std::cout << "Copy constructor called for " << name << std::endl;‬
‭}‬

/‭ / Destructor‬
‭~Dog() {‬
‭std::cout << "Destructor called for " << name << std::endl;‬
‭}‬

‭void bark() {‬
‭std::cout << name << " says Woof!" << std::endl;‬
‭}‬
‭};‬

‭int main() {‬
‭// Calls default constructor‬
‭Dog myDog;‬
‭myDog.bark();‬

‭std::cout << "\n--- Creating another dog ---\n";‬

/‭ / Calls parameterized constructor‬


‭Dog buddy("Buddy", 3);‬
‭buddy.bark();‬

‭std::cout << "\n--- Creating a copy ---\n";‬

/‭ / Calls copy constructor‬


‭Dog anotherDog = buddy; // Or Dog anotherDog(buddy);‬
‭anotherDog.bark();‬

s‭ td::cout << "\n--- End of main function ---\n";‬


‭// Objects will be destroyed here in reverse order of creation (anotherDog, buddy,‬
‭myDog)‬
‭return 0;‬
‭}‬
‭7.3. Encapsulation (Access Specifiers)‬
‭ ncapsulation is the bundling of data (attributes) and methods (functions) that‬
E
‭operate on the data into a single unit (class). It also involves restricting direct access‬
‭to some of an object's components, which is achieved using access specifiers:‬
‭●‬ ‭public: Members are accessible from anywhere.‬
‭●‬ ‭private: Members are only accessible from within the same class. (Default for‬
‭ lasses)‬
c
‭ ‬ ‭protected: Members are accessible from within the same class and by derived‬

‭classes.‬
‭ include <iostream>‬
#
‭#include <string>‬

‭ lass BankAccount {‬
c
‭private: // Private members (data hiding)‬
‭std::string accountNumber;‬
‭double balance;‬
‭std::string ownerName;‬

‭public: // Public interface (controlled access)‬


‭// Constructor‬
‭BankAccount(std::string accNum, std::string owner, double initialBalance) {‬
‭accountNumber = accNum;‬
‭ownerName = owner;‬
‭if (initialBalance >= 0) {‬
‭balance = initialBalance;‬
‭} else {‬
‭balance = 0;‬
‭std::cout << "Initial balance cannot be negative. Setting to 0." << std::endl;‬
‭}‬
‭std::cout << "Account created for " << ownerName << std::endl;‬
‭}‬

/‭ / Public methods to interact with private data (getters and setters)‬


‭void deposit(double amount) {‬
‭if (amount > 0) {‬
‭balance += amount;‬
s‭ td::cout << "Deposited: $" << amount << ". New balance: $" << balance <<‬
‭std::endl;‬
‭} else {‬
‭std::cout << "Deposit amount must be positive." << std::endl;‬
‭}‬
‭}‬

‭void withdraw(double amount) {‬


‭if (amount > 0 && balance >= amount) {‬
‭balance -= amount;‬
‭std::cout << "Withdrew: $" << amount << ". New balance: $" << balance <<‬
‭std::endl;‬
‭} else if (amount <= 0) {‬
‭std::cout << "Withdrawal amount must be positive." << std::endl;‬
‭} else {‬
‭std::cout << "Insufficient balance. Current balance: $" << balance << std::endl;‬
‭}‬
‭}‬

‭ ouble getBalance() const { // 'const' means this method does not modify object's‬
d
‭state‬
‭return balance;‬
‭}‬

‭std::string getOwnerName() const {‬


‭return ownerName;‬
‭}‬
‭};‬

‭int main() {‬
‭BankAccount myAccount("123456789", "John Doe", 1000.0);‬

/‭ / Direct access to private members is NOT allowed:‬


‭// myAccount.balance = 5000.0; // ERROR! 'balance' is private‬

s‭ td::cout << "Current balance for " << myAccount.getOwnerName() << ": $" <<‬
‭myAccount.getBalance() << std::endl;‬

‭myAccount.deposit(200.0);‬
‭ yAccount.withdraw(150.0);‬
m
‭myAccount.withdraw(2000.0); // Insufficient balance‬

‭std::cout << "Final balance: $" << myAccount.getBalance() << std::endl;‬

‭return 0;‬
‭}‬

‭7.4. Inheritance‬
I‭nheritance allows a new class (derived or child class) to inherit properties and‬
‭behaviors from an existing class (base or parent class). This promotes code‬
‭reusability.‬

‭ include <iostream>‬
#
‭#include <string>‬

/‭ / Base class‬
‭class Animal {‬
‭public:‬
‭std::string name;‬
‭int age;‬

‭Animal(std::string n, int a) : name(n), age(a) { // Initializer list for constructor‬


‭std::cout << "Animal constructor called for " << name << std::endl;‬
‭}‬

‭void eat() {‬
‭std::cout << name << " is eating." << std::endl;‬
‭}‬

‭void sleep() {‬
‭std::cout << name << " is sleeping." << std::endl;‬
‭}‬
‭};‬

/‭ / Derived class 'Dog' inherits from 'Animal' using 'public' inheritance‬


‭class Dog : public Animal {‬
‭public:‬
‭std::string breed;‬

/‭ / Dog constructor calls Animal's constructor using initializer list‬


‭Dog(std::string n, int a, std::string b) : Animal(n, a), breed(b) {‬
‭std::cout << "Dog constructor called for " << name << std::endl;‬
‭}‬

‭void bark() {‬
‭std::cout << name << " (" << breed << ") says Woof!" << std::endl;‬
‭}‬

‭// Dog can also use inherited methods like eat() and sleep()‬
‭};‬

/‭ / Another derived class 'Cat'‬


‭class Cat : public Animal {‬
‭public:‬
‭Cat(std::string n, int a) : Animal(n, a) {‬
‭std::cout << "Cat constructor called for " << name << std::endl;‬
‭}‬

‭void meow() {‬
‭std::cout << name << " says Meow!" << std::endl;‬
‭}‬
‭};‬

‭int main() {‬
‭Dog myDog("Buddy", 3, "Golden Retriever");‬
‭myDog.eat(); // Inherited from Animal‬
‭myDog.sleep(); // Inherited from Animal‬
‭myDog.bark(); // Specific to Dog‬

‭std::cout << std::endl;‬

‭ at myCat("Whiskers", 2);‬
C
‭myCat.eat(); // Inherited from Animal‬
‭myCat.meow(); // Specific to Cat‬

‭return 0;‬
‭}‬

‭7.5. Polymorphism (Function Overriding, Virtual Functions)‬


‭ olymorphism means "many forms". In C++, it allows objects of different classes to be‬
P
‭treated as objects of a common base class. This is achieved primarily through‬‭virtual‬
‭functions‬‭.‬
‭●‬ ‭Function Overriding:‬‭When a derived class provides‬‭a specific implementation‬
f‭ or a function that is already defined in its base class.‬
‭ ‬ ‭Virtual Functions:‬‭A function declared with the virtual‬‭keyword in the base class.‬

‭It ensures that the correct overridden function is called based on the‬‭actual‬
‭object type‬‭at runtime, not the pointer/reference‬‭type. This is known as‬‭runtime‬
‭polymorphism‬‭or‬‭dynamic dispatch‬‭.‬
‭ include <iostream>‬
#
‭#include <string>‬

‭ lass Shape {‬
c
‭public:‬
‭std::string name;‬

‭Shape(std::string n) : name(n) {}‬

/‭ / Declare 'draw' as a virtual function‬


‭// This allows derived classes to provide their own implementation,‬
‭// and the correct version will be called at runtime via a base class pointer/reference.‬
‭virtual void draw() {‬
‭std::cout << "Drawing a generic Shape: " << name << std::endl;‬
‭}‬

/‭ / Virtual destructor is good practice when you have virtual functions‬


‭virtual ~Shape() {‬
‭std::cout << "Shape destructor called for " << name << std::endl;‬
‭}‬
‭};‬

‭ lass Circle : public Shape {‬


c
‭public:‬
‭double radius;‬
‭Circle(std::string n, double r) : Shape(n), radius(r) {}‬

/‭ / Override the draw function‬


‭void draw() override { // 'override' keyword (C++11) is optional but good practice for‬
‭clarity‬
‭std::cout << "Drawing a Circle: " << name << " with radius " << radius << std::endl;‬
‭}‬

‭~Circle() override {‬
‭std::cout << "Circle destructor called for " << name << std::endl;‬
‭}‬
‭};‬

‭ lass Rectangle : public Shape {‬


c
‭public:‬
‭double width;‬
‭double height;‬

‭Rectangle(std::string n, double w, double h) : Shape(n), width(w), height(h) {}‬

/‭ / Override the draw function‬


‭void draw() override {‬
‭std::cout << "Drawing a Rectangle: " << name << " with width " << width << " and‬
‭height " << height << std::endl;‬
‭}‬

‭~Rectangle() override {‬
‭std::cout << "Rectangle destructor called for " << name << std::endl;‬
‭}‬
‭};‬

‭int main() {‬
‭// Base class pointer pointing to derived class objects‬
‭Shape* s1 = new Circle("My Circle", 5.0);‬
‭Shape* s2 = new Rectangle("My Rectangle", 4.0, 6.0);‬
‭Shape* s3 = new Shape("Generic Shape");‬

‭// Calling draw() through base class pointers‬


/‭ / Due to 'virtual' keyword, the correct derived class's draw() is called at runtime.‬
‭s1->draw(); // Calls Circle::draw()‬
‭s2->draw(); // Calls Rectangle::draw()‬
‭s3->draw(); // Calls Shape::draw()‬

/‭ / Clean up dynamically allocated memory‬


‭delete s1;‬
‭delete s2;‬
‭delete s3;‬

s‭ td::cout << "\n--- Without Virtual Function (for comparison) ---" << std::endl;‬
‭// If 'draw' was NOT virtual in Shape:‬
‭// Shape* s4 = new Circle("Another Circle", 7.0);‬
‭// s4->draw(); // Would call Shape::draw() instead of Circle::draw() - Slicing problem‬
‭// delete s4;‬

‭return 0;‬
‭}‬

‭7.6. Abstraction (Abstract Classes and Pure Virtual Functions)‬


‭ bstraction means showing only essential information and hiding the implementation‬
A
‭details. In C++, this is achieved using‬‭abstract classes‬‭and‬‭pure virtual functions‬‭.‬
‭●‬ ‭Pure Virtual Function:‬‭A virtual function declared‬‭with = 0; in the base class. It‬
‭has no implementation in the base class.‬
‭○‬ ‭Example: virtual void draw() = 0;‬
‭ ‬ ‭Abstract Class:‬‭A class that contains at least one‬‭pure virtual function.‬

‭○‬ ‭You‬‭cannot create objects‬‭of an abstract class.‬
‭○‬ ‭Derived classes‬‭must implement‬‭all pure virtual functions‬‭from the abstract‬
‭base class, or they too become abstract.‬
‭ include <iostream>‬
#
‭#include <string>‬

/‭ / Abstract Base Class‬


‭class Vehicle {‬
‭public:‬
‭std::string make;‬
‭std::string model;‬
‭Vehicle(std::string m, std::string mod) : make(m), model(mod) {}‬

/‭ / Pure virtual function: Forces derived classes to implement this.‬


‭// A class with at least one pure virtual function is an abstract class.‬
‭virtual void startEngine() = 0;‬

/‭ / Regular virtual function (can be overridden, but not mandatory)‬


‭virtual void drive() {‬
‭std::cout << make << " " << model << " is driving." << std::endl;‬
‭}‬

/‭ / Virtual destructor for proper cleanup in polymorphic hierarchies‬


‭virtual ~Vehicle() {‬
‭std::cout << "Vehicle destructor called for " << make << " " << model << std::endl;‬
‭}‬
‭};‬

/‭ / Concrete Derived Class (must implement startEngine)‬


‭class Car : public Vehicle {‬
‭public:‬
‭Car(std::string m, std::string mod) : Vehicle(m, mod) {}‬

/‭ / Implementing the pure virtual function‬


‭void startEngine() override {‬
‭std::cout << make << " " << model << " Car engine starts with a key turn." <<‬
‭std::endl;‬
‭}‬

/‭ / Can optionally override drive()‬


‭void drive() override {‬
‭std::cout << make << " " << model << " Car is cruising on the highway." << std::endl;‬
‭}‬

‭~Car() override {‬
‭std::cout << "Car destructor called for " << make << " " << model << std::endl;‬
‭}‬
‭};‬
/‭ / Another Concrete Derived Class‬
‭class Motorcycle : public Vehicle {‬
‭public:‬
‭Motorcycle(std::string m, std::string mod) : Vehicle(m, mod) {}‬

/‭ / Implementing the pure virtual function‬


‭void startEngine() override {‬
‭std::cout << make << " " << model << " Motorcycle engine starts with a kick." <<‬
‭std::endl;‬
‭}‬

‭~Motorcycle() override {‬
‭std::cout << "Motorcycle destructor called for " << make << " " << model <<‬
‭std::endl;‬
‭}‬
‭};‬

‭int main() {‬
‭// Vehicle myVehicle; // ERROR: Cannot create an object of an abstract class‬

‭ ar myCar("Toyota", "Camry");‬
C
‭Motorcycle myBike("Harley-Davidson", "Iron 883");‬

/‭ / Using base class pointers for polymorphic behavior‬


‭Vehicle* v1 = &myCar;‬
‭Vehicle* v2 = &myBike;‬

v‭ 1->startEngine(); // Calls Car::startEngine()‬


‭v1->drive(); // Calls Car::drive()‬

‭std::cout << std::endl;‬

v‭ 2->startEngine(); // Calls Motorcycle::startEngine()‬


‭v2->drive(); // Calls Vehicle::drive() (Motorcycle didn't override it)‬

/‭ / Example with dynamic allocation (remember to delete!)‬


‭Vehicle* anotherCar = new Car("Honda", "Civic");‬
‭anotherCar->startEngine();‬
‭delete anotherCar; // Calls Car destructor, then Vehicle destructor‬
‭return 0;‬
‭}‬

‭8. Standard Template Library (STL) - Your DSA Powerhouse‬


‭ he STL is a set of C++ template classes that provide common programming data‬
T
‭structures and functions. It's a must-know for DSA.‬

‭8.1. Containers‬
‭Containers are objects that store collections of other objects (elements).‬

‭8.1.1. std::vector‬
‭A dynamic array that can grow or shrink in size. Very versatile and commonly used.‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::sort‬

‭int main() {‬
‭// Declare a vector of integers‬
‭std::vector<int> numbers;‬

/‭ / Add elements to the end (dynamic resizing)‬


‭numbers.push_back(10);‬
‭numbers.push_back(20);‬
‭numbers.push_back(5);‬
‭numbers.push_back(30);‬

s‭ td::cout << "Vector elements: ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Access elements (like an array)‬


‭std::cout << "First element: " << numbers[0] << std::endl;‬
‭std::cout << "Second element (at): " << numbers.at(1) << std::endl; // at() provides‬
‭ ounds checking‬
b
/‭ / Size and Capacity‬
‭std::cout << "Size of vector: " << numbers.size() << std::endl;‬
‭std::cout << "Capacity of vector: " << numbers.capacity() << std::endl; // Memory‬
‭allocated‬

/‭ / Iterate using traditional for loop‬


‭std::cout << "Elements using traditional loop: ";‬
‭for (size_t i = 0; i < numbers.size(); ++i) {‬
‭std::cout << numbers[i] << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Check if empty‬
‭if (numbers.empty()) {‬
‭std::cout << "Vector is empty." << std::endl;‬
‭} else {‬
‭std::cout << "Vector is not empty." << std::endl;‬
‭}‬

/‭ / Remove last element‬


‭numbers.pop_back();‬
‭std::cout << "After pop_back: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Insert element at a specific position (expensive for vectors)‬


‭// numbers.begin() gives an iterator to the first element‬
‭numbers.insert(numbers.begin() + 1, 15); // Insert 15 at index 1‬
‭std::cout << "After inserting 15 at index 1: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Erase element at a specific position‬


‭numbers.erase(numbers.begin()); // Erase first element‬
s‭ td::cout << "After erasing first element: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Sort the vector (requires <algorithm>)‬


‭std::sort(numbers.begin(), numbers.end());‬
‭std::cout << "After sorting: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Clear all elements‬


‭numbers.clear();‬
‭std::cout << "After clearing, size: " << numbers.size() << std::endl;‬

‭return 0;‬
‭}‬

‭8.1.2. std::list‬
‭ doubly linked list. Good for frequent insertions/deletions anywhere, but slow random‬
A
‭access.‬

‭ include <iostream>‬
#
‭#include <list>‬
‭#include <algorithm> // For std::sort‬

‭int main() {‬
‭std::list<int> myList;‬

/‭ / Add elements‬
‭myList.push_back(10); // Add to end‬
‭myList.push_front(5); // Add to beginning‬
‭myList.push_back(20);‬
‭myList.push_front(1);‬
s‭ td::cout << "List elements: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 1 5 10 20‬

/‭ / Access front and back elements‬


‭std::cout << "Front element: " << myList.front() << std::endl;‬
‭std::cout << "Back element: " << myList.back() << std::endl;‬

/‭ / Remove elements‬
‭myList.pop_front(); // Remove from beginning‬
‭myList.pop_back(); // Remove from end‬
‭std::cout << "After pop_front and pop_back: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 10‬

/‭ / Insert at specific position (using iterator)‬


‭auto it = myList.begin(); // Iterator to first element (5)‬
‭it++; // Iterator to second element (10)‬
‭myList.insert(it, 7); // Insert 7 before 10‬
‭std::cout << "After inserting 7: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 7 10‬

/‭ / Erase at specific position (using iterator)‬


‭it = myList.begin();‬
‭it++; // Iterator to 7‬
‭myList.erase(it); // Erase 7‬
‭std::cout << "After erasing 7: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 10‬
/‭ / Sort the list (list has its own sort method, more efficient for lists)‬
‭myList.sort();‬
‭std::cout << "After sorting: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Reverse the list‬


‭myList.reverse();‬
‭std::cout << "After reversing: ";‬
‭for (int val : myList) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬

‭8.1.3. std::deque (Double-Ended Queue)‬


‭ double-ended queue. Supports efficient insertion and deletion at both ends. Good‬
A
‭for implementing queues or deques.‬

‭ include <iostream>‬
#
‭#include <deque>‬

‭int main() {‬
‭std::deque<int> myDeque;‬

/‭ / Add elements to front and back‬


‭myDeque.push_back(10);‬
‭myDeque.push_front(5);‬
‭myDeque.push_back(15);‬
‭myDeque.push_front(2);‬

s‭ td::cout << "Deque elements: ";‬


‭for (int val : myDeque) {‬
‭std::cout << val << " ";‬
}‭ ‬
‭std::cout << std::endl; // Output: 2 5 10 15‬

/‭ / Access elements (like vector)‬


‭std::cout << "Front element: " << myDeque.front() << std::endl;‬
‭std::cout << "Back element: " << myDeque.back() << std::endl;‬
‭std::cout << "Element at index 1: " << myDeque[1] << std::endl; // Output: 5‬

/‭ / Remove elements from front and back‬


‭myDeque.pop_front();‬
‭myDeque.pop_back();‬
‭std::cout << "After pop_front and pop_back: ";‬
‭for (int val : myDeque) {‬
‭std::cout << val << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 10‬

‭return 0;‬
‭}‬

‭8.1.4. std::map and std::unordered_map‬


‭●‬ ‭std::map‬‭: Stores key-value pairs in sorted order of‬‭keys (implemented using a‬
s‭ elf-balancing binary search tree, usually Red-Black Tree).‬
‭ ‬ ‭std::unordered_map‬‭: Stores key-value pairs in an unordered‬‭fashion‬

‭(implemented using hash table). Provides average O(1) complexity for insertion,‬
‭deletion, and access.‬
‭ include <iostream>‬
#
‭#include <map>‬
‭#include <string>‬
‭#include <unordered_map>‬

‭int main() {‬
‭// --- std::map (sorted by key) ---‬
‭std::map<std::string, int> studentScores;‬

/‭ / Insert elements‬
‭studentScores["Alice"] = 95;‬
s‭ tudentScores["Bob"] = 88;‬
‭studentScores["Charlie"] = 92;‬
‭studentScores.insert({"David", 78}); // Another way to insert‬

s‭ td::cout << "--- std::map (sorted order by key) ---" << std::endl;‬
‭// Iterate through map (elements are sorted by key)‬
‭for (const auto& pair : studentScores) {‬
‭std::cout << pair.first << ": " << pair.second << std::endl;‬
‭}‬

/‭ / Accessing values‬
‭std::cout << "Alice's score: " << studentScores["Alice"] << std::endl;‬
‭// If key doesn't exist, it will be inserted with default value (0 for int)‬
‭std::cout << "Eve's score (newly inserted): " << studentScores["Eve"] << std::endl;‬

/‭ / Check if a key exists‬


‭if (studentScores.count("Bob")) { // Returns 1 if exists, 0 otherwise‬
‭std::cout << "Bob is in the map." << std::endl;‬
‭}‬

/‭ / Erase an element‬
‭studentScores.erase("Eve");‬
‭std::cout << "After erasing Eve, map size: " << studentScores.size() << std::endl;‬

/‭ / --- std::unordered_map (unordered, average O(1)) ---‬


‭std::unordered_map<std::string, int> cityPopulations;‬

‭ ityPopulations["New York"] = 8000000;‬


c
‭cityPopulations["London"] = 9000000;‬
‭cityPopulations["Tokyo"] = 14000000;‬

s‭ td::cout << "\n--- std::unordered_map (unordered) ---" << std::endl;‬


‭// Iterate through unordered_map (order is not guaranteed)‬
‭for (const auto& pair : cityPopulations) {‬
‭std::cout << pair.first << ": " << pair.second << std::endl;‬
‭}‬

‭std::cout << "Population of Tokyo: " << cityPopulations["Tokyo"] << std::endl;‬


‭return 0;‬
‭}‬

‭8.1.5. std::set and std::unordered_set‬


‭●‬ ‭std::set‬‭: Stores unique elements in sorted order (implemented‬‭using a‬
s‭ elf-balancing binary search tree).‬
‭ ‬ ‭std::unordered_set‬‭: Stores unique elements in an unordered‬‭fashion‬

‭(implemented using hash table). Provides average O(1) complexity for insertion,‬
‭deletion, and lookup.‬
‭ include <iostream>‬
#
‭#include <set>‬
‭#include <string>‬
‭#include <unordered_set>‬

‭int main() {‬
‭// --- std::set (sorted unique elements) ---‬
‭std::set<int> uniqueNumbers;‬

/‭ / Insert elements‬
‭uniqueNumbers.insert(10);‬
‭uniqueNumbers.insert(5);‬
‭uniqueNumbers.insert(20);‬
‭uniqueNumbers.insert(5); // Duplicate, will not be inserted‬

s‭ td::cout << "--- std::set (sorted unique elements) ---" << std::endl;‬
‭// Iterate through set (elements are sorted)‬
‭for (int num : uniqueNumbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 10 20‬

/‭ / Check if an element exists‬


‭if (uniqueNumbers.count(10)) { // Returns 1 if exists, 0 otherwise‬
‭std::cout << "10 is in the set." << std::endl;‬
‭}‬
‭if (uniqueNumbers.find(25) == uniqueNumbers.end()) { // find() returns iterator to‬
‭end if not found‬
‭std::cout << "25 is not in the set." << std::endl;‬
‭}‬

/‭ / Erase an element‬
‭uniqueNumbers.erase(10);‬
‭std::cout << "After erasing 10: ";‬
‭for (int num : uniqueNumbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 20‬

/‭ / --- std::unordered_set (unordered unique elements, average O(1)) ---‬


‭std::unordered_set<std::string> uniqueWords;‬

‭ niqueWords.insert("apple");‬
u
‭uniqueWords.insert("banana");‬
‭uniqueWords.insert("apple"); // Duplicate, will not be inserted‬
‭uniqueWords.insert("orange");‬

s‭ td::cout << "\n--- std::unordered_set (unordered unique elements) ---" <<‬


‭std::endl;‬
‭// Iterate through unordered_set (order is not guaranteed)‬
‭for (const std::string& word : uniqueWords) {‬
‭std::cout << word << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭if (uniqueWords.count("banana")) {‬
‭std::cout << "banana is in the unordered set." << std::endl;‬
‭}‬

‭return 0;‬
‭}‬

‭8.1.6. std::stack‬
‭A LIFO (Last-In, First-Out) container adapter. Think of a stack of plates.‬

‭ include <iostream>‬
#
‭#include <stack> // For std::stack‬

‭int main() {‬
‭std::stack<int> myStack;‬

/‭ / Push elements onto the stack‬


‭myStack.push(10); // 10 is at bottom‬
‭myStack.push(20);‬
‭myStack.push(30); // 30 is at top‬

‭std::cout << "Stack size: " << myStack.size() << std::endl;‬

/‭ / Access top element‬


‭std::cout << "Top element: " << myStack.top() << std::endl; // Output: 30‬

/‭ / Pop elements (removes from top)‬


‭myStack.pop(); // Removes 30‬
‭std::cout << "After pop, new top: " << myStack.top() << std::endl; // Output: 20‬

/‭ / Iterate and pop all elements (note: cannot directly iterate a stack)‬
‭std::cout << "Popping all elements: ";‬
‭while (!myStack.empty()) {‬
‭std::cout << myStack.top() << " ";‬
‭myStack.pop();‬
‭}‬
‭std::cout << std::endl; // Output: 20 10 (LIFO)‬

‭std::cout << "Is stack empty? " << (myStack.empty() ? "Yes" : "No") << std::endl;‬

‭return 0;‬
‭}‬

‭8.1.7. std::queue‬
‭A FIFO (First-In, First-Out) container adapter. Think of a line at a store.‬
‭ include <iostream>‬
#
‭#include <queue> // For std::queue‬

‭int main() {‬
‭std::queue<std::string> myQueue;‬

/‭ / Push elements onto the queue‬


‭myQueue.push("Alice"); // Alice is first in line‬
‭myQueue.push("Bob");‬
‭myQueue.push("Charlie"); // Charlie is last in line‬

‭std::cout << "Queue size: " << myQueue.size() << std::endl;‬

/‭ / Access front and back elements‬


‭std::cout << "Front element: " << myQueue.front() << std::endl; // Output: Alice‬
‭std::cout << "Back element: " << myQueue.back() << std::endl; // Output: Charlie‬

/‭ / Pop elements (removes from front)‬


‭myQueue.pop(); // Removes Alice‬
‭std::cout << "After pop, new front: " << myQueue.front() << std::endl; // Output: Bob‬

/‭ / Iterate and pop all elements‬


‭std::cout << "Processing queue: ";‬
‭while (!myQueue.empty()) {‬
‭std::cout << myQueue.front() << " ";‬
‭myQueue.pop();‬
‭}‬
‭std::cout << std::endl; // Output: Bob Charlie (FIFO)‬

‭std::cout << "Is queue empty? " << (myQueue.empty() ? "Yes" : "No") << std::endl;‬

‭return 0;‬
‭}‬

‭8.1.8. std::priority_queue‬
‭ container adapter that provides constant time lookup of the largest (or smallest)‬
A
‭element. Elements are retrieved in order of priority (largest by default). Implemented‬
‭using a heap.‬

‭ include <iostream>‬
#
‭#include <queue> // For std::priority_queue‬
‭#include <vector> // Default underlying container for priority_queue‬

‭int main() {‬
‭// Max-heap (default): largest element has highest priority‬
‭std::priority_queue<int> maxHeap;‬

‭ axHeap.push(10);‬
m
‭maxHeap.push(30);‬
‭maxHeap.push(20);‬
‭maxHeap.push(5);‬

s‭ td::cout << "Max-Heap elements (popped in order): ";‬


‭while (!maxHeap.empty()) {‬
‭std::cout << maxHeap.top() << " "; // Access the largest element‬
‭maxHeap.pop(); // Remove the largest element‬
‭}‬
‭std::cout << std::endl; // Output: 30 20 10 5‬

/‭ / Min-heap: smallest element has highest priority‬


‭// Use std::greater<int> as the comparator‬
‭std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;‬

‭ inHeap.push(10);‬
m
‭minHeap.push(30);‬
‭minHeap.push(20);‬
‭minHeap.push(5);‬

s‭ td::cout << "Min-Heap elements (popped in order): ";‬


‭while (!minHeap.empty()) {‬
‭std::cout << minHeap.top() << " "; // Access the smallest element‬
‭minHeap.pop(); // Remove the smallest element‬
‭}‬
‭std::cout << std::endl; // Output: 5 10 20 30‬

‭return 0;‬
‭}‬

‭8.2. Iterators‬
‭Iterators are like pointers that allow you to traverse through containers.‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <list>‬
‭#include <map>‬

‭int main() {‬
‭std::vector<int> numbers = {10, 20, 30, 40, 50};‬

/‭ / Get an iterator to the beginning of the vector‬


‭std::vector<int>::iterator it_vec = numbers.begin();‬

s‭ td::cout << "Vector elements using iterator: ";‬


‭while (it_vec != numbers.end()) { // Iterate until the end iterator‬
‭std::cout << *it_vec << " "; // Dereference iterator to get value‬
‭it_vec++; // Move to the next element‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Const iterator (for read-only access)‬


‭std::vector<int>::const_iterator const_it_vec = numbers.cbegin();‬
‭std::cout << "Vector elements using const iterator: ";‬
‭while (const_it_vec != numbers.cend()) {‬
‭std::cout << *const_it_vec << " ";‬
‭// *const_it_vec = 100; // ERROR: Cannot modify value through const iterator‬
‭const_it_vec++;‬
‭}‬
‭std::cout << std::endl;‬

s‭ td::list<std::string> names = {"Alice", "Bob", "Charlie"};‬


‭std::list<std::string>::iterator it_list = names.begin();‬
‭std::cout << "List elements using iterator: ";‬
‭for (; it_list != names.end(); ++it_list) {‬
‭std::cout << *it_list << " ";‬
}‭ ‬
‭std::cout << std::endl;‬

s‭ td::map<char, int> grades = {{'A', 90}, {'B', 80}, {'C', 70}};‬


‭std::map<char, int>::iterator it_map = grades.begin();‬
‭std::cout << "Map elements using iterator: ";‬
‭for (; it_map != grades.end(); ++it_map) {‬
‭std::cout << it_map->first << ":" << it_map->second << " "; // Access key and value‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Reverse iterators (iterate from end to beginning)‬


‭std::cout << "Vector elements in reverse using reverse iterator: ";‬
‭for (std::vector<int>::reverse_iterator rit = numbers.rbegin(); rit != numbers.rend();‬
‭++rit) {‬
‭std::cout << *rit << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬

‭8.3. Algorithms‬
‭ he <algorithm> header provides a rich set of functions for common operations on‬
T
‭ranges (like containers).‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For sort, find, reverse, min_element, max_element, etc.‬
‭#include <numeric> // For std::accumulate‬

‭int main() {‬
‭std::vector<int> numbers = {5, 2, 8, 1, 9, 4, 7, 3, 6};‬

s‭ td::cout << "Original vector: ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Sorting‬
‭std::sort(numbers.begin(), numbers.end()); // Sorts in ascending order‬
‭std::cout << "Sorted vector: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Reversing‬
‭std::reverse(numbers.begin(), numbers.end());‬
‭std::cout << "Reversed vector: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

/‭ / Finding an element‬
‭auto it_find = std::find(numbers.begin(), numbers.end(), 5);‬
‭if (it_find != numbers.end()) {‬
‭std::cout << "Found 5 at index: " << std::distance(numbers.begin(), it_find) <<‬
‭std::endl;‬
‭} else {‬
‭std::cout << "5 not found." << std::endl;‬
‭}‬

/‭ / Min and Max elements‬


‭auto min_val_it = std::min_element(numbers.begin(), numbers.end());‬
‭auto max_val_it = std::max_element(numbers.begin(), numbers.end());‬
‭std::cout << "Min element: " << *min_val_it << std::endl;‬
‭std::cout << "Max element: " << *max_val_it << std::endl;‬

/‭ / Sum of elements (using std::accumulate from <numeric>)‬


‭int sum = std::accumulate(numbers.begin(), numbers.end(), 0); // 0 is initial sum‬
‭std::cout << "Sum of elements: " << sum << std::endl;‬

/‭ / Count occurrences‬
‭int count_fives = std::count(numbers.begin(), numbers.end(), 5);‬
‭std::cout << "Number of 5s: " << count_fives << std::endl;‬

/‭ / Check if all elements satisfy a condition‬


‭bool all_positive = std::all_of(numbers.begin(), numbers.end(), [](int i){ return i > 0;‬
‭});‬
‭std::cout << "Are all elements positive? " << (all_positive ? "Yes" : "No") << std::endl;‬

/‭ / Lambda functions (used with algorithms for custom logic)‬


‭// Sort in descending order using a lambda‬
‭std::sort(numbers.begin(), numbers.end(), [](int a, int b){‬
‭return a > b; // Custom comparison for descending‬
‭});‬
‭std::cout << "Sorted descending: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬

‭9. Recursion‬
‭ ecursion is a programming technique where a function calls itself to solve a smaller‬
R
‭instance of the same problem. Essential for many DSA problems (trees, graphs,‬
‭dynamic programming).‬

‭Key components of a recursive function:‬


‭1.‬ ‭Base Case:‬‭The condition that stops the recursion.‬‭Without it, the function would‬
‭ all itself indefinitely, leading to a stack overflow.‬
c
‭ .‬ ‭Recursive Step:‬‭The part where the function calls‬‭itself with a modified (smaller)‬
2
‭input.‬
‭#include <iostream>‬

/‭ / Example 1: Factorial calculation‬


‭// n! = n * (n-1)!‬
‭// Base case: 0! = 1‬
‭long long factorial(int n) {‬
/‭ / Base case‬
‭if (n == 0 || n == 1) {‬
‭return 1;‬
‭}‬
‭// Recursive step‬
‭return n * factorial(n - 1);‬
‭}‬

/‭ / Example 2: Fibonacci sequence‬


‭// F(n) = F(n-1) + F(n-2)‬
‭// Base cases: F(0) = 0, F(1) = 1‬
‭int fibonacci(int n) {‬
‭// Base cases‬
‭if (n <= 0) {‬
‭return 0;‬
‭}‬
‭if (n == 1) {‬
‭return 1;‬
‭}‬
‭// Recursive step‬
‭return fibonacci(n - 1) + fibonacci(n - 2);‬
‭}‬

/‭ / Example 3: Sum of digits of a number‬


‭// sumDigits(123) = 3 + sumDigits(12)‬
‭// Base case: sumDigits(0) = 0‬
‭int sumDigits(int n) {‬
‭// Base case‬
‭if (n == 0) {‬
‭return 0;‬
‭}‬
‭// Recursive step‬
‭return (n % 10) + sumDigits(n / 10);‬
‭}‬

‭int main() {‬
‭std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 5 * 4 * 3 * 2 * 1 = 120‬
‭std::cout << "Factorial of 0: " << factorial(0) << std::endl; // 1‬
s‭ td::cout << "\nFibonacci sequence up to 10 terms: " << std::endl;‬
‭for (int i = 0; i < 10; ++i) {‬
‭std::cout << fibonacci(i) << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 0 1 1 2 3 5 8 13 21 34‬

s‭ td::cout << "\nSum of digits of 456: " << sumDigits(456) << std::endl; // 4 + 5 + 6 = 15‬
‭std::cout << "Sum of digits of 12345: " << sumDigits(12345) << std::endl; // 1 + 2 + 3 +‬
‭ + 5 = 15‬
4

‭return 0;‬
‭}‬

‭10. Basic Data Structures‬


‭ nderstanding how basic data structures work is fundamental, even if you use STL‬
U
‭containers.‬

‭10.1. Linked Lists‬


‭ linear data structure where elements are not stored in contiguous memory‬
A
‭locations. Each element (node) contains data and a pointer to the next node.‬

‭#include <iostream>‬

/‭ / Define a Node structure for the linked list‬


‭struct Node {‬
‭int data;‬
‭Node* next; // Pointer to the next node‬

/‭ / Constructor for Node‬


‭Node(int val) : data(val), next(nullptr) {}‬
‭};‬

/‭ / Class for Linked List operations‬


‭class LinkedList {‬
‭private:‬
‭Node* head; // Pointer to the first node of the list‬

‭public:‬
/‭ / Constructor‬
‭LinkedList() : head(nullptr) {}‬

/‭ / Destructor to free memory‬


‭~LinkedList() {‬
‭Node* current = head;‬
‭while (current != nullptr) {‬
‭Node* nextNode = current->next;‬
‭delete current;‬
‭current = nextNode;‬
‭}‬
‭head = nullptr; // Ensure head is null after deletion‬
‭std::cout << "Linked List destroyed." << std::endl;‬
‭}‬

/‭ / Function to insert a new node at the beginning‬


‭void insertAtBeginning(int val) {‬
‭Node* newNode = new Node(val);‬
‭newNode->next = head; // New node points to current head‬
‭head = newNode; // Head becomes the new node‬
‭std::cout << "Inserted " << val << " at beginning." << std::endl;‬
‭}‬

/‭ / Function to insert a new node at the end‬


‭void insertAtEnd(int val) {‬
‭Node* newNode = new Node(val);‬
‭if (head == nullptr) { // If list is empty‬
‭head = newNode;‬
‭return;‬
‭}‬
‭Node* current = head;‬
‭while (current->next != nullptr) { // Traverse to the last node‬
‭current = current->next;‬
‭}‬
‭current->next = newNode; // Last node points to new node‬
‭std::cout << "Inserted " << val << " at end." << std::endl;‬
‭}‬

‭// Function to delete a node with a specific value‬


‭void deleteNode(int val) {‬
‭if (head == nullptr) {‬
‭std::cout << "List is empty. Cannot delete " << val << std::endl;‬
‭return;‬
‭}‬

/‭ / If head node needs to be deleted‬


‭if (head->data == val) {‬
‭Node* temp = head;‬
‭head = head->next;‬
‭delete temp;‬
‭std::cout << "Deleted " << val << " from beginning." << std::endl;‬
‭return;‬
‭}‬

‭ ode* current = head;‬


N
‭Node* prev = nullptr;‬
‭while (current != nullptr && current->data != val) {‬
‭prev = current;‬
‭current = current->next;‬
‭}‬

‭if (current == nullptr) { // Value not found‬


‭std::cout << val << " not found in the list." << std::endl;‬
‭} else { // Value found‬
‭prev->next = current->next; // Bypass the current node‬
‭delete current;‬
‭std::cout << "Deleted " << val << "." << std::endl;‬
‭}‬
‭}‬

/‭ / Function to display the linked list‬


‭void displayList() {‬
‭if (head == nullptr) {‬
‭std::cout << "List is empty." << std::endl;‬
‭return;‬
‭}‬
‭Node* current = head;‬
‭std::cout << "Linked List: ";‬
‭while (current != nullptr) {‬
‭std::cout << current->data << " -> ";‬
‭current = current->next;‬
‭}‬
‭std::cout << "nullptr" << std::endl;‬
‭}‬

/‭ / Function to search for a value‬


‭bool search(int val) {‬
‭Node* current = head;‬
‭while (current != nullptr) {‬
‭if (current->data == val) {‬
‭return true;‬
‭}‬
‭current = current->next;‬
‭}‬
‭return false;‬
‭}‬
‭};‬

‭int main() {‬
‭LinkedList myList;‬

‭ yList.insertAtEnd(10);‬
m
‭myList.insertAtBeginning(5);‬
‭myList.insertAtEnd(20);‬
‭myList.insertAtBeginning(1);‬
‭myList.displayList(); // Output: 1 -> 5 -> 10 -> 20 -> nullptr‬

s‭ td::cout << "Searching for 10: " << (myList.search(10) ? "Found" : "Not Found") <<‬
‭std::endl;‬
‭std::cout << "Searching for 100: " << (myList.search(100) ? "Found" : "Not Found")‬
‭<< std::endl;‬

‭ yList.deleteNode(10);‬
m
‭myList.displayList(); // Output: 1 -> 5 -> 20 -> nullptr‬

‭ yList.deleteNode(1);‬
m
‭myList.displayList(); // Output: 5 -> 20 -> nullptr‬
‭ yList.deleteNode(25); // Not found‬
m
‭myList.displayList(); // Output: 5 -> 20 -> nullptr‬

‭ yList.deleteNode(20);‬
m
‭myList.displayList(); // Output: 5 -> nullptr‬

‭ yList.deleteNode(5);‬
m
‭myList.displayList(); // Output: List is empty.‬

‭return 0;‬
‭}‬

‭10.2. Stacks (Manual Implementation)‬


‭A LIFO data structure. Can be implemented using arrays or linked lists.‬

‭ include <iostream>‬
#
‭#include <vector> // Using vector as underlying storage for simplicity‬

‭ lass MyStack {‬
c
‭private:‬
‭std::vector<int> elements;‬

‭public:‬
‭// Push an element onto the stack‬
‭void push(int val) {‬
‭elements.push_back(val);‬
‭std::cout << "Pushed: " << val << std::endl;‬
‭}‬

/‭ / Pop an element from the stack‬


‭int pop() {‬
‭if (empty()) {‬
‭std::cerr << "Error: Stack is empty. Cannot pop." << std::endl;‬
‭return -1; // Or throw an exception‬
‭}‬
‭int top_val = elements.back();‬
‭elements.pop_back();‬
s‭ td::cout << "Popped: " << top_val << std::endl;‬
‭return top_val;‬
‭}‬

/‭ / Get the top element without removing it‬


‭int top() {‬
‭if (empty()) {‬
‭std::cerr << "Error: Stack is empty. No top element." << std::endl;‬
‭return -1; // Or throw an exception‬
‭}‬
‭return elements.back();‬
‭}‬

/‭ / Check if the stack is empty‬


‭bool empty() const {‬
‭return elements.empty();‬
‭}‬

/‭ / Get the size of the stack‬


‭size_t size() const {‬
‭return elements.size();‬
‭}‬
‭};‬

‭int main() {‬
‭MyStack s;‬

s‭ .push(10);‬
‭s.push(20);‬
‭s.push(30);‬

s‭ td::cout << "Current top element: " << s.top() << std::endl; // Output: 30‬
‭std::cout << "Stack size: " << s.size() << std::endl; // Output: 3‬

s‭ .pop(); // Popped: 30‬


‭std::cout << "Current top element: " << s.top() << std::endl; // Output: 20‬

s‭ .pop(); // Popped: 20‬


‭s.pop(); // Popped: 10‬
‭s.pop(); // Error: Stack is empty. Cannot pop.‬

s‭ td::cout << "Is stack empty? " << (s.empty() ? "Yes" : "No") << std::endl; // Output:‬
‭Yes‬

‭return 0;‬
‭}‬

‭10.3. Queues (Manual Implementation)‬


‭A FIFO data structure. Can be implemented using arrays or linked lists.‬

‭ include <iostream>‬
#
‭#include <list> // Using std::list as underlying storage for simplicity (efficient front/back‬
‭operations)‬

‭ lass MyQueue {‬
c
‭private:‬
‭std::list<int> elements;‬

‭public:‬
‭// Enqueue (add to back)‬
‭void enqueue(int val) {‬
‭elements.push_back(val);‬
‭std::cout << "Enqueued: " << val << std::endl;‬
‭}‬

/‭ / Dequeue (remove from front)‬


‭int dequeue() {‬
‭if (empty()) {‬
‭std::cerr << "Error: Queue is empty. Cannot dequeue." << std::endl;‬
‭return -1; // Or throw an exception‬
‭}‬
‭int front_val = elements.front();‬
‭elements.pop_front();‬
‭std::cout << "Dequeued: " << front_val << std::endl;‬
‭return front_val;‬
‭}‬
/‭ / Get front element without removing‬
‭int front() {‬
‭if (empty()) {‬
‭std::cerr << "Error: Queue is empty. No front element." << std::endl;‬
‭return -1; // Or throw an exception‬
‭}‬
‭return elements.front();‬
‭}‬

/‭ / Check if queue is empty‬


‭bool empty() const {‬
‭return elements.empty();‬
‭}‬

/‭ / Get size of queue‬


‭size_t size() const {‬
‭return elements.size();‬
‭}‬
‭};‬

‭int main() {‬
‭MyQueue q;‬

‭ .enqueue(10);‬
q
‭q.enqueue(20);‬
‭q.enqueue(30);‬

s‭ td::cout << "Current front element: " << q.front() << std::endl; // Output: 10‬
‭std::cout << "Queue size: " << q.size() << std::endl; // Output: 3‬

‭ .dequeue(); // Dequeued: 10‬


q
‭std::cout << "Current front element: " << q.front() << std::endl; // Output: 20‬

‭ .dequeue(); // Dequeued: 20‬


q
‭q.dequeue(); // Dequeued: 30‬
‭q.dequeue(); // Error: Queue is empty. Cannot dequeue.‬

s‭ td::cout << "Is queue empty? " << (q.empty() ? "Yes" : "No") << std::endl; // Output:‬
‭Yes‬
‭return 0;‬
‭}‬

‭10.4. Trees (Binary Tree, Binary Search Tree)‬


‭Binary Tree‬
‭ tree data structure in which each node has at most two children, referred to as the‬
A
‭left child and the right child.‬

‭ include <iostream>‬
#
‭#include <queue> // For level order traversal‬

/‭ / Node structure for a Binary Tree‬


‭struct TreeNode {‬
‭int data;‬
‭TreeNode* left;‬
‭TreeNode* right;‬

‭TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}‬


‭};‬

/‭ / Functions for Binary Tree traversals‬


‭void inorderTraversal(TreeNode* node) {‬
‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭inorderTraversal(node->left);‬
‭std::cout << node->data << " ";‬
‭inorderTraversal(node->right);‬
‭}‬

‭void preorderTraversal(TreeNode* node) {‬


‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭std::cout << node->data << " ";‬
‭preorderTraversal(node->left);‬
‭preorderTraversal(node->right);‬
‭}‬

‭void postorderTraversal(TreeNode* node) {‬


‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭postorderTraversal(node->left);‬
‭postorderTraversal(node->right);‬
‭std::cout << node->data << " ";‬
‭}‬

‭void levelOrderTraversal(TreeNode* root) {‬


‭if (root == nullptr) {‬
‭return;‬
‭}‬
‭std::queue<TreeNode*> q;‬
‭q.push(root);‬

‭while (!q.empty()) {‬
‭TreeNode* current = q.front();‬
‭q.pop();‬
‭std::cout << current->data << " ";‬

‭if (current->left != nullptr) {‬


‭q.push(current->left);‬
‭}‬
‭if (current->right != nullptr) {‬
‭q.push(current->right);‬
‭}‬
‭}‬
‭}‬

/‭ / Function to delete the entire tree (post-order traversal for deletion)‬


‭void deleteTree(TreeNode* node) {‬
‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭deleteTree(node->left);‬
‭deleteTree(node->right);‬
s‭ td::cout << "Deleting node: " << node->data << std::endl;‬
‭delete node;‬
‭}‬

‭int main() {‬
‭// Constructing a sample Binary Tree:‬
‭// 1‬
‭// / \‬
‭// 2 3‬
‭// / \‬
‭// 4 5‬
‭TreeNode* root = new TreeNode(1);‬
‭root->left = new TreeNode(2);‬
‭root->right = new TreeNode(3);‬
‭root->left->left = new TreeNode(4);‬
‭root->left->right = new TreeNode(5);‬

s‭ td::cout << "Inorder Traversal (Left, Root, Right): ";‬


‭inorderTraversal(root); // Output: 4 2 5 1 3‬
‭std::cout << std::endl;‬

s‭ td::cout << "Preorder Traversal (Root, Left, Right): ";‬


‭preorderTraversal(root); // Output: 1 2 4 5 3‬
‭std::cout << std::endl;‬

s‭ td::cout << "Postorder Traversal (Left, Right, Root): ";‬


‭postorderTraversal(root); // Output: 4 5 2 3 1‬
‭std::cout << std::endl;‬

s‭ td::cout << "Level Order Traversal: ";‬


‭levelOrderTraversal(root); // Output: 1 2 3 4 5‬
‭std::cout << std::endl;‬

/‭ / Clean up memory‬
‭deleteTree(root);‬
‭root = nullptr; // Important to set to nullptr after deletion‬

‭return 0;‬
‭}‬
‭Binary Search Tree (BST)‬
‭ binary tree where for each node, all elements in its left subtree are less than the‬
A
‭node's data, and all elements in its right subtree are greater than the node's data.‬

‭#include <iostream>‬

/‭ / Node structure for a Binary Search Tree‬


‭struct BSTNode {‬
‭int data;‬
‭BSTNode* left;‬
‭BSTNode* right;‬

‭BSTNode(int val) : data(val), left(nullptr), right(nullptr) {}‬


‭};‬

/‭ / Function to insert a new node into BST‬


‭BSTNode* insert(BSTNode* root, int val) {‬
‭if (root == nullptr) {‬
‭return new BSTNode(val);‬
‭}‬
‭if (val < root->data) {‬
‭root->left = insert(root->left, val);‬
‭} else if (val > root->data) {‬
‭root->right = insert(root->right, val);‬
‭}‬
‭// If val == root->data, do nothing (assuming unique values, or handle duplicates)‬
‭return root;‬
‭}‬

/‭ / Function to search for a value in BST‬


‭BSTNode* search(BSTNode* root, int val) {‬
‭if (root == nullptr || root->data == val) {‬
‭return root;‬
‭}‬
‭if (val < root->data) {‬
‭return search(root->left, val);‬
‭} else {‬
‭return search(root->right, val);‬
‭}‬
‭}‬

/‭ / Function to find the minimum value node in a BST (leftmost node)‬


‭BSTNode* findMin(BSTNode* node) {‬
‭BSTNode* current = node;‬
‭while (current && current->left != nullptr) {‬
‭current = current->left;‬
‭}‬
‭return current;‬
‭}‬

/‭ / Function to delete a node from BST‬


‭BSTNode* deleteNode(BSTNode* root, int key) {‬
‭if (root == nullptr) {‬
‭return root;‬
‭}‬

/‭ / Traverse to find the node to be deleted‬


‭if (key < root->data) {‬
‭root->left = deleteNode(root->left, key);‬
‭} else if (key > root->data) {‬
‭root->right = deleteNode(root->right, key);‬
‭} else { // Node with only one child or no child‬
‭if (root->left == nullptr) {‬
‭BSTNode* temp = root->right;‬
‭delete root;‬
‭return temp;‬
‭} else if (root->right == nullptr) {‬
‭BSTNode* temp = root->left;‬
‭delete root;‬
‭return temp;‬
‭}‬
‭// Node with two children: Get the inorder successor (smallest in the right‬
‭subtree)‬
‭BSTNode* temp = findMin(root->right);‬
‭// Copy the inorder successor's content to this node‬
‭root->data = temp->data;‬
/‭ / Delete the inorder successor‬
‭root->right = deleteNode(root->right, temp->data);‬
}‭ ‬
‭return root;‬
‭}‬

/‭ / Inorder traversal (prints sorted elements for BST)‬


‭void inorderTraversalBST(BSTNode* node) {‬
‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭inorderTraversalBST(node->left);‬
‭std::cout << node->data << " ";‬
‭inorderTraversalBST(node->right);‬
‭}‬

/‭ / Function to delete the entire BST‬


‭void deleteBST(BSTNode* node) {‬
‭if (node == nullptr) {‬
‭return;‬
‭}‬
‭deleteBST(node->left);‬
‭deleteBST(node->right);‬
‭delete node;‬
‭}‬

‭int main() {‬
‭BSTNode* root = nullptr;‬
‭root = insert(root, 50);‬
‭insert(root, 30);‬
‭insert(root, 20);‬
‭insert(root, 40);‬
‭insert(root, 70);‬
‭insert(root, 60);‬
‭insert(root, 80);‬

s‭ td::cout << "Inorder traversal of BST: ";‬


‭inorderTraversalBST(root); // Output: 20 30 40 50 60 70 80 (sorted!)‬
‭std::cout << std::endl;‬
/‭ / Search‬
‭if (search(root, 40) != nullptr) {‬
‭std::cout << "40 found in BST." << std::endl;‬
‭} else {‬
‭std::cout << "40 not found." << std::endl;‬
‭}‬
‭if (search(root, 90) != nullptr) {‬
‭std::cout << "90 found in BST." << std::endl;‬
‭} else {‬
‭std::cout << "90 not found in BST." << std::endl;‬
‭}‬

/‭ / Delete nodes‬
‭std::cout << "\nDeleting 20 (leaf node):" << std::endl;‬
‭root = deleteNode(root, 20);‬
‭std::cout << "Inorder traversal: ";‬
‭inorderTraversalBST(root);‬
‭std::cout << std::endl;‬

s‭ td::cout << "\nDeleting 70 (node with one child):" << std::endl;‬


‭root = deleteNode(root, 70);‬
‭std::cout << "Inorder traversal: ";‬
‭inorderTraversalBST(root);‬
‭std::cout << std::endl;‬

s‭ td::cout << "\nDeleting 50 (root node with two children):" << std::endl;‬
‭root = deleteNode(root, 50);‬
‭std::cout << "Inorder traversal: ";‬
‭inorderTraversalBST(root);‬
‭std::cout << std::endl;‬

/‭ / Clean up memory‬
‭deleteBST(root);‬
‭root = nullptr;‬

‭return 0;‬
‭}‬
‭10.5. Graphs (Adjacency List/Matrix)‬
‭A graph is a non-linear data structure consisting of nodes (vertices) and edges.‬
‭●‬ ‭Adjacency Matrix:‬‭A 2D array where matrix[i][j] is‬‭1 if there's an edge between‬
v‭ ertex i and j, else 0. Good for dense graphs, but uses more space for sparse‬
‭graphs.‬
‭ ‬ ‭Adjacency List:‬‭An array of lists (or vectors), where‬‭adj[i] contains a list of all‬

‭vertices adjacent to vertex i. Good for sparse graphs, more memory efficient.‬
‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <list> // For adjacency list‬
‭#include <queue> // For BFS‬
‭#include <stack> // For DFS (iterative)‬

/‭ / --- Adjacency Matrix Representation ---‬


‭void addEdgeMatrix(std::vector<std::vector<int>>& adjMatrix, int u, int v) {‬
‭adjMatrix[u][v] = 1; // For directed graph‬
‭// For undirected graph: adjMatrix[v][u] = 1;‬
‭}‬

‭void printMatrix(const std::vector<std::vector<int>>& adjMatrix) {‬


‭int V = adjMatrix.size();‬
‭std::cout << "\nAdjacency Matrix:" << std::endl;‬
‭for (int i = 0; i < V; ++i) {‬
‭for (int j = 0; j < V; ++j) {‬
‭std::cout << adjMatrix[i][j] << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭}‬
‭}‬

/‭ / --- Adjacency List Representation ---‬


‭void addEdgeList(std::vector<std::list<int>>& adjList, int u, int v) {‬
‭adjList[u].push_back(v); // For directed graph‬
‭// For undirected graph: adjList[v].push_back(u);‬
‭}‬
‭void printList(const std::vector<std::list<int>>& adjList) {‬
‭int V = adjList.size();‬
‭std::cout << "\nAdjacency List:" << std::endl;‬
‭for (int i = 0; i < V; ++i) {‬
‭std::cout << "Vertex " << i << ":";‬
‭for (int neighbor : adjList[i]) {‬
‭std::cout << " -> " << neighbor;‬
‭}‬
‭std::cout << std::endl;‬
‭}‬
‭}‬

/‭ / --- Graph Traversal: Breadth-First Search (BFS) ---‬


‭// Uses a queue to explore neighbors level by level‬
‭void BFS(const std::vector<std::list<int>>& adjList, int startNode) {‬
‭int V = adjList.size();‬
‭std::vector<bool> visited(V, false);‬
‭std::queue<int> q;‬

v‭ isited[startNode] = true;‬
‭q.push(startNode);‬

s‭ td::cout << "\nBFS Traversal (starting from " << startNode << "): ";‬
‭while (!q.empty()) {‬
‭int u = q.front();‬
‭q.pop();‬
‭std::cout << u << " ";‬

‭for (int v : adjList[u]) {‬


‭if (!visited[v]) {‬
‭visited[v] = true;‬
‭q.push(v);‬
‭}‬
‭}‬
}‭ ‬
‭std::cout << std::endl;‬
‭}‬

‭// --- Graph Traversal: Depth-First Search (DFS) ---‬


/‭ / Uses recursion (or a stack) to explore as deep as possible along each branch‬
‭void DFS_recursive(const std::vector<std::list<int>>& adjList, int u, std::vector<bool>&‬
‭visited) {‬
‭visited[u] = true;‬
‭std::cout << u << " ";‬

‭for (int v : adjList[u]) {‬


‭if (!visited[v]) {‬
‭DFS_recursive(adjList, v, visited);‬
‭}‬
‭}‬
‭}‬

‭void DFS_iterative(const std::vector<std::list<int>>& adjList, int startNode) {‬


‭int V = adjList.size();‬
‭std::vector<bool> visited(V, false);‬
‭std::stack<int> s;‬

s‭ .push(startNode);‬
‭visited[startNode] = true;‬

s‭ td::cout << "\nDFS Traversal (iterative, starting from " << startNode << "): ";‬
‭while (!s.empty()) {‬
‭int u = s.top();‬
‭s.pop();‬
‭std::cout << u << " ";‬

/‭ / Push unvisited neighbors onto the stack (order might vary from recursive)‬
‭// For consistent order with recursive DFS, push in reverse order of adjacency list‬
‭for (auto it = adjList[u].rbegin(); it != adjList[u].rend(); ++it) {‬
‭int v = *it;‬
‭if (!visited[v]) {‬
‭visited[v] = true;‬
‭s.push(v);‬
‭}‬
‭}‬
}‭ ‬
‭std::cout << std::endl;‬
‭}‬
‭int main() {‬
‭int V = 5; // Number of vertices (0, 1, 2, 3, 4)‬

/‭ / Adjacency Matrix Example‬


‭std::vector<std::vector<int>> adjMatrix(V, std::vector<int>(V, 0));‬
‭addEdgeMatrix(adjMatrix, 0, 1);‬
‭addEdgeMatrix(adjMatrix, 0, 4);‬
‭addEdgeMatrix(adjMatrix, 1, 2);‬
‭addEdgeMatrix(adjMatrix, 1, 3);‬
‭addEdgeMatrix(adjMatrix, 1, 4);‬
‭addEdgeMatrix(adjMatrix, 2, 3);‬
‭addEdgeMatrix(adjMatrix, 3, 4);‬
‭printMatrix(adjMatrix);‬

/‭ / Adjacency List Example‬


‭std::vector<std::list<int>> adjList(V);‬
‭addEdgeList(adjList, 0, 1);‬
‭addEdgeList(adjList, 0, 4);‬
‭addEdgeList(adjList, 1, 2);‬
‭addEdgeList(adjList, 1, 3);‬
‭addEdgeList(adjList, 1, 4);‬
‭addEdgeList(adjList, 2, 3);‬
‭addEdgeList(adjList, 3, 4);‬
‭printList(adjList);‬

/‭ / BFS Traversal‬
‭BFS(adjList, 0); // Start BFS from vertex 0‬

/‭ / DFS Traversal (Recursive)‬


‭std::vector<bool> visited_dfs_rec(V, false);‬
‭std::cout << "\nDFS Traversal (recursive, starting from 0): ";‬
‭DFS_recursive(adjList, 0, visited_dfs_rec);‬
‭std::cout << std::endl;‬

/‭ / DFS Traversal (Iterative)‬


‭DFS_iterative(adjList, 0);‬
‭return 0;‬
‭}‬

‭11. Basic Algorithms‬


‭11.1. Searching Algorithms (Linear, Binary)‬
‭Linear Search‬
‭ equentially checks each element until a match is found or the end of the list is‬
S
‭reached.‬
‭●‬ ‭Time Complexity: O(N) in worst case.‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::find (though we implement it here)‬

/‭ / Function for Linear Search‬


‭int linearSearch(const std::vector<int>& arr, int target) {‬
‭for (size_t i = 0; i < arr.size(); ++i) {‬
‭if (arr[i] == target) {‬
‭return i; // Return index if found‬
‭}‬
‭}‬
‭return -1; // Return -1 if not found‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {10, 5, 20, 15, 25, 30};‬
‭int target1 = 15;‬
‭int target2 = 100;‬

i‭nt index1 = linearSearch(numbers, target1);‬


‭if (index1 != -1) {‬
‭std::cout << target1 << " found at index: " << index1 << std::endl;‬
‭} else {‬
‭std::cout << target1 << " not found." << std::endl;‬
‭}‬

‭int index2 = linearSearch(numbers, target2);‬


‭if (index2 != -1) {‬
‭std::cout << target2 << " found at index: " << index2 << std::endl;‬
‭} else {‬
‭std::cout << target2 << " not found." << std::endl;‬
‭}‬

‭return 0;‬
‭}‬

‭Binary Search‬
‭ fficiently finds an element in a‬‭sorted‬‭array by‬‭repeatedly dividing the search interval‬
E
‭in half.‬
‭●‬ ‭Time Complexity: O(log N)‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::sort and std::binary_search‬

/‭ / Function for Binary Search (iterative)‬


‭int binarySearch(const std::vector<int>& arr, int target) {‬
‭int low = 0;‬
‭int high = arr.size() - 1;‬

‭while (low <= high) {‬


‭int mid = low + (high - low) / 2; // Avoids potential overflow compared to (low +‬
‭ igh) / 2‬
h

‭if (arr[mid] == target) {‬


‭return mid; // Target found‬
‭} else if (arr[mid] < target) {‬
‭low = mid + 1; // Target is in the right half‬
‭} else {‬
‭high = mid - 1; // Target is in the left half‬
‭}‬
}‭ ‬
‭return -1; // Target not found‬
‭}‬
‭int main() {‬
‭std::vector<int> numbers = {10, 5, 20, 15, 25, 30};‬

/‭ / Binary search requires a sorted array!‬


‭std::sort(numbers.begin(), numbers.end());‬
‭std::cout << "Sorted array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl; // Output: 5 10 15 20 25 30‬

i‭nt target1 = 15;‬


‭int target2 = 100;‬

i‭nt index1 = binarySearch(numbers, target1);‬


‭if (index1 != -1) {‬
‭std::cout << target1 << " found at index: " << index1 << std::endl;‬
‭} else {‬
‭std::cout << target1 << " not found." << std::endl;‬
‭}‬

i‭nt index2 = binarySearch(numbers, target2);‬


‭if (index2 != -1) {‬
‭std::cout << target2 << " found at index: " << index2 << std::endl;‬
‭} else {‬
‭std::cout << target2 << " not found." << std::endl;‬
‭}‬

/‭ / Using STL's std::binary_search (returns true/false)‬


‭if (std::binary_search(numbers.begin(), numbers.end(), 20)) {‬
‭std::cout << "20 is present in the array (using std::binary_search)." << std::endl;‬
‭}‬

‭return 0;‬
‭}‬

‭11.2. Sorting Algorithms (Bubble, Selection, Insertion, Merge, Quick)‬


‭Bubble Sort‬
‭ epeatedly steps through the list, compares adjacent elements and swaps them if‬
R
‭they are in the wrong order.‬
‭●‬ ‭Time Complexity: O(N^2)‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::swap‬

‭void bubbleSort(std::vector<int>& arr) {‬


‭int n = arr.size();‬
‭for (int i = 0; i < n - 1; ++i) {‬
‭// Last i elements are already in place‬
‭for (int j = 0; j < n - i - 1; ++j) {‬
‭if (arr[j] > arr[j + 1]) {‬
‭std::swap(arr[j], arr[j + 1]);‬
‭}‬
‭}‬
‭}‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {64, 34, 25, 12, 22, 11, 90};‬
‭std::cout << "Original array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭bubbleSort(numbers);‬

s‭ td::cout << "Sorted array (Bubble Sort): ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬
‭Selection Sort‬
‭Finds the minimum element from the unsorted part and puts it at the beginning.‬
‭●‬ ‭Time Complexity: O(N^2)‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::swap‬

‭void selectionSort(std::vector<int>& arr) {‬


‭int n = arr.size();‬
‭for (int i = 0; i < n - 1; ++i) {‬
‭int min_idx = i;‬
‭for (int j = i + 1; j < n; ++j) {‬
‭if (arr[j] < arr[min_idx]) {‬
‭min_idx = j;‬
‭}‬
‭}‬
‭// Swap the found minimum element with the first element‬
‭std::swap(arr[min_idx], arr[i]);‬
‭}‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {64, 25, 12, 22, 11};‬
‭std::cout << "Original array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭selectionSort(numbers);‬

s‭ td::cout << "Sorted array (Selection Sort): ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭return 0;‬
‭}‬

‭Insertion Sort‬
‭ uilds the final sorted array one item at a time. It iterates through the input elements‬
B
‭and inserts each element into its correct position in the already sorted part of the‬
‭array.‬
‭●‬ ‭Time Complexity: O(N^2) in worst case, O(N) in best case (already sorted).‬

‭ include <iostream>‬
#
‭#include <vector>‬

‭void insertionSort(std::vector<int>& arr) {‬


‭int n = arr.size();‬
‭for (int i = 1; i < n; ++i) {‬
‭int key = arr[i];‬
‭int j = i - 1;‬

/‭ / Move elements of arr[0..i-1], that are greater than key,‬


‭// to one position ahead of their current position‬
‭while (j >= 0 && arr[j] > key) {‬
‭arr[j + 1] = arr[j];‬
‭j = j - 1;‬
‭}‬
‭arr[j + 1] = key;‬
‭}‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {12, 11, 13, 5, 6};‬
‭std::cout << "Original array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭insertionSort(numbers);‬
s‭ td::cout << "Sorted array (Insertion Sort): ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬

‭Merge Sort‬
‭ divide and conquer algorithm. It divides the input array into two halves, calls itself‬
A
‭for the two halves, and then merges the two sorted halves.‬
‭●‬ ‭Time Complexity: O(N log N)‬
‭●‬ ‭Space Complexity: O(N)‬

‭ include <iostream>‬
#
‭#include <vector>‬

/‭ / Merges two subarrays of arr[].‬


‭// First subarray is arr[left..mid]‬
‭// Second subarray is arr[mid+1..right]‬
‭void merge(std::vector<int>& arr, int left, int mid, int right) {‬
‭int n1 = mid - left + 1;‬
‭int n2 = right - mid;‬

/‭ / Create temporary arrays‬


‭std::vector<int> L(n1);‬
‭std::vector<int> R(n2);‬

/‭ / Copy data to temp arrays L[] and R[]‬


‭for (int i = 0; i < n1; ++i) {‬
‭L[i] = arr[left + i];‬
‭}‬
‭for (int j = 0; j < n2; ++j) {‬
‭R[j] = arr[mid + 1 + j];‬
‭}‬
/‭ / Merge the temp arrays back into arr[left..right]‬
‭int i = 0; // Initial index of first subarray‬
‭int j = 0; // Initial index of second subarray‬
‭int k = left; // Initial index of merged subarray‬

‭while (i < n1 && j < n2) {‬


‭if (L[i] <= R[j]) {‬
‭arr[k] = L[i];‬
‭i++;‬
‭} else {‬
‭arr[k] = R[j];‬
‭j++;‬
‭}‬
‭k++;‬
‭}‬

/‭ / Copy the remaining elements of L[], if any‬


‭while (i < n1) {‬
‭arr[k] = L[i];‬
‭i++;‬
‭k++;‬
‭}‬

/‭ / Copy the remaining elements of R[], if any‬


‭while (j < n2) {‬
‭arr[k] = R[j];‬
‭j++;‬
‭k++;‬
‭}‬
‭}‬

/‭ / Main function that sorts arr[left..right] using merge()‬


‭void mergeSort(std::vector<int>& arr, int left, int right) {‬
‭if (left >= right) { // Base case: array with 0 or 1 element is sorted‬
‭return;‬
‭}‬
‭int mid = left + (right - left) / 2;‬
‭mergeSort(arr, left, mid); // Sort first half‬
‭mergeSort(arr, mid + 1, right); // Sort second half‬
‭merge(arr, left, mid, right); // Merge the sorted halves‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {12, 11, 13, 5, 6, 7};‬
‭std::cout << "Original array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭mergeSort(numbers, 0, numbers.size() - 1);‬

s‭ td::cout << "Sorted array (Merge Sort): ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭return 0;‬
‭}‬

‭Quick Sort‬
‭ divide and conquer algorithm. It picks an element as a pivot and partitions the given‬
A
‭array around the picked pivot.‬
‭●‬ ‭Time Complexity: O(N log N) on average, O(N^2) in worst case.‬
‭●‬ ‭Space Complexity: O(log N) due to recursion stack.‬

‭ include <iostream>‬
#
‭#include <vector>‬
‭#include <algorithm> // For std::swap‬

/‭ / Function to partition the array around a pivot‬


‭// This implementation uses the last element as pivot‬
‭int partition(std::vector<int>& arr, int low, int high) {‬
‭int pivot = arr[high]; // Choose the last element as pivot‬
‭int i = (low - 1); // Index of smaller element‬
‭for (int j = low; j <= high - 1; ++j) {‬
‭// If current element is smaller than or equal to pivot‬
‭if (arr[j] <= pivot) {‬
‭i++; // Increment index of smaller element‬
‭std::swap(arr[i], arr[j]);‬
‭}‬
‭}‬
‭std::swap(arr[i + 1], arr[high]); // Put pivot in its correct sorted position‬
‭return (i + 1);‬
‭}‬

/‭ / Main function that implements QuickSort‬


‭void quickSort(std::vector<int>& arr, int low, int high) {‬
‭if (low < high) {‬
‭// pi is partitioning index, arr[pi] is now at right place‬
‭int pi = partition(arr, low, high);‬

/‭ / Separately sort elements before partition and after partition‬


‭quickSort(arr, low, pi - 1);‬
‭quickSort(arr, pi + 1, high);‬
‭}‬
‭}‬

‭int main() {‬
‭std::vector<int> numbers = {10, 7, 8, 9, 1, 5};‬
‭std::cout << "Original array: ";‬
‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬

‭quickSort(numbers, 0, numbers.size() - 1);‬

s‭ td::cout << "Sorted array (Quick Sort): ";‬


‭for (int num : numbers) {‬
‭std::cout << num << " ";‬
‭}‬
‭std::cout << std::endl;‬
‭return 0;‬
‭}‬

‭11.3. Hashing‬
‭ ashing is a technique to convert a large key into a small key (hash value) that can be‬
H
‭used to store and retrieve data efficiently. Hash tables (like std::unordered_map and‬
‭std::unordered_set) use hashing.‬

‭ include <iostream>‬
#
‭#include <string>‬
‭#include <vector>‬
‭#include <list> // For chaining in case of collisions‬

/‭ / Simple Hash Table implementation using chaining‬


‭class HashTable {‬
‭private:‬
‭int capacity;‬
‭// A vector of lists to handle collisions (chaining)‬
‭std::vector<std::list<std::pair<std::string, int>>> table;‬

/‭ / Simple hash function for strings‬


‭int hashFunction(const std::string& key) {‬
‭long long hash_val = 0;‬
‭for (char c : key) {‬
‭hash_val = (hash_val * 31 + c) % capacity; // A common way to hash strings‬
‭}‬
‭return static_cast<int>(hash_val);‬
‭}‬

‭public:‬
‭HashTable(int size) : capacity(size) {‬
‭table.resize(capacity);‬
‭}‬

/‭ / Insert a key-value pair‬


‭void insert(const std::string& key, int value) {‬
‭int index = hashFunction(key);‬
‭for (auto& pair : table[index]) {‬
‭if (pair.first == key) {‬
‭pair.second = value; // Update value if key already exists‬
‭std::cout << "Updated key: " << key << std::endl;‬
‭return;‬
‭}‬
}‭ ‬
‭table[index].push_back({key, value}); // Add new key-value pair‬
‭std::cout << "Inserted key: " << key << ", value: " << value << " at index " << index <<‬
‭std::endl;‬
‭}‬

/‭ / Search for a key and return its value‬


‭int search(const std::string& key) {‬
‭int index = hashFunction(key);‬
‭for (const auto& pair : table[index]) {‬
‭if (pair.first == key) {‬
‭std::cout << "Found key: " << key << ", value: " << pair.second << std::endl;‬
‭return pair.second;‬
‭}‬
‭}‬
‭std::cout << "Key: " << key << " not found." << std::endl;‬
‭return -1; // Indicate not found‬
‭}‬

/‭ / Delete a key-value pair‬


‭void remove(const std::string& key) {‬
‭int index = hashFunction(key);‬
‭auto& chain = table[index]; // Get the list at this index‬
‭for (auto it = chain.begin(); it != chain.end(); ++it) {‬
‭if (it->first == key) {‬
‭chain.erase(it); // Erase from the list‬
‭std::cout << "Removed key: " << key << std::endl;‬
‭return;‬
‭}‬
‭}‬
‭std::cout << "Key: " << key << " not found for removal." << std::endl;‬
‭}‬

‭void display() {‬
s‭ td::cout << "\n--- Hash Table Contents ---" << std::endl;‬
‭for (int i = 0; i < capacity; ++i) {‬
‭std::cout << "Bucket " << i << ": ";‬
‭if (table[i].empty()) {‬
‭std::cout << "Empty";‬
‭} else {‬
‭for (const auto& pair : table[i]) {‬
‭std::cout << "[" << pair.first << ":" << pair.second << "] ";‬
‭}‬
‭}‬
‭std::cout << std::endl;‬
‭}‬
‭std::cout << "--------------------------" << std::endl;‬
‭}‬
‭};‬

‭int main() {‬
‭HashTable myHashTable(10); // Create a hash table with 10 buckets‬

‭ yHashTable.insert("apple", 10);‬
m
‭myHashTable.insert("banana", 20);‬
‭myHashTable.insert("cherry", 30);‬
‭myHashTable.insert("date", 40); // Might cause collision depending on hash function‬
‭myHashTable.insert("grape", 50);‬

‭myHashTable.display();‬

‭ yHashTable.search("banana");‬
m
‭myHashTable.search("kiwi");‬

‭ yHashTable.insert("apple", 100); // Update existing key‬


m
‭myHashTable.display();‬

‭ yHashTable.remove("cherry");‬
m
‭myHashTable.remove("mango"); // Not found‬

‭myHashTable.display();‬
‭return 0;‬
‭}‬

‭12. File I/O‬


‭Reading from and writing to files.‬

‭ include <iostream>‬
#
‭#include <fstream> // Required for file stream operations‬
‭#include <string>‬

‭int main() {‬
‭std::string filename = "example.txt";‬

/‭ / --- Writing to a file ---‬


‭// Create an ofstream object (output file stream)‬
‭// std::ios::out: open for writing (default)‬
‭// std::ios::trunc: truncate file to 0 length if it exists (default)‬
‭// std::ios::app: append to the end of the file‬
‭std::ofstream outFile(filename, std::ios::out); // Opens file for writing, creates if not‬
‭exists, truncates if exists‬

‭if (outFile.is_open()) {‬
‭outFile << "Hello, C++ File I/O!" << std::endl;‬
‭outFile << "This is the second line." << std::endl;‬
‭outFile << 12345 << std::endl; // Write numbers too‬
‭outFile.close(); // Close the file‬
‭std::cout << "Data written to " << filename << std::endl;‬
‭} else {‬
‭std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl;‬
‭}‬

/‭ / --- Reading from a file ---‬


‭// Create an ifstream object (input file stream)‬
‭std::ifstream inFile(filename); // Opens file for reading‬

‭if (inFile.is_open()) {‬
‭std::string line;‬
s‭ td::cout << "\nReading from " << filename << ":" << std::endl;‬
‭while (std::getline(inFile, line)) { // Read line by line until end of file‬
‭std::cout << line << std::endl;‬
‭}‬
‭inFile.close(); // Close the file‬
‭} else {‬
‭std::cerr << "Error: Could not open file " << filename << " for reading." << std::endl;‬
‭}‬

/‭ / --- Appending to a file ---‬


‭std::ofstream appendFile(filename, std::ios::app); // Open in append mode‬

‭if (appendFile.is_open()) {‬
‭appendFile << "Appended new line." << std::endl;‬
‭appendFile.close();‬
‭std::cout << "\nData appended to " << filename << std::endl;‬
‭} else {‬
‭std::cerr << "Error: Could not open file " << filename << " for appending." <<‬
‭std::endl;‬
‭}‬

/‭ / Read again to see appended content‬


‭std::ifstream inFileAppended(filename);‬
‭if (inFileAppended.is_open()) {‬
‭std::string line;‬
‭std::cout << "\nReading from " << filename << " after appending:" << std::endl;‬
‭while (std::getline(inFileAppended, line)) {‬
‭std::cout << line << std::endl;‬
‭}‬
‭inFileAppended.close();‬
‭}‬

‭return 0;‬
‭}‬

‭ his guide covers the essential C++ concepts and foundational DSA elements. To truly‬
T
‭master these, consistent practice is key. Work through problems on platforms like‬
‭LeetCode, HackerRank, or Codeforces, applying these concepts. Good luck with your‬
‭placements!‬

You might also like