Finding the height of a BST without returning anything from the function - c++

I am trying to learn various data structures, and I am currently learning about trees, namely binary search trees. I have gotten most every function down, with the exception of the get height function. I found quite a lot of pseudo code on how to write this recursively, and returning the recursive path to find the height. This is what I came up with:
int getHeight(struct node* node)
{
if (node == nullptr)
return 0;
else
{
int leftDepth = getHeight(node->left);
int rightDepth = getHeight(node->right);
if (leftDepth > rightDepth)
return(leftDepth+1);
else return(rightDepth+1);
}
}
This is fine, but I wanted to stay consistent with how I wrote out my other functions. The other functions are templates, that each have a public wrapper function that is called in the driver. Then, this wrapper calls the private function that actually preforms the action that is intended. So, what I have for the get height is this:
template <typename T>
int binarySearch<T>::getHeight()
{
int height = 0;
getHeight(rootNode, height, 0);
return height;
}
template <typename T>
void binarySearch<T>::getHeight(Node *node, int &max, int layer)
{
int tempRight = 0;
int tempLeft = 0;
if (node == nullptr)
{
tempRight = -1;
tempLeft = -1;
max--;
}
else
{
if (node->left != nullptr)
{
tempLeft = 1;
getHeight(node->left, max, layer);
}
if (node->right != nullptr)
{
tempRight = 1;
getHeight(node->right, max, layer);
}
}
if (tempLeft > tempRight)
{
max++;
}
else
{
max++;
}
}
I intended to do something similar to a depth first search, in that I would increment a layer counter, to test to see if I am on the same layer, and if I am, to only increment the max counter once. I am a bit confused on the logical flow of the recursive get height, so my implementation makes little to no sense. Can someone point me in the right direction to information regarding the breakdown of the get height recursive function, or assist in correcting my poor attempt at doing what I intended? Thanks!

I'm not really sure, what you want to achieve, but here's a shot:
void getHeight(struct node* node, int &max, int layer) {
if (!node) return;
if (layer>max) {
max = layer;
}
getHeight(node->left, max, layer+1);
getHeight(node->right, max, layer+1);
}
You need to initialize max to 0 before calling getHeight.

Related

Binary Tree numLeaf algorithm not working

I'm writing a program to try and get the number of leaves in a binary tree. What I did was I checked if the current ptr was a leaf, and if not, to keeps going to the next subtree. However, when I run it, it keeps returning 2. What am I doing wrong?
I didn't include the source code because its relatively standard (has a rLink, lLink, etc.). There are no errors when I run this:
template <class elemType>
long int bSearchTreeType<elemType>::getLeaves(nodeType<elemType> * current, long int count) const {
if(current->rLink == NULL && current->lLink == NULL) {
count += 1;
return count;
}
if(current->rLink!=NULL) {
getLeaves(current->rLink);
}
if(current->lLink!=NULL) {
getLeaves(current->lLink);
}
}
template <class elemType>
long int bSearchTreeType<elemType>::leaves() const {
if(this->root!=NULL) {
return this->getLeaves(this->root);
}
}
Edit: I declared the function with count = 1 in the parameter list. Thats why I'm able to to that.
I find a few issues.
1) You are calling return this->getLeaves(this->root); but there is no method with that method signature in the code sample you have here.
bSearchTreeType<elemType>::getLeaves(nodeType<elemType> * current, long int count)
2) Your code doesn't handle cases where the current can be null i.e) for tree's like
2
/
1
3) You are not returning anything after traversing the left and the right sub tree.
You could write something like this
template <class elemType>
long int bSearchTreeType<elemType>::getLeaves(nodeType<elemType> * current)
{
if(current == NULL)
return 0;
if(current->rLink == NULL && current->lLink == NULL)
return 1;
int leftSubTree = getLeaves(current->lLink);
int rightSubTree = getLeaves(current->rLink);
return leftSubTree + rightSubTree;
}

search function causes program to crash

