├── Week 1 OOP ├── Delete.txt ├── week-1-oop.zip ├── inheritance_overloading_polymorphism.zip ├── GraduateStudent.cpp ├── Student.cpp ├── GraduateStudent.h ├── Student.h └── main (1).cpp ├── Week 10 Heap ├── deleteme.txt ├── MaxHeap ├── MaxHeap with Insert and Remove └── PriorityQueue ├── Week 3 Search algorithms ├── Week 11 Intro to Graphs ├── deleteme.txt ├── Adjacency List ├── Adjacency Matrix ├── Depth First Search └── Breadth First Search (BFS) ├── Week 12 Graph Algorithms ├── deleteme.txt └── Topological Sort ├── Week 3 Search ├── deleteme.txt ├── Linear search ├── bubbleSort └── Binary search.cpp ├── Week 6 Stack and Queue ├── Deque │ ├── deleteme.txt │ ├── Deque with vector │ └── Deque with linked list ├── Queue │ ├── deleteme.txt │ ├── queue with linked list │ └── queue with vector └── Stack │ ├── deleteme.txt │ ├── Stack with Vector │ └── Linked LIst ├── README.md ├── Week 8 Binary Search Trees (BST) ├── Iterative │ ├── deleteme.txt │ ├── Insert and Print │ ├── Search │ └── Remove └── Recursive │ └── deleteme.txt ├── Week 5 Linked Lists ├── Double Linked List │ ├── deleteme.txt │ ├── Traversal │ ├── prepend.cpp │ ├── append.cpp │ ├── search.cpp │ ├── RemoveNode │ └── InsertNodeAfter.cpp ├── Single Linked Lists │ ├── CreateEmptyList-1.cpp │ ├── Prepend.cpp │ ├── Append.cpp │ ├── Search.cpp │ ├── InsertAfterNode.cpp │ ├── RemoveNode.cpp │ └── In class coding └── Recursion on Linked List ├── Week 2 Pointers ├── main 5.cpp ├── main 8.cpp ├── default-template-arguments.cpp ├── class-templates.cpp ├── main 3.cpp ├── main.cpp ├── multiple-template-parameters.cpp ├── template-specialization.cpp ├── pointer_vectors ├── intro-to-templates.cpp ├── main 6.cpp ├── main 4.cpp ├── Polymorphism └── Template-Exercise-6.cpp ├── Week 4 Sorting ├── QuickSort └── Radix sort ├── Week 7 └── Hash Tables │ └── Hash Table with linked list └── Week 9 AVL Balanced Tree └── AVLNode and AVLTree Class /Week 1 OOP/Delete.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 10 Heap/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 3 Search algorithms: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 11 Intro to Graphs/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 12 Graph Algorithms/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 3 Search/deleteme.txt: -------------------------------------------------------------------------------- 1 | delete me 2 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Deque/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Queue/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Stack/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataStructures 2 | in-class files 3 | -------------------------------------------------------------------------------- /Week 8 Binary Search Trees (BST) /Iterative/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 8 Binary Search Trees (BST) /Recursive/deleteme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/deleteme.txt: -------------------------------------------------------------------------------- 1 | //delete this file 2 | -------------------------------------------------------------------------------- /Week 1 OOP/week-1-oop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkML/DataStructures/HEAD/Week 1 OOP/week-1-oop.zip -------------------------------------------------------------------------------- /Week 1 OOP/inheritance_overloading_polymorphism.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkML/DataStructures/HEAD/Week 1 OOP/inheritance_overloading_polymorphism.zip -------------------------------------------------------------------------------- /Week 1 OOP/GraduateStudent.cpp: -------------------------------------------------------------------------------- 1 | #include "GraduateStudent.h" 2 | 3 | void GraduateStudent::setThesisTitle(string t) { 4 | thesisTitle = t; 5 | } 6 | 7 | string GraduateStudent::getThesisTitle() { 8 | return thesisTitle; 9 | } 10 | -------------------------------------------------------------------------------- /Week 1 OOP/Student.cpp: -------------------------------------------------------------------------------- 1 | #include "Student.h" 2 | 3 | void Student::setName(string n) { 4 | name = n; 5 | } 6 | 7 | void Student::setGPA(double g) { 8 | gpa = g; 9 | } 10 | 11 | string Student::getName() { 12 | return name; 13 | } 14 | 15 | double Student::getGPA() { 16 | return gpa; 17 | } 18 | -------------------------------------------------------------------------------- /Week 1 OOP/GraduateStudent.h: -------------------------------------------------------------------------------- 1 | #ifndef GRADUATESTUDENT_H_ 2 | #define GRADUATESTUDENT_H_ 3 | 4 | #include "Student.h" 5 | 6 | class GraduateStudent : public Student { 7 | public: 8 | void setThesisTitle(string t); 9 | string getThesisTitle(); 10 | 11 | private: 12 | string thesisTitle; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Week 1 OOP/Student.h: -------------------------------------------------------------------------------- 1 | #ifndef STUDENT_H_ 2 | #define STUDENT_H_ 3 | 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | class Student { 9 | public: 10 | void setName(string n); 11 | void setGPA(double g); 12 | string getName(); 13 | double getGPA(); 14 | 15 | private: 16 | string name; 17 | double gpa; 18 | }; 19 | 20 | #endif -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/Traversal: -------------------------------------------------------------------------------- 1 | // forward andreverse traversal thru the double linked list 2 | 3 | 4 | void Traverse() const { 5 | DoublyLinkedNode* curNode = head; //start at head 6 | 7 | while (curNode != nullptr){ 8 | cout << curNode->data << " "; 9 | curNode = curNode->next; 10 | } 11 | } 12 | 13 | void TraverseReverse() const { 14 | DoublyLinkedNode* curNode = tail; // Start at tail 15 | 16 | while (curNode) { 17 | cout << curNode->data << " "; 18 | curNode = curNode->previous; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Week 2 Pointers/main 5.cpp: -------------------------------------------------------------------------------- 1 | 2 | //Simple Swap with Single Pointers 3 | 4 | /* Swap function pass by reference ("var a and b are alias for variables") 5 | void swap(int& a, int& b) { 6 | int temp = a; 7 | a = b; 8 | b = temp; 9 | } 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | void swap(int* a, int* b) { 16 | int temp = *a; // Swap values using pointers. we dereference 17 | *a = *b; 18 | *b = temp; 19 | } 20 | 21 | int main() { 22 | int x = 5, y = 10; 23 | cout << "Before swap: x = " << x << ", y = " << y << endl; 24 | swap(&x, &y); 25 | cout << "After swap: x = " << x << ", y = " << y << endl; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /Week 1 OOP/main (1).cpp: -------------------------------------------------------------------------------- 1 | #include "GraduateStudent.h" 2 | 3 | int main() { 4 | // Create a GraduateStudent object 5 | GraduateStudent gradStudent; 6 | 7 | // Set values using inherited methods 8 | gradStudent.setName("Alice"); 9 | gradStudent.setGPA(3.8); 10 | 11 | // Set thesis title specific to GraduateStudent 12 | gradStudent.setThesisTitle("Machine Learning in Healthcare"); 13 | 14 | // Display information 15 | cout << "Graduate Student Details:" << endl; 16 | cout << "Name: " << gradStudent.getName() << endl; 17 | cout << "GPA: " << gradStudent.getGPA() << endl; 18 | cout << "Thesis Title: " << gradStudent.getThesisTitle() << endl; 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /Week 2 Pointers/main 8.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | pass by reference using pointers as function arguments 3 | 4 | *******************************************************************************/ 5 | #include 6 | 7 | using namespace std; 8 | 9 | void Increment(int *a) { 10 | *a = *a + 1; //dereference a to get its value and then add 1 and store it 11 | } 12 | 13 | int main() 14 | { 15 | int a = 10; 16 | cout << "before function call a is: " << a << endl; 17 | Increment(&a); 18 | cout << "after function call a is: " << a << endl; 19 | Increment(&a); 20 | cout << "after 2nd function call a is: " << a << endl; 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/CreateEmptyList-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | //individual nodes in the linked list 5 | class Node { 6 | public: 7 | int data; // data of the node: age, student id, membership id, etc 8 | Node* next; // Pointer to the next node 9 | 10 | //Constructor to initialize the node with data 11 | Node(int value) : data(value), next(nullptr) {} 12 | }; 13 | 14 | // numList class will manage the entire linked list 15 | class Linked_list { 16 | private: 17 | Node* head; 18 | Node* tail; 19 | public: 20 | Linked_list() : head(nullptr), tail(nullptr) {} 21 | }; 22 | 23 | int main(){ 24 | 25 | Linked_list numList; //create empty list 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /Week 2 Pointers/default-template-arguments.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Default Template Arguments 4 | Templates can have default arguments, providing fallback type 5 | if none is specified 6 | 7 | *******************************************************************************/ 8 | #include 9 | using namespace std; 10 | 11 | template 12 | class Box { 13 | T value; 14 | public: 15 | Box(T val) : value(val) {} 16 | T getValue() { 17 | return value; 18 | } 19 | }; 20 | 21 | int main() 22 | { 23 | Box<> defaultBox(5.2); // uses int as default type 24 | cout << defaultBox.getValue() << endl; 25 | 26 | Box anotherBox(3.14); 27 | cout << anotherBox.getValue() << endl; 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /Week 2 Pointers/class-templates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | /* 4 | 5 | Class templates allow you to define a class that work with different data types 6 | 7 | template 8 | class ClassName { 9 | T data; // generic data types 10 | public: 11 | ClassName(T value) : data(value) {} 12 | T getValue() { 13 | return data; 14 | } 15 | } 16 | 17 | */ 18 | 19 | template 20 | class Box { 21 | T value; 22 | public: 23 | Box(T val) : value(val) {} 24 | T getValue() { 25 | return value; 26 | } 27 | }; 28 | 29 | int main() { 30 | Box intBox(10); // Box instance of type int 31 | Box doubleBox(3.14); // Box instance of type double 32 | 33 | cout << intBox.getValue() << endl; 34 | cout << doubleBox.getValue() << endl; 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /Week 2 Pointers/main 3.cpp: -------------------------------------------------------------------------------- 1 | //double pointers part 2 2 | 3 | #include 4 | using namespace std; 5 | 6 | int main(){ 7 | int num = 10; 8 | int* ptr1 = # 9 | int** ptr2 = &ptr1; 10 | int*** ptr3 = &ptr2; 11 | 12 | cout << "address of num is: " << &num << endl; 13 | cout << "value of num is: " << num << endl; 14 | cout << "value of ptr1 is: " << ptr1 << endl; 15 | cout << "value of ptr2 is: " << ptr2 << endl; 16 | 17 | cout << "value of *ptr1 is " << *ptr1 << endl; 18 | cout << "value of *ptr2 is " << *ptr2 << endl; 19 | cout << "value of **ptr2 is " << **ptr2 << endl; 20 | 21 | cout << "value of ptr3 is: " << ptr3 << endl; 22 | cout << "value of *ptr3 is: " << *ptr3 << endl; 23 | cout << "value of **ptr3 is: " << **ptr3 << " " << *(*ptr3) << endl; 24 | cout << "value of ***ptr3 is: " << ***ptr3 << " " << *(*(*ptr3)) << endl; 25 | } -------------------------------------------------------------------------------- /Week 2 Pointers/main.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Intro to Stack and Heap 4 | operators: new and delete 5 | 6 | *******************************************************************************/ 7 | #include 8 | 9 | using namespace std; 10 | 11 | #include 12 | using namespace std; 13 | 14 | int main() { 15 | int* num1 = new int(11); // a memory allocation of size int is made on the Heap 16 | int* num2 = num1; 17 | 18 | cout << "num1 = " << num1 << endl; 19 | cout << "num2 = " << num2 << endl; 20 | 21 | cout << "num1 = " << *num1 << endl; 22 | cout << "num2 = " << *num2 << endl; 23 | 24 | 25 | delete num1; // Deallocate the dynamically allocated memory on the Heap 26 | 27 | num1 = nullptr; //after delete, the pointers still point to the freed memory location 28 | num2 = nullptr; //always set pointers to nullptr after de-allocating to avoid "dangling pointer" problem 29 | return 0; //pointers get deleted when they fall out of scope 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /Week 2 Pointers/multiple-template-parameters.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Multiple Template Parameters 3 | Define templates with multiple parameters for more flexibility 4 | 5 | template 6 | class ClassName { 7 | T1 data1; 8 | T2 data2; 9 | public: 10 | ClassName(T1 value, T2 value2) : data1(value1), data2(value2) {} 11 | void display() { 12 | cout << data1 << " and " << data2 << endl; 13 | } 14 | } 15 | *******************************************************************************/ 16 | #include 17 | using namespace std; 18 | 19 | template 20 | class Pair { 21 | T1 first; 22 | T2 second; 23 | public: 24 | Pair(T1 a, T2 b) : first(a), second(b) {} 25 | void display() { 26 | cout << first << ", " << second << endl; 27 | } 28 | }; 29 | 30 | int main() 31 | { 32 | Pair myPair(10, 3.14); 33 | myPair.display(); 34 | 35 | Pair anotherPair("Hello", '#'); 36 | anotherPair.display(); 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /Week 10 Heap/MaxHeap: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class MaxHeap { 5 | private: 6 | int* heapArray; 7 | int allocationSize; 8 | int heapSize; 9 | 10 | void ResizeArray() { 11 | int newAllocationSize = allocationSize * 2; 12 | int* newArray = new int[newAllocationSize]; 13 | if (newArray) { 14 | // Copy from existing array to new array 15 | for (int i = 0; i < heapSize; i++) { 16 | newArray[i] = heapArray[i]; 17 | } 18 | 19 | // Delete old array and set pointer to new 20 | delete[] heapArray; 21 | heapArray = newArray; 22 | 23 | // Update allocation size 24 | allocationSize = newAllocationSize; 25 | } 26 | } 27 | 28 | public: 29 | MaxHeap() { 30 | allocationSize = 1; 31 | heapArray = new int[allocationSize]; 32 | heapSize = 0; 33 | } 34 | 35 | ~MaxHeap() { 36 | delete[] heapArray; 37 | } 38 | 39 | 40 | }; 41 | 42 | int main(){ 43 | 44 | MaxHeap* myMaxHeap = new MaxHeap(); 45 | myMaxHeap ? cout << "myMaxHeap is created" : cout << "error!"; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Week 4 Sorting /QuickSort: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | void Swap(int* arr, int i, int j){ 6 | int temp = arr[i]; 7 | arr[i] =arr[j]; 8 | arr[j] = temp; 9 | } 10 | 11 | int Partition(int arr[], int low, int high) { 12 | int pivot = arr[high]; 13 | int i = low - 1; 14 | for (int j = low; j < high; j++) { 15 | if (arr[j] <= pivot) { 16 | i++; 17 | Swap(arr, i, j); 18 | } 19 | } 20 | Swap(arr, i + 1, high); 21 | return i + 1; 22 | } 23 | 24 | void QuickSort(int arr[], int low, int high) { 25 | if (low < high) { 26 | int p = Partition(arr, low, high); 27 | QuickSort(arr, low, p - 1); 28 | QuickSort(arr, p + 1, high); 29 | } 30 | } 31 | 32 | int main() { 33 | //int Arr[] = {13, 8, 6, 5, 63, 96, 5, 10}; 34 | //int Arr[] = { 47, 3, 15, 89, 22, 7, 61, 11}; 35 | int Arr[] = {47, 3, 15, 89, 22, 7, 61, 10}; 36 | int n = sizeof(Arr) / sizeof(Arr[0]); 37 | 38 | QuickSort(Arr, 0, n - 1); 39 | 40 | cout << "Sorted List\n"; 41 | for (int i = 0; i < n; i++) { 42 | cout << Arr[i] << " "; 43 | } 44 | cout << endl; 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /Week 3 Search/Linear search: -------------------------------------------------------------------------------- 1 | //Linear search starts at the first element and compares elements one-by-one 2 | //Linear seaerch compares all elements in the array if the search key is not present in the array. 3 | 4 | #include 5 | using namespace std; 6 | 7 | int LinearSearch(const int* numbers, int numbersSize, int key) { 8 | for (int i = 0; i < numbersSize; i++) { 9 | if (numbers[i] == key) { 10 | return i; 11 | } 12 | } 13 | 14 | return -1; // not found 15 | } 16 | 17 | int main() { 18 | int numbers[] = {2, 4, 7, 10, 11, 32, 45, 87}; 19 | const int numbersSize = sizeof(numbers) / sizeof(numbers[0]); 20 | int key = 0; 21 | 22 | cout << "NUMBERS: "; 23 | for (int i = 0; i < numbersSize; i++) { 24 | cout << numbers[i] << " "; 25 | } 26 | cout << endl; 27 | 28 | cout << "Enter a value: "; 29 | cin >> key; 30 | 31 | int keyIndex = LinearSearch(numbers, numbersSize, key); 32 | 33 | if (keyIndex == -1) { 34 | cout << key << " was not found." << endl; 35 | } 36 | else { 37 | cout << "Found " << key << " at index " << keyIndex << "." << endl; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /Week 2 Pointers/template-specialization.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Template specialization 3 | Assign different behavior for a specific type. 4 | sometimes a partiular type such as 'string' requires a behavior that 5 | deviates from generic behavior defined by the template 6 | 7 | template<> 8 | class Classname { 9 | // specialized implementation for SpecificType 10 | } 11 | *******************************************************************************/ 12 | #include 13 | using namespace std; 14 | 15 | template 16 | class Box { 17 | T value; 18 | public: 19 | Box(T val) : value(val) {} 20 | T getValue(){ 21 | return value; 22 | } 23 | }; 24 | // describe specialized behaviro for string type 25 | template<> 26 | class Box { 27 | string value; 28 | public: 29 | Box(string val) : value("String: " + val) {} 30 | string getValue() { 31 | return value; 32 | } 33 | }; 34 | 35 | int main() 36 | { 37 | Box intBox(10); 38 | Box stringBox("Hello"); 39 | 40 | cout << intBox.getValue() << endl; 41 | cout << stringBox.getValue() << endl; 42 | return 0; 43 | } -------------------------------------------------------------------------------- /Week 2 Pointers/pointer_vectors: -------------------------------------------------------------------------------- 1 | // we can pass vectors as function arguments 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | // Step 1: Function that takes a pointer to a vector 8 | int SumOfElements(vector* vecPtr) { 9 | int sum = 0; 10 | 11 | // Step 2: Check if the pointer is valid 12 | // If vecPtr is nullptr, dereferencing it would cause a crash. 13 | if (vecPtr == nullptr) { 14 | return -1; // Return -1 to indicate an error 15 | } 16 | 17 | // Step 3: Iterate through the vector using pointer access 18 | // vecPtr->size() is equivalent to (*vecPtr).size() 19 | for (int i = 0; i < vecPtr->size(); i++) { 20 | sum += (*vecPtr)[i]; // Dereference pointer, then access element 21 | } 22 | 23 | // Step 4: Return the computed sum 24 | return sum; 25 | } 26 | 27 | int main() { 28 | // Step 5: Create a vector of integers 29 | vector A = {1, 2, 3, 4, 5, 6, 7}; 30 | 31 | // Step 6: Pass the address of vector A (&A) to the function 32 | // This allows the function to access the same vector without copying it 33 | int total = SumOfElements(&A); 34 | 35 | // Step 7: Print the result 36 | cout << total; 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /Week 3 Search/bubbleSort: -------------------------------------------------------------------------------- 1 | // bubbleSort sorting algorithm with early loop termination if list is already sorted 2 | 3 | #include 4 | using namespace std; 5 | 6 | void swap(int* a, int* b); 7 | void bubbleSort(int array[], int size); 8 | 9 | int main(){ 10 | //create array of unsorted ints 11 | int array[] = {9,2,5,8,3,1,10}; 12 | //calculate # of elements in array 13 | int size = sizeof(array) / sizeof(array[0]); 14 | bubbleSort(array, size); 15 | //print sorted array 16 | for(int i = 0; i < size; i++){ 17 | cout << array[i] << " "; 18 | } 19 | return 0; 20 | } 21 | 22 | void swap(int* a, int* b){ 23 | int temp; 24 | temp = *a; 25 | *a = *b; 26 | *b = temp; 27 | } 28 | 29 | void bubbleSort(int array[], int size){ 30 | // check variable to short circuit the loop in case the list is sorted 31 | bool swapped = false; 32 | for(int i = size - 1; i > 0; i--){ 33 | for(int j = 0; j < i; j++){ 34 | if(array[j] > array[j+1]){ //compare 2 numbers 35 | swap(&array[j], &array[j+1]); 36 | swapped = true; 37 | } 38 | } 39 | if(swapped != true){ 40 | break; 41 | } 42 | } 43 | 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /Week 2 Pointers/intro-to-templates.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Function templates allow you to write a single function that works with any data type. 4 | 5 | *******************************************************************************/ 6 | #include 7 | using namespace std; 8 | 9 | // step 1. Define a function template 10 | template 11 | T add(T a, T b) { //'T' is a placeholder for a data type 12 | return a + b; // generic function for any type T where '+' operation is valid 13 | } 14 | 15 | int main() { 16 | //ex1. compiler deduces that T is int because both arguments are ints 17 | int intResult = add(5,10); 18 | cout << "Integer addition: " << intResult << endl; 19 | 20 | //ex2. compiler is explicitly told T is int, useful for mixed types 21 | int mixedResult = add(5,10.5); 22 | cout << "Integer plus Float: " << mixedResult << endl; 23 | 24 | //ex3. use template with floating point data typename 25 | double doubleResult = add(5.5, 2.3); 26 | cout << "Double addition: " << doubleResult << endl; 27 | 28 | //ex4.use template with strings 29 | string stringResult = add("Hello,", "World!"); 30 | cout << "String concatenation: " << stringResult << endl; 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /Week 2 Pointers/main 6.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Relevance to Data Structures: 3 | // Understanding basic pointer operations is crucial for implementing dynamic data structures. 4 | // Pointers allow direct memory access, enabling efficient manipulation of arrays, linked lists, and trees. 5 | // Pointer arithmetic helps traverse arrays or allocate contiguous memory blocks dynamically. 6 | 7 | #include 8 | using namespace std; 9 | int main() 10 | { 11 | int arr[] = {1,2,3,4,5}; 12 | 13 | int *ptr = arr; 14 | 15 | cout << arr << endl; //arr is the address of the first element in the array which is index 0, 16 | cout << *arr << endl; // dereferencing arr pointer gives us value at the address 17 | 18 | cout << arr[0] << endl; 19 | cout << arr + 1 << endl; // since arr hold integer data type it adds 4 bytes to the base address 20 | cout << *(arr + 1); 21 | 22 | int A[] = {2,4,5,8,1}; 23 | cout << A << endl; 24 | cout << &A[0] << endl; 25 | cout << A[0] << endl; 26 | cout << *A << endl; 27 | 28 | // loop thru to print elements in the array 29 | for (int i = 0; i < 5; i++) { 30 | cout << A[i] << " "; 31 | } 32 | cout << endl; 33 | // using pointers to interate thru the array we use pointer arithmetic 34 | // useful in low-level programming or embedded systems to directly access memory 35 | for (int i = 0; i < 5; ++i) { 36 | cout << *(A + i) << " "; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Week 2 Pointers/main 4.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Pointer-to-Pointer can be useful for 3 | 1. modifying original pointers. i.e. change the address stored in a pointer 4 | 2. dybnamic memory amangement i.e. managing an array of pointers or multi-dimensional arrays. 5 | 3. complex data structures: linked liss, tree, graphs, i.e. nodes with pointers 6 | 4. function parameters 7 | */ 8 | 9 | 10 | 11 | #include 12 | using namespace std; 13 | 14 | void swap(int** a, int** b){ 15 | int temp = **a; 16 | **a = **b; 17 | **b = temp; 18 | } 19 | 20 | int main(){ 21 | 22 | int num1 = 20; 23 | int num2 = 40; 24 | //create pointers 25 | int* ptr1 = &num1; 26 | int* ptr2 = &num2; 27 | //swap using pointer to pointer 28 | 29 | cout << "Befores swap: num1 = " << *ptr1 << " num2 = " << *ptr2 << endl; 30 | swap(&ptr1, &ptr2); //&ptr1 is type int** 31 | //after swap 32 | cout << "After swap: num1 = " << num1 << " num2 = " << num2 << endl; 33 | } 34 | 35 | /* 36 | 37 | void swap(int** a, int** b) { // a and b are pointer to a pointer. var a receives an address 38 | int temp = **a; 39 | **a = **b; 40 | **b = temp; 41 | } 42 | 43 | int main() { 44 | int num1 = 20, num2 = 40; 45 | int* ptr1 = &num1; 46 | int* ptr2 = &num2; 47 | 48 | cout << "Before swap: num1 = " << *ptr1 << ", num2 = " << *ptr2 << endl; 49 | swap(&ptr1, &ptr2); // pass address of pointers 50 | cout << "After swap: num1 = " << *ptr1 << ", num2 = " << *ptr2 << endl; 51 | 52 | return 0; 53 | } 54 | */ 55 | -------------------------------------------------------------------------------- /Week 2 Pointers/Polymorphism: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Polymorphism with virtual functions 4 | 5 | *******************************************************************************/ 6 | #include 7 | 8 | using namespace std; 9 | 10 | class Student { 11 | public: 12 | string name; 13 | Student(string name) : name(name) {} 14 | // keyword "virtual" makes it a virtual function 15 | virtual void print() { 16 | cout << name << endl; 17 | } 18 | }; 19 | 20 | //Derived class from base class Student 21 | class CoEStudent : public Student { 22 | public: 23 | string major; 24 | CoEStudent(string name, string major) : Student(name), major(major) {} 25 | void print() { 26 | cout << name << ": " << major << endl; 27 | } 28 | 29 | }; 30 | int main() 31 | { 32 | Student bob("Bob"); 33 | Student dave("Dave"); 34 | Student jennifer("Jennifer"); 35 | CoEStudent john("John","computer science"); 36 | CoEStudent frank("Frank","software engineering"); 37 | CoEStudent mary("Mary", "ECE"); 38 | 39 | Student* students[] = { &bob, &dave, &jennifer, &john, &frank, &mary}; 40 | 41 | for (int i = 0; i < 6; i++) { 42 | students[i]->print(); 43 | //the actual print() method executed is determined at runtime based on 44 | //the actual type of the object. This is known as dynamic or late binding. 45 | } 46 | 47 | 48 | 49 | 50 | return 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Week 3 Search/Binary search.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | binary search, checks the middle element of the list first. The list must be sorted first, then binary search checks the middle element in the list to match. 3 | binary search will then search the first half and otherwise the last half. Each step reduces the "search space" that need to be searched by half. 4 | */ 5 | 6 | #include 7 | using namespace std; 8 | 9 | int BinarySearch(const int* numbers, int numbersSize, int key) { 10 | int low = 0; 11 | int high = numbersSize - 1; 12 | 13 | while (high >= low) { 14 | int mid = (high + low) / 2; 15 | if (numbers[mid] < key) { 16 | low = mid + 1; 17 | } 18 | else if (numbers[mid] > key) { 19 | high = mid - 1; 20 | } 21 | else { 22 | return mid; 23 | } 24 | } 25 | 26 | return -1; // not found 27 | } 28 | 29 | int main() { 30 | int numbers[] = { 2, 4, 7, 10, 11, 32, 45, 87 }; 31 | const int numbersSize = sizeof(numbers) / sizeof(numbers[0]); 32 | int key = 0; 33 | 34 | cout << "NUMBERS: "; 35 | for (int i = 0; i < numbersSize; i++) { 36 | cout << numbers[i] << " "; 37 | } 38 | cout << endl; 39 | 40 | cout << "Enter a value: "; 41 | cin >> key; 42 | 43 | int keyIndex = BinarySearch(numbers, numbersSize, key); 44 | 45 | if (keyIndex == -1) { 46 | cout << key << " was not found." << endl; 47 | } 48 | else { 49 | cout << "Found " << key << " at index " << keyIndex << "." << endl; 50 | } 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/Prepend.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Node { 6 | public: 7 | int data; 8 | Node* next; 9 | 10 | Node(int value) : data(value), next(nullptr) {} 11 | }; 12 | 13 | class LinkedList { 14 | private: 15 | Node* head; 16 | Node* tail; 17 | public: 18 | LinkedList() : head(nullptr), tail(nullptr) {} 19 | 20 | // member function to prepend a node 21 | void prepend(int value){ 22 | Node* newNode = new Node(value); // call Node constructor and save node in Heap memory 23 | 24 | if(head == nullptr){ // check if List is empty 25 | head = newNode; 26 | tail = newNode; 27 | } else { // list is not empty 28 | newNode->next = head; // new node points to 1st node in list 29 | head = newNode; // update head pointer to point to new Node which is now 1st node in list 30 | } 31 | } 32 | 33 | void printList() { 34 | Node* temp = head; 35 | while (temp != nullptr){ 36 | cout << temp->data << " "; 37 | temp = temp->next; 38 | } 39 | cout << endl; // Optional, just to print a newline after list is printed 40 | } 41 | }; 42 | 43 | int main() { 44 | LinkedList* myLinkedList = new LinkedList(); //use the default constructor 45 | myLinkedList->prepend(5); // Prepend a node 46 | myLinkedList->prepend(6); 47 | myLinkedList->printList(); // Corrected function name to 'printList' 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/Append.cpp: -------------------------------------------------------------------------------- 1 | //Append to front of Linked List 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Node { 7 | public: 8 | int data; 9 | Node* next; 10 | 11 | //constructor 12 | Node(int value) : data(value), next(nullptr) {} 13 | //destructor 14 | ~Node() {} 15 | 16 | }; 17 | 18 | 19 | class LinkedList { 20 | private: 21 | Node* head; 22 | Node* tail; 23 | 24 | public: //create a empty list 25 | LinkedList() : head(nullptr), tail(nullptr) {} 26 | 27 | ~LinkedList() { 28 | Node* temp = head; 29 | while(head) { 30 | head = head->next; 31 | delete temp; 32 | temp = head; 33 | } 34 | } 35 | 36 | 37 | void printList() { 38 | Node* temp = head; 39 | while (temp != nullptr){ 40 | cout << temp->data << " "; 41 | temp = temp->next; 42 | } 43 | } 44 | 45 | void append(int value){ 46 | Node* newNode = new Node(value); 47 | //check if LL is empty 48 | if (head == nullptr) { 49 | head = newNode; 50 | tail = newNode; 51 | } else { 52 | tail->next = newNode; 53 | tail = newNode; 54 | 55 | } 56 | } 57 | 58 | 59 | }; 60 | 61 | using namespace std; 62 | 63 | int main() 64 | { 65 | 66 | LinkedList* myLinkedList = new LinkedList(); 67 | 68 | myLinkedList->append(3); 69 | myLinkedList->printList(); 70 | cout << endl; 71 | 72 | myLinkedList->append(1); 73 | myLinkedList->printList(); 74 | cout << endl; 75 | 76 | myLinkedList->append(100); 77 | myLinkedList->printList(); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Deque/Deque with vector: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | using namespace std; 6 | 7 | class Deque { 8 | private: 9 | vector data; 10 | 11 | public: 12 | Deque() {} 13 | 14 | void push_front(int val) { 15 | data.insert(data.begin(), val); 16 | } 17 | 18 | void push_back(int val) { 19 | data.push_back(val); 20 | } 21 | 22 | void pop_front() { 23 | if (empty()) { 24 | throw out_of_range("Deque is empty"); 25 | } 26 | data.erase(data.begin()); 27 | } 28 | 29 | void pop_back() { 30 | if (empty()) { 31 | throw out_of_range("Deque is empty"); 32 | } 33 | data.pop_back(); 34 | } 35 | 36 | int front() { 37 | if (empty()) { 38 | throw out_of_range("Deque is empty"); 39 | } 40 | return data.front(); 41 | } 42 | 43 | int back() { 44 | if (empty()) { 45 | throw out_of_range("Deque is empty"); 46 | } 47 | return data.back(); 48 | } 49 | 50 | bool empty() const { 51 | return data.empty(); 52 | } 53 | 54 | size_t getSize() const { 55 | return data.size(); 56 | } 57 | }; 58 | 59 | int main() { 60 | Deque dq; 61 | 62 | // Push elements to the front 63 | dq.push_front(1); 64 | dq.push_front(2); 65 | dq.push_front(3); 66 | 67 | // Push elements to the back 68 | dq.push_back(4); 69 | dq.push_back(5); 70 | dq.push_back(6); 71 | 72 | // Output front and back elements 73 | cout << "Front element: " << dq.front() << endl; 74 | cout << "Back element: " << dq.back() << endl; 75 | 76 | // Pop elements from front and back 77 | dq.pop_front(); 78 | dq.pop_back(); 79 | 80 | // Output front and back elements after popping 81 | cout << "Front element after pop: " << dq.front() << endl; 82 | cout << "Back element after pop: " << dq.back() << endl; 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/Search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // Node class definition 5 | class Node { 6 | public: 7 | int data; 8 | Node* next; 9 | 10 | // Constructor 11 | Node(int value) : data(value), next(nullptr) {} 12 | }; 13 | 14 | // List class definition 15 | class List { 16 | public: 17 | Node* head; 18 | Node* tail; 19 | 20 | // Constructor 21 | List() : head(nullptr), tail(nullptr) {} 22 | 23 | // search for a key in the list 24 | Node* ListSearch(int key) { 25 | Node* curNode = head; 26 | while (curNode != nullptr) { 27 | if (curNode->data == key) { 28 | return curNode; 29 | } 30 | curNode = curNode->next; 31 | } 32 | return nullptr; 33 | } 34 | 35 | // Function to append a node to the end of the list 36 | void ListAppend(int value) { 37 | Node* newNode = new Node(value); 38 | if (head == nullptr) { // Check if the list is empty 39 | head = newNode; // If empty, set both head and tail to the new node 40 | tail = newNode; 41 | } else { 42 | tail->next = newNode; // If not empty, append the new node after the current tail 43 | tail = newNode; // Update the tail pointer to the new node 44 | } 45 | } 46 | }; 47 | 48 | int main() { 49 | // Create a list 50 | List myList; 51 | 52 | // Append six elements to the list 53 | myList.ListAppend(10); 54 | myList.ListAppend(20); 55 | myList.ListAppend(30); 56 | myList.ListAppend(40); 57 | myList.ListAppend(50); 58 | myList.ListAppend(60); 59 | 60 | // Search for a key in the list 61 | int key = 40; 62 | Node* result = myList.ListSearch(key); 63 | 64 | // Display the search result 65 | if (result != nullptr) { 66 | cout << "Key " << key << " found in the list." << endl; 67 | } else { 68 | cout << "Key " << key << " not found in the list." << endl; 69 | } 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Queue/queue with linked list: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Node { 6 | public: 7 | int data; 8 | Node* next; 9 | 10 | Node(int d) : data(d), next(nullptr) {} 11 | }; 12 | 13 | class LinkedListQueue { 14 | private: 15 | Node* head; 16 | Node* tail; 17 | 18 | public: 19 | LinkedListQueue() : head(nullptr), tail(nullptr) {} 20 | 21 | void enqueue(int item) { 22 | Node* newNode = new Node(item); 23 | if (isEmpty()) { 24 | head = tail = newNode; 25 | } else { 26 | tail->next = newNode; 27 | tail = newNode; 28 | } 29 | } 30 | 31 | int dequeue() { 32 | if (isEmpty()) { 33 | cout << "Error: Queue is empty!" << endl; 34 | return -1; // Return a default value or throw an exception 35 | } 36 | 37 | int dequeuedData = head->data; 38 | Node* temp = head; 39 | head = head->next; 40 | delete temp; 41 | if (head == nullptr) { 42 | tail = nullptr; // Update tail if the queue becomes empty 43 | } 44 | return dequeuedData; 45 | } 46 | 47 | bool isEmpty() { 48 | return head == nullptr; 49 | } 50 | 51 | void printQueue() { 52 | Node* current = head; 53 | cout << "Queue: "; 54 | while (current != nullptr) { 55 | cout << current->data << " "; 56 | current = current->next; 57 | } 58 | cout << endl; 59 | } 60 | }; 61 | 62 | int main() { 63 | LinkedListQueue queue; 64 | 65 | // Enqueue some elements into the queue 66 | queue.enqueue(5); 67 | queue.enqueue(10); 68 | queue.enqueue(15); 69 | queue.enqueue(20); 70 | 71 | // Print the queue 72 | queue.printQueue(); // Output: Queue: 5 10 15 20 73 | 74 | // Dequeue elements from the queue 75 | cout << "Dequeued: " << queue.dequeue() << endl; // Output: Dequeued: 5 76 | cout << "Dequeued: " << queue.dequeue() << endl; // Output: Dequeued: 10 77 | 78 | // Print the queue after dequeuing 79 | queue.printQueue(); // Output: Queue: 15 20 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /Week 11 Intro to Graphs/Adjacency List: -------------------------------------------------------------------------------- 1 | //Undirected Graph using Adjacency List 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | class Graph { 8 | private: 9 | vector> adjList; 10 | 11 | public: 12 | Graph(int vertices) : adjList(vertices) {} 13 | 14 | // Add undirected edge u--v 15 | void addEdge(int u, int v) { 16 | adjList[u].push_back(v); 17 | adjList[v].push_back(u); 18 | } 19 | 20 | void printGraph() const { 21 | for (int i = 0; i < (int)adjList.size(); ++i) { 22 | cout << i << ": "; 23 | for (int neighbor : adjList[i]) { 24 | cout << neighbor << " "; 25 | } 26 | cout << endl; 27 | } 28 | } 29 | 30 | // get degree(v) of a vertex 31 | int degree(int v) const { 32 | return adjList[v].size(); 33 | } 34 | 35 | // check if there is edge between vertices u and v 36 | bool hasEdge(int u, int v) const { 37 | for (int neighbor : adjList[u]) { 38 | if (neighbor == v) 39 | return true; 40 | } 41 | return false; 42 | } 43 | }; 44 | 45 | int main() { 46 | int vertices = 5; 47 | Graph g(vertices); 48 | 49 | // Build: 50 | // 0 -- 1 -- 2 51 | // | | 52 | // 4 --------3 53 | g.addEdge(0,1); 54 | g.addEdge(1,2); 55 | g.addEdge(2,3); 56 | g.addEdge(3,4); 57 | g.addEdge(4,0); 58 | 59 | cout << "Adjacency List:\n"; 60 | g.printGraph(); 61 | cout << endl; 62 | 63 | 64 | cout << "Degree of vertex 0: " << g.degree(0) << endl; 65 | cout << "Degree of vertex 3: " << g.degree(3) << endl; 66 | cout << endl; 67 | 68 | // --- USER INPUT SECTION --- 69 | int u, v; 70 | cout << "Enter two vertices to check if an edge exists between them:\n"; 71 | cout << "u: "; 72 | cin >> u; 73 | cout << "v: "; 74 | cin >> v; 75 | 76 | if (u < 0 || u >= vertices || v < 0 || v >= vertices) { 77 | cout << "Invalid vertex index! Must be between 0 and " 78 | << vertices - 1 << "." << endl; 79 | return 0; 80 | } 81 | 82 | cout << "Does an edge exist between " << u << " and " << v << "? " 83 | << (g.hasEdge(u, v) ? "Yes" : "No") << endl; 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /Week 11 Intro to Graphs/Adjacency Matrix: -------------------------------------------------------------------------------- 1 | //Adjacency matrix 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | class Graph { 8 | private: 9 | vector> adjMatrix; // 2D matrix 10 | int numVertices; 11 | 12 | public: 13 | Graph(int vertices) : numVertices(vertices) { 14 | // Initialize matrix with all zeros 15 | adjMatrix = vector>(vertices, vector(vertices, 0)); 16 | } 17 | 18 | // Add undirected edge u--v 19 | void addEdge(int u, int v) { 20 | adjMatrix[u][v] = 1; 21 | adjMatrix[v][u] = 1; // because undirected 22 | } 23 | 24 | // Print the adjacency matrix 25 | void printMatrix() const { 26 | cout << "Adjacency Matrix:\n"; 27 | for (int i = 0; i < numVertices; i++) { 28 | for (int j = 0; j < numVertices; j++) { 29 | cout << adjMatrix[i][j] << " "; 30 | } 31 | cout << endl; 32 | } 33 | } 34 | 35 | // 1. Degree of a vertex 36 | int degree(int v) const { 37 | int count = 0; 38 | for (int j = 0; j < numVertices; j++) { 39 | if (adjMatrix[v][j] == 1) 40 | count++; 41 | } 42 | return count; 43 | } 44 | 45 | // 2. Check if an edge exists between u and v 46 | bool hasEdge(int u, int v) const { 47 | return adjMatrix[u][v] == 1; 48 | } 49 | }; 50 | 51 | int main() { 52 | int vertices = 5; 53 | Graph g(vertices); 54 | 55 | //graph g: 56 | // 0 -- 1 -- 2 57 | // | | 58 | // 4 --------3 59 | g.addEdge(0,1); 60 | g.addEdge(1,2); 61 | g.addEdge(2,3); 62 | g.addEdge(3,4); 63 | g.addEdge(4,0); 64 | 65 | g.printMatrix(); 66 | cout << endl; 67 | 68 | cout << "Degree of vertex 0: " << g.degree(0) << endl; 69 | cout << "Degree of vertex 3: " << g.degree(3) << endl; 70 | cout << endl; 71 | 72 | int u, v; 73 | cout << "Enter two vertices to check for an edge:\n"; 74 | cin >> u >> v; 75 | 76 | if (u < 0 || u >= vertices || v < 0 || v >= vertices) { 77 | cout << "Invalid vertex index.\n"; 78 | return 0; 79 | } 80 | 81 | cout << "Does an edge exist between " << u << " and " << v << "? " 82 | << (g.hasEdge(u, v) ? "Yes" : "No") << endl; 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Queue/queue with vector: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class VectorQueue { 6 | private: 7 | vector data; // stores queue elements 8 | int frontIndex; // points to the current front of the queue 9 | 10 | public: 11 | // Constructor 12 | VectorQueue() : frontIndex(0) {} 13 | 14 | // Enqueue operation - add item to the back 15 | void enqueue(int item) { 16 | data.push_back(item); 17 | cout << "Enqueued: " << item << endl; 18 | } 19 | 20 | // Dequeue operation - remove item from the front 21 | int dequeue() { 22 | if (isEmpty()) { 23 | cout << "Error: Queue is empty!" << endl; 24 | return -1; 25 | } 26 | 27 | int dequeuedValue = data[frontIndex]; 28 | frontIndex++; 29 | 30 | return dequeuedValue; 31 | } 32 | 33 | // Peek at the front element 34 | int front() { 35 | if (isEmpty()) { 36 | cout << "Error: Queue is empty!" << endl; 37 | return -1; 38 | } 39 | return data[frontIndex]; 40 | } 41 | 42 | // Check if the queue is empty 43 | bool isEmpty() { 44 | return frontIndex >= data.size(); 45 | } 46 | 47 | // Print all elements in the queue 48 | void printQueue() { 49 | cout << "Queue: "; 50 | for (int i = frontIndex; i < data.size(); ++i) { 51 | cout << data[i] << " "; 52 | } 53 | cout << endl; 54 | } 55 | 56 | // Return number of elements 57 | int size() { 58 | return data.size() - frontIndex; 59 | } 60 | }; 61 | 62 | 63 | int main() { 64 | VectorQueue queue; 65 | 66 | // Enqueue some elements 67 | queue.enqueue(10); 68 | queue.enqueue(20); 69 | queue.enqueue(30); 70 | queue.enqueue(40); 71 | 72 | // Print queue 73 | queue.printQueue(); // Output: Queue: 10 20 30 40 74 | 75 | // Dequeue elements 76 | cout << "Dequeued: " << queue.dequeue() << endl; // Output: Dequeued: 10 77 | cout << "Dequeued: " << queue.dequeue() << endl; // Output: Dequeued: 20 78 | 79 | // Print queue after dequeues 80 | queue.printQueue(); // Output: Queue: 30 40 81 | 82 | // Check the front 83 | cout << "Front element: " << queue.front() << endl; // Output: 30 84 | 85 | cout << "Queue size: " << queue.size() << endl; 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/prepend.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Node class representing a single element in the doubly linked list 6 | class Node { 7 | public: 8 | int data; // Data stored in the node 9 | Node* next; // Pointer to the next node 10 | Node* prev; // Pointer to the previous node 11 | 12 | // Constructor to initialize a new node with given data 13 | Node(int item) : data(item), next(nullptr), prev(nullptr) {} 14 | }; 15 | 16 | // DoublyLinkedList class to manage the list 17 | class DoublyLinkedList { 18 | public: 19 | Node* head; // Pointer to the first node in the list 20 | Node* tail; // Pointer to the last node in the list 21 | 22 | // Constructor to initialize an empty list 23 | DoublyLinkedList() : head(nullptr), tail(nullptr) {} 24 | 25 | // Member function to prepend a new node at the beginning of the list 26 | void ListPrepend(int item) { 27 | // Step 1: Create a new node with the given data 28 | Node* newNode = new Node(item); 29 | 30 | // Step 2: Check if the list is empty 31 | if (head == nullptr) { // If the list is empty, set head and tail to the new node 32 | head = newNode; 33 | tail = newNode; 34 | } else { // If the list is not empty, insert the new node at the beginning 35 | newNode->next = head; // Set new node's next to current head 36 | head->prev = newNode; // Set current head's previous to new node 37 | head = newNode; // Update head to the new node 38 | } 39 | } 40 | 41 | // Display the list from head to tail 42 | void PrintForward() { 43 | Node* current = head; 44 | while (current) { 45 | cout << current->data << " "; 46 | current = current->next; 47 | } 48 | cout << endl; 49 | } 50 | }; 51 | 52 | int main() { 53 | // Step 1: Create an empty doubly linked list 54 | DoublyLinkedList myList; 55 | 56 | // Step 2: Prepend values to the list using the member function 57 | cout << "Prepending values: 30, 20, 10 using member function" << endl; 58 | myList.ListPrepend(30); 59 | myList.ListPrepend(20); 60 | myList.ListPrepend(10); 61 | 62 | // Step 3: Display the list to verify the prepend function works 63 | cout << "List after Prepend (Forward): "; 64 | myList.PrintForward(); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Stack/Stack with Vector: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Stack { 7 | private: 8 | vector elements; // Use a vector to store elements 9 | 10 | public: 11 | // Constructor: Initialize an empty stack 12 | Stack() { 13 | // The vector is automatically initialized when it's constructed. 14 | // No additional setup is required here. 15 | } 16 | 17 | // Push operation: Adds an item to the top of the stack 18 | void push(int item) { 19 | elements.push_back(item); // Add the item to the end of the vector 20 | } 21 | 22 | // Pop operation: Removes the item at the top of the stack 23 | void pop() { 24 | if (!isEmpty()) { 25 | elements.pop_back(); // Remove the last element from the vector 26 | } else { 27 | cout << "Stack is empty! Cannot pop." << endl; 28 | } 29 | } 30 | 31 | // Top operation: Returns the item at the top of the stack without removing it 32 | int peek() { 33 | if (!isEmpty()) { 34 | return elements.back(); // Return the last element of the vector 35 | } 36 | cout << "Stack is empty! Cannot get top element." << endl; 37 | return -1; 38 | } 39 | 40 | // isEmpty operation: Checks if the stack is empty 41 | bool isEmpty() { 42 | return elements.empty(); // Check if the vector is empty 43 | } 44 | 45 | int GetLength() { 46 | if (!isEmpty()) { 47 | return elements.size(); 48 | } 49 | return 0; 50 | } 51 | }; 52 | 53 | int main() { 54 | Stack stack; 55 | 56 | // Push elements onto the stack 57 | stack.push(10); 58 | stack.push(20); 59 | stack.push(30); 60 | 61 | //Get length of queue 62 | cout << "The number of items in the stack: " << stack.GetLength() << endl; 63 | // Print the top element of the stack 64 | cout << "Top element of the stack: " << stack.peek() << endl; 65 | 66 | // Pop elements from the stack and print them 67 | cout << "Popping elements from the stack:" << endl; 68 | while (!stack.isEmpty()) { 69 | cout << stack.peek() << " "; 70 | stack.pop(); 71 | } 72 | cout << endl; 73 | 74 | // Check if the stack is empty after each operation and print the result 75 | cout << "Is the stack empty? " << (stack.isEmpty() ? "Yes" : "No") << endl; 76 | 77 | return 0; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /Week 12 Graph Algorithms/Topological Sort: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | class Graph { 7 | private: 8 | vector> adjList; 9 | 10 | public: 11 | Graph(int vertices) : adjList(vertices) {} 12 | 13 | 14 | // Directed edge u -> v 15 | void addDirectedEdge(int u, int v) { 16 | adjList[u].push_back(v); 17 | } 18 | 19 | void printGraph() const { 20 | cout << "Adjacency List (Directed):\n"; 21 | for (int i = 0; i < adjList.size(); ++i) { 22 | cout << i << ": "; 23 | for (int neighbor : adjList[i]) { 24 | cout << neighbor << " "; 25 | } 26 | cout << endl; 27 | } 28 | cout << endl; 29 | } 30 | 31 | // Topological Sort (Kahn's Algorithm) 32 | vector topologicalSort() const { 33 | int n = adjList.size(); 34 | vector indegree(n, 0); 35 | 36 | // 1. Compute indegree of each vertex 37 | for (int u = 0; u < n; ++u) { 38 | for (int v : adjList[u]) { 39 | indegree[v]++; 40 | } 41 | } 42 | 43 | // 2. Queue of vertices with indegree 0 44 | queue q; 45 | for (int v = 0; v < n; ++v) { 46 | if (indegree[v] == 0) 47 | q.push(v); 48 | } 49 | 50 | // 3. Process queue 51 | vector order; 52 | while (!q.empty()) { 53 | int u = q.front(); 54 | q.pop(); 55 | order.push_back(u); 56 | 57 | for (int v : adjList[u]) { 58 | indegree[v]--; 59 | if (indegree[v] == 0) 60 | q.push(v); 61 | } 62 | } 63 | 64 | // 4. Cycle check 65 | if (order.size() != n) { 66 | cout << "Cycle detected! Topological sort not possible.\n"; 67 | } 68 | 69 | return order; 70 | } 71 | }; 72 | 73 | int main() { 74 | // Create a graph with 6 nodes: 0,1,2,3,4,5 75 | Graph g(6); 76 | 77 | // Add directed edges (DAG example) 78 | g.addDirectedEdge(5, 2); 79 | g.addDirectedEdge(5, 0); 80 | g.addDirectedEdge(4, 0); 81 | g.addDirectedEdge(4, 1); 82 | g.addDirectedEdge(2, 3); 83 | g.addDirectedEdge(3, 1); 84 | 85 | // Show the graph 86 | g.printGraph(); 87 | 88 | // Run Kahn's Topological Sort 89 | vector topo = g.topologicalSort(); 90 | 91 | // Print result 92 | cout << "Topological Sort Order: "; 93 | for (int v : topo) { 94 | cout << v << " "; 95 | } 96 | cout << endl; 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /Week 11 Intro to Graphs/Depth First Search: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include //for backtracking 4 | using namespace std; 5 | 6 | class Graph { 7 | private: 8 | // Adjacency matrix: adjMatrix[u][v] = 1 if there is an edge u--v, else 0 9 | vector> adjMatrix; 10 | int numVertices; 11 | 12 | public: 13 | // Constructor: create V × V matrix initialized to 0 14 | Graph(int vertices) : numVertices(vertices) { 15 | adjMatrix = vector>(vertices, vector(vertices, 0)); 16 | } 17 | 18 | // Add undirected edge u -- v 19 | void addEdge(int u, int v) { 20 | adjMatrix[u][v] = 1; // Edge u → v 21 | adjMatrix[v][u] = 1; // Edge v → u (undirected) 22 | } 23 | 24 | // Print adjacency matrix 25 | void printGraph() const { 26 | cout << "Adjacency Matrix Representation:\n"; 27 | for (int i = 0; i < numVertices; i++) { 28 | for (int j = 0; j < numVertices; j++) { 29 | cout << adjMatrix[i][j] << " "; 30 | } 31 | cout << endl; 32 | } 33 | } 34 | 35 | 36 | 37 | void DFS(int startVertex) const { 38 | vector visited(numVertices, false); // Track visited nodes 39 | stack st; // Stack for DFS 40 | 41 | // Mark start vertex as visited 42 | visited[startVertex] = true; 43 | 44 | // Push start vertex onto the stack 45 | st.push(startVertex); 46 | 47 | cout << "DFS starting from vertex " << startVertex << ": "; 48 | 49 | // Continue until stack is empty 50 | while (!st.empty()) { 51 | // Pop the top of the stack 52 | int curr = st.top(); 53 | st.pop(); 54 | 55 | // Visit (print) the current vertex 56 | cout << curr << " "; 57 | 58 | // Explore each neighbor of the current vertex 'curr' row 59 | for (int neighbor = 0; neighbor < numVertices; neighbor++) { 60 | if (adjMatrix[curr][neighbor] == 1 && !visited[neighbor]) { 61 | visited[neighbor] = true; 62 | st.push(neighbor); 63 | } 64 | } 65 | } 66 | 67 | cout << endl; 68 | } 69 | }; 70 | 71 | int main() { 72 | Graph g(8); // Create a graph with 8 vertices (0–7) 73 | 74 | // SAME edges as your previous example 75 | g.addEdge(1, 5); 76 | g.addEdge(1, 3); 77 | g.addEdge(1, 4); 78 | g.addEdge(3, 6); 79 | g.addEdge(0, 1); 80 | g.addEdge(0, 5); 81 | g.addEdge(7, 4); 82 | g.addEdge(1, 2); 83 | 84 | // Print the graph's adjacency matrix 85 | g.printGraph(); 86 | 87 | // Run DFS starting from vertex 3 88 | g.DFS(3); 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/append.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Node class representing a single element in the doubly linked list 6 | class Node { 7 | public: 8 | int data; // Data stored in the node 9 | Node* next; // Pointer to the next node 10 | Node* prev; // Pointer to the previous node 11 | 12 | // Constructor to initialize a new node with given data 13 | Node(int item) : data(item), next(nullptr), prev(nullptr) {} 14 | }; 15 | 16 | // DoublyLinkedList class to manage the list 17 | class DoublyLinkedList { 18 | public: 19 | Node* head; // Pointer to the first node in the list 20 | Node* tail; // Pointer to the last node in the list 21 | 22 | // Constructor to initialize an empty list 23 | DoublyLinkedList() : head(nullptr), tail(nullptr) {} 24 | 25 | // Member function to append a new node at the end of the list 26 | void ListAppend(int item) { 27 | // Step 1: Create a new node with the given data 28 | Node* newNode = new Node(item); 29 | 30 | // Step 2: Check if the list is empty 31 | if (head == nullptr) { // If the list is empty, set head and tail to the new node 32 | head = newNode; 33 | tail = newNode; 34 | } else { // If the list is not empty, add the new node to the end 35 | tail->next = newNode; // Set the next pointer of current tail to new node 36 | newNode->prev = tail; // Set the previous pointer of new node to current tail 37 | tail = newNode; // Update tail to the new node 38 | } 39 | } 40 | 41 | // Display the list from head to tail 42 | void PrintForward() { 43 | Node* current = head; 44 | while (current != nullptr) { 45 | cout << current->data << " "; 46 | current = current->next; 47 | } 48 | cout << endl; 49 | } 50 | 51 | void PrintReverse() { 52 | Node* current = tail; 53 | while (current != nullptr) { 54 | cout << current->data << " "; 55 | current = current->prev; 56 | } 57 | cout << endl; 58 | } 59 | 60 | }; 61 | 62 | int main() { 63 | // Step 1: Create an empty doubly linked list 64 | DoublyLinkedList* myList = new DoublyLinkedList(); 65 | 66 | // Step 2: Append values to the list using the member function 67 | cout << "Appending values: 10, 20, 30 using member function" << endl; 68 | myList->ListAppend(10); 69 | myList->ListAppend(20); 70 | myList->ListAppend(30); 71 | myList->ListAppend(40); 72 | 73 | 74 | // Step 3: Display the list to verify the append function works 75 | cout << "List after Append (Forward): "; 76 | myList->PrintForward(); 77 | cout << "List printed in Reverse order" << endl; 78 | myList->PrintReverse(); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /Week 11 Intro to Graphs/Breadth First Search (BFS): -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | class Graph { 7 | private: 8 | vector> adjList; // adjacency list representation 9 | 10 | public: 11 | // Constructor 12 | Graph(int vertices) : adjList(vertices) {} 13 | 14 | // Add undirected edge u--v 15 | void addEdge(int u, int v) { 16 | adjList[u].push_back(v); 17 | adjList[v].push_back(u); 18 | } 19 | 20 | // Print adjacency list 21 | void printGraph() const { 22 | cout << "Adjacency List Representation:\n"; 23 | for (int i = 0; i < adjList.size(); ++i) { 24 | cout << i << ": "; 25 | for (int neighbor : adjList[i]) { 26 | cout << neighbor << " "; 27 | } 28 | cout << endl; 29 | } 30 | } 31 | 32 | // Return degree of a vertex 33 | int degree(int v) const { 34 | return adjList[v].size(); 35 | } 36 | 37 | // Check if there is an edge between u and v 38 | bool hasEdge(int u, int v) const { 39 | for (int neighbor : adjList[u]) { 40 | if (neighbor == v) 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | // Breadth-First Search (BFS) traversal from a given starting vertex 47 | void BFS(int startVertex) const { 48 | int n = adjList.size(); // Number of vertices 49 | vector visited(n, false); // Track visited vertices 50 | queue q; // Queue for BFS 51 | 52 | visited[startVertex] = true; 53 | q.push(startVertex); 54 | 55 | cout << "BFS traversal starting from vertex " << startVertex << ": "; 56 | 57 | while (!q.empty()) { 58 | int curr = q.front(); 59 | q.pop(); 60 | cout << curr << " "; 61 | 62 | // Visit all neighbors of current vertex 63 | for (int neighbor : adjList[curr]) { 64 | if (!visited[neighbor]) { 65 | visited[neighbor] = true; 66 | q.push(neighbor); 67 | } 68 | } 69 | } 70 | cout << endl; 71 | } 72 | }; 73 | 74 | 75 | int main() { 76 | // Create a graph with n vertices (0 to n-1) 77 | Graph g(8); 78 | 79 | // Add undirected edges 80 | g.addEdge(1, 5); 81 | g.addEdge(1, 3); 82 | g.addEdge(1, 4); 83 | g.addEdge(3, 6); 84 | g.addEdge(0, 1); 85 | g.addEdge(0, 5); 86 | g.addEdge(7, 4); 87 | g.addEdge(1, 2); 88 | 89 | 90 | // Print graph structure 91 | g.printGraph(); 92 | 93 | // Run BFS from vertex 0 94 | g.BFS(3); 95 | 96 | return 0; 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/InsertAfterNode.cpp: -------------------------------------------------------------------------------- 1 | // Insert a node after a specific node in the list 2 | #include 3 | using namespace std; 4 | 5 | // Node class definition 6 | class Node { 7 | public: 8 | int data; 9 | Node* next; 10 | 11 | // Constructor 12 | Node(int value) : data(value), next(nullptr) {} 13 | }; 14 | 15 | // List class definition 16 | class List { 17 | public: 18 | Node* head; 19 | Node* tail; 20 | 21 | // Constructor 22 | List() : head(nullptr), tail(nullptr) {} 23 | 24 | // Function to append a node to the end of the list 25 | void ListAppend(int value) { 26 | Node* newNode = new Node(value); 27 | if (head == nullptr) { // Check if the list is empty 28 | head = newNode; // If empty, set both head and tail to the new node 29 | tail = newNode; 30 | } else { 31 | tail->next = newNode; // If not empty, append the new node after the current tail 32 | tail = newNode; // Update the tail pointer to the new node 33 | } 34 | } 35 | 36 | // Search for a value 37 | Node* ListSearch(int key) { 38 | Node* curNode = head; 39 | while (curNode != nullptr) { 40 | if (curNode->data == key) { 41 | return curNode; // found 42 | } 43 | curNode = curNode->next; 44 | } 45 | return nullptr; // not found 46 | } 47 | 48 | 49 | 50 | // Function to insert a node after a given node in the list 51 | void ListInsertAfter(Node* curNode, Node* newNode) { 52 | if (head == nullptr) { // List empty 53 | head = newNode; 54 | tail = newNode; 55 | } else if (curNode == tail) { // Insert after tail 56 | tail->next = newNode; 57 | tail = newNode; 58 | } else { 59 | newNode->next = curNode->next; 60 | curNode->next = newNode; 61 | } 62 | } 63 | 64 | // Function to print the elements of the list 65 | void PrintList() { 66 | Node* curNode = head; 67 | cout << "List: "; 68 | while (curNode != nullptr) { 69 | https://www.onlinegdb.com/classroom cout << curNode->data << " "; 70 | curNode = curNode->next; 71 | } 72 | cout << endl; 73 | } 74 | }; 75 | 76 | int main() { 77 | List myList; 78 | myList.ListAppend(10); 79 | myList.ListAppend(20); 80 | myList.ListAppend(30); 81 | myList.ListAppend(40); 82 | 83 | myList.PrintList(); 84 | 85 | Node* newNode = new Node(25); 86 | 87 | // use the new search method 88 | Node* curNode = myList.ListSearch(20); 89 | 90 | // insert after 20 if it exists and has a next node 91 | if (curNode && curNode->next) { 92 | myList.ListInsertAfter(curNode, newNode); 93 | } 94 | 95 | myList.PrintList(); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /Week 4 Sorting /Radix sort: -------------------------------------------------------------------------------- 1 | //Radix sort 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | //Before sorting, we need to know the maximum number of digits among all numbers to determine how many digit “passes” 7 | //(ones place, tens place, hundreds place, …) to perform. 8 | // Returns the length, in number of digits, of an integer value 9 | int RadixGetLength(int value) { 10 | if (value == 0) { //case if input is 0, 11 | return 1; 12 | } 13 | int digits = 0; 14 | while (value != 0) { 15 | digits++; 16 | value /= 10; //123 -> 12 -> 1 -> 0. strip off 1 digit at a time 17 | } 18 | return digits; //returns number of digits in the number i.e. digits in 3 for 123 19 | } 20 | 21 | // Returns the maximum length, in number of digits, out of all array elements 22 | int RadixGetMaxLength(const int* numbers, int numbersSize) { 23 | int maxDigits = 0; 24 | for (int i = 0; i < numbersSize; i++) { 25 | int digitCount = RadixGetLength(numbers[i]); // call RadixGetLength() to get the number of digits for the current element 26 | if (digitCount > maxDigits) { 27 | maxDigits = digitCount; 28 | } 29 | } 30 | return maxDigits; //after examinining every element, it returns the maximum digit length 31 | } 32 | 33 | //Radix Sort for positive integers 34 | void RadixSort(int* array, int arraySize) { 35 | vector> buckets(10); //vector wich each cell holding another vector. 36 | // outer vector is 0-9, inner vector is initialy empty 37 | 38 | int maxDigits = RadixGetMaxLength(array, arraySize); //tells the algorithm how many passes to make 39 | int pow10 = 1; 40 | 41 | for (int digitIndex = 0; digitIndex < maxDigits; digitIndex++) { 42 | // Place numbers into buckets by current digit 43 | for (int i = 0; i < arraySize; i++) { 44 | int bucketIndex = (array[i] / pow10) % 10; 45 | buckets[bucketIndex].push_back(array[i]); 46 | } 47 | 48 | // Copy numbers back to orginal array of number from buckets in order before moving on to the next digit place 49 | int copyBackIndex = 0; 50 | for (int i = 0; i < 10; i++) { 51 | for (size_t j = 0; j < buckets[i].size(); j++) { 52 | array[copyBackIndex++] = buckets[i][j]; 53 | } 54 | buckets[i].clear(); 55 | } 56 | pow10 *= 10; // Next digit place 57 | } 58 | } 59 | 60 | int main() { 61 | int arr[] = {170, 45, 75, 90, 802, 24, 2, 66}; 62 | int n = sizeof(arr) / sizeof(arr[0]); 63 | 64 | cout << "Original array: "; 65 | for (int i = 0; i < n; i++) cout << arr[i] << " "; 66 | cout << "\n"; 67 | 68 | RadixSort(arr, n); 69 | 70 | cout << "Sorted array: "; 71 | for (int i = 0; i < n; i++) cout << arr[i] << " "; 72 | cout << "\n"; 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Stack/Linked LIst: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | // Node Class 6 | 7 | class Node { 8 | public: 9 | int data; 10 | Node* next; 11 | 12 | Node(int value) : data(value), next(nullptr) {} 13 | }; 14 | 15 | // LinkedList Class 16 | 17 | class LinkedList { 18 | private: 19 | Node* head; // Points to the first node 20 | 21 | public: 22 | LinkedList() : head(nullptr) {} 23 | 24 | // Add a new node to the front of the list 25 | void prepend(int value) { 26 | Node* newNode = new Node(value); 27 | newNode->next = head; 28 | head = newNode; 29 | } 30 | 31 | // Remove the front node 32 | void removeFront() { 33 | if (head == nullptr) { 34 | cout << "List is empty. Nothing to remove.\n"; 35 | return; 36 | } 37 | Node* temp = head; 38 | head = head->next; 39 | delete temp; 40 | } 41 | 42 | // Return the value at the front of the list 43 | int getFront() const { 44 | if (head == nullptr) { 45 | cout << "List is empty.\n"; 46 | return -1; 47 | } 48 | return head->data; 49 | } 50 | 51 | // Check if the list is empty 52 | bool isEmpty() const { 53 | return head == nullptr; 54 | } 55 | }; 56 | 57 | 58 | //Stack Class (Wrapper Around LinkedList) 59 | 60 | class Stack { 61 | private: 62 | LinkedList list; // ENCAPSULATION Principle. The stack contains a linked list as a private member no code 63 | // outside the stack can call any of of the LL functions 64 | 65 | public: 66 | // Push = add to the front of the list 67 | void push(int value) { 68 | list.prepend(value); 69 | } 70 | 71 | // Pop = remove from the front of the list 72 | void pop() { 73 | list.removeFront(); 74 | } 75 | 76 | // Top = look at the front of the list 77 | int top() const { 78 | return list.getFront(); 79 | } 80 | 81 | // Check if stack is empty 82 | bool isEmpty() const { 83 | return list.isEmpty(); 84 | } 85 | }; 86 | 87 | 88 | // Demonstration in main() 89 | 90 | int main() { 91 | Stack s; 92 | 93 | cout << "Pushing 10, 20, 30 onto the stack...\n"; 94 | s.push(10); // calls LinkedList::prepend() 95 | s.push(20); 96 | s.push(30); 97 | 98 | cout << "Top element: " << s.top() << endl; 99 | 100 | cout << "Popping one element...\n"; 101 | s.pop(); // calls LinkedList::removeFront() 102 | cout << "Top element now: " << s.top() << endl; 103 | 104 | cout << "Popping all elements...\n"; 105 | s.pop(); 106 | s.pop(); 107 | 108 | //ternary conditional operator to shorten code: 109 | //(condition) ? expression_if_true : expression_if_false; 110 | 111 | cout << "Stack empty? " << (s.isEmpty() ? "Yes" : "No") << endl; 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Node class representing a single element in the doubly linked list 6 | class Node { 7 | public: 8 | int data; // Data stored in the node 9 | Node* next; // Pointer to the next node 10 | Node* prev; // Pointer to the previous node 11 | 12 | // Constructor to initialize a new node with given data 13 | Node(int item) : data(item), next(nullptr), prev(nullptr) {} 14 | }; 15 | 16 | // DoublyLinkedList class to manage the list 17 | class DoublyLinkedList { 18 | public: 19 | Node* head; // Pointer to the first node in the list 20 | Node* tail; // Pointer to the last node in the list 21 | 22 | // Constructor to initialize an empty list 23 | DoublyLinkedList() : head(nullptr), tail(nullptr) {} 24 | 25 | // Member function to search for a node with a specific value 26 | Node* ListSearch(int dataValue) { 27 | // Step 1: Start from the head of the list 28 | Node* currentNode = head; 29 | 30 | // Step 2: Traverse the list to find the node with the given data 31 | while (currentNode) { 32 | if (currentNode->data == dataValue) { 33 | return currentNode; // Return the node if found 34 | } 35 | currentNode = currentNode->next; // Move to the next node 36 | } 37 | 38 | // Step 3: If the node was not found, return nullptr 39 | return nullptr; 40 | } 41 | 42 | // Member function to append a new node at the end of the list (for testing purposes) 43 | void ListAppend(int item) { 44 | Node* newNode = new Node(item); 45 | 46 | if (!tail) { 47 | head = newNode; 48 | tail = newNode; 49 | } else { 50 | tail->next = newNode; 51 | newNode->prev = tail; 52 | tail = newNode; 53 | } 54 | } 55 | 56 | // Display the list from head to tail 57 | void PrintForward() { 58 | Node* current = head; 59 | while (current) { 60 | cout << current->data << " "; 61 | current = current->next; 62 | } 63 | cout << endl; 64 | } 65 | }; 66 | 67 | int main() { 68 | // Step 1: Create an empty doubly linked list 69 | DoublyLinkedList myList; 70 | 71 | // Step 2: Append values to the list to create sample data 72 | cout << "Appending values: 10, 20, 30, 40, 50" << endl; 73 | myList.ListAppend(10); 74 | myList.ListAppend(20); 75 | myList.ListAppend(30); 76 | myList.ListAppend(40); 77 | myList.ListAppend(50); 78 | 79 | // Step 3: Display the list before searching 80 | cout << "Current List: "; 81 | myList.PrintForward(); 82 | 83 | // Step 4: Search for an existing value 84 | int searchValue = 30; 85 | Node* foundNode = myList.ListSearch(searchValue); 86 | if (foundNode) { 87 | cout << "Node with value " << searchValue << " found!" << endl; 88 | } else { 89 | cout << "Node with value " << searchValue << " not found!" << endl; 90 | } 91 | 92 | // Step 5: Search for a non-existing value 93 | searchValue = 100; 94 | foundNode = myList.ListSearch(searchValue); 95 | if (foundNode) { 96 | cout << "Node with value " << searchValue << " found!" << endl; 97 | } else { 98 | cout << "Node with value " << searchValue << " not found!" << endl; 99 | } 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/RemoveNode.cpp: -------------------------------------------------------------------------------- 1 | // remove a specified node from the list 2 | #include 3 | using namespace std; 4 | 5 | // Node class definition 6 | class Node { 7 | public: 8 | int data; 9 | Node* next; 10 | 11 | // Constructor 12 | Node(int value) : data(value), next(nullptr) {} 13 | }; 14 | 15 | // List class definition 16 | class List { 17 | public: 18 | Node* head; 19 | Node* tail; 20 | 21 | // Constructor 22 | List() : head(nullptr), tail(nullptr) {} 23 | 24 | // Function to append a node to the end of the list 25 | void ListAppend(int value) { 26 | Node* newNode = new Node(value); 27 | if (head == nullptr) { // Check if the list is empty 28 | head = newNode; // If empty, set both head and tail to the new node 29 | tail = newNode; 30 | } else { 31 | tail->next = newNode; // If not empty, append the new node after the current tail 32 | tail = newNode; // Update the tail pointer to the new node 33 | } 34 | } 35 | 36 | // Function to remove the node after a given node in the list 37 | void ListRemoveNodeAfter(Node* curNode) { 38 | if (curNode == nullptr) { 39 | // Special case, remove head 40 | Node* nodeToRemove = head; 41 | head = head->next; 42 | delete nodeToRemove; 43 | 44 | 45 | if (head == nullptr) { // Remove last item 46 | tail = nullptr; 47 | } 48 | } else if (curNode->next) { 49 | Node* nodeToRemove = curNode->next; 50 | Node* succeedingNode = nodeToRemove->next; 51 | curNode->next = succeedingNode; 52 | delete nodeToRemove; 53 | 54 | if (succeedingNode == nullptr) { // Removed tail 55 | tail = curNode; 56 | } 57 | } 58 | } 59 | 60 | // Function to print the elements of the list 61 | void PrintList() { 62 | Node* curNode = head; 63 | cout << "List: "; 64 | while (curNode != nullptr) { 65 | cout << curNode->data << " "; 66 | curNode = curNode->next; 67 | } 68 | cout << endl; 69 | } 70 | }; 71 | 72 | int main() { 73 | // Create a list 74 | List myList; 75 | 76 | // Build initial list: 10 20 30 40 50 60 77 | myList.ListAppend(10); 78 | myList.ListAppend(20); 79 | myList.ListAppend(30); 80 | myList.ListAppend(40); 81 | myList.ListAppend(50); 82 | myList.ListAppend(60); 83 | cout << "Initial "; 84 | myList.PrintList(); 85 | 86 | // 1 Remove HEAD (pass nullptr) 87 | myList.ListRemoveNodeAfter(nullptr); 88 | cout << "After removing HEAD (should remove 10): "; 89 | myList.PrintList(); 90 | 91 | // 2 Remove node after HEAD (remove 30) 92 | myList.ListRemoveNodeAfter(myList.head); 93 | cout << "After removing node after HEAD (should remove 30): "; 94 | myList.PrintList(); 95 | 96 | // 3 Remove a middle node’s next (remove 50 after 40) 97 | Node* cur = myList.head; 98 | while (cur && cur->data != 40) cur = cur->next; 99 | myList.ListRemoveNodeAfter(cur); 100 | cout << "After removing node after 40 (should remove 50): "; 101 | myList.PrintList(); 102 | 103 | // 4 Remove the TAIL (remove 60 after 40, updates tail) 104 | myList.ListRemoveNodeAfter(cur); 105 | cout << "After removing tail (should remove 60): "; 106 | myList.PrintList(); 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /Week 2 Pointers/Template-Exercise-6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // For std::sort, std::max_element 4 | using namespace std; 5 | 6 | // 1. Function Template: Apply Discount 7 | template 8 | T applyDiscount(T price, T discountPercentage) { 9 | return price - (price * discountPercentage / 100); 10 | } 11 | 12 | // 2. Class Template: Shopping Cart 13 | template 14 | class ShoppingCart { 15 | vector items; 16 | public: 17 | void addItem(const T& item) { 18 | items.push_back(item); 19 | } 20 | void displayItems() { 21 | cout << "Items in the cart:" << endl; 22 | for (const auto& item : items) { 23 | cout << "- "; 24 | item.display(); 25 | } 26 | } 27 | vector& getItems() { return items; } // For accessing items in the cart 28 | }; 29 | 30 | // 3. Multiple Template Arguments: Item with ID and Price 31 | template 32 | class Item { 33 | Key id; 34 | Value price; 35 | public: 36 | Item(Key id, Value price) : id(id), price(price) {} 37 | void display() const { 38 | cout << "ID: " << id << ", Price: " << price << endl; 39 | } 40 | Value getPrice() const { return price; } 41 | }; 42 | 43 | // 4. Template Specialization: Fragile Items 44 | template<> 45 | class Item { 46 | int id; 47 | float price; 48 | bool fragile; 49 | public: 50 | Item(int id, float price, bool fragile) : id(id), price(price), fragile(fragile) {} 51 | Item(int id, float price) : id(id), price(price), fragile(false) {} 52 | void display() const { 53 | cout << "ID: " << id << ", Price: " << price; 54 | cout << (fragile ? " (Fragile Item)" : "") << endl; 55 | } 56 | float getPrice() const { return price; } 57 | }; 58 | 59 | // 5. Default Template Arguments: StandardCart for Prices 60 | template 61 | class StandardCart { 62 | vector prices; 63 | public: 64 | void addPrice(const T& price) { 65 | prices.push_back(price); 66 | } 67 | void displayPrices() { 68 | cout << "Prices in the cart:" << endl; 69 | for (const auto& price : prices) { 70 | cout << price << endl; 71 | } 72 | } 73 | }; 74 | 75 | int main() { 76 | // 1. Function Template Example 77 | cout << "Discounted price: " << applyDiscount(100.0, 15.0) << endl; 78 | 79 | // 2. Class Template Example 80 | ShoppingCart> cart; 81 | cart.addItem(Item(101, 499.99)); // Add a regular item 82 | cart.addItem(Item(202, 799.99, true)); // Add a fragile item 83 | cart.displayItems(); 84 | 85 | // 3. Sort Items by Price 86 | auto& items = cart.getItems(); 87 | sort(items.begin(), items.end(), [](const auto& a, const auto& b) { 88 | return a.getPrice() < b.getPrice(); 89 | }); 90 | 91 | cout << "\nItems sorted by price:" << endl; 92 | cart.displayItems(); 93 | 94 | // 4. Find Most Expensive Item 95 | auto mostExpensive = max_element(items.begin(), items.end(), [](const auto& a, const auto& b) { 96 | return a.getPrice() < b.getPrice(); 97 | }); 98 | cout << "\nMost expensive item:" << endl; 99 | mostExpensive->display(); 100 | 101 | // 5. Default Template Arguments Example 102 | StandardCart<> priceCart; 103 | priceCart.addPrice(100.0); 104 | priceCart.addPrice(250.5); 105 | priceCart.addPrice(499.99); 106 | priceCart.displayPrices(); 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /Week 6 Stack and Queue/Deque/Deque with linked list: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // Node class for the Deque 5 | class Node { 6 | public: 7 | int data; 8 | Node* next; 9 | Node* prev; 10 | 11 | 12 | Node(int dataValue) : data(dataValue), next(nullptr), prev(nullptr) {} 13 | }; 14 | 15 | // Deque class definition 16 | class Deque { 17 | private: 18 | Node* front; 19 | Node* rear; 20 | 21 | public: 22 | Deque() : front(nullptr), rear(nullptr) {} 23 | 24 | bool isEmpty() const { return front == nullptr; } 25 | 26 | // Insert at front 27 | void PushFront(int value) { 28 | Node* newNode = new Node(value); 29 | 30 | if (isEmpty()) { 31 | front = rear = newNode; 32 | } else { 33 | newNode->next = front; // Link new node's next to old front 34 | front->prev = newNode; // Link old front's prev back to new node 35 | front = newNode; // Update front pointer 36 | } 37 | } 38 | 39 | // Insert at rear 40 | void PushBack(int value) { 41 | Node* newNode = new Node(value); 42 | 43 | if (isEmpty()) { 44 | front = rear = newNode; 45 | } else { 46 | rear->next = newNode; // Link old rear's next to new node 47 | newNode->prev = rear; // Link new node's prev to old rear 48 | rear = newNode; // Update rear pointer 49 | } 50 | } 51 | 52 | 53 | void PopFront() { 54 | if (isEmpty()) { 55 | cout << "Deque is empty!" << endl; 56 | return; 57 | } 58 | 59 | Node* temp = front; 60 | front = front->next; 61 | 62 | if (front == nullptr) 63 | rear = nullptr; 64 | else 65 | front->prev = nullptr; 66 | 67 | delete temp; 68 | } 69 | 70 | void PopBack() { 71 | if (isEmpty()) { 72 | cout << "Deque is empty!" << endl; 73 | return; 74 | } 75 | 76 | Node* temp = rear; 77 | rear = rear->prev; 78 | 79 | if (rear == nullptr) 80 | front = nullptr; 81 | else 82 | rear->next = nullptr; 83 | 84 | delete temp; 85 | } 86 | 87 | void PrintQueue() const { 88 | Node* current = front; 89 | cout << "Deque contents (front => rear): "; 90 | while (current != nullptr) { 91 | cout << current->data << " "; 92 | current = current->next; 93 | } 94 | cout << endl; 95 | } 96 | 97 | 98 | ~Deque() { 99 | while (!isEmpty()) { 100 | PopFront(); 101 | } 102 | } 103 | }; 104 | 105 | int main() { 106 | Deque dq; 107 | 108 | cout << "Inserting at rear: 10, 20, 30" << endl; 109 | dq.PushBack(10); 110 | dq.PushBack(20); 111 | dq.PushBack(30); 112 | dq.PrintQueue(); // Expected: 10 20 30 113 | // Output with Deque contents (front => rear): 10 20 30 114 | 115 | cout << "Inserting at front: 5" << endl; 116 | dq.PushFront(5); 117 | dq.PrintQueue(); // Expected: 5 10 20 30 118 | // Output with Deque contents (front => rear): 5 10 20 30 119 | 120 | cout << "Deleting from rear" << endl; dq.PopBack(); 121 | dq.PrintQueue(); // Expected: 5 10 20 122 | // Output with Deque contents (front => rear): 5 10 20 123 | 124 | cout << "Deleting from front" << endl; 125 | dq.PopFront(); 126 | dq.PrintQueue(); // Expected: 10 20 127 | // Output with Deque contents (front => rear): 10 20 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /Week 8 Binary Search Trees (BST) /Iterative/Insert and Print: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | //Iterative Insert node into tree 6 | // Define the Node structure for BST 7 | class BSTNode { 8 | public: 9 | int key; // Value of the node 10 | BSTNode* left; // Pointer to the left child 11 | BSTNode* right; // Pointer to the right child 12 | 13 | // Constructor to create a new node 14 | BSTNode(int nodeKey) : key(nodeKey), left(nullptr), right(nullptr) {} 15 | }; 16 | 17 | // Define the BST class with the iterative search function 18 | class BinarySearchTree { 19 | private: 20 | BSTNode* root; // Root of the BST 21 | 22 | //helper function to delete all tree nodes 23 | void DeleteTree(BSTNode* subtreeRoot) { 24 | if (subtreeRoot) { 25 | DeleteTree(subtreeRoot->left); 26 | DeleteTree(subtreeRoot->right); 27 | delete subtreeRoot; 28 | } 29 | } 30 | 31 | public: 32 | // Constructor initializes an empty BST 33 | BinarySearchTree() : root(nullptr) {} 34 | 35 | ~BinarySearchTree() { 36 | DeleteTree(root); 37 | } 38 | 39 | // Insert a node iteratively in the tree 40 | void InsertNode(BSTNode* newNode) { 41 | if (root == nullptr) { 42 | root = newNode; 43 | } 44 | else { 45 | BSTNode* currentNode = root; 46 | while (currentNode) { 47 | if (newNode->key < currentNode->key) { 48 | if (currentNode->left == nullptr) { 49 | currentNode->left = newNode; 50 | currentNode = nullptr; 51 | } 52 | else { 53 | currentNode = currentNode->left; 54 | } 55 | } 56 | else { 57 | if (currentNode->right == nullptr) { 58 | currentNode->right = newNode; 59 | currentNode = nullptr; 60 | } 61 | else { 62 | currentNode = currentNode->right; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | void PrintInorder() { 70 | if (root == nullptr) { 71 | cout << "Tree is empty." << endl; 72 | return; 73 | } 74 | 75 | stack nodeStack; 76 | BSTNode* current = root; 77 | 78 | cout << "Iterative Inorder Traversal: "; 79 | 80 | while (current != nullptr || !nodeStack.empty()) { 81 | // 1. Go as far left as possible 82 | while (current != nullptr) { 83 | nodeStack.push(current); 84 | current = current->left; 85 | } 86 | 87 | // 2. Visit the top node 88 | current = nodeStack.top(); 89 | nodeStack.pop(); 90 | cout << current->key << " "; 91 | 92 | // 3. Move to the right subtree 93 | current = current->right; 94 | } 95 | 96 | cout << endl; 97 | } 98 | 99 | }; 100 | 101 | int main() { 102 | BinarySearchTree bst; 103 | BSTNode* n1 = new BSTNode(8); 104 | BSTNode* n2 = new BSTNode(3); 105 | BSTNode* n3 = new BSTNode(10); 106 | BSTNode* n4 = new BSTNode(1); 107 | BSTNode* n5 = new BSTNode(6); 108 | BSTNode* n6 = new BSTNode(14); 109 | BSTNode* n7 = new BSTNode(4); 110 | BSTNode* n8 = new BSTNode(7); 111 | BSTNode* n9 = new BSTNode(13); 112 | 113 | // Insert nodes into the BST 114 | bst.InsertNode(n1); 115 | bst.InsertNode(n2); 116 | bst.InsertNode(n3); 117 | bst.InsertNode(n4); 118 | bst.InsertNode(n5); 119 | bst.InsertNode(n6); 120 | bst.InsertNode(n7); 121 | bst.InsertNode(n8); 122 | bst.InsertNode(n9); 123 | 124 | //Print the tree content 125 | bst.PrintInorder(); 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /Week 7/Hash Tables/Hash Table with linked list: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include //provides dynamic array (resizable) for storing buckets 4 | #include //doubly linked list holding Node struct of our data 5 | #include //Provides pair object to store key-value pair 6 | #include // Provides hash for computing hash codes of string keys 7 | using namespace std; 8 | 9 | 10 | //Simple Hash Table with Chaining 11 | 12 | class ChainingHashTable { 13 | private: 14 | static const int BUCKET_COUNT = 7; //static keyword make every instance of hash table have the same bucket count of 7 15 | vector>> buckets; 16 | 17 | // Hash function: Take the hash of key, reduce it so it fits in my bucket range, and store that small number as the index where this key belongs.” 18 | int HashFunction(string key) { 19 | hash hasher; 20 | int index = (int)(hasher(key) % BUCKET_COUNT); 21 | return index; 22 | } 23 | 24 | public: 25 | ChainingHashTable() { 26 | buckets.resize(BUCKET_COUNT); 27 | } 28 | 29 | // Insert a new key-value pair 30 | void Insert(string key, string value) { 31 | int index = HashFunction(key); 32 | 33 | // Check if key already exists 34 | for (auto& kv : buckets[index]) { 35 | if (kv.first == key) { 36 | kv.second = value; // update existing 37 | return; 38 | } 39 | } 40 | // Add new pair 41 | buckets[index].push_back({key, value}); 42 | } 43 | 44 | // Remove a key if it exists 45 | void Remove(string key) { 46 | int index = HashFunction(key); 47 | for (auto it = buckets[index].begin(); it != buckets[index].end(); ++it) { 48 | if (it->first == key) { 49 | buckets[index].erase(it); 50 | return; 51 | } 52 | } 53 | } 54 | 55 | // Find a key and return true/false, storing value in outValue 56 | bool Find(string key, string& outValue) { 57 | int index = HashFunction(key); 58 | for (auto& kv : buckets[index]) { 59 | if (kv.first == key) { 60 | outValue = kv.second; 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | // print the contents of the table 68 | void PrintTable() { 69 | cout << "=== Hash Table Buckets ===\n"; 70 | for (int i = 0; i < BUCKET_COUNT; i++) { 71 | cout << "Bucket " << i << ": "; 72 | for (auto& kv : buckets[i]) { 73 | cout << "(" << kv.first << " -> " << kv.second << ") "; 74 | } 75 | cout << endl; 76 | } 77 | } 78 | }; 79 | 80 | 81 | int main() { 82 | vector titles = { 83 | "1984", "Dune", "The Hobbit", 84 | "Moby Dick", "Frankenstein", "Pride and Prejudice", 85 | "Hamlet", "The Odyssey", "The Catcher in the Rye", 86 | "To Kill a Mockingbird" 87 | }; 88 | 89 | vector authors = { 90 | "George Orwell", "Frank Herbert", "J.R.R. Tolkien", 91 | "Herman Melville", "Mary Shelley", "Jane Austen", 92 | "William Shakespeare", "Homer", "J.D. Salinger", 93 | "Harper Lee" 94 | }; 95 | 96 | ChainingHashTable library; 97 | 98 | cout << "Inserting books into hash table...\n"; 99 | for (int i = 0; i < (int)titles.size(); i++) { 100 | library.Insert(titles[i], authors[i]); 101 | } 102 | 103 | library.PrintTable(); 104 | 105 | cout << "\nFinding some books...\n"; 106 | vector searchKeys = {"Dune", "The Hobbit", "The Great Gatsby"}; 107 | for (int i = 0; i < (int)searchKeys.size(); i++) { 108 | string author; 109 | if (library.Find(searchKeys[i], author)) 110 | cout << searchKeys[i] << " -> " << author << endl; 111 | else 112 | cout << searchKeys[i] << " not found.\n"; 113 | } 114 | 115 | cout << "\nRemoving two books...\n"; 116 | library.Remove("1984"); 117 | library.Remove("Moby Dick"); 118 | 119 | library.PrintTable(); 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/RemoveNode: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | 6 | 7 | class Node { 8 | public: 9 | int data; // Data stored in the node 10 | Node* next; // Pointer to the next node 11 | Node* prev; // Pointer to the previous node 12 | 13 | // Constructor to initialize a new node with given data 14 | Node(int item) : data(item), next(nullptr), prev(nullptr) {} 15 | }; 16 | 17 | // DoublyLinkedList class to manage the list 18 | class DoublyLinkedList { 19 | public: 20 | Node* head; // Pointer to the first node in the list 21 | Node* tail; // Pointer to the last node in the list 22 | 23 | // Constructor to initialize an empty list 24 | DoublyLinkedList() : head(nullptr), tail(nullptr) {} 25 | 26 | // Member function to remove a specific node from the list 27 | // We can remove a node directly because you can walk both directions of the list 28 | 29 | void RemoveNode(Node* currentNode) { 30 | if (currentNode == nullptr) { 31 | cout << "Error: Given node is NULL. Cannot remove a NULL node." << endl; 32 | return; 33 | } 34 | 35 | // Step 1: create the successor and predecessor of the node to be removed 36 | Node* successor = currentNode->next; 37 | Node* predecessor = currentNode->prev; 38 | 39 | // Step 2: Update the pointers to remove the node from the chain 40 | if (successor) { 41 | successor->prev = predecessor; // Set successor's previous to predecessor 42 | } 43 | if (predecessor) { 44 | predecessor->next = successor; // Set predecessor's next to successor 45 | } 46 | 47 | // Step 3: Update head or tail if needed 48 | if (currentNode == head) { 49 | head = successor; // If removing the head, move it to the next node 50 | } 51 | if (currentNode == tail) { 52 | tail = predecessor; // If removing the tail, move it to the previous node 53 | } 54 | 55 | // Step 4: Free memory of the removed node 56 | delete currentNode; 57 | } 58 | 59 | // Member function to append a new node at the end of the list (for testing purposes) 60 | void Append(int item) { 61 | Node* newNode = new Node(item); 62 | 63 | if (!tail) { 64 | head = newNode; 65 | tail = newNode; 66 | } else { 67 | tail->next = newNode; 68 | newNode->prev = tail; 69 | tail = newNode; 70 | } 71 | } 72 | 73 | // Member function to search for a node with a specific value (for finding removal target) 74 | Node* Search(int dataValue) { 75 | Node* currentNode = head; 76 | while (currentNode) { 77 | if (currentNode->data == dataValue) { 78 | return currentNode; // Return the node if found 79 | } 80 | currentNode = currentNode->next; 81 | } 82 | return nullptr; 83 | } 84 | 85 | // Display the list from head to tail 86 | void PrintForward() { 87 | Node* current = head; 88 | while (current) { 89 | cout << current->data << " "; 90 | current = current->next; 91 | } 92 | cout << endl; 93 | } 94 | }; 95 | 96 | int main() { 97 | // Step 1: Create an empty doubly linked list 98 | DoublyLinkedList* myList = new DoublyLinkedList(); 99 | 100 | // Step 2: Append values to the list to create sample data 101 | cout << "Appending values: 10, 20, 30, 40, 50" << endl; 102 | myList->Append(10); 103 | myList->Append(20); 104 | myList->Append(30); 105 | myList->Append(40); 106 | myList->Append(50); 107 | 108 | // Step 3: Display the list before removal 109 | cout << "Current List: "; 110 | myList->PrintForward(); 111 | 112 | // Step 4: Remove a node with a specific value 113 | int removeValue = 30; 114 | Node* nodeToRemove = myList->Search(removeValue); 115 | if (nodeToRemove != nullptr) { 116 | cout << "Removing " << removeValue << endl; 117 | myList->RemoveNode(nodeToRemove); 118 | } else { 119 | cout << "Node with value " << removeValue << " not found. Cannot remove." << endl; 120 | } 121 | 122 | // Step 5: Display the list after removal 123 | cout << "List after Remove (Forward): "; 124 | myList->PrintForward(); 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Single Linked Lists/In class coding: -------------------------------------------------------------------------------- 1 | // In Class codig of single linked list 2 | 3 | #include 4 | using namespace std; 5 | 6 | class Node { 7 | public: 8 | int data; 9 | Node* next; 10 | //constructor 11 | Node(int value):data(value), next(nullptr) {} 12 | }; 13 | 14 | class Linked_list { 15 | private: 16 | Node* head; 17 | Node* tail; 18 | public: 19 | //we'll add our ADT functionality here 20 | //constructor 21 | Linked_list():head(nullptr), tail(nullptr) {} 22 | 23 | //destructor 24 | ~Linked_list() { 25 | Node* temp = head; 26 | while(head != nullptr) { 27 | head = head->next; 28 | delete temp; 29 | temp = head; 30 | } 31 | } 32 | 33 | //member function to prepend a Node 34 | void prepend(int value){ 35 | Node* newNode = new Node(value); 36 | if(head == nullptr){ 37 | //list is empty 38 | head = newNode; 39 | tail = newNode; 40 | } 41 | else 42 | { 43 | //list is not empty 44 | newNode->next = head; 45 | head = newNode; 46 | 47 | } 48 | } 49 | 50 | //member function to print or display our linked list 51 | void printList(){ 52 | Node* temp = head; 53 | while (temp != nullptr){ 54 | cout << temp->data << " "; 55 | temp = temp->next; 56 | } 57 | cout << endl; 58 | } 59 | 60 | 61 | //member function to add value to end of list 62 | void append(int value){ 63 | Node* newNode = new Node(value); 64 | //check if list is empty 65 | if(head == nullptr){ 66 | head = newNode; 67 | tail = newNode; 68 | } 69 | else 70 | { 71 | tail->next = newNode; 72 | tail = newNode; 73 | } 74 | 75 | } 76 | 77 | //Search for a value in the list and return its address 78 | Node* listSearch(int key) { 79 | Node* curNode = head; 80 | while(curNode != nullptr){ 81 | if(curNode->data == key){ 82 | return curNode; 83 | } 84 | curNode = curNode->next; 85 | } 86 | return nullptr; 87 | } 88 | 89 | //member function to insert a node after a given node in the list 90 | void listInsertAfter(Node* curNode, Node* newNode){ 91 | //3 cases to insert 92 | //1st case insert at beginning of list 93 | if(head == nullptr){ 94 | head = newNode; 95 | tail = newNode; 96 | } 97 | else if(curNode == tail) 98 | { 99 | tail->next = newNode; 100 | tail = newNode; 101 | } 102 | else { 103 | newNode->next = curNode->next; 104 | curNode->next = newNode; 105 | } 106 | } 107 | 108 | 109 | void listRemoveNodeAfter(Node* curNode){ 110 | if(curNode == nullptr){ 111 | Node* nodeToRemove = head; // remove the 1st node in the list 112 | head = head->next; 113 | delete nodeToRemove; 114 | 115 | if(head == nullptr){ // if the deleted node was last one, list is now empty, so update tail pointer 116 | tail = nullptr; 117 | } 118 | } 119 | else if(curNode->next != nullptr){ 120 | Node* nodeToRemove = curNode->next; 121 | Node* succeedingNode = nodeToRemove->next; 122 | curNode->next = succeedingNode; 123 | delete nodeToRemove; 124 | 125 | if(succeedingNode == nullptr){ 126 | tail = curNode; 127 | } 128 | } 129 | } 130 | 131 | }; 132 | 133 | 134 | int main(){ 135 | Linked_list* myLinkedList = new Linked_list(); 136 | 137 | //Write test cases here: 138 | 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Double Linked List/InsertNodeAfter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Node class representing a single element in the doubly linked list 6 | class Node { 7 | public: 8 | int data; // Data stored in the node 9 | Node* next; // Pointer to the next node 10 | Node* prev; // Pointer to the previous node 11 | 12 | // Constructor to initialize a new node with given data 13 | Node(int item) : data(item), next(nullptr), prev(nullptr) {} 14 | }; 15 | 16 | // DoublyLinkedList class to manage the list 17 | class DoublyLinkedList { 18 | public: 19 | Node* head; // Pointer to the first node in the list 20 | Node* tail; // Pointer to the last node in the list 21 | 22 | // Constructor to initialize an empty list 23 | DoublyLinkedList() : head(nullptr), tail(nullptr) {} 24 | 25 | // Member function to insert a new node after a specific node 26 | void ListInsertNodeAfter(Node* currentNode, Node* newNode) { 27 | 28 | if (head == nullptr) { //empty list 29 | head = newNode; 30 | tail = newNode; 31 | } 32 | else if (currentNode == tail) { //inserting after tail 33 | tail->next = newNode; 34 | newNode->prev = tail; 35 | tail = newNode; 36 | } 37 | else { 38 | Node* successor = currentNode->next; //inserting after middle node 39 | newNode->next = successor; 40 | newNode->prev = currentNode; 41 | currentNode->next = newNode; 42 | successor->prev = newNode; 43 | } 44 | } 45 | 46 | // Member function to append a new node at the end of the list (for testing purposes) 47 | void ListAppend(int item) { 48 | Node* newNode = new Node(item); 49 | 50 | if (!tail) { 51 | head = newNode; 52 | tail = newNode; 53 | } else { 54 | tail->next = newNode; 55 | newNode->prev = tail; 56 | tail = newNode; 57 | } 58 | } 59 | 60 | // Member function to search for a node with a specific value (for finding insertion point) 61 | Node* ListSearch(int dataValue) { 62 | Node* currentNode = head; 63 | while (currentNode) { 64 | if (currentNode->data == dataValue) { 65 | return currentNode; // Return the node if found 66 | } 67 | currentNode = currentNode->next; 68 | } 69 | return nullptr; 70 | } 71 | 72 | // Display the list from head to tail 73 | void PrintForward() { 74 | Node* current = head; 75 | while (current) { 76 | cout << current->data << " "; 77 | current = current->next; 78 | } 79 | cout << endl; 80 | } 81 | }; 82 | 83 | int main() { 84 | DoublyLinkedList* myList = new DoublyLinkedList(); 85 | 86 | // 1. Create sample data 87 | cout << "Appending values: 10, 20, 30, 40, 50\n"; 88 | myList->ListAppend(10); 89 | myList->ListAppend(20); 90 | myList->ListAppend(30); 91 | myList->ListAppend(40); 92 | myList->ListAppend(50); 93 | 94 | cout << "Initial list: "; 95 | myList->PrintForward(); // 10 20 30 40 50 96 | 97 | // 2. Insert in the middle: insert 35 after 30 98 | Node* node30 = myList->ListSearch(30); 99 | if (node30 != nullptr) { 100 | Node* node35 = new Node(35); 101 | myList->ListInsertNodeAfter(node30, node35); 102 | cout << "After inserting 35 after 30: "; 103 | myList->PrintForward(); // 10 20 30 35 40 50 104 | } 105 | 106 | // 3. Insert after the tail: insert 60 after 50 107 | Node* node50 = myList->ListSearch(50); 108 | if (node50 != nullptr) { 109 | Node* node60 = new Node(60); 110 | myList->ListInsertNodeAfter(node50, node60); 111 | cout << "After inserting 60 after 50 (tail): "; 112 | myList->PrintForward(); // 10 20 30 35 40 50 60 113 | } 114 | 115 | // 4. Insert after the head: insert 15 after 10 116 | Node* node10 = myList->ListSearch(10); 117 | if (node10 != nullptr) { 118 | Node* node15 = new Node(15); 119 | myList->ListInsertNodeAfter(node10, node15); 120 | cout << "After inserting 15 after 10: "; 121 | myList->PrintForward(); // 10 15 20 30 35 40 50 60 122 | } 123 | 124 | // 5. Insert into an empty list 125 | DoublyLinkedList* emptyList = new DoublyLinkedList(); 126 | Node* node100 = new Node(100); 127 | emptyList->ListInsertNodeAfter(nullptr, node100); // inserting into empty list 128 | cout << "Insert into empty list (should be 100): "; 129 | emptyList->PrintForward(); // 100 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /Week 8 Binary Search Trees (BST) /Iterative/Search: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | //Iterative Search 6 | // Define the Node structure for BST 7 | class BSTNode { 8 | public: 9 | int key; // Value of the node 10 | BSTNode* left; // Pointer to the left child 11 | BSTNode* right; // Pointer to the right child 12 | 13 | // Constructor to create a new node 14 | BSTNode(int nodeKey) : key(nodeKey), left(nullptr), right(nullptr) {} 15 | }; 16 | 17 | // Define the BST class with the iterative search function 18 | class BinarySearchTree { 19 | private: 20 | BSTNode* root; // Root of the BST 21 | 22 | //helper function to delete all tree nodes 23 | void DeleteTree(BSTNode* subtreeRoot) { 24 | if (subtreeRoot) { 25 | DeleteTree(subtreeRoot->left); 26 | DeleteTree(subtreeRoot->right); 27 | delete subtreeRoot; 28 | } 29 | } 30 | 31 | public: 32 | // Constructor initializes an empty BST 33 | BinarySearchTree() : root(nullptr) {} 34 | 35 | ~BinarySearchTree() { 36 | DeleteTree(root); 37 | } 38 | 39 | // Insert a node iteratively in the tree 40 | void InsertNode(BSTNode* newNode) { 41 | if (root == nullptr) { 42 | root = newNode; 43 | } 44 | else { 45 | BSTNode* currentNode = root; 46 | while (currentNode) { 47 | if (newNode->key < currentNode->key) { 48 | if (currentNode->left == nullptr) { 49 | currentNode->left = newNode; 50 | currentNode = nullptr; 51 | } 52 | else { 53 | currentNode = currentNode->left; 54 | } 55 | } 56 | else { 57 | if (currentNode->right == nullptr) { 58 | currentNode->right = newNode; 59 | currentNode = nullptr; 60 | } 61 | else { 62 | currentNode = currentNode->right; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | void PrintInorder() { 70 | if (root == nullptr) { 71 | cout << "Tree is empty." << endl; 72 | return; 73 | } 74 | 75 | stack nodeStack; 76 | BSTNode* current = root; 77 | 78 | cout << "Iterative Inorder Traversal: "; 79 | 80 | while (current != nullptr || !nodeStack.empty()) { 81 | // 1. Go as far left as possible 82 | while (current != nullptr) { 83 | nodeStack.push(current); 84 | current = current->left; 85 | } 86 | 87 | // 2. Visit the top node 88 | current = nodeStack.top(); 89 | nodeStack.pop(); 90 | cout << current->key << " "; 91 | 92 | // 3. Move to the right subtree 93 | current = current->right; 94 | } 95 | 96 | cout << endl; 97 | } 98 | 99 | // Iterative search function for a key, node by node in the tree 100 | BSTNode* Search(int SearchKey) { 101 | BSTNode* currentNode = root; // Start the search from the root 102 | 103 | // Continue searching until cur is NULL 104 | while (currentNode) { 105 | if (SearchKey == currentNode->key) { 106 | // Key is found, return the current node 107 | return currentNode; 108 | } else if (SearchKey < currentNode->key) { 109 | // If key is less than currentNode's key, go left 110 | currentNode = currentNode->left; 111 | } else { 112 | // If key is greater than cur's key, go right 113 | currentNode = currentNode->right; 114 | } 115 | } 116 | // Key was not found in the tree 117 | return nullptr; 118 | } 119 | }; 120 | 121 | int main() { 122 | BinarySearchTree bst; 123 | BSTNode* n1 = new BSTNode(8); 124 | BSTNode* n2 = new BSTNode(3); 125 | BSTNode* n3 = new BSTNode(10); 126 | BSTNode* n4 = new BSTNode(1); 127 | BSTNode* n5 = new BSTNode(6); 128 | BSTNode* n6 = new BSTNode(14); 129 | BSTNode* n7 = new BSTNode(4); 130 | BSTNode* n8 = new BSTNode(7); 131 | BSTNode* n9 = new BSTNode(13); 132 | 133 | // Insert nodes into the BST 134 | bst.InsertNode(n1); 135 | bst.InsertNode(n2); 136 | bst.InsertNode(n3); 137 | bst.InsertNode(n4); 138 | bst.InsertNode(n5); 139 | bst.InsertNode(n6); 140 | bst.InsertNode(n7); 141 | bst.InsertNode(n8); 142 | bst.InsertNode(n9); 143 | 144 | //Print the tree content 145 | bst.PrintInorder(); 146 | bst.Search(100) ? cout << "key found in tree" : cout << "key NOT found"; 147 | return 0; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /Week 5 Linked Lists/Recursion on Linked List: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class SinglyLinkedNode { 5 | public: 6 | int data; 7 | SinglyLinkedNode* next; 8 | 9 | // Constructor initializes node 10 | SinglyLinkedNode(int val) : data(val), next(nullptr) {} 11 | }; 12 | 13 | 14 | class SinglyLinkedList { 15 | private: 16 | SinglyLinkedNode* head; 17 | SinglyLinkedNode* tail; 18 | 19 | /* 20 | Example: 10 → 20 → 30 → 40 → nullptr 21 | TraverseRecursive(head): 22 | 23 | Call 1: node = 10 → not nullptr → print 10, recurse on 20 24 | Call 2: node = 20 → not nullptr → print 20, recurse on 30 25 | Call 3: node = 30 → not nullptr → print 30, recurse on 40 26 | Call 4: node = 40 → not nullptr → print 40, recurse on nullptr 27 | Call 5: node = nullptr → base case hit → return immediately 28 | 29 | Unwinding: 30 | No more actions (all printing was done before recursion) 31 | 32 | Output: 33 | 10 20 30 40 34 | */ 35 | void TraverseRecursive(SinglyLinkedNode* node) const { 36 | if (node == nullptr){ 37 | return; // base case 38 | } 39 | Visit(node); // process current node 40 | TraverseRecursive(node->next); // recursive call on the rest of nodes 41 | } 42 | 43 | /* 44 | Example: 10 → 20 → 30 → 40 → nullptr 45 | TraverseReverseRecursive(head): 46 | 47 | Call 1: node = 10 → not nullptr → recurse on 20 48 | Call 2: node = 20 → not nullptr → recurse on 30 49 | Call 3: node = 30 → not nullptr → recurse on 40 50 | Call 4: node = 40 → not nullptr → recurse on nullptr 51 | Call 5: node = nullptr → base case hit → return immediately 52 | 53 | Unwinding: 54 | Return to node = 40 → print 40 55 | Return to node = 30 → print 30 56 | Return to node = 20 → print 20 57 | Return to node = 10 → print 10 58 | 59 | Output: 60 | 40 30 20 10 61 | */ 62 | void TraverseReverseRecursive(SinglyLinkedNode* node) const { 63 | if (node == nullptr){ 64 | return; // base case 65 | } 66 | TraverseReverseRecursive(node->next); // recursive call on the rest of the nodes 67 | Visit(node); // process the node 68 | } 69 | 70 | /* 71 | SearchRecursiveHelper(node, target): 72 | 73 | Base case A: node == nullptr → not found → return false 74 | Base case B: node->data == target → found → return true 75 | Recursive case: search the rest of the list 76 | return SearchRecursiveHelper(node->next, target) 77 | */ 78 | bool SearchRecursiveHelper(SinglyLinkedNode* node, int target) const { 79 | if (node == nullptr) return false; // base case: not found 80 | if (node->data == target) return true; // base case: found 81 | return SearchRecursiveHelper(node->next, target); // recurse further 82 | } 83 | 84 | // Visit = print node data 85 | void Visit(SinglyLinkedNode* node) const { 86 | std::cout << node->data << " "; 87 | } 88 | 89 | public: 90 | // Constructor initializes list to empty 91 | SinglyLinkedList() : head(nullptr), tail(nullptr) {} 92 | 93 | // Append in O(1) using tail 94 | void Append(int val) { 95 | SinglyLinkedNode* newNode = new SinglyLinkedNode(val); 96 | if (head == nullptr) { // empty list 97 | head = newNode; 98 | tail = newNode; 99 | } else { // add to tail 100 | tail->next = newNode; 101 | tail = newNode; 102 | } 103 | } 104 | 105 | 106 | //destructor 107 | ~SinglyLinkedList() { 108 | SinglyLinkedNode* cur = head; 109 | while (cur != nullptr) { 110 | SinglyLinkedNode* nextNode = cur->next; 111 | delete cur; 112 | cur = nextNode; 113 | } 114 | head = nullptr; 115 | tail = nullptr; 116 | } 117 | 118 | 119 | //Public Wrapper Functions 120 | void Traverse() const { 121 | TraverseRecursive(head); 122 | } 123 | 124 | void TraverseReverse() const { 125 | TraverseReverseRecursive(head); 126 | } 127 | 128 | bool SearchRecursive(int target) const { 129 | return SearchRecursiveHelper(head, target); 130 | } 131 | 132 | 133 | }; 134 | 135 | 136 | int main() { 137 | SinglyLinkedList* list = new SinglyLinkedList(); 138 | 139 | // Build list: 10 → 20 → 30 → 40 140 | list->Append(10); 141 | list->Append(20); 142 | list->Append(30); 143 | list->Append(40); 144 | 145 | cout << "Recursive traversal (head -> tail): "; 146 | list->Traverse(); 147 | cout << endl; 148 | 149 | cout << "Recursive reverse traversal (tail -> head): "; 150 | list->TraverseReverse(); 151 | cout << endl; 152 | 153 | // Test recursive search 154 | cout << "Search 30: " << (list->SearchRecursive(30) ? "Found" : "Not Found") << endl; 155 | cout << "Search 99: " << (list->SearchRecursive(99) ? "Found" : "Not Found") << endl; 156 | 157 | delete list; // destructor clears memory 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /Week 10 Heap/MaxHeap with Insert and Remove: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | using namespace std; 4 | 5 | class MaxHeap { 6 | public: 7 | int* heapArray; 8 | int allocationSize; 9 | int heapSize; 10 | 11 | void ResizeArray() { 12 | int newAllocationSize = allocationSize * 2; 13 | int* newArray = new int[newAllocationSize]; 14 | if (newArray) { 15 | // Copy from existing array to new array 16 | for (int i = 0; i < allocationSize; i++) { 17 | newArray[i] = heapArray[i]; 18 | } 19 | 20 | // Delete old array and set pointer to new 21 | delete[] heapArray; 22 | heapArray = newArray; 23 | 24 | // Update allocation size 25 | allocationSize = newAllocationSize; 26 | } 27 | } 28 | 29 | //constructor 30 | MaxHeap() { 31 | allocationSize = 1; 32 | heapArray = new int[allocationSize]; 33 | heapSize = 0; 34 | } 35 | //destructor 36 | ~MaxHeap() { 37 | delete[] heapArray; 38 | } 39 | 40 | 41 | // PercolateUp 42 | void PercolateUp(int nodeIndex) { 43 | while (nodeIndex > 0) { 44 | // Compute the parent node's index 45 | int parentIndex = (nodeIndex - 1) / 2; 46 | 47 | // Check for a violation of the max-heap property 48 | if (heapArray[nodeIndex] <= heapArray[parentIndex]) { 49 | // No violation, so percolate up is done 50 | return; 51 | } 52 | else { 53 | // Swap heapArray[nodeIndex] and heapArray[parentIndex] 54 | int temp = heapArray[nodeIndex]; 55 | heapArray[nodeIndex] = heapArray[parentIndex]; 56 | heapArray[parentIndex] = temp; 57 | 58 | // Continue the loop from the parent node 59 | nodeIndex = parentIndex; 60 | } 61 | } 62 | } 63 | 64 | 65 | // PercolateDown (as provided) 66 | void PercolateDown(int nodeIndex) { 67 | int childIndex = 2 * nodeIndex + 1; 68 | int value = heapArray[nodeIndex]; 69 | 70 | while (childIndex < heapSize) { 71 | // Find the max among the node and all the node's children 72 | int maxValue = value; 73 | int maxIndex = -1; 74 | for (int i = 0; i < 2 && i + childIndex < heapSize; i++) { 75 | if (heapArray[i + childIndex] > maxValue) { 76 | maxValue = heapArray[i + childIndex]; 77 | maxIndex = i + childIndex; 78 | } 79 | } 80 | 81 | // Check for a violation of the max-heap property 82 | if (maxValue == value) { 83 | return; 84 | } 85 | else { 86 | // Swap heapArray[nodeIndex] and heapArray[maxIndex] 87 | int temp = heapArray[nodeIndex]; 88 | heapArray[nodeIndex] = heapArray[maxIndex]; 89 | heapArray[maxIndex] = temp; 90 | 91 | // Continue loop from the max index node 92 | nodeIndex = maxIndex; 93 | childIndex = 2 * nodeIndex + 1; 94 | } 95 | } 96 | } 97 | 98 | 99 | // Insert element into Heap 100 | void Insert(int value) { 101 | // Resize if needed 102 | if (heapSize == allocationSize) { 103 | ResizeArray(); 104 | } 105 | 106 | // Add the new value to the end of the array 107 | heapArray[heapSize] = value; 108 | heapSize++; 109 | 110 | // Percolate up from the last index to restore heap property 111 | PercolateUp(heapSize - 1); 112 | } 113 | 114 | 115 | // Remove root element which is the Max value of the Heap 116 | int Remove() { 117 | // Save the max value from the root of the heap 118 | int maxValue = heapArray[0]; 119 | 120 | // Move the last item in the array into index 0 121 | int replaceValue = heapArray[heapSize - 1]; 122 | heapSize--; 123 | if (heapSize > 0) { 124 | heapArray[0] = replaceValue; 125 | 126 | // Percolate down to restore max-heap property 127 | PercolateDown(0); 128 | } 129 | 130 | // Return the max value 131 | return maxValue; 132 | } 133 | 134 | }; 135 | 136 | 137 | 138 | int main() { 139 | MaxHeap heap; 140 | 141 | // Test Insert() 142 | cout << "Inserting values: 50, 30, 20, 40, 70, 60, 80" << endl; 143 | heap.Insert(50); 144 | heap.Insert(30); 145 | heap.Insert(20); 146 | heap.Insert(40); 147 | heap.Insert(70); 148 | heap.Insert(60); 149 | heap.Insert(80); 150 | 151 | cout << "Heap after inserts (internal array order): "; 152 | // Access internal structure for demonstration 153 | // (This relies on understanding that valid elements are heapArray[0..heapSize-1]) 154 | for (int i = 0; i < 7; i++) { 155 | cout << heap.heapArray[i] << " "; 156 | } 157 | cout << endl; 158 | 159 | // Test Remove() 160 | cout << "\nRemoving values (should come out in descending order): " << endl; 161 | while (true) { 162 | // Stop when heap is empty 163 | if (heap.heapSize == 0) break; 164 | 165 | int removedValue = heap.Remove(); 166 | cout << "Removed max = " << removedValue << endl; 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /Week 10 Heap/PriorityQueue: -------------------------------------------------------------------------------- 1 | /* 2 | Suppose you are managing a hospital emergency room. Patients arrive with varying degrees 3 | of severity, and you need to prioritize their treatment based on severity levels. 4 | You decide to use a priority queue to manage the patient queue, where patients with 5 | higher severity levels are treated first. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | class PriorityQueue { 14 | private: 15 | vector heapArray; // Vector to store elements of the priority queue 16 | 17 | // Helper function to percolate up an element to its correct position in the max-heap 18 | void MaxHeapPercolateUp(int nodeIndex) { 19 | while (nodeIndex > 0) { // Loop until the root is reached 20 | int parentIndex = (nodeIndex - 1) / 2; // Calculate the index of the parent node 21 | if (heapArray[nodeIndex] <= heapArray[parentIndex]) // If current node <= parent node, heap property is satisfied 22 | return; // Exit loop 23 | else { 24 | swap(heapArray[nodeIndex], heapArray[parentIndex]); // Otherwise, swap current node with parent node 25 | nodeIndex = parentIndex; // Move up to the parent node and continue percolating up 26 | } 27 | } 28 | } 29 | 30 | // Helper function to percolate down an element to its correct position in the max-heap 31 | void MaxHeapPercolateDown(int nodeIndex, int arraySize) { 32 | int childIndex = 2 * nodeIndex + 1; // Calculate the index of the left child 33 | int value = heapArray[nodeIndex]; // Store the value of the current node 34 | 35 | while (childIndex < arraySize) { // Loop while the current node has at least one child 36 | // Find the maximum value among the current node and its children 37 | int maxValue = value; // Initialize the maximum value with the value of the current node 38 | int maxIndex = -1; // Initialize the index of the maximum value 39 | 40 | // Iterate through the children nodes (maximum 2 children) 41 | for (int i = 0; i < 2 && i + childIndex < arraySize; i++) { 42 | if (heapArray[i + childIndex] > maxValue) { // If a child node has a greater value than the current maximum value 43 | maxValue = heapArray[i + childIndex]; // Update the maximum value 44 | maxIndex = i + childIndex; // Update the index of the maximum value 45 | } 46 | } 47 | 48 | if (maxValue == value) // If the maximum value is the value of the current node, heap property is satisfied 49 | return; // Exit loop 50 | else { 51 | swap(heapArray[nodeIndex], heapArray[maxIndex]); // Otherwise, swap the current node with the maximum value node 52 | nodeIndex = maxIndex; // Move down to the maximum value node and continue percolating down 53 | childIndex = 2 * nodeIndex + 1; // Update the index of the left child 54 | } 55 | } 56 | } 57 | 58 | public: 59 | // Function to insert a new element into the priority queue 60 | void Enqueue(int value) { 61 | heapArray.push_back(value); // Add the new element to the end of the heap 62 | MaxHeapPercolateUp(heapArray.size() - 1); // Percolate up the new element to its correct position 63 | } 64 | 65 | // Function to remove and return the element with the highest priority (root of the max-heap) 66 | int Dequeue() { 67 | if (heapArray.empty()) { // If the priority queue is empty 68 | cout << "Priority queue is empty!" << endl; // Output error message 69 | return -1; // Return -1 indicating failure 70 | } 71 | 72 | int maxVal = heapArray[0]; // Store the value of the root node (element with highest priority) 73 | swap(heapArray[0], heapArray.back()); // Swap the root node with the last node 74 | heapArray.pop_back(); // Remove the last node (previously the root) 75 | MaxHeapPercolateDown(0, heapArray.size()); // Percolate down the new root to its correct position 76 | return maxVal; // Return the value of the removed element (highest priority) 77 | } 78 | 79 | // Function to return the value of the element with the highest priority (root of the max-heap) without removing it 80 | int Peek() { 81 | if (heapArray.empty()) { // If the priority queue is empty 82 | cout << "Priority queue is empty!" << endl; // Output error message 83 | return -1; // Return -1 indicating failure 84 | } 85 | 86 | return heapArray[0]; // Return the value of the root node (element with highest priority) 87 | } 88 | 89 | // Function to check if the priority queue is empty 90 | bool IsEmpty() { 91 | return heapArray.empty(); // Return true if the priority queue is empty, false otherwise 92 | } 93 | 94 | // Function to return the number of elements (nodes) in the priority queue 95 | int GetLength() { 96 | return heapArray.size(); // Return the size of the heap array 97 | } 98 | }; 99 | 100 | int main() { 101 | PriorityQueue priorityQueue; // Create an instance of the PriorityQueue class 102 | 103 | // Patients enter the ER in this order - Enqueue patients with their priorities, 104 | priorityQueue.Enqueue(10); // John: Severe injury (Priority: 10) 105 | priorityQueue.Enqueue(5); // Alice: Moderate injury (Priority: 5) 106 | priorityQueue.Enqueue(15); // Bob: Critical condition (Priority: 15) 107 | priorityQueue.Enqueue(3); // Sarah: Minor injury (Priority: 3) 108 | 109 | // Dequeue and treat patients in order of priority 110 | cout << "Treating patients in priority order:" << endl; 111 | while (!priorityQueue.IsEmpty()) { // Loop until the priority queue is empty 112 | cout << "Treating patient with priority: " << priorityQueue.Peek() << endl; // Peek at the next patient's priority 113 | priorityQueue.Dequeue(); // Treat the patient by removing them from the priority queue 114 | } 115 | 116 | // Check if the priority queue is empty after treating all patients 117 | if (priorityQueue.IsEmpty()) { 118 | cout << "All patients treated. Priority queue is empty." << endl; 119 | } 120 | 121 | // Get the total number of patients treated 122 | cout << "Total number of patients treated: " << priorityQueue.GetLength() << endl; 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /Week 9 AVL Balanced Tree/AVLNode and AVLTree Class: -------------------------------------------------------------------------------- 1 | // AVLTreeRecursiveDemo 2 | 3 | #include 4 | using namespace std; 5 | 6 | class AVLNode { 7 | private: 8 | int key; // The integer value (key) stored in this node 9 | int height; // The height of this node (distance to farthest leaf) 10 | AVLNode* left; // Pointer to left child 11 | AVLNode* right; // Pointer to right child 12 | AVLNode* parent; // Pointer to parent node 13 | 14 | public: 15 | // Constructor 16 | // Initializes a new node with the given key and null pointers. 17 | AVLNode(int k) 18 | : key(k), height(0), left(nullptr), right(nullptr), parent(nullptr) {} 19 | 20 | 21 | //GETTERS 22 | 23 | 24 | // Returns the integer key stored in this node. 25 | int GetKey() const { 26 | return key; 27 | } 28 | 29 | // Returns the height of this node. 30 | // The height represents the number of edges on the longest path 31 | // from this node down to a leaf. 32 | int GetHeight() const { 33 | return height; 34 | } 35 | 36 | // Returns a pointer to the left child of this node. 37 | // If there is no left child, returns nullptr. 38 | AVLNode* GetLeft() const { 39 | return left; 40 | } 41 | 42 | // Returns a pointer to the right child of this node. 43 | // If there is no right child, returns nullptr. 44 | AVLNode* GetRight() const { 45 | return right; 46 | } 47 | 48 | // Returns a pointer to this node’s parent. 49 | // This is useful for updating ancestor heights after insertions. 50 | AVLNode* GetParent() const { 51 | return parent; 52 | } 53 | 54 | 55 | //SETTERS 56 | 57 | 58 | // Sets the parent pointer of this node. 59 | // Does not modify the parent's children; only this node’s reference. 60 | void SetParent(AVLNode* p) { 61 | parent = p; 62 | } 63 | 64 | // Sets the left child pointer to point to a new node. 65 | // If the new node is not null, its parent pointer is updated 66 | // to point back to the current node. 67 | // Finally, this node’s height is recalculated to maintain correctness. 68 | void SetLeft(AVLNode* node) { 69 | left = node; 70 | if (node) node->parent = this; 71 | UpdateHeight(); 72 | } 73 | 74 | // Sets the right child pointer to point to a new node. 75 | // If the new node is not null, its parent pointer is updated 76 | // to point back to the current node. 77 | // Finally, this node’s height is recalculated to maintain correctness. 78 | void SetRight(AVLNode* node) { 79 | right = node; 80 | if (node) node->parent = this; 81 | UpdateHeight(); 82 | } 83 | 84 | 85 | // FIND HEIGHT AND BALANCE 86 | 87 | 88 | // Recalculates the height of this node based on its children. 89 | // The height is 1 more than the maximum height of its two subtrees. 90 | void UpdateHeight() { 91 | int lh = (left ? left->height : -1); 92 | int rh = (right ? right->height : -1); 93 | height = (lh > rh ? lh : rh) + 1; 94 | } 95 | 96 | // Computes the balance factor of this node. 97 | // Balance Factor = height(left subtree) - height(right subtree) 98 | // A perfectly balanced node has balance = 0. 99 | // Positive means left-heavy, negative means right-heavy. 100 | int GetBalance() const { 101 | int lh = (left ? left->height : -1); 102 | int rh = (right ? right->height : -1); 103 | return lh - rh; 104 | } 105 | }; 106 | 107 | class AVLTree { 108 | private: 109 | AVLNode* root; // Pointer to the root node of the tree 110 | 111 | // Recursively deletes all nodes in post-order traversal. 112 | // This ensures child nodes are deleted before their parent. 113 | void DeleteTree(AVLNode* subtreeRoot) { 114 | if (subtreeRoot) { 115 | DeleteTree(subtreeRoot->GetLeft()); 116 | DeleteTree(subtreeRoot->GetRight()); 117 | delete subtreeRoot; 118 | } 119 | } 120 | 121 | // Recursive BST-style insertion. 122 | // Returns a pointer to the new (or unchanged) subtree root. 123 | AVLNode* InsertRec(AVLNode* node, int key) { 124 | // Base case: empty position found, create a new node 125 | if (!node) { 126 | return new AVLNode(key); 127 | } 128 | 129 | // Recur down the tree according to BST ordering 130 | if (key < node->GetKey()) { 131 | AVLNode* leftChild = InsertRec(node->GetLeft(), key); 132 | node->SetLeft(leftChild); 133 | } 134 | else if (key > node->GetKey()) { 135 | AVLNode* rightChild = InsertRec(node->GetRight(), key); 136 | node->SetRight(rightChild); 137 | } 138 | // Duplicate keys are ignored 139 | 140 | // Update height before returning to parent 141 | node->UpdateHeight(); 142 | return node; 143 | } 144 | 145 | // Helper function to print nodes in sorted order (in-order traversal) 146 | void PrintInOrderRec(AVLNode* node) const { 147 | if (!node) return; 148 | PrintInOrderRec(node->GetLeft()); 149 | cout << node->GetKey() 150 | << "(h=" << node->GetHeight() 151 | << ", bf=" << node->GetBalance() << ") "; 152 | PrintInOrderRec(node->GetRight()); 153 | } 154 | 155 | public: 156 | // Constructor initializes an empty tree 157 | AVLTree() : root(nullptr) {} 158 | 159 | // Destructor calls recursive DeleteTree() to free memory 160 | ~AVLTree() { 161 | DeleteTree(root); 162 | } 163 | 164 | // Returns a pointer to the root of the tree 165 | AVLNode* GetRoot() const { 166 | return root; 167 | } 168 | 169 | // Public interface for insertion 170 | // Calls recursive helper function starting from root. 171 | void Insert(int key) { 172 | root = InsertRec(root, key); 173 | } 174 | 175 | // Public interface to print tree contents in sorted order 176 | // Also displays each node’s height and balance factor. 177 | void PrintInOrder() const { 178 | PrintInOrderRec(root); 179 | cout << "\n"; 180 | } 181 | }; 182 | 183 | int main() { 184 | AVLTree tree; 185 | 186 | // Insert some nodes (BST behavior) 187 | tree.Insert(3); 188 | tree.Insert(2); 189 | tree.Insert(4); 190 | tree.Insert(1); 191 | tree.Insert(5); 192 | 193 | cout << "In-order traversal (key, height, balance):\n"; 194 | tree.PrintInOrder(); 195 | 196 | // Print root details 197 | if (auto* r = tree.GetRoot()) { 198 | cout << "Root: " << r->GetKey() 199 | << " | Height: " << r->GetHeight() 200 | << " | Balance: " << r->GetBalance() << "\n"; 201 | } 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /Week 8 Binary Search Trees (BST) /Iterative/Remove: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | //Iterative Insert node into tree 6 | // Define the Node structure for BST 7 | class BSTNode { 8 | public: 9 | int key; // Value of the node 10 | BSTNode* left; // Pointer to the left child 11 | BSTNode* right; // Pointer to the right child 12 | 13 | // Constructor to create a new node 14 | BSTNode(int nodeKey) : key(nodeKey), left(nullptr), right(nullptr) {} 15 | }; 16 | 17 | // Define the BST class with the iterative search function 18 | class BinarySearchTree { 19 | private: 20 | BSTNode* root; // Root of the BST 21 | 22 | //helper function to delete all tree nodes 23 | void DeleteTree(BSTNode* subtreeRoot) { 24 | if (subtreeRoot) { 25 | DeleteTree(subtreeRoot->left); 26 | DeleteTree(subtreeRoot->right); 27 | delete subtreeRoot; 28 | } 29 | } 30 | 31 | public: 32 | // Constructor initializes an empty BST 33 | BinarySearchTree() : root(nullptr) {} 34 | 35 | ~BinarySearchTree() { 36 | DeleteTree(root); 37 | } 38 | 39 | // Insert a node iteratively in the tree 40 | void InsertNode(BSTNode* newNode) { 41 | if (root == nullptr) { 42 | root = newNode; 43 | } 44 | else { 45 | BSTNode* currentNode = root; 46 | while (currentNode) { 47 | if (newNode->key < currentNode->key) { 48 | if (currentNode->left == nullptr) { 49 | currentNode->left = newNode; 50 | currentNode = nullptr; 51 | } 52 | else { 53 | currentNode = currentNode->left; 54 | } 55 | } 56 | else { 57 | if (currentNode->right == nullptr) { 58 | currentNode->right = newNode; 59 | currentNode = nullptr; 60 | } 61 | else { 62 | currentNode = currentNode->right; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | void printInorder() { 70 | if (root == nullptr) { 71 | cout << "Tree is empty." << endl; 72 | return; 73 | } 74 | 75 | stack nodeStack; 76 | BSTNode* current = root; 77 | 78 | cout << "Iterative Inorder Traversal: "; 79 | 80 | while (current != nullptr || !nodeStack.empty()) { 81 | // 1. Go as far left as possible 82 | while (current != nullptr) { 83 | nodeStack.push(current); 84 | current = current->left; 85 | } 86 | 87 | // 2. Visit the top node 88 | current = nodeStack.top(); 89 | nodeStack.pop(); 90 | cout << current->key << " "; 91 | 92 | // 3. Move to the right subtree 93 | current = current->right; 94 | } 95 | 96 | cout << endl; 97 | } 98 | 99 | bool Remove(int key) { 100 | BSTNode* parent = nullptr; 101 | BSTNode* currentNode = root; 102 | 103 | // Search for the node 104 | while (currentNode) { 105 | // Check if currentNode has a matching key 106 | if (currentNode->key == key) { 107 | if (currentNode->left == nullptr && currentNode->right == nullptr) { 108 | // Remove leaf 109 | 110 | if (parent == nullptr) { // Node is root 111 | root = nullptr; 112 | } 113 | else if (parent->left == currentNode) { 114 | parent->left = nullptr; 115 | } 116 | else { 117 | parent->right = nullptr; 118 | } 119 | delete currentNode; 120 | return true; // Node found and removed 121 | } 122 | else if (currentNode->left && currentNode->right == nullptr) { 123 | // Remove node with only left child 124 | 125 | if (parent == nullptr) { // Node is root 126 | root = currentNode->left; 127 | } 128 | else if (parent->left == currentNode) { 129 | parent->left = currentNode->left; 130 | } 131 | else { 132 | parent->right = currentNode->left; 133 | } 134 | delete currentNode; 135 | return true; // Node found and removed 136 | } 137 | else if (currentNode->left == nullptr && currentNode->right) { 138 | // Remove node with only right child 139 | 140 | if (parent == nullptr) { // Node is root 141 | root = currentNode->right; 142 | } 143 | else if (parent->left == currentNode) { 144 | parent->left = currentNode->right; 145 | } 146 | else { 147 | parent->right = currentNode->right; 148 | } 149 | delete currentNode; 150 | return true; // Node found and removed 151 | } 152 | else { 153 | // Remove node with two children 154 | 155 | // Find successor (leftmost child of right subtree) 156 | BSTNode* successor = currentNode->right; 157 | while (successor->left) { 158 | successor = successor->left; 159 | } 160 | currentNode->key = successor->key; // Copy successor's key to current node 161 | parent = currentNode; 162 | 163 | // Reassign currentNode and key so that loop continues with new key 164 | currentNode = currentNode->right; 165 | key = successor->key; 166 | } 167 | } 168 | else if (currentNode->key < key) { 169 | // Search right 170 | 171 | parent = currentNode; 172 | currentNode = currentNode->right; 173 | } 174 | else { 175 | // Search left 176 | 177 | parent = currentNode; 178 | currentNode = currentNode->left; 179 | } 180 | } 181 | return false; // Node not found 182 | } 183 | 184 | }; 185 | 186 | int main() { 187 | BinarySearchTree bst; 188 | bst.InsertNode(new BSTNode(10)); 189 | bst.InsertNode(new BSTNode(5)); 190 | bst.InsertNode(new BSTNode(15)); 191 | bst.InsertNode(new BSTNode(3)); 192 | bst.InsertNode(new BSTNode(6)); 193 | bst.InsertNode(new BSTNode(14)); 194 | bst.InsertNode(new BSTNode(4)); 195 | bst.InsertNode(new BSTNode(7)); 196 | bst.InsertNode(new BSTNode(13)); 197 | 198 | //Print the tree content 199 | cout << "Initial (inorder): "; 200 | bst.printInorder(); // Expect: 3 4 5 6 7 10 13 14 15 201 | 202 | // 1) Remove a LEAF 203 | cout << "\nRemove 7 (leaf) -> " << (bst.Remove(7) ? "OK" : "FAIL") << '\n'; 204 | cout << "After: "; bst.printInorder(); // Expect: 3 4 5 6 10 13 14 15 205 | 206 | // 2) Remove ONE-CHILD node (3 has right child 4) 207 | cout << "\nRemove 3 (one child) -> " << (bst.Remove(3) ? "OK" : "FAIL") << '\n'; 208 | cout << "After: "; bst.printInorder(); // Expect: 4 5 6 10 13 14 15 209 | 210 | // 3) Remove TWO-CHILDREN node (5 has 4,6) 211 | cout << "\nRemove 5 (two children) -> " << (bst.Remove(5) ? "OK" : "FAIL") << '\n'; 212 | cout << "After: "; bst.printInorder(); // Expect: 4 6 10 13 14 15 213 | 214 | // 4) Remove ROOT (10 has two children) 215 | cout << "\nRemove 10 (root, two children) -> " << (bst.Remove(10) ? "OK" : "FAIL") << '\n'; 216 | cout << "After: "; bst.printInorder(); // Expect: 4 6 13 14 15 217 | 218 | // 5) Remove MISSING key 219 | cout << "\nRemove 999 (missing) -> " << (bst.Remove(999) ? "OK" : "FAIL") << '\n'; 220 | cout << "After: "; bst.printInorder(); // Expect unchanged: 4 6 13 14 15 221 | 222 | // 6) Drain remaining nodes to empty 223 | int toRemove[] = {4, 6, 13, 14, 15}; 224 | for (int k : toRemove) { 225 | cout << "Remove " << k << " -> " << (bst.Remove(k) ? "OK" : "FAIL") << '\n'; 226 | cout << "After: "; bst.printInorder(); 227 | } 228 | return 0; 229 | } 230 | --------------------------------------------------------------------------------