Find the Shortest Cycle in Graph - c++

I have a problem with finding cycles in graph. In the condition we have to find the shortest cycle in directed graph.
My graph is (A,B,C,D) and the connections (arcs) between the elements are:
(A->B), (A->A), (B->C), (B->A), (C->D), (C->A), (D->A)
and so cycles are the following:
А->B->C->D->A; A->B->C->A; A->B->A; A->A.
Program should print the shortest cycle, ie A->A. To solve it i need first to find all cycles, then put them each in a separate list and finally bring the smallest list, which will be the shortest cycle (A-> A), but I do not know how to realize it. At the moment I made connections (arcs) between elements.
This is my code:
#include <iostream>
using namespace std;
const int N = 10;
struct elem
{
char key;
elem *next;
} *g1[N];
void init(elem *g[N])
{
for (int i = 0; i < N; i++)
g[i] = NULL;
}
int search_node(char c, elem *g[N])
{
for (int i = 0; i < N; i++)
if (g[i])
if (g[i]->key == c)
{
return 1;
}
return 0;
}
int search_arc(char from, char to, elem *g[N])
{
if (search_node(from, g) && search_node(to, g))
{
int i = 0;
while (g[i]->key != from) i++;
elem *p = g[i]->next;
while (true)
{
if (p == NULL)
{
break;
}
if (p->key == to)
{
return 1;
}
p = p->next;
}
}
return 0;
}
void add_node(char c, elem *g[N])
{
if (search_node(c, g))
cout << "Node already exists.\n";
int i = 0;
while (g[i] && (i < N)) i++;
if (g[i] == NULL)
{
g[i] = new elem;
g[i]->key = c;
g[i]->next = NULL;
}
else
{
cout << "Maximum nodes reached.\n";
}
}
void add_arc(char from, char to, elem *g[N])
{
if (search_arc(from, to, g))
cout << "Arc already exists.\n";
else
{
if (!search_node(from, g))
add_node(from, g);
if (!search_node(to, g))
add_node(to, g);
int i = 0;
while (g[i]->key != from) i++;
elem *p = new elem;
p->key = to;
p->next = g[i]->next;
g[i]->next = p;
}
}
void print(elem *g[N])
{
for (int i = 0; i < N; i++)
{
if (g[i] != NULL)
{
elem *p = g[i];
while (p)
{
cout << p->key << "\t";
p = p->next;
}
cout << endl;
}
}
}
void iscycle(elem *g[N])
{
}
int main()
{
system ("cls");
cout << "init: " << endl;
init(g1);
cout << "graph 1: " << endl;
add_arc('a', 'b', g1);
add_arc('a', 'a', g1);
add_arc('b', 'c', g1);
add_arc('b', 'a', g1);
add_arc('c', 'a', g1);
add_arc('c', 'd', g1);
add_arc('d', 'a', g1);
print(g1);
cout << "cycles: ";
iscycle(g1);
system("pause");
return 0;
}
This is my example graph picture: graph

If You're looking for a complete answer, then just check other answers - there are tons of questions regarding used algorithms and I've also found an answer with code ported to many different programming languages (Cpp version is also there)
Algorithm explanation
C++ version
I'd strongly recommend though, that You take a look at algorithms and implement them here, without removing already written code. It's much better to write it yourself, then just copy-past - You'll learn a lot more ;)
If You need any more precise help, write Your current status & we'll see.

Related

How to print the lead of A* algorithm

