├── README.md ├── .gitignore ├── Student.hpp ├── LICENSE ├── BinarySearchTree_main.cpp ├── Stack.hpp ├── Extendable_Fixed_Vector_main.cpp ├── Queue.hpp ├── Stack.hxx ├── DLinkedList_main.cpp ├── SLinkedList_main.cpp ├── Queue.hxx ├── BinarySearchTree.hpp ├── Student.cpp ├── Queue_Stack_main.cpp ├── SLinkedList.hpp ├── DLinkedList.hpp ├── FixedVector.hpp ├── ExtendableVector.hpp ├── CircularDummyNodeDLinkedList.hxx ├── DummyNodeDLinkedList.hxx ├── DLinkedList.hxx ├── SLinkedList.hxx └── BinarySearchTree.hxx /README.md: -------------------------------------------------------------------------------- 1 | # CPSC-131-Data-Structures-code 2 | CPSC 131 Data Structures code 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /Student.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | 8 | 9 | class Student 10 | { 11 | friend std::ostream & operator<<( std::ostream & os, const Student & student ); 12 | friend std::istream & operator>>( std::istream & is, Student & student ); 13 | 14 | friend bool operator==( const Student & lhs, const Student & rhs ); 15 | friend bool operator <( const Student & lhs, const Student & rhs ); 16 | 17 | public: 18 | Student(); 19 | Student( std::string name, unsigned nsem = 1U ); 20 | 21 | void updateNSemesters(); 22 | void name ( const std::string & name ); 23 | void semesters ( unsigned semesters ); 24 | 25 | private: 26 | std::string _name; 27 | unsigned _numOfSemesters = 0; 28 | }; 29 | 30 | 31 | bool operator!=( const Student & lhs, const Student & rhs ); 32 | bool operator<=( const Student & lhs, const Student & rhs ); 33 | bool operator> ( const Student & lhs, const Student & rhs ); 34 | bool operator>=( const Student & lhs, const Student & rhs ); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fall 2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /BinarySearchTree_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "BinarySearchTree.hpp" 5 | 6 | 7 | 8 | 9 | 10 | 11 | int main() 12 | { 13 | // Ricardo // 14 | BinarySearchTree studentGrades, gradeBook; // / // 15 | studentGrades.insert( "Ricardo", 2.5 ); // Ellen // 16 | studentGrades.insert( "Ellen", 3.5 ); // / \ // 17 | studentGrades.insert( "Chen", 2.5 ); // Chen Kevin // 18 | studentGrades.insert( "Kevin", 3.25 ); // \ // 19 | studentGrades.insert( "Kumar", 3.05 ); // Kumar // 20 | 21 | 22 | gradeBook = studentGrades; // test assignment operator, copy constructor, and destructor 23 | 24 | 25 | std::string myKey = "Ellen"; // find grade of one student 26 | auto value = studentGrades.search( myKey ); 27 | 28 | std::cout << "Grade of " << myKey << " is " << value << '\n'; 29 | 30 | studentGrades.printInorder(); // print the entire BST 31 | 32 | if( gradeBook.getHeight() != 3 ) std::cerr << "Tree height does not match expected\n"; 33 | 34 | gradeBook.remove( "Ellen" ); 35 | if( gradeBook.getHeight() != 2 ) std::cerr << "Tree height does not match expected\n"; 36 | } 37 | 38 | 39 | 40 | // Explicit instantiation - a technique to ensure all functions of the template are created and semantically checked. By default, 41 | // only functions called get instantiated so you won't know it has compile errors until you actually call it. 42 | template class BinarySearchTree; 43 | -------------------------------------------------------------------------------- /Stack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include // size_t 4 | 5 | #include "SLinkedList.hpp" 6 | 7 | // Stack is an adapter that provides a consistent LIFO interface over an underlying container. Here, that underlying container is 8 | // defaulted to a singly linked list. Other choices include a double linked list, vector, and so on. 9 | // Underlying container must support the following functions: 10 | // o) void push( const T & element) 11 | // o) T pop() 12 | // o) T & top() 13 | // o) bool empty() 14 | // o) size_t size() 15 | // 16 | // 17 | // Note: C++20 "Concepts" will enable better enforcement of these constraints, but for now ... 18 | template> 19 | class Stack 20 | { 21 | public: 22 | void push( const T & element ); 23 | T pop(); 24 | T & top(); // peek() in zyBook 25 | bool empty(); // isEmpty() in zyBook 26 | std::size_t size(); // getLength() in zyBook 27 | 28 | private: 29 | UnderlyingContainer collection; 30 | }; 31 | 32 | 33 | 34 | 35 | 36 | // Partial Specialization for a fixed sized Stack over a fixed sized standard array 37 | template 38 | class Stack> 39 | { 40 | public: 41 | void push( const T & element ); 42 | T pop(); 43 | T & top(); // peek() in zyBook 44 | bool empty(); // isEmpty() in zyBook 45 | std::size_t size(); // getLength() in zyBook 46 | 47 | private: 48 | std::array collection; 49 | std::size_t nextAvailableSlot = 0; // index of the next available slot that an element can be inserted. A value of zero implies an empty stack 50 | }; 51 | 52 | 53 | 54 | 55 | #include "Stack.hxx" 56 | -------------------------------------------------------------------------------- /Extendable_Fixed_Vector_main.cpp: -------------------------------------------------------------------------------- 1 | #include // quoted() 2 | #include 3 | #include 4 | 5 | #include "ExtendableVector.hpp" 6 | #include "FixedVector.hpp" 7 | #include "Student.hpp" 8 | 9 | 10 | 11 | 12 | namespace // anonymous 13 | { 14 | template 15 | void test( T & vector ) 16 | { 17 | std::cout << "\n\nTesting " << std::quoted( __func__ ) << '\n'; 18 | 19 | Student s( "Adam", 2 ); 20 | vector.push_back( s ); 21 | vector.push_back( Student( "Bob", 1 ) ); 22 | vector.push_back( Student( "Dolores", 3 ) ); 23 | for( const auto & student : vector ) std::cout << student; 24 | 25 | 26 | // add student Carla between Bob and Dolores 27 | // vector.insert( 2, Student( "Carla" ) ); // index form 28 | vector.insert( vector.begin() + 2, Student( "Carla" ) ); // iterator form 29 | for( const auto & student : vector ) std::cout << student; 30 | 31 | 32 | // update Carla's record 33 | vector[2].updateNSemesters(); 34 | std::cout << vector[2]; 35 | 36 | 37 | // remove student Adam (element at index 0) 38 | // vector.erase( 0 ); // index form 39 | vector.erase( vector.begin() ); // iterator form 40 | for( const auto & student : vector ) std::cout << student; 41 | 42 | 43 | // Copy and assignment 44 | auto aCopy = vector; // copy 45 | for( const auto & student : aCopy ) std::cout << student; 46 | 47 | vector.clear(); 48 | for( const auto & student : aCopy ) std::cout << student; 49 | 50 | vector = aCopy; // assignment; 51 | for( const auto & student : aCopy ) std::cout << student; 52 | } 53 | } // anonymous namespace 54 | 55 | 56 | 57 | int main() 58 | { 59 | FixedVector fixedStudentVector( 10 ); 60 | ExtendableVector extendableStudentVector; // in contrast to FixedVector, capacity is not specified 61 | 62 | test( fixedStudentVector ); 63 | test( extendableStudentVector ); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /Queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include // size_t 4 | 5 | #include "DLinkedList.hpp" 6 | 7 | // Queue is an adapter that provides a consistent FIFO interface over an underlying container. Here, that underlying container is 8 | // defaulted to a singly linked list. Other choices include a double linked list, vector, and so on. 9 | // Underlying container must support the following functions: 10 | // o) void push( const T & element) 11 | // o) T pop() 12 | // o) T & top() 13 | // o) bool empty() 14 | // o) size_t size() 15 | // 16 | // 17 | // Note: C++20 "Concepts" will enable better enforcement of these constraints, but for now ... 18 | template> 19 | class Queue 20 | { 21 | public: 22 | void push( const T & element ); 23 | T pop(); 24 | T & top(); // peek() in zyBook 25 | bool empty(); // isEmpty() in zyBook 26 | std::size_t size(); // getLength() in zyBook 27 | 28 | private: 29 | UnderlyingContainer collection; 30 | }; 31 | 32 | 33 | 34 | 35 | 36 | // Partial Specialization for a fixed sized Queue over a fixed sized standard array 37 | template 38 | class Queue> 39 | { 40 | public: 41 | void push( const T & element ); 42 | T pop(); 43 | T & top(); // peek() in zyBook 44 | bool empty(); // isEmpty() in zyBook 45 | std::size_t size(); // getLength() in zyBook 46 | 47 | private: 48 | std::array collection; 49 | std::size_t _front = 0; // index of the front element 50 | std::size_t _rear = 0; // index of an empty location where the next element will enter 51 | std::size_t _size = 0; // number of elements in the queue, 0 indicates an empty queue 52 | }; 53 | 54 | 55 | 56 | 57 | #include "Queue.hxx" 58 | -------------------------------------------------------------------------------- /Stack.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include // size_t 3 | #include // out_of_range 4 | #include // to_string() 5 | 6 | #include "Stack.hpp" 7 | 8 | 9 | 10 | 11 | 12 | 13 | /******************************************************************************* 14 | ** Primary implementation 15 | *******************************************************************************/ 16 | template 17 | void Stack::push( const T & element ) 18 | { 19 | collection.prepend( element ); 20 | } 21 | 22 | 23 | 24 | template 25 | T Stack::pop() 26 | { 27 | // Note: zyBook returns the value popped, the C++ standard template library does not. 28 | // popping an element from an empty stack error handling is handled by the underlying container 29 | auto element = collection.front(); 30 | collection.removeFront(); 31 | return element; 32 | } 33 | 34 | 35 | 36 | template 37 | T & Stack::top() 38 | { 39 | // Note: viewing an element from an empty stack error handling is handled by the underlying container 40 | return collection.front(); 41 | } 42 | 43 | 44 | 45 | template 46 | bool Stack::empty() 47 | { 48 | return collection.empty(); 49 | } 50 | 51 | 52 | 53 | template 54 | std::size_t Stack::size() 55 | { 56 | return collection.size(); 57 | } 58 | 59 | 60 | 61 | 62 | 63 | 64 | /******************************************************************************* 65 | ** Partial Specialization for a fixed sized Stack over a fixed sized standard array 66 | *******************************************************************************/ 67 | template 68 | void Stack>::push( const T & element ) 69 | { 70 | if( nextAvailableSlot >= collection.size() ) throw std::out_of_range( "ERROR: Attempt to add to an already full stack of " + std::to_string( collection.size() ) + " elements." ); 71 | 72 | collection[nextAvailableSlot++] = element; 73 | } 74 | 75 | 76 | 77 | template 78 | T Stack>::pop() 79 | { 80 | if( empty() ) throw std::out_of_range( "ERROR: Attempt to remove an element from an empty stack" ); 81 | 82 | // Note, zyBook returns the value popped, the C++ standard template library does not. 83 | return collection[--nextAvailableSlot]; 84 | } 85 | 86 | 87 | 88 | template 89 | T & Stack>::top() 90 | { 91 | if( empty() ) throw std::out_of_range( "ERROR: Attempt to view an element from an empty stack" ); 92 | 93 | return collection[nextAvailableSlot-1]; 94 | } 95 | 96 | 97 | 98 | template 99 | bool Stack>::empty() 100 | { 101 | return nextAvailableSlot == 0; 102 | } 103 | 104 | 105 | 106 | template 107 | std::size_t Stack>::size() 108 | { 109 | return nextAvailableSlot; 110 | } 111 | -------------------------------------------------------------------------------- /DLinkedList_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "DLinkedList.hpp" 6 | #include "Student.hpp" 7 | 8 | namespace 9 | { 10 | // Forward traverse the list by "visiting" the node and then recurse 11 | void forwardPrint( DLinkedList::Iterator i ) 12 | { 13 | // Implementations not using nullptr to detect the end of the list, such as circular linked lists or lists using sententials 14 | // (dummy nodes), will result in an either an infinite recursive loop or dereferencing invalid iterators, so let's avoid it. 15 | if( DLinkedList collection; collection.end() != nullptr ) return; 16 | 17 | if( i == nullptr ) return; 18 | 19 | std::cout << *i; 20 | forwardPrint( i.next() ); 21 | } 22 | 23 | 24 | 25 | 26 | // Backward traverse the list by recursing and then "visiting" the node 27 | void backwardPrint( DLinkedList::Iterator i ) 28 | { 29 | // Implementations not using nullptr to detect the end of the list, such as circular linked lists or lists using sententials 30 | // (dummy nodes), will result in an either an infinite recursive loop or dereferencing invalid iterators, so let's avoid it. 31 | if( DLinkedList collection; collection.end() != nullptr ) return; 32 | 33 | if( i == nullptr ) return; 34 | 35 | backwardPrint( i.next() ); 36 | std::cout << *i; 37 | } 38 | } // namespace 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | int main() 48 | { 49 | DLinkedList students; 50 | 51 | Student s; 52 | for( int i = 0; i < 5; i++ ) 53 | { 54 | s.name( "Student_" + std::to_string(i) ); 55 | s.semesters(2); 56 | students.prepend(s); 57 | } 58 | 59 | DLinkedList classRoster( students ); 60 | for( int i = 1; i <= 5; i++ ) 61 | { 62 | s.name( "Student_" + std::to_string(i * 10) ); 63 | s.semesters(2); 64 | classRoster.append(s); 65 | } 66 | 67 | students = classRoster; 68 | std::cout << "Front and back:\n" << classRoster.front() << classRoster.back() << "\n\n"; 69 | 70 | std::cout << "Range-based for loop traversal:\n"; 71 | for( const auto & student : students ) std::cout << student; //requires begin() and end() member functions 72 | std::cout << "\n\n"; 73 | 74 | std::cout << "backward iteration traversal:\n"; 75 | for( auto i = students.rbegin(); i != students.rend(); --i ) std::cout << *i; 76 | std::cout << "\n\n"; 77 | 78 | classRoster.insertBefore( classRoster.begin() + 2, Student( "Bob" ) ); 79 | 80 | std::cout << "Recursive forward traversal:\n"; 81 | forwardPrint( students.begin() ); 82 | std::cout << "\n\n"; 83 | 84 | 85 | std::cout << "Recursive backward traversal:\n"; 86 | backwardPrint( students.begin() ); 87 | std::cout << "\n\n"; 88 | 89 | 90 | std::cout << "Pop until empty traversal:\n"; 91 | while( !students.empty() ) 92 | { 93 | std::cout << students.back(); 94 | students.removeBack(); 95 | } 96 | } 97 | 98 | // For testing purposes, explicitly instantiate the class template. Template 99 | // class member functions are only instantiated, and thus semantically checked 100 | // by the compiler, when used. Explicitly instantiating the class forces all 101 | // the member functions to be instantiated, and thus semantically checked by the 102 | // compiler. It enables the compiler to find errors in your code. 103 | template class DLinkedList; 104 | -------------------------------------------------------------------------------- /SLinkedList_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "SLinkedList.hpp" 6 | #include "Student.hpp" 7 | 8 | 9 | 10 | 11 | namespace 12 | { 13 | // Forward traverse the list by "visiting" the node and then recurse 14 | void forwardPrint( SLinkedList::Iterator i ) 15 | { 16 | if( i == nullptr ) return; 17 | 18 | std::cout << *i; 19 | forwardPrint( i.next() ); 20 | } 21 | 22 | 23 | 24 | 25 | // Backward traverse the list by recursing and then "visiting" the node 26 | void backwardPrint( SLinkedList::Iterator i ) 27 | { 28 | if( i == nullptr ) return; 29 | 30 | backwardPrint( i.next() ); 31 | std::cout << *i; 32 | } 33 | } // namespace 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | int main() 43 | { 44 | SLinkedList students; 45 | 46 | Student s; 47 | for( int i = 0; i < 5; i++ ) 48 | { 49 | s.name( "Student_" + std::to_string(i) ); 50 | s.semesters(2); 51 | students.prepend(s); 52 | } 53 | 54 | SLinkedList classRoster( students ); 55 | for( int i = 1; i <= 5; i++ ) 56 | { 57 | s.name( "Student_" + std::to_string(i * 10) ); 58 | s.semesters(2); 59 | classRoster.append(s); 60 | } 61 | 62 | students = classRoster; 63 | std::cout << "Front and back:\n" << classRoster.front() << classRoster.back() << "\n\n"; 64 | 65 | std::cout << "Range-based for loop traversal:\n"; 66 | for( const auto & student : students ) std::cout << student; //requires SLinkedList::begin() and SLinkedList::end() member functions 67 | std::cout << "\n\n"; 68 | 69 | classRoster.insertAfter( classRoster.begin()+2, Student("Bob") ); 70 | 71 | std::cout << "Recursive forward traversal:\n"; 72 | // Printing the list does not alter the list 73 | forwardPrint( students.begin() ); 74 | std::cout << "\n\n"; 75 | 76 | 77 | std::cout << "Recursive backward traversal:\n"; 78 | // Printing the list does not alter the list 79 | backwardPrint( students.begin() ); 80 | std::cout << "\n\n"; 81 | 82 | // reverse the list 83 | // This changes the list 84 | students.reverse(); 85 | 86 | std::cout << "Recursive forward traversal (after reversal):\n"; 87 | // Printing the list does not alter the list 88 | forwardPrint( students.begin() ); 89 | std::cout << "\n\n"; 90 | 91 | 92 | std::cout << "Recursive backward traversal (after reversal):\n"; 93 | // Printing the list does not alter the list 94 | backwardPrint( students.begin() ); 95 | std::cout << "\n\n"; 96 | 97 | 98 | 99 | std::cout << "Pop until empty traversal:\n"; 100 | while( !students.empty() ) 101 | { 102 | std::cout << students.front(); 103 | students.removeFront(); 104 | } 105 | 106 | 107 | SLinkedList testScores; 108 | testScores.append( 89 ); 109 | testScores.append( 92 ); 110 | testScores.append( 75 ); 111 | testScores.append( 97 ); 112 | std::cout << "\nTest score average is: " << testScores.add() / testScores.size() << "%\n"; 113 | } 114 | 115 | // For testing purposes, explicitly instantiate the class template. Template 116 | // class member functions are only instantiated, and thus semantically checked 117 | // by the compiler, when used. Explicitly instantiating the class forces all 118 | // the member functions to be instantiated, and thus semantically checked by the 119 | // compiler. It enables the compiler to find errors in your code. 120 | // template class SLinkedList; 121 | -------------------------------------------------------------------------------- /Queue.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include // move() ? 3 | #include // size_t 4 | #include // out_of_range 5 | #include // to_string() 6 | 7 | #include "Queue.hpp" 8 | 9 | 10 | 11 | 12 | 13 | 14 | /******************************************************************************* 15 | ** Primary implementation 16 | *******************************************************************************/ 17 | template 18 | void Queue::push( const T & element ) 19 | { 20 | collection.prepend( element ); // insert at the front 21 | } 22 | 23 | 24 | 25 | template 26 | T Queue::pop() 27 | { 28 | // Note: zyBook returns the value popped, the C++ standard template library does not. 29 | // popping an element from an empty queue error handling is handled by the underlying container 30 | auto element = collection.back(); 31 | collection.removeBack(); 32 | return element; 33 | } 34 | 35 | 36 | 37 | template 38 | T & Queue::top() 39 | { 40 | // Note: viewing an element from an empty queue error handling is handled by the underlying container 41 | return collection.back(); 42 | } 43 | 44 | 45 | 46 | template 47 | bool Queue::empty() 48 | { 49 | return collection.empty(); 50 | } 51 | 52 | 53 | 54 | template 55 | std::size_t Queue::size() 56 | { 57 | return collection.size(); 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | /******************************************************************************* 66 | ** Partial Specialization for a fixed sized Queue over a fixed sized standard array 67 | *******************************************************************************/ 68 | template 69 | void Queue>::push( const T & element ) 70 | { 71 | if( _size >= CAPACITY ) throw std::out_of_range( "ERROR: Attempt to add to an already full queue of " + std::to_string( CAPACITY ) + " elements." ); 72 | 73 | collection[_rear] = element; 74 | 75 | _rear = ( _rear + 1 ) % CAPACITY; 76 | ++_size; 77 | } 78 | 79 | 80 | 81 | template 82 | T Queue>::pop() 83 | { 84 | if( empty() ) throw std::out_of_range( "ERROR: Attempt to remove an element from an empty queue" ); 85 | 86 | auto temp = std::move( collection[_front] ); 87 | 88 | _front = ( _front + 1 ) % CAPACITY; 89 | --_size; 90 | 91 | // Note, zyBook returns the value popped, the C++ standard template library does not. 92 | return temp; 93 | } 94 | 95 | 96 | 97 | template 98 | T & Queue>::top() 99 | { 100 | if( empty() ) throw std::out_of_range( "ERROR: Attempt to view an element from an empty queue" ); 101 | 102 | return collection[_front]; 103 | } 104 | 105 | 106 | 107 | template 108 | bool Queue>::empty() 109 | { 110 | return _size == 0; 111 | } 112 | 113 | 114 | 115 | template 116 | std::size_t Queue>::size() 117 | { 118 | return _size; 119 | } 120 | -------------------------------------------------------------------------------- /BinarySearchTree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************************************************************************* 5 | ** Binary Search Tree Abstract Data Type Definition (Duplicate keys allowed) 6 | *******************************************************************************/ 7 | template 8 | class BinarySearchTree { 9 | public: 10 | BinarySearchTree () = default; 11 | BinarySearchTree ( const BinarySearchTree & original ); // performs a deep copy 12 | BinarySearchTree & operator= ( BinarySearchTree rhs ); // performs a deep copy assignment NOTE: INTENTIONALLY PASSED BY VALUE (delegates to copy constructor) 13 | ~BinarySearchTree (); // performs a deep node destruction 14 | 15 | // Queries 16 | Value search ( const Key & key ) const; // zyBook 6.4, 6.10: Returns the value associated with the first node found matching given key. Throws invalid_argument if key not found 17 | void insert ( const Key & key, const Value & value ); // zyBook 6.5, 6.9, 6.10: Inserts a new node populated with key and value in a proper location obeying the BST ordering property. 18 | void remove ( const Key & key ); // zyBook 6.6, 6.9, 6.10: Removes the first-found matching node, restructuring the tree to preserve the BST ordering property. 19 | void printInorder() const; // zyBook 6.7: Prints the contents of the tree in ascending sorted order 20 | int getHeight () const; // zyBook 6.8: Returns the height of the tree, or -1 if tree is empty 21 | 22 | void clear(); // Returns the tree to an empty state releasing all nodes 23 | 24 | 25 | private: 26 | struct Node; 27 | Node * root_ = nullptr; 28 | 29 | // Helper functions 30 | void clear ( Node * node ); 31 | void insertIterative( Node * node ); // zyBook Figure 6.9.1: BSTInsert algorithm for BSTs with nodes containing parent pointers. 32 | void insertRecursive( Node * parent, Node * nodeToInsert ); // zyBook Figure 6.10.2: Recursive BST insertion and removal. 33 | void remove ( Node * node ); // zyBook Figure 6.9.3: BSTRemoveKey and BSTRemoveNode algorithms for BSTs with nodes containing parent pointers. (Figure 6.10.2 identical but passes parent instead of using parent pointer in Node) 34 | void printInorder ( Node * node ) const; // zyBook Figure 6.7.1: BST inorder traversal algorithm. 35 | int getHeight ( Node * node ) const; // zyBook Figure 6.8.3: BSTGetHeight algorithm. 36 | 37 | 38 | Node * makeCopy( Node * node ); // Copy constructor helper function 39 | 40 | Node * searchIterative( const Key & key ) const; // zyBook Figure 6.4.1: BST search algorithm. 41 | Node * searchRecursive( Node * node, const Key & key ) const; // zyBook Figure 6.10.1: BST recursive search algorithm. 42 | 43 | bool replaceChild( Node * parent, // zyBook Figure 6.9.2: BSTReplaceChild algorithm. 44 | Node * currentChild, 45 | Node * newChild ); 46 | }; 47 | 48 | 49 | 50 | 51 | 52 | 53 | // Include template function definitions 54 | #include "BinarySearchTree.hxx" 55 | -------------------------------------------------------------------------------- /Student.cpp: -------------------------------------------------------------------------------- 1 | #include // quoted() 2 | #include 3 | #include 4 | 5 | #include "Student.hpp" 6 | 7 | 8 | 9 | 10 | /****************************************************************************** 11 | ** Constructors, assignments, and destructor 12 | ******************************************************************************/ 13 | // Because at least one other constructor has been declared, the compiler won't 14 | // synthesize the default constructor, so define it explicitly 15 | Student::Student() = default; 16 | 17 | 18 | Student::Student( std::string name, unsigned nsem ) 19 | : _name( name ), _numOfSemesters( nsem ) // Example: constructor initialization list to initialize instance attributes 20 | {} 21 | 22 | 23 | 24 | 25 | /****************************************************************************** 26 | ** Queries 27 | ******************************************************************************/ 28 | 29 | 30 | 31 | 32 | /****************************************************************************** 33 | ** Mutators 34 | ******************************************************************************/ 35 | 36 | void Student::updateNSemesters() 37 | { _numOfSemesters++; } 38 | 39 | void Student::name( const std::string & name ) 40 | { _name = name; } 41 | 42 | void Student::semesters( unsigned semesters ) 43 | { _numOfSemesters = semesters; } 44 | 45 | 46 | 47 | /****************************************************************************** 48 | ** Logical Operators 49 | ** Notes: 50 | ** 1) These are intentionally non-member functions to supports symmetry 51 | ** (design decision) 52 | ** 2) short circuit boolean algebra intentionally used to define multiple 53 | ** column (i.e. attribute) sort order 54 | ** 3) implement only "equal to" and "less than" functions and define all the 55 | ** others in terms of these two 56 | ** 4) C++20 changes this technique with the introduction of the spaceship 57 | ** operator 58 | ******************************************************************************/ 59 | bool operator<( const Student & lhs, const Student & rhs ) 60 | { 61 | if ( auto result = lhs._name.compare(rhs._name); result != 0 ) return result < 0; 62 | else if( lhs._numOfSemesters != rhs._numOfSemesters ) return lhs._numOfSemesters < rhs._numOfSemesters; 63 | 64 | // At this point all attributes are equal, so the lhs cannot be less than the rhs 65 | return false; 66 | } 67 | 68 | bool operator==( const Student & lhs, const Student & rhs ) 69 | { 70 | return lhs._name == rhs._name 71 | && lhs._numOfSemesters == rhs._numOfSemesters; 72 | // alternative: 73 | // return !( lhs < rhs ) && !( rhs < lhs ); 74 | } 75 | 76 | bool operator!=( const Student & lhs, const Student & rhs ) { return !( lhs == rhs ); } 77 | bool operator<=( const Student & lhs, const Student & rhs ) { return !( rhs < lhs ); } 78 | bool operator> ( const Student & lhs, const Student & rhs ) { return ( rhs < lhs ); } 79 | bool operator>=( const Student & lhs, const Student & rhs ) { return !( lhs < rhs ); } 80 | 81 | 82 | 83 | 84 | /****************************************************************************** 85 | ** Insertion and Extraction Operators 86 | ** Notes: 87 | ** 1) These must be non-member functions 88 | ******************************************************************************/ 89 | std::ostream & operator<<( std::ostream & os, const Student & student ) 90 | { 91 | os << "Name: " << std::quoted( student._name ); 92 | os << ", No. of semesters= " << student._numOfSemesters << '\n'; 93 | 94 | return os; 95 | } 96 | 97 | std::istream & operator>>( std::istream & is, Student & student ) 98 | { 99 | is >> std::quoted( student._name ); 100 | is >> student._numOfSemesters; 101 | 102 | return is; 103 | } 104 | -------------------------------------------------------------------------------- /Queue_Stack_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "DLinkedList.hpp" 11 | #include "Queue.hpp" 12 | #include "Stack.hpp" 13 | #include "Student.hpp" 14 | 15 | 16 | 17 | // Queue calls the first element "front", stack calls it "top". Create a function to delegate to the right thing 18 | template 19 | auto top( Container & c ) 20 | { return c.top(); } 21 | 22 | template 23 | auto top( std::queue & c ) 24 | { return c.front(); } 25 | 26 | 27 | 28 | // A simple test driver to exercise the container. Sense stacks and queues have (nearly) the same interface, the same test driver 29 | // is used for both. The container tested is intentionally passed by value (makes a local copy). 30 | template 31 | void test( Container_Type myContainer ) 32 | { 33 | std::cout << "Using: " << typeid( myContainer ).name() << '\n' // come compilers generate more readable results with 34 | << "Function: " << __func__ << '\n'; // type_info::name(), others with __func__, so let's do them 35 | // both just so we cover our bases 36 | 37 | // A stack is an Abstract Data Type, usually implemented as a limited interlace over some other data structure 38 | // Things you can do to a stack: 39 | myContainer.push( {"Tom" } ); 40 | myContainer.push( {"Aaron" } ); 41 | myContainer.push( {"Brenda"} ); 42 | myContainer.pop(); 43 | myContainer.push( {"Katelyn"} ); 44 | 45 | 46 | // Display the contents. Stacks and queues do not allow traversal (you can't see anything but the top (stack and queue) and both 47 | // (queue only), so to display the contents we have to inspect each element at the top and then remove it until the container is 48 | // empty. 49 | while( !myContainer.empty() ) 50 | { 51 | std::cout << top( myContainer ); 52 | myContainer.pop(); 53 | } 54 | std::cout << '\n'; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | int main() 64 | { 65 | try 66 | { 67 | /////////////////// Stacks ////////////////////// 68 | // zyBook Stack Usage 69 | Stack myStack_1; // empty stack where stack is implemented over a singly linked list (the default) 70 | test( myStack_1 ); 71 | 72 | Stack> myStack_2; // empty stack where stack is implemented over a double linked list 73 | test( myStack_2 ); 74 | 75 | // array based Stack, not in zyBook 76 | Stack> myStack_3; // empty stack where stack is implemented over a fixed sized standard array 77 | test( myStack_3 ); 78 | 79 | // Standard Stack usage with standard containers 80 | std::stack myStack_4; // default standard stack (uses std::deque as underlying container) 81 | test( myStack_4 ); 82 | 83 | std::stack> myStack_5; // standard stack with standard doubly linked list as underlying container 84 | test( myStack_5 ); 85 | 86 | std::stack> myStack_6; // standard stack with standard vector as underlying container 87 | test( myStack_6 ); 88 | 89 | 90 | 91 | 92 | /////////////////// Queues ////////////////////// 93 | // zyBook Queue Usage 94 | Queue myQueue_1; // empty queue where queue is implemented over a doubly linked list (the default) 95 | test( myQueue_1 ); 96 | 97 | // array based Queue, not in zyBook 98 | Queue> myQueue_3; // empty queue where queue is implemented over a fixed sized standard array 99 | test( myQueue_3 ); 100 | 101 | // Standard Queue usage with standard containers 102 | std::queue myQueue_4; // default standard queue (uses std::deque as underlying container) 103 | test( myQueue_4 ); 104 | 105 | std::queue> myQueue_5; // standard queue with standard doubly linked list as underlying container 106 | test( myQueue_5 ); 107 | } 108 | 109 | catch (const std::exception & ex) 110 | { 111 | std::cerr << ex.what() << '\n'; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /SLinkedList.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /******************************************************************************* 4 | ** A singly linked list 5 | *******************************************************************************/ 6 | template 7 | class SLinkedList 8 | { 9 | public: 10 | class Iterator; // A forward iterator 11 | 12 | SLinkedList(); // empty list constructor 13 | SLinkedList ( const SLinkedList & original ); // copy constructor 14 | SLinkedList & operator=( const SLinkedList & rhs ); // copy assignment 15 | ~SLinkedList(); // destructor 16 | 17 | 18 | bool empty() const; // returns true if list has no items 19 | void clear(); // remove all elements setting size to zero 20 | size_t size() const; // returns the number of elements in the list 21 | 22 | 23 | Data_t & front(); // return list's front element 24 | void prepend( const Data_t & element ); // add element to front of list (aka push_front) 25 | void removeFront(); // remove element at front of list (aka pop_front) 26 | 27 | Data_t & back(); // return list's back element 28 | void append( const Data_t & element ); // add element to back of list (aka push_back) 29 | 30 | Iterator insertAfter( const Iterator & position, const Data_t & element ); // Inserts element into list after the one occupied at position 31 | Iterator removeAfter( const Iterator & position ); // Removes from list the element after the one occupied at position 32 | 33 | Iterator begin(); // Returns an Iterator to the list's front element, nullptr if list is empty 34 | Iterator end (); // Returns an Iterator beyond the list's back element. Do not dereference this Iterator 35 | 36 | 37 | 38 | /************************************************************************** 39 | ** Extended interface to demonstrate recursive functions 40 | **************************************************************************/ 41 | // Add a function, named "reverse", to the Singly Linked List that reverses the list without copying the list or moving data in 42 | // the list. Implement the function recursively. The function must be implemented recursively. 43 | // 44 | // Client visible interface (the public function) 45 | void reverse(); 46 | 47 | 48 | // Add a function, named "add" that returns the sum of all elements in the list. The function must be implemented recursively. 49 | // Assume operator+(lhs, rhs) is defined 50 | // 51 | // Client visible interface (the public function) 52 | Data_t add(); 53 | private: 54 | struct Node; 55 | 56 | Node * _head = nullptr; // head of the list 57 | Node * _tail = nullptr; // tail of the list 58 | size_t _size = 0; 59 | 60 | // Add a function, named "reverse" ... 61 | // The private helper function 62 | void reverse( Node * currentNode ); 63 | 64 | // Add a function, named "add" ... 65 | // The private helper function 66 | Data_t add( Node * currentNode ); 67 | }; 68 | 69 | 70 | 71 | 72 | /******************************************************************************* 73 | ** A singly linked list forward iterator 74 | *******************************************************************************/ 75 | template 76 | class SLinkedList::Iterator 77 | { 78 | friend class SLinkedList; 79 | 80 | public: 81 | // Compiler synthesized constructors and destructor are fine, just what we 82 | // want (shallow copies, no ownership) but needed to explicitly say that 83 | // because there is also a user defined constructor 84 | Iterator ( ) = default; 85 | Iterator ( const Iterator & ) = default; 86 | Iterator ( Iterator && ) = default; 87 | Iterator & operator=( const Iterator & ) = default; 88 | Iterator & operator=( Iterator && ) = default; 89 | ~Iterator ( ) = default; 90 | 91 | Iterator( Node * position ); // Implicit conversion constructor 92 | 93 | // Pre and post Increment operators move the position to the next node in the list 94 | Iterator & operator++(); // advance the iterator one node (pre -increment) 95 | Iterator operator++( int ); // advance the iterator one node (post-increment) 96 | 97 | Iterator next ( size_t delta = 1 ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 98 | Iterator operator+( size_t rhs ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 99 | 100 | // Dereferencing and member access operators provide access to data. The 101 | // iterator can be constant or non-constant, but the iterator, by 102 | // definition, points to a non-constant linked list. 103 | Data_t & operator* () const; 104 | Data_t * operator->() const; 105 | 106 | // Equality operators 107 | bool operator==( const Iterator & rhs ) const; 108 | bool operator!=( const Iterator & rhs ) const; 109 | 110 | private: 111 | Node * _node = nullptr; 112 | }; 113 | 114 | 115 | // Including template definitions here allows a consistent approach of separating interface (header file) from implementation (source file) 116 | #include "SLinkedList.hxx" 117 | -------------------------------------------------------------------------------- /DLinkedList.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /******************************************************************************* 4 | ** A doubly linked list 5 | *******************************************************************************/ 6 | template 7 | class DLinkedList 8 | { 9 | public: 10 | class Iterator; // A forward iterator 11 | 12 | DLinkedList(); // empty list constructor 13 | DLinkedList ( const DLinkedList & original ); // copy constructor 14 | DLinkedList & operator=( const DLinkedList & rhs ); // copy assignment 15 | ~DLinkedList(); // destructor 16 | 17 | 18 | bool empty() const; // returns true if list has no items 19 | void clear(); // remove all elements setting size to zero 20 | size_t size() const; // returns the number of elements in the list 21 | 22 | 23 | Data_t & front(); // return list's front element 24 | void prepend( const Data_t & element ); // add element to front of list (aka push_front) 25 | void removeFront(); // remove element at front of list (aka pop_front) 26 | 27 | Data_t & back(); // return list's back element 28 | void append( const Data_t & element ); // add element to back of list (aka push_back) 29 | void removeBack(); // remove element at back of list (aka pop_back) 30 | 31 | Iterator insertBefore( const Iterator & position, const Data_t & element ); // Inserts element into list before the one occupied at position 32 | Iterator remove ( const Iterator & position ); // Removes from list the element occupied at position 33 | 34 | Iterator begin() const; // Returns an Iterator to the list's front element, nullptr if list is empty 35 | Iterator end () const; // Returns an Iterator beyond the list's back element. Do not dereference this Iterator 36 | 37 | Iterator rbegin() const; // Returns an Iterator to the list's back element, nullptr if list is empty 38 | Iterator rend () const; // Returns an Iterator beyond the list's front element. Do not dereference this Iterator 39 | 40 | 41 | private: 42 | struct Node; 43 | 44 | Node * _head = nullptr; // head of the list 45 | Node * _tail = nullptr; // tail of the list 46 | size_t _size = 0; 47 | }; 48 | 49 | 50 | 51 | 52 | /******************************************************************************* 53 | ** A doubly linked list bidirectional iterator 54 | *******************************************************************************/ 55 | template 56 | class DLinkedList::Iterator 57 | { 58 | friend class DLinkedList; 59 | 60 | public: 61 | // Compiler synthesized constructors and destructor are fine, just what we 62 | // want (shallow copies, no ownership) but needed to explicitly say that 63 | // because there is also a user defined constructor 64 | Iterator ( ) = default; 65 | Iterator ( const Iterator & ) = default; 66 | Iterator ( Iterator && ) = default; 67 | Iterator & operator=( const Iterator & ) = default; 68 | Iterator & operator=( Iterator && ) = default; 69 | ~Iterator ( ) = default; 70 | 71 | Iterator( Node * position ); // Implicit conversion constructor 72 | 73 | // Pre and post Increment operators move the position to the next node in the list 74 | Iterator & operator++(); // advance the iterator one node (pre -increment) 75 | Iterator operator++( int ); // advance the iterator one node (post-increment) 76 | 77 | // Pre and post Increment operators move the position to the next node in the list 78 | Iterator & operator--(); // retreat the iterator one node (pre -decrement) 79 | Iterator operator--( int ); // retreat the iterator one node (post-decrement) 80 | 81 | Iterator next ( size_t delta = 1 ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 82 | Iterator operator+( size_t rhs ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 83 | 84 | Iterator prev ( size_t delta = 1 ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 85 | Iterator operator-( size_t rhs ) const; // Return an iterator delta nodes after this node (this iterator doesn't change) 86 | 87 | // Dereferencing and member access operators provide access to data. The 88 | // iterator can be constant or non-constant, but the iterator, by 89 | // definition, points to a non-constant linked list. 90 | Data_t & operator* () const; 91 | Data_t * operator->() const; 92 | 93 | // Equality operators 94 | bool operator==( const Iterator & rhs ) const; 95 | bool operator!=( const Iterator & rhs ) const; 96 | 97 | private: 98 | Node * _node = nullptr; 99 | }; 100 | 101 | 102 | // Including template definitions here allows a consistent approach of separating interface (header file) from implementation (source file) 103 | // 104 | // There are three implementations of this interface provided. One implements the doubly linked list with direct head and tail pointers, one with 105 | // dummy head and tail nodes, and the other is a circularly doubly linked list. The important thing to take away is the they all share the same 106 | // interface, and that the specific implementation can change without affecting consumers of the class. 107 | 108 | #include "DLinkedList.hxx" 109 | // #include "CircularDummyNodeDLinkedList.hxx" 110 | // #include "DummyNodeDLinkedList.hxx" 111 | -------------------------------------------------------------------------------- /FixedVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // move(), move_backward(), copy() 4 | #include // size_t 5 | #include // range_error 6 | 7 | 8 | 9 | 10 | // Template Class Definition 11 | template 12 | class FixedVector 13 | { 14 | public: 15 | // Constructors, destructor, and assignments 16 | FixedVector ( std::size_t capacity = 64 ); // power of 2, but nothing special about 2^6 17 | FixedVector ( const FixedVector & other ); // Copy constructor 18 | FixedVector & operator=( const FixedVector & rhs ); // Copy assignment 19 | ~FixedVector (); 20 | 21 | // Queries 22 | T & at ( std::size_t index ); // Checks bounds, throws std::range_error 23 | T & operator[]( std::size_t index ); // No bounds checking 24 | 25 | std::size_t size(); 26 | bool empty(); 27 | 28 | 29 | // Iterators 30 | T * begin(); 31 | T * end(); 32 | 33 | 34 | // Mutators 35 | void push_back( const T & value ); // Checks capacity, throws std::range_error 36 | 37 | std::size_t erase( std::size_t index ); // Checks bounds, throws std::range_error 38 | T * erase( T * position ); // Checks bounds, throws std::range_error 39 | 40 | void set( std::size_t index, const T & value ); // Checks bounds, throws std::range_error 41 | 42 | std::size_t insert( std::size_t beforeIndex, const T & value ); // Checks capacity, throws std::range_error 43 | T * insert( T * beforePosition, const T & value ); // Checks capacity, throws std::range_error 44 | 45 | void clear(); 46 | 47 | 48 | private: 49 | std::size_t _size = 0; // number of elements in the data structure 50 | std::size_t const _capacity = 0; // length of the array 51 | T * _array = nullptr; // pointer to dynamically allocated array 52 | }; 53 | 54 | 55 | 56 | 57 | 58 | 59 | // Implementation 60 | 61 | // Constructor with initial capacity argument 62 | template 63 | FixedVector::FixedVector( std::size_t capacity ) 64 | : _size( 0 ), _capacity( capacity ), _array( new T[capacity]() ) 65 | {} 66 | 67 | 68 | 69 | template 70 | std::size_t FixedVector::size() 71 | { return _size; } 72 | 73 | 74 | 75 | template 76 | bool FixedVector::empty() 77 | { return _size == 0; } 78 | 79 | 80 | 81 | template 82 | T * FixedVector::begin() 83 | { return _array; } 84 | 85 | 86 | 87 | template 88 | T * FixedVector::end() 89 | { return _array + _size; } // Note the pointer arithmetic used 90 | 91 | 92 | 93 | template 94 | void FixedVector::clear() 95 | { 96 | // destroy the elements held allowing resources to be released. But maintain "this" FixedVector's capacity 97 | while( _size != 0 ) _array[--_size].~T(); // Direct call to destructor 98 | } 99 | 100 | 101 | 102 | template 103 | T & FixedVector::at( std::size_t index ) 104 | { 105 | if( index >= _size ) throw std::range_error( "index out of bounds" ); 106 | 107 | return _array[ index ]; 108 | } 109 | 110 | 111 | 112 | template 113 | void FixedVector::push_back( const T & value ) 114 | { insert( _size, value ); } // delegate to insert() leveraging error checking 115 | 116 | 117 | 118 | // Overloaded Array-Access Operator 119 | template 120 | T & FixedVector::operator[]( std::size_t index ) 121 | { return _array[ index ]; } // Note: array bounds intentionally not checked 122 | 123 | 124 | 125 | template 126 | void FixedVector::set( std::size_t index, const T & value ) 127 | { at( index ) = value; } // delegate to at() leveraging error checking 128 | 129 | 130 | 131 | // Removes element from position. Elements from higher positions are shifted back to fill gap. 132 | // Vector size decrements 133 | template 134 | std::size_t FixedVector::erase( std::size_t index ) 135 | { 136 | if( index >= _size ) throw std::range_error( "index out of bounds" ); 137 | 138 | // shift elements to the left and decrement the number of elements in the container 139 | std::move( _array + index + 1, _array + _size, _array + index ); // Note the pointer arithmetic here 140 | --_size; 141 | 142 | return index; 143 | } 144 | 145 | 146 | 147 | template 148 | T * FixedVector::erase( T * position ) 149 | { 150 | // delegate to delete by index 151 | auto index = position - begin(); // Note the pointer arithmetic here 152 | erase( index ); 153 | return position; 154 | } 155 | 156 | 157 | 158 | // Copies x to element at position. Items at that position and higher are shifted over to make room. Vector size increments. 159 | template 160 | std::size_t FixedVector::insert( std::size_t beforeIndex, const T & value ) 161 | { 162 | if( _size >= _capacity ) throw std::range_error( "insufficient capacity to add another element" ); 163 | if( beforeIndex > _size ) beforeIndex = _size; // insert at the back 164 | 165 | // move elements to create space starting from the right and working left 166 | std::move_backward( _array + beforeIndex, _array + _size, _array + _size + 1 ); // Note the pointer arithmetic here 167 | 168 | _array[ beforeIndex ] = value; // make a copy of the element in the empty slot 169 | ++_size; 170 | 171 | return beforeIndex; 172 | } 173 | 174 | 175 | 176 | template 177 | T* FixedVector::insert( T * beforePosition, const T & value ) 178 | { 179 | auto index = beforePosition - begin(); // Note the pointer arithmetic here 180 | insert( index, value ); 181 | return beforePosition; 182 | } 183 | 184 | 185 | 186 | // Copy Constructor 187 | template 188 | FixedVector::FixedVector( const FixedVector & other ) 189 | : _size( other._size ), _capacity( other._capacity ), _array( new T[other._capacity]() ) 190 | { 191 | // Copy each element from the other vector to this vector 192 | std::copy_n( other._array, other._size, _array ); 193 | } 194 | 195 | 196 | 197 | // Overloaded Assignment Operator 198 | template 199 | FixedVector & FixedVector::operator=( const FixedVector & rhs ) 200 | { 201 | if( this != &rhs ) 202 | { 203 | // Being fixed size, the already allocated array can be reused. Capacity is not adjusted. If _capacity < rhs._capacity, then some 204 | // rhs elements may not be copied and the vector is truncated. 205 | std::copy( rhs._array, rhs._array + rhs._size, _array ); 206 | _size = rhs._size; 207 | } 208 | 209 | return *this; 210 | } 211 | 212 | 213 | 214 | // Destructor 215 | template 216 | FixedVector::~FixedVector() 217 | { delete[] _array; } 218 | -------------------------------------------------------------------------------- /ExtendableVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // move(), move_backward(), copy() 4 | #include // size_t 5 | #include // range_error 6 | 7 | 8 | 9 | 10 | // Template Class Definition 11 | template 12 | class ExtendableVector 13 | { 14 | public: 15 | // Constructors, destructor, and assignments 16 | ExtendableVector ( std::size_t capacity = 64 ); // power of 2, but nothing special about 2^6 17 | ExtendableVector ( const ExtendableVector & other ); // Copy constructor 18 | ExtendableVector & operator=( const ExtendableVector & rhs ); // Copy assignment 19 | ~ExtendableVector (); 20 | 21 | // Queries 22 | T & at ( std::size_t index ); // Checks bounds, throws std::range_error 23 | T & operator[]( std::size_t index ); // No bonds checking 24 | 25 | std::size_t size(); 26 | bool empty(); 27 | 28 | 29 | // Iterators 30 | T * begin(); 31 | T * end(); 32 | 33 | 34 | // Mutators 35 | void push_back( const T & value ); // Checks capacity, throws std::range_error 36 | 37 | std::size_t erase( std::size_t index ); // Checks bounds, throws std::range_error 38 | T * erase( T * position ); // Checks bounds, throws std::range_error 39 | 40 | void set( std::size_t index, const T & value ); // Checks bounds, throws std::range_error 41 | 42 | std::size_t insert( std::size_t beforeIndex, const T & value ); // Checks capacity, throws std::range_error 43 | T * insert( T * beforePosition, const T & value ); // Checks capacity, throws std::range_error 44 | 45 | void clear(); 46 | 47 | 48 | private: 49 | std::size_t _size = 0; // number of elements in the data structure 50 | std::size_t _capacity = 0; // length of the array 51 | T * _array = nullptr; // pointer to dynamically allocated array 52 | 53 | void reserve( size_t newCapacity ); // helper function to change capacity 54 | }; 55 | 56 | 57 | 58 | 59 | 60 | 61 | // Implementation 62 | 63 | // Constructor with initial capacity argument 64 | template 65 | ExtendableVector::ExtendableVector( std::size_t capacity ) 66 | : _size( 0 ), _capacity( capacity ), _array( new T[capacity]() ) 67 | {} 68 | 69 | 70 | 71 | template 72 | std::size_t ExtendableVector::size() 73 | { return _size; } 74 | 75 | 76 | 77 | template 78 | bool ExtendableVector::empty() 79 | { return _size == 0; } 80 | 81 | 82 | 83 | template 84 | T * ExtendableVector::begin() 85 | { return _array; } 86 | 87 | 88 | 89 | template 90 | T * ExtendableVector::end() 91 | { return _array + _size; } // Note the pointer arithmetic used 92 | 93 | 94 | 95 | template 96 | void ExtendableVector::clear() 97 | { 98 | // destroy the elements held allowing resources to be released. But maintain "this" ExtendableVector's capacity 99 | while( _size != 0 ) _array[--_size].~T(); // Direct call to destructor 100 | } 101 | 102 | 103 | 104 | template 105 | T & ExtendableVector::at( std::size_t index ) 106 | { 107 | if( index >= _size ) throw std::range_error( "index out of bounds" ); 108 | 109 | return _array[ index ]; 110 | } 111 | 112 | 113 | 114 | template 115 | void ExtendableVector::push_back( const T & value ) 116 | { insert( _size, value ); } // delegate to insert() leveraging error checking 117 | 118 | 119 | 120 | // Overloaded Array-Access Operator 121 | template 122 | T & ExtendableVector::operator[]( std::size_t index ) 123 | { return _array[ index ]; } // Note: array bounds intentionally not checked 124 | 125 | 126 | 127 | template 128 | void ExtendableVector::set( std::size_t index, const T & value ) 129 | { at( index ) = value; } // delegate to at() leveraging error checking 130 | 131 | 132 | 133 | // Removes element from position. Elements from higher positions are shifted back to fill gap. 134 | // Vector size decrements 135 | template 136 | std::size_t ExtendableVector::erase( std::size_t index ) 137 | { 138 | if( index >= _size ) throw std::range_error( "index out of bounds" ); 139 | 140 | // shift elements to the left and decrement the number of elements in the container 141 | std::move( _array + index + 1, _array + _size, _array + index ); // Note the pointer arithmetic here 142 | --_size; 143 | 144 | return index; 145 | } 146 | 147 | 148 | 149 | template 150 | T * ExtendableVector::erase( T * position ) 151 | { 152 | // delegate to delete by index 153 | auto index = position - begin(); // Note the pointer arithmetic here 154 | erase( index ); 155 | return position; 156 | } 157 | 158 | 159 | 160 | // Copies x to element at position. Items at that position and higher are shifted over to make room. Vector size increments. 161 | template 162 | std::size_t ExtendableVector::insert( std::size_t beforeIndex, const T & value ) 163 | { 164 | if( _size >= _capacity ) reserve( 2 * _capacity ); // If at max capacity, double the capacity 165 | if( beforeIndex > _size ) beforeIndex = _size; // insert at the back 166 | 167 | // move elements to create space starting from the right and working left 168 | std::move_backward( _array + beforeIndex, _array + _size, _array + _size + 1 ); // Note the pointer arithmetic here 169 | 170 | _array[ beforeIndex ] = value; // make a copy of the element in the empty slot 171 | ++_size; 172 | 173 | return beforeIndex; 174 | } 175 | 176 | 177 | 178 | template 179 | T* ExtendableVector::insert( T * beforePosition, const T & value ) 180 | { 181 | auto index = beforePosition - begin(); // Note the pointer arithmetic here 182 | insert( index, value ); 183 | return beforePosition; 184 | } 185 | 186 | 187 | 188 | template 189 | void ExtendableVector::reserve( std::size_t newCapacity ) 190 | { 191 | if( newCapacity > _capacity ) 192 | { 193 | T * newArray = new T[newCapacity](); 194 | // Move values to new array 195 | std::move( _array, _array + _size, newArray ); 196 | 197 | delete[] _array; 198 | _array = newArray; 199 | _capacity = newCapacity; 200 | } 201 | } 202 | 203 | 204 | 205 | // Copy Constructor 206 | template 207 | ExtendableVector::ExtendableVector( const ExtendableVector & other ) 208 | : _size( other._size ), _capacity( other._capacity ), _array( new T[other._capacity]() ) 209 | { 210 | // Copy each element from the other vector to this vector 211 | std::copy_n( other._array, other._size, _array ); 212 | } 213 | 214 | 215 | 216 | // Overloaded Assignment Operator 217 | template 218 | ExtendableVector & ExtendableVector::operator=( const ExtendableVector & rhs ) 219 | { 220 | if( this != &rhs ) 221 | { 222 | // Can the stuff in the right hand side (rhs) fit into this vector? If not, expand this vector's capacity 223 | reserve( rhs._size ); 224 | 225 | std::copy( rhs._array, rhs._array + rhs._size, _array ); 226 | _size = rhs._size; 227 | } 228 | 229 | return *this; 230 | } 231 | 232 | 233 | 234 | // Destructor 235 | template 236 | ExtendableVector::~ExtendableVector() 237 | { delete[] _array; } 238 | -------------------------------------------------------------------------------- /CircularDummyNodeDLinkedList.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // length_error, invalid_argument 4 | 5 | #include "DLinkedList.hpp" 6 | 7 | 8 | 9 | 10 | /******************************************************************************* 11 | ** A doubly linked list's node 12 | *******************************************************************************/ 13 | template 14 | struct DLinkedList::Node 15 | { 16 | Node( const Data_t & element ) : _data( element ) {} 17 | 18 | Data_t _data; // linked list element value 19 | Node * _next = this; // next item in the list 20 | Node * _prev = this; // previous item in the list 21 | }; 22 | 23 | 24 | 25 | 26 | 27 | 28 | /******************************************************************************* 29 | ** DLinkedList with two dummy nodes function definitions 30 | *******************************************************************************/ 31 | // empty list constructor 32 | template 33 | DLinkedList::DLinkedList() 34 | : _head( new Node( Data_t() ) ) // _tail not used 35 | {} 36 | 37 | 38 | 39 | // copy constructor 40 | template 41 | DLinkedList::DLinkedList( const DLinkedList & original ) 42 | : DLinkedList() 43 | { 44 | // Walk the original list adding copies of the elements to this (initially empty) list maintaining order 45 | for( const auto & element : original ) append( element ); 46 | } 47 | 48 | 49 | 50 | // copy assignment 51 | template 52 | DLinkedList & DLinkedList::operator=( const DLinkedList & rhs ) 53 | { 54 | if( this != &rhs ) // avoid self assignment 55 | { 56 | // Release the contents of this list first 57 | clear(); // An optimization might be possible by reusing already allocated nodes 58 | 59 | // Walk the right hand side list adding copies of the elements to this list maintaining order 60 | for( const auto & element : rhs ) append( element ); 61 | } 62 | return *this; 63 | } 64 | 65 | 66 | 67 | // destructor 68 | template 69 | DLinkedList::~DLinkedList() 70 | { 71 | clear(); 72 | delete _head; 73 | } 74 | 75 | 76 | 77 | template 78 | bool DLinkedList::empty() const 79 | { 80 | return _head->_next == _head; 81 | // can also use (_size == 0) or (_head->_prev == _head); 82 | } 83 | 84 | 85 | 86 | template 87 | void DLinkedList::clear() 88 | { 89 | while( !empty() ) removeFront(); 90 | } 91 | 92 | 93 | 94 | template 95 | size_t DLinkedList::size() const 96 | { 97 | return _size; 98 | } 99 | 100 | 101 | 102 | template 103 | Data_t & DLinkedList::front() 104 | { 105 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 106 | return _head->_next->_data; 107 | } 108 | 109 | 110 | 111 | template 112 | void DLinkedList::prepend( const Data_t & element ) 113 | { 114 | insertBefore( _head->_next, element ); 115 | } 116 | 117 | 118 | 119 | template 120 | void DLinkedList::removeFront() 121 | { 122 | remove( _head->_next ); 123 | } 124 | 125 | 126 | 127 | template 128 | Data_t & DLinkedList::back() 129 | { 130 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 131 | return _head->_prev->_data; 132 | } 133 | 134 | 135 | 136 | template 137 | void DLinkedList::append( const Data_t & element ) 138 | { 139 | insertBefore( _head, element ); 140 | } 141 | 142 | 143 | 144 | template 145 | void DLinkedList::removeBack() 146 | { 147 | remove( _head->_prev ); 148 | } 149 | 150 | 151 | 152 | template 153 | typename DLinkedList::Iterator DLinkedList::insertBefore( const Iterator & current, const Data_t & element ) 154 | { 155 | Node * newNode = new Node( element ); // create new node 156 | 157 | newNode->_next = current._node; 158 | newNode->_prev = current._node->_prev; 159 | 160 | current._node->_prev->_next = newNode; 161 | current._node->_prev = newNode; 162 | 163 | ++_size; 164 | return newNode; 165 | } 166 | 167 | 168 | 169 | template 170 | typename DLinkedList::Iterator DLinkedList::remove( const Iterator & current ) 171 | { 172 | if( empty() ) throw std::length_error( "attempt to remove from an empty list" ); 173 | if( current == _head ) throw std::invalid_argument( "Attempt to remove at an invalid location" ); 174 | 175 | current._node->_next->_prev = current._node->_prev; 176 | current._node->_prev->_next = current._node->_next; 177 | 178 | --_size; 179 | 180 | Iterator returnNode( current._node->_next ); // return the node after the one removed 181 | delete current._node; // delete what used to be the old node 182 | return returnNode; 183 | } 184 | 185 | 186 | 187 | template 188 | typename DLinkedList::Iterator DLinkedList::begin() const 189 | { 190 | return Iterator( _head->_next ); 191 | } 192 | 193 | 194 | 195 | template 196 | typename DLinkedList::Iterator DLinkedList::end() const 197 | { 198 | return Iterator( _head ); 199 | } 200 | 201 | 202 | 203 | template 204 | typename DLinkedList::Iterator DLinkedList::rbegin() const 205 | { 206 | return Iterator( _head->_prev ); 207 | } 208 | 209 | 210 | 211 | template 212 | typename DLinkedList::Iterator DLinkedList::rend() const 213 | { 214 | return Iterator( _head ); 215 | } 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | /******************************************************************************* 228 | ** DLinkedList::Iterator Function definitions 229 | *******************************************************************************/ 230 | template 231 | DLinkedList::Iterator::Iterator( Node * p ) 232 | : _node( p ) 233 | {} 234 | 235 | 236 | 237 | template 238 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator++() // pre-increment 239 | { 240 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to increment null Iterator" ); 241 | _node = _node->_next; 242 | return *this; 243 | } 244 | 245 | 246 | 247 | template 248 | typename DLinkedList::Iterator DLinkedList::Iterator::operator++( int ) // post-increment 249 | { 250 | Iterator temp( *this ); 251 | operator++(); // Delegate to pre-increment leveraging error checking 252 | return temp; 253 | } 254 | 255 | 256 | 257 | template 258 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator--() // pre -decrement 259 | { 260 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to decrement null Iterator" ); 261 | _node = _node->_prev; 262 | return *this; 263 | } 264 | 265 | 266 | 267 | template 268 | typename DLinkedList::Iterator DLinkedList::Iterator::operator--( int ) // post-decrement 269 | { 270 | Iterator temp( *this ); 271 | operator--(); // Delegate to pre-decrement leveraging error checking 272 | return temp; 273 | } 274 | 275 | 276 | 277 | template 278 | typename DLinkedList::Iterator DLinkedList::Iterator::next( size_t delta ) const 279 | { 280 | // advance a copy of this iterator delta times 281 | Iterator p( *this ); 282 | for( ; delta > 0 && p != nullptr; --delta, ++p ); /* intentionally empty body */ 283 | 284 | return p; 285 | } 286 | 287 | 288 | 289 | template 290 | typename DLinkedList::Iterator DLinkedList::Iterator::operator+( size_t rhs ) const 291 | { 292 | return next( rhs ); 293 | } 294 | 295 | 296 | 297 | template 298 | typename DLinkedList::Iterator DLinkedList::Iterator::prev( size_t delta ) const 299 | { 300 | // retreat a copy of this iterator delta times 301 | Iterator p( *this ); 302 | for( ; delta > 0 && p != nullptr; --delta, --p ); /* intentionally empty body */ 303 | 304 | return p; 305 | } 306 | 307 | 308 | template 309 | typename DLinkedList::Iterator DLinkedList::Iterator::operator-( size_t rhs ) const 310 | { 311 | return prev( rhs ); 312 | } 313 | 314 | 315 | 316 | template 317 | Data_t & DLinkedList::Iterator::operator*() const 318 | { 319 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 320 | return _node->_data; 321 | } 322 | 323 | 324 | 325 | template 326 | Data_t * DLinkedList::Iterator::operator->() const 327 | { 328 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 329 | return &( _node->_data ); 330 | } 331 | 332 | 333 | 334 | template 335 | bool DLinkedList::Iterator::operator==( const Iterator & rhs ) const 336 | { 337 | return _node == rhs._node; 338 | } 339 | 340 | 341 | 342 | template 343 | bool DLinkedList::Iterator::operator!=( const Iterator & rhs ) const 344 | { 345 | return !( *this == rhs ); 346 | } 347 | -------------------------------------------------------------------------------- /DummyNodeDLinkedList.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // length_error, invalid_argument 4 | 5 | #include "DLinkedList.hpp" 6 | 7 | 8 | 9 | 10 | /******************************************************************************* 11 | ** A doubly linked list's node 12 | *******************************************************************************/ 13 | template 14 | struct DLinkedList::Node 15 | { 16 | Node( const Data_t & element ) : _data( element ) {} 17 | 18 | Data_t _data; // linked list element value 19 | Node * _next = nullptr; // next item in the list 20 | Node * _prev = nullptr; // previous item in the list 21 | }; 22 | 23 | 24 | 25 | 26 | 27 | 28 | /******************************************************************************* 29 | ** DLinkedList with two dummy nodes function definitions 30 | *******************************************************************************/ 31 | // empty list constructor 32 | template 33 | DLinkedList::DLinkedList() 34 | : _head( new Node( Data_t() ) ), _tail( new Node( Data_t() ) ) 35 | { 36 | // have them point to each other 37 | _head->_next = _tail; 38 | _tail->_prev = _head; 39 | } 40 | 41 | 42 | 43 | // copy constructor 44 | template 45 | DLinkedList::DLinkedList( const DLinkedList & original ) 46 | : DLinkedList() 47 | { 48 | // Walk the original list adding copies of the elements to this (initially empty) list maintaining order 49 | for( const auto & element : original ) append( element ); 50 | } 51 | 52 | 53 | 54 | // copy assignment 55 | template 56 | DLinkedList & DLinkedList::operator=( const DLinkedList & rhs ) 57 | { 58 | if( this != &rhs ) // avoid self assignment 59 | { 60 | // Release the contents of this list first 61 | clear(); // An optimization might be possible by reusing already allocated nodes 62 | 63 | // Walk the right hand side list adding copies of the elements to this list maintaining order 64 | for( const auto & element : rhs ) append( element ); 65 | } 66 | return *this; 67 | } 68 | 69 | 70 | 71 | // destructor 72 | template 73 | DLinkedList::~DLinkedList() 74 | { 75 | clear(); 76 | delete _head; 77 | delete _tail; 78 | } 79 | 80 | 81 | 82 | template 83 | bool DLinkedList::empty() const 84 | { 85 | return _head->_next == _tail; 86 | // can also use return (_size == 0); 87 | } 88 | 89 | 90 | 91 | template 92 | void DLinkedList::clear() 93 | { 94 | while( !empty() ) removeFront(); 95 | } 96 | 97 | 98 | 99 | template 100 | size_t DLinkedList::size() const 101 | { 102 | return _size; 103 | } 104 | 105 | 106 | 107 | template 108 | Data_t & DLinkedList::front() 109 | { 110 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 111 | return _head->_next->_data; 112 | } 113 | 114 | 115 | 116 | template 117 | void DLinkedList::prepend( const Data_t & element ) 118 | { 119 | insertBefore( _head->_next, element ); 120 | } 121 | 122 | 123 | 124 | template 125 | void DLinkedList::removeFront() 126 | { 127 | remove( _head->_next ); 128 | } 129 | 130 | 131 | 132 | template 133 | Data_t & DLinkedList::back() 134 | { 135 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 136 | return _tail->_prev->_data; 137 | } 138 | 139 | 140 | 141 | template 142 | void DLinkedList::append( const Data_t & element ) 143 | { 144 | insertBefore( _tail, element ); 145 | } 146 | 147 | 148 | 149 | template 150 | void DLinkedList::removeBack() 151 | { 152 | remove( _tail->_prev ); 153 | } 154 | 155 | 156 | 157 | template 158 | typename DLinkedList::Iterator DLinkedList::insertBefore( const Iterator & current, const Data_t & element ) 159 | { 160 | if( current == _head ) throw std::invalid_argument( "Attempt to insert before an invalid location" ); 161 | 162 | Node * newNode = new Node( element ); // create new node 163 | 164 | newNode->_next = current._node; 165 | newNode->_prev = current._node->_prev; 166 | 167 | current._node->_prev->_next = newNode; 168 | current._node->_prev = newNode; 169 | 170 | ++_size; 171 | return newNode; 172 | } 173 | 174 | 175 | 176 | template 177 | typename DLinkedList::Iterator DLinkedList::remove( const Iterator & current ) 178 | { 179 | if( empty() ) throw std::length_error ( "attempt to remove from an empty list" ); 180 | if( current == _head || current == _tail ) throw std::invalid_argument( "Attempt to remove at an invalid location" ); 181 | 182 | current._node->_next->_prev = current._node->_prev; 183 | current._node->_prev->_next = current._node->_next; 184 | 185 | --_size; 186 | 187 | Iterator returnNode( current._node->_next ); // return the node after the one removed 188 | delete current._node; // delete what used to be the old node 189 | return returnNode; 190 | } 191 | 192 | 193 | 194 | template 195 | typename DLinkedList::Iterator DLinkedList::begin() const 196 | { 197 | return Iterator( _head->_next ); 198 | } 199 | 200 | 201 | 202 | template 203 | typename DLinkedList::Iterator DLinkedList::end() const 204 | { 205 | return Iterator(_tail); 206 | } 207 | 208 | 209 | 210 | template 211 | typename DLinkedList::Iterator DLinkedList::rbegin() const 212 | { 213 | return Iterator( _tail->_prev ); 214 | } 215 | 216 | 217 | 218 | template 219 | typename DLinkedList::Iterator DLinkedList::rend() const 220 | { 221 | return Iterator(_head); 222 | } 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | /******************************************************************************* 235 | ** DLinkedList::Iterator Function definitions 236 | *******************************************************************************/ 237 | template 238 | DLinkedList::Iterator::Iterator( Node * p ) 239 | : _node( p ) 240 | {} 241 | 242 | 243 | 244 | template 245 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator++() // pre-increment 246 | { 247 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to increment null Iterator" ); 248 | _node = _node->_next; 249 | return *this; 250 | } 251 | 252 | 253 | 254 | template 255 | typename DLinkedList::Iterator DLinkedList::Iterator::operator++( int ) // post-increment 256 | { 257 | Iterator temp( *this ); 258 | operator++(); // Delegate to pre-increment leveraging error checking 259 | return temp; 260 | } 261 | 262 | 263 | 264 | template 265 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator--() // pre -decrement 266 | { 267 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to decrement null Iterator" ); 268 | _node = _node->_prev; 269 | return *this; 270 | } 271 | 272 | 273 | 274 | template 275 | typename DLinkedList::Iterator DLinkedList::Iterator::operator--( int ) // post-decrement 276 | { 277 | Iterator temp( *this ); 278 | operator--(); // Delegate to pre-decrement leveraging error checking 279 | return temp; 280 | } 281 | 282 | 283 | 284 | template 285 | typename DLinkedList::Iterator DLinkedList::Iterator::next( size_t delta ) const 286 | { 287 | // advance a copy of this iterator delta times 288 | Iterator p( *this ); 289 | for( ; delta > 0 && p != nullptr; --delta, ++p ); /* intentionally empty body */ 290 | 291 | return p; 292 | } 293 | 294 | 295 | 296 | template 297 | typename DLinkedList::Iterator DLinkedList::Iterator::operator+( size_t rhs ) const 298 | { 299 | return next( rhs ); 300 | } 301 | 302 | 303 | 304 | template 305 | typename DLinkedList::Iterator DLinkedList::Iterator::prev( size_t delta ) const 306 | { 307 | // retreat a copy of this iterator delta times 308 | Iterator p( *this ); 309 | for( ; delta > 0 && p != nullptr; --delta, --p ); /* intentionally empty body */ 310 | 311 | return p; 312 | } 313 | 314 | 315 | template 316 | typename DLinkedList::Iterator DLinkedList::Iterator::operator-( size_t rhs ) const 317 | { 318 | return prev( rhs ); 319 | } 320 | 321 | 322 | 323 | template 324 | Data_t & DLinkedList::Iterator::operator*() const 325 | { 326 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 327 | return _node->_data; 328 | } 329 | 330 | 331 | 332 | template 333 | Data_t * DLinkedList::Iterator::operator->() const 334 | { 335 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 336 | return &( _node->_data ); 337 | } 338 | 339 | 340 | 341 | template 342 | bool DLinkedList::Iterator::operator==( const Iterator & rhs ) const 343 | { 344 | return _node == rhs._node; 345 | } 346 | 347 | 348 | 349 | template 350 | bool DLinkedList::Iterator::operator!=( const Iterator & rhs ) const 351 | { 352 | return !( *this == rhs ); 353 | } 354 | -------------------------------------------------------------------------------- /DLinkedList.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // length_error, invalid_argument 4 | 5 | #include "DLinkedList.hpp" 6 | 7 | 8 | 9 | 10 | /******************************************************************************* 11 | ** A doubly linked list's node 12 | *******************************************************************************/ 13 | template 14 | struct DLinkedList::Node 15 | { 16 | Node( const Data_t & element ) : _data( element ) {} 17 | 18 | Data_t _data; // linked list element value 19 | Node * _next = nullptr; // next item in the list 20 | Node * _prev = nullptr; // previous item in the list 21 | }; 22 | 23 | 24 | 25 | 26 | 27 | 28 | /******************************************************************************* 29 | ** DLinkedList Function definitions 30 | *******************************************************************************/ 31 | // empty list constructor 32 | template 33 | DLinkedList::DLinkedList() = default; 34 | 35 | 36 | 37 | // copy constructor 38 | template 39 | DLinkedList::DLinkedList( const DLinkedList & original ) 40 | { 41 | // Walk the original list adding copies of the elements to this (initially empty) list maintaining order 42 | for( const auto & element : original ) append( element ); 43 | } 44 | 45 | 46 | 47 | // copy assignment 48 | template 49 | DLinkedList & DLinkedList::operator=( const DLinkedList & rhs ) 50 | { 51 | if( this != &rhs ) // avoid self assignment 52 | { 53 | // Release the contents of this list first 54 | clear(); // An optimization might be possible by reusing already allocated nodes 55 | 56 | // Walk the right hand side list adding copies of the elements to this list maintaining order 57 | for( const auto & element : rhs ) append( element ); 58 | } 59 | return *this; 60 | } 61 | 62 | 63 | 64 | // destructor 65 | template 66 | DLinkedList::~DLinkedList() 67 | { clear(); } 68 | 69 | 70 | 71 | template 72 | bool DLinkedList::empty() const 73 | { return _head == nullptr; } // can also use return (_size == 0); 74 | 75 | 76 | 77 | template 78 | void DLinkedList::clear() 79 | { while( !empty() ) removeFront(); } 80 | 81 | 82 | 83 | template 84 | size_t DLinkedList::size() const 85 | { return _size; } 86 | 87 | 88 | 89 | template 90 | Data_t& DLinkedList::front() 91 | { 92 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 93 | return _head->_data; 94 | } 95 | 96 | 97 | 98 | template 99 | void DLinkedList::prepend( const Data_t& element ) 100 | { insertBefore( _head, element ); } 101 | 102 | 103 | 104 | template 105 | void DLinkedList::removeFront() 106 | { remove( _head ); } 107 | 108 | 109 | 110 | template 111 | Data_t& DLinkedList::back() 112 | { 113 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 114 | return _tail->_data; 115 | } 116 | 117 | 118 | 119 | template 120 | void DLinkedList::append( const Data_t & element ) 121 | { insertBefore( end(), element ); } 122 | 123 | 124 | 125 | template 126 | void DLinkedList::removeBack() 127 | { remove( _tail ); } 128 | 129 | 130 | 131 | template 132 | typename DLinkedList::Iterator DLinkedList::insertBefore( const Iterator & current, const Data_t & element ) 133 | { 134 | Node * newNode = new Node( element ); // create new node 135 | 136 | if ( empty() ) _head = _tail = newNode; 137 | 138 | else if( current == _head ) // Insert at front of list 139 | { 140 | newNode->_next = _head; 141 | _head->_prev = newNode; 142 | _head = newNode; 143 | } 144 | 145 | else if( current == nullptr ) // Insert at back of list 146 | { 147 | _tail->_next = newNode; 148 | newNode->_prev = _tail; 149 | _tail = newNode; 150 | } 151 | 152 | else // Insert in the middle of the list 153 | { 154 | newNode->_next = current._node; 155 | newNode->_prev = current._node->_prev; 156 | 157 | current._node->_prev->_next = newNode; 158 | current._node->_prev = newNode; 159 | } 160 | 161 | ++_size; 162 | return newNode; 163 | } 164 | 165 | 166 | 167 | template 168 | typename DLinkedList::Iterator DLinkedList::remove( const Iterator & current ) 169 | { 170 | if( empty() ) throw std::length_error( "attempt to remove from an empty list" ); 171 | if( current == nullptr ) throw std::length_error( "attempt to remove from past the end of list" ); 172 | 173 | 174 | if( current._node == _head ) 175 | { 176 | if( current._node == _tail ) //current.node is both the head and the tail 177 | { 178 | _head = _tail = nullptr; 179 | } 180 | else // current.node is the head but not the tail 181 | { 182 | _head = _head->_next; 183 | _head->_prev = nullptr; 184 | 185 | } 186 | } 187 | 188 | else if( current._node == _tail ) // current.node is the tail but not the head 189 | { 190 | _tail = _tail->_prev; 191 | _tail->_next = nullptr; 192 | } 193 | 194 | else // current.node is between the head and the tail 195 | { 196 | current._node->_next->_prev = current._node->_prev; 197 | current._node->_prev->_next = current._node->_next; 198 | } 199 | 200 | --_size; 201 | 202 | Iterator returnNode( current._node->_next ); // return the node after the one removed 203 | delete current._node; // delete what used to be the old node 204 | return returnNode; 205 | } 206 | 207 | 208 | 209 | template 210 | typename DLinkedList::Iterator DLinkedList::begin() const 211 | { return Iterator(_head); } 212 | 213 | 214 | 215 | template 216 | typename DLinkedList::Iterator DLinkedList::end() const 217 | { return nullptr; } 218 | 219 | 220 | 221 | template 222 | typename DLinkedList::Iterator DLinkedList::rbegin() const 223 | { return Iterator( _tail ); } 224 | 225 | 226 | 227 | template 228 | typename DLinkedList::Iterator DLinkedList::rend() const 229 | { return nullptr; } 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | /******************************************************************************* 242 | ** DLinkedList::Iterator Function definitions 243 | *******************************************************************************/ 244 | template 245 | DLinkedList::Iterator::Iterator( Node * p ) 246 | : _node(p) 247 | {} 248 | 249 | 250 | 251 | template 252 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator++() // pre-increment 253 | { 254 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to increment null Iterator" ); 255 | _node = _node->_next; 256 | return *this; 257 | } 258 | 259 | 260 | 261 | template 262 | typename DLinkedList::Iterator DLinkedList::Iterator::operator++( int ) // post-increment 263 | { 264 | Iterator temp( *this ); 265 | operator++(); // Delegate to pre-increment leveraging error checking 266 | return temp; 267 | } 268 | 269 | 270 | 271 | template 272 | typename DLinkedList::Iterator & DLinkedList::Iterator::operator--() // pre -decrement 273 | { 274 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to decrement null Iterator" ); 275 | _node = _node->_prev; 276 | return *this; 277 | } 278 | 279 | 280 | 281 | template 282 | typename DLinkedList::Iterator DLinkedList::Iterator::operator--( int ) // post-decrement 283 | { 284 | Iterator temp( *this ); 285 | operator--(); // Delegate to pre-decrement leveraging error checking 286 | return temp; 287 | } 288 | 289 | 290 | 291 | template 292 | typename DLinkedList::Iterator DLinkedList::Iterator::next( size_t delta ) const 293 | { 294 | // advance a copy of this iterator delta times 295 | Iterator p( *this ); 296 | for(; delta > 0 && p != nullptr; --delta, ++p ); /* intentionally empty body */ 297 | 298 | return p; 299 | } 300 | 301 | 302 | 303 | template 304 | typename DLinkedList::Iterator DLinkedList::Iterator::operator+( size_t rhs ) const 305 | { return next(rhs); } 306 | 307 | 308 | 309 | template 310 | typename DLinkedList::Iterator DLinkedList::Iterator::prev( size_t delta ) const 311 | { 312 | // retreat a copy of this iterator delta times 313 | Iterator p( *this ); 314 | for( ; delta > 0 && p != nullptr; --delta, --p ); /* intentionally empty body */ 315 | 316 | return p; 317 | } 318 | 319 | 320 | template 321 | typename DLinkedList::Iterator DLinkedList::Iterator::operator-( size_t rhs ) const 322 | { return prev(rhs); } 323 | 324 | 325 | 326 | template 327 | Data_t & DLinkedList::Iterator::operator* () const 328 | { 329 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 330 | return _node->_data; 331 | } 332 | 333 | 334 | 335 | template 336 | Data_t * DLinkedList::Iterator::operator->() const 337 | { 338 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 339 | return &(_node->_data); 340 | } 341 | 342 | 343 | 344 | template 345 | bool DLinkedList::Iterator::operator==( const Iterator & rhs ) const 346 | { return _node == rhs._node; } 347 | 348 | 349 | 350 | template 351 | bool DLinkedList::Iterator::operator!=( const Iterator & rhs ) const 352 | { return !(*this == rhs); } 353 | -------------------------------------------------------------------------------- /SLinkedList.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // length_error, invalid_argument 4 | 5 | #include "SLinkedList.hpp" 6 | 7 | 8 | 9 | 10 | 11 | 12 | /******************************************************************************* 13 | ** A singly linked list's node 14 | *******************************************************************************/ 15 | template 16 | struct SLinkedList::Node 17 | { 18 | Node( const Data_t & element ) : _data( element ) {} 19 | 20 | Data_t _data; // linked list element value 21 | Node * _next = nullptr; // next item in the list 22 | }; 23 | 24 | 25 | 26 | 27 | /******************************************************************************* 28 | ** SLinkedList Function definitions 29 | *******************************************************************************/ 30 | 31 | /******************************************************************************* 32 | ** Extended interface to demonstrate recursive functions 33 | *******************************************************************************/ 34 | 35 | // Add a function, named "reverse", to the Singly Linked List that reverses the list without copying the list or moving data in 36 | // the list. Implement the function recursively. The function must be implemented recursively. 37 | // 38 | // Client visible interface (the public function) 39 | template 40 | void SLinkedList::reverse() 41 | { 42 | // Kick off the recursion starting at the head 43 | reverse( _head ); 44 | 45 | // Now swap the head and tail pointers 46 | auto tmp = _head; 47 | _head = _tail; 48 | _tail = tmp; 49 | } 50 | 51 | 52 | // Add a function, named "reverse" ... 53 | // The private helper function 54 | template 55 | void SLinkedList::reverse( Node * currentNode ) 56 | { 57 | // There is nothing more to do if we're reversing and empty list or we're at the last node in a non-empty list (base case) 58 | if( currentNode == nullptr || currentNode->_next == nullptr ) return; 59 | 60 | // Move down the list to the next node (recurse) 61 | reverse( currentNode->_next ); 62 | 63 | // While unwinding (moving from tail towards head), make the next node point to this node 64 | currentNode->_next->_next = currentNode; 65 | currentNode ->_next = nullptr; 66 | } 67 | 68 | 69 | 70 | 71 | // Add a function, named "add" that returns the sum of all elements in the list. The function must be implemented recursively. 72 | // Assume operator+(lhs, rhs) is defined 73 | // 74 | // Client visible interface (the public function) 75 | template 76 | Data_t SLinkedList::add() 77 | { 78 | return add( _head ); 79 | } 80 | 81 | 82 | // Add a function, named "add" ... 83 | // The private helper function 84 | template 85 | Data_t SLinkedList::add( Node * currentNode ) 86 | { 87 | // Base case - reached the end of the list. Can't return zero, Data_t may not be an integer type. Return the value initialized 88 | // default value instead 89 | if( currentNode == nullptr ) return Data_t(); 90 | 91 | // Use a postorder traversal where the left hand side is progressively easier and easier and eventually trivial to solve 92 | return currentNode->_data + add( currentNode->_next ); 93 | } 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | // empty list constructor 109 | template 110 | SLinkedList::SLinkedList() = default; 111 | 112 | 113 | 114 | // copy constructor 115 | template 116 | SLinkedList::SLinkedList( const SLinkedList & original ) 117 | { 118 | // Walk the original list adding copies of the elements to this (initially empty) list maintaining order 119 | for( Node * current = original._head; current != nullptr; current = current->_next ) 120 | { append( current->_data ); } 121 | } 122 | 123 | 124 | 125 | // copy assignment 126 | template 127 | SLinkedList & SLinkedList::operator=( const SLinkedList & rhs ) 128 | { 129 | if( this != &rhs ) // avoid self assignment 130 | { 131 | // Release the contents of this list first 132 | clear(); // An optimization might be possible by reusing already allocated nodes 133 | 134 | // Walk the right hand side list adding copies of the elements to this list maintaining order 135 | for( Node * position = rhs._head; position != nullptr; position = position->_next ) 136 | { append( position->_data ); } 137 | } 138 | return *this; 139 | } 140 | 141 | 142 | 143 | // destructor 144 | template 145 | SLinkedList::~SLinkedList() 146 | { clear(); } 147 | 148 | 149 | 150 | template 151 | bool SLinkedList::empty() const 152 | { return _head == nullptr; } // can also use return (_size == 0); 153 | 154 | 155 | 156 | template 157 | void SLinkedList::clear() 158 | { while( !empty() ) removeFront(); } 159 | 160 | 161 | 162 | template 163 | size_t SLinkedList::size() const 164 | { return _size; } 165 | 166 | 167 | 168 | template 169 | Data_t& SLinkedList::front() 170 | { 171 | if( empty() ) throw std::length_error( "empty list" ); 172 | return _head->_data; 173 | } 174 | 175 | 176 | 177 | template 178 | void SLinkedList::prepend( const Data_t& element ) 179 | { insertAfter( nullptr, element ); } 180 | 181 | 182 | 183 | template 184 | void SLinkedList::removeFront() 185 | { removeAfter( nullptr ); } 186 | 187 | 188 | 189 | template 190 | Data_t& SLinkedList::back() 191 | { 192 | if( empty() ) throw std::length_error( "attempt to access data from an empty list" ); 193 | return _tail->_data; 194 | } 195 | 196 | 197 | 198 | template 199 | void SLinkedList::append( const Data_t & element ) 200 | { insertAfter( _tail, element ); } 201 | 202 | 203 | 204 | template 205 | typename SLinkedList::Iterator SLinkedList::insertAfter( const Iterator & position, const Data_t & element ) 206 | { 207 | Node * newNode = new Node( element ); // create new node 208 | 209 | if ( empty() ) _head = _tail = newNode; 210 | 211 | else if( position == nullptr ) // Insert at front of list 212 | { 213 | // Relink the head 214 | newNode->_next = _head; // head now follows newNode 215 | _head = newNode; // newNode is now the head 216 | } 217 | 218 | else if( position == _tail ) // Insert at back of list 219 | { 220 | // Relink the tail 221 | _tail->_next = newNode; // tail now precedes newNode 222 | _tail = newNode; // newNode is now the tail 223 | } 224 | 225 | else // Insert in the middle of the list 226 | { 227 | // Relink the node at current 228 | newNode->_next = position._node->_next; 229 | position._node->_next = newNode; 230 | } 231 | 232 | ++_size; 233 | return newNode; 234 | } 235 | 236 | 237 | 238 | template 239 | typename SLinkedList::Iterator SLinkedList::removeAfter( const Iterator & position ) 240 | { 241 | if( empty() ) throw std::length_error( "attempt to remove from an empty list" ); 242 | if( position == _tail ) return nullptr; // removing after the tail intentionally does nothing 243 | 244 | 245 | Node * oldNode = nullptr; // the node to be removed 246 | if( position == nullptr ) // remove the node at the head of the list 247 | { 248 | oldNode = _head; 249 | _head = _head->_next; 250 | } 251 | 252 | else 253 | { 254 | oldNode = position._node->_next; // remove from middle of list 255 | position._node->_next = position._node->_next->_next; // unlink the old node 256 | } 257 | 258 | --_size; 259 | 260 | if( oldNode == _tail ) _tail = nullptr; // remove the tail of the list 261 | 262 | Iterator returnNode( oldNode->_next ); // return the node after the one removed 263 | delete oldNode; // delete what used to be the old node 264 | return returnNode; 265 | } 266 | 267 | 268 | 269 | template 270 | typename SLinkedList::Iterator SLinkedList::begin() 271 | { return Iterator(_head); } 272 | 273 | 274 | 275 | template 276 | typename SLinkedList::Iterator SLinkedList::end() 277 | { return Iterator(); } 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | /******************************************************************************* 290 | ** SLinkedList::Iterator Function definitions 291 | *******************************************************************************/ 292 | template 293 | SLinkedList::Iterator::Iterator( Node * p ) 294 | : _node(p) 295 | {} 296 | 297 | 298 | 299 | template 300 | typename SLinkedList::Iterator & SLinkedList::Iterator::operator++() // pre-increment 301 | { 302 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to increment null Iterator" ); 303 | _node = _node->_next; 304 | return *this; 305 | } 306 | 307 | 308 | 309 | template 310 | typename SLinkedList::Iterator SLinkedList::Iterator::operator++( int ) // post-increment 311 | { 312 | Iterator temp( *this ); 313 | operator++(); // Delegate to pre-increment leveraging error checking 314 | return temp; 315 | } 316 | 317 | 318 | 319 | template 320 | typename SLinkedList::Iterator SLinkedList::Iterator::next( size_t delta ) const 321 | { 322 | // advance a copy of this iterator delta times 323 | Iterator p( *this ); 324 | for(; delta > 0 && p != nullptr; --delta, ++p ) /* intentionally empty body */; 325 | return p; 326 | } 327 | 328 | 329 | 330 | template 331 | typename SLinkedList::Iterator SLinkedList::Iterator::operator+( size_t rhs ) const 332 | { return next(rhs); } 333 | 334 | 335 | 336 | template 337 | Data_t & SLinkedList::Iterator::operator* () const 338 | { 339 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 340 | return _node->_data; 341 | } 342 | 343 | 344 | 345 | template 346 | Data_t * SLinkedList::Iterator::operator->() const 347 | { 348 | if( _node == nullptr ) throw std::invalid_argument( "Attempt to dereference null Iterator" ); 349 | return &(_node->_data); 350 | } 351 | 352 | 353 | 354 | template 355 | bool SLinkedList::Iterator::operator==( const Iterator & rhs ) const 356 | { return _node == rhs._node; } 357 | 358 | 359 | 360 | template 361 | bool SLinkedList::Iterator::operator!=( const Iterator & rhs ) const 362 | { return !(*this == rhs); } 363 | -------------------------------------------------------------------------------- /BinarySearchTree.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include // max(), swap() 5 | 6 | #include "BinarySearchTree.hpp" 7 | 8 | 9 | // This source file has both iterative and recursive implementations for some functions so the two can be studied and compared side 10 | // by side. To select which version to use (compile and runtime), define either USING_ITERATIVE_FUNCTIONS or 11 | // USING_RECURSIVE_FUNCTIONS macro at compile time with the -DUSING_ITERATIVE_FUNCTIONS (or -DUSING_RECURSIVE_FUNCTIONS) compiler 12 | // switch, or by uncommenting out the line below to change the default from USING_RECURSIVE_FUNCTIONS to USING_ITERATIVE_FUNCTIONS 13 | 14 | //#define USING_ITERATIVE_FUNCTIONS 15 | #ifndef USING_ITERATIVE_FUNCTIONS 16 | #define USING_RECURSIVE_FUNCTIONS 17 | #else 18 | #undef USING_RECURSIVE_FUNCTIONS 19 | #endif 20 | 21 | 22 | 23 | 24 | 25 | /******************************************************************************* 26 | ** Binary Search Tree Node Definition 27 | *******************************************************************************/ 28 | template 29 | struct BinarySearchTree::Node 30 | { 31 | friend std::ostream & operator<<( std::ostream & stream, const Node & node ) 32 | { 33 | stream << "Key: \"" << node.key_ << "\", Value: \"" << node.value_ << "\"\n"; 34 | return stream; 35 | } 36 | 37 | 38 | // Constructors 39 | Node( const Key & key = Key(), const Value & value = Value() ); // Also serves as the default constructor 40 | 41 | // Public instance attributes 42 | Key key_; 43 | Value value_; 44 | 45 | // Private instance attributes 46 | Node * left_ = nullptr; 47 | Node * right_ = nullptr; 48 | Node * parent_ = nullptr; 49 | }; 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | /******************************************************************************* 60 | ** BinarySearchTree Definitions 61 | *******************************************************************************/ 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | // Constructors, destructor, assignments 65 | //////////////////////////////////////////////////////////////////////////////// 66 | template 67 | BinarySearchTree::BinarySearchTree( const BinarySearchTree & original ) 68 | { root_ = makeCopy( original.root_ ); } 69 | 70 | 71 | 72 | 73 | template 74 | typename BinarySearchTree::Node * BinarySearchTree::makeCopy( Node * originalNode ) 75 | { 76 | if( originalNode == nullptr ) return nullptr; 77 | 78 | auto node = new Node( originalNode->key_, originalNode->value_ ); 79 | node->left_ = makeCopy( originalNode->left_ ); 80 | node->right_ = makeCopy( originalNode->right_ ); 81 | 82 | if( node->left_ != nullptr ) node->left_ ->parent_ = node; 83 | if( node->right_ != nullptr ) node->right_->parent_ = node; 84 | 85 | return node; 86 | } 87 | 88 | 89 | 90 | 91 | // Passing by value delegates copying the tree to the copy constructor, keeping the "copy" knowledge in one place. The destructor 92 | // destroys the old tree when the rhs parameter goes out of scope. (Copy and swap idiom) 93 | template 94 | BinarySearchTree & BinarySearchTree::operator=( BinarySearchTree rhs ) 95 | { 96 | auto temp = rhs.root_; 97 | rhs.root_ = root_; 98 | root_ = temp; 99 | 100 | return *this; 101 | } 102 | 103 | 104 | 105 | 106 | template 107 | BinarySearchTree::~BinarySearchTree() 108 | { clear(); } 109 | 110 | 111 | 112 | 113 | template 114 | void BinarySearchTree::clear() 115 | { 116 | clear( root_ ); 117 | root_ = nullptr; 118 | } 119 | 120 | 121 | 122 | 123 | template 124 | void BinarySearchTree::clear( Node * node ) 125 | { 126 | if( node == nullptr ) return; 127 | 128 | clear( node->left_ ); 129 | clear( node->right_ ); 130 | 131 | delete node; 132 | } 133 | 134 | 135 | 136 | 137 | //////////////////////////////////////////////////////////////////////////////// 138 | // Search 139 | //////////////////////////////////////////////////////////////////////////////// 140 | template 141 | Value BinarySearchTree::search( const Key & key ) const 142 | { 143 | #if defined(USING_ITERATIVE_FUNCTIONS) 144 | auto node = searchIterative( key ); // zyBook 6.4.1: BST search algorithm. 145 | 146 | #elif defined(USING_RECURSIVE_FUNCTIONS) 147 | auto node = searchRecursive( root_, key ); // 6.10.1: BST recursive search algorithm. 148 | 149 | #endif 150 | 151 | if( node == nullptr ) throw std::invalid_argument( "Key not found" ); 152 | return node->value_; 153 | } 154 | 155 | 156 | 157 | 158 | // zyBook 6.4.1: BST search algorithm. 159 | template 160 | typename BinarySearchTree::Node * BinarySearchTree::searchIterative( const Key & key ) const 161 | { 162 | auto cur = root_; 163 | 164 | while( cur != nullptr ) 165 | { 166 | if ( key == cur->key_ ) return cur; // Found 167 | else if( key < cur->key_ ) cur = cur->left_; 168 | else cur = cur->right_; 169 | } 170 | 171 | return nullptr; // Not found 172 | } 173 | 174 | 175 | 176 | 177 | // zyBook 6.10.1: BST recursive search algorithm. 178 | template 179 | typename BinarySearchTree::Node * BinarySearchTree::searchRecursive( Node * node, const Key & key ) const 180 | { 181 | if( node != nullptr ) 182 | { 183 | if ( key == node->key_ ) return node; 184 | else if( key < node->key_ ) return searchRecursive( node->left_, key ); 185 | else return searchRecursive( node->right_, key ); 186 | } 187 | 188 | return nullptr; 189 | } 190 | 191 | 192 | 193 | 194 | //////////////////////////////////////////////////////////////////////////////// 195 | // Insert 196 | //////////////////////////////////////////////////////////////////////////////// 197 | template 198 | void BinarySearchTree::insert( const Key & key, const Value & value ) 199 | { 200 | auto node = new Node( key, value ); 201 | 202 | #if defined(USING_ITERATIVE_FUNCTIONS) 203 | insertIterative( node ); // Figure 6.9.1: BSTInsert algorithm for BSTs with nodes containing parent pointers. 204 | 205 | #elif defined(USING_RECURSIVE_FUNCTIONS) 206 | if( root_ == nullptr ) root_ = node; // Figure 6.10.2: Recursive BST insertion and removal. 207 | else insertRecursive( root_, node ); // Figure 6.10.2: Recursive BST insertion and removal. 208 | 209 | #endif 210 | } 211 | 212 | 213 | 214 | 215 | // Figure 6.9.1: BSTInsert algorithm for BSTs with nodes containing parent pointers. 216 | template 217 | void BinarySearchTree::insertIterative( Node * node ) 218 | { 219 | node->left_ = nullptr; // insert as a leaf (added to zyBook algorithm for completeness) 220 | node->right_ = nullptr; 221 | 222 | if( root_ == nullptr ) // Insert first node 223 | { 224 | root_ = node; 225 | node->parent_ = nullptr; 226 | 227 | return; 228 | } 229 | 230 | auto cur = root_; 231 | while( cur != nullptr ) // Search for insertion point, starting with the root 232 | { 233 | if( node->key_ < cur->key_ ) 234 | { 235 | if( cur->left_ == nullptr ) // Found left insertion point 236 | { 237 | cur->left_ = node; 238 | node->parent_ = cur; 239 | cur = nullptr; // Stop searching 240 | } 241 | else 242 | { 243 | cur = cur->left_; // Continue searching left for insertion point 244 | } 245 | } 246 | 247 | else // node->key_ >= cur->key_ (This algorithm allows duplicate keys) 248 | { 249 | if( cur->right_ == nullptr ) 250 | { // Found right insertion point 251 | cur->right_ = node; 252 | node->parent_ = cur; 253 | cur = nullptr; // Stop searching 254 | } 255 | else 256 | { 257 | cur = cur->right_; // Continue searching right for insertion point 258 | } 259 | } 260 | } 261 | } 262 | 263 | 264 | 265 | 266 | // zyBook Figure 6.10.2: Recursive BST insertion and removal. (Assumes parent and nodeToInsert are not null) 267 | template 268 | void BinarySearchTree::insertRecursive( Node * parent, Node * nodeToInsert ) 269 | { 270 | if( nodeToInsert->key_ < parent->key_ ) 271 | { 272 | if( parent->left_ == nullptr ) 273 | { 274 | parent->left_ = nodeToInsert; 275 | nodeToInsert->parent_ = parent; // Not in zyBook algorithm, but needed when tree node contains parent pointer 276 | } 277 | else insertRecursive( parent->left_, nodeToInsert ); 278 | } 279 | else 280 | { 281 | if( parent->right_ == nullptr ) 282 | { 283 | parent->right_ = nodeToInsert; 284 | nodeToInsert->parent_ = parent; // Not in zyBook algorithm, but needed when tree node contains parent pointer 285 | } 286 | else insertRecursive( parent->right_, nodeToInsert ); 287 | } 288 | } 289 | 290 | 291 | 292 | 293 | //////////////////////////////////////////////////////////////////////////////// 294 | // Remove 295 | //////////////////////////////////////////////////////////////////////////////// 296 | // zyBook Figure 6.9.3: BSTRemoveKey and BSTRemoveNode algorithms for BSTs with nodes containing parent pointers. 297 | template 298 | void BinarySearchTree::remove( const Key & key ) 299 | { 300 | #if defined(USING_ITERATIVE_FUNCTIONS) 301 | auto node = searchIterative( key ); 302 | 303 | #elif defined(USING_RECURSIVE_FUNCTIONS) 304 | auto node = searchRecursive( root_, key ); 305 | 306 | #endif 307 | 308 | remove( node ); 309 | } 310 | 311 | 312 | 313 | 314 | // zyBook Figure 6.9.3: BSTRemoveKey and BSTRemoveNode algorithms for BSTs with nodes containing parent pointers. 315 | template 316 | void BinarySearchTree::remove( Node * node ) 317 | { 318 | if( node == nullptr ) return; 319 | 320 | // Case 1: Internal node with 2 children 321 | if ( node->left_ != nullptr && node->right_ != nullptr) 322 | { 323 | // Find successor (leftmost child of right subtree) 324 | auto succNode = node->right_; 325 | while( succNode->left_ != nullptr ) succNode = succNode->left_; 326 | 327 | // Copy value/data from succNode to node 328 | node->key_ = succNode->key_; 329 | node->value_ = succNode->value_; 330 | 331 | // Recursively remove succNode 332 | remove( succNode ); 333 | } 334 | 335 | 336 | else 337 | { 338 | // Case 2: Root node (with 1 or 0 children) 339 | if (node == root_) 340 | { 341 | if( node->left_ != nullptr ) root_ = node->left_; 342 | else root_ = node->right_; 343 | 344 | // Make sure the new root, if non-null, has a null parent 345 | if( root_ != nullptr ) root_->parent_ = nullptr; 346 | } 347 | 348 | 349 | 350 | // Case 3: Internal with left child only 351 | else if( node->left_ != nullptr ) replaceChild( node->parent_, node, node->left_ ); 352 | 353 | // Case 4: Internal with right child only OR leaf 354 | else replaceChild( node->parent_, node, node->right_ ); 355 | 356 | delete node; // Not in zyBook algorithm, but needed to prevent memory leak 357 | } 358 | } 359 | 360 | 361 | 362 | 363 | // zyBook Figure 6.9.2: BSTReplaceChild algorithm. 364 | template 365 | bool BinarySearchTree::replaceChild( Node * parent, 366 | Node * currentChild, 367 | Node * newChild ) 368 | { 369 | if( parent->left_ != currentChild && parent->right_ != currentChild ) return false; 370 | 371 | if( parent->left_ == currentChild ) parent->left_ = newChild; 372 | else parent->right_ = newChild; 373 | 374 | if( newChild != nullptr ) newChild->parent_ = parent; 375 | 376 | return true; 377 | } 378 | 379 | 380 | 381 | 382 | //////////////////////////////////////////////////////////////////////////////// 383 | // Print 384 | //////////////////////////////////////////////////////////////////////////////// 385 | template 386 | void BinarySearchTree::printInorder() const 387 | { 388 | printInorder( root_ ); 389 | } 390 | 391 | 392 | 393 | 394 | // zyBook Figure 6.7.1: BST inorder traversal algorithm. 395 | template 396 | void BinarySearchTree::printInorder( Node * node ) const 397 | { 398 | if( node == nullptr ) return; 399 | 400 | printInorder( node->left_ ); 401 | std::cout << *node; 402 | printInorder( node->right_ ); 403 | } 404 | 405 | 406 | 407 | 408 | //////////////////////////////////////////////////////////////////////////////// 409 | // Height 410 | //////////////////////////////////////////////////////////////////////////////// 411 | template 412 | int BinarySearchTree::getHeight() const 413 | { 414 | return getHeight( root_ ); 415 | } 416 | 417 | 418 | 419 | 420 | // zyBook Figure 6.8.3: BSTGetHeight algorithm. 421 | template 422 | int BinarySearchTree::getHeight( Node * node ) const 423 | { 424 | if( node == nullptr ) return -1; 425 | 426 | auto leftHeight = getHeight( node->left_ ); 427 | auto rightHeight = getHeight( node->right_ ); 428 | 429 | return 1 + std::max( leftHeight, rightHeight ); 430 | } 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | /******************************************************************************* 446 | ** BinarySearchTree::Node Definitions 447 | *******************************************************************************/ 448 | template 449 | BinarySearchTree::Node::Node( const Key & key, const Value & value ) 450 | : key_( key ), value_( value ) 451 | {} 452 | --------------------------------------------------------------------------------