C++ Binary search tree compare data of nodes and remove duplicates - c++

I have created a binary search tree in c++ and have loaded it up with two types of data, strings and ints. I am reading a text file and loading the tree up alphabetically with the words I am pulling, and also the number of the line the word is found on. I am able to print the words and the numbers just fine. What I am wanting to do now is check to see if a word has already been printed, and if it has then I will only print out the number of the line from which the word is found on. The way I am thinking about doing this is by comparing previous data as the tree is traversed and printed. This is my print function.
void inOrderPrint(Node *rootPtr ) {
if ( rootPtr != NULL ) {
for (int i =0; rootPtr->data[i]; i++){
while(ispunct(rootPtr->data[i]))
rootPtr->data.erase(i,1);
}
rootPtr->data = rootPtr->data.substr(0,10);
inOrderPrint( rootPtr->left );
cout << (rootPtr->data)<<rootPtr->lineNum <<endl;
inOrderPrint( rootPtr->right );
}
}
This is what I was thinking:
if (rootPtr->data == previous rootPtr->data)
cout<<setw(10)<<theCurrentNode lineNum;
else
do normal printing
I think that if this function were to run on the first node and it compares it to the non existent previous node, it would automatically try to compare it to NULL, the if statement would return false and it would move on to the else.
Any suggestions on how to go about doing this with actual c++ syntax? Or does anyone see a flaw in my logic?
Thanks in advance!

This answer will describe how to make the program print unique entries and the line number of the first occurrence in the file. If there are duplicate occurrences it will print only the line number of the first occurrence for each duplicate occurrence. The approach is to make sure that there are no duplicate nodes in the tree and to count redundant occurrences.
To do this we might modify the node structure as follows:
struct Node{
string data;
int lineNum;
int count =1;
Node* left;
Node* right;
};
The function Insert might be edited to count duplicates like this:
Node* Insert(Node* rootPtr,string data,int lineNum){
if(rootPtr == NULL){
rootPtr = GetNewNode(data,lineNum);
for (int i =0; rootPtr->data[i]; i++){
while(ispunct(rootPtr->data[i]))
rootPtr->data.erase(i,1);
}
rootPtr->data = rootPtr->data.substr(0,10);
return rootPtr;
}
else if(data< rootPtr->data){
rootPtr->left = Insert(rootPtr->left,data,lineNum);
for (int i =0; rootPtr->data[i]; i++){
while(ispunct(rootPtr->data[i]))
rootPtr->data.erase(i,1);
}
rootPtr->data = rootPtr->data.substr(0,10);
}
else if(data > rootPtr->data) {
rootPtr->right = Insert(rootPtr->right,data,lineNum);
for (int i =0; rootPtr->data[i]; i++){
while(ispunct(rootPtr->data[i]))
rootPtr->data.erase(i,1);
}
rootPtr->data = rootPtr->data.substr(0,10);
}
else if(data == rootPtr->data)
++rootPtr->count;
return rootPtr;
}
Finally the print function can be modified:
void inOrderPrint(Node *rootPtr ) {
//ofstream outputFile;
//outputFile.open("Output.txt");
if ( rootPtr != NULL ) {
inOrderPrint( rootPtr->left );
cout << (rootPtr->data)<<" " << rootPtr->lineNum <<endl;
int j =rootPtr->count;
while( --j )
cout << rootPtr->lineNum <<endl;
//outputFile << (rootPtr->data)<<rootPtr->lineNum <<endl;
inOrderPrint( rootPtr->right );
}
}
Now this should be much closer to what you want. It would also be a good idea to separate the text processing from the node processing. (This answer sort of assumes that you will take care of that.) Otherwise duplicate nodes will be created if the preprocessed text does not match the processed text.
Good luck!

Related

C++ Hash Table and Linked List Issues

