adding the ptrs implementation of iterator incrementing
This commit is contained in:
@@ -23,14 +23,14 @@ class tree_iterator {
|
||||
public:
|
||||
tree_iterator() : ptr_(NULL) {}
|
||||
tree_iterator(TreeNode<T>* 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<T> & 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<T> operator++(int) { tree_iterator<T> temp(*this); ++(*this); return temp; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
41
lectures/20_trees_III/ds_set_main.cpp
Normal file
41
lectures/20_trees_III/ds_set_main.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <iostream>
|
||||
#include "ds_set_ptrs.h"
|
||||
|
||||
int main() {
|
||||
// create a set of integers
|
||||
ds_set<int> 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<int>::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;
|
||||
}
|
||||
166
lectures/20_trees_III/ds_set_ptrs.h
Normal file
166
lectures/20_trees_III/ds_set_ptrs.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <utility> // for std::pair
|
||||
#include <vector>
|
||||
|
||||
template <class T>
|
||||
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 T>
|
||||
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<TreeNode<T>*> 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<T> & 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<T>* 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<T>* 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<T> operator++(int) { tree_iterator<T> temp(*this); ++(*this); return temp; }
|
||||
tree_iterator<T> & operator--() { /* implementation omitted */ }
|
||||
tree_iterator<T> operator--(int) { tree_iterator<T> temp(*this); --(*this); return temp; }
|
||||
private:
|
||||
// representation
|
||||
std::vector<TreeNode<T>*> 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 T>
|
||||
class ds_set {
|
||||
public:
|
||||
ds_set(){
|
||||
root = NULL;
|
||||
m_size = 0;
|
||||
}
|
||||
typedef tree_iterator<T> iterator;
|
||||
int size() { return m_size; }
|
||||
iterator find(const T& key){
|
||||
std::vector<TreeNode<T>*> ptrs;
|
||||
return find(key, root, ptrs);
|
||||
}
|
||||
std::pair<iterator, bool> insert(const T& key){
|
||||
std::pair<iterator, bool> temp;
|
||||
std::vector<TreeNode<T>*> 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<T> begin() const {
|
||||
std::vector<TreeNode<T>*> ptrs;
|
||||
TreeNode<T>* p = root;
|
||||
while (p) {
|
||||
ptrs.push_back(p);
|
||||
p = p->left; // go left
|
||||
}
|
||||
return tree_iterator<T>(ptrs);
|
||||
}
|
||||
tree_iterator<T> end() const { return tree_iterator<T>(); }
|
||||
private:
|
||||
TreeNode<T>* root;
|
||||
int m_size;
|
||||
iterator find(const T& key, TreeNode<T>* root, std::vector<TreeNode<T>*>& 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<iterator, bool> insertHelper(const T& key, TreeNode<T>*& node, std::vector<TreeNode<T>*>& ptrs) {
|
||||
// if node is nullptr, insert the key here
|
||||
if (!node) {
|
||||
node = new TreeNode<T>(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 <class T>
|
||||
typename ds_set<T>::iterator ds_set<T>::find(const T& key, TreeNode<T>* root, std::vector<TreeNode<T>*>& 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user