Creating a unified search function for a binary search tree - c++

I'm trying to create a search function in a binary search tree that can be used by both the insert and search functions.
I tried passing my cursor as a reference
template<class key_type, class data_type>
bool binary_tree<key_type, data_type>::internal_search(node *& cursor, key_type query) {
if (cursor == NULL) {
return false;
}else if (cursor->key == query) {
return true;
}
if (cursor->key < query) {
internal_search(cursor->left, query);
}
else {
internal_search(cursor->right, query);
}
}
Here is the insert function I'm trying to use it in
template<class key_type, class data_type>
void binary_tree<key_type, data_type>::insert(key_type key_in, data_type data_in) {
node * local_cursor = start;
if (!internal_search(local_cursor, key_in)) {
local_cursor = new node;
local_cursor->key = key_in;
local_cursor->data = data_in;
local_cursor->left = NULL;
local_cursor->right = NULL;
size++;
}
else {
std::cout << "entry already present" << std::endl;
}
}
Here is the search function I'm trying to use it in
template<class key_type, class data_type>
data_type binary_tree<key_type, data_type>::search(key_type query) {
node * local_cursor = start;
if (internal_search(local_cursor, query)) {
return local_cursor->data;
}
std::cout << "search query not found" << std::endl;
}
Neither passing through as a reference or returning as a value have worked
I don't understand why when I run this code the start pointer is always NULL when inserting a new value into the binary search tree.
I also tried rewriting the code with the internal_search function returning a node pointer but that didn't work either.
Why does start point to NULL everytime instead of the new node I assigned it to?
here's the header if that might help
#pragma once
template <class key_type, class data_type>
class binary_tree
{
private:
struct node {
key_type key;
data_type data;
node * left;
node * right;
};
node * start;
int size;
bool internal_search(node *, key_type);
void print_preorder(node * cursor = start);
void file_preorder( std::ofstream&, node *);
void file_inorder(std::ofstream&, node *);
void print_inorder_pri(node *);
void print_postorder(node *);
void file_postorder(std::ofstream&, node *);
public:
binary_tree();
void insert(key_type);
void remove();
bool is_empty();
data_type search(key_type);
void print_preorder();
void file_preorder(std::ofstream&);
void print_inorder();
void file_inorder(std::ofstream&);
void print_postorder();
void file_postorder(std::ofstream&);
void print_level();
bool load_file(std::string);
void save_file(std::string);
~binary_tree();
};