I have been going through the debugger but can't seem to pinpoint exactly what is going wrong. I have come to my own conclusion i must be missing a nullptr check somewhere or something. If anyone can provide some help it would be greatly appreciated.
error message from debugger
error msg
which looks like makes the program crash on this line:
if (node->children_[index] == nullptr) {
search function
Node* search(const string& word, Node* node, int index) const {
Node* temp;
//same as recurssive lookup just difference is returns node weather terminal or not
if (index < word.length()) {
index = node->getIndex(word[index]);
if (node->children_[index] == nullptr) {
return nullptr;
}
else {
temp = search(word, node->children_[index], index++);
}
}
return temp; // this would give you ending node of partialWord
}
Node struct for reference
struct Node {
bool isTerminal_;
char ch_;
Node* children_[26];
Node(char c = '\0') {
isTerminal_ = false;
ch_ = c;
for (int i = 0; i < 26; i++) {
children_[i] = nullptr;
}
}
//given lower case alphabetic charachters ch, returns
//the associated index 'a' --> 0, 'b' --> 1...'z' --> 25
int getIndex(char ch) {
return ch - 'a';
}
};
Node* root_;
int suggest(const string& partialWord, string suggestions[]) const {
Node* temp;
temp = search(partialWord, root_, 0);
int count = 0;
suggest(partialWord, temp, suggestions, count);
return count;
}
Might be a very simple thing. Without digging I am not sure about the rank of the -> operator versus the == operator. I would take a second and try putting parenthesis around the "node->children_[index] == nullptr" part like this:
(node->children_[index]) == nullptr
just to make sure that the logic runs like you seem to intend.
Dr t
I believe the root cause is that you're using index for two distinct purposes: as an index into the word you're looking for, and as an index into the node's children.
When you get to the recursion, index has changed meaning, and it's all downhill from there.
You're also passing index++ to the recursion, but the value of index++ is the value it had before the increment.
You should pass index + 1.
[An issue in a different program would be that the order of evaluation of function parameters is unspecified, and you should never both modify a variable and use it in the same parameter list. (I would go so far as to say that you should never modify anything in a parameter list, but many disagree.)
But you shouldn't use the same variable here at all, so...]
I would personally restructure the code a little, something like this:
Node* search(const string& word, Node* node, int index) const {
// Return immediately on failure.
if (index >= word.length())
{
return nullptr;
}
int child_index = node->getIndex(word[index]);
// The two interesting cases: we either have this child or we don't.
if (node->children_[child_index] == nullptr) {
return nullptr;
}
else {
return search(word, node->children_[child_index], index + 1);
}
}
(Side note: returning a pointer to a non-const internal Node from a const function is questionable.)

A* Performance at large maps

i would like some help for my AStar algorithm search, which takes from my point of view far to long. Even though my map is with 500 * 400 coordinates(objectively is my tile graph a bit smaller since I don't took the walls into the TileGraph.) large, I would like to expect the result after a few seconds. The world looks like this, despite the task not being mine
I want to search from marked coordinates "Start"(120|180) to "Ziel"(320|220), which currently takes 48 minutes. And sorry for all, who don't speak german, but the text at the picture isn't important.
At first I want to show you, what I've programmed for A*. In General adapted myself to the pseudocode at https://en.wikipedia.org/wiki/A*_search_algorithm .
bool AStarPath::Processing(Node* Start, Node* End)
m_Start = Start;
m_End = End;
for (Node* n : m_SearchRoom->GetAllNodes())
{
DistanceToStart[n] = std::numeric_limits<float>::infinity();
CameFrom[n] = nullptr;
}
DistanceToStart[m_Start] = 0;
NotEvaluatedNodes.AddElement(0, m_Start);
while (NotEvaluatedNodes.IsEmpty() == false)
{
Node* currentNode = NotEvaluatedNodes.GetElement();
NotEvaluatedNodes.DeleteElement();
if (currentNode == m_End)
{
ReconstructPath();
return true;
}
EvaluatedNodes.insert(currentNode);
ExamineNeighbours(currentNode);
}
return false;
//End Processing
void AStarPath::ExamineNeighbours(Node* current)
for (Node* neighbour : m_SearchRoom->GetNeighbours(current))
{
if (std::find(EvaluatedNodes.begin(), EvaluatedNodes.end(), neighbour) != EvaluatedNodes.end())
{
continue;
}
bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);
float tentative_g_score = DistanceToStart[current] + DistanceBetween(current, neighbour);
if (InOpenSet == true && tentative_g_score >= DistanceToStart[neighbour])
{
continue;
}
CameFrom[neighbour] = current;
DistanceToStart[neighbour] = tentative_g_score;
float Valuation = tentative_g_score + DistanceBetween(neighbour, m_End);
if (InOpenSet == false)
{
NotEvaluatedNodes.AddElement(Valuation, neighbour);
}
else
{
NotEvaluatedNodes.UpdatePriority(neighbour, Valuation);
}
}
//END ExamineNeighbours
double AStarPath::DistanceBetween(Node* a, Node* b)
return sqrt(pow(m_SearchRoom->GetNodeX(a) - m_SearchRoom->GetNodeX(b), 2)
+ pow(m_SearchRoom->GetNodeY(a) - m_SearchRoom->GetNodeY(b), 2));
//END DistanceBetween
I'm sorry for the bad formatting, but I don't really know how to work with the code blocks here.
class AStarPath
private:
std::unordered_set<Node*> EvaluatedNodes;
Binary_Heap NotEvaluatedNodes;
std::unordered_map<Node*, float> DistanceToStart;
std::unordered_map<Node*, Node*> CameFrom;
std::vector<Node*> m_path;
TileGraph* m_SearchRoom;
//END Class AStarPath
Anyway, i have thought myself over my problem already and changed some things.
Firstly, I implemented a binary heap instead of the std::priority_queue. I used a page at policyalmanac for it, but I'm not permitted to add another link, so I can't really give you the address. It improved the performance, but it still takes quite long as I told at the beginning.
Secondly, I used unordered containers (if there are two options), so that the containers don't have to be sorted after the changes. For my EvaluatedNodes I took the std::unordered_set, since from my knowledge it's fastest for std::find, which I use for containment checks.
The usage of std::unordered_map is caused by the need of having seperate keys and values.
Thirdly, I thought about splitting my map into nodes, which represent multiple coordinates(instead of now where one node represents one coordinate) , but I'm not really sure how to choose them. I thought about setting points at position, that the algorithm decises based on the length and width of the map and add neighbouring coordinates, if there aren't a specific distance or more away from the base node/coordinate and I can reach them only from previous added coordinates. To Check whether there is a ability to walk, I would have used the regular A*, with only the coordinates(converted to A* nodes), which are in these big nodes. Despite this I'm unsure which coordinates I should take for the start and end of this pathfinding. This would probably reduce the number of nodes/coordinates, which are checked, if I only use the coordinates/nodes, which were part of the big nodes.(So that only nodes are used, which where part of the bigger nodes at an upper level)
I'm sorry for my english, but hope that all will be understandable. I'm looking forward to your answers and learning new techniques and ways to handle problems and as well learn about all the hundreds of stupids mistakes I produced.
If any important aspect is unclear or if I should add more code/information, feel free to ask.
EDIT: Binary_Heap
class Binary_Heap
private:
std::vector<int> Index;
std::vector<int> m_Valuation;
std::vector<Node*> elements;
int NodesChecked;
int m_NumberOfHeapItems;
void TryToMoveElementUp(int i_pos);
void TryToMoveElementDown(int i_pos);
public:
Binary_Heap(int i_numberOfElements);
void AddElement(int Valuation, Node* element);
void DeleteElement();
Node* GetElement();
bool IsEmpty();
bool ContainsElement(Node* i_node);
void UpdatePriority(Node* i_node, float newValuation);
Binary_Heap::Binary_Heap(int i_numberOfElements)
Index.resize(i_numberOfElements);
elements.resize(i_numberOfElements);
m_Valuation.resize(i_numberOfElements);
NodesChecked = 0;
m_NumberOfHeapItems = 0;
void Binary_Heap::AddElement(int valuation, Node* element)
++NodesChecked;
++m_NumberOfHeapItems;
Index[m_NumberOfHeapItems] = NodesChecked;
m_Valuation[NodesChecked] = valuation;
elements[NodesChecked] = element;
TryToMoveElementUp(m_NumberOfHeapItems);
void Binary_Heap::DeleteElement()
elements[Index[1]] = nullptr;
m_Valuation[Index[1]] = 0;
Index[1] = Index[m_NumberOfHeapItems];
--m_NumberOfHeapItems;
TryToMoveElementDown(1);
bool Binary_Heap::IsEmpty()
return m_NumberOfHeapItems == 0;
Node* Binary_Heap::GetElement()
return elements[Index[1]];
bool Binary_Heap::ContainsElement(Node* i_element)
return std::find(elements.begin(), elements.end(), i_element) != elements.end();
void Binary_Heap::UpdatePriority(Node* i_node, float newValuation)
if (ContainsElement(i_node) == false)
{
AddElement(newValuation, i_node);
}
else
{
int treePosition;
for (int i = 1; i < Index.size(); i++)
{
if (elements[Index[i]] == i_node)
{
treePosition = i;
break;
}
}
//Won't influence each other, since only one of them will change the position
TryToMoveElementUp(treePosition);
TryToMoveElementDown(treePosition);
}
void Binary_Heap::TryToMoveElementDown(int i_pos)
int nextPosition = i_pos;
while (true)
{
int currentPosition = nextPosition;
if (2 * currentPosition + 1 <= m_NumberOfHeapItems)
{
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
{
nextPosition = 2 * currentPosition;
}
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition + 1]])
{
nextPosition = 2 * currentPosition + 1;
}
}
else
{
if (2 * currentPosition <= m_NumberOfHeapItems)
{
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
{
nextPosition = 2 * currentPosition;
}
}
}
if (currentPosition != nextPosition)
{
int tmp = Index[currentPosition];
Index[currentPosition] = Index[nextPosition];
Index[nextPosition] = tmp;
}
else
{
break;
}
}
void Binary_Heap::TryToMoveElementUp(int i_pos)
int treePosition = i_pos;
while (treePosition != 1)
{
if (m_Valuation[Index[treePosition]] <= m_Valuation[Index[treePosition / 2]])
{
int tmp = Index[treePosition / 2];
Index[treePosition / 2] = Index[treePosition];
Index[treePosition] = tmp;
treePosition = treePosition / 2;
}
else
{
break;
}
}
This line introduces major inefficiency, as it needs to iterate over all the nodes in the queue, in each iteration.
bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);
Try using a more efficient data structure, e.g. the unordered_set you use for EvaluatedNodes. Whenever you push or pop a node from the heap, modify the set accordingly to always contain only the nodes in the heap.

Implementing min function

Good day, I found this priority queue implementation and I am trying to get a min version of it (instead of max). I have no idea where to start. I tried mixing the signs of the functions (naive attempt) but it didn't get me far. Any help of how to implement it and a few words explaining it are very wellcome. The source is below:
Note I have left it's comments
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;
class PriorityQueue
{
vector<int> pq_keys;
void shiftRight(int low, int high);
void shiftLeft(int low, int high);
void buildHeap();
public:
PriorityQueue(){}
PriorityQueue(vector<int>& items)
{
pq_keys = items;
buildHeap();
}
/*Insert a new item into the priority queue*/
void enqueue(int item);
/*Get the maximum element from the priority queue*/
int dequeue();
/*Just for testing*/
void print();
};
void PriorityQueue::enqueue(int item)
{
pq_keys.push_back(item);
shiftLeft(0, pq_keys.size() - 1);
return;
}
int PriorityQueue::dequeue()
{
assert(pq_keys.size() != 0);
int last = pq_keys.size() - 1;
int tmp = pq_keys[0];
pq_keys[0] = pq_keys[last];
pq_keys[last] = tmp;
pq_keys.pop_back();
shiftRight(0, last-1);
return tmp;
}
void PriorityQueue::print()
{
int size = pq_keys.size();
for (int i = 0; i < size; ++i)
cout << pq_keys[i] << " ";
cout << endl;
}
void PriorityQueue::shiftLeft(int low, int high)
{
int childIdx = high;
while (childIdx > low)
{
int parentIdx = (childIdx-1)/2;
/*if child is bigger than parent we need to swap*/
if (pq_keys[childIdx] > pq_keys[parentIdx])
{
int tmp = pq_keys[childIdx];
pq_keys[childIdx] = pq_keys[parentIdx];
pq_keys[parentIdx] = tmp;
/*Make parent index the child and shift towards left*/
childIdx = parentIdx;
}
else
{
break;
}
}
return;
}
void PriorityQueue::shiftRight(int low, int high)
{
int root = low;
while ((root*2)+1 <= high)
{
int leftChild = (root * 2) + 1;
int rightChild = leftChild + 1;
int swapIdx = root;
/*Check if root is less than left child*/
if (pq_keys[swapIdx] < pq_keys[leftChild])
{
swapIdx = leftChild;
}
/*If right child exists check if it is less than current root*/
if ((rightChild <= high) && (pq_keys[swapIdx] < pq_keys[rightChild]))
{
swapIdx = rightChild;
}
/*Make the biggest element of root, left and right child the root*/
if (swapIdx != root)
{
int tmp = pq_keys[root];
pq_keys[root] = pq_keys[swapIdx];
pq_keys[swapIdx] = tmp;
/*Keep shifting right and ensure that swapIdx satisfies
heap property aka left and right child of it is smaller than
itself*/
root = swapIdx;
}
else
{
break;
}
}
return;
}
void PriorityQueue::buildHeap()
{
/*Start with middle element. Middle element is chosen in
such a way that the last element of array is either its
left child or right child*/
int size = pq_keys.size();
int midIdx = (size -2)/2;
while (midIdx >= 0)
{
shiftRight(midIdx, size-1);
--midIdx;
}
return;
}
int main()
{
//example usage
PriorityQueue asd;
asd.enqueue(2);
asd.enqueue(3);
asd.enqueue(4);
asd.enqueue(7);
asd.enqueue(5);
asd.print();
cout<< asd.dequeue() << endl;
asd.print();
return 0;
}
Well generally in such problems, i.e. algorithms based on comparison of elements, you can redefine what does (a < b) mean. (That is how things in standard library work by the way. You can define your own comparator.)
So if you change it's meaning to the opposite. You will reverse the ordering.
You need to identify every comparison of elements, and switch it. So for every piece of code like this
/*if child is bigger than parent we need to swap*/
if (pq_keys[childIdx] > pq_keys[parentIdx])
invert it's meaning/logic.
Simple negation should do the trick:
/*if child is NOT bigger than parent we need to swap*/
if !(pq_keys[childIdx] > pq_keys[parentIdx])
You do not even need to understand algorithm. Just inverse meaning of what lesser element is.
Edit:
Additional note. You could actually refactor it into some kind of bool compare(T a, T b). And use this function where comparison is used. So whenever you want to change the behaviour you just need to change one place and it will be consistent. But that is mostly to avoid work to look for every such occurrence, and stupid bugs and when you miss one.
Easier:
std::prioroty_queue<int, std::vector<int>, std::greater<int>> my_queue;
If this is part of an exercise, then I suggest following the standard library's design principles: split the problem up:
data storage (e.g. std::vector)
sorting or "heapifying" algorithm (c.f. std::make_heap etc.)
ordering criteria (to be used by 2. above)
Your class should give you some leeway to change any of these independently. With that in place, you can trivially change the "less-than" ordering for a "greater than" one.