So I'm trying to work on a project for my C++ class where I read a .txt file that has 53 lines of cities, states, and superfluous information afterwards.
(example: Port Jervis,NY,New York,36071,Orange,36071,41.3782,-74.6909,16410.0,1317)
After reading the file, I separate out the city name (example: Port Jervis) and state code (example: NY) and uses the value of the two letters in the state code as the key for a hash table of 13 elements. So N=13 + Y=24 = key of 37, and since the hash has 13 elements it's 37 % 13 = hash-key of 11.
So far so good and I'm able to get all that done correctly, however when it comes to displaying the results is where I'm running into an issue as each element of the hash-table is missing one link in the linked list. So it only displays 40 outputs of the 53, with 1 missing per element and I'm really not sure why.
So I e-mailed my professor my code and he said that my insert method is not correct which he believes is causing this error. My current insert method looks like
void insert(int key, string city, string state)//insert value
{
int hash = KeyModFunction(key); //function that's %13 for hash-key
Node* tmpInsert = new Node(key, city, state); //create node to work with
if(table[hash]==NULL)//checks if table is empty
{
table[hash] = tmpInsert; //if empty, make new node with key/city/state values
}
else//if not empty
{
Node *runner = table[hash]; //made node to run through the list
while(runner->next != NULL)//make it to the end
{
runner=runner->next; // go go go
}
runner->next = tmpInsert; //and point the end at the new node to be inserted
}
} //end insert
And my professor suggested it should look something more like
if(table[hash]->next == NULL)
{
table[hash]->next = tmpInsert;
table[hash]->myCity = city;
table[hash]->myState = state;
}
else
{
// You can figure out the else code based on the above
However, whenever I put that into my code, it no longer compiles and says there is a segment fault. But when I run it through a debugger it says "[Inferior 1 (process 5453) exited normally]" which I'm not going to lie, I'm not sure what the means and have been unable to find a concrete answer online for. But I'm assuming the exited normally is a good thing, however, nothing is displayed.
I've been beating my head against this all week trying to figure out a solution and it's finally come to the point where I know I'm just getting too in my own head about it, so I've come here hoping to find some guidance, advice, or at the very least someone to point me in the right direction. If more of my code is needed on here, let me know, I just didn't want to dump my whole project on here cause I legitimately want to figure it out instead of having someone just do it for me, but yeah, I'm stuck. Thanks in advance for any help!
****2:12PST - 5/17/2020 UPDATE****
So in all fairness the insert code was plucked and modified from other peoples code I've found online looking into how to do this, so that might be why it looks better than my professor (also I'm pretty sure he mention C++ isn't his most familiar language). And yes, we are supposed to implement the hash table ourselves.
So here is the full program:
class Node{
public:
int key;
string myCity;
string myState;
Node *next;
Node(int key, string myCity, string myState)//constructor
{
this->key = key;
this->myCity = myCity;
this->myState = myState;
this->next = NULL;
}
};//end Node
class Hash{
private:
int BUCKET; //number of over all values
Node** table;
public:
//Constructor
Hash(int V)
{
this->BUCKET = V; //setting the BUCKET size to max number of enteries
table = new Node*[BUCKET]; //create table with size of BUCKET
for(int i = 0; i < BUCKET; i++) //fill table with NULL values
{
table[i] = NULL;
}
} //end constructor
//KeyModFunction
int KeyModFunction(int x) //getting the hash key value
{
return (x % BUCKET);
} //end KeyModFunction
//Insert Function
void insert(int key, string city, string state)//insert value
{
int hash = KeyModFunction(key); //function that's %13 for hash-key
Node* tmpInsert = new Node(key, city, state); //create node to work with
if(table[hash]==NULL)//checks if table is empty
{
table[hash] = tmpInsert; //if empty, make new node with key/city/state values
}
else//if not empty
{
Node *runner = table[hash]; //made node to run through the list
while(runner->next != NULL)//make it to the end
{
runner=runner->next; // go go go
}
runner->next = tmpInsert; //and point the end at the new node to be inserted
}
} //end insert
//Display function
void displayHash()
{
for(int loop = 0; loop < BUCKET; loop++)
{
cout<<loop;
if(table[loop]->next != NULL)
{
Node* tmp;
tmp = table[loop]->next;
do
{
cout<<" -->"<<tmp->myCity<<"/"<<tmp->myState;
tmp = tmp->next;
}while(tmp!=NULL);
}
cout<<endl;
}
}//end displayHash
}; //end Hash Class
int main() {
cout << "CSP 31B - Read and Process Assignment\n\n";
char myAlpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //the key array for all the letter values
Hash myTbl(13); //create hashmap with BUCKET size of 13
string fCity, fState, fExtra; //string variables to hold info
int key = 0; //hash value of the state code (two letters added together)
ifstream myfile("CityOut.txt");
while ( getline(myfile, fCity, ',') && getline(myfile, fState, ','))
{
getline(myfile, fExtra);
for(int i = 0; i < sizeof(myAlpha)/sizeof(myAlpha[0]); i++)
{
if(fState.at(0) == myAlpha[i])
{
key += i;
}
else if(fState.at(1) == myAlpha[i])
{
key += i;
}
}
int checkNum = 1;
cout << "DEBUGGER: City name: "<<fCity <<" State code: " << fState.at(0) << fState.at(1) <<" key = "<<key<<endl; //temporary statement for debugging purposes
myTbl.insert(key, fCity, fState);
key = 0; //reset hash number to zero for next line of CityOut.txt
}
cout<<endl<<endl<<endl;
myTbl.displayHash();
return 0;
}//end main
Then the output should look something like:
but each table element should have 1 more output
Your print code skips the first element of your hash table.
This code:
cout<<loop;
if(table[loop]->next != NULL)
{
Node* tmp;
tmp = table[loop]->next;
do
{
cout<<" -->"<<tmp->myCity<<"/"<<tmp->myState;
tmp = tmp->next;
}while(tmp!=NULL);
}
Should be :
cout<<loop;
if(table[loop] != NULL)
{
Node* tmp;
tmp = table[loop];
do
{
cout<<" -->"<<tmp->myCity<<"/"<<tmp->myState;
tmp = tmp->next;
}while(tmp!=NULL);
}

Trie data structure using class C++

I am trying to implement trie data structure in C++ using class. In TrieNode class I have a TrieNode *children[26]; array and an isEndOfWord boolean value to determine if it is the end word. In that same class I have other functions appropriate to function like getters and setters and additionally insert and search.
Whenever I try to add a new word it is also setting the bool value as true at the end of each word by setting true to isEndOfWord. But in searching function it is not determining the end of the word. Please guide me as I am new to this data structure, and please comment on the way i write the code and what is the appropriate way to write it(in a Professional way, if interested). Thanks!
#include<cstdio>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
class TrieNode{
private:
TrieNode *children[26];
bool isEndOfWord;
public:
TrieNode(){
for(int i = 0; i < 26; i++){
children[i] = NULL;
}
isEndOfWord = false;
}
bool checkNull(char temp){
cout<<"\nIncheckNULL "<<temp<<" "<<(temp - 'a')<<" \n";
if(children[temp - 'a'] == NULL){
return true;
}
else{
return false;
}
}
void setNode(char temp){
cout<<"Setting node \n";
children[temp - 'a'] = new TrieNode();
}
TrieNode *getNode(char temp){
return children[temp - 'a'];
}
void setEndWord(){
this->isEndOfWord = true;
}
bool getEndWord(){
return this->isEndOfWord;
}
void insert(TrieNode*, string);
bool search(TrieNode*, string);
};
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
//cout<<"key is "<<key<<endl;
int length = sizeof(key)/sizeof(key[0]);
//cout<<"find length\n";
for(int i = 0; key[i] != '\0'; i++){
cout<<"TEST null check key is "<<key[i]<<endl;
if(crawl->checkNull(key[i])){
cout<<"null check key is "<<key[i]<<endl;
crawl->setNode(key[i]);
crawl = crawl->getNode(key[i]);
if(key[i + 1] == '\0'){
cout<<"In setting end word\n";
if(crawl->getEndWord()){
cout<<"Word already exists";
}
else{
crawl->setEndWord();
cout<<"End word setted "<<crawl->getEndWord()<<endl;
}
}
}
else{
if(key[i + 1] == '\0'){
cout<<"In setting end word\n";
if(crawl->getEndWord()){
cout<<"Word already exists";
}
else{
crawl->setEndWord();
cout<<"End word setted\n";
}
}
else{
crawl = crawl->getNode(key[i]);
}
}
}
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
cout<<"key is "<<key<<endl;
cout<<"\n In search\n";
int length = sizeof(key)/sizeof(key[0]);
for(int i = 0; key[i] != '\0'; i++){
if(crawl->checkNull(key[i])){
cout<<"INside search checknull"<<endl;
cout<<"Word does not exists"<<"sorry"<<endl;
break;
}
else{
cout<<"IN each character getting getEndWord "<<crawl->getEndWord()<<endl;
if(key[i + 1] == '\0'){
if(crawl->getEndWord()){
cout<<"Word Exists";
}
else{
cout<<"Word does not exists"<<"sorry"<<endl;
break;
}
}
else{
crawl = crawl->getNode(key[i]);
}
}
}
}
int main(){
TrieNode *root = new TrieNode();
cout<<"starting"<<endl;
root->insert(root, "hello");
cout<<"first added"<<endl;
root->insert(root, "anna");
root->insert(root, "anni");
cout<<"words added"<<endl;
root->search(root, "hello");
root->search(root, "anny");
}
Your insert and search functions can be simplified a bit.
Consider this. (Read the comments in the below code, they illustrate what the code does)
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
if (!crawl) {
crawl = new TrieNode();
}
cout << "Adding " << key << " to the trie" << endl;
for (int index = 0, auto str_iterator = str.begin(); str_iterator < str.end(); ++str_iterator, ++index) {
char key_char = *str_iterator;
if(crawl -> checkNull(key_char)){
// If a node representing the char does not exist then make it
crawl -> setNode(key_char);
}
crawl = crawl -> getNode(key_char);
if (index == key.length() - 1) {
// We are at the last character, time to mark an end of word
crawl -> setEndWord();
}
}
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
if (!crawl) {
cout << "Trie is empty!" << endl;
return false;
}
cout << "Searching for " << key << " in the trie" << endl;
for (int index = 0, auto str_iterator = str.begin(); str_iterator < str.end(); ++str_iterator, ++index) {
char key_char = *str_iterator;
if(crawl -> checkNull(key_char)){
cout << "Key is not in the trie" << endl;
return false;
}
crawl = crawl -> getNode(key_char);
if (index == key.length() - 1) {
if (!(crawl -> getEndWord())) {
cout << "Word is physically present in trie, but not present as a distinct word" << endl;
return false;
} else {
return true;
}
}
}
cout << "Code should not reach here" << endl; // IMO throw an exception I guess
return false;
}
Take advantage of the power of C++ std::string
Also your whole temp - 'a' logic is a bit iffy to me. I wouldn't much around with ASCII values unless I needed to
Why are you including a whole bunch of C headers? Just iostream should suffice to do what cstdio does.
if(!ptr) is a much more natural way to check for NULL.
In production don't use using namespace std; Instead just preface stuff like cout and endl with std::. The reason for this is to avoid polluting the standard namespace.
Read a good CPP OOP book :). It will help you a lot.
Also I lol'd at anna and anni. Your anna and anni must be proud to be in your trie :D
There are many things I'd give you feedback on, but this isn't a code review site, it's for specific questions. I'll point out briefly a few things I notice though:
1) don't include C headers; use c++ ones instead.
2) what type is string?
3) you compute length (incorrectly, assuming answer to question 2 is "the standard c++ string class"), but you don't use it.
4) search() returns a bool but you don't return anything. When you find the end of a word, you should return from the function.
5) search() calls checkNull() at the top of the for loop without ensuring that it's not null. After this: crawl = crawl->getNode(key[i]); it could be null, but then you loop and go through the pointer without testing it.
6) setNode is a public function, and unconditionally overwrites whatever is in the slot for the given variable. You can clobber an existing child if someone calls it with the same character twice and leak (and probably lose data in your tree.
7) search doesn't need to be a member of TrieNode. In fact, it doesn't access any data through "this". You probably don't want the TrieNode to be public at all, but an internal implenetation detail of Trie, which is where the search function should live, where the root should be stored and managed.
8) in c++ use nullptr instead of NULL
9) Looks like you need to debug search(), because it is not on the last letter when you check for end of word.
10) you need a destructor and need to deallocate your nodes. Or store them in unique_ptr<> for automatic deletion when your object goes out of scope.
11) don't "using namespace std;" in headers. It makes your headers toxic to include in my code.
The insert and search functions are a mess.
They use rather contrived ways to check the end of the string, duplicated unnecessarily and with a bug in one of the branches.
Here are simpler versions.
They use string size for the loop bounds, and the actions needed at the end of the loop are made after the loop, which is more natural.
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
for(int i = 0; i < (int) (key.size()); i++){
if(crawl->checkNull(key[i])){
crawl->setNode(key[i]);
}
crawl = crawl->getNode(key[i]);
}
crawl->setEndWord();
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
for(int i = 0; i < (int) (key.size()); i++){
if(crawl->checkNull(key[i])){
return false;
}
crawl = crawl->getNode(key[i]);
}
return crawl->getEndWord();
}
I used the same style, but omitted the debug outputs for readability.
Also, the code did not actually use search as a function, it didn't return a value.
Instead, it relied on debug output to show the result.
This is now corrected.
A main function complementing these is as follows.
int main(){
TrieNode *root = new TrieNode();
cout<<"starting"<<endl;
root->insert(root, "hello");
cout<<"first added"<<endl;
root->insert(root, "anna");
root->insert(root, "anni");
cout<<"words added"<<endl;
cout << root->search(root, "hello") << endl; // 1
cout << root->search(root, "anny") << endl; // 0
}

