I am trying to insert the Morse Code into a binary tree but my Insert() is acting up. I'm stumped as to why it's failing.
Binary tree class:
class BST
{
private:
struct Node
{
string letter;
string code;
Node *left;
Node *right;
};
Node *root;
public:
BST()
{
root = NULL;
}
void Insert(Node *&r, string letter, string code)
{
if(r == NULL)
{
r = new Node;
r->letter = letter;
r->code = code;
r->left = r->right = NULL;
}
}
void Insert(string letter, string code)
{
Node *r = root;
if(r != NULL)
{
for(int i = 0; i < code.length(); i++)
{
if(code[i] == '.') r = r->left;
else if(code[i] == '-') r = r->right;
else break;
}
Insert(r, letter, code);
}
else Insert(root, letter, code);
}
};
Main:
struct alphaTree
{
string letter;
string code;
};
alphaTree tree[] = {
{"ROOT", ""}, {"E", "."}, {"T", "-"}, {"I", ".."}, {"A", ".-"}, {"N", "-."},
{"M", "--"}, {"S", "..."}, {"U", "..-"}, {"R", ".-."}, {"W", ".--"}, {"D", "-.."},
{"K", "-.-"}, {"G", "--."}, {"O", "---"}, {"H", "...."}, {"V", "...-"}, {"F", "..-."},
{"L", ".-.."}, {"P", ".--."}, {"J", ".---"}, {"B", "-..."}, {"X", "-..-"}, {"C", "-.-."},
{"Y", "-.--"}, {"Z", "--.."}, {"Q", "--.-"}
};
for(int i = 0; i < 27; i++)
{
t.Insert(tree[i].letter, tree[i].code);
}
The first 3 elements of my tree[] array get inserted but the program crashes when trying to insert "I".
When you insert a new node parents left and right are not being updated.
You may think parents are updated because you are passing pointer by reference, but you are passing r and not r->left or r->root which you should.
One possible solution is to use **r instead if *r in void Insert(string letter, string code)
You can change something like: (Working example here)
void Insert(string letter, string code)
{
Node **r = &root;
if(*r != NULL)
{
for(int i = 0; i < code.length(); i++)
{
assert(*r);
if(code[i] == '.') r = &((*r)->left);
else if(code[i] == '-') r = &((*r)->right);
else break;
}
Insert(*r, letter, code);
}
else Insert(root, letter, code);
}
Not related to your problem, but as side notes:
In for(int i = 0; i < code.length(); i++) you are comparing an
unsigned (length) with singed int. You should change i to
std::string::size_type.
In function void Insert(string letter, string code) you should add
assert(*r) before accessing *r children.
Your problem is here:
for(int i = 0; i < code.length(); i++)
{
if(code[i] == '.') r = r->left;
else if(code[i] == '-') r = r->right;
else break;
}
When this loop starts, you make sure r is never NULL. Howover, as there is only one element in the tree it will immediately become NULL for the next iteration when you set it to either r = r->left or r = r->right, as there is only one node in the tree and both it's left and right are NULL.
You are not checking if r becomes NULL while looping.
The sooner you get to an element with two or more symbols in the morse code, your application crashes trying to read from r when it's a NULL pointer.
You probably wants to extend your code to something more like this:
for(int i = 0; i < code.length(); i++)
{
if (code[i] == '.')
{
if (r->left)
r = r->left;
else
{
// todo: code to bind a new node to r->left
break;
}
}
else // its always either '.' or '-' so there is no need to double check the symbol here
{
if (r->right)
r = r->right;
else
{
// todo: code to bind a new node to r->right
break;
}
}
}
Now you are making sure the loop stops while r is still a valid pointer. The last valid pointer in the descending tree.
Of course you still have to write the decision to create the new node to the left or right side of this node.
Related
I have a problem with my C++ code. It says that the all the functions starting with isPerfectRec() couldn't be resolved...Why? I tried a lot of things but apparently they don't work. I have a lot of assigments like to verify if the binary search tree is perfect, to find the second largest element in a binary search tree and so on..
#include <stdio.h>
#include<iostream>
#include<stack>
template<typename T> class BinarySearchTree {
public:
BinarySearchTree<T> *root, *left_son, *right_son, *parent;
T *pinfo;
BinarySearchTree() {
left_son = right_son = NULL;
root = this;
pinfo = NULL;
}
void setInfo(T info) {
pinfo = new T;
*pinfo = info;
}
void insert(T x) {
if (pinfo == NULL)
setInfo(x);
else
insert_rec(x);
}
bool isPerfectRec(BinarySearchTree *root, int d, int level = 0)
{
// An empty tree is perfect
if (*root == NULL)
return true;
// If leaf node, then its depth must be same as
// depth of all other leaves.
if (*root->left_son == NULL && root->*right_son == NULL)
return (d == level+1);
// If internal node and one child is empty
if (root->*left_son == NULL || root->*right_son == NULL)
return false;
// Left and right subtrees must be perfect.
return isPerfectRec(root->*left_son, d, level+1) &&
isPerfectRec(root->*right_son, d, level+1);
}
// Wrapper over isPerfectRec()
bool isPerfect(BinarySearchTree *root)
{
int d = findADepth(root);
return isPerfectRec(root, d);
}
int findADepth(BinarySearchTree *node)
{
int d = 0;
while (node != NULL)
{
d++;
node = node->left_son;
}
return d;
}
// A function to find 2nd largest element in a given tree.
void secondLargestUtil(BinarySearchTree *root, int &c)
{
// Base cases, the second condition is important to
// avoid unnecessary recursive calls
if (root == NULL || c >= 2)
return;
// Follow reverse inorder traversal so that the
// largest element is visited first
secondLargestUtil(root->right_son, c);
// Increment count of visited nodes
c++;
// If c becomes k now, then this is the 2nd largest
if (c == 2)
{
std::cout << "2nd largest element is "
<< root->pinfo;
printf("\n___\n");
return;
}
// Recur for left subtree
secondLargestUtil(root->left_son, c);
}
void secondLargest(BinarySearchTree *root)
{
// Initialize count of nodes visited as 0
int c = 0;
// Note that c is passed by reference
secondLargestUtil(root, c);
}
bool hasOnlyOneChild(int pre[], int size)
{
int nextDiff, lastDiff;
for (int i=0; i<size-1; i++)
{
nextDiff = pre[i] - pre[i+1];
lastDiff = pre[i] - pre[size-1];
if (nextDiff*lastDiff < 0)
return false;;
}
return true;
}
BinarySearchTree * readListInter(){
BinarySearchTree* root = NULL;//returning object
BinarySearchTree* temp;
BinarySearchTree* input;//new node to add
int x;
std::cout << "enter number (>0 to stop): ";
std::cin >> x;
while(x>=0){
input = BinarySearchTree(x);
if(root == NULL){//if root is empty
root = input;
temp = root;//temp is use to store value for compare
}
else{
temp = root; //for each new addition, must start at root to find correct spot
while(input != NULL){
if( x < temp->pinfo){//if smaller x to add to left
if(temp->left_son == NULL){//left is empty
temp->left_son = input;
input = NULL;//new node added, exit the loop
}
else{//if not empty set temp to subtree
temp = temp->left_son;//need to move left from the current position
}
}
else{//otherwise x add to right
if(temp->right_son == NULL){//right is empty
temp->right_son = input;
input = NULL;//new node added, exit the loop
}
else{
temp = temp->right_son;//need to move right from the current position
}
}
}
}
std::cin >> x;
}
return root;
}
};
int main() {
BinarySearchTree<int> *r = new BinarySearchTree<int>;
BinarySearchTree<int> *r1 = new BinarySearchTree<int>;
BinarySearchTree<int> *p = new BinarySearchTree<int>;
p = readListInter();
r->insert(6);
r->insert(8);
r->insert(1);
r->insert(9);
r->insert(10);
r->insert(4);
r->insert(13);
r->insert(12);
printf("\n___\n");
r1->insert(6);
r1->insert(8);
r1->insert(1);
r1->insert(9);
r1->insert(10);
r1->insert(4);
r1->insert(13);
r1->insert(12);
printf("\n___\n");
r->isPerfect(r);
int pre[] = {8, 3, 5, 7, 6};
int size = sizeof(pre)/sizeof(pre[0]);
if (hasOnlyOneChild(pre, size) == true )
printf("Yes");
else
printf("No");
s
return 0;
}
I think you need to write BinarySearchTree<T> instead of BinarySearchTree as a datatype in those functions.
I have to create binary tree in which nodes store a char value. The task is to find the largest lexicographically root to leaf path created by these chars.
The given input should be a string where first char is value to store and after space there are hints in which node it is stored. L means left node, R of course right one.
The output should be a found string and number of chars given in input (not whitespaces).
This is my code. I'm pretty sure that mistake is in rootToLeafPath(), because I've already checked method which creates tree. I'm giving you also, print method if you want to see all paths.
#include <stdio.h>
#include <iostream>
#include <string.h>
int allCharCounter = 0;
char oldest_word[65];
struct Tree_node{
struct Tree_node *right;
struct Tree_node *left;
char value;
};
Tree_node* getTree(struct Tree_node* root){
char c = getchar();
char edge = c;
Tree_node* korzen = new Tree_node();
if(root == NULL){
root = new Tree_node();
}
korzen = root;
while(!feof(stdin)){
c = getchar();
if(c == 82){//R
allCharCounter++;
if(root->right == NULL){
root->right = new Tree_node();
}
root = root->right;
}else if(c == 76){//L
allCharCounter++;
if(root->left == NULL){
root->left = new Tree_node();
}
root = root->left;
}else if(c > 96 && c < 123){
allCharCounter++;
root->value = edge;
root = korzen;
edge = c;
}
}
root->value = edge;
root = korzen;
return root;
}
void printPath(char *path, int length){
int i;
for(i = 0; i <= length; i++){
printf("%c ", path[i]);
}
printf("\n");
}
void rootToLeafPath(struct Tree_node *nodeptr, char *current_path, int index){
if(nodeptr != NULL){
current_path[index] = nodeptr->value;
if(nodeptr->left == NULL && nodeptr->right == NULL){
if(strcmp(oldest_word,current_path)< 0){
//memset(oldest_word,0,sizeof(oldest_word));
strncpy(oldest_word, current_path, 65);
}
//printPath(current_path, index);
}
rootToLeafPath(nodeptr->left, current_path,index+1);
rootToLeafPath(nodeptr->right, current_path,index+1);
}
}
int main(){
struct Tree_node* root = NULL;
struct Tree_node* test = NULL;
root = getTree(root);
char current_path [65] ={};
rootToLeafPath(root, current_path,0);
std::cout<< oldest_word;
fprintf(stdout,"\n%d", allCharCounter+1); //-> ok
}
So for input:
s LR
z LRR
m RR
p LRLRL
k
w LRL
a LL
t L
h R
j LRLR
a LRRR
The output should be:
ktsza
38
But my code creates:
ktszap
38
I thought maybe I need to clear oldest_word before giving it a new value, but didn't work. For me it looks like it remembers longer value which was before. In this example, 'ktswjp' was the word in array before, but then it found new one which was 'ktsza', but the 'p' stayed.
Appreciate any help.
In rootToLeafPath, you assign a value to current_path[index] = nodeptr->value; to store the next character. When you're done with that character, you don't clear it out so it stays in the buffer, resulting in it appearing at the end of strings that should be shorter.
The solution is to reset it to the nul character before you return, with
current_path[index] = '\0';
after your recursive calls to rootToLeafPath are done.
I'm working a project for class that requires creating a binary search tree of criminal names with up to 8 attributes per criminal.
I set up a string array att[] that will read in the attributes for each criminal, and then be passed to my BSTInsert class function. Through debugging I can see that the array is correct when it's just in the setupTree function. Once it's passed to BSTInsert, instead of having each string it only has one string, and on top of that nothing is copied from the array to the node in the tree.
Can anyone tell me what I'm doing wrong?
Here's my code for setting up the tree:
void setupTree(BST& criminals)
{
ifstream fin("criminals.txt");
string temp;
fin >> temp;
//FINISHED means it has all the criminals
while (temp != "FINISHED")
{
//SUSPECT lets it know to read in a new name and new attributes
if (temp == "SUSPECT")
{
string name;
string att[8];
int count = 0;
fin >> temp;
//if there is a false "suspect" line, quit
if (temp == "FINISHED") return;
name = temp;
fin >> temp;
while (temp != "SUSPECT" && temp != "FINISHED")
{
att[count] = temp;
count++;
fin >> temp;
}
criminals.BSTInsert(name, att, count);
}
}
}
Here's my class function for inserting a node:
bool BST::BSTInsert(treetype name, treetype att[], int count)
{
//gets the memory for the node. If unable, returns fail.
node* newNode = new node;
if (newNode == NULL)
{
return false;
}
newNode->count = 0;
//initializes the node with the given information to place
for (int i = 0; i < count; i++)
{
newNode->att[newNode->count] = att[count];
newNode->count++;
}
newNode->name = name;
newNode->left = newNode->right = NULL;
//if the tree is empty, creates this node as the root
if (root == NULL)
{
root = newNode;
root->parent = NULL;
}
else
{
//the tree is not empty, so it will use the parent to insert the node
node* current = root;
node* parent = NULL;
//finds the insertion spot
while (current != NULL)
{
parent = current;
if (name <= current->name)
{
current = current->left;
}
else
{
current = current->right;
}
}
//inserts the new node onto the correct side of the parent
if (name <= parent->name)
{
parent->left = newNode;
}
else
{
parent->right = newNode;
}
newNode->parent = parent;
}
return true;
treetype att[] doesn't pass an array, it passes a pointer to an array - it decays to treetype att*.
That said, your problem is here:
for (int i = 0; i < count; i++)
{
newNode->att[newNode->count] = att[count];
newNode->count++;
}
This copies the wrong element of att (beyond the end of the array) into every att in newNode. What you meant was
for (int i = 0; i < count; i++)
{
newNode->att[newNode->count] = att[newNode->count];
newNode->count++;
}
I've been working on the input from file and think I have the logic right, but my nodes aren't linking properly. I'm able to set the root correctly and the program is able to walk through the string and load the nodes properly, just not link them. Can anyone help me sort through my logic and figure out the problem?
The input string is (A (B (D G) E) (C () F)).
struct node
{
string data;
node* left;
node* right;
};
void tree::build_tree(string &input, int i, node *n)
{
if(i > input.length())
return *n = NULL;
if(input[i] == '(')
{
string data; string temp;
int prev_i = i;
//get_data retrieves the identifier
data = get_data(input, temp, i+1);
//get_data_num retrieves the new position in the string
i = get_data_num(input, temp, i+1);
if(input[prev_i] == '('&& input[i] == ')')
{
i += 1;
*n = NULL;
}
else
{
// Allocate a new node and assign the data and
// set the pointer to the branches to null
*n = new node;
(*n)->data = data;
(*n)->left = NULL;
(*n)->right = NULL;
if(input[i] == ' ')
{i += 1; }
//Pass the address of the nodes
build_tree(input, i, &(*n)->left);
build_tree(input, i, &(*n)->right);
}
}
else if(isalnum(input[i]) || input[i] == '_' || input[i] == '-')
{
string data; string temp;
int prev_i = i;
data = get_data(input, temp, i);
i = get_data_num(input, temp, i);
if(input[prev_i] == '('&& input[i] == ')')
{
i += 1;
*n = NULL;
}
else
{
*n = new node;
(*n)->data = data;
(*n)->left = NULL;
(*n)->right = NULL;
if(input[i] == ' ')
{ i += 1; }
build_tree(input, i, &((*n)->left));
build_tree(input, i, &((*n)->right));
}
}
else if(input[i] == ' ')
{
i += 1;
}
else if(input[i] == ')')
{
i += 1;
*n = NULL;
}
else
{
cout << "The input tree is not in the correct format!" << endl;
}
}
I believe the issue is that you are not setting the value of the left and right pointers. You are passing the values of the pointers. You need to pass a pointer to the pointers (left and right), to set the value in the structure. The other alternative is to use references instead of pointers.
Here are the modifications I came up with for the code you supplied:
Changed the call to build_tree for the node argument be a pointer to
a pointer.
Changed assignments of values accordingly.
Changed call to build_tree to pass the address of left and right (to
get a pointer to a pointer).
Remove the assignment/conditions to set the root_node. So when you
call the build_tree you need to pass in the address of root. This
will set the node just like all of the nodes which follow, so it does not need
to be a special case.
Added assignment of NULL for left and right in case there is not a
branch (may not need to do this, but I feel it is good practice to
make sure all items have some initial values).
void tree::build_tree(string &input, int i, node **n)
{
if(input[i] == '(')
{
string data; string temp;
//get_data retrieves the identifier
data = get_data(input, temp, i+1);
//get_data_num retrieves the new position in the string
i = get_data_num(input, temp, i+1);
// Allocate a new node and assign the data and
// set the pointer to the branches to null
*n = new node;
(*n)->data = data;
(*n)->left = NULL;
(*n)->right = NULL;
if(input[i] == ' ')
{ i += 1; }
// Pass the address of the nodes
build_tree(input, i, &(*n)->left);
build_tree(input, i, &(*n)->right);
}
else if(isalnum(input[i]) || input[i] == '_' || input[i] == '-')
{
string data; string temp;
data = get_data(input, temp, i);
i = get_data_num(input, temp, i);
*n = new node;
(*n)->data = data;
(*n)->left = NULL;
(*n)->right = NULL;
if(input[i+1] == ' ')
{ i += 1; }
build_tree(input, i, &((*n)->left));
build_tree(input, i, &((*n)->right));
}
else if(input[i] == ' ')
{
i += 1;
}
else if(input[i] == ')')
{
*n = NULL;
}
else
{
cout << "The input tree is not in the correct format!" << endl;
}
}
Then for the initial call,
build_tree(testString,0,&root);
Since the get_data and get_data_num were not supplied, I was not able to test the changes which were made.
so heres my header and cpp file.
template<typename T> struct TreeNode
{
TreeNode(const T& value, TreeNode<T>* left = NULL, TreeNode<T>* right = NULL)
{
Value = value;
Left = left;
Right = right;
}
T Value;
TreeNode<T>* Left;
TreeNode<T>* Right;
bool IsLeaf() const
{
return Left == NULL && Right == NULL;
}
};
and now my cpp file
#include "TreeNode.h"
#include <iostream>
#include <string>
using namespace std;
float ValueOf(TreeNode<char>* root);
float ValueOf(TreeNode<char>* root)
{
if (root->IsLeaf())
return root->Value - '0';
float expressionValueL = ValueOf(root->Left);
float expressionValueR = ValueOf(root->Right);
if (root->Value == '+')
return expressionValueL+expressionValueR;
else if (root->Value == '-')
return expressionValueL-expressionValueR;
else if (root->Value == '*')
return expressionValueL*expressionValueR;
else if (root->Value == '/')
return expressionValueL/expressionValueR;
}
void main ()
{
TreeNode<char>* treeRoot = nullptr;
TreeNode<char>* currentNode = treeRoot;
string expr;
cout<<"please input expression to be tested:";
getline (cin, expr);
cout<<endl;
int size = expr.size();
for (int i=0; i<size; i++)
{
char test = expr[i];
if ((test=='1')||(test=='0')||(test=='2')||(test=='3')||(test=='4')||(test=='5')||(test=='6')||(test=='7')||(test=='8')||(test=='9'))
{
TreeNode<char> newLeaf = (expr[i]);
if (currentNode == nullptr)
{
treeRoot=&newLeaf;
currentNode = &newLeaf;
}
else
currentNode->Right = &newLeaf;
}
else if ((expr[i]=='+')||(expr[i]=='-'))
{
TreeNode<char> newRoot = test;
newRoot.Left = treeRoot;
treeRoot = &newRoot;
currentNode = &newRoot;
}
else if (((expr[i]=='*')||(expr[i]=='/'))&&(currentNode->Right==nullptr))
{
TreeNode<char> newRoot = test;
newRoot.Left = treeRoot;
treeRoot = &newRoot;
currentNode = &newRoot;
}
else if (((expr[i]=='*')||(expr[i]=='/'))&&(currentNode->Right!=nullptr))
{
TreeNode<char> newChild = test;
newChild.Left = currentNode->Right;
currentNode->Right = &newChild;
currentNode = &newChild;
}
}
cout<<ValueOf(treeRoot)<<endl;
system("pause");
}
the problem is that every time i run it, and i input something like 3*4-2, all of the digits in the tree gets overwritten to what the last digit inserted was, so its interpreted as 2*2-2 and gives me 2 as an answer, instead of 10 can anyone tell me what my problem is? thanks =).
btw this program assumes wellformed expressions and single digit numbers.
TreeNode<char> newLeaf = (expr[i]); creates an object on stack - it's invalidated when you leave the enclosing scope. You should not store pointers to such objects.
Use TreeNode<char> * newLeaf = new TreeNode<char>(expr[i]); - and corresponding for any other node that you assign to ->Right and ->Left - aka need to keep alive beyond the scope where you create them.
As mentioned by Erik,
TreeNode<char> newLeaf = (expr[i]);
is a local stack variable; instead of do following:
TreeNode<char> *newLeaf = new TreeNode<char>(expr[i]);
And then assign to proper leg. Also, below condition,
if ((test=='1')||(test=='0')||(test=='2')||(test=='3')||(test=='4')||(test=='5')||(test=='6')||(test=='7')||(test=='8')||(test=='9'))
Can be squeezed to,
if(test >= '0' && test <= '9')
In the same way, you can also co-relate the last two else if() statements for better code