-I wrote a program to find the shortest path from a source node to a target node. Everything is fine, the program found the shortest path. But i have a problem, that is not able to print or get each node in the path. I tried many ways but no result. Hope anyone can help me, thanks everyone.
///////////////////////////////
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
typedef struct Node
{
int vertex;
int g;
int h;
int f;
Node* parent;
Node(int vertex)
{
this->vertex = vertex;
this->g = 0;
this->h = 0;
this->f = 0;
this->parent=NULL;
}
Node(int vertex,int g, int h, int f,Node*parent)
{
this->vertex = vertex;
this->g = g;
this->h = h;
this->f = f;
this->parent = parent;
}
}Node;
struct Edge
{
int source;
int dest;
int g;
int h;
};
struct comp
{
bool operator()(const Node* lhs, const Node* rhs) const {
return lhs->f < rhs->f;
}
};
std::vector<Node*>openList;
std::vector<Node*>closeList;
Node* startPos;
Node* endPos;
static const int WeightW = 10;
class Graph
{
public:
std::vector<std::vector<Edge>>adjlist;
Graph(const std::vector<Edge>& edges, int N)
{
adjlist.resize(N);
for (auto &edge:edges)
{
adjlist[edge.source].push_back(edge);
}
}
};
int isContains(std::vector<Node*>* Nodelist, int vertex);
void printPath(Node*node);
void findShortestPath(const Graph& grap,Node* start,Node* end, int N)
{
Node* node;
openList.push_back(start);
while (openList.size()>0)
{
node = openList[0];
closeList.push_back(node);
openList.erase(openList.begin());
std::cout << "start" << std::endl;
int u = node->vertex;
std::cout << "V: " << u << " g :" << node->g << std::endl;
std::cout << "continous" << std::endl;
for (auto v : grap.adjlist[u])
{
if (v.dest == end->vertex)
{
std::cout << "FindNode " << v.dest << std::endl;
printPath(node);
return;
}
if (isContains(&closeList, v.dest) == -1)
{
if (isContains(&openList, v.dest) == -1)
{
int vertex = v.dest;
std::cout <<"V: "<< vertex << std::endl;
int h = v.h;
int currentg = node->g + v.g;
int f = currentg + h;
std::cout <<"vertext: "<<vertex<< " h: " << h << " g: " << currentg << " f: " << f << std::endl;
Node* newNode = new Node(vertex, currentg, h, f,node->parent);
openList.push_back(newNode);
}
}
}
std::cout<<"Close: ";
for (size_t i = 0; i < closeList.size(); i++)
{
std::cout << closeList[i]->vertex << " ";
}
std::cout << std::endl;
sort(openList.begin(), openList.end(),comp());
std::cout << "Open: ";
for (size_t i = 0; i < openList.size(); i++)
{
std::cout << openList[i]->vertex << " ";
}
std::cout << std::endl;
std::cout << "end" << std::endl;
std::cout << std::endl;
}
}
void printPath(Node* node)
{
std::cout << std::endl;
if (node->parent != NULL)
printPath(node->parent);
std::cout << node->vertex << " ";
}
int isContains(std::vector<Node*>* Nodelist,int vertex)
{
for (int i = 0; i < Nodelist->size(); i++)
{
if (Nodelist->at(i)->vertex== vertex)
{
return i;
}
}
return -1;
}
int main()
{
//{Node,Node,G,H}
//Firt Node
//second Node
//G is the movement cost to move from the starting point to a given square on the grid
// following the path generated to get there
//H is the estimated movement cost to move from that given square on the grid to the final destination
std::vector<Edge>edges =
{
{0,1,5,17},
{0,2,5,13},
{1,0,5,16},
{1,3,3,16},
{1,2,4,13},
{2,0,5,16},
{2,1,4,17},
{2,3,7,16},
{2,4,7,16},
{2,7,8,11},
{3,2,7,13},
{3,7,11,11},
{3,10,16,4},
{3,11,13,7},
{3,12,14,10},
{4,2,7,13},
{4,5,4,20},
{4,7,5,11},
{5,4,4,16},
{5,6,9,17},
{6,5,9,20},
{6,13,12,7},
{7,3,11,16},
{7,4,5,16},
{7,8,3,10},
{8,7,3,11},
{8,9,4,8},
{9,8,4,10},
{9,13,3,7},
{9,15,8,0},
{10,3,16,16},
{10,11,5,7},
{10,13,7,7},
{10,15,4,0},
{11,3,13,16},
{11,10,5,4},
{11,12,9,10},
{11,14,4,5},
{12,3,14,16},
{12,11,9,7},
{12,14,5,5},
{13,9,3,8},
{13,10,7,4},
{13,15,7,0},
{14,11,4,7},
{14,12,5,10},
{15,9,8,8},
{15,10,4,4},
{15,13,7,7},
};
int n = edges.size();
Graph grap(edges, n);
//std::cout << h << std::endl;
Node* start = new Node(0);
Node* end = new Node(15);
findShortestPath(grap, start, end, n);
//Astar astar;
//Node* startPos = new Node(5, 1);
//Node* endPos = new Node(1, 8);
//astar.printMap();
//astar.search(startPos, endPos);
//cout << endl;
//astar.printMap();
system("pause");
return 0;
}
Your program doesn't find the shortest path. It gives the wrong output. (you're on the right track though)
I will assume you are trying to find the shortest path by using BFS. Let's take a look at line 113:
sort(openList.begin(), openList.end(),comp());
Here you're sorting your BFS queue (vector in your case) and thus destroying the right order.
Delete that line.
Congrats, now your program finds the shortest path!
Next, as I understand, for each node you branch into, you remember which node you came from in order to backtrack the path once you reach the destination or final node.
In line 102:
Node* newNode = new Node(vertex, currentg, h, f,node->parent);
you are assigning the new node's grandparent instead of parent. Change that line to
Node* newNode = new Node(vertex, currentg, h, f,node);
Now your printPath function works properly and prints the right path. (just add the target node)
Anyways, your code has a lot of space for improvements. Check out other implementations online and try to see if you can code it as short and clean for practice. Good luck!