Why does my recursion not return to previous pointer?

I am working on an assignment in which we must create a 20-questions type game from a binary search tree. We read the tree in from a text file that is formatted like this:
Does it walk on 4 legs?
Does it fly?
*centipede?
Is it an insect?
*bird?
*butterfly?
Does it purr?
Does it howl?
*mouse?
*dog?
*cat?
Later, I am going to allow the user to add to this list. At the moment, however, I am unable to accurately read the list into a binary search tree. I have set it up so that (I think) it will use recursion and return to the previous "current" node pointer when it ends a loop of the function. Currently, however, the current node pointer remains the same.
The below function is passed a vector of the strings from the text file.
string line;
string guess;
bool start = true;
void buildTree(vector<string> gameData, Node* current, int &counter)
{
//fill node with question or answer
//recursive:
// add to the left until we encounter an asterisk
// add to the right
line = gameData[counter];
//if a question
if (line[0] != '*')
{
if (current->getData().empty())
{
current->setData(line);
cout << current->getData() << endl;
}
if (!start)
{
//if noChild is empty AND current isn't a guess, go to noChild
if ((current->getNo()->getData().empty())
&& (current->isGuess() == false))
{
current = current->getNo();
}
//otherwise, go to yes
else {
current = current->getYes();
}
}
while (counter < gameData.size())
{
if (!start) { counter++; }
start = false;
buildTree(gameData, current, counter);
}
}
//if a guess
else
{
//if data is full, go to no
if (current->getData().empty() == false)
{
current = current->getNo();
}
//otherwise, go to yes
else
{
//current = current->getYes();
for (int i = 1; i < line.size(); i++)
{
guess.push_back(line[i]);
}
current->setData(guess);
guess.clear();
cout << current->getData() << endl;
counter++;
current->setGuess(true);
}
}
}

