Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

In this lab, you will be implementing AVL trees. The point is just to fully impl

ID: 3911981 • Letter: I

Question

In this lab, you will be implementing AVL trees. The point is just to fully implement an AVL tree that accepts a Data class as its type (it is a template class). The Data class DoubleData defines a method to compare to other DoubleData objects, which you should use when comparing those objects. AVLTree is a template class, so all the code is going in the header file again.

---------------------------------------------------------------------------------------------

#ifndef AVLTREE_H

#define AVLTREE_H

#include "Data.h"

template <typename T>

class AVLTree {

private:

struct AVLNode {

AVLNode* leftChild;

AVLNode* rightChild;

T* data;

int duplicates; // used if there are duplicate values in the tree

// instead of changing rotation rules

int height;

AVLNode () : // default constructor

leftChild {nullptr},

rightChild {nullptr},

data {nullptr},

duplicates {0},

height {0} {};

~AVLNode () = default;

AVLNode (T& value) :

leftChild {nullptr},

rightChild {nullptr},

duplicates {0},

height {0} {

data = new T{value};

};

AVLNode (T&& value):

leftChild {nullptr},

rightChild {nullptr},

duplicates {0},

height {0} {

data = new T{value};

}

AVLNode (T& value, AVLNode* left, AVLNode* right) :

leftChild {left},

rightChild {right},

duplicates {0},

height {0} {

data = new T{value};

};

AVLNode (T&& value, AVLNode* left, AVLNode* right) :

leftChild {left},

rightChild {right},

duplicates {0},

height {0} {

data = new T{value};

}

};

AVLNode* root;

// accessors -------------------------------------------------------------

// will return the height of a given AVLNode*. Look at the definition for

// height. -1 if the tree is empty, or max height of children + 1.

// Must use recursion, since it counts leaves-up and we start traversals

// at root.

int getHeight(AVLNode* node) const {

// CODE HERE

return 0; // PLACEHOLDER FOR COMPILATION

}

// returns the depth from the current subtree (node is subroot)

// must use recursion.

int getDepthAux(const T& value, AVLNode* node) const {

// CODE HERE

return 0; // PLACEHOLDER

}

// driver function for getDepthAux(T&,AVLNode*), which does the recursion.

// getDepth(T&,AVLNode*) does an extra check for node not found in tree.

int getDepth(const T& value, AVLNode* node) const {

if (!findNode(value, node)){

return -1; // return -1 if node does not exist in tree

} else {

return getDepthAux(value, node);

}

}

// returns the AVLNode* that points to the node containing the

// parameter value in its data member.

// the node parameter is the root of the current subtree.

// Must use recursion.

AVLNode* findNode(const T& value, AVLNode* node) const {

// CODE HERE

return nullptr; // PLACEHOLDER

}

// returns the AVLNode* that points to the node containing the

// parameter value in its data member.

AVLNode* findNode(const T& value) const {

return findNode(value, root);

}

// method to clone a subtree and return it.

AVLNode* clone (AVLNode* node) const {

if (!node){

return nullptr;

} else {

AVLNode* temp = new AVLNode (*node->data,

clone(node->leftChild),

clone(node->rightChild));

temp->duplicates = node->duplicates;

temp->height = getHeight(node);

return temp;

}

}

// Possibly several functions to be used by printing traversal functions

// (below). These functions may need to know what the last leaf in a

// subtree is to print nicely (by my standards, anyway).

// CODE HERE

// should print the tree in a preorder traversal

void printPreorder(AVLNode* node) const {

// CODE HERE

}

// should print the tree in an inorder traversal

void printInorder(AVLNode* node) const {

// CODE HERE

}

// should print the tree in a postorder traversal

void printPostorder(AVLNode* node) const {

// CODE HERE

}

// mutators ------------------------------------------------------------

// inserts a new value into the given subtree with node as the root.

// If the value already exists, just incrememnt the duplicates counter.

// Else, create memory for it and place pointers appropriately.

// Must use recursion.

void insert(T& value, AVLNode* & node){

// CODE HERE

}

// will balance the tree with node as the offending root, like

// alpha in our class discussions. Should call onf of the rotate functions.

// Don't forget to set the height at the end!

void balance(AVLNode* & node){

// CODE HERE

}

// Rotate binary tree node with left child, i.e. a single rotation

// for case 1. Update the heights, and set new root.

void rotateLeft(AVLNode*& node){

// CODE HERE

}

// Rotate binary tree node with right child, i.e. a single rotation

// for case 4. Update the heights, and set new root.

void rotateRight(AVLNode*& node){

// CODE HERE

}

// Double rotate binary tree node: first left child with its right

// child, then subroot with its new left child (was grandchild previously).

// I.e. rotation case 2. Update the heights, and set new root.

void rotateDoubleLeft(AVLNode*& node){

// CODE HERE

}

// Double rotate binary tree node: first left child with its right

// child, then subroot with its new left child (was grandchild previously).

// I.e. rotation case 2. Update the heights, and set new root.

void rotateDoubleRight(AVLNode*& node){

// CODE HERE

}

// removes a given value from the tree. If the Node containing the value

// has duplicates, decrement the duplicates. Else deallocate the memory and

// recursively call remove to fix the tree, as discussed in class.

void remove(T& value, AVLNode*& node){

// CODE HERE

}

// private function to recursively empty

void empty(AVLNode* node){

// CODE HERE

}

public:

AVLTree():

root {nullptr} {};

~AVLTree(){

empty();

}

// copy constructor: should copy all of the data from the other tree

// into this tree.

AVLTree(const AVLTree<T>& other){

root = clone(other.root);

}

// accessors --------------------------------------------------------

int getHeight() const {

return getHeight(root);

}

// searches the tree for a value. If it is found, the height

// is returned. If not, then -1 is returned.

int getHeight(const T& value) const {

AVLNode* node = findNode(value);

return node ? node->height : -1; // ternary operator

}

// returns the depth for the whole tree. should be 0 if the

// tree is nonempty, and -1 if it is empty.

int getDepth() const {

if (root){

return 0;

} else {

return -1;

}

}

// returns the depth for a given value.

// should be -1 if tree is empty, or the number of steps

// down from root node if not.

int getDepth(T& value) const {

if (!root){

return -1;

} else {

return getDepth(value, root);

}

}

// will return the balance factor of a value in the tree.

// if the value does not exist, return -128 (the lowest value for

// a 1-byte char). If it does exist, return the balance factor.

char getBalanceFactor(T& value) const {

// CODE HERE

return 0; // PLACEHOLDER FOR COMPILATION

}

// driver function to call the private preorder traversal

void printPreorder(){

std::cout << "[";

printPreorder(root);

std::cout << "]" << std::endl;

}

// driver function to call the private preorder traversal

void printInorder(){

std::cout << "[";

printInorder(root);

std::cout << "]" << std::endl;

}

// driver function to call the private preorder traversal

void printPostorder(){

std::cout << "[";

printPostorder(root);

std::cout << "]" << std::endl;

}

// should print the tree in a level-order traversal (NOT driver function)

void printLevelOrder(){

// CODE HERE

}

// mutators -----------------------------------------------------

// call private balance(1) on tree

void balance(){

balance(root);

}

// calls private remove function to remove starting at root

void remove(T& value){

remove(value, root);

}

void remove(T&& value){

T temp = T{value};

remove(temp);

}

// driver function for emptying the tree, since there is no public access

// to root of tree (as many other functions do in this file)

void empty(){

// CODE HERE

}

// calls private insert function to insert starting at root

void insert(T& value){

insert(value, root);

}

void insert(T&& value){

T temp = T{value};

insert(temp);

}

};