Circular Queue That has a display function that displays even numbers only

hi so i have this circular queue c++ program i need its display function only displays even inserted numbers only can someone here help please here's the code
i need a way that this program only displays even numbers only i'v been trying to use the %2==0 on some location that might make sense but mostly i get them wrong or empty
.
.
.
.
.
.
#include <iostream>
using namespace std;
int cqueue[5];
int front = -1, rear = -1, n=5;
void insertCQ(int val) {
if ((front == 0 && rear == n-1) || (front == rear+1)) {
cout<<"Queue Overflow \n";
return;
}
if (front == -1) {
front = 0;
rear = 0;
} else {
if (rear == n - 1)
rear = 0;
else
rear = rear + 1;
}
cqueue[rear] = val ;
}
void deleteCQ() {
if (front == -1) {
cout<<"Queue Underflow\n";
return ;
}
cout<<"Element deleted from queue is : "<<cqueue[front]<<endl;
if (front == rear) {
front = -1;
rear = -1;
} else {
if (front == n - 1)
front = 0;
else
front = front + 1;
}
}
void displayCQ() {
int f = front, r = rear;
if (front == -1) {
cout<<"Queue is empty"<<endl;
return;
}
cout<<"Queue elements are :\n";
if (f <= r) {
while (f <= r){
cout<<cqueue[f]<<" ";
f++;
}
} else {
while (f <= n - 1) {
cout<<cqueue[f]<<" ";
f++;
}
f = 0;
while (f <= r) {
cout<<cqueue[f]<<" ";
f++;
}
}
cout<<endl;
}
int main() {
int ch, val;
cout<<"1)Insert\n";
cout<<"2)Delete\n";
cout<<"3)Display\n";
cout<<"4)Exit\n";
do {
cout<<"Enter choice : "<<endl;
cin>>ch;
switch(ch) {
case 1:
cout<<"Input for insertion: "<<endl;
cin>>val;
insertCQ(val);
break;
case 2:
deleteCQ();
break;
case 3:
displayCQ();
break;
case 4:
cout<<"Exit\n";
break;
default: cout<<"Incorrect!\n";
}
} while(ch != 4);
return 0;
}
Here's a quick example using std::queue:
#include <iostream>
#include <queue>
int main()
{
// This deque is declared just to more easily instantiate the queue
std::deque<int> deck{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::queue<int> q{deck};
while (!q.empty()) {
if (q.front() % 2 == 0) {
std::cout << q.front() << ' ';
}
q.pop();
}
std::cout << '\n';
}
You were on the right path using mod 2, but everything about your "queue" is not correct. To put it simply, you have not written a queue, and definitely not a circular queue.
Queues are FIFO data structures; first in, first out. Think of it like a tunnel. I will only add elements to one end, and I will only remove elements from the other. Everything in the middle does not matter, except to know the size of the queue. This means that the only visible elements are the first and last. If I want to see the second element of the queue, I must remove the first element.
The code you've written treats your array like a list instead where you have free access to all elements.
It's better to write an actual queue class, and I would hope that if you're writing data structures, that you are able to write a class.
Here's an extremely basic queue class that exhibits the behavior you are asking about. There is nothing circular about it. The word circular implies that your queue should be implemented with a circular linked list and not an array. It's worth noting that it requires at least C++11, but that really shouldn't be a problem today.
IMPORTANT
This code leaves out a lot of necessary error-checking. For example, back() will likely lead to undefined behavior if called on an empty queue, among other things. This was intentional to avoid a flat-out copy/paste being employed because cheating on homework is bad.
#include <array>
#include <iostream>
namespace Q {
class queue {
public:
int &front() { return m_arr[0]; }
int &back() { return m_arr[m_size - 1]; }
void push(int val) {
if (m_size < 5) {
m_arr[m_size] = val;
++m_size;
}
}
// Shifts entire array one to the left
void pop() {
if (m_size > 0) {
for (int i = 1; i < m_size; ++i) {
m_arr[i - 1] = m_arr[i];
}
--m_size;
}
}
bool empty() const { return m_size == 0; }
private:
std::array<int, 5> m_arr = {0};
int m_size = 0;
};
}; // namespace Q
int main() {
Q::queue q;
for (int i = 1; i <= 5; ++i) {
q.push(i);
}
while (!q.empty()) {
if (q.front() % 2 == 0) {
std::cout << q.front() << ' ';
}
q.pop();
}
std::cout << '\n';
}

Huffman Encoding priority queue

I'm working on an assignment where I should write an encoding and decoding application for the Huffman algorithm, based on a priority queue. We have to read a file, count the frequencies of the letters and then start the algorithm. I have the following problem:
My counting function works fine but it stores the frequency of every letter in an array - even if it's zero. But if I want to use that array to build my min heap I get major problems because of the zeros. Therefore I need to find a way to 'eliminate' them. I can't just skip them because then the min heap algorithm doesn't work anymore (wrong neighbours). So I wanted to transfer all non-zero entries in a vector and use the vector instead of the array. But there I always get an error that tells me that there's a problem with the vector size. I don't really know how to deal with that problem. (My min heap still uses the array because I can't even transfer the entries in a vector).
(Please ignore the main I was just trying stuff there!)
using namespace std;
struct huffman_node
{ char data;
int frequency;
bool vector;
huffman_node *left;
huffman_node *right;
};
void swap_huffman_nodes(huffman_node &a, huffman_node &b)
{ char store_data = a.data;
int store_frequency = a.frequency;
a.data = b.data;
a.frequency=b.frequency;
b.data = store_data;
b.frequency = store_frequency;
huffman_node *store_left = a.left;
huffman_node *store_right= a.right;
a.left = b.left;
a.right = b.right;
b.left = store_left;
b.right = store_right;
}
void print_node (huffman_node a)
{ cout << a.data << a.frequency << endl;
}
string line;
huffman_node Table[52];
vector <huffman_node> non_zero;
void build_table()
{ for (int i=1; i<27; i++)
{ Table[i].data = (char) (i+64);
Table[i].left = NULL;
Table[i].right = NULL;
}
for (int i=27; i<53; i++)
{ Table[i].data = (char) (i+70);
Table[i].left = NULL;
Table[i].right = NULL;
}
}
int counter =0;
void count(){
ifstream yourfile ("example.txt");
if (yourfile.is_open())
{
while ( getline (yourfile,line) )
{
/*cout << line << '\n'; */
unsigned long z=line.length();
int i=0;
while ( i < z)
{ /* cout << line[i] << endl; */
for (int j=65; j<91; j++)
{ if ((int) line[i] == j)
{ int k=-64+j;
Table[k].frequency++;
}
}
for (int j=97; j<123; j++)
{ if ((int) line[i] == j)
{ int k=-70+j;
Table[k].frequency++;
}
}
i++;
}
}
for (int i=1; i<53; i++)
{ if (Table[i].frequency!=0)
{ non_zero.push_back(Table[i]);
counter ++;
}
}
yourfile.close();
}
else cout << "Unable to open file";
}
class heap{
public:
void buildheap()
{
for (int i=1; i<53; i++)
{reheap(i);
};
}
void reheap(int new_index)
{ int parent_index = new_index/2;
while (parent_index > 0 && Table[parent_index].frequency > Table[new_index].frequency)
{ swap_huffman_nodes(Table[parent_index], Table[new_index]);
parent_index=parent_index/2;
new_index=new_index/2;
}
};
void delete_root()
{ int non_null_entries=0;
for (int i=1; i<53; i++)
{ if (Table[i].frequency!=-1) {non_null_entries++;};
}
swap_huffman_nodes(Table[1],Table[non_null_entries]);
Table[non_null_entries].frequency=-1;
non_null_entries--;
rebuild_heap_root_deletion(1, non_null_entries);
}
void rebuild_heap_root_deletion(int new_root,int non_null_entries){
int n;
if (2 * new_root > non_null_entries){
return;
}
if (2 * new_root + 1 <= non_null_entries
&& Table[2*new_root+1].frequency < Table[2*new_root].frequency){
n = 2 * new_root + 1;
} else {
n = 2 * new_root;
}
if (Table[new_root].frequency > Table[n].frequency){
swap_huffman_nodes(Table[new_root], Table[n]);
rebuild_heap_root_deletion(n, non_null_entries);
}
}
void add_element(huffman_node new_heap_element)
{ for (int i=52; i>0;i-- )
{ if (Table[i].frequency==-1 && Table[i-1].frequency!=-1)
{ Table[i]=new_heap_element;
reheap(i);
break;
}
}
}
void print_Table()
{
for (int i=1; i<53; i++)
{ /*if (Table[i].frequency != -1) */
cout << Table[i].frequency << " , " << Table[i].data << endl;
}
}
bool empty_heap() // a heap is empty here if there are only "invalid huffman nodes" in it except the first one that contains all information.
{ for (int i=2; i < 53; i++)
{ if (Table[i].frequency!=-1)
{ return false;}
}
return true;
}
};
int main(){
ofstream myfile ("example.txt");
if (myfile.is_open())
{
myfile << "Flori ist ein Koala.";
myfile << "";
myfile.close();
}
else cout << "Unable to open file";
build_table();
count();
heap allan;
cout << "\n";
allan.buildheap();
allan.print_Table();
int i=0;
/*while(i<500)
{
huffman_node base_1 = Table[1];
allan.delete_root();
huffman_node base_2 = Table[1];
allan.delete_root();
huffman_node parent;
parent.data = '/';
parent.frequency = base_1.frequency + base_2.frequency;
parent.left = &base_1;
parent.right = &base_2;
allan.add_element(parent);
i++;
}
return 0;
}

Adding multiple nodes to a tree C++

So I've been working on a project for school for sometime, and I've run up against a wall. My add_node function isn't working correctly, and I know why. What I'm trying to do is take in a file with multiple randomly generated letters, and create trees out of them, then make a confirmation.
The thing is that it overwrites the same node, instead of making multiple nodes. I figured this out using Visual studios debugger, but I have no idea what to implement to fix it. What happens is that instead of having multiple nodes create a tree (like gagtttca), it makes one node and overwrites it. The node becomes g, then a, etc. How would I go about adding more nodes to the tree without overwriting it? The add_node function is the very last one.
#include "stdafx.h"
#include <iostream>
#include <stack>
#include <fstream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
class myTreeNode
{
public:
char Data;
myTreeNode *childA; //A's always go in child1
myTreeNode *childT; //T's always go in child2
myTreeNode *childC; //c's always go in child3
myTreeNode *childG; //G's always go in child4
};
class Tree
{
public:
myTreeNode * Root;
Tree()
{
Root = new myTreeNode;
Root->Data = '-';
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
}
bool add_a_word(string word);
bool is_this_word_in_the_tree(string word);
bool add_node(myTreeNode * parent, char letter);
bool add_words(vector<string> w);
};
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words);
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads);
bool write_out_the_vector_to_screen(vector<string> my_vector);
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name);
ofstream out;
int main()
{
out.open("my_results.txt");
vector<string> words_in_genome;
char * genome_file_name = "my_genome.txt";//make certain to place this file in the correct folder. Do not change path.
if (!get_words_from_the_file(genome_file_name, words_in_genome))
return 1;
Tree * trees = new Tree();
trees->add_words(words_in_genome);
char * reads_file_name = "reads.txt"; //make certain to place this file in the correct folder. Do not change path.
if (!get_the_reads_from_file(reads_file_name, reads_to_be_tested))
return 1;
for (int i = 0; i < reads_to_be_tested.size(); i++)
{
out <<reads_to_be_tested[i] << " " << trees->is_this_word_in_the_tree(reads_to_be_tested[i]);
}
cout << "All done" << endl;
//Write out a file named "myResults.txt".
//For each read, list its sequence and either "Yes" or "No".
//This will indicate if it does or doesn't map to the genome.
/** Used for debugging
cout << "words" << endl;
write_vector_to_screen(words);
write_vector_to_file(words,"testing.txt");
cout << "reads" << endl;
write_vector_to_screen(reads);
***/
out.close();
}
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words)
{
int i, j;
int len = 0;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "I could not find " << my_file_name << endl;
cout << "Check the location.\n";
return false;
}
char * my_word = new char[11];
while (in.peek() != EOF) { in >> my_word[0]; len++; }
in.clear(); in.close(); in.open(my_file_name);
for (i = 0; i<10; i++)
{
in >> my_word[i];
if (my_word[i]<97) my_word[i] += 32; //makes it lowercase
}
my_word[10] = '\0';
vector_of_words.push_back(my_word);
for (i = 1; i<(len - 10 - 1); i++) //read until the end of the file
{
//shift
for (j = 0; j<9; j++) my_word[j] = my_word[j + 1];
in >> my_word[9];
if (my_word[9]<97) my_word[9] += 32; //makes it lowercase
my_word[10] = '\0';
cout << i << "\t" << my_word << endl; cout.flush();
vector_of_words.push_back(my_word);
}
in.clear(); in.close();
return true;
}
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads)
{
int i;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "The read file " << my_file_name << " could not be opened.\nCheck the location.\n";
return false;
}
char * word = new char[20]; //this is a default, we'll be looking at words of size 10
while (in.peek() != EOF)
{
in.getline(word, 20, '\n');
for (i = 0; i<10; i++) { if (word[i]<97) word[i] += 32; } //makes it lowercase
reads.push_back(word);
}
in.clear(); in.close();
delete word;
return true;
}
bool write_out_the_vector_to_screen(vector<string> my_vector)
{
int i;
for (i = 0; i < my_vector.size(); i++)
{
cout << my_vector[i].c_str() << endl;
}
return true;
}
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name)
{
ofstream out;
out.open(my_file_name);
int i;
for (i = 0; i<my_vector.size(); i++)
out << my_vector[i].c_str()<< endl;
out.clear();
out.close();
return true;
}
bool Tree::add_words(vector<string> w)
{
for (int i = 0; i < w.size(); i++)
add_a_word(w[i]);
return true;
}
bool Tree::add_a_word(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
tempNode = tempNode->childA;
else
{
add_node(tempNode, word[i]);//add a node: what letter, who's my parent
tempNode = tempNode->childA;
}
}
else if (word[i]== 'g')
{
if (tempNode->childG != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childG;
}
}
else if (word[i] == 'c')
{
if (tempNode->childC != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childC;
}
}
else if (word[i] == 't')
{
if (tempNode->childT != NULL)
tempNode = tempNode->childT;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childT;
}
}
else
{
cout << "The tree is full, or can't find data" << endl;
return NULL;
break;
}
}
}
}
}
bool Tree::is_this_word_in_the_tree(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
char com1, com2, com3, com4;
if (tempNode == NULL)
{
cout << "The tree is empty. Sorry" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
{
if (tempNode->childA)
{
tempNode = tempNode->childA;
com1 = 'y';
}
}
else
{
com1 = 'n';
}
}
if (word[i] == 'g')
{
if (tempNode->childG != NULL)
{
if (tempNode->childG)
{
tempNode = tempNode->childG;
com2 = 'y';
}
}
else
{
com2 = 'n';
}
}
if (word[i] == 't')
{
if (tempNode->childT != NULL)
{
if (tempNode->childT)
{
tempNode = tempNode->childG;
com3 = 'y';
}
}
else
{
com3 = 'n';
}
}
if (word[i] == 'c')
{
if (tempNode->childC != NULL)
{
if (tempNode->childC)
{
tempNode = tempNode->childC;
com4 = 'y';
}
}
else
{
com4 = 'n';
}
}
}
out << com1 << com2 << com3 << com4 << endl;
if (com1 == com2 == com3 == com4)
{
out << "The test passed" << endl;
}
else
{
out << "The test failed" << endl;
return false;
}
}
}
return true;
}
bool Tree::add_node(myTreeNode * parent, char letter)
{
//Can't figure out how to fix error. Run-Time error is that it overwrites the node instead of adding it.
//How would i make it so it's a new node every time?//
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Like I stated before, this is a project. I'm not here looking for an easy way out. I just want learn how to fix my code.
The most fundamental problem in your code is the simple obviousness that you're not comfortable using pointers. From the looks of it you may have come from other languages where the vernacular of:
Type *p = new Type;
p = Something;
was common. It is anything-but-common in C++. As in C, dynamic allocation is managed by a returned address, which is saved, cared for, and if all goes well, eventually disposed of. Those addresses are kept in pointer variables. Pointers in C++ don't hold objects; they hold addresses.
That said, I'm not going to destroy everything you wrote. I'm not going to sugar coat this; it would be shooting fish in a barrel. I'm rather going to describe what you should be doing in add_node, show you where you went wrong, and finally proffer up a simple example that eliminates much of the cruft (file io, etc) in your existing code, focusing rather on the real problem at hand: tree node management and the pointer-jockeying that is needed to accomplish it.
The Task
You should be starting at the root node, and for each successive letter in your string, move down the tree. When you encounter a path you want to take, but can't because there is no node hanging there yet, that is when you allocate a new node, hang it, move to it, and continue the process until there are no more characters in your input string.
Your Code
That said, review the comments in the following
bool Tree::add_node(myTreeNode * parent, char letter)
{
myTreeNode * tempNode = new myTreeNode;
// this is outright wrong. you just leaked the memory
// you allocated above. this has no place here and
// should be removed.
//
// Note: the remainder of this analysis will assume you
// have, in fact, removed this line.
tempNode = Root;
// all of this belongs in your myTreeNode constructor.
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
// this is flat-out impossible. Assuming you fixed your incorrect
// Root assignment mentioned above, you just allocated a new node
// therefore this can NEVER be NULL (an exception would have thrown
// on a failure to allocate).
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
// This NEVER changes. Nowhere in the code below this is
// tempNode ever assigned a different value. this loop
// should not even be here. A simple if-else-if stack or
// a switch on letter is all that is needed.
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Sample Code
The following strips out all the file io, and most of the insanity regarding managing the tree. There are only two member functions, add_word and has_word (the latter used to validate something is indeed present).
What makes this code work is how a pointer-to-pointer is used in the addition and check functions add_word and has_word. For addition, we start at the root node pointer, and with each successive character in the input string, move down the tree. When a child pointer is hit that is NULL, we allocate a new node, hang it, and move on. The check function has_word does exactly the same thing, save for one difference: it doesn't hang new nodes. When it encounters a NULL where there shouldn't be one, it means something is wrong and the input word is not in the tree.
#include <iostream>
#include <random>
#include <string>
struct myTreeNode
{
char data;
myTreeNode *childA;
myTreeNode *childT;
myTreeNode *childC;
myTreeNode *childG;
myTreeNode( char c )
: data(c), childA(), childT(), childC(), childG()
{
}
~myTreeNode()
{
delete childA;
delete childT;
delete childC;
delete childG;
}
// squelch these
myTreeNode(const myTreeNode&) = delete;
myTreeNode& operator=(const myTreeNode&) = delete;
};
class Tree
{
private:
myTreeNode *Root;
public:
Tree() : Root( new myTreeNode('-')) { }
~Tree() { delete Root; }
// squelch these
Tree(const Tree&) = delete;
Tree& operator =(const Tree&) = delete;
// adds a given string into the tree if it isn't already there.
void add_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default:
std::cerr << "skipping unsupported char '" << c << "'\n";
}
if (!*pp)
*pp = new myTreeNode(c);
}
}
// returns true if the given string is in the tree
bool has_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default: // should never happen with proper input
return false;
}
if (!*pp)
return false;
}
return true;
}
};
////////////////////////////////////////////////////////////////////////////////
int main()
{
// setup a random device and some uniform distributions
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<> dchar(0,3);
std::uniform_int_distribution<> dlen(3,8);
// our restricted alphabet. random indexes for creating our
// strings will be coming by indexing with dchar(rng)
char s[] = {'a', 't', 'c', 'g' };
// build set of random strings
std::vector<std::string> strs;
for (int i=0; i<20; ++i)
{
std::string str;
int len = dlen(rng);
for (int j=0; j<len; ++j)
str.push_back(s[dchar(rng)]); // push random char
strs.emplace_back(str);
}
// drop list of strins into tree
Tree tree;
for (auto const& str : strs)
{
std::cout << str << '\n';
tree.add_word(str);
}
// now verify every string we just inserted is in the tree
for (auto const& str : strs)
{
if (!tree.has_word(str))
{
std::cerr << "Word \"" << str << "\" should be in tree, but was NOT\n";
std::exit(EXIT_FAILURE);
}
}
std::cout << "All test words found!!\n";
return EXIT_SUCCESS;
}
Output (varies due to random generators)
gctccgga
agtccatt
gagcg
gtggg
tca
aga
cacaggg
cga
tgga
ttatta
cagg
aac
tatttg
gccttat
acctcca
tgagac
aagacg
tgc
aaccgg
tca
All test words found!!
Summary
I strongly advise you run this in the debugger and step through it with a firm grasp on the watch-window. Follow pointer trails to see how things set up as the program progresses. There are many things I did not talk about: proper construction, initialization, Rule of Three compliance etc. I also could have (and would have had this not been an academic case) used smart pointers such as std::unique_ptr<> or std::shared_ptr<>. I sincerely hope you get something out of this. It's only going to get worse from here.
Best of luck
I don't know why but
this :
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
Doesn't look right for me, haven't done c++ for a while and nodes but i don't think that's how you gotta do it? Will check and edit this.

