diff --git a/lectures/19_trees_II/ds_set.h b/lectures/19_trees_II/ds_set.h index fca846f..2697601 100644 --- a/lectures/19_trees_II/ds_set.h +++ b/lectures/19_trees_II/ds_set.h @@ -23,14 +23,14 @@ class tree_iterator { public: tree_iterator() : ptr_(NULL) {} tree_iterator(TreeNode* p) : ptr_(p) {} - tree_iterator(const tree_iterator& old) : ptr_(old.ptr_) {} + tree_iterator(const tree_iterator& other) : ptr_(other.ptr_) {} ~tree_iterator() {} - tree_iterator& operator=(const tree_iterator& old) { ptr_ = old.ptr_; return *this; } + tree_iterator& operator=(const tree_iterator& other) { ptr_ = other.ptr_; return *this; } // operator* gives constant access to the value at the pointer const T& operator*() const { return ptr_->key; } // comparison operators are straightforward - bool operator== (const tree_iterator& rgt) { return ptr_ == rgt.ptr_; } - bool operator!= (const tree_iterator& rgt) { return ptr_ != rgt.ptr_; } + bool operator== (const tree_iterator& other) { return ptr_ == other.ptr_; } + bool operator!= (const tree_iterator& other) { return ptr_ != other.ptr_; } // increment & decrement operators tree_iterator & operator++() { // if i have right subtree, find left most element of those @@ -47,6 +47,7 @@ public: // go up one more time ptr_ = ptr_->parent; } + // when we return, ptr_ should point to the destination node. return *this; } tree_iterator operator++(int) { tree_iterator temp(*this); ++(*this); return temp; } diff --git a/lectures/19_trees_II/ds_set_main.cpp b/lectures/19_trees_II/ds_set_main.cpp index 4e76ef0..8f35199 100644 --- a/lectures/19_trees_II/ds_set_main.cpp +++ b/lectures/19_trees_II/ds_set_main.cpp @@ -8,8 +8,11 @@ int main() { // insert some values into the set numbers.insert(10); numbers.insert(5); + numbers.insert(88); numbers.insert(20); + numbers.insert(49); numbers.insert(15); + numbers.insert(36); numbers.insert(5); // Duplicate value (won't be inserted) // print the elements of the set @@ -27,5 +30,13 @@ int main() { std::cout << value << " is not found in the set." << std::endl; } + // check if a specific value exists in the set + value = 66; + if (numbers.find(value) != numbers.end()) { + std::cout << value << " is found in the set." << std::endl; + } else { + std::cout << value << " is not found in the set." << std::endl; + } + return 0; } diff --git a/lectures/20_trees_III/ds_set_main.cpp b/lectures/20_trees_III/ds_set_main.cpp new file mode 100644 index 0000000..2885b44 --- /dev/null +++ b/lectures/20_trees_III/ds_set_main.cpp @@ -0,0 +1,41 @@ +#include +#include "ds_set_ptrs.h" + +int main() { + // create a set of integers + ds_set numbers; + + // insert some values into the set + numbers.insert(10); + numbers.insert(5); + numbers.insert(88); + numbers.insert(20); + numbers.insert(49); + numbers.insert(15); + numbers.insert(36); + numbers.insert(5); // Duplicate value (won't be inserted) + + // print the elements of the set + std::cout << "The elements in the set are:" << std::endl; + for(ds_set::iterator itr = numbers.begin(); itr != numbers.end(); ++itr){ + std::cout << *itr << " "; + } + std::cout << std::endl; + + // check if a specific value exists in the set + int value = 15; + if (numbers.find(value) != numbers.end()) { + std::cout << value << " is found in the set." << std::endl; + } else { + std::cout << value << " is not found in the set." << std::endl; + } + + // check if a specific value exists in the set + value = 66; + if (numbers.find(value) != numbers.end()) { + std::cout << value << " is found in the set." << std::endl; + } else { + std::cout << value << " is not found in the set." << std::endl; + } + return 0; +} diff --git a/lectures/20_trees_III/ds_set_ptrs.h b/lectures/20_trees_III/ds_set_ptrs.h new file mode 100644 index 0000000..bc5d868 --- /dev/null +++ b/lectures/20_trees_III/ds_set_ptrs.h @@ -0,0 +1,166 @@ +#include // for std::pair +#include + +template +class TreeNode { +public: + T key; + TreeNode* left; + TreeNode* right; + // one way to allow implementation of iterator increment & decrement + TreeNode* parent; + // constructor + TreeNode(const T& k) { + key = k; + left = NULL; + right = NULL; + } +}; + +// ------------------------------------------------------------------- +// TREE NODE ITERATOR CLASS +template +class tree_iterator { +public: + // default constructor + tree_iterator() = default; // including = default explicitly tells someone reading the code that + // you intend for the compiler to generate the default constructor. + // It makes your intent clearer. + // constructor + tree_iterator(std::vector*> p) : ptrs(p) {} + // copy constructor + tree_iterator(const tree_iterator& other) : ptrs(other.ptrs) {} + // destructor + ~tree_iterator() {} + // assignment operator + tree_iterator& operator=(const tree_iterator& other) { ptrs = other.ptrs; return *this; } + // operator* gives constant access to the value at the pointer + const T& operator*() const { return (ptrs.back())->key; } + // comparison operators are straightforward + bool operator== (const tree_iterator& other) { return ptrs == other.ptrs; } + bool operator!= (const tree_iterator& other) { return ptrs != other.ptrs; } + // increment & decrement operators + tree_iterator & operator++() { + // prefix ++ + if (ptrs.empty()) return *this; // end() reached + // ptrs.back() gives us the pointer points to the current node, i.e, that's where we are now. + TreeNode* curr = ptrs.back(); + // case 1: has right subtree + if (curr->right) { + curr = curr->right; + ptrs.push_back(curr); + // go as left as possible + while (curr->left) { + curr = curr->left; + ptrs.push_back(curr); + } + } + // case 2: no right subtree, go up + else { + TreeNode* last = ptrs.back(); + ptrs.pop_back(); + // keep going up as long as I am (here I means last) the right child of my parent. + while (!ptrs.empty() && ptrs.back()->right == last) { + last = ptrs.back(); + ptrs.pop_back(); + } + // if ptrs empty now, we hit end() + } + // when we return, ptrs.back() should point to the destination node. + return *this; + } + tree_iterator operator++(int) { tree_iterator temp(*this); ++(*this); return temp; } + tree_iterator & operator--() { /* implementation omitted */ } + tree_iterator operator--(int) { tree_iterator temp(*this); --(*this); return temp; } +private: + // representation + std::vector*> ptrs; // store pointers to every node which is on the path from root to current node +}; + +// internally it's a binary search tree +template +class ds_set { +public: + ds_set(){ + root = NULL; + m_size = 0; + } + typedef tree_iterator iterator; + int size() { return m_size; } + iterator find(const T& key){ + std::vector*> ptrs; + return find(key, root, ptrs); + } + std::pair insert(const T& key){ + std::pair temp; + std::vector*> ptrs; + temp = insertHelper(key, root, ptrs); + if(temp.second == true){ + ++m_size; + } + return temp; + } + // ITERATORS + // return an iterator to the first (leftmost) node of the binary search tree, + // which can be found by traversing to the leftmost node starting from the root. + tree_iterator begin() const { + std::vector*> ptrs; + TreeNode* p = root; + while (p) { + ptrs.push_back(p); + p = p->left; // go left + } + return tree_iterator(ptrs); + } + tree_iterator end() const { return tree_iterator(); } +private: + TreeNode* root; + int m_size; + iterator find(const T& key, TreeNode* root, std::vector*>& ptrs); + // as there are multiple templated classes involved, writing this function outside of the class definition may be too complicated. + // helper function to perform the insertion + std::pair insertHelper(const T& key, TreeNode*& node, std::vector*>& ptrs) { + // if node is nullptr, insert the key here + if (!node) { + node = new TreeNode(key); + ptrs.push_back(node); // add the new node to the path + return {iterator(ptrs), true}; // return an iterator to the new node, and true for success + } + + // if key is already present in the tree, no insertion occurs + if (key == node->key) { + return {iterator(ptrs), false}; // return an iterator to the existing node, and false for failure + } + + // add the current node to the path + ptrs.push_back(node); + + // recursively insert into the left or right subtree + if (key < node->key) { + return insertHelper(key, node->left, ptrs); // Traverse left + } else { + return insertHelper(key, node->right, ptrs); // Traverse right + } + } + +}; + +template +typename ds_set::iterator ds_set::find(const T& key, TreeNode* root, std::vector*>& ptrs){ + // base case (if root doesn't even exist) + if(root == NULL){ + return end(); + } + + // add current node to the path + ptrs.push_back(root); + // general case + if(key < root->key){ + return find(key, root->left, ptrs); + }else if(key > root->key){ + return find(key, root->right, ptrs); + }else{ + return tree_iterator(ptrs); + } +} +