Returning a vector from a function in order to print the contents - c++

My program has a function named 'WordLadder' that uses BFS to make a path from one word in a dictionary to another. I was given a starter code that prints the number of nodes in the path, but I want to print the path itself. Currently, I have appended the words to a vector as they enter a queue but I am not able to return the vector as part of my 'WordLadder' function in order to print it in the main program.
I just want the program to print a path based on the two words I picked, i.e. "TOON - POON - POIN - POIE - PLIE - PLEE - PLEA", if the start word is "TOON" in the dictionary and the target word is "PLEA" in the dictionary.
I tried to declare the vector outside of the function and print it in main with this code, but I was unsuccessful.
void print(std::vector < int >
const & transformation) {
std::cout << "The vector elements are : ";
for (int i = 0; i < transformation.size(); i++)
std::cout << transformation.at(i) << ' ';
}
I have attempted to return the vector inside of the function, but I receive this error
error: no viable conversion
from returned value of type
'vector<std::__cxx11::string>' (aka
'vector<basic_string<char> >') to
function return type 'int'
return transformation;
Here is my code. Any help would be appreciated, as I am new to C++.
// To check if strings differ by exactly one character
bool nextWord(string & a, string & b) {
int count = 0; // counts how many differences there
int n = a.length();
// Iterator that loops through all characters and returns false if there is more than one different letter
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
count++;
}
if (count > 1) {
return false;
}
}
return count == 1 ? true : false;
}
// A queue item to store the words
struct QItem {
string word;
};
// Returns length of shortest chain to reach 'target' from 'start' using minimum number of adjacent moves. D is dictionary
int wordLadder(string & start, string & target, set < string > & ew) {
//Create vector to store path in a
vector < string > transformation;
// Create a queue for BFS and insert 'start' as source vertex
queue < QItem > Q;
QItem item = {
start
};
Q.push(item);
// While queue is not empty
while (!Q.empty()) {
// Take the front word
QItem curr = Q.front();
transformation.push_back(Q.front().word);
Q.pop();
// Go through all words of dictionary
for (set < string > ::iterator it = ew.begin(); it != ew.end(); it++) {
// Proccess the next word according to BFS
string temp = * it;
if (nextWord(curr.word, temp)) {
// Add this word to queue from the dictionary
item.word = temp;
Q.push(item);
// Pop from dictionary so that this word is not repeated
ew.erase(temp);
// If we reached target
if (temp == target) {
return trasformation;
}
}
}
}
return 0;
}
// Driver program
int main() {
string start;
string target;
// make dictionary
std::ifstream file("english-words.txt");
set < string > ew;
copy(istream_iterator < string > (file),
istream_iterator < string > (),
inserter(ew, ew.end()));
cout << endl;
cout << "Enter Start Word" << endl;
cin >> start;
cout << "Enter Target Word" << endl;
cin >> target;
cout << wordLadder(start, target, ew);
return 0;
}

There are multiple problems.
You were on the right track when you said "I tried to declare the vector outside of the function and print it in main..."
So, change wordLadder to take the vector by reference.
int wordLadder(vector<string> &transformation, string & start, string & target, set < string > & ew)
Then declare it in main and pass it to wordLadder
vector<string> t;
wordLadder(t, start, target, ew);
print(t);
You will have to also change print to take a vector of the right type, ie. string and not int
void print(std::vector < string > &transformation)

Related

How can you check if a sequence of nodes exists in an undirected graph, where each node is adjacent to the next?

I have an undirected graph of letters in a rectangular format, where each node has an edge to the adjacent neighboring node.
For example:
d x f p
o y a a
z t i b
l z t z
In this graph node "d" is adjacent to [x, y, o].
I want to check if the sequence of nodes "dot" exists in the graph, where each subsequent node is adjacent to the next. The main application is a word search game, where only words with adjacent letters count. For example, the sequence "zap" does NOT count, since the nodes are not adjacent. I do not need to check if the sequence is a real word, only that it is adjacent in the graph.
My graph.h is as follows:
// graph.h
#include <queue>
#include "SLList.h"
#include "DynArray.h"
template<typename Type>
class Graph {
public:
struct Edge {
unsigned int toVertex; // index to vertex the edge connects to
};
struct Vertex {
// the data that this vertex is storing
Type element;
// the list of edges that connect this vertex to another vertex
SLList<Edge> edges;
///////////////////////////////////////////////////////////////////////////
// Function : addEdge
// Parameters : toVertex - the index of the vertex we are adjacent to
///////////////////////////////////////////////////////////////////////////
void addEdge(const unsigned int& toVertex) {
Edge e;
e.toVertex = toVertex;
edges.addHead(e);
}
};
private:
// dynarray of vertices
DynArray<Vertex> vertices;
// helper function to check if a vertex is a in a queue
bool IsInQueue(DynArray<Edge> arrayOfEdges, unsigned int _toVertex) {
for (unsigned int i = 0; i < arrayOfEdges.size(); ++i) {
if (arrayOfEdges[i].toVertex == _toVertex)
return true;
}
return false;
}
public:
/////////////////////////////////////////////////////////////////////////////
// Function : addVertex
// Parameters : value - the data to store in this vertex
// Return : unsigned int - the index this vertex was added at
/////////////////////////////////////////////////////////////////////////////
unsigned int addVertex(const Type& value) {
Vertex v;
v.element = value;
vertices.append(v);
return vertices.size();
}
/////////////////////////////////////////////////////////////////////////////
// Function : operator[]
// Parameters : index - the index in the graph to access
// Return : Vertex& - the vertex stored at the specified index
/////////////////////////////////////////////////////////////////////////////
Vertex& operator[](const unsigned int& index) {
return vertices[index];
}
/////////////////////////////////////////////////////////////////////////////
// Function : size
// Return : unsiged int - the number of vertices in the graph
/////////////////////////////////////////////////////////////////////////////
unsigned int size() const {
return vertices.size();
}
/////////////////////////////////////////////////////////////////////////////
// Function : clear
// Notes : clears the graph and readies it for re-use
/////////////////////////////////////////////////////////////////////////////
void clear() {
// for each node, remove all its edges
// then remove the node from the array
for (unsigned int i = 0; i < vertices.size(); ++i) {
vertices[i].edges.clear();
}
vertices.clear();
}
};
So far I tried:
my algorithm:
finding the starting node
setting a current node to this start node
searching all edges of the current node for the next node in sequence without visiting nodes that have been visited
if next node in sequence is found then current is set to next and next is incremented
if current == end and next == null then return true
else false
However, this does not work every time. For example, it works for "dot", but not "pay" in the above graph. This is because once it visits the second "a" it marks as visited and cannot find "y" anymore. I believe there are other problems with this algorithm.
I have searched other answers on here, but they only explain how to find a path from a start node to an end node, where the path doesn't matter. In this case, the path is what matters.
Solution in c++ using my graph.h preferred.
Here is a simple Depth-First Search-based procedure that attempts to find a path that creates a specified string in a grid of characters. This DFS is an example of a basic brute-force algorithm, as it simply tries all possible paths that could be right. In the below program, I use my own Graph class (sorry), but it should be simple enough to understand. Here is my code in C++:
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
struct Graph{
int rows, cols;
vector <vector<char>> grid;
// DFS: Recursively tries all possible paths - SEE THE BELOW FUNCTION FIRST
void dfs(int r, int c, size_t len, string &str, bool &done, auto &vis, auto &path){
// if (len == str.size()), that means that we've found a path that
// corresponds to the whole string, meaning that we are done.
if(len == str.size()){
done = true;
return;
}
// Check all nodes surrounding the node at row r and column c
for(int next_r = r-1; next_r <= r+1; ++next_r){
for(int next_c = c-1; next_c <= c+1; ++next_c){
// Bounds check on next_r and next_c
if(next_r < 0 || next_r >= rows){continue;}
else if(next_c < 0 || next_c >= cols){continue;}
// KEY: We don't visit nodes that we have visited before!
if(vis[next_r][next_c]){
continue;
}
// ONLY if grid[next_r][next_c] happens to be the next character in str
// that we are looking for, recurse.
if(grid[next_r][next_c] == str[len]){
vis[next_r][next_c] = true;
path.push_back({next_r, next_c});
dfs(next_r, next_c, len + 1, str, done, vis, path);
// If done is true, that means we must've set it to true in
// the previous function call, which means we have found
// a valid path. This means we should keep return-ing.
if(done){return;}
vis[next_r][next_c] = false;
path.pop_back();
}
}
if(done){return;} // see the above comment
}
}
// Returns a vector <pair<int, int>> detailing the path, if any, in the grid
// that would produce str.
vector <pair<int, int>> get_path_of(string &str){
bool done = false;
vector <pair<int, int>> path;
// Try starting a DFS from every possible starting point until we find a valid
// path
for(int r = 0; r < rows; ++r){
for(int c = 0; c < cols; ++c){
vector <vector<bool>> vis(rows, vector <bool> (cols, false));
dfs(r, c, 0, str, done, vis, path);
// Found a path during the above function call! We can return now
if(done){
return path;
}
}
}
return {};
}
Graph(int r, int c){
rows = r;
cols = c;
grid = vector <vector<char>> (r, vector <char> (c));
}
};
int main()
{
// Input in the number of rows and columns in the grid
int R, C;
cin >> R >> C;
Graph G(R, C);
// Input the letters of the grid to G
for(int i = 0; i < R; ++i){
for(int j = 0; j < C; ++j){
cin >> G.grid[i][j];
}
}
// Input the strings to find in G
string str;
while(cin >> str){
vector <pair<int, int>> path = G.get_path_of(str);
cout << "PATH OF " << str << ": ";
for(const pair <int, int> &item : path){
cout << "{" << item.first << ", " << item.second << "} ";
}
cout << "\n";
}
return 0;
}
If you have any questions, please don't hesitate to ask!

C++ How to find the longest possible combination of decreasing numbers in an array

I am working on a problem in which I'm given a list of numbers representing the diameter of cake layers (for example: 9 12 10 7 4 6 11 5). With this list, I have to find the length of the longest combination of numbers that are equal to or decreasing (stacking cake layers from greatest diameter at the bottom to smallest at the top). You are allowed to skip over numbers, but you can't come back to them. I.e. with the previous list, the length of the longest combination would be 5 with the combination being (12,10,7,6,5).
I believe that the best way to solve this would be feeding the array into a tree and returning the height of the tree. This is currently the code I have, with a working tree implementation above the main
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string sizeInput, transfer; //Strings to hold input and transfer to array
int maxLayers = 0, numOfInputs = 0, numNodes = 0; //ints for holding the max height and the number of inputs by the user
int cakeSizes [30]; //Array holding sizes of the cakes input, no more than 30
cout << "Cake sizes: ";
getline(cin,sizeInput); //Gets input from user and puts into a stringstream
stringstream readInput(sizeInput);
while(readInput >> transfer)
{
cakeSizes[numOfInputs] = stoi(transfer); //Puts the numbers into the array and counts how many were placed
numOfInputs++;
}
for(int i=0; i<numOfInputs; i++) //Puts the array into a tree
{
Tree<int> cakeStack; //Creates tree to hold combination
initialize(cakeStack);
for(int j=i; j<numOfInputs; j++)
{
if(cakeSizes[j]<=cakeSizes[j-1])
{
insert(cakeStack, cakeSizes[j]);
}
}
if(height(cakeStack) > maxLayers) //Checks if the new combination tree's height is greater than the last
{
maxLayers = height(cakeStack);
}
destroy(cakeStack); //Destroys the tree from the previous combination in preparation for new one
}
cout << endl << "You can build a cake with " << maxLayers << " layers.";
}
This actually works for combinations that are always decreasing (like 5,4,2,1 and 8,3,2,1), but it fails when interrupting numbers are thrown in (like with 5,4,2,8,1). I'm almost certain that the problem lies here:
for(int j=i; j<numOfInputs; j++)
{
if(cakeSizes[j]<=cakeSizes[j-1])
{
insert(cakeStack, cakeSizes[j]);
}
}
But I'm unsure of how to implement it an a way that checks all combinations of the array (like skipping numbers that wouldn't give the longest combination), rather than running straight down the list unable to skip numbers.
The tree is definitely the way to go. You build the tree by inserting each value under the smallest node larger than it. Then when the tree is finished you iterate through it looking for the longest path.
What I did in the code below is I made a head node to store the sub trees and it needed a really large value so that all the inputs would fit under it. But then when I print the tree or look for a path I need to ignore that head node, so I have to keep track of the depth.
#include <iostream>
#include <vector>
#include <climits>
struct Tree {
Tree(int value) : value(value) {}
int value;
std::vector<Tree> children;
};
// Recursively check this level of the tree
void insert_node(Tree& node, int value)
{
// if the new value is bigger than where
// we are then stop descending
if (value > node.value)
return;
// if the new value fits under this
// parent then check all the children
bool inserted = false;
for (Tree& child : node.children)
// if we find a child large enough
// then insert ourselves inside
if (value < child.value)
{
insert_node(child, value);
inserted = true;
}
// if the new value fits under this parent but
// not under any of the children then put it here
if (!inserted)
node.children.push_back(value);
}
void print_tree(Tree node,
std::vector<bool> flags = std::vector<bool>(100, true),
bool last = false,
int depth = 0)
{
for (int i = 1; i < depth; ++i)
{
if (flags[i])
std::cout << "| ";
else
std::cout << " ";
}
// Don't print our fake head
if (depth > 0)
{
std::cout << "+- " << node.value << '\n';
if (last) flags[depth] = false;
}
int n = 0;
for (Tree child : node.children)
{
last = (n++ == node.children.size() - 1);
print_tree(child, flags, last, depth + 1);
}
flags[depth] = true;
}
void print_path(std::vector<int> path)
{
std::cout << "Path:";
for (int value : path)
std::cout << " " << value;
std::cout << "\n";
}
void print_paths(Tree node,
std::vector<int>& max_path,
std::vector<int> path = std::vector<int>(),
int depth = 0)
{
// Don't add our fake head
if (depth > 0)
path.push_back(node.value);
if (node.children.size() == 0)
{
print_path(path);
// check if this path is the longest one yet
if (max_path.size() < path.size())
max_path = path;
}
for (Tree child : node.children)
print_paths(child, max_path, path, depth + 1);
}
int main()
{
Tree head(INT_MAX);
std::vector<int> input = {9, 12, 10, 7, 4, 6, 11, 5};
// Build the tree
for (int value : input)
insert_node(head, value);
// Print the tree
std::cout << "Tree:\n";
print_tree(head);
std::cout << "\n";
// Print the paths and
// find the longest one
// and then print it too
std::vector<int> max_path;
print_paths(head, max_path);
std::cout << "\nLongest ";
print_path(max_path);
return 0;
}

