I have an AVL Tree implementation that can successfully build a tree by insertions. Rotations etc. also work fine. However my delete function has problems. It poorly balances and sometimes crashes. To me, the rotations make sense (I've commented appropriate places) but somehow it doesn't work. How can I fix this?
void delete_elem(node** t, int val, uint delete_flag) {
if(!(*t))
return;
if((*t)->data < val)
delete_elem(&(*t)->right, val, delete_flag);
else if((*t)->data > val)
delete_elem(&(*t)->left, val, delete_flag);
else { // found elem
// if there's a duplicate, remove it
if(delete_flag == DELETE_NO_FORCE && (*t)->dup_count) {
--(*t)->dup_count;
printf("Duplicate element removed. %d left.\n", (*t)->dup_count);
}
else { // there were no duplicates
node* del;
if(!(*t)->left && (*t)->right) { // no left but right
del = (*t);
(*t) = (*t)->right;
free(del);
}
else if(!(*t)->right && (*t)->left) { // no right but left
del = (*t);
(*t) = (*t)->left;
free(del);
}
else if(!(*t)->left && !(*t)->right) { // additionally, you can use the is_leaf function we wrote before, in here
free(*t);
*t = NULL;
}
else { // has both children
del = get_successor(*t, *t); // get the in-order successor in tree *t of *t
printf("succ of %d is %d.\n", (*t)->data, del->data);
(*t)->data = del->data; // swap the successor's data with t's data
(*t)->dup_count = del->dup_count; // IMPORTANT! We support duplicates! Duplicate count must also be swapped!
delete_elem(&(*t)->right, del->data, DELETE_FORCE); // We must now delete this guy forcefully!
}
if(!(*t)) // if t was deleted and not needed anymore, don't bother. Update height if it exists
return;
(*t)->height = Max(get_height((*t)->left), get_height((*t)->right)) + 1;
int bfactor = get_balance(*t);
if(bfactor > 1 && get_balance((*t)->left) > 0)
rotate_once_right(t);
else if(bfactor > 1 && get_balance((*t)->left) < 0)
rotate_twice_left(t);
else if(bfactor < -1 && get_balance((*t)->right) > 0)
rotate_once_left(t);
else if(bfactor < -1 && get_balance((*t)->right) < 0)
rotate_twice_right(t);
}
}
}
The above is my delete function.
The following are some screenshots of the crash occuring:
These are my rotation functions. They seem to be working right, as everytime I create a tree, it's always reported to be an AVL Tree. However, just in case:
void rotate_once_left(node** k1) {
node* temp = (*k1)->left;
(*k1)->left = temp->right;
temp->right = *k1;
(*k1)->height = Max(get_height((*k1)->left), get_height((*k1)->right)) + 1;
temp->height = Max(get_height(temp->left), (*k1)->height) + 1;
*k1 = temp;
}
void rotate_once_right(node** k1) {
node* temp = (*k1)->right;
(*k1)->right = temp->left;
temp->left = *k1;
(*k1)->height = Max(get_height((*k1)->left), get_height((*k1)->right)) + 1;
temp->height = Max(get_height(temp->right), (*k1)->height) + 1;
*k1 = temp;
}
void rotate_twice_left(node** k1) {
rotate_once_right(&(*k1)->left);
rotate_once_left(k1);
}
void rotate_twice_right(node** k1) {
rotate_once_left(&(*k1)->right);
rotate_once_right(k1);
}
Update: No more crash, but some unbalance is left after deletion. Sample test case:
Related
I'm currently working on an assignment where the N most frequent words in a book (.txt) must be printed. The issue that I'm currently facing is that when I add a node to one of my trees, it simply replaces the root node and thus, the tree remains as a single node.
Code snippet which adds words from the file "stopwords.txt" to a tree named stopwords:
Dict stopwords = newDict();
if (!readFile("stopwords.txt"))
{
fprintf(stderr, "Can't open stopwords\n");
exit(EXIT_FAILURE);
}
FILE *fp = fopen("stopwords.txt", "r");
while (fgets(buf, MAXLINE, fp) != NULL)
{
token = strtok(buf, "\n");
DictInsert(stopwords, buf); //the root is replaced here
}
fclose(fp);
The data structures are defined as follows:
typedef struct _DictNode *Link;
typedef struct _DictNode
{
WFreq data;
Link left;
Link right;
int height;
} DictNode;
typedef struct _DictRep *Dict;
struct _DictRep
{
Link root;
};
typedef struct _WFreq {
char *word; // word buffer (dynamically allocated)
int freq; // count of number of occurences
} WFreq;
Code to insert and rebalance tree:
// create new empty Dictionary
Dict newDict(void)
{
Dict d = malloc(sizeof(*d));
if (d == NULL)
{
fprintf(stderr, "Insufficient memory!\n");
exit(EXIT_FAILURE);
}
d->root = NULL;
return d;
}
// insert new word into Dictionary
// return pointer to the (word,freq) pair for that word
WFreq *DictInsert(Dict d, char *w)
{
d->root = doInsert(d->root, w); //the root is replaced here before doInsert runs
return DictFind(d, w);
}
static int depth(Link n)
{
if (n == NULL)
return 0;
int ldepth = depth(n->left);
int rdepth = depth(n->right);
return 1 + ((ldepth > rdepth) ? ldepth : rdepth);
}
static Link doInsert(Link n, char *w)
{
if (n == NULL)
{
return newNode(w);
}
// insert recursively
int cmp = strcmp(w, n->data.word);
if (cmp < 0)
{
n->left = doInsert(n->left, w);
}
else if (cmp > 0)
{
n->right = doInsert(n->right, w);
}
else
{ // (cmp == 0)
// if time is already in the tree,
// we can return straight away
return n;
}
// insertion done
// correct the height of the current subtree
n->height = 1 + max(height(n->left), height(n->right));
// rebalance the tree
int dL = depth(n->left);
int dR = depth(n->right);
if ((dL - dR) > 1)
{
dL = depth(n->left->left);
dR = depth(n->left->right);
if ((dL - dR) > 0)
{
n = rotateRight(n);
}
else
{
n->left = rotateLeft(n->left);
n = rotateRight(n);
}
}
else if ((dR - dL) > 1)
{
dL = depth(n->right->left);
dR = depth(n->right->right);
if ((dR - dL) > 0)
{
n = rotateLeft(n);
}
else
{
n->right = rotateRight(n->right);
n = rotateLeft(n);
}
}
return n;
}
static Link newNode(char *w)
{
Link n = malloc(sizeof(*n));
if (n == NULL)
{
fprintf(stderr, "Insufficient memory!\n");
exit(EXIT_FAILURE);
}
n->data.word = w;
n->data.freq = 1;
n->height = 1;
n->left = NULL;
n->right = NULL;
return n;
}
// Rotates the given subtree left and returns the root of the updated
// subtree.
static Link rotateLeft(Link n)
{
if (n == NULL)
return n;
if (n->right == NULL)
return n;
Link rightNode = n->right;
n->right = rightNode->left;
rightNode->left = n;
n->height = max(height(n->left), height(n->right)) + 1;
rightNode->height = max(height(rightNode->right), n->height) + 1;
return rightNode;
}
// Rotates the given subtree right and returns the root of the updated
// subtree.
static Link rotateRight(Link n)
{
if (n == NULL)
return n;
if (n->left == NULL)
return n;
Link leftNode = n->left;
n->left = leftNode->right;
leftNode->right = n;
n->height = max(height(n->left), height(n->right)) + 1;
leftNode->height = max(height(leftNode->right), n->height) + 1;
return leftNode;
}
I believe that most of the code is functional and it is simply the insertion which fails. When I attempted to debug this with gdb, I had discovered that the root node (d->root) was replaced before the recursive insert function (doInsert) was run, causing the program to always return the node n which, as a result, already exists in the tree. For example, if the text file contained the following:
a
b
c
then the program would first insert "a" as stopwords->root, then "b" would replace "a" and become the new stopwords->root, finally "c" would replace "b" as the stopwords->root, resulting in a tree with one node, "c".
There are many inconsistencies in your code.
One mistake is here:
d->root = doInsert(d->root, w);
You reassign unconditionally the root each time when you insert a new node.
You are supposed to return the new node from the function doInsert and to reassign the root only if the new node had become a new root.
But other mistake that you make is that you return from doInsert a local variable n that was not newly allocated but that was initialized to point to the previous root.
Inside doInsert you need to allocate a new node NEW and use a variable x to walk down from the root until you find a place to insert a new allocated node NEW. If x stops at root then you reinitialize the d->root = NEW.
Your function newNode just stores the passed string pointer, so what is pointed at will change when you modify the original string.
To prevent that, you should copy the input string on node insertions.
To archive that,
n->data.word = w;
should be
n->data.word = malloc(strlen(w) + 1);
if (n->data.word == NULL)
{
fprintf(stderr, "Insufficient memory!\n");
exit(EXIT_FAILURE);
}
strcpy(n->data.word, w);
Add #include <string.h> to use strlen() and strcpy() if it isn't.
I made avl tree and it successfully compiles. however, while it is inserting, it inserts only 1 numbers and makes a segmentation fault. Where do I need to change to make the code successful?
I got help from this file
https://www.geeksforgeeks.org/avl-tree-set-1-insertion/
and made a static internal function _insert to make the code look simpler.
/* internal function
This function uses recursion to insert the new data into a leaf node
return pointer to new root
*/
static NODE *_insert(NODE *root, NODE *newPtr)
{
if (root) return newPtr;
if (newPtr->data < root->data)
root->left = _insert(root->left, newPtr);
else if (newPtr->data >= root->data)
root->right = _insert(root->right, newPtr);
root->height = 1 + max(root->left->height, root->right->height);
int bal = getHeight(root);
if (bal > 1 && newPtr->data < root->left->data)
return rotateRight(root);
if (bal<-1 && newPtr->data > root->right->data)
return rotateLeft(root);
if (bal > 1 && newPtr->data > root->left->data)
{
root->left = rotateLeft(root->left);
return rotateRight(root);
}
if (bal < -1 && newPtr->data < root->right->data)
{
root->right = rotateRight(root->right);
return rotateLeft(root);
}
return root;
}
int AVL_Insert(AVL_TREE *pTree, int data)
{
NODE *pNode = _makeNode(data);
if (!pNode) return 0;
pTree->root = _insert(pTree->root, pNode);
if (!pTree->root) return 0;
else return 1;
}
``````
````````````
/* internal function
Exchanges pointers to rotate the tree to the right
updates heights of the nodes
return new root
*/
static NODE *rotateRight(NODE *root)
{
NODE *t1 = root->left;
NODE *t2 = t1->right;
t1->right = root;
root->left = t2;
root->height = max(root->left->height, root->right->height) + 1;
t1->height = max(t1->left->height, t1->right->height) + 1;
return t1;
}
/* internal function
Exchanges pointers to rotate the tree to the left
updates heights of the nodes
return new root
*/
static NODE *rotateLeft(NODE *root)
{
NODE *t1 = root->right;
NODE *t2 = t1->left;
t1->left = root;
root->right = t2;
root->height = max(root->left->height, root->right->height) + 1;
t1->height = max(t1->left->height, t1->right->height) + 1;
}
//main code
srand(time(NULL));
for (int i = 0; i < MAX_ELEM; i++)
{
data = rand() % (MAX_ELEM * 3) + 1; // random number
// data = i+1; // sequential number
fprintf(stdout, "%d ", data);
// insert function call
AVL_Insert(tree, data);
}
fprintf(stdout, "\n");
When I run the visual studio, I get only 1 number inserted
And when I run this in linux, I get segmentation fault(core dumped)
How can I solve this? thank you
You made a mistake here:
static NODE *_insert(NODE *root, NODE *newPtr)
{
if (root) return newPtr;
When there is no root (i.e. the empty tree), then the new tree consists solely of the new node. Hence, this should be:
if (!root) return newPtr;
This means, your segmentation fault happens in the next line:
if (newPtr->data < root->data)
because you dereference the null pointer root here.
I'm working on a project about the N-queens game 8-puzzle for school and I'm encountering a problem for a week now. I have to implemente Uniform Cost Search function to solve the game. I implemented BFS and DFS, it worked. But with UCS I have a Segmentation fault which is probably related to my popBest function because BFS and UCS are related. Besides, the segmentation error is also related to temp in my code because there no error when I remove it. Thanks for helping me. Here's my code:
Item* popBest(list_t* list)
{
assert(list);
assert(list->numElements);
assert(list->first);
// the first element is best.
Item* best = list->first;
int minf = best->f;
// Search from the second element (if it exists).
Item* item = best->next;
while (item) {
if (item->f < minf) {
minf = item->f;
best = item;
}
item = item->next;
}
delList(list, best);
return best;
}
void ucs(void)
{
Item *cur_node, *child_p, *temp;
int i;
cur_node->f = 0;
while (listCount(&openList_p)) {
cur_node = popBest(&openList_p);
addFirst(&closedList_p, cur_node);
if (evaluateBoard(cur_node) == 0.0) {
showSolution(cur_node);
printf("\nParcours en largeur (bfs)\n");
return;
}
else {
/* Enumerate adjacent states */
for (i = 0; i < MAX_BOARD; i++) {
child_p = getChildBoard(cur_node, i);
child_p->f = cur_node->f + 1;
if (child_p != NULL) { // it's a valid child!
temp = onList(&openList_p, child_p->board);
/* Ignore this child if already visited */
if (temp == NULL) {
/* Add child node to openList*/
addLast(&openList_p, child_p);
}
else if (temp != NULL && child_p->f < temp->f) {
delList(&openList_p, temp);
addLast(&openList_p, child_p);
}
}
}
}
}
return;
}
I have a problem with a binary tree. In my code i'm planing to implement functions like adding, deleting and printing. While Adding and printing works fine, im having troubles with deleting. My thoughts - problem is in pointers. I'm adding through 2 pointers and deleting with 1, but i cant figure out how to get it to work. Please help.
#include <stdio.h>
#include <stdlib.h>
#include "bintree.h"
void paste_node(Tree ** tr, int x)
{
Tree *tree_bin;
if ((*tr) == NULL) {
tree_bin = (Tree *) malloc(sizeof(Tree));
tree_bin->item = x;
tree_bin->lchild = tree_bin->rchild = NULL;
*tr = tree_bin;
return;
}
if (x < (*tr)->item) {
paste_node(&((*tr)->lchild), x);
} else {
paste_node(&((*tr)->rchild), x);
}
}
Tree * minimum(Tree *tr)
{
if (tr->lchild == NULL) return tr;
return minimum(tr->lchild);
}
void delete_node(Tree ** tr, int num)
{
if (tr == NULL) return;
if (num < tr->item)
tr->lchild = delete_node(tr->lchild, num);
else if (num > tr->item)
tr->rchild = delete_node(tr->rchild, num);
else {
if (tr->lchild == NULL) {
Tree *tree_bin = tr->rchild;
free(tr);
return;
}
else if (tr->rchild == NULL) {
Tree *tree_bin = tr->lchild;
free(tr);
return;
}
Tree *tree_bin = minimum(tr->rchild);
tr->item = tree_bin->item;
tr->rchild = delete_node(tr->rchild, tree_bin->item);
}
return;
}
void print_tree(Tree *tr, int depth)
{
if (tr != NULL) {
print_tree(tr->lchild, depth + 1);
for(int i = 0; i < depth; ++i) printf(" ");
printf("%d<\n", tr->item);
print_tree(tr->rchild, depth + 1);
}
}
int check_node(Tree **tr, int x) {
if ((*tr) == NULL) {
return 1;
}
if (x < (*tr)->item) {
check_node(&((*tr)->lchild), x);
} else if (x == (*tr)->item) {
return -1;
} else {
check_node(&((*tr)->rchild), x);
}
}
You can't simply delete a node of a binary tree. You can delete a leaf easily enough, just set the parent's lchild or rchild partner to null. But you can't delete an internal node without also deleting all its children. What you can do is rotate the node so that the left child (or right child) becomes the root of the sub-tree, and the root becomes a child. The reattach the new sub-tree root to the tree. Repeat until finally the node you wish to delete becomes a leaf, and remove it.
I am having trouble getting the element from the binary tree at a specific index. The function that i am having trouble with is generic tree_get_at_index(tree_node* t, int index) {
The assignment asks me to find the element at a particular index in a binary tree. For example the 0 index should return the lowest element in the binary tree and the index = treesize should return the largest element in the tree. i have a size function in my tree which works correctly but i cannot get the indexing to work for some reason. any help would be appreciated. thank you
Right now i am getting seg fault after the tree runs once.
#include "tree.h"
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
/* Memory Management */
/* This constructor returns a pointer to a new tree node that contains the
* given element.*/
tree_node* new_tree_node(generic e) {
/*TODO: Complete this function!*/
tree_node* to_return = malloc(sizeof(tree_node));
to_return->element = e;
to_return->left = NULL;
to_return->right = NULL;
return to_return;
}
/* This function is expected to free the memory associated with a node and all
* of its descendants.*/
void free_tree(tree_node* t) {
/*TODO: Complete this function!*/
if (t != NULL){
free_tree(t->left);
free_tree(t->right);
free(t);
}
}
/* End Memory Management */
/* Tree Storage and Access */
bool tree_contains(tree_node* t, generic e) {
/*TODO: Complete this function!*/
/*
if (t == NULL || t->element != e) {
return false;
}
else if (t->element == e) {
return true;
}
return tree_contains(t,e);
}
*/
if(t == NULL )
return false;
else if(t->element == e)
return true;
else if (e<t->element)
return tree_contains(t->left,e);
else
return tree_contains(t->right,e);
}
tree_node* tree_add(tree_node* t, generic e) {
/*TODO: Complete this function!*/
if(t==NULL)
t = new_tree_node(e);
else if(e == t->element)
return t;
else if(e > (t->element))
{
t->right = tree_add(t->right,e);
}
else if(e < (t->element))
{
t->left = tree_add(t->left,e);
}
return t;
}
tree_node* tree_remove(tree_node* t, generic e) {
/*TODO: Complete this function!*/
if (t == NULL) return t;
else if (e < t->element)
t->left = tree_remove(t->left, e);
else if (e > t->element)
t->right = tree_remove(t->right, e);
else
{
if (t->left == NULL)
{
tree_node *temp = t->right;
free(t);
return temp;
}
else if (t->right == NULL)
{
tree_node *temp = t->left;
free(t);
return temp;
}
else {
tree_node* current = t->right;
tree_node* temp = t->right;
while (current->left != NULL)
current = current->left;
t->element = current->element;
while (temp->left->left != NULL)
temp = temp->left;
temp->left = current->right;
free(current);
}
}
return t;
}
/* End Tree Storage and Access */
/* Size and Index */
/* Return the size of the tree rooted at the given node.
* The size of a tree is the number of nodes it contains.
* This function should work on subtrees, not just the root.
* If t is NULL, it is to be treated as an empty tree and you should
* return 0.
* A single node is a tree of size 1.*/
int tree_size(tree_node* t) {
/*TODO: Complete this function!*/
if (t==NULL)
return 0;
else
return(tree_size(t->left) + 1 + tree_size(t->right));
}
/* Return the element at the given index in the given tree.
* To be clear, imagine the tree is a sorted array, and you are
* to return the element at the given index.
*
* Assume indexing is zero based; if index is zero then the minimum
* element should be returned, for example. If index is one then
* the second smallest element should bereturned, and so on.*/
generic tree_get_at_index(tree_node* t, int index) {
//assert(index >=0 && index < tree_size(t));
/*TODO: Complete this function!*/
//tree_node* new_node = t;
// int min = 0;
// int max = tree_size(t);
// int current = (min+max)/2;
int current = index;
printf("tree size: %d \n", tree_size(t));
//while( new_node != NULL){
if(current == (tree_size(t)-1)){
return t->element;
printf("index = tree size \n");
}
else if(index < (tree_size(t->left))){
//current--;
return tree_get_at_index(t->left, index);
printf("index < tree size \n"); //= new_node->right;
}
else if(index > (tree_size(t->left))){
return tree_get_at_index(t->right, index);
printf("index > tree size \n");
}
return t->element;
//return (generic)0;
}
/* End Size and Index */
We will try filling a virtual array, as you know the size of each subtree you could skip the indexes
generic tree_get_at_index(tree_node* t, int index) {
// sanity check
assert(t);
assert(index > 0);
int leftCount=tree_size(t->left);
if(index < leftCount ) {
// good chance that the node we seek is in the left children
return tree_get_at_index(t->left, index);
}
if(index==leftCount) {
// looking at the "middle" of the sub tree
return t->element;
}
// else look at the right sub tree as it was its own array
return tree_get_at_index(t->right, index - leftCount - 1);
}
generic tree_get_at_index(tree_node* t, int index) {
assert(index >=0 && index <= tree_size(t));//I don't know how you define the tree_size function,but,according to the "if" below,you need to add equal mark
printf("tree size: %d \n", tree_size(t));
//while( new_node != NULL){
if(index == tree_size(t)){
return t->element;
}
else if(index <= tree_size(t->left)){//I think you miss the equal situation here
//current--;
return tree_get_at_index(t->left, index); //= new_node->right;
}
else /*if(index > tree_size(t->left))*/{//do not need any condition here
return tree_get_at_index(t->right, index);
}
// return t->element; //unnecessary
//return (generic)0;
}