#endif

---------------------------------------------------------------------------

Data.h

#ifndef DATA_H
#define DATA_H

#include <iostream>


class DoubleData {
private:
double* data;

public:
DoubleData();
DoubleData(double a);
~DoubleData() = default;

// accessors -------------------------------
double getData() const ;
double compare(DoubleData& other) const;
friend std::ostream& operator<<(std::ostream& os, const DoubleData& dd);

// mutators --------------------------------
void setData(double _data);
};


#endif

Explanation / Answer

#ifndef AVLTREE_H

#define AVLTREE_H

#include "Data.h"

template <typename T>

class AVLTree {

private:

struct AVLNode {

AVLNode* leftChild;

AVLNode* rightChild;

T* data;

int duplicates; // used if there are duplicate values in the tree

// instead of changing rotation rules

int height;

AVLNode () : // default constructor

leftChild {nullptr},

rightChild {nullptr},

data {nullptr},

duplicates {0},

height {0} {};

~AVLNode () = default;

AVLNode (T& value) :

leftChild {nullptr},

rightChild {nullptr},

duplicates {0},

height {0} {

data = new T{value};

};

AVLNode (T&& value):

leftChild {nullptr},

rightChild {nullptr},

duplicates {0},

height {0} {

data = new T{value};

}

AVLNode (T& value, AVLNode* left, AVLNode* right) :

leftChild {left},

rightChild {right},

duplicates {0},

height {0} {

data = new T{value};

};

AVLNode (T&& value, AVLNode* left, AVLNode* right) :

leftChild {left},

rightChild {right},

duplicates {0},

height {0} {

data = new T{value};

}

};

AVLNode* root;

// accessors -------------------------------------------------------------

// will return the height of a given AVLNode*. Look at the definition for

// height. -1 if the tree is empty, or max height of children + 1.

// Must use recursion, since it counts leaves-up and we start traversals

// at root.

int getHeight(AVLNode* node) const {

// CODE HERE

int height_left = 0;

int height_right = 0;

if( node->left ) height_left = avl_node_height( node->left );

if( node->right ) height_right = avl_node_height( node->right );

return height_right > height_left ? ++height_right : ++height_left;

return 0; // PLACEHOLDER FOR COMPILATION

}

// returns the depth from the current subtree (node is subroot)

// must use recursion.

int getDepthAux( AVLNode* node,int depth) const {

// CODE HERE

int i = 0;

if( node->left ) avl_traverse_node_dfs( node->left, depth + 2 );

for( i = 0; i < depth; i++ ) putchar( ' ' );

printf( "%d: %d ", node->value, avl_balance_factor( node ) );

if( node->right ) avl_traverse_node_dfs( node->right, depth + 2 );

return 0; // PLACEHOLDER

}

// driver function for getDepthAux(T&,AVLNode*), which does the recursion.

// getDepth(T&,AVLNode*) does an extra check for node not found in tree.

int getDepth(const T& value, AVLNode* node) const {

if (!findNode(value, node)){

return -1; // return -1 if node does not exist in tree

} else {

return getDepthAux(value, node);

}

}

// returns the AVLNode* that points to the node containing the

// parameter value in its data member.

// the node parameter is the root of the current subtree.

// Must use recursion.

AVLNode* findNode(const T& value, AVLNode* node) const {

// CODE HERE

AVLNode* current = node->root;

while( current && current->value != value ) {

if( value > current->value )

current = current->right;

else

current = current->left;

return nullptr; // PLACEHOLDER

}

// returns the AVLNode* that points to the node containing the

// parameter value in its data member.

AVLNode* findNode(const T& value) const {

return findNode(value, root);

}

// method to clone a subtree and return it.

AVLNode* clone (AVLNode* node) const {

if (!node){

return nullptr;

} else {

AVLNode* temp = new AVLNode (*node->data,

clone(node->leftChild),

clone(node->rightChild));

temp->duplicates = node->duplicates;

temp->height = getHeight(node);

return temp;

}

}

// Possibly several functions to be used by printing traversal functions

// (below). These functions may need to know what the last leaf in a

// subtree is to print nicely (by my standards, anyway).

// CODE HERE

// should print the tree in a preorder traversal

void printPreorder(AVLNode* node) const {

// CODE HERE

if (node == NULL)

return;

/* first print data of node */

cout << node->data << " ";

/* then recur on left sutree */

printPreorder(node->left);

/* now recur on right subtree */

printPreorder(node->right);

}

// should print the tree in an inorder traversal

void printInorder(AVLNode* node) const {

// CODE HERE

if (node == NULL)

return;

/* first recur on left child */

printInorder(node->left);

/* then print the data of node */

cout << node->data << " ";

/* now recur on right child */

printInorder(node->right);

}

// should print the tree in a postorder traversal

void printPostorder(AVLNode* node) const {

// CODE HERE

if (node == NULL)

return;

// first recur on left subtree

printPostorder(node->left);

// then recur on right subtree

printPostorder(node->right);

// now deal with the node

cout << node->data << " ";

}

// mutators ------------------------------------------------------------

// inserts a new value into the given subtree with node as the root.

// If the value already exists, just incrememnt the duplicates counter.

// Else, create memory for it and place pointers appropriately.

// Must use recursion.

void insert(T& value, AVLNode* node){

// CODE HERE

AVLNode *node = NULL;

AVLNode* next = NULL;

AVLNode* last = NULL;

if( tree->root == NULL ) {

node = avl_create_node();

node->value = value;

tree->root = node;

/* Okay. We have a root already. Where do we put this? */

}

else

{

next = tree->root;

while( next != NULL ) {

last = next;

if( value < next->value ) {

next = next->left;

} else if( value > next->value ) {

next = next->right;

/* Have we already inserted this node? */

} else if( value == next->value ) {

/* This shouldn't happen. */

}

}

node = avl_create_node();

node->value = value;

if( value < last->value ) last->left = node;

if( value > last->value ) last->right = node;

}

avl_balance( node);

}

// will balance the tree with node as the offending root, like

// alpha in our class discussions. Should call onf of the rotate functions.

// Don't forget to set the height at the end!

void balance(AVLNode* & node){

// CODE HERE

avl_balance_node( (AVLNode* node );

avl_balance( (AVLNode* node );

}

/* Balance a given node */

(AVLNode* avl_balance_node( (AVLNode* node ) {

(AVLNode* newroot = NULL;

/* Balance our children, if they exist. */

if( node->left )

node->left = avl_balance_node( node->left );

if( node->right )

node->right = avl_balance_node( node->right );

int bf = avl_balance_factor( node );

if( bf >= 2 ) {

/* Left Heavy */

if( avl_balance_factor( node->left ) <= -1 )

newroot = avl_rotate_leftright( node );

else

newroot = avl_rotate_leftleft( node );

} else if( bf <= -2 ) {

/* Right Heavy */

if( avl_balance_factor( node->right ) >= 1 )

newroot = avl_rotate_rightleft( node );

else

newroot = avl_rotate_rightright( node );

} else {

/* This node is balanced -- no change. */

newroot = node;

}

return( newroot );

}

/* Balance a given tree */

void avl_balance( (AVLNode* node ) {

(AVLNode* newroot = NULL;

newroot = avl_balance_node( tree->root );

if( newroot != tree->root ) {

tree->root = newroot;

}

}

// Rotate binary tree node with left child, i.e. a single rotation

// for case 1. Update the heights, and set new root.

void rotateLeft(AVLNode*& node){

// CODE HERE

}

// Rotate binary tree node with right child, i.e. a single rotation

// for case 4. Update the heights, and set new root.

void rotateRight(AVLNode*& node){

// CODE HERE

}

// Double rotate binary tree node: first left child with its right

// child, then subroot with its new left child (was grandchild previously).

// I.e. rotation case 2. Update the heights, and set new root.

void rotateDoubleLeft(AVLNode*& node){

// CODE HERE

(AVLNode* a = node;

(AVLNode* b = a->left;

a->left = b->right;

b->right = a;

return( b );

}

// Double rotate binary tree node: first left child with its right

// child, then subroot with its new left child (was grandchild previously).

// I.e. rotation case 2. Update the heights, and set new root.

void rotateDoubleRight(AVLNode*& node){

// CODE HERE

(AVLNode *a = node;

(AVLNode *b = a->right;

a->right = b->left;

b->left = a;

return( b );

}

// removes a given value from the tree. If the Node containing the value

// has duplicates, decrement the duplicates. Else deallocate the memory and

// recursively call remove to fix the tree, as discussed in class.

void remove(struct Node* root, int key){

// CODE HERE

if (root == NULL)

return root;

// If the key to be deleted is smaller than the

// root's key, then it lies in left subtree

if ( key < root->key )

root->left = deleteNode(root->left, key);

// If the key to be deleted is greater than the

// root's key, then it lies in right subtree

else if( key > root->key )

root->right = deleteNode(root->right, key);

// if key is same as root's key, then This is

// the node to be deleted

else

{

// node with only one child or no child

if( (root->left == NULL) || (root->right == NULL) )

{

struct Node *temp = root->left ? root->left :

root->right;

// No child case

if (temp == NULL)

{

temp = root;

root = NULL;

}

else // One child case

*root = *temp; // Copy the contents of

// the non-empty child

free(temp);

}

else

{

// node with two children: Get the inorder

// successor (smallest in the right subtree)

struct Node* temp = minValueNode(root->right);

// Copy the inorder successor's data to this node

root->key = temp->key;

// Delete the inorder successor

root->right = deleteNode(root->right, temp->key);

}

}

// private function to recursively empty

void empty(AVLNode* node){

// CODE HERE

}

public:

AVLTree():

root {nullptr} {};

~AVLTree(){

empty();

}

// copy constructor: should copy all of the data from the other tree

// into this tree.

AVLTree(const AVLTree<T>& other){

root = clone(other.root);

}

// accessors --------------------------------------------------------

int getHeight() const {

return getHeight(root);

}

// searches the tree for a value. If it is found, the height

// is returned. If not, then -1 is returned.

int getHeight(const T& value) const {

AVLNode* node = findNode(value);

return node ? node->height : -1; // ternary operator

}

// returns the depth for the whole tree. should be 0 if the

// tree is nonempty, and -1 if it is empty.

int getDepth() const {

if (root){

return 0;

} else {

return -1;

}

}

// returns the depth for a given value.

// should be -1 if tree is empty, or the number of steps

// down from root node if not.

int getDepth(T& value) const {

if (!root){

return -1;

} else {

return getDepth(value, root);

}

}

// will return the balance factor of a value in the tree.

// if the value does not exist, return -128 (the lowest value for

// a 1-byte char). If it does exist, return the balance factor.

// CODE HERE

double getBalanceFactor(node *root)

{

double sum = 0;

getBalFactCore(root,1,&sum);

return sum;

}

int getBalFactCore(node *root, int level, double *sum)

{

if (root==null)

return 0;

int desc = getBalFactCore(root->left,level+1,sum) + getBalFactCore(root->right,level+1,sum);

*sum += desc*1.0/level;

return desc+1;

}

return 0; // PLACEHOLDER FOR COMPILATION

}

// driver function to call the private preorder traversal

void printPreorder(){

std::cout << "[";

printPreorder(root);

std::cout << "]" << std::endl;

}

// driver function to call the private preorder traversal

void printInorder(){

std::cout << "[";

printInorder(root);

std::cout << "]" << std::endl;

}

// driver function to call the private preorder traversal

void printPostorder(){

std::cout << "[";

printPostorder(root);

std::cout << "]" << std::endl;

}

// should print the tree in a level-order traversal (NOT driver function)

void printLevelOrder(){

// CODE HERE

printLevelOrder(root);

printGivenLevel(root,level);

}

void printLevelOrder(struct node* root)

{

int h = height(root);

int i;

for (i=1; i<=h; i++)

printGivenLevel(root, i);

}

/* Print nodes at a given level */

void printGivenLevel(struct node* root, int level)

{

if (root == NULL)

return;

if (level == 1)

printf("%d ", root->data);

else if (level > 1)

{

printGivenLevel(root->left, level-1);

printGivenLevel(root->right, level-1);

}

}

// mutators -----------------------------------------------------

// call private balance(1) on tree

void balance(){

balance(root);

}

// calls private remove function to remove starting at root

void remove(T& value){

remove(value, root);

}

void remove(T&& value){

T temp = T{value};

remove(temp);

}

// driver function for emptying the tree, since there is no public access

// to root of tree (as many other functions do in this file)

void empty(){

// CODE HERE

deleteTree(node);

}

void deleteTree(struct node* node)

{

if (node == NULL) return;

/* first delete both subtrees */

deleteTree(node->left);

deleteTree(node->right);

/* then delete the node */

printf(" Deleting node: %d", node->data);

free(node);

}

// calls private insert function to insert starting at root

void insert(T& value){

insert(value, root);

}

void insert(T&& value){

T temp = T{value};

insert(temp);

}

#endif

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote