I have a homework assignment to implement a binary search tree (create, delete, search). I used the example provided by the teacher but I can't make it work.
Here's my code so far:
void insert_node(int k){
struct node *nodnou,*flow,*parent;
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
if (root==NULL)
{
root = (struct node*)malloc(sizeof(node));
root = nodnou;
}
else
{
flow = (struct node*)malloc(sizeof(node));
parent = (struct node*)malloc(sizeof(node));
flow = root;
parent = root;
while (((flow->st!=NULL) || (flow->dr!=NULL)) && flow!=NULL)
{
if (k<flow->nr)
{
parent = flow;
flow = flow->st;
}
else
{
parent = flow;
flow = flow->dr;
}
}
if (k<flow->nr)
{
parent->st = nodnou;
}
else
{
parent->dr = nodnou;
}
}
}
The way of thinking: This function gets the value of the node we want to insert as the k parameter. The function will only insert the root of the tree (root is global variable).
I think my biggest problem is the while loop that sweeps through the tree to find the spot for the new node.
If I use while (flow!=NULL) it won't work because the flow pointer gets an assignment to something that does not exist. Please help me understand where I am wrong (homework).
Your code has several important flaws, not the least of which is a misunderstanding of how dynamic memory allocation works in C. Never follow a pattern like this:
Type *pointer = malloc(sizeof(Type));
pointer = <<something else>>
It literally leaks memory and gains you nothing in two short lines. This isn't an object-reference based language like Java or C#. Pointers are variables that hold memory addresses. Just like an int can hold an integer, a pointer holds an address. And just like the following example:
int n = 6;
n = 5; //Hmm. Where did the 6 go? Oh yeah, We overwrote it with 5.
You will lose your allocation link doing the same thing with pointers:
struct node *root = malloc(sizeof(*root));
root = nodnou; // memory allocated above is gone. forever leaked.
Pointers are variables. Just like any other variable, they hold values. In the case of a pointer, however, its value is an address. You can have pointers to almost anything in C, including pointers to pointers; variables that hold the address of pointer variables. And I bring them up because they proffer a particularly elegant solution to your insertion requirements.
The following is a general implementation for a binary tree insertion that supports no duplicates in the tree (the code gets even shorter if you allow duplicates). Furthermore, it does this using exactly zero local variables beyond the provided function parameters, and I challenge you to dissect this and determine how it works. It even works on an initially NULL tree root pointer, eliminating the need to special casing if (root) {} else {} logic:
void insert_node(struct node **pp, int k)
{
while (*pp)
{
if (k < (*pp)->nr) // move down left side?
pp = &(*pp)->st;
else if ((*pp)->nr < k) // move down right side?
pp = &(*pp)->dr;
else return; // found the key, no dupes. leave
}
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
If your tree should support duplicates you need to be consistent about which side they are inserted on, but it shortens the above algorithm considerably:
void insert_node(struct node **pp, int k)
{
while (*pp)
pp = (k < (*pp)->nr) ? &(*pp)->st : &(*pp)->dr;
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
In either case, invoked on the caller side like this:
struct node *root = NULL;
insert(&root, 5);
insert(&root, 10);
insert(&root, 7);
...etc...
I think you should use while(flow != NULL) and insert your element as flow after that. The way it is right now it will stop in cases when it shouldn't and do weird things whenever it stops. Try working through some examples with pen and paper.
You almost got it. Keep Up!
First you need to understand a bit better memory allocation. In reality, you only need the very first malloc() call in your function. That is the memory you allocate for the node you are appending to the tree during each insert_node() call. All remainingr mallocs you are performing are unnecesary. It seems that you intuitively feel you need to allocate memory for the other pointers you are using, but all of them are temporary and don't require any allocation, just assignment to a valid node before attempting to de-reference them. In fact, those unnecesary allocations will create what is known as a memory leak (memory you request and fail to release) in code like this:
root = (struct node*)malloc(sizeof(node));
root = nodnou;
The second assignmet (root = nodnou) overwrites the result of the previous malloc() call and since you didn't save the overwritten pointer value in any other place, you will no longer be able to release that memory, it will be marked as used for the lifetime of your application!
Next, you can simplify the code that is walking the tree looking for the insertion point.
You seem to worry that flow becomes NULL, but it doesn't matter. The important node is parent. After the while loop ends, it will be pointing to the actual node where the inserted node needs to be linked. Here is a modified version of your code.
void insert_node(int k) {
struct node *nodnou, *flow, *parent;
// this is the only memory allocation that should be done
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
parent = NULL;
if( root == NULL ) {
root = nodnou;
} else {
flow = root;
// We will walk the tree in order until we reach the bottom of the
// tree (flow becomes null). What we are trying to do is to find out
// the node that will become the parent of the new node we are inserting
// It doesn't matter if flow becomes NULL, the important value after the
// while loop ends is parent
while( flow != NULL ) {
// update the current potential parent node
parent = flow;
if( k < flow->nr ) {
// the number we are inserting is lower than the flow node, walk to the left
flow = flow->st;
} else {
// the number we are inserting is greater or equal than the flow node,
// walk to the right
flow = flow->dr;
}
}
// We have reached the bottom, now compare number again with the node that
// will become parent, to find out if we need to link this node to the left
// or to the right of the parent node
if( k < parent->nr ) {
parent->st = nodnou;
} else {
parent->dr = nodnou;
}
}
}
That's it. Try to code the rest of the tree operations and don't hesitate to ask if you become confused. =)
Related
I have a function called addMod that, when called, adds a node to a certain index of an array of Module struct LinkedLists called modules contained within a System struct. A Module struct has a string field, two int fields, and a pointer to the next Module, the first three fields being initialized according to arguments provided in addMod. addMod roughly looks like this:
int addMod(System *system, const char *text, int num1, int num2, int index) {
Module *temp = malloc(sizeof(Module));
Module *current;
temp->next = NULL;
if ([any of the constructors are invalid]) return 0;
temp->text = malloc(strlen(text)+1);
strcpy(temp->text, text);
temp->num1 = num1; temp->num2 = num2;
if (!system->modules[index]) {
system->modules[index] = temp; //If there are no modules in the LinkedList at the given index, makes the head = temp.
}
else {
if (system->compare(temp, system->modules[index]) <= 0) { //compare is a func pointer field of system that compares two Modules to see in what order they should be. Here, we check if temp should become the head of modules[index].
temp->next = system->modules[index]; //Assigns the current head as the module following temp.
system->modules[index] = temp; //Makes temp the current head.
}
else {
current = system->modules[index];
while (current->next && system->compare(temp, current->next) > 0) { //While current isn't the last node in the LinkedList and temp comes after the node after current
current = current->next;
}
temp->next = current->next; //Adds temp in between current and current->next.
current->next = temp;
}
}
return 1;
}
All of the above works as expected, except when printing the contents of system, the console indicates there's a memory leak that I'm assuming is because I fail to properly free temp based on what valgrind tells me. My problem is not knowing where to free it- it seems anywhere I put it causes a segfault after printing the contents. From my understanding, I have to make sure that no other variables are depending upon the value being held by temp, but I can't seem to find a way to do that considering every possible ending of my if statement leads to assigning temp to a node within modules. Putting free(temp) between the logic and return 1 also yields a segfault, I'm assuming because I often malloc temp again when calling addMod multiple times in succession.
In summary, to add a new node to a LinkedList that may or may not be populated, in which this new node may be inserted in any arbitrary position in the LinkedList, I have to allocate memory to a temporary node so that I can insert it later. Where do I free this allocated memory once I have successfully inserted the node?
Assuming your management of a System instance is sound (a big assumption, since I cannot see that code), you have giant hole in the memory allocation of temp with a subsequent hard return 0 in the condition where the "constructor" check fails. More to the point:
Module *temp = malloc(sizeof(Module)); // memory allocated here...
Module *current;
temp->next = NULL;
if ([any of the constructors are invalid])
return 0; // and leaked here.
It may be as simple as swapping the check around. Obviously other code that is supposed to free the dynamic allocations should be considered and evaluated as well.
A Simpler Approach
The node addition code is complicated and it need not be. In the end all you should really care about is finding the place where your new node resides.
If the slot in the table is empty, its the first node in that list.
IF the slot in the table is NOT empty, find the sorted location and insert it there.
Both of those can be accomplished with a single while-loop by using a pointer-to-pointer, where said entity hold the address of the pointer that will hold the new node in either of the cases above, and as a bonus, surgical insertion is literally two assignments.
It's done like this. Note that most of this code is just making the Module object safely. The actual insertion is only a single while-loop and some pointer assignments. It assumes the table in System initially contains NULL entries:
int addMod(System *system, const char *text, int num1, int num2, int index)
{
// allocate new node here
Module *temp = malloc(sizeof *temp);
if (temp == NULL)
{
perror("Failed to allocate new Module");
return 0;
}
size_t len = strlen(text);
temp->text = malloc(len + 1);
if (temp->text == NULL)
{
perror("Failed to allocate module name");
free(temp);
return 0;
}
// finish copying member data
memcpy(temp->text, text, len);
temp->text[len] = 0;
temp->num1 = num1;
temp->num2 = num2;
// now find where it belongs, and set next appropriately
Module **pp = system->modules + index;
while (*pp && system->compare(temp, *pp) <= 0)
pp = &(*pp)->next;
temp->next = *pp;
*pp = temp;
return 1;
}
Understand this is from deriving what I think your System type looks like, as it was never presented:
typedef struct System
{
Module *modules[MAX_MODULES];
int (*compare)(const Module* lhs, const Module *rhs);
} System;
I'm fairly confident it is similar to this. Of course, you'll have to adapt if it isn't. I suggest you review this and step through it in a debugger. There is no substitute for watching it live.
Best of luck.
So i'm writing this function that makes the smallest node at the end of the linked list. So Far it works except for the last element. if the linked list is created as nodes containing items from 0~9, the list after this function is {1,2,3,4,5,6,7,8,0,9} and the zero does not become at the end.
Any ideas why?
my function;
int connecting(linkk A)
{
linkk L = A;
for (int i = 0; i<sizeof(L);i++)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
L = L->next;
}
else{L=L->next;}
}
return 0;
}
Let's start with what I think you should be doing differently:
The name of your function is connecting. Given the description of what you'd like the function to do, this is not a good name.
I can infer from the usage that linkk is a typedef'ed pointer. Hiding pointers this way is not a good idea, most of the time.
Your function returns an int. It's always 0. Why? This does not make any sense.
Because linkk is probably a pointer to a node, and you pass the head pointer by value (i.e. a copy of it) to the function, you're unable to handle the case where the head of your list is the minimum. Either return the "new" head pointer, or pass a pointer to the head pointer to be able to modify the head pointer.
Your use of sizeof is completely wrong, as already suggested. sizeof(L) will give you the size (in chars) of the pointer, thus probably 8 on a 64 bit system or 4 on a 32 bit system.
You're not changing the nodes, but rather moving the values in between the nodes. This is OK if it's what you want to do, but the description of your algorithm suggest that you want to move the node instead.
Your function is doing too much, IMO. This can be hard to split, but a better approach would be to separate finding / extracting the minimum and putting it at the end of the list.
You're modifying a lot more than you originally wanted to. Consider a list like 1, 2, 3, 0, 4. Using your algorithm, the list will be changed to 2, 3, 1, 4, 0. Doing this is not only bad for the performance, but also very surprising for the caller. Surprises aren't good when it comes to programming!
So, let's get to a hopefully good implementation, step by step:
struct node {
int item;
struct node * next;
};
I assume that you want to move the node containing the minimum value to the end of the list, as in your description. I'm also going to keep this into a single function receiving a struct node * head pointer, despite my point above, in order to keep closer to the original code. Let's get out the special / base cases first: Moving the minimum element of an empty list as well as of a single element list is trivial: Do nothing.
if (head == NULL || head->next == NULL) {
return head;
}
I'm returning the "new" head of the list to allow the caller to update it's own head pointer. (As already said, head is just a copy of the caller's head pointer, modifying it will not have any effect at the call site).
Because we're dealing with a singly linked list here, and the implementation should not unnecessarily iterate over the list, we should remember the node we've previously visited. Otherwise we couldn't easily extract a node from the list:
struct node * follow, * point;
follow follows directly behind the point.
Initially, we place the point to the second node of the list (we already checked that there are at least 2 nodes in the list). follow will thus point to the head:
point = head->next;
follow = head;
Since we want to find the minimum item, we need to keep track of the minimum of the already searched part of the list. We initialize it with the head node's value:
int currentMinimum = head->item;
Now we're ready to iterate over the list, in order to find the node containing the minimum. But we need not only find the node containing the minimum, but also the one before it and the one after it, in order to be able to extract it easily. So, 3 pointers:
struct node * predecessor, * minimum, * successor;
As we set the currentMinimum to heads item, we should also set the pointers accordingly:
predecessor = NULL; // Nothing preceding the head
minimum = head;
successor = head->next;
Now let's iterate, moving the point completely over the list, until it falls off at the end:
while (point != NULL) {
// to be continued
follow = point;
point = point->next;
}
// when we're here, follow will point to the last node
In each iteration, we need to check if we found a smaller value than the current minimum, and eventually remember the node containing it:
if (point->item < currentMinimum) {
predecessor = follow;
minimum = point;
successor = point->next;
currentMinimum = point->item;
}
Now, when we get out of the loop, the following state should be reached:
minimum points to the node containing the minimum.
follow points to the last node of the list.
The two above could be the same, which is a special case!
predecessor could still be NULL, which is another special case!
Considering first the special case of minimum = follow: In that case, the minimum is already at the end of the list, so profit! Otherwise, we need to "cut" the node at minimum out of the list and append it to the last node, pointed to by follow:
if (follow != minimum) {
if (predecessor != NULL) {
predecessor->next = successor; // Cut out
minimum->next = NULL; // will be the last node
follow->next = minimum; // append at end
} else {
// to be continued
}
}
As you can see, there's the second special case to consider: If predecessor is still NULL, then no item was smaller than heads item. (Therefore, we could also test for minimum == head) Thus, the first node of the list will be moved to the end. We need to inform the caller about this!
head = head->next; // Second node is now the first one, though this is not all we need to do, see further down!
minimum->next = NULL; // Will be the last node
follow->next = minimum; // Append at end
Since the assignment to head only changed the function parameter (which is a copy of the pointer with which the function has been called), we need to return the (possibly modified!) head pointer, giving the caller the ability to update its own head pointer:
return head;
A caller would thus use this function like so:
struct node * head = get_a_fancy_list();
head = move_minimum_to_end(head); // This is our function being called!
Finally, a thing to consider: As you can see, moving the node (instead of the item) is more complicated. We need to modify at least 2 pointers in order to achieve what we want. In contrast: Moving the item value requires two modifications of item values (and iterating is easier). Moving the node instead of the item thus makes only sense when pointer assignments are faster than item assignments. Since the items are of type int this is not the case here.
Moving the item instead of the node containing the item is considerably easier. First of all, we need to keep track of the minimum (value as well as node):
struct node * minimum;
int currentMinimum;
To iterate, we're again going to use two pointers. It can be done with a single one, but the code is going to be more readable this way:
struct node * point, * follow;
We start off with the same initial state:
minimum = head;
currentMinimum = head->item;
follow = head;
point = head->next;
Iterating is similar to the other implementation, as is the iteration step:
while (point != NULL) {
if (point->item < currentMinimum) {
minimum = point;
currentMinimum = point->item;
}
follow = point;
point = point->next;
}
// follow points to the last node now
Now, doing the same as the previous implementation, we can swap the items of the last node and the node with the minimum:
minimum->item = follow->item;
follow->item = currentMinimum; // contains minimum->item
There's no point in checking for follow != minimum like in the previous approach: You could do it, but swapping the item of a node with its own item won't do any harm. OTOH adding an if will add a branch, and thus a possible performance penalty.
Since we didn't change the list structure (the linking between nodes), there's not much more to consider. We don't even need to inform the caller about a new head, as there will never be any change to it. For style purposes, I'd add it either way:
return head;
Ok, this got a bit long now, but hopefully it's easy to understand!
Try this function
int connecting(linkk A)
{
linkk L = A;
while(L->next!=NULL)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
}
L=L->next;
}
return 0;
}
I looked at several BST Insert articles but none of them were structured the same as mine or encountered the same problem.
My problem is my Binary Tree isn't being built correctly. It's really strange because I'm copy+pasting most of the code from a previous project where it works fine, the only difference is the data the nodes contain, and the condition for looping through the tree uses strcmp rather than integer comparison.
This is my insert function:
//insert parameter node data into a Binary Tree
TreeNodePtr insertNode(BinaryTree bst, Record d)
{
//if root is null insert into root
if(bst.root == NULL)
{
bst.root = (TreeNodePtr) malloc(sizeof(TreeNode));
bst.root->data = d;
bst.root->left = NULL;
bst.root->right = NULL;
return bst.root;
}
//we need to traverse the tree so declare a pointer "curr" to do so
TreeNodePtr curr = (TreeNodePtr) malloc(sizeof(TreeNode));
curr = bst.root;
//loop until we find an appropriate empty space or a match (no duplicates)
while (strcmp(d.lastName, curr->data.lastName) != 0)
{
if (strcmp(d.lastName, curr->data.lastName) < 0)
{ // if left
if(curr->left==NULL)
{
curr->left = (TreeNodePtr) malloc(sizeof(TreeNode));
curr->left->data = d;
curr->left->left = NULL;
curr->left->right = NULL;
return bst.root;
}
curr=curr->left;
}
else if (strcmp(d.lastName, curr->data.lastName) > 0)
{ // try right
if(curr->right==NULL)
{
curr->right = (TreeNodePtr) malloc(sizeof(TreeNode));
curr->right->data = d;
curr->right->left = NULL;
curr->right->right = NULL;
return bst.root;
}
curr=curr->right;
}
}
return bst.root;
}
Here is the code in the main function which uses the insert function to build the tree (note that records is a correctly populated array, each index containing one node's data):
//declare BST and build it
BinaryTree phoneTree;
phoneTree.root = NULL;
for (int i=0; i < sizeof(records) / sizeof(Record); i++)
{
Record tmpRecord;
tmpRecord.firstName = records[i].firstName;
tmpRecord.lastName = records[i].lastName;
tmpRecord.phoneNum = records[i].phoneNum;
phoneTree.root = insertNode(phoneTree, tmpRecord);
}
And for reference, here are the tree structs:
//phone data record struct
typedef struct
{
char *firstName;
char *lastName;
char *phoneNum;
}Record;
//define the tree node which contains the data
typedef struct treeNode
{
Record data;
struct treeNode *left,*right;
}TreeNode,*TreeNodePtr;
//define binary tree struct
typedef struct
{
TreeNodePtr root;
}BinaryTree;
I've been staring at the program that works and comparing it to this program for about 5 hours now and I can't figure out what's going wrong. I know the tree isn't populated correctly because if i try to print phoneTree.root->right.data or phoneTree.root->left.data attributes, the program crashes. In the program I'm borrowing the code from, these attributes are printed without error. The root is still inserted correctly and it's attributes can be printed.
Any insight as to what I'm doing incorrectly is greatly appreciated.
There is one definite mistake, which could be causing you problems. You need to pass "bst" by reference, so that the function can modify "bst.root". Try rewriting the function as:
TreeNodePtr insertNode(BinaryTree* bst, Record d)
and use "bst->" in place of "bst."
You said that it worked with integers. Now that may be a clue to another mistake. Your record contains only pointers to strings. Do these pointers remain valid throughout the lifetime of the tree? Maybe you need to make copies of the strings within the record.
Couple of other minor things:
//we need to traverse the tree so declare a pointer "curr" to do so
TreeNodePtr curr = (TreeNodePtr) malloc(sizeof(TreeNode));
curr = bst.root;
malloc is redundant here, the result is immediately overwritten.
And:
}
else if (strcmp(d.lastName, curr->data.lastName) > 0)
{ // try right
you can replace this with "} else {" as you already did this strcmp operation.
Thanks for all the great tips, they've all contributed to my understanding of memory management in C.
Strangely, I found the problem is actually rooted in my array for loop. I found the method of using sizeof(array) / sizeof(arraydatatype) from multiple sources on the internet and this site so I attempted it, but it doesn't work the way I tried. In:
for (int i=0; i < sizeof(records) / sizeof(Record); i++)
{
Record tmpRecord;
tmpRecord.firstName = records[i].firstName;
tmpRecord.lastName = records[i].lastName;
tmpRecord.phoneNum = records[i].phoneNum;
phoneTree.root = insertNode(phoneTree, tmpRecord);
}
I replaced "i < sizeof(records) / sizeof(Record)" with just"i < 3" (array should only have 3 elements at this point), and everything worked as it should. It's a really dumb source of the problem, but funny that despite all the answers provided none mentioned it :p
Since we're already here, can anyone explain why that was going wrong / how to properly loop through an array in such a manner?
I wrote this binary search tree data structure but I can't seem to figure out why or how the first node being printed is ZERO (0).
My code is publicly accessible here on ideone
void insert_tree(tree **l, int x, tree *parent)
{
tree *p; /* temp pointer */
if (*l == NULL) {
p = malloc(sizeof(tree));
p->item = x;
p->left = p->right = NULL;
p->parent = parent;
*l = p; /* link into parent's record */
return;
}
if (x < (*l)->item)
insert_tree(&((*l)->left), x, *l);
else
insert_tree(&((*l)->right), x, *l);
}
I think my confusion lies in the indirection and use of pointers (and pointer to pointer). I understand there are other ways to go about it but I am trying to stick with this particular use of pointers for good reason.
The problem in your code is that you are adding an empty node in the main, without initializing its item, left, or right: when you do this
tree *root = malloc(sizeof(tree));
the root gets assigned an uninitialized chunk of memory. You could place the first element into the first element directly, but this is not necessary: your insertion code already handles the situation when the root is NULL.
When you change your code to assign root a NULL, like this,
tree *root = NULL;
your program works correctly (demo).
This might seem like a silly question to some of you and I know that I get things mixed up quite often but I need to understand the code so I can stop obsessing about it and focus on the real matter of why I need to use it.
So, in the code I see several assignments like this:
struct bst_node** node = root;
node = &(*node)->left;
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
This example is taken from literateprograms.org.
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead, but the code seems to work where I can't make sense of it and I'm wondering if it's because I'm misunderstanding what's happening at those lines. Particularly, at one place in the code where it is deleting a node by constantly moving the "deleted" data to the bottom of the tree to safely remove the node without having to "break things", I'm lost because I don't get how
old_node = *node;
if ((*node)->left == NULL) {
*node = (*node)->right;
free_node(old_node);
else if ((*node)->right == NULL) {
*node = (*node)->left;
free_node(old_node);
} else {
struct bst_node **pred = &(*node)->left;
while ((*pred)->right != NULL) {
pred = &(*pred)->right;
}
psudo-code: swap values of *pred and *node when the
bottom-right of the left tree of old_node has been found.
recursive call with pred;
}
can keep the tree structure intact. I don't understand how this makes sure the structure is intact and would appreciate some help from somebody who knows what's going on. I interpret node being a local variable on the stack, created at the function call. Since it is a double pointer it points to a location in the stack (I assume this, since they did &(*node) previously to the function call), of either it's own stack or the function before, which then points to said node on the heap.
In the example code above what I think it is supposed to do is switch either left or right, since one of them is NULL, and then switch the one that isn't (assuming the other one isn't NULL?) As I said, I'm not sure about how this would work. My question mostly relates to the fact that I think &(*node) <=> node but I want to know if that's not the case etc.
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
Yes. It is taking the address of the right member of *node. The -> takes precedence over &; see C++ Operator Precedence (-> is 2 and & is 3 in that list) (it's the same general precedence as C).
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead,
Your premise is off. There is no expression &(*node), as explained above, the & applies to the entire (*node)->left, not (*node).
In that code the double pointers are just that, a pointer to a pointer. Just as this works:
int x = 0;
int *xptr = &x;
*xptr = 5;
assert(x == 5);
This is the same, it changes the value of the pointer x:
int someint;
int *x = &someint;
int **xptr = &x;
*xptr = NULL;
assert(x == NULL);
In that code snippet you posted, assigning a pointer to *node changes the value of the pointer that node points to. So, e.g. (pseudo-ish code):
typedef struct bst_node_ {
struct bst_node_ *left;
struct bst_node_ *right;
} bst_node;
bst_node * construct_node () {
return a pointer to a new bst_node;
}
void create_node (bst_node ** destination_ptr) {
*destination_ptr = construct_node();
}
void somewhere () {
bst_node *n = construct_node();
create_node(&n->left); // after this, n->left points to a new node
create_node(&n->right); // after this, n->right points to a new node
}
Noting again that &n->left is the same as &(n->left) because of precedence rules. I hope that helps.
In C++ you can pass arguments to a function by reference, which is essentially the same as passing a pointer except syntactically it leads to code that is a bit easier to read.
That is useful
&(*node)->left <=>&((*node)->left)
The variable edited by this code is *node. I need the context fo this code to give more info