For loop and vectors causing trouble

I am trying to create a for loop that runs through a vector, pushes something into a queue, updates all appropriate elements of the struct of the vector and then loops again until the vector is empty.
The problem I am having is that the function to run through my for loop does not seem to be updating my vector elements, I am getting the same output even though I know it should be changing.
Here is the link to a gist of the project https://gist.github.com/sgodfrey321/6cffd85896432b2942aa , it just has a lot of filler, I had to input alot of stuff by hand so it is kinda messy.
So to start with I pass my vector to the function in a while loop
void queueNodes(vector<vertex>& list, queue<vertex>& q);
as such
int counter = 0;
while (counter < 11) {
queueNodes(nodes, q);
counter++;
}
the while loop is to make sure that I run through the vector enough times. Now in the function I check to see if incomingEdges of the struct is 0 and if it is I push it into the queue and update the next door incomingEdges:
void queueNodes(vector<vertex>& nodes, queue<vertex>& q) {
for (auto i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
q.push(i);
cout << "foo";
} else {
cout << "bar";
}
}
Now I would expect that when I use the function again with the updated vector I would see a change in output as updating the next door incomingEdges would cause some to trigger the condition I am looking for. I have written outputs that show the next door incomingEdges are in fact decreasing however I can not seem to use the updated vector in the function call.
Do I need to return the vector somehow? I am sure that I am calling the function incorrectly, but does anyone have any ideas?
Thank You
edit: forgot to pass by reference the queue!
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct vertex {
char vertexName;
int incomingEdges;
vertex* nextDoorTop;
vertex* nextDoorMiddle;
vertex* nextDoorBottom;
};
void queueNodes(vector<vertex>& nodes);
int main() {
vertex s, A, G, D, B, H, E, C, I, F, t;
s.vertexName = 's';
s.incomingEdges = 0;
s.nextDoorTop = &A;
s.nextDoorMiddle = &D;
s.nextDoorBottom = &G;
A.vertexName = 'A';
A.incomingEdges = 2;
A.nextDoorTop = &B;
A.nextDoorMiddle = &E;
G.vertexName = 'G';
G.incomingEdges = 1;
G.nextDoorTop = &D;
G.nextDoorMiddle = &E;
G.nextDoorBottom = &H;
D.vertexName = 'D';
D.incomingEdges = 2;
D.nextDoorMiddle = &E;
B.vertexName = 'B';
B.incomingEdges = 1;
B.nextDoorTop = &C;
H.vertexName = 'H';
H.incomingEdges = 1;
H.nextDoorTop = &E;
H.nextDoorMiddle = &I;
E.vertexName = 'E';
E.incomingEdges = 4;
E.nextDoorTop = &C;
E.nextDoorMiddle = &F;
E.nextDoorBottom = &I;
C.vertexName = 'C';
C.incomingEdges = 3;
C.nextDoorMiddle = &t;
I.vertexName = 'I';
I.incomingEdges = 2;
I.nextDoorTop = &F;
I.nextDoorMiddle = &t;
F.vertexName = 'F';
F.incomingEdges = 2;
F.nextDoorMiddle = &t;
t.vertexName = 't';
t.incomingEdges = 3;
vector<vertex> nodes { s, A, G, D, B, H, E, C, I, F, t };
cout << "Vertex Name: " << " Number Of Edges: " << endl;
for (auto i : nodes) {
cout << i.vertexName << " " << i.incomingEdges << " "
<< endl;
}
int counter = 0;
while (counter < 11) {
queueNodes(nodes);
counter++;
}
return 0;
}
void queueNodes(vector<vertex>& nodes) {
for (auto& i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
cout << "foo";
} else {
cout << "bar";
}
}
}