Exponential tree implementation

I was trying to implement exponential tree from documentation, but here is one place in the code which is not clear for me how to implement it:
#include<iostream>
using namespace std;
struct node
{
int level;
int count;
node **child;
int data[];
};
int binary_search(node *ptr,int element)
{
if(element>ptr->data[ptr->count-1]) return ptr->count;
int start=0;
int end=ptr->count-1;
int mid=start+(end-start)/2;
while(start<end)
{
if(element>ptr->data[mid]) { start=mid+1;}
else
{
end=mid;
}
mid=start+(end-start)/2;
}
return mid;
}
void insert(node *root,int element)
{
node *ptr=root,*parent=NULL;
int i=0;
while(ptr!=NULL)
{
int level=ptr->level,count=ptr->count;
i=binary_search(ptr,element);
if(count<level){
for(int j=count;j<=i-1;j--)
ptr->data[j]=ptr->data[j-1];
}
ptr->data[i]=element;
ptr->count=count+1;
return ;
}
parent=ptr,ptr=ptr->child[i];
//Create a new Exponential Node at ith child of parent and
//insert element in that
return ;
}
int main()
{
return 0;
}
Here is a link for the paper I'm referring to:
http://www.ijcaonline.org/volume24/number3/pxc3873876.pdf
This place is in comment, how can I create a new exponential node at level i? Like this?
parent->child[i]=new node;
insert(parent,element);
The presence of the empty array at the end of the structure indicates this is C style code rather than C++ (it's a C Hack for flexible arrays). I'll continue with C style code as idiomatic C++ code would prefer use of standard containers for the child and data members.
Some notes and comments on the following code:
There were a number of issues with the pseudo-code in the linked paper to a point where it is better to ignore it and develop the code from scratch. The indentation levels are unclear where loops end, all the loop indexes are not correct, the check for finding an insertion point is incorrect, etc....
I didn't include any code for deleting the allocated memory so the code will leak as is.
Zero-sized arrays may not be supported by all compilers (I believe it is a C99 feature). For example VS2010 gives me warning C4200 saying it will not generate the default copy/assignment methods.
I added the createNode() function which gives the answer to your original question of how to allocate a node at a given level.
A very basic test was added and appears to work but more thorough tests are needed before I would be comfortable with the code.
Besides the incorrect pseudo-code the paper has a number of other errors or at least questionable content. For example, concerning Figure 2 it says "which clearly depicts that the slope of graph is linear" where as the graph is clearly not linear. Even if the author meant "approaching linear" it is at least stretching the truth. I would also be interested in the set of integers they used for testing which doesn't appear to be mentioned at all. I assumed they used a random set but I would like to see at least several sets of random numbers used as well as several predefined sets such as an already sorted or inversely sorted set.
.
int binary_search(node *ptr, int element)
{
if (ptr->count == 0) return 0;
if (element > ptr->data[ptr->count-1]) return ptr->count;
int start = 0;
int end = ptr->count - 1;
int mid = start + (end - start)/2;
while (start < end)
{
if (element > ptr->data[mid])
start = mid + 1;
else
end = mid;
mid = start + (end - start)/2;
}
return mid;
}
node* createNode (const int level)
{
if (level <= 0) return NULL;
/* Allocate node with 2**(level-1) integers */
node* pNewNode = (node *) malloc(sizeof(node) + sizeof(int)*(1 << (level - 1)));
memset(pNewNode->data, 0, sizeof(int) * (1 << (level - 1 )));
/* Allocate 2**level child node pointers */
pNewNode->child = (node **) malloc(sizeof(node *)* (1 << level));
memset(pNewNode->child, 0, sizeof(int) * (1 << level));
pNewNode->count = 0;
pNewNode->level = level;
return pNewNode;
}
void insert(node *root, int element)
{
node *ptr = root;
node *parent = NULL;
int i = 0;
while (ptr != NULL)
{
int level = ptr->level;
int count = ptr->count;
i = binary_search(ptr, element);
if (count < (1 << (level-1)))
{
for(int j = count; j >= i+1; --j)
ptr->data[j] = ptr->data[j-1];
ptr->data[i] = element;
++ptr->count;
return;
}
parent = ptr;
ptr = ptr->child[i];
}
parent->child[i] = createNode(parent->level + 1);
insert(parent->child[i], element);
}
void InOrderTrace(node *root)
{
if (root == NULL) return;
for (int i = 0; i < root->count; ++i)
{
if (root->child[i]) InOrderTrace(root->child[i]);
printf ("%d\n", root->data[i]);
}
if (root->child[root->count]) InOrderTrace(root->child[root->count]);
}
void testdata (void)
{
node* pRoot = createNode(1);
for (int i = 0; i < 10000; ++i)
{
insert(pRoot, rand());
}
InOrderTrace(pRoot);
}