I have a data structure that uses a template class such that it can store any data type - ints, floats, strings, etc.
Because the data will be organized, I need a way to compare two values. Usually I can use > or <, but in the case of strings that will not work because using the >/< operators on a string won't tell me which comes first alphabetically. For that, I need to use the compare() function.
However since the data structure is a template class, I can't tell it to use the compare() function because it won't recognize the compare() function for anything BUT a string.
As a work-around, I tried writing two compare functions:
template (class t)
int BinaryTree<T>::compareVals(T v1, T v2);
and
template (class t)
int BinaryTree<T>::compareVals(string v1, string v2);
So that in the case of the value being a string type, the program would use the method with the compare() function in it.
However trying to do that, I am getting a compiler error basically telling me I can't overload the function.
So I'm out of ideas. How can I make this template class correctly compare and sort strings as well as numbers?
Thank you very much for your input!
Here is the entire class, for reference:
#ifndef binarytree_class
#define binarytree_class
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::string;
using std::vector;
template <class T>
class BinaryTree{
public:
struct TreeNode{
TreeNode * leftChild,
* rightChild;
T key;
vector<T> data;
int size;
};
BinaryTree();
~BinaryTree();
bool isEmpty();
int getSize();
void add(T key, T data);
void remove(T key);
int getHeight();
bool keyExists(T key);
int getKeyHeight(T key);
void displayAll();
private:
int size;
TreeNode * root;
TreeNode * findParent(TreeNode * start, TreeNode * child); //finds the parent node of child in subtree starting at root start
TreeNode * findNode(TreeNode * node, T input); //find node with data input in subtree at root node
TreeNode * findMin(TreeNode * node);
void removeNode(TreeNode * node); //Small part of algorithm (case: 2 children) borrowed from tech-faq.com/binary-tree-deleting-a-node.html
void displaySubTree(TreeNode * node); //displays subtree starting at node
void sortAdd(TreeNode * eNode, TreeNode * nNode); //adds a new node nNode to subtree starting at root eNode
void destroySubTree(TreeNode * node); //destroys subtree starting at node.
void display(TreeNode * node, string indent, bool last); //Algorithm borrowed from http://stackoverflow.com/questions/1649027/how-do-i-print-out-a-tree-structure
char leftOrRight(TreeNode * eNode, TreeNode * nNode); //compares keys in existing node vs new node and returns L or R
int calcHeight(TreeNode * node, int depth); //calculates the height from node. Algorithm borrowed from wiki.answers.com
int compareVals(T v1, T v2);
int compareVals(string v1, string v2);
};
template <class T>
BinaryTree<T>::BinaryTree<T>() : size(0){}
template <class T>
bool BinaryTree<T>::isEmpty(){
return (!size);
}
template <class T>
int BinaryTree<T>::compareVals(T v1, T v2){
int result;
v1 <= v2? result = -1 : result = 1;
return result;
}
template <class T>
int BinaryTree<T>::compareVals(string v1, string v2){
int result;
result = v1.compare(v2);
if(result >= 0)
result = -1;
else
result = 1;
return result;
}
template <class T>
int BinaryTree<T>::getSize(){
return size;
}
template <class T>
void BinaryTree<T>::add(T key, T data){
bool done = false;
TreeNode * temp;
if(keyExists(key)){
temp = findNode(root,key);
temp->size++;
temp->data.push_back(key);
}
else{
temp = new TreeNode;
temp->leftChild = 0;
temp->rightChild = 0;
temp->key = key;
temp->size = 0;
temp->data.push_back(data);
if(isEmpty())
root = temp;
else
sortAdd(root, temp);
size++;
}
}
template <class T>
void BinaryTree<T>::sortAdd(TreeNode * eNode, TreeNode * nNode){
if(leftOrRight(eNode, nNode) == 'L'){
if(eNode->leftChild == 0)
eNode->leftChild = nNode;
else
sortAdd(eNode->leftChild,nNode);
} else {
if(eNode->rightChild == 0)
eNode->rightChild = nNode;
else
sortAdd(eNode->rightChild,nNode);
}
}
template <class T>
char BinaryTree<T>::leftOrRight(TreeNode * eNode, TreeNode * nNode){
char result;
if(compareVals(nNode->key, eNode->key) == -1)
result = 'L';
else
result = 'R';
return result;
}
template <class T>
void BinaryTree<T>::displayAll(){
display(root,"",true);
}
template <class T>
void BinaryTree<T>::display(TreeNode * node, string indent, bool last){
if(!isEmpty()){
cout << indent;
if(last){
cout << "\\-";
indent += " ";
} else {
cout << "|-";
indent += "| ";
}
cout << node->key << "\n";
if(node->leftChild != 0)
display(node->leftChild, indent, false);
if(node->rightChild != 0)
display(node->rightChild, indent, true);
} else
cout << "TREE IS EMPTY" << "\n\n";
}
template <class T>
int BinaryTree<T>::getHeight(){
if(!root)
cout << "ERROR: getHeight() root is NULL!" << "\n";
int result;
if(isEmpty())
result = 0;
else
result = calcHeight(root, 1);
return result;
}
template <class T>
int BinaryTree<T>::getKeyHeight(T key){
int result = -1;
if(!keyExists(key))
cout << "ERROR: Trying to get height of nonexistant key " << key << "\n";
else{
TreeNode * temp = findNode(root, key);
result = getHeight() - calcHeight(temp,1);
}
return result;
}
template <class T>
int BinaryTree<T>::calcHeight(TreeNode * node, int depth){ //Algorithm borrowed from wiki.answers.com
int leftDepth,
rightDepth,
result;
if(node->leftChild)
leftDepth = calcHeight(node->leftChild, depth+1);
else
leftDepth = depth;
if(node->rightChild)
rightDepth = calcHeight(node->rightChild, depth+1);
else
rightDepth = depth;
if(leftDepth > rightDepth)
result = leftDepth;
else
result = rightDepth;
return result;
}
template <class T>
void BinaryTree<T>::remove(T input){
if(!keyExists(input))
cout << "ERR: trying to remove nonexistant key " << input << "\n";
else{
TreeNode * temp = findNode(root,input);
removeNode(temp);
}
}
template <class T>
bool BinaryTree<T>::keyExists(T key){
bool result;
if(isEmpty())
result = false;
else{
if(findNode(root,key) != 0)
result = true;
else
result = false;
}
return result;
}
template <class T>
typename BinaryTree<T>::TreeNode * BinaryTree<T>::findNode(TreeNode * node, T input){
TreeNode * result = 0; //Returns 0 if none found
if(node->key == input)
result = node;
else{
if(node->leftChild != 0)
result = findNode(node->leftChild, input);
if(result == 0 && node->rightChild != 0)
result = findNode(node->rightChild, input);
}
return result;
}
template <class T>
void BinaryTree<T>::removeNode(TreeNode * node){
TreeNode * parent = 0;
if(node != root)
parent = findParent(root,node);
if(node->leftChild && node->rightChild){ //Case: both children (algorithm borrowed from tech-faq.com)
TreeNode * temp = findMin(node->rightChild);
string tkey = temp->key;
removeNode(temp);
node->key = tkey;
} else {
if(parent){
if(!(node->leftChild) && !(node->rightChild)){ //case: no children & not root
if(parent->leftChild == node)
parent->leftChild = 0;
else
parent->rightChild = 0;
}
if(!(node->leftChild) && node->rightChild){ //case: right child only & not root
if(parent->leftChild == node)
parent->leftChild = node->rightChild;
else
parent->rightChild = node->rightChild;
}
if(node->leftChild && !(node->rightChild)){ //case: left child only & not root
if(parent->leftChild == node)
parent->leftChild = node->leftChild;
else
parent->rightChild = node->leftChild;
}
delete node;
size--;
}
else{
if(node->leftChild) //case: left child only & root
root = node->leftChild;
else //case: right child only & root
root = node->rightChild;
delete node; //case: no children & root intrinsically covered
size--;
}
}
}
template <class T>
typename BinaryTree<T>::TreeNode * BinaryTree<T>::findMin(TreeNode * node){
TreeNode * result;
if(node->leftChild == 0)
result = node;
else
result = findMin(node->leftChild);
return result;
}
template <class T>
typename BinaryTree<T>::TreeNode * BinaryTree<T>::findParent(TreeNode * start, TreeNode * child){
TreeNode * result = 0;
if(start->leftChild){
if(start->leftChild->key == child->key)
result = start;
else
result = findParent(start->leftChild, child);
}
if(start->rightChild && result == 0){
if(start->rightChild->key == child->key)
result = start;
else
result = findParent(start->rightChild, child);
}
return result;
}
template <class T>
void BinaryTree<T>::destroySubTree(TreeNode * node){
TreeNode * parent = 0;
if(node != root)
parent = findParent(root,node);
if(node->leftChild)
destroySubTree(node->leftChild);
if(node->rightChild)
destroySubTree(node->rightChild);
if(parent){
if(parent->leftChild == node)
parent->leftChild = 0;
else
parent->rightChild = 0;
}
size--;
delete node;
}
template <class T>
BinaryTree<T>::~BinaryTree<T>(){
if(!isEmpty())
destroySubTree(root);
}
#endif
When T is std::string, you will end up having to functions with identical signature, that's why your compiler is complaining. To overcome this, just make compareVals a template of its own. I would also suggest you make them static, so that you can call them without an object.
static int compareVals(std::string const& v1, std::string const& v2)
{
//compare std::string
}
template<typename U>
static int compareVals(U v1, U v2)
{
//compare everything else
}
In fact, std::string does have built in relational operators, so for your specific use case it might not be necessary to define a compare function like the above. At any rate, this approach might still be useful to avoid making the assumption operators are defined for every data type your BinaryTree might end up supporting in the future.
Related
I'm currently working through AVL trees and am curious why the output (pre order traversal) is only showing two levels of indentation, as if one of the second order nodes is pointing to three separate 3rd level node. I'm note sure if this is an issue with my print function, or the actual code.
#include <iostream>
#include <list>
#include <vector>
#include <iomanip>
using namespace std;
template <class T>
class BST;
template <class T>
class BSTNode{
T data;
BSTNode<T>* parent;
BSTNode<T>* left;
BSTNode<T>* right;
int height;
public:
BSTNode(T data = T(), BSTNode<T>* parent = nullptr, BSTNode<T>* left = nullptr,
BSTNode<T>* right = nullptr, int height = 0) : data(data), parent(parent), left(left), right(right), height(height){}
friend class BST<T>;
int getSize() const;
};
template <class T>
class BST{
public:
BSTNode<T>* root = nullptr;
BST() {}
~BST();
BST(const BST& rhs) {*this = rhs;};
BST& operator=(const BST& rhs);
void printPreOrder(BSTNode<T>* p, int indent);
void insert(T item, BSTNode<T> * & node);
void remove(BSTNode<T>*& temp); //pass pointer by reference because we will be changing the pointer
void clear(BSTNode<T>*& root); //clears the entire tree;
BST<T>* clone(BSTNode<T>* start);
void rotateRight(BSTNode<T> * & node); //rotate the left child (the left child becomes the parent and parent becomes the right child)
void rotateLeft(BSTNode<T> * & node); //rotate the right child (the right child becomes the parent and the parent becomes the left child)
void doubleRotateRight(BSTNode<T> *& node); //same result as rotate right
void doubleRotateLeft(BSTNode<T> *& node); //same result as rotate left
int height(BSTNode<T> * node); //return height
void balance(BSTNode<T> * &node);
};
template <class T>
int BSTNode<T>::getSize() const {
int count = 1;
if(left != nullptr){
count += left->getSize();
};
if(right != nullptr){
count += right->getSize();
};
return count;
}
template <class T>
void BST<T>::printPreOrder(BSTNode<T>* p, int indent){
if(p) {
if (indent) {
std::cout << std::setw(indent) << '\t';
}
cout<< p->data << "\n ";
if(p->left) printPreOrder(p->left, indent+2);
if(p->right) printPreOrder(p->right, indent+2);
}
}
template <class T>
void BST<T>::insert(T item, BSTNode<T> * & node){
if(!node){
node = new BSTNode<T>(item);
}
else if(item < node->data){
insert(item, node->left);
}
else{
insert(item, node->right);
}
balance(node);
}
template <class T>
void BST<T>::remove(BSTNode<T>*& temp){
if(temp->left == nullptr && temp->right == nullptr){
if(temp->parent == nullptr){
root = nullptr;
}
else if(temp->parent->left == temp){
temp->parent->left = nullptr;
}
else{
temp->parent->right = nullptr;
}
delete temp;
}
else if(temp->left == nullptr){ //promote right
BSTNode<T>* toDelete = temp->right;
temp->data = toDelete->data;
temp->left = toDelete->left;
temp->right = toDelete->right;
if(temp->left){
temp->left->parent = temp->left;
}
if(temp->right){
temp->right->parent = temp;
}
delete toDelete;
}
else if(temp->right == nullptr){ //promote left
BSTNode<T>* toDelete = temp->left;
temp->data = toDelete->data;
temp->left = toDelete->left;
temp->right = toDelete->right;
if(temp->left){
temp->left->parent = temp->left;
}
if(temp->right){
temp->right->parent = temp;
}
delete toDelete;
}
else{
BSTNode<T>* maxOfLeft = temp->left;
while(maxOfLeft){
maxOfLeft = maxOfLeft->right;
}
temp->data = maxOfLeft->data;
remove(maxOfLeft);
}
balance(temp);
}
template <class T>
void BST<T>::clear(BSTNode<T>*& root) {
if (root) {
if (root->left) {
clear(root->left);
}
if (root->right) {
clear(root->right);
}
delete root;
}
root = nullptr;
};
template <class T>
BST<T>::~BST(){
clear(root);
}
template <class T>
BST<T>* BST<T>::clone(BSTNode<T>* start){
if(!start){
return nullptr;
}
else{
return new BSTNode<T>(start->data, clone(start->left), clone(start->right));
}
}
template <class T>
BST<T>& BST<T>::operator=(const BST<T>& rhs){
if(this == &rhs){
return *this;
}
clear(root);
root = clone(rhs.root);
return *this;
}
template <class T>
void BST<T>::rotateRight(BSTNode<T> * & node){
BSTNode<T> * newParent = node->left;
node->left = newParent->right;
newParent->right = node;
node->height = max(height(node->right),height(node->left)) + 1;
newParent->height = max(height(newParent->left), node->height) + 1;
node = newParent; //set new root (we can't access newParent outside of this function!)
}
template <class T>
void BST<T>::rotateLeft(BSTNode<T> * & node){
BSTNode<T> * newParent = node->right;
node->right = newParent->left;
newParent->left = node;
node->height = max(height(node->right), height(node->left)) + 1;
newParent->height = max(height(newParent->right), node->height) + 1;
node = newParent; //set new root (we can't access newParent outside of this function!)
}
template <class T>
int BST<T>::height(BSTNode<T> *node){
if(node){
return node->height;
};
return -1;
}
template <class T>
void BST<T>::doubleRotateRight(BSTNode<T> * &node){
rotateLeft(node->left);
rotateRight(node);
}
template<class T>
void BST<T>::doubleRotateLeft(BSTNode<T> * &node){
rotateRight(node->right);
rotateLeft(node);
}
template<class T>
void BST<T>::balance(BSTNode<T> * &node){
if(!node){
return;
}
if(height(node->left) > height(node->right) + 1){
if(height(node->left->left) >= height(node->left->right)){
rotateRight(node);
}
else{
doubleRotateRight(node);
};
};
if(height(node->right) > height(node->left) + 1){
if(height(node->right->right) >= height(node->right->left)){
rotateLeft(node);
}
else{
doubleRotateLeft(node);
}
}
node->height = max(height(node->right), height(node->left)) + 1;
}
int main() {
vector<int>data = {5, 3, 4, 2, 1, 2, 6, 7};
BST<int> test;
for(int r : data){
test.insert(r, test.root);
}
test.printPreOrder(test.root,0);
cout << endl;
return 0;
}
Here is the output I am getting:
3
2
1
2
5
4
6
7
From my understanding, I should be getting the following output:
3
2
1
2
5
4
6
7
Don't use tabs.
Consider this example:
#include <iostream>
#include <iomanip>
int main() {
for (int indent=0;indent<5;++indent) {
if (indent) {
std::cout << std::setw(indent) << "\t" << "tabs" << "\n";
std::cout << std::setw(indent) << " " << "spaces" << "\n";
}
}
}
Possible output is:
tabs
spaces
tabs
spaces
tabs
spaces
tabs
spaces
I am currently writing a program to mimic the functions of a linked list. I think the the code is crashing due to memory leaks, but I cannot figure out where they might be. I have tried deleting after every new, but when I delete it causes the functions to not operate correctly. I also think I my push_back() function may not be operating correctly, because my output does not match the output of std::list. Any help would be greatly appreciated. Thanks!
When I run the program this is the output I receive:
4000
33
100
3
33
100
423
423
100
33
200
100
4000
double free or corruption (fasttop)
Aborted (core dumped)
This is the output I am expecting:
4000
33
100
3
33
100
423
423
100
423
33
200
100
Header File:
#ifndef MYLIST_H
#define MYLIST_H
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::cerr;
using std::string;
template <typename T>
class Node
{
public:
T m_element;
Node<T> *m_prev;
Node<T> *m_next;
// Helps you make a dummy/sentinel/junk node
Node(Node<T> *in_prev, Node<T> *in_next):
m_prev(in_prev), m_next(in_next){}
Node(const T &x, Node<T> *in_prev, Node<T> *in_next):
m_element(x), m_prev(in_prev), m_next(in_next){}
};
template <typename T>
class MyList
{
private:
Node<T> *m_sentinel = nullptr;
int m_size;
public:
// A list size of 0 will have 1 sentinel node.
MyList();
~MyList();
MyList<T> & operator=(const MyList<T> &source);
MyList(const MyList<T> &source);
T & front();
T & back();
void assign(int count, const T &value);
// Default list size of 0, with one sentinel node, as above in constructor
void clear();
void push_front(const T &x);
void push_back(const T &x);
void pop_back();
void pop_front();
// Simplified version that only takes one int position.
// Inserts before element at position i.
// Not exactly like std::
// You do NOT need a special case for 0-big lists (i.e., no if size == 0)
// You should be able to insert in 0 big list.
void insert(int i, const T &x);
// Removes all elements in list that are equal to value.
// You do NOT need a special case for 0-big lists (i.e., no if size == 0).
void remove(T value);
// Removes element at position i.
void erase(int i);
void reverse();
bool empty();
int size();
// Mimicking C++ iterator trickery from here down.
// You don't need to edit these two.
int begin()
{
return 0;
}
int end()
{
return size();
}
};
#include "MyList.hpp"
#endif
HPP File:
template <typename T>
MyList<T>::MyList()
{
m_sentinel = new Node<T>(nullptr, nullptr);
m_sentinel->m_next = m_sentinel;
m_sentinel->m_prev = m_sentinel;
m_size = 0;
}
template <typename T>
MyList<T>::~MyList()
{
Node<T> *temp;
for(int i = 0; i < m_size; i++)
{
temp = m_sentinel->m_prev;
delete m_sentinel->m_prev;
m_sentinel->m_prev = temp->m_prev;
}
delete m_sentinel;
m_sentinel = nullptr;
}
template <typename T>
MyList<T> & MyList<T>::operator=(const MyList<T> &source)
{
this->~MyList();
Node<T>* tempNode = new Node<T>(nullptr, nullptr);
tempNode = source.m_sentinel->m_next;
m_sentinel = new Node<T>(nullptr, nullptr);
m_sentinel->m_next = m_sentinel;
m_sentinel->m_prev = m_sentinel;
for(int i = 0; i < source.m_size; i++)
{
this->push_back(tempNode->m_element);
tempNode = tempNode->m_next;
}
}
template <typename T>
MyList<T>::MyList(const MyList<T> &source)
{
*this = source;
}
template <typename T>
T & MyList<T>::front()
{
return m_sentinel->m_next->m_element;
}
template <typename T>
T & MyList<T>::back()
{
return m_sentinel->m_prev->m_element;
}
template <typename T>
void MyList<T>::assign(int count, const T &value)
{
while(m_size)
{
this->pop_back();
}
for(int i = 0; i < count; i++)
{
this->push_back(value);
}
m_size = count;
}
// Default list size of 0, with one sentinel node, as above in constructor
template <typename T>
void MyList<T>::clear()
{
while(m_size)
{
this->pop_back();
}
}
template <typename T>
void MyList<T>::push_front(const T &x)
{
Node<T> *tempNode = new Node<T>(x, m_sentinel, m_sentinel->m_next);
m_sentinel->m_next->m_prev = tempNode;
m_sentinel->m_next = tempNode;
m_size++;
}
template <typename T>
void MyList<T>::push_back(const T &x)
{
Node<T> *tempNode = new Node<T>(x, m_sentinel->m_prev, m_sentinel);
m_sentinel->m_prev = tempNode;
tempNode->m_prev->m_next = tempNode;
m_size++;
}
template <typename T>
void MyList<T>::pop_back()
{
//edit
if(m_size != 0)
{
//check if temp node is needed
//Node<T> *tempNode = m_sentinel->m_prev;
m_sentinel->m_prev = m_sentinel->m_prev->m_prev;
delete m_sentinel->m_prev->m_next;
m_sentinel->m_prev->m_next = nullptr;
m_sentinel->m_prev->m_next = m_sentinel;
//delete tempNode;
m_size--;
}
}
template <typename T>
void MyList<T>::pop_front()
{
if(m_size != 0)
{
m_size--;
m_sentinel->m_next = m_sentinel->m_next->m_next;
delete m_sentinel->m_next->m_prev;
m_sentinel->m_next->m_prev = nullptr;
m_sentinel->m_next->m_prev = m_sentinel;
}
}
// Simplified version that only takes one int position.
// Inserts before element at position i.
// Not exactly like std::
// You do NOT need a special case for 0-big lists (i.e., no if size == 0)
// You should be able to insert in 0 big list.
template <typename T>
void MyList<T>::insert(int i, const T &x)
{
if(i >= 0 && i < m_size)
{
Node<T> *tempNode = m_sentinel;
for(int j = 0; j < i; j++)
{
tempNode = tempNode->m_next;
}
Node<T> *insertNode = new Node<T>(x, tempNode, tempNode->m_next);
tempNode->m_prev->m_next = insertNode;
tempNode->m_next->m_prev = insertNode;
m_size++;
}
}
// Removes all elements in list that are equal to value.
// You do NOT need a special case for 0-big lists (i.e., no if size == 0).
template <typename T>
void MyList<T>::remove(T value)
{
Node<T> *currentNode = m_sentinel->m_next;
Node<T> *tempNode = currentNode->m_next;
int counter = m_size;
for(int i = 0; i < counter; i++)
{
if(currentNode->m_element == value)
{
currentNode->m_prev->m_next = currentNode->m_next;
currentNode->m_next->m_prev = currentNode->m_prev;
tempNode = currentNode->m_next;
m_size--;
delete currentNode;
currentNode = nullptr;
currentNode = tempNode;
}
else
{
currentNode = currentNode->m_next;
}
}
}
// Removes element at position i.
template <typename T>
void MyList<T>::erase(int i)
{
if(i >= 0 && i < m_size)
{
Node<T> *currentNode = m_sentinel->m_next;
for(int j = 0; j < i; j++)
{
currentNode = currentNode->m_next;
}
currentNode->m_prev->m_next = currentNode->m_next;
currentNode->m_next->m_prev = currentNode ->m_prev;
delete currentNode;
currentNode = nullptr;
m_size--;
}
}
template <typename T>
void MyList<T>::reverse()
{
Node<T> *nextNode = m_sentinel->m_next;
Node<T> *prevNode = m_sentinel->m_prev;
for(int i = 0; i < (m_size/2); i++)
{
std::swap(nextNode->m_element, prevNode->m_element);
nextNode = nextNode->m_next;
prevNode = prevNode->m_prev;
}
}
template <typename T>
bool MyList<T>::empty()
{
if(m_size==0)
{
return true;
}
else
{
return false;
}
}
template <typename T>
int MyList<T>::size()
{
return m_size;
}
Driver to test if MyList function works like std::list:
#include <list>
#include "MyList.h"
int main()
{
MyList<int> l;
//std::list<int> l;
l.push_back(4000);
l.push_back(200);
l.push_back(100);
cout << l.front() << endl;
l.front() = 33;
cout << l.front() << endl;
cout << l.back() << endl;
cout << l.size() << endl;
l.push_back(4000);
l.push_back(200);
l.push_back(100);
cout << l.front() << endl;
cout << l.back() << endl;
l.push_front(423);
cout << l.front() << endl;
MyList<int> sink;
sink = l;
cout << sink.front() << endl;
cout << sink.back() << endl;
l.insert(l.begin(), 3);
l.insert(l.end(), 20);
l.pop_front();
l.reverse();
int j = 0;
for(auto i = 0; i < l.size(); i++)
{
cout << l.back() << endl;
l.pop_back();
j++;
}
return 0;
}
Thanks!
UPDATED COPY CONSTRUCTOR AND ASSIGNMENT OPERATOR
template <typename T>
MyList<T> & MyList<T>::operator=(const MyList<T> &source)
{
MyList<T> temp(source);
std::swap(temp.m_sentinel, m_sentinel);
return *this;
}
template <typename T>
MyList<T>::MyList(const MyList<T> &source)
{
Node<T> *temp = source.m_sentinel;
m_sentinel = nullptr;
while(temp != nullptr)
{
push_back(temp->m_element);
}
}
Now it gives me a segfault and crashes when calling the push_back() function. I'm lost. I've tried implementing this several different ways but every time I either get a double free or a segfault.
this is my current avltree implementation, avltree.h:
#pragma once
#include <math.h>
#include <algorithm>
#include <iostream>
namespace avltree {
template <class T>
struct node {
static node * null_node;
node *left = null_node;
node *right = null_node;
node *parent = null_node;
int height = 0;
T value;
node(T value) :value(value) {}
node(T value, int height) :height(height), value(value) {}
};
template <class T>
node<T>* node<T>::null_node = new node(0, -1);
template <class T>
struct avltree {
public:
node<T> *root;
avltree(T value);
node<T> *insert(T value);
void print(void);
void print_with_height(void);
private:
node<T> *insert(T value, node<T> *x);
node<T> *left_rotate(node<T> *x);
node<T> *right_rotate(node<T> *x);
void retrace(node<T> *n);
void update_root();
void print(node<T> *n);
void print(node<T> *n, int depth);
void update_height(node<T> *n);
};
template <class T>
avltree<T>::avltree(T value) :root(new node<T>(value)) { }
template <class T>
node<T> *avltree<T>::insert(T value) {
auto n = insert(value, root);
update_root();
return n;
}
template <class T>
void avltree<T>::retrace(node<T> *n) {
while (n != node<T>::null_node) {
update_height(n);
if (n->left->height - n->right->height > 1) {
if (n->left->left->height >= n->left->right->height) {
right_rotate(n);
}
else {
left_rotate(n);
right_rotate(n);
}
}
else
if (n->right->height - n->left->height > 1) {
if (n->right->right->height >= n->right->left->height) {
left_rotate(n);
}
else {
right_rotate(n);
left_rotate(n);
}
}
n = n->parent;
}
}
template <class T>
node<T> *avltree<T>::insert(T value, node<T> *n) {
if (n->value > value) {
if (n->left != node<T>::null_node) {
n->left->height++;
insert(value, n->left);
}
else {
auto new_node = new node<T>(value);
n->left = new_node;
new_node->parent = n;
retrace(n);
return new_node;
}
}
if (n->value < value) {
if (n->right != node<T>::null_node) {
n->right->height++;
insert(value, n->right);
}
else {
auto new_node = new node<T>(value);
n->right = new_node;
new_node->parent = n;
retrace(n);
return new_node;
}
}
update_height(n);
return n;
}
template <class T>
node<T> *avltree<T>::left_rotate(node<T> *x) {
node<T> *y = x->right;
if (y == node<T>::null_node) {
return node<T>::null_node;
}
y->parent = x->parent;
if (x->parent->right == x) {
x->parent->right = y;
}
else {
x->parent->left = y;
}
y->left = x;
x->parent = y;
x->right = y->left;
x->right->parent = x;
update_height(x);
update_height(y);
return x;
}
template <class T>
node<T> *avltree<T>::right_rotate(node<T> *x) {
node<T> *y = x->left;
if (y == node<T>::null_node) {
return node<T>::null_node;
}
y->parent = x->parent;
if (x->parent->right == x) {
x->parent->right = y;
}
else {
x->parent->left = y;
}
y->right = x;
x->parent = y;
x->left = y->right;
x->left->parent = x;
update_height(x);
update_height(y);
return x;
}
template <class T>
void avltree<T>::update_root() {
auto n = root, last = root;
while (n != node<T>::null_node) {
last = n;
n = n->parent;
}
root = last;
}
template <class T>
void avltree<T>::print(void) {
print(root);
cout << endl;
}
template <class T>
void avltree<T>::print(node<T> *n) {
if (n->left != node<T>::null_node) {
print(n->left);
}
cout << n->value << " ";
if (n->right != node<T>::null_node) {
print(n->right);
}
}
template <class T>
void avltree<T>::print(node<T> *n, int height) {
if (n->left != node<T>::null_node) {
print(n->left, height);
}
if (n->height == height) {
std::cout << n->value << ", ";
}
if (n->right != node<T>::null_node) {
print(n->right, height);
}
}
template <class T>
void avltree<T>::print_with_height(void) {
int height = root->height;
for (int height = root->height; height >= 0; height--) {
std::cout << "height = " << height << "\t:";
print(root, height);
std::cout << std::endl;
}
std::cout << std::endl;
}
template <class T>
void avltree<T>::update_height(node<T> *n) {
n->height = std::max(n->left->height, n->right->height) + 1;
}
}
And the following is main file:
#include "stdafx.h"
#include "avltree.h"
int main()
{
avltree::avltree<int> tree(1);
for (int i = 2; i < 128; i++) {
tree.insert(i);
}
tree.print_with_height();
return 0;
}
The problem with above code is that at iteration 124 it will generate an infinite loop inside retrace call in insert. Basically, the nodes will keep being rotated in such a way that root node cannot be reached (resulting in an infinite loop). I have checked the trivial examples (for a few nodes) and they work. Everything for a run is provided, I would appreciate if someone with experience in avltrees could take their time and run this program, perhaps finding out what goes wrong inside retrace/rotations.
Getting an infinite loop like that indicates a likely problem with your rotate functions. A quick look at left_rotate shows
y->left = x;
x->parent = y;
x->right = y->left;
What is y->left in that last assignment? x, so what you're really is x->right = x. This is clearly a problem that needs to be fixed.
A similar problem exists in right_rotate.
Hi I am trying to make a doubly linked list to store individual numbers as nodes of a doubly linked list and then add them together and print them out for a homework assignment. I am having a lot of trouble getting this to work and have traced my problem to my add node functions as they don't update the pointers correctly. For example on the AddToFront() function I can't understand how I can get the prev pointer to work and point to the node behind it.
I can't use the STL and have to implement the LL myself in case anyone is wondering. Thanks!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
using namespace std;
/////// PART A
template <class T>
class List {
private:
struct Node {
T data;
Node *next;
Node *prev;
};
Node *front, *current, *rear;
public:
List();
~List();
void AddtoFront (T newthing);
void AddtoRear (T newthing);
bool FirstItem (T & item);
bool LastItem (T & item);
bool NextItem (T & item);
bool PrevItem (T & item);
};
template <class T>
List<T>::List() {
front = NULL; current = NULL; rear = NULL;
}
template <class T>
List<T>::~List() {
}
template <class T>
void List<T>::AddtoFront (T newthing) {
if (front == NULL) {
Node *temp;
temp = new Node;
temp->data = newthing;
temp->next = front;
temp->prev = NULL;
front = temp;
} else {
Node *temp;
temp = new Node;
front->prev = temp;
temp->data = newthing;
temp->next = front;
temp->prev = NULL;
front = temp;
}
}
template <class T>
void List<T>::AddtoRear (T newthing) {
if (rear == NULL) {
Node *temp;
temp = new Node;
temp->data = newthing;
temp->prev = rear;
temp->next = NULL;
rear = temp;
} else {
Node *temp;
temp = new Node;
rear->next = temp;
temp->data = newthing;
temp->prev = rear;
temp->next = NULL;
rear = temp;
}
}
template <class T>
bool List<T>::FirstItem (T & item) {
if (front == NULL) { return false; }
current = front;
item = front->data;
return true;
}
template <class T>
bool List<T>::LastItem (T & item) {
if (rear == NULL) { return false; }
current = rear;
item = rear->data;
return true;
}
template <class T>
bool List<T>::NextItem (T & item) {
if (current != NULL) current = current->next;
if (current == NULL) { return false; }
item = current->data;
return true;
}
template <class T>
bool List<T>::PrevItem (T & item) {
if (current == NULL) { return false; }
if (current->prev != NULL) current = current->prev;
item = current->data;
return true;
}
/////// PART B
class BigNumber {
private:
//complete here...
//include here a List of integers, or shorts etc
List<int>L;
public:
//complete here...
//what methods do you need?
//e.g., ReadFromString, PrintBigNumber, AddBigNumbers
BigNumber();
~BigNumber();
void ReadFromString(char * decstring);
void PrintBigNumber();
void AddBigNumbers(BigNumber B1, BigNumber B2);
};
BigNumber::BigNumber(){
// anything here?
}
BigNumber::~BigNumber(){
//you can keep that empty
}
void BigNumber::ReadFromString (char * decstring ) {
//read a string, adding a new node per digit of the decimal string
// To translate 'digits' to integers: myinteger=decstring[index]-48
//You need to use the AddtoFront()
int temp;
for (unsigned i=0; i < strlen(decstring); ++i) {
//cin >> decstring[i];
temp = decstring[i]-48;
//L.AddtoFront(temp);
L.AddtoRear(temp);
//cout <<"Number added!" <<endl;
}
}
void BigNumber::PrintBigNumber () {
//complete here, print the list (i.e., use FirstItem() and NextItem() )
int val;
if (L.FirstItem(val)) {
cout << val;
} else {
cout << "print failed";
}
//if (L.FirstItem(val)) { cout << "true-first";} else { cout <<"false-first";};
//if (L.LastItem(val)) { cout << "true";} else { cout <<"false";};
//L.FirstItem(val);
//cout << val;
/*while (L.PrevItem(val)){
cout << val;
//cout <<"Print error!Value not here.";
}*/
}
void BigNumber::AddBigNumbers(BigNumber B1,BigNumber B2){
//complete here.
//use FirstItem(), NextItem() and AddNode()
//to add two big numbers, what do you have to do? Be careful about the carry
//Remember to add the last carry, the resulting number can have one more digit than B1 or B2
}
/////// PART C
BigNumber B1, B2, RES;
int main (int argc, char ** argv) {
//use command line arguments
if(argc!=3){printf("usage: executable number1 number2\n");exit(0);}
B1.ReadFromString(argv[1]);
B2.ReadFromString(argv[2]);
//print
cout << endl<< "Add the following numbers " << endl;
B1.PrintBigNumber();
cout << " + ";
B2.PrintBigNumber();
cout << " = " << endl;
//compute the addition
RES.AddBigNumbers(B1,B2);
//print the result
RES.PrintBigNumber();
cout << endl;
return 0;
}
EDIT: I added in a line each in AddToFront() and AddToRear(). Is this on the right track?
AddtoFront needs to also update the prev pointer for front. That is, you're currently doing
temp -> front <-> rest_of_list
where it needs to be
temp <-> front <-> rest_of_list.
Also, you might notice that the two branches of your if statement in AddtoFront are identical... :)
I am trying to create a custom Binary Search Tree, and I have everything done except for the rotate function, which seems to be not moving a node over. The rotate function only gets called when a node is searched and found, and if the node has a right child. For simplicity I will only add the functions that are used in this, to make it shorter:
#include <iostream>
using namespace std;
template <typename T>
class MRBST {
public:
MRBST();
~MRBST();
void push(const T &);
bool search(const T &);
void PrintPreorder();
private:
struct Node {
T data;
Node *left;
Node *right;
Node (const T & theData, Node *lt, Node *rt)
: data(theData), left(lt), right(rt) {}
};
Node *root;
void push(const T &, Node * &) const;
void remove(const T &, Node * &) const;
Node* findMin(Node *t) const {
if (t == NULL) {
return NULL;
}
if (t->left == NULL) {
return t;
}
return findMin(t->left);
}
void preorder(Node * &);
bool search(const T &, Node *);
Node* findNode(const T & x, Node * t) {
if (t == NULL) {
return NULL;
}
if (x < t->data) {
return findNode(x, t->left);
} else if (x > t->data) {
return findNode(x, t->right);
} else if (x == t->data) {
return t;
}
return NULL;
}
void rotate(Node *);
};
template <typename T>
void MRBST<T>::PrintPreorder() {
preorder(root);
cout << endl;
}
template <typename T>
void MRBST<T>::preorder(Node * & t) {
if (t != NULL) {
cout << t->data << endl;
preorder(t->left);
preorder(t->right);
}
}
template <typename T>
bool MRBST<T>::search(const T & x) {
if (search(x, root)) {
Node *temp = findNode(x, root);
rotate(temp);
return true;
} else {
return false;
}
}
template <typename T>
void MRBST<T>::rotate(Node * k1) {
if (k1->right == NULL) {
return;
} else {
Node *temp = k1->right;
k1->right = temp->left;
temp->left = k1;
}
}
template <typename T>
bool MRBST<T>::search(const T & x, Node *t) {
if (t == NULL) {
return false;
} else if (x < t->data) {
return search(x, t->left);
} else if (x > t->data) {
return search(x, t->right);
} else {
return true;
}
}
I have a simple testing file that just adds some numbers to the tree, and then searches, followed by a print out in Preordering.
#include <iostream>
#include "MRBST.h"
using namespace std;
int main() {
MRBST<int> binaryTree;
binaryTree.push(5);
binaryTree.push(20);
binaryTree.push(3);
binaryTree.push(3);
binaryTree.push(4);
binaryTree.push(22);
binaryTree.push(17);
binaryTree.push(18);
binaryTree.push(8);
binaryTree.push(9);
binaryTree.push(1);
binaryTree.PrintPreorder();
cout << endl;
binaryTree.search(17);
binaryTree.PrintPreorder();
cout << endl;
}
With the output looking a something like this:
5
3
1
4
20
17
8
9
18
22
5
3
1
4
20
17
8
9
22
If anyone could give some insight into what is going wrong with my rotate function, that would be a great help.
Why are you doing rotate on search? It should be read-only.
You're losing the node because of that.
Look at your rotate:
Node *temp = k1->right;
k1->right = temp->left;
temp->left = k1;
Assume k1=x has right=y and left=z, look step by step:
Node *temp = k1->right;
temp =k1->right = y, k1 = x, k1->left = z
k1->right = temp->left;
k1->right = ?, k1->left = z, temp = y
temp->left = k1;
k1->right = ?, k1->left = z, temp->left = x.
Now - where did Y go? You lost it.
Look closely at your rotate() function, step by step, to see whether it does what you want it to do or not.