After some trivial modifications (including the one related to #Scheff's comment), I got it compiled.
However, start was effectively always equal to NULL.
I discovered that the problem was that ìnternal_search was always returning NULL, i.e.
the value of the node* before node creation and not the address of node* where to create the new node. Therefore, it was needed to replace (node* &) by (node** &).
Here is the code that seems to work, (with a main() for test) at least for the simple test searchthat was causing problem to the PO. Some work must be done to improve (e.g. a recursive insert) and complete the code (e.g. to delete the object binary_tree) but this is out of the scope of the question (fortunately!).
#include <iostream>
template <class key_type, class data_type>
class binary_tree
{
private:
struct node {
key_type key;
data_type data;
node* left = NULL;
node* right = NULL;
};
node* start = NULL;
int size = 0;
bool internal_search(node** &cursor, key_type);
//void print_preorder(node * cursor = start);
//void file_preorder( std::ofstream&, node *);
void file_inorder(std::ofstream&, node *);
void print_inorder_pri(node *);
void print_postorder(node *);
void file_postorder(std::ofstream&, node *);
public:
binary_tree() {};
void insert(key_type, data_type);
void remove();
bool is_empty();
data_type search(key_type);
//void print_preorder();
void file_preorder(std::ofstream&);
void print_inorder();
void file_inorder(std::ofstream&);
void print_postorder();
void file_postorder(std::ofstream&);
void print_level();
bool load_file(std::string);
void save_file(std::string);
void print_start () {std::cout << start << "\n";} // Added
//~binary_tree();
};
template<class key_type, class data_type>
bool binary_tree<key_type, data_type>::internal_search (node** &cursor, key_type query) {
if (*cursor == NULL) {
return false;
} else if ((*cursor)->key == query) {
return true;
}
if ((*cursor)->key < query) {
cursor = &((*cursor)->left);
return internal_search(cursor, query);
} else {
cursor = &((*cursor)->right);
return internal_search(cursor, query);
}
}
template<class key_type, class data_type>
void binary_tree<key_type, data_type>::insert(key_type key_in, data_type data_in) {
node** local_cursor = &start;
if (!internal_search(local_cursor, key_in)) {
*local_cursor = new node;
(*local_cursor)->key = key_in;
(*local_cursor)->data = data_in;
size++;
}
else {
std::cout << "entry already present" << std::endl;
}
}
template<class key_type, class data_type>
data_type binary_tree<key_type, data_type>::search(key_type query) {
node** local_cursor = &start;
if (internal_search(local_cursor, query)) {
return (*local_cursor)->data;
}
std::cout << "search query not found" << std::endl;
return 0;
}
int main() {
binary_tree<int,int> tree;
tree.insert (0,0);
tree.insert (2,3);
tree.insert (-2,3);
tree.insert (-1,-1);
std::cout << "start = ";
tree.print_start();
std::cout << tree.search(2) << "\n";
return 0;
}

Related

Seg fault Queue using Linked List (C++)

The following is a new programmer's attempt at a Queue. It seg faults in the Push() function, when I try to print the data in the first node. Looks like front_ptr is not actually getting set in head_insert. What am I doing wrong here, is this a completely wrong approach?
Thanks.
#include <cstdlib>
#include <iostream>
using namespace std;
class node {
public:
typedef double data_type;
node(data_type init_data = 0, node * init_next = NULL) {
data = init_data;
next_node = init_next;
}
void set_data(data_type new_data) { data = new_data; }
void set_next(node * new_next) { next_node = new_next; }
data_type get_data() { return data; }
node * get_next() { return next_node; }
private:
data_type data;
node * next_node;
};
void head_insert(node::data_type val, node* head_ptr) {
node* insert_ptr = new node(val, head_ptr);
head_ptr = insert_ptr;
}
void list_insert(node::data_type val, node* prev_ptr) {
node* insert_ptr = new node(val, prev_ptr->get_next());
prev_ptr->set_next(insert_ptr);
}
void head_remove(node* head_ptr) {
node* remove_ptr = head_ptr;
head_ptr = head_ptr->get_next();
delete remove_ptr;
}
void list_remove(node * prev_ptr) {
node* remove_ptr = prev_ptr->get_next();
prev_ptr->set_next(remove_ptr->get_next());
delete remove_ptr;
}
void list_clear(node* head_ptr) {
while (head_ptr != NULL) {
head_remove(head_ptr);
}
}
class queue {
public:
queue() {
size = 0;
front_ptr = NULL;
rear_ptr = NULL;
}
//~queue() {}
bool empty() { return (size == 0);}
void push(node::data_type val) {
if (empty()) {
head_insert(val, front_ptr);
cout << "Here: " << front_ptr->get_data() << endl;
rear_ptr = front_ptr;
}
else {
list_insert(val, rear_ptr);
}
size++;
}
void pop() {
if (!empty()) {
head_remove(front_ptr);
size--;
}
}
private:
node* front_ptr;
node* rear_ptr;
int size;
};
int main() {
cout << "START" << endl;
double testVal = 1;
queue* qList = new queue();
qList->push(testVal);
cout << "END" << endl;
return 0;
}
Any help is greatly appreciated.
Your front_ptr remains null pointer in push, because head_insert accepts it by value. Dereferencing null pointer then crashes the program. Make parameters that you want to be modified by a function reference parameters like void head_insert(node::data_type val, node*& head_ptr).
Also, you can avoid crash of null pointer dereferencing by checking it before, for example like that:
cout << "Here: " << (front_ptr ? front_ptr->get_data() : 0./0.) << endl;

Creating objects with new in function and "this is nullptr" exception

I've been trying to make a template class (called List) which stores different type of objects. I created Base class to be like base in my program and Human class. Base can create new Human and to have access to them all, has a (private) pointer to List * first_h (in every List is stored Human* me, List * next and List * first_h (first_h in list)).
The problem is, when I add like more than 1 Human to my Base, I can't display them properly. I think it's because of creating new Human's in Base method (void Base::create_human(string name)) but everything I did don't work it out.
There are my classes:
class Human
{
private:
string name;
public:
Human(string name) { this->name = name; }
void display() { cout << "My name: " << name << endl; }
};
template <class T>
class List
{
private:
T* me;
List <T>* next;
List <T>* first;
public:
void set_me(T* me) { this->me = me; }
T* get_me() { return this->me; }
void set_next(List* next) { this->next = next; }
List <T>* get_next() { return this->next; }
void set_first(List* first) { this->first = first; }
List <T>* get_first() { return this->first; }
void add(T*& created);
void display();
};
class Base
{
private:
List <Human>* first_h;
public:
void set_first_h(List <Human>*& first) { this->first_h = first; }
List <Human>* get_first_h() { return this->first_h; }
void create_human(string name)
{
Human* created = new Human(name);
this->first_h->add(created);
}
};
and methods:
template <class T>
void List<T>::add(T*& created)
{
List <T>* temp = this->get_first();
List <T>* new_list;
if ((this->get_me()) == nullptr)
{
this->set_next(nullptr);
this->set_me(created);
this->set_first(this);
}
else
{
new_list = new List <T>;
temp = this->get_first();
while (temp != nullptr)
{
temp = temp->get_next();
}
new_list->set_next(nullptr);
new_list->set_first(this->get_first());
temp->set_next(new_list);
}
}
template <class T>
void List<T>::display()
{
List <T>* temp_list = this;
T* temp;
if (temp_list == nullptr)
{
std::cout << "There is nothing!" << endl;
}
while (temp_list != nullptr)
{
temp = temp_list->get_me();
temp->display();
temp_list = temp_list->get_next();
}
std::cout << "End!" << endl;
}
and my main function:
int main()
{
Base Main;
List <Human>* first_h = new List <Human>();
Main.set_first_h(first_h);
Main.create_human("Jane");
Main.create_human("John");
Main.create_human("Mary");
Main.get_first_h()->display();
system("pause");
return 0;
}
Sorry for my English and thank you in advance!
Edit:
I found out what was wrong:
in add function:
new_list->set_next(nullptr);
new_list->set_me(created);
new_list->set_first(this->get_first());
temp->set_next(new_list);
I forgot about:
new_list->set_me(created);
the mistake in add function as one of you wrote.
Your loop
while (temp != nullptr)
{
temp = temp->get_next();
}
runs till temp is nullptr and then you do
temp->set_next(new_list);
So, as you see, inside set_next() the this pointer is nullptr.
Please learn how to use a debugger and look at the call stack.

Trouble implementing a templated singly linked list

I'm trying to implement a templated singly linked list and I'm fairly new to C++
#include <iostream>
#include <string>
#define NEWL "\n"
#define PRINT(s) std::cout << s
#define PRINTL(s) std::cout << s << NEWL
#define PRINTERR(e) std::cerr << e << NEWL
////// Class for a Node
template<class Data> class Node {
Node<Data>* next_ptr;
Data data;
public:
Node(Node<Data>* nxt_ptr) :next_ptr(nxt_ptr) {};
Node(Data d, Node<Data>* nxt_ptr) :data(d), next_ptr(nxt_ptr) {};
Node<Data>* get_next() { return next_ptr; }
Data& get_data() { return data; }
friend std::ostream& operator<<(std::ostream& out, const Node<Data>& node) {
out << node.data;
return out;
};
};
////// Class for a SinglyLinkedList
template<class Data> class SLinkedList {
Node<Data>* head_ptr;
int max_size;
public:
SLinkedList() : head_ptr(nullptr) {};
bool is_empty() {
return head_ptr == nullptr;
};
bool is_full() {
return get_size() == max_size;
};
int get_size() {
if (is_empty()) {
return 0;
}
int count = 0;
for (Node<Data>* it_ptr = head_ptr; it_ptr != nullptr; it_ptr = it_ptr->get_next()) {
count++;
}
return count;
};
void add(Data d) {
if (is_full()) {
throw std::exception("List is full!");
}
Node<Data> new_node(d, head_ptr);
head_ptr = &new_node;
};
void print_content() {
int count = 1;
PRINTL("This list contains:");
for (Node<Data>* it_ptr = head_ptr; it_ptr != nullptr; it_ptr = it_ptr->get_next()) {
PRINTL("\t["<< count << "]" << " at " << it_ptr << " : " << *it_ptr);
count++;
}
}
};
////// Main function
int main()
{
SLinkedList<int> sll;
sll.add(42);
sll.print_content();
}
I can't get this to work. Somehow iterating the list with for-loops does not work. It always results in an Reading Access Violation Exception about a pointer to 0xCCCCCCD0 and I have no idea how to fix this.
Your add function is incorrect
Node<Data> new_node(d, head_ptr);
creates a new function local Node in add. You then set head to the address of that local variable. When the function ends all local variables are destroyed so now head points to an object that no longer exists.
To fix that you need to use the new keyword to create a dynamic object that will live on after the function ends.
Node<Data>* new_node = new Node(d, head_ptr);
head_ptr = new_node;
The down side with this is you need to remember to call delete on all of the nodes you created in the list destructor.
You also have some other bugs in your code. You never set max_size in your constructor so using it at all except to give it a value is undefined behavior as we have no idea what the value of it is going to be. You also never increase the size of the list when you add nodes into the list.

Search Function Using Derived Classes in a Hash Table

I am having a bit of an issue with my derived classes and how they utilize the search function that they inherit from their parent class.
Here is my .h file
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
#define TABLESIZE 13
#ifndef HASH_H
#define HASH_H
namespace HTGroup
{
template<class T>
class HashTable
{
protected:
struct item {
T x;
item* next;
};
item* HT[TABLESIZE];
virtual int hash(T key) = 0;
virtual int collision(T key, int &value) = 0;
public:
HashTable();
virtual void printGrid();
void insert(T key);
void remove(T key);
void search(T key);
int indexItems(int index);
};
template<class T>
class DHT1 : public HashTable<T>
{
protected:
int hash(T key);
int collision(T key, int &value);
struct item {
T x;
item* next;
};
item* HT[TABLESIZE];
public:
DHT1();
void printGrid();
};
template<class T>
class DHT2 : public HashTable<T>
{
protected:
int hash(T key);
int collision(T key, int &value);
struct item {
T x;
item* next;
};
item* HT[TABLESIZE];
public:
DHT2();
void printGrid();
};
}
#endif
Here is what I have implemented for the search function:
template<class T>
void HashTable<T>::search(T key)
{
int index = hash(key);
bool foundKey = false;
string item;
item* temp = HT[index];
while(temp != NULL)
{
if(temp->x == key)
{
foundKey = true;
item = temp->x;
}
temp = temp->next;
}
if(foundKey == true)
{
cout << "Item was found." << endl;
}
else
{
cout << "Item was not found." << endl;
}
}
And this is how I am calling the function in my main:
hashy1.search(item);
I am getting an error from the compiler with this line from my search implementation:
item* temp = HT[index];
Giving me this error:
[Error] 'temp' was not declared in this scope
From my understanding whenever an object of a derived class is calling the search function it is getting confused with whether or not the pointer created is of the parent class or the derived class.
The weird thing though is that it has let me create other pointers in my remove function without any issues and it works fine:
template<class T>
void HashTable<T>::remove(T key)
{
int index = hash(key);
item* delPtr; //Where I am allowed to create pointers with
item* P1; //no issues
item* P2;
if(HT[index]->x == "")
{
cout << key << " was not found in the hash table" << endl;
}
else if ( HT[index]->x == key && HT[index]->next == NULL)
{
HT[index]->x = "";
cout << key << " was removed from the hash table" << endl;
}
else if(HT[index]->x == key)
{
delPtr = HT[index];
HT[index] = HT[index]->next;
delete delPtr;
cout << key << " was removed from the hash table" << endl;
}
else
{
P1 = HT[index]->next;
P2 = HT[index];
while(P1 != NULL && P1->x != key)
{
P2 = P1;
P1 = P1->next;
}
if(P1 == NULL)
{
cout << key << " was not found in the hash table" << endl;
}
else
{
delPtr = P1;
P1 = P1->next;
P2->next = P1;
delete delPtr;
cout << key << " was removed from the hash table" << endl;
}
}
}
I've tried creating the pointer in the .h file like this:
template<class T>
class DHT1 : public HashTable<T>
{
protected:
int hash(T key);
int collision(T key, int &value);
struct item {
T x;
item* next;
item* temp; // Added declaration
};
item* HT[TABLESIZE];
public:
DHT1();
void printGrid();
};
But that still gives me declaration issues
Are there different methods I should be using when implementing my search function such as any extra parameters in the function call? Or maybe I am just not getting the logic down right?
Thank you for any responses!
You declared item as a std::string, and then you use item in the same scope as a type.
string item; // <-- declaring as string
item* temp = HT[index]; // <-- Compiler doesn't know what to do with this line except to give an error.
The simplest solution is to name your std::string variable something else other than item.

Segmentation fault issues when creating a Binary Search Tree

Ill start out with giving all the code I think is relevant. Basically a Binary Search Tree was already defined that worked and we need to add a parent node functionality. I have done this but I keep getting segmentation faults.
template <class TKey>
class bst {
private:
struct node {
node() { key=TKey(); link[0]=link[1]=NULL; parent=NULL; }
operator TKey () { return key; }
void print();
TKey key;
node *link[2];
node *parent;
};
public:
class iterator {
public:
private:
friend class bst<TKey>;
node *p;
};
node *prev_node;
iterator begin() { }
iterator end() { }
bst() { Troot=NULL; }
~bst() { clear(Troot); }
bool empty() { return Troot==NULL; }
void clear() { clear(Troot); Troot=NULL; }
void erase(TKey &key);
void insert(TKey &key);
void print_inorder() { print_inorder(Troot); }
void print_bylevel();
private:
void clear(node *);
node *minmax_key(node *, int);
node *erase(node *, TKey &);
node *insert(node *, TKey &);
void print_inorder(node *);
node *Troot;
};
Thats the Class Definition.
template <class TKey>
void bst<TKey>::insert(TKey &key)
{
Troot = insert(Troot, key);
}
template <class TKey>
class bst<TKey>::node *bst<TKey>::insert(node *T, TKey &key)
{
cout << "insert1" << endl;
if (T == NULL) {
T = new node;
T->key = key;
if (prev_node != NULL)
T->parent = prev_node;
cout << T->parent->key;
} else if (T->key == key) {
cout << "key " << key << " already in tree" << endl;
} else {
prev_node = T;
int dir = T->key < key;
T->link[dir] = insert(T->link[dir], key);
}
return T;
}
These are the insert functions. Im guessing I am doing something out of order because I am still really rusty with recursion. When I run the test program that uses the tree it outputs the inser1 line but then gives a seg fault. So i know it is messing up on the first insert. any help? If you need to see the rest of the code I can put it up but itll be a lot of stuff that isnt actually relevent to the changes Ive made.
I'm thinking the segfault is on this line
cout << T->parent->key;
If T->parent is null, which it is if T is the newly created root (ie if prev_node == NULL), then you can't access the 'key' of a NULL value.
NOTE: Be aware that I have only skimmed your code, so this is only the first thing I have come across, there could be other bugs.
EDIT:
What do you mean by "I am still having problems", what problems are you having?
It probably isn't how I would implement a BST insert but I can't see anything jumping out saying it is wrong.
How I would implement it is rather than having a global variable prev_node, I would probably change your code like so:
template <class TKey>
void bst<TKey>::insert(TKey &key)
{
// Note that I have an initial prev_node of NULL
Troot = insert(Troot, key, NULL);
}
// Note the extra function parameter
template <class TKey>
class bst<TKey>::node *bst<TKey>::insert(node *T, TKey &key, node *prev_node)
{
cout << "insert1" << endl;
if (T == NULL) {
T = new node;
T->key = key;
// I have a habit of always using braces, so that it is easier to read.
// This would have helped you with your initial problem.
if (prev_node != NULL) {
T->parent = prev_node;
}
//cout << T->parent->key;
} else if (T->key == key) {
cout << "key " << key << " already in tree" << endl;
} else {
int dir = T->key < key;
T->link[dir] = insert(T->link[dir], key, T); // Note diff here
}
return T;
}
Unless you are using prev_node somewhere else.
But this shouldn't change how the insert works, unless in your implementation:
prev_node is not null initially for some reason (which would be the case on successive inserts, unless you are resetting it somewhere else).
something is changing prev_node while you are using it (think thread-safety)