Incorrect count output / Having difficulty trying to create a HashTable/Set using open addressing

I'm trying to create a program that opens a .txt file containing a speech and assigns each word to a space in the array/set based on the hash value. Collisions are accounted for using open addressing method. The program should be able to perform the following functions: add(), remove(), find(), count() which keeps count of the elements IN the array/set, and loadfactor(). A template header.h file was provided that required some filling in, but my unfamiliarity with that style of coding was making it difficult for me to understand it. Below I have provided the code I have so far, and everything seems to be working except the mCount. The speech contains about 300 words but when I run the code, the count output shows 17. I'm assuming the error is in my resizing function but I am unsure.
//hashset.h file
#pragma once
#include <cmath>
#include <functional>
#include <vector>
template <typename TValue, typename TFunc>
class HashSet
{
private:
// Unlike Java, the C++ hashtable array won't be full of null references.
// It will be full of "Entry" objects, each of which tracks its current state
// as EMPTY, FULL (with a value), or NIL (value was once here, but was removed).
class Entry
{
public:
enum EntryState { EMPTY, FULL, NIL };
TValue value;
EntryState state;
Entry() : value(), state(EMPTY) {}
Entry(const TValue& v) : value(v), state(EMPTY) {}
};
TFunc mHash;
std::vector<Entry> mTable;
std::size_t mCount;
public:
// Constructs a hashtable with the given size, using the given function for
// computing h(k).
// hash must be a callable object (function, functor, etc.) that takes a parameter
// of type TValue and returns std::size_t (an integer).
HashSet(int size, TFunc hash) : mHash(hash)
{
// initialize count
mCount = 0;
// hashtable array cannot be same data type as that of what is being stored (cannot be string)
// requirement #4 - if user inputs array size that is not a power of 2, constructor must round to nearest power of 2 value
size = pow(2, (int(log(size - 1) / log(2)) | 1));
mTable.resize(size); // resizes the vector to have given size.
// Each element will be default-constructed to have state EMPTY.
}
void resize(int new_size) {
HashSet aux{ new_size, mHash }; //double the size, same hash function
for (const auto& entry : mTable)
if (entry.state == Entry::FULL && entry.state == Entry::EMPTY && entry.state == Entry::NIL) //there is an element
aux.add(entry.value); //insert it on the new set
*this = aux;
}
// Inserts the given value into the set.
void add(const TValue& value)
{
// Use the type std::size_t for working with hash table indices.
// Invoke the mHash function, passing the key to calculate h(k), as in
// size_t hashCode = mHash(value);
// Mod down to size.
// Go to the table at that index and do the insertion routine.
// Note, if table is full when trying to add an element, it should double in size
// to keep table size a power of 2
if (double(mCount) / mTable.size() > 0.8) // load factor comparison
this->resize(2 * mTable.size()); // call resize function if array is too small to accommodate addition
size_t hashCode = mHash(value) % mTable.size(); // mod value by table size to get starting index
if (mTable[hashCode].state == Entry::EMPTY || mTable[hashCode].state == Entry::NIL) { // NIL space CAN be replaced with value
mTable[hashCode].value = value; // store value in vector index specified by hashCode
mCount++; // increment counter when word is added
}
else {
for (std::size_t i = 1; i < mTable.size(); i++) {
// use open addressing to find next open space
if (mTable[hashCode].state != Entry::EMPTY) {
hashCode = ((mHash(value) % mTable.size()) + ((int)(pow(i, 2) + i) >> 1)) % mTable.size(); // h(k) + f(i) or h(k) + ((i^2 + i)) / 2
}
else if (mTable[hashCode].value == value) { // account for duplicates
break; // exit for-loop
}
else if (mTable[hashCode].state == Entry::EMPTY || mTable[hashCode].state == Entry::NIL) { // NIL space CAN be replaced with value
mTable[hashCode].value = value; // store value in vector index specified by new hashCode
mCount++; // increment counter when word is added
break; // exit for-loop
}
else
break; // exit for-loop
}
}
}
// Returns true if the given value is present in the set.
bool find(const TValue& key)
{
size_t hashCode = mHash(key) % mTable.size(); // mod value by table size to get starting index to do retrace
if (mTable[hashCode].value == key)
return true;
else if (mTable[hashCode].state != Entry::EMPTY || mTable[hashCode].state == Entry::NIL) { // check that set is not empty or has a NIL state
for (std::size_t i = 1; i < mTable.size(); i++) {
// use open addressing again to find key
if (mTable[hashCode].value != key)
hashCode = ((mHash(key) % mTable.size()) + ((int)(pow(i, 2) + i) >> 1)) % mTable.size();
else if (mTable[hashCode].value == key) {
return true; // value found at speecified location
break; // exit for-loop as first instance of value has been found
}
//else if (i == mTable.size()) // end of table reached, element not in set
//return false;
}
}
else // end of table reached, element was not in set
return false;
}
// Removes the given value from the set.
void remove(const TValue& key)
{
size_t hashCode = mHash(key) % mTable.size(); // mod value by table size to get starting index to do retrace
if (mTable[hashCode].value == key) {
mTable[hashCode].value = Entry::NIL; // replace value with NIL so find() op does not return a false when searching for element
mCount--; // decrement element counter
}
else if (mTable[hashCode].state != Entry::EMPTY || mTable[hashCode].state != Entry::NIL) { // check that there is a value to be removed
for (std::size_t i = 1; i < mTable.size(); i++) {
// use open addressing again to find key
if (mTable[hashCode].value != key) {
hashCode = ((mHash(key) % mTable.size()) + ((int)(pow(i, 2) + i) >> 1)) % mTable.size();
}
else {
mTable[hashCode].value = Entry::NIL; // if found after open addressing, replace with NIL
mCount--; // decrement element counter
}
}
}
}
int count() {
return mCount;
}
double loadFactor() {
double a = double(mCount) / mTable.size();
return a;
}
};
// main function
#include "hashset.h"
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string testline;
vector<string> word;
HashSet<std::string, std::hash<std::string> > obj1{ 50, std::hash<std::string>{} };
ifstream Test("speech.txt");
if (!Test)
{
cout << "There was an error opening the file.\n";
return 0;
}
//store words in vector
while (Test >> testline) {
word.push_back(testline);
//obj1.add(testline);
}
//output whole vector with position numbers for each entry
cout << "Array contents:\n";
for (int i = 0; i < word.size(); i++) {
obj1.add(word[i]);
cout << word[i] << "(" << i << ")" << endl;
}
cout << "current count: " << obj1.count() << endl;
obj1.add("abcd"); // should hash to 4
if (obj1.find("abcd"))
cout << "abcd is in the set " << endl;
else
cout << "abcd is not in set " << endl;
obj1.add("adcb"); // should hash to 4 then 5 after probing
if (obj1.find("adcb"))
cout << "adcb is in the set " << endl;
else
cout << "adcb is not in set " << endl;
obj1.add("acde"); // should hash to 4 then 7 after probing
if (obj1.find("acde"))
cout << "acde is in the set " << endl;
else
cout << "acde is not in set " << endl;
obj1.remove("adcb"); // 5 should have NIL
if (obj1.find("adcb"))
cout << "adcb is in the set " << endl;
else
cout << "adcb is not in set " << endl;
if (obj1.find("acde"))
cout << "acde is still in the set " << endl;
else
cout << "acde is not in set " << endl;
cout << "final count: " << obj1.count() << endl;
system("pause");
exit(0);
}
}
The errors around NIL are because the enum defining NIL is part of the Entry class. You need to prefix NIL with the class name so the compile knows where the keyword comes from.
else if (mTable[hashCode] != NULL || mTable == Entry::NIL) { // getting error NIL identifier not found
The HashSet variable declaration is complaining because you are passing the wrong types. HashSet constructor takes a size and and a hash function. You are passing it a size and a string. Note the comment above the HashSet constructor
// hash must be a callable object (function, functor, etc.) that takes a parameter
// of type TValue and returns std::size_t (an integer).
This is your clue how to construct a HashSet object.

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;
}