Skip List C++ segmentation fault

I'm trying to implement the Skip List using this article Skip List.
Code:
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<limits>
using namespace std;
template<class T>
class SkipList{
private:
class SkipNode{
public:
T* key; //Pointer to the key
SkipNode** forward; //Forward nodes array
int level; //Node level
//SkipNode constructor
SkipNode(T* key, int maxlvl, int lvl){
forward = new SkipNode*[maxlvl];
this->key=key;
level=lvl;
}
//Method that print key and level node
print(){
cout << "(" << *key << "," << level << ") ";
}
};
SkipNode *header,*NIL; //Root and End pointers
float probability; //Level rate
int level; //Current list level
int MaxLevel; //Maximum list levels number
//Function that returns a random level between 0 and MaxLevel-1
int randomLevel(){
int lvl = 0;
while( (float(rand())/RAND_MAX < probability) && (lvl < MaxLevel-1) )
lvl++;
return lvl;
}
public:
//SkipList constructor
SkipList(float probability, int maxlvl){
this->probability = probability;
MaxLevel = maxlvl;
srand(time(0));
header=new SkipNode(NULL,MaxLevel,0); //Header initialization
T* maxValue = new T;
*maxValue = numeric_limits<T>::max(); //Assign max value that T can reach
NIL = new SkipNode(maxValue,0,0); //NIL initialization
level=0; //First level
for(int i=0; i<MaxLevel; i++){ //Every header forward node points to NIL
header->forward[i]=NIL;
}
}
//SkipList destructor
~SkipList(){
delete header;
delete NIL;
}
//Method that search for a key in the list
SkipNode* search(T* key){
SkipNode* cursor = header;
//Scan the list
for(int i=level; i>=0; i--)
while(*(cursor->forward[i]->key) < (*key))
cursor=cursor->forward[i];
cursor=cursor->forward[0];
if(*(cursor->key) == *key)
return cursor;
return NULL;
}
//Method that insert a key in the list
SkipList* insert(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Node already inserted
return this;
}
int lvl = randomLevel(); //New node random level
if(lvl > level){ //Adding missing levels
for(int i=level+1; i<=lvl; i++)
update[i]=header;
level=lvl;
}
SkipNode* x = new SkipNode(key,MaxLevel,lvl); //New node creation
for(int i=0; i<=lvl; i++){ //Fixing pointers
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
return this;
}
//Method that delete a key in the list
SkipList* erase(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Deletetion of the founded key
for(int i=0; i<=level && update[i]->forward[i] == cursor; i++){
update[i]->forward[i] = cursor->forward[i];
}
delete cursor;
while(level>0 && header->forward[level]==NIL){
level=level-1;
}
}
return this;
}
//Method that print every key with his level
SkipList* print(){
SkipNode* cursor = header->forward[0];
int i=1;
while (cursor != NIL) {
cursor->print();
cursor = cursor->forward[0];
if(i%15==0) cout << endl; i++;
}
cout << endl;
return this;
}
};
main(){
SkipList<int>* list = new SkipList<int>(0.80, 8);
int v[100];
for(int i=0; i<100; i++){
v[i]=rand()%100;
list->insert(&v[i]);
}
list->print();
cout << endl << "Deleting ";
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
list->erase(&v[h]);
}
cout << endl;
list->print();
cout << endl;
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
if(list->search(&v[h]))
cout << " is in the list" << endl;
else
cout << " isn't in the list" << endl;
}
delete list;
}
It gives me Segmentation Fault on line 59 (the for-cycle on the insert), but I can't understand why. May you help me please? I will accept any other improvement that you suggest. My deadline is on two days, that's why I'm asking for help.
EDIT:
I've corrected the code with bebidek suggestions (Thanks). Now first level is 0. It seems to be working, but sometimes some nodes is not inserted correctly and the search give a bad result.
LAST EDIT:
It works, thanks to all
ONE MORE EDIT:
Added comments to code, if you have any suggestion you're welcome
The biggest problem in your code is probably NIL=new SkipNode(numeric_limits<T*>::max());
First of all i suspect you want the key pointer to point to a memory address that contains the biggest possible int value.
But that's not what's actually happening here. Instead the key pointer points to the biggest possible memory-address which is most likely not available for your process.
Also the forward property probably contains an array of junk pointers.
Then when the first loop in the insert method is executed this leads to 2 problems:
while(*(cursor->forward[i]->key) < *(key)) will compare the key value to an invalid pointer
cursor=cursor->forward[i]; will re-assign cursor to an invalid pointer
I would first suggest you'd change the design to let SkipNode keep a value to T instead of a pointer:
class SkipNode{
public:
T key;
SkipNode* forward[100];
This will make a lot of pointer related code unnecessary and make the code simpler so less likely to run into access violation.
Also it might be cleaner to use an actual NULL (or event better nullptr) value instead of a dummy NIL value to indicate the end of the list.
So, first problem is when you create NIL node:
NIL=new SkipNode(numeric_limits<T*>::max());
As argument you should use pointer to existing variable, for example:
T* some_name = new T;
*some_name = numeric_limits<T>::max();
NIL = new SkipNode(some_name);
Notice, I used T instead of T* in numeric_limits. Of course you have to remember about deleting this variable in destructor.
Second problem is that level variable in your code sometimes is inclusive (I mean level number level exists) as in line 61, and sometimes exclusive (level number level doesn't exist) as in line 71. You have to be consistent.
Third problem is in line 52. You probably mean cursor=cursor->forward[1];, but after loop i = 0, and forward[0] doesn't have any sense in your code.
EDIT:
Fourth and fifth problem is in erase function.
cursor->~SkipNode();
It won't delete your node, but only run empty destructor. Use delete cursor; instead.
And in loop you probably wanted to write update[i]->forward[i] == cursor instead of !=.
ONE MORE EDIT:
You haven't implemented any destructor of SkipList and also you forgot about delete list; at the end of main(). These two will give you a memory leak.
ANOTHER EDIT:
srand(time(0));
This line should be executed once at the beginning of main and that's all. If you execute it before each random generation, you will get the same result every time (as time(0) counts only seconds and your program can run function randomLevel() more than once a second).
You also forgot about rewriting precision variable in constructor of SkipList.
NEXT EDIT:
In your insert function you don't have level randomization. I mean, you do not have ability of inserting node of level less than level of whole skip list. It's not error which will crash your program or give wrong results, but time complexity of queries in your structure is O(n) instead of O(log n).
You should use lvl instead of level in this loop in insert function:
for(int i=1; i<level; i++){
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
And also minimum result of your random function randomLevel should be 1 instead of 0, as you don't want node witch level=0.

Count word in trie implementation

I'm implementing a trie to implmenta spelling dictionary. The basic element of a trie is a trienode, which consists of a letter part (char), a flag(whether this char is the last char of a word), and an array of 26 pointers.
Private part of the TrieNode class include:
ItemType item;//char
bool isEnd;//flag
typedef TrieNode* TrieNodePtr;
TrieNodePtr myNode;
TrieNodePtr array[26];//array of pointers
This is part of the test call:
Trie t4 = Trie();
t4.insert("for");
t4.insert("fork");
t4.insert("top");
t4.insert("tops");
t4.insert("topsy");
t4.insert("toss");
t4.print();
cout << t4.wordCount() << endl;
Right now I'm trying to traverse the trie to count how many words there are (how many flags are set to true).
size_t TrieNode::wordCount() const{
for (size_t i = 0; i < 26; i++){
if (array[i] == nullptr){
return 0;
}
if (array[i]->isEnd && array[i] != nullptr){
cout << "I'm here" << endl;
return 1 + array[i]->wordCount();
}
else if(!array[i]->isEnd && array[i]!=nullptr){
cout << "I'm there" << endl;
return 0 + array[i]->wordCount();
}
else{
// do nothing
}
}
}
Every time the function returns 0. I know it's because when the first element in the array is null, then the function exits, so the count is always 0. But I don't know how to avoid this, since every time I have start from the first pointer. I also get a warning:not all control paths return a value. I'm not sure where this comes from. How do I make the function continue to the next pointer in the array if the current pointer is null? Is there a more efficient way to count words? Thank you!
Here is a simple and clear way to do it(using depth-first search):
size_t TrieNode::wordCount() const {
size_t result = isEnd ? 1 : 0;
for (size_t i = 0; i < 26; i++){
if (array[i] != null)
result += array[i]->wordCount();
return result;
}