Swapping Nodes on a single linked list - c++
I am trying to make a swapNode function that can take any two nodes and swap them. I've made an algorithm that works if they're at least 2 nodes away, but I can't seem to come up with an algorithm that will work if they are closer to each other.
Here's what I wrote so far:
void swapNode(call * &head, call * &first, call * &second){
call * firstPrev = NULL;
call * secPrev = NULL;
call * current = head;
//set previous for first
while((current->next != first) ){
current = current->next;
}
firstPrev = current;
current = head;
//set previous for second
while((current->next != second) ){
current = current->next;
}
secPrev = current;
current = second->next;
//set firstPrev-> next to second
firstPrev->next = second;
//set secPrev->next to first
secPrev->next = first;
//set second->next = first->next
second->next = first->next;
//set first->next to current
first->next = current;
current = head;
while(current->next != NULL){
cout << current->number << endl;
current = current->next;
}
cout << current->number << endl;
}
EDIT:
I now have this as my swap part, but it still doesn't seem to work correctly
//swap firstPrev-> next with second->next
tmp = firstPrev->next;
second->next = firstPrev->next;
second->next = tmp;
//swap swap first->next with second->next
tmp = first->next;
second->next = first->next;
second->next = tmp;
EDIT2:
This one doesn't seem to work either, I get a seg fault.
//swap previous's->next
tmp =firstPrev->next;
secPrev->next = firstPrev->next;
secPrev->next = tmp;
//swap swap first->next with second->next
tmp = first->next;
second->next = first->next;
second->next = tmp;
Say we have:
Node1 -> Node2 -> Node3 -> Node4 -> Node5
To swap two nodes, you need to swap the next values of the ones before each of them, and also the next values of the nodes you want to swap.
So to swap, say, Node2 and Node3, you effectively have to swap Node1->next with Node2->next, and Node2->next with Node3->next. That will work, even if they're right next to each other (or even if it's the same node). For example:
Swap Node1->next and Node2->next
Node1->next = Node3
Node2->next = Node2
Swap Node2->next with Node3->next
Node2->next = Node4
Node3->next = Node2
This comes out as:
Node1 -> Node3 -> Node2 -> Node4 -> Node5
Swapped!
As unwind noted in the comments section, if swapping Node1 with anything, you'll have to set a new head for the linked list.
In response to the edit of the question:
Your code for swapping almost right. However, you need to swap the firstPrev with secPrev. It just so happened in my example that we were swapping one of the node's next values twice, because they were next to each other. But logically, we want to swap the nexts of the two previous ones, and then swap the nexts of the actual nodes. Try this:
//swap firstPrev-> next with secPrev->next
tmp = firstPrev->next;
secPrev->next = firstPrev->next;
secPrev->next = tmp;
//swap swap first->next with second->next
tmp = first->next;
second->next = first->next;
second->next = tmp;
If you're getting a segfault, check the tmp variable - it could be an error of allocation or deletion somewhere. Where do you get the segfault?
In most real-life scenarios, swapping the values will be the best solution:
void swapNode(call * &head, call * &first, call * &second) {
// swap values, assuming the payload is an int:
int tempValue = first->value;
first->value = second->value;
second->value = tempValue;
}
If that's not allowed, then you want to do a similar-style swap on the ->next instead of the ->value component. And then do another swap on the firstPrev->next and secondPrev->next components. Watch out for the special case where first or second == head.
You will have to also swap the next component of the previous node, otherwise the linked list will not remain joined together. Note that my struct is called node.
int swapNode( node *&head * &first, node * &second)
{
//first we will declare the
//previous of the swapping nodes
node *firstprev=NULL;
node*secprev=NULL;
node*current=head;
//set previous first
while(current->next!=first)
{
current=current->next;
}
firstprev=current;
//seting 2nd previous
while(current->next!=second)
{
current=current->next;
}
// swap values, assuming the payload is an int:
int tempValue = first->value;
first->value = second->value;
second->value = tempValue;
//swaping next of the nodes
firstprev->next=second;
secprev->next=first;
return;
}
Rule of thumb: "Always separate data from pointers and never swap pointers, only the data!". Make swap explicit without using memcpy(), thus you can avoid alignment problems. It causes no performance penalty in terms of algorithmic complexity, but makes your code more readable and safe.
Here p1 is the 1st node to be swaped, p2 is 2nd node to be swaped. And prevnode is the node that is previous of p2
temp=head;
while(temp!=NULL){
if(temp->link==p1){
temp->link=p2;
prevnode->link=p2->link;
p2->link=p1->link;
t=p1->link;
while(t!=prevnode)
t=t->link;
cout<<" YES";
cout<<"["<<t->num<<"] ";
p1->link=prevnode->link;
prevnode->link=p1;
temp=p1;
}//if_
cout<<" "<<temp->num;
temp=temp->link;
}
While I am not 100% sure the answer should involve references to node pointer (or pointers to pointers) and this should then handle a case when one of the nodes is the head of list as well.
void swapNodes(node *&first, node *&second)
{
node *t = second->next;
second->next = first->next;
first->next = t;
t = second;
second = first;
first = t;
}
Then you can call it for example:
swapNodes(head, secPrev->next);
or
swapNodes(firstPrev->next, head);
or
swapNodes(firstPrev->next, secPrev->next)
and it should work automagically.
EDIT:
swapNodes could be even more readable:
void swapNodes(node *&first, node *&second)
{
std::swap(first->next, second->next);
std::swap(first, second);
}
void swap()
{
struct node *temp=0,*nxt,*ptr;
ptr=head;
int count=0;
while(ptr)
{
nxt=ptr->link;
if(nxt)
{
if(count==0)
head=nxt;
count++;
ptr->link=nxt->link;
nxt->link=ptr;
if(temp!=NULL)
temp->link=nxt;
temp=ptr;
if(ptr->link==NULL)
break;
ptr=nxt->link->link;
}
}
}
Thank you everyone for your answers! I do realize that this question has been asked almost two years ago and an answer long been accepted, but I have been a bit confused by the answers. Thus, despite the questioner probably not caring about new answers, I would like to add my version, in case other readers were confused as well and to document my own approach. Some of this may have been more fitting as comments, but I do not have the reputation to comment, yet.
First, no matter how often I look at it - on the whiteboard or in the debugger - I cannot seem to avoid ending up with a loop in the first node, i.e. pointing to itself, if I do not use a conditional to distinguish between cases of nodes being adjacent and not, even with the abstract steps or the concrete code from the currently accepted answer by Smashery. I have looked around a bit on the Internet to find code in the style of the currently accepted answer to avoid such a conditional, but to me it is surprisingly tricky to avoid and my searches have not turned up such a solution (unless I am wrong about the proposed one possibly being incorrect). If there was some clever expression that would yield the first node's address when they are adjacent and the first node's successor's address when they are not, then that conditional would not be needed, because figuring out the second node's new successor is what (apparently) necessitates that conditional.
Second, I have the same question about the consecutive assignments to the same variables in the accepted answer as other commentators. I hope I am not being extremely dense here, but assigning different values to the same variable in sequence, unless perhaps in case of side-effects, to me just never seems to leave the variable with any other value than the last assignment, no matter what configuration of nodes I consider, and thus makes the previous assignments apparently redundant. If I am wrong about that and that approach would in fact solve this problem then I would have been able to eliminate the last conditional in the code below, which I was trying to get rid off when I first looked on the Internet for a solution to swapping nodes without special-casing adjacent nodes. I am not quite sure, but it sounded like Smashery purposefully left those repeated assignments out of logical rigor and to better illustrate the procedure - I may have misunderstood, though.
Third, on this and other sites I have often seen the statement from other answers repeated that it is better to swap the content of the nodes, rather than the pointers. Of course, in the case of simple integers, as in the examples so far, that does apparently yield shorter, simpler code. However, when we discuss linked lists with nodes containing integers it is usually as a stand-in for the backing data structure of a more complex and generic container. As such, I don't think swapping the contents of the nodes is really that easy, at least if the implementation of the data structure cannot make assumptions about the copy semantics of the container's items. Also, being able to swap around the contents of nodes like that implies to me that the linked list has ownership of the contents of those nodes, because otherwise code outside of the linked list's method might hold references to the objects in those nodes, whose values suddenly change underneath them.
I do admit, though, that this might depend on the semantics of the container. For an array, a swap method may be expected to change the value underneath references to a certain index of that array. That would mean that the references are not meant to refer to a specific object, but to a position in a container that one can index. If we consider a linked list as a means to only order a set of objects, which have their use outside of the linked list, a user would probably expect a swap operation to only exchange the position, not the contents.
Imagine, for example, that the linked list represents objects of type "car". Each car has an owner and that owner references their car through a pointer to it. Now suppose the linked list represents the order a set of cars are scheduled to be serviced for inspection at a car dealership. If we swapped the contents of two nodes, in order to exchange the schedule slots for two cars, and did it by exchanging their contents, then the servicing would in fact happen in the new, right order - but people would also end up suddenly owning different cars! (I wouldn't mind swapping with a Tesla, though, because I am only driving a Corolla.)
If the linked list was, as in the example of the array, based on indexing semantics then the position in the node might simply represent the order in which the cars are loaded onto a ship for transport. At this point, the cars don't have any owners and we really only care what slot they are in. Then, I suppose it really doesn't hurt to swap out cars, i.e. the contents of the objects referenced by the nodes.
Finally to the code. As I said above, I haven't been able to avoid the special-casing for adjacent nodes.
First, the definition of an auxiliary method:
int find_node(int v, node* root, node** n, node** pn) {
int i = 0;
for (*n = root; *n != NULL && (*n)->v != v; ++i) {
*pn = *n;
*n = (*n)->next;
}
return i;
}
This method finds a node by its value. The integer returned is the zero-based position (call it its index if you like) of the node in the linked list. I found detecting adjacency through position, rather than pointer comparisons, more readable. The start of the list is root. The method sets n to point to the node containing the passed value. In pn, the method stores the predecessor of n.
The following is the actual swap:
void swap_nodes(node **root, int v1, int v2) {
if (v1 == v2) return;
node *n1, *n2, *pn1, *pn2;
int p1 = find_node(v1, *root, &n1, &pn1);
int p2 = find_node(v2, *root, &n2, &pn2);
if (p1 > p2) {
std::swap(n1, n2);
std::swap(pn1, pn2);
std::swap(p1, p2);
}
if (p1 == 0) *root = n2;
else pn1->next = n2;
node* nn1 = n1->next;
n1->next = n2->next;
if (p2 - p1 > 1) {
n2->next = nn1;
pn2->next = n1;
} else {
n2->next = n1;
}
}
I'm sorry that I have changed the signature of the OP's method a bit. I found it more convenient to pass in the values of the nodes to swap, as opposed to node pointers. If you pass in node pointers to the respective nodes only, you would have to do another traversal to find the predecessors with this solution, which felt a bit awkward to me. If we cannot distinguish nodes by these values, e.g. values are not unique, we will need pointers to the nodes, though.
As with the explanation for find_node above, we first find the positions, nodes, and predecessors for the node values passed to swap_nodes through v1 and v2. The values for the first and second nodes are all swapped if the second node to swap appears before the first. It's not much code to do so, reduces special casing, and makes it a bit easier to visualize.
Now, we are left with just two more conditionals, neither of which seemed trivial to avoid. If the first node is at the head of the linked list, i.e. at position zero, the root needs to point to the second node. Otherwise, the first node's predecessor will point to the second node.
The previous value of the first node's successor needs to be remembered, in case the nodes are not adjacent. Then, the first node's successor is set to the current successor of the second node. This is the only change that applies to all cases: the new successor of the first node being the old successor of the second node is the only certainty and helpful to start off with in order to remember the pointer operations and their sequence when implementing this swap.
Last, if the positions of the nodes differ by more than one, they are not adjacent. Then, the second node's new successor becomes the first node's old successor - saved away above - and the second node's predecessor now points to the first node. If they are adjacent, there are no nodes between the nodes to swap that need updating, so simply linking the second node to the first is all that is left to do.
Related
Return a pointer to a sorted list; linked list in C++
i want this function sortPair to take 2 Node pointers and return a pointer to a list of the 2 elements sorted alphabetically. The code below is what I have so far. If someone could let me know where I went wrong, that would be great. struct Node{ string val; Node* next; }; Node* sortPair (Node* p1, Node* p2){ //Assert that neither pointer is null assert(p1!=NULL); assert(p2!=NULL); Node* head=NULL; Node* current=NULL; Node* last = NULL; current = new Node(); if(p1-> val >p2-> val) //If p1->val comes before p2->val in the alphabet { current->val = p1->val; head = current; last = current; current = new Node(); current -> val = p2->val; last = current; last ->next = NULL; } else { current->val = p2->val; head = current; last = current; current = new Node(); current -> val = p1->val; last = current; last ->next = NULL; } return head; }
A linked list is just a series of nodes that are linked by each element having a pointer to the next one. From your code, it seems like you do not want to make a list of the two nodes, but rather insert a node into a list that already exists. If all you want to do is to make a linked list of the two nodes that are there, then set the one with the lower or higher value, depending on how you sort them, to point at the other one. For example, if you are sorting from smallest to biggest, set the smallest node's next pointer to point to the bigger one and return the pointer of the smallest one. If you want to add one of the nodes into a list of nodes, then you must loop through the list until you find one that is larger or smaller than the node you want to insert. I recommend using a while loop. If you want to merge two lists, then you must make a new loop that inserts each element of one list into the other list. There is no need to make a new node for any of this, just use the ones you have and change the next pointers. Hope this helps.
c++ understanding a circular doubly linked list
I'm having a problem figuring out how to set my ptr's for prev and next when I add a node with at least one existing node. Adding the first node is easy, just setting the ptrs to the front. I need help viewing this mentally, also this program is a queue so each node gets added to the back of the list. if (Empty()) { front = new qnode; front->next=front; front->prev=front; front->data = item; } else if (front->prev=front) { front->prev = new qnode; front->prev->next= front; front->next=front->prev; front->prev->data = item; } else { } What i have now still not getting it else { front->prev= new qnode; front->prev->data= item; front->prev->next=front; front->prev=front->prev->prev; }
I hope this image helps a bit I have created pictures for 1 item 2 items and 3 items the pointers just point to the actual object meaning the black rectangle is the whole object if front being blue and prev being brown(those are just there as references) I really hope this helps linked list can get really tricky and drawing always helps me. so to add the item at the front of the list you have some code like this: //ok first I'll define some variables for you //last === the last node in the list //head === the first node in the list //node === the new node you are adding; qnode node = new qnode; node.data = data; //whatever data you are holding node->next = last; //last element in the list since it is circular; node->prev = head; //you want the new node to point the the first node since it's getting added before that; head->next = node; //you want the head of the node to point to the new node not the last item last->prev = node; //last node now should point to the new node you just added not the head;
I am not sure why you need an else if, as I think the case with one element is not different from that with multiple elements. In any case you have a syntax error there - you wanted to write == instead of =. More to the point, what you want to do is: Create a new qnode Set the data of the new node to the item Set the next of the new node to the front Set the prev of the new node to front->prev Set the prev of the front to the new node. You may check for yourself on a piece of paper how this works.
In a circular doubly linked list, you have 2 pointers, one to the head and one to the tail (last element in the list). If a element has a next, the next element's prev should be the element pointing at it through it's next element. Your head pointer should never move unless the head element is removed. Your tail will move along always ensuring that the element it points to has head as it's next element. Hopefully this helps
Since you always add at the end, the case of a queue with a single element is identical with the case of a list with multiple elements. Take a paper and a pen, make some sketches and it will come very easy to you.
Is it possible to make efficient pointer-based binary heap implementations?
Is it even possible to implement a binary heap using pointers rather than an array? I have searched around the internet (including SO) and no answer can be found. The main problem here is that, how do you keep track of the last pointer? When you insert X into the heap, you place X at the last pointer and then bubble it up. Now, where does the last pointer point to? And also, what happens when you want to remove the root? You exchange the root with the last element, and then bubble the new root down. Now, how do you know what's the new "last element" that you need when you remove root again?
Solution 1: Maintain a pointer to the last node In this approach a pointer to the last node is maintained, and parent pointers are required. When inserting, starting at the last node navigate to the node below which a new last node will be inserted. Insert the new node and remember it as the last node. Move it up the heap as needed. When removing, starting at the last node navigate to the second-to-last node. Remove the original last node and remember the the new last node just found. Move the original last node into the place of the deleted node and then move it up or down the heap as needed. It is possible to navigate to the mentioned nodes in O(log(n)) time and O(1) space. Here is a description of the algorithms but the code is available below: For insert: If the last node is a left child, proceed with inserting the new node as the right child of the parent. Otherwise... Start at the last node. Move up as long as the current node is a right child. If the root was not reached, move to the sibling node at the right (which necessarily exists). Then (whether or not the root was reached), move down to the left as long as possible. Proceed by inserting the new node as the left child of the current node. For remove: If the last node is the root, proceed by removing the root. Otherwise... Start at the last node. Move up as long as the current node is a left child. If the root was not reached, move to the sibling left node (which necessarily exists). Then (whether or not the root was reached), move down to the right as long as possible. We have arrived at the second-to-last node. However, there are some things to be careful about: When removing, there are two special cases: when the last node is being removed (unlink the node and change the last node pointer), and when the second-to-last node is being removed (not really special but the possibility must be considered when replacing the deleted node with the last node). When moving nodes up or down the heap, if the move affects the last node, the last-node pointer must be corrected. Long ago I have made an implementation of this. In case it helps someone, here is the code. Algorithmically it should be correct (has also been subjected to stress testing with verification), but there is no warranty of course. Solution 2: Reach the last node from the root This solution requires maintaining the node count (but not parent pointers or the last node). The last (or second-to-last) node is found by navigating from the root towards it. Assume the nodes are numbered starting from 1, as per the typical notation for binary heaps. Pick any valid node number and represent it in binary. Ignore the first (most significant) 1 bit. The remaining bits define the path from the root to that node; zero means left and one means right. For example, to reach node 11 (=1011b), start at the root then go left (0), right (1), right (1). This algorithm can be used in insert to find where to place the new node (follow the path for node node_count+1), and in remove to find the second-to-last-node (follow the path for node node_count-1). This approach is used in libuv for timer management; see their implementation of the binary heap. Usefulness of Pointer-based Binary Heaps Many answers here and even literature say that an array-based implementation of a binary heap is strictly superior. However I contest that because there are situations where the use of an array is undesirable, typically because the upper size of the array is not known in advance and on-demand reallocations of an array are not deemed acceptable, for example due to latency or possibility of allocation failure. The fact that libuv (a widely used event loop library) uses a binary heap with pointers only further speaks for this. It is worth noting that the Linux kernel uses (pointer-based) red-black trees as a priority queue in a few cases, for example for CPU scheduling and timer management (for the same purpose as in libuv). I find it likely that changing these to use a pointer-based binary heap will improve performance. Hybrid Approach It is possible to combine Solution 1 and Solution 2 into a hybrid approach which dynamically picks either of the algorithms (for finding the last or second-to-last node), the one with a lower cost, measured in the number of edges that need to be traversed. Assume we want to navigate to node number N, and highest_bit(X) means the 0-based index of the highest-order bit in N (0 means the LSB). The cost of navigating from the root (Solution 2) is highest_bit(N). The cost of navigating from the previous node which is on the same level (Solution 1) is: 2 * (1 + highest_bit((N-1) xor N)). Note that in the case of a level change the second equation will yield a wrong (too large) result, but in that case traversal from the root is more efficient anyway (for which the estimate is correct) and will be chosen, so there is no need for special handling. Some CPUs have an instruction for highest_bit allowing very efficient implementation of these estimates. An alternative approach is to maintain the highest bit as a bit mask and do these calculation with bit masks instead of bit indices. Consider for example that 1 followed by N zeroes squared is equal to 1 followed by 2N zeroes). In my testing it has turned out that Solution 1 is on average faster than Solution 2, and the hybrid approach appeared to have about the same average performance as Solution 2. Therefore the hybrid approach is only useful if one needs to minimize the worst-case time, which is (twice) better in Solution 2; since Solution 1 will in the worst case traverse the entire height of the tree up and then down. Code for Solution 1 Note that the traversal code in insert is slightly different from the algorithm described above but still correct. struct Node { Node *parent; Node *link[2]; }; struct Heap { Node *root; Node *last; }; void init (Heap *h) { h->root = NULL; h->last = NULL; } void insert (Heap *h, Node *node) { // If the heap is empty, insert root node. if (h->root == NULL) { h->root = node; h->last = node; node->parent = NULL; node->link[0] = NULL; node->link[1] = NULL; return; } // We will be finding the node to insert below. // Start with the current last node and move up as long as the // parent exists and the current node is its right child. Node *cur = h->last; while (cur->parent != NULL && cur == cur->parent->link[1]) { cur = cur->parent; } if (cur->parent != NULL) { if (cur->parent->link[1] != NULL) { // The parent has a right child. Attach the new node to // the leftmost node of the parent's right subtree. cur = cur->parent->link[1]; while (cur->link[0] != NULL) { cur = cur->link[0]; } } else { // The parent has no right child. This can only happen when // the last node is a right child. The new node can become // the right child. cur = cur->parent; } } else { // We have reached the root. The new node will be at a new level, // the left child of the current leftmost node. while (cur->link[0] != NULL) { cur = cur->link[0]; } } // This is the node below which we will insert. It has either no // children or only a left child. assert(cur->link[1] == NULL); // Insert the new node, which becomes the new last node. h->last = node; cur->link[cur->link[0] != NULL] = node; node->parent = cur; node->link[0] = NULL; node->link[1] = NULL; // Restore the heap property. while (node->parent != NULL && value(node->parent) > value(node)) { move_one_up(h, node); } } void remove (Heap *h, Node *node) { // If this is the only node left, remove it. if (node->parent == NULL && node->link[0] == NULL && node->link[1] == NULL) { h->root = NULL; h->last = NULL; return; } // Locate the node before the last node. Node *cur = h->last; while (cur->parent != NULL && cur == cur->parent->link[0]) { cur = cur->parent; } if (cur->parent != NULL) { assert(cur->parent->link[0] != NULL); cur = cur->parent->link[0]; } while (cur->link[1] != NULL) { cur = cur->link[1]; } // Disconnect the last node. assert(h->last->parent != NULL); h->last->parent->link[h->last == h->last->parent->link[1]] = NULL; if (node == h->last) { // Deleting last, set new last. h->last = cur; } else { // Not deleting last, move last to node's place. Node *srcnode = h->last; replace_node(h, node, srcnode); // Set new last unless node=cur; in this case it stays the same. if (node != cur) { h->last = cur; } // Restore the heap property. if (srcnode->parent != NULL && value(srcnode) < value(srcnode->parent)) { do { move_one_up(h, srcnode); } while (srcnode->parent != NULL && value(srcnode) < value(srcnode->parent)); } else { while (srcnode->link[0] != NULL || srcnode->link[1] != NULL) { bool side = srcnode->link[1] != NULL && value(srcnode->link[0]) >= value(srcnode->link[1]); if (value(srcnode) > value(srcnode->link[side])) { move_one_up(h, srcnode->link[side]); } else { break; } } } } } Two other functions are used: move_one_up moves a node one step up in the heap, and replace_node replaces moves an existing node (srcnode) into the place held by the node being deleted. Both work only by adjusting the links to and from the other nodes, there is no actual moving of data involved. These functions should not be hard to implement, and the mentioned link includes my implementations.
The pointer based implementation of the binary heap is incredibly difficult when compared to the array based implementation. But it is fun to code it. The basic idea is that of a binary tree. But the biggest challenge you will have is to keep it left-filled. You will have difficulty in finding the exact location as to where you must insert a node. For that, you must know binary traversal. What we do is. Suppose our heap size is 6. We will take the number + 1, and convert it to bits. The binary representation of 7 is, "111". Now, remember to always omit the first bit. So, now we are left with "11". Read from left-to-right. The bit is '1', so, go to the right child of the root node. Then the string left is "1", the first bit is '1'. As you have only 1 bit left, this single bit tells you where to insert the new node. As it is '1' the new node must be the right child of the current node. So, the raw working of the process is that, convert the size of the heap into bits. Omit the first bit. According to the leftmost bit, go to the right child of the current node if it is '1', and to the left child of the current node if it is '0'. After inserting the new node, you will bubble it up the heap. This tells you that you will be needing the parent pointer. So, you go once down the tree and once up the tree. So, your insertion operation will take O(log N). As for the deletion, it is still a challenge to find the last node. I hope you are familiar with deletion in a heap where we swap it with the last node and do a heapify. But for that you need the last node, for that too, we use the same technique as we did for finding the location to insert the new node, but with a little twist. If you want to find the location of the last node, you must use the binary representation of the value HeapSize itself, not HeapSize + 1. This will take you to the last node. So, the deletion will also cost you O(log N). I'm having trouble in posting the source code here, but you can refer to my blog for the source code. In the code, there is Heap Sort too. It is very simple. We just keep deleting the root node. Refer to my blog for explanation with figures. But I guess this explanation would do. I hope my answer has helped you. If it did, let me know...! ☺
For those saying this is a useless exercise, there are a couple of (admittedly rare) use cases where a pointer-based solution is better. If the max size of the heap is unknown, then an array implementation will need to stop-and-copy into fresh storage when the array fills. In a system (e.g. embedded) where there are fixed response time constraints and/or where free memory exists, but not a big enough contiguous block, this may be not be acceptable. The pointer tree lets you allocate incrementally in small, fixed-size chunks, so it doesn't have these problems. To answer the OP's question, parent pointers and/or elaborate tracking aren't necessary to determine where to insert the next node or find the current last one. You only need the bits in the binary rep of the heap's size to determine the left and right child pointers to follow. Edit Just saw Vamsi Sangam#'s explanation of this algorithm. Nonetheless, here's a demo in code: #include <stdio.h> #include <stdlib.h> typedef struct node_s { struct node_s *lft, *rgt; int data; } NODE; typedef struct heap_s { NODE *root; size_t size; } HEAP; // Add a new node at the last position of a complete binary tree. void add(HEAP *heap, NODE *node) { size_t mask = 0; size_t size = ++heap->size; // Initialize the mask to the high-order 1 of the size. for (size_t x = size; x; x &= x - 1) mask = x; NODE **pp = &heap->root; // Advance pp right or left depending on size bits. while (mask >>= 1) pp = (size & mask) ? &(*pp)->rgt : &(*pp)->lft; *pp = node; } void print(NODE *p, int indent) { if (!p) return; for (int i = 0; i < indent; i++) printf(" "); printf("%d\n", p->data); print(p->lft, indent + 1); print(p->rgt, indent + 1); } int main(void) { HEAP h[1] = { NULL, 0 }; for (int i = 0; i < 16; i++) { NODE *p = malloc(sizeof *p); p->lft = p->rgt = NULL; p->data = i; add(h, p); } print(h->root, 0); } As you'd hope, it prints: 0 1 3 7 15 8 4 9 10 2 5 11 12 6 13 14 Sift-down can use the same kind of iteration. It's also possible to implement the sift-up without parent pointers using either recursion or an explicit stack to "save" the nodes in the path from root to the node to be sifted.
A binary heap is a complete binary tree obeying the heap property. That's all. The fact that it can be stored using an array, is just nice and convenient. But sure, you can implement it using a linked structure. It's a fun exercise! As such, it is mostly useful as an exercise or in more advanced datastructures( meldable, addressable priority queues for example ), as it is quite a bit more involved than doing the array version. For example, think about siftup/siftdown procedures, and all the edge cutting/sewing you'll need to get right. Anyways, it's not too hard, and once again, good fun!
There are a number of comments pointing out that by a strict definition it is possible to implement a binary heap as a tree and still call it a binary heap. Here is the problem -- there is never a reason to do so since using an array is better in every way. If you do searches to try to find information on how to work with a heap using pointers you are not going to find any -- no one bothers since there is no reason to implement a binary heap in this way. If you do searches on trees you will find lots of helpful materials. This was the point of my original answer. There is nothing that stops people from doing it this way but there is never a reason to do so. You say -- I have to do so, I've got an legacy system and I have pointers to nodes I need to put them in a heap. Make an array of those pointers and work with them in this array as you would a standard array based heap, when you need the contents dereference them. This will work better than any other way of implementing your system. I can think of no other reason to implement a heap using pointers. Original Answer: If you implement it with pointers then it is a tree. A heap is a heap because of how you can calculate the location of the children as a location in the array (2 * node index +1 and 2 * node index + 2). So no, you can't implement it with pointers, if you do you've implemented a tree. Implementing trees is well documented if you search you will find your answers.
I have searched around the internet (including SO) and no answer can be found. Funny, because I found an answer on SO within moments of googling it. (Same Google search led me here.) Basically: The node should have pointers to its parent, left child, and right child. You need to keep pointers to: the root of the tree (root) (duh) the last node inserted (lastNode) the leftmost node of the lowest level (leftmostNode) the rightmost node of the next-to-lowest level (rightmostNode) Now, let the node to be inserted be nodeToInsert. Insertion algorithm in pseudocode: void insertNode(Data data) { Node* parentNode, nodeToInsert = new Node(data); if(root == NULL) { // empty tree parent = NULL; root = nodeToInsert; leftmostNode = root; rightmostNode = NULL; } else if(lastNode.parent == rightmostNode && lastNode.isRightChild()) { // level full parentNode = leftmostNode; leftmostNode = nodeToInsert; parentNode->leftChild = nodeToInsert; rightmostNode = lastNode; } else if (lastNode.isLeftChild()) { parentNode = lastNode->parent; parentNode->rightChild = nodeToInsert; } else if(lastNode.isRightChild()) { parentNode = lastNode->parent->parent->rightChild; parentNode->leftChild = nodeToInsert; } nodeToInsert->parent = parentNode; lastNode = nodeToInsert; heapifyUp(nodeToInsert); } Pseudocode for deletion: Data deleteNode() { Data result = root->data; if(root == NULL) throw new EmptyHeapException(); if(lastNode == root) { // the root is the only node free(root); root = NULL; } else { Node* newRoot = lastNode; if(lastNode == leftmostNode) { newRoot->parent->leftChild = NULL; lastNode = rightmostNode; rightmostNode = rightmostNode->parent; } else if(lastNode.isRightChild()) { newRoot->parent->rightChild = NULL; lastNode = newRoot->parent->leftChild; } else if(lastNode.isLeftChild()) { newRoot->parent->leftChild = NULL; lastNode = newRoot->parent->parent->leftChild->rightChild; } newRoot->leftChild = root->leftChild; newRoot->rightChild = root->rightChild; newRoot->parent = NULL; free(root); root = newRoot; heapifyDown(root); } return result; } heapifyUp() and heapifyDown() shouldn’t be too hard, though of course you’ll have to make sure those functions don’t make leftmostNode, rightmostNode, or lastNode point at the wrong place. TL;DR Just use a goddamn array.
Sorting Nodes (Linked-list) C++
I'm trying to sort a linked-list. I have a Node called head and it's pointing to the next node, etc. But when I try to sort the Nodes by the value they carry, I get the sort working because I see it printing out the stuff in the if-statement, but I'm not getting back the linked-list. Where did I go wrong? Node* head; void sortlist(){ Node * runner = head; Node * runner2; for(runner = head; runner->next != NULL; runner = runner->next){ for(runner2 = runner->next; runner2->next != NULL; runner2 = runner2->next){ if(runner->freq < runner2->freq){ cout<< runner->freq<< " is LT "<<runner2->freq<< endl; Node * temp = runner; runner = runner2; runner2 = temp; } } } head = runner; } I'm only getting back the first Node.
In order to swap two elements in a linked list, consider what you need to change. For example, to get from Head -> First -> Second -> (NULL) to Head -> Second -> First -> (NULL) you need to update: Head.next, First.next and Second.next. You don't change any of those things when trying to swap nodes, so it can't possibly do what you expect. Just swapping the values (ie, swap(runner->freq, runner2->freq)) would be much simpler.
you will stop when runner->next == NULL;, which is supposed to be the last element. And then you set head = runner;, which means the head will always be the last element after this routine. Furthermore, I do not trust this swapping. It seems you vaguely want to do an insertion sort. If you want to do a simple sorting on linked lists, I suggest you to use selection sort: You create another empty list l2, and each time you remove the minimum element from your first list, and add it as the head of l2. The code for the second list is simple: void prepend(Node* node, Node** list){ //null checks if you want node->next = *list; *list=node->next; }
Tree delete node
I am trying to finish the delete function. Here is the pseudo code, notice the end: I don't know if the pseudo code is wrong though. Here is how I interpreted it: Node* minNode = Minimum(toDelete->right); int tmp = 0; tmp = minNode->val; // delete(&tmp); free(minNode); minNode=NULL; toDelete->val=tmp; except once it deletes it, it starts filling a trillion zeroes when printing. Is what I am doing making sense? The rest of the code I have is right, or I think so anyway. It only screws up in this scenario. Here's the minimum function as well Node* BST::Minimum(Node *curr) { // if (curr->left != NULL) { // return(Minimum(curr->left)); // } // return curr; Node* node = curr; while (node->left != NULL) { node = node->left; } return node; }
That is some terrible pseudocode, and at a glance it didn't even look right (if this is a binary search tree, as BST would indicate, then the circled part is wrong). There is much better information about binary search trees available on the internet. At any rate, you're trying to find the smallest element in the right subtree, since that will be less than all the other elements in the right subtree but greater than all the other elements in the left subtree. Looks like your minimum function is right. You need to delete the reference to minNode after you free it. So (minNode->parent)->left = NULL or something slightly more tedious if you don't have a parent pointer. Right now that left just points to an empty space in memory, leading to completely random behavior.