push attribute data to trie, add to multiple keys

my knowledge is limited but I have been working (hacking) at this specific data structure for awhile
I use a trie to store ontology strings that are then returned as a stack including the 'gap' proximity when get (string) is called. As an add on the trie stores attributes on the key. The further down the string the greater the detail of the attribute. This is working well for my purposes.
As an additional add on, I use a wildcard to apply an attribute to all child nodes. For example, to add 'paws' to all subnodes of 'mammals.dogs.' I push(mammals.dogs.*.paws). Now, all dogs have paws.
The problem is only the first dog get paws. The function works for push attributes without wild
If you want I can clean this up and give a simplified version, but in the past i've found on stackoverflow it is better to just give the code; I use 'z' as the '*' wild
void Trie::push(ParseT & packet)
{
if (root==NULL) AddFirstNode(); // condition 1: no nodes exist, should this be in wrapper
const string codeSoFar=packet.ID;
AddRecord(root, packet, codeSoFar); //condotion 2: nodes exist
}
void Trie::AddFirstNode(){ // run-once, initial condition of first node
nodeT *tempNode=new nodeT;
tempNode->attributes.planType=0;
tempNode->attributes.begin = 0;
tempNode->attributes.end = 0;
tempNode->attributes.alt_end = 0;
root=tempNode;
}
//add record to trie with mutal recursion through InsertNode
//record is entered to trie one char at a time, char is removed
//from record and function repeats until record is Null
void Trie::AddRecord(nodeT *w, ParseT &packet, string codeSoFar)
{
if (codeSoFar.empty()) {
//copy predecessor vector at level n, overwrites higher level vectors
if (!packet.predecessorTemp.empty())
w->attributes.predecessorTemp = packet.predecessorTemp;
return; //condition 0: record's last char
}
else { //keep parsing down record path
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char || codeSoFar[0] == 'z') {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
InsertNode(w, packet, codeSoFar); //condition 3: no existing char --> mutal recursion
}
}
//AddRecord() helper function
void Trie::InsertNode(nodeT *w, ParseT &packet, string codeSoFar) // add new char to vector array
{
for (unsigned int i=0; i <=w->alpha.size(); i++) { // loop and insert tokens in sorted vector
if (i==w->alpha.size() || codeSoFar[0] < w->alpha[i].token_char) { //look for end of vector or indexical position
//create new TokenT
tokenT *tempChar=new tokenT;
tempChar->next=NULL;
tempChar->token_char=codeSoFar[0];
//create new nodeT
nodeT *tempLeaf=new nodeT;
tempLeaf->attributes.begin = 0;
tempLeaf->attributes.end = 0;
tempLeaf->attributes.planType = 0;
tempLeaf->attributes.alt_end = 0;
//last node
if (codeSoFar.size() == 1){
tempLeaf->attributes.predecessorTemp = packet.predecessorTemp;
}
//link TokenT with its nodeT
tempChar->next=tempLeaf;
AddRecord(tempLeaf, packet, codeSoFar.substr(1)); //mutual recursion --> add next char in record, if last char AddRecord will terminate
w->alpha.insert(w->alpha.begin()+i, *tempChar);
return;
}
}
}
root is global nodeT *w
struct ParseT {
string ID; //XML key
int begin = 0; //planned or actual start date
int end = 0; //planned or actual end date - if end is empty then assumed started but not compelted and flag with 9999 and
int alt_end = 0; //in case of started without completion 9999 case, then this holds expected end
int planType = 0; //actuals == 1, forecast == 2, planned == 3
map<string, string> aux;
vector<string> resourceTemp;
vector<string> predecessorTemp;
};
In this code
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char || codeSoFar[0] == 'z') {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
you are returning as soon as you call AddRecord, even if it is because of a wildcard. It might be easier to have a separate loop when codeSoFar[0] == 'z' that goes through all the alphas and adds the record. Then have an else clause that does your current code.
Edit: Here is what I meant, in code form:
else { //keep parsing down record path
// Handle wildcards
if (codeSoFar[0] == 'z') {
for (unsigned int i = 0; i < w->alpha.size(); i++) {
AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
else {
// Not a wildcard, look for a match
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char) {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
InsertNode(w, packet, codeSoFar); //condition 3: no existing char --> mutal recursion
}
}