So I am trying to debug my code to reverse a double-linked list. It runs correctly, but for some reason, when I try to assign the front -> next = front -> prev. It gives to next to NULL, which occurs because NULL is behind the first element of the linked list. I am trying to figure out a way to overcome this issue.
void List::reverse(Node *front) //front == head
{
cout << "Head: " << front << endl;
if(front == NULL)
{
cout << "Head is NULL" << endl;
return;
}
Node *temp = front->next;
cout << "Node *temp = front -> next: " << temp << endl;
front -> next = front -> prev;
cout << "front -> next = front -> prev: " << front->next << endl;
front -> prev = temp;
cout << "front -> prev = temp: " << front->prev << endl;
cout << "-------" << endl;
if(front->prev == NULL)
{
cout << "front->prev: " << front ->prev << endl;
cout << "ITS BEEN REVERSED" << endl;
return;
}
reverse(front->prev);
}
The error is front -> next = front -> prev
The List I'm using for an example is
["Hello", "Human", "test", "Boo"]
With the code above, this is what outputs within the console as I was debugging this.
Reverse Test Below:
--------------
Before Reversed: [Hello, Human, test, Boo]
Head: 0x55a98ae172c0
Node *temp = front -> next: 0x55a98ae17300
front -> next = front -> prev: 0
front -> prev = temp: 0x55a98ae17300
-------
Head: 0x55a98ae17300
Node *temp = front -> next: 0x55a98ae17340
front -> next = front -> prev: 0
front -> prev = temp: 0x55a98ae17340
-------
Head: 0x55a98ae17340
Node *temp = front -> next: 0x55a98ae17380
front -> next = front -> prev: 0
front -> prev = temp: 0x55a98ae17380
-------
Head: 0x55a98ae17380
Node *temp = front -> next: 0
front -> next = front -> prev: 0
front -> prev = temp: 0
-------
front->prev: 0
ITS BEEN REVERSED
---
After Reversed: [Hello]
--------------
I think everything else is pretty okay; I just dont know how to change the
front -> next = front -> prev
It would help you understand what is going on if you draw out the state of your list on each step.
Your list looks like this upon entry to the initial call to reverse():
+-----------+ +-----------+ +----------+ +---------+
| "Hello" [n] -> | "Human" [n] -> | "test" [n] -> | "Boo" [n] -> NULL
NULL <- [p] | <- [p] | <- [p] | <- [p] |
+-----------+ +-----------+ +----------+ +---------+
^
head
front
front is not NULL, so you swap front->next and front-prev, and now the list looks like this:
+-----------+ +-----------+ +----------+ +---------+
| "Hello" [n] -> NULL | "Human" [n] -> | "test" [n] -> | "Boo" [n] -> NULL
+- [p] | <------- [p] | <- [p] | <- [p] |
| +-----------+ +-----------+ +----------+ +---------+
| ^ ^
| head |
| front |
+-------------------------+
front->prev is not NULL, so you execute reverse(front->prev).
front is not NULL, so you swap front->next and front-prev, and now the list looks like this:
+-------------------------------------+
| |
\/ |
+-----------+ +-----------+ | +----------+ +---------+
| "Hello" [n] -> NULL | "Human" [n] -+ | "test" [n] -> | "Boo" [n] -> NULL
+- [p] | +- [p] | <-- [p] | <- [p] |
| +-----------+ | +-----------+ +----------+ +---------+
| ^ | ^ ^ ^
| head | | front |
| +--|-----------------+
+-------------------------+
front->prev is not NULL, so you execute reverse(front->prev).
front is not NULL, so you swap front->next and front-prev, and now the list looks like this:
+-------------------------------------+
| +-------------|----------------+
\/ \/ | |
+-----------+ +-----------+ | +----------+ | +---------+
| "Hello" [n] -> NULL | "Human" [n] -+ | "test" [n] -+ | "Boo" [n] -> NULL
+- [p] | +- [p] | +- [p] | <-- [p] |
| +-----------+ | +-----------+ | +----------+ +---------+
| ^ | ^ | ^ ^ ^
| head | | | | front |
| +--|--------------|--+ |
+-------------------------+ +-------------------+
front->prev is not NULL, so you execute reverse(front->prev).
front is not NULL, so you swap front->next and front-prev, and now the list looks like this:
+-------------------------------------+ +----------------------------------+
| +--------------|--|-------------+ |
\/ \/ | \/ | |
+-----------+ +-----------+ | +----------+ | +---------+ |
| "Hello" [n] -> NULL | "Human" [n] -+ | "test" [n] -+ | "Boo" [n] -+
+- [p] | +- [p] | +- [p] | NULL <- [p] |
| +-----------+ | +-----------+ | +----------+ +---------+
| ^ | ^ | ^ ^ ^
| head | | | | | front
| +--|--------------|--+ |
+-------------------------+ +------------------------+
And finally, front->prev is NULL, so you exit.
The above is a little messy, so let's re-draw it cleaner with the new arrows:
+---------+ +----------+ +-----------+ +-----------+
| "Boo" [n] -> | "test" [n] -> | "Human" [n] -> | "Hello" [n] -> NULL
NULL <- [p] | <- [p] | <- [p] | <- [p] |
+---------+ +----------+ +-----------+ +-----------+
^
head
Look, the nodes were reversed correctly!
However, notice anything wrong? When you try to printout the new list, you only see Hello, because that is where head is still pointing! You need to update head to point at Boo instead:
+---------+ +----------+ +-----------+ +-----------+
| "Boo" [n] -> | "test" [n] -> | "Human" [n] -> | "Hello" [n] -> NULL
NULL <- [p] | <- [p] | <- [p] | <- [p] |
+---------+ +----------+ +-----------+ +-----------+
^
head
If your List class is maintaining both head and tail pointers, you can simply swap them:
void List::reverse(Node *front)
{
...
}
void List::reverse()
{
reverse(head);
Node *temp = head;
tail = head;
head = temp;
}
But, if you don't have a tail pointer, then you will need to make reverse() return the new head pointer after its work is finished, eg:
Node* List::reverse(Node *front)
{
...
if(front == NULL)
{
...
return NULL;
}
...
if(front->prev == NULL)
{
...
return front;
}
return reverse(front->prev);
}
void List::reverse()
{
head = reverse(head);
}
Related
This is the solution to printing elements of a linked list.
Why isn't it Node *current = new Node; and then current = head;?
void printLinkedList(Node* head)
{
Node *current = head;
while(current!=NULL){
cout << current -> data << endl;
current = current -> next;
}
}
This is a great spot to draw pictures!
Imagine we have a linked list pointed at by head:
head
|
v
+------+ +-----+ +-----+ +-----+
| i'm | -> | the | -> | bad | -> | guy | -> null
+------+ +-----+ +-----+ +-----+
If we use the line of code
Node *current = new Node;
then memory looks like this:
head current
| |
v v
+------+ +-----+ +-----+ +-----+ +------+
| i'm | -> | the | -> | bad | -> | guy | -> null | duh! | -> ?
+------+ +-----+ +-----+ +-----+ +------+
The goal of the function is to print the existing list pointed at by head, but here we've got a pointer to a new linked list cell that isn't a part of the existing list. As a result, we've committed two Programming Sins:
We've allocated memory for an object we don't need.
We've broken the contract we made with the client.
On the other hand, if we write
Node *current = head;
then memory looks like this:
head
|
v
+------+ +-----+ +-----+ +-----+
| i'm | -> | the | -> | bad | -> | guy | -> null
+------+ +-----+ +-----+ +-----+
^
|
current
Here, current is now pointing into the existing list, so we can walk the list to find what we need. No new nodes need to be created here, so we don't create any new nodes.
Generally speaking, in C++ you should avoid using new unless you really truly want to create a new linked list cell. In this case, we don't want to do that, which is why we create current and have it point to an existing linked list cell.
Hope this helps!
Because the node already exists.
new would create a new one.
You don't need or want to create a new one.
You just want to "use" a pointer to an existing node.
Here's it's just the function argument being copied into a variable with a different name. It's actually completely unnecessary.
Below is the code,
#include<bits/stdc++.h>
using namespace std;
class node{
public:
int data;
node * next;
node(){
this->next = NULL;
}
node(int data){
this->data = data;
this->next = NULL;
}
};
class linkedList{
public:
node * head;
node * tail;
linkedList(){
this->head = NULL;
}
void getTail(node *& t){
t = head;
while(t->next != NULL){
t = t->next;
}
}
void insertAtEnd(int data){
node * newNode = new node(data);
if(head == NULL){
head = newNode;
return;
}
node * temp = head;
while(temp->next != NULL){
temp = temp->next;
}
temp->next = newNode;
}
void print(){
node * temp = head;
while(temp != NULL){
printf("%d->", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
void swap(node *& a, node *& b){
node * temp = a;
a = b;
b = temp;
}
void swapNode(node ** start, node ** end){
swap(*start, *end);
swap(((*start)->next), (*end)->next);
}
};
int main(){
///////////////////////////////////////
linkedList * ll1 = new linkedList(); //
ll1->insertAtEnd(6); //
ll1->insertAtEnd(2); //
ll1->insertAtEnd(1); //
ll1->insertAtEnd(3); //
ll1->print(); /////////////////////
ll1->swapNode(&ll1->head, &ll1->head->next->next->next);// ----> This is working
//////////////////////////////////////////////////////////
linkedList * ll2 = new linkedList();
ll2->insertAtEnd(6);
ll2->insertAtEnd(2);
ll2->insertAtEnd(1);
ll2->insertAtEnd(3);
ll2->print();
node * tail;
ll2->getTail(tail);
ll2->swapNode(&ll2->head, &tail); // This is not working // Going into a infinte loop
ll2->print();
}
When the tail node is stored in some other variable there seems be a forever loop.
While the code is working when the tail node is given by traversing to last using next pointer.
So, below is the output for the first example of the linked list, that is,
6->2->1->3->NULL
1->2->6->3->NULL
For the linked list #2, the output goes like this
6->2->1->3->NULL
3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2->1->3->2-> there's no ending
When you swap two nodes a and b you must also fix the pointers reaching a and reaching b. For example
x1 -> x2 -> a -> x3 -> x4 -> b -> x5 -> x6 -> NULL
to swap nodes a and b to don't need to fix only a.next and b.next but also x2.next and x4.next.
That part of code is missing from your implementation.
Pictures often help when dealing with linked lists. Here is the starting list.
head
|
V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Now we'll call ll1->swapNode(&ll1->head, &ll1->head->next->next->next), which adds two variables to the picture.
start
|
V
head end
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Next is the call to swap(*start, *end), which swaps the head pointer and the next pointer in the node whose data is 1. So the head pointer will point to the 3 node, while the 1 node will point to the 6 node.
start
|
V
end head
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | | 3 | NULL |
------------ ------------ ------------ ------------
^ |
| |
L---------------------------------------
Finally, the call to swap(((*start)->next), (*end)->next), which swaps the next pointers in the nodes whose data is 3 and 6. So the 3 node will point to the 2 node, while the 6 node's pointer will become null.
start
|
V
head
|
V
------------
| 3 | next |
------------ end
| |
V V
------------ ------------ ------------
| 6 | NULL | | 2 | next | -> | 1 | next |
------------ ------------ ------------
^ |
| |
L---------------------------------------
Hooray! The nodes were swapped.
Now look at the call ll2->swapNode(&ll2->head, &tail);, which adds three variables to the initial picture.
start end
| |
V V
head tail
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Next is the call to swap(*start, *end), which swaps the head pointer and tail (not the tail pointer of the list, but the variable local to main()).
end start
| |
V V
tail head
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
There's already a noticeable difference from the first case in that the nodes of the list are unchanged. Hmm... well, let's keep going and call swap(((*start)->next), (*end)->next), which swaps the next pointers in the nodes whose data is 3 and 6. So the 3 node will point to the 2 node, while the 6 node's pointer will become null. This is what happened in the first case, so will it work out?
end start
| |
V V
tail head
| |
V V
------------ ------------ ------------ ------------
| 6 | NULL | | 2 | next | -> | 1 | next | -> | 3 | next |
------------ ------------ ------------ ------------
^ |
| |
L---------------------------------------
PROBLEM! We are left with a cycle! What happened to the step where the 1 node was changed to point to the 6 node? That's right, we changed tail instead, and the result was disaster.
I would recommend three main things to move your code to more robust ground. (That is, don't try to fix your current error yet. Do these first as they'll change the playing field.)
Get rid of #include<bits/stdc++.h>; instead include the headers you need. For this example, you need just #include <cstdio>.
Make node a private implementation detail of linkedList. Robustness tends to increase along with encapsulation. If no one knows the data structures used by linkedList, you limit what other code can do. With fewer possibilities to account for, it's easier to make sure you accounted for all of them.
Don't declare a field (linkedList::tail) that is never used. Someone will want to use that field at some point without realizing it contains garbage.
(There are other improvements to be made. These three are the most important in my view.)
It is my first time encountering this type of pointer. It points then points again, what does this imply? I encountered this on a doubly linked list program.
listPointer->leftPointer->rightPointer = newNodePtr;
It points then points again, what does this imply?
The code implies that there's a kind of
struct Node {
Node* leftPointer;
Node* rightPointer;
};
which is used recursively to build a tree.
listPointer is declared as
Node* listPointer;
newNodePtr is declared as
Node* newNodePtr;
The tricky part will be to manage that all of these pointers have properly allocated memory to point to when
listPointer->leftPointer->rightPointer = newNodePtr;
is executed at runtime.
Regarding a doubly linked list as you tagged your question so:
I'd prefer a different naming of the struct and its members
struct DoubleLinkedListItem {
DoubleLinkedListItem* previous;
DoubleLinkedListItem* next;
};
for clarity.
It probably is part of inserting a new element. If you do it correctly, you only need to know about the element after where you wish to insert it, and the element you are inserting. The other element can be gotten by following the links.
Starting from
listPtr
|
v
---------- ----------
| |->| |
| Node L | | Node |
| |<-| |
---------- ----------
newNodePtr
|
v
----------
| |
| Node N |
| |
----------
First do
listPointer->leftPointer->rightPointer = newNodePtr;
So that the preceding element points to the new element
newNodePtr listPtr
| |
v v
---------- ---------- ----------
| |--->| | | |
| Node L | | Node N | | Node |
| |<-| | | |-| |
---------- | ---------- | ----------
| |
|-------------|
Then do
newNodePtr->leftPointer = listPointer->leftPointer;
So that the new element points to the preceding element
newNodePtr listPtr
| |
v v
---------- ---------- ----------
| |--->| | | |
| Node L |<---| Node N | | Node |
| |<-| | | |-| |
---------- | ---------- | ----------
| |
|-------------|
Then do
listPointer->leftPointer = newNodePtr;
So that the following element points back to the new element
newNodePtr listPtr
| |
v v
---------- ---------- ----------
| |->| | | |
| Node L | | Node N | | Node |
| |<-| |<-| |
---------- ---------- ----------
Then finally
newNodePtr->rightPointer = listPointer;
So that the new element points at the following element
newNodePtr listPtr
| |
v v
---------- ---------- ----------
| |->| |->| |
| Node L | | Node N | | Node |
| |<-| |<-| |
---------- ---------- ----------
Maybe used to remove listPointer from the link list like mentioned below:
listPointer->leftPointer->rightPointer = newNodePtr;
newNodePtr->rightPointer = listPointer
void add_node(int n) {
node *tmp = new node;
tmp->data = n;
tmp->next = NULL;
if(head == NULL) {
head = tmp;
tail = tmp;
}
else {
tail->next = tmp;
tail = tail->next;
}
}
in the else statement, why cant i directly assign tail=tmp; instead of tail=tail->next; is there anything Im missing? Thanks in advance!
Lets take a small simple list:
+-------+ +-------+ +-------+
| node1 | --> | node2 | --> | node3 |
+-------+ +-------+ +-------+
^ ^
| |
head tail
Now to add a new node at the end of the list, we first add the new node to the list, by making the tails next pointer point to the new node:
+-------+ +-------+ +-------+ +-------+
| node1 | --> | node2 | --> | node3 | --> | node4 |
+-------+ +-------+ +-------+ +-------+
^ ^
| |
head tail
This is what the assignment tail->next = tmp does.
Then we update tail to point to the new tail:
+-------+ +-------+ +-------+ +-------+
| node1 | --> | node2 | --> | node3 | --> | node4 |
+-------+ +-------+ +-------+ +-------+
^ ^
| |
head tail
This is what the assignment tail = tail->next does. Of course, this could also be done by doing tail = tmp.
The order of the two assignments is important.
Now if you do it the opposite way, by assigning tail = tail->next first, when we have
+-------+ +-------+ +-------+
| node1 | --> | node2 | --> | node3 |
+-------+ +-------+ +-------+
^
|
head
You no longer have a tail! You don't know where the list ends, and where to insert the new node unless you loop over the whole list to find a node whose next pointer is a null pointer.
You can do that, but it will not produce the correct result. Trace it with a debugger if you can't figure out why from looking at the code.
Structure looked like:
class DList{
private:
struct Elem {
Elem *prev, *next;
};
Elem *_head, *_tail;
}
Now I have two existing node: cur and cur->next, and i wanna insert a new node ins between them. Here's what i did:
cur->next = ins;//step-1-1 cur
ins->prev = cur;//step-1-2 cur
ins->next = cur->next;//step-2-1 cur->next
cur->next->prev = ins;//step-2-2 cur->next
The problem is in further traverse loop of my program my pointer cannot reach _tail anymore. Did i mishook something in my insertion part? (The loop works perfectly once i comment the insertion at middle codes above)
Yep, there's a miswiring here. Let's draw pictures!
Imagine things look like this, initially:
curr
|
v
+----------+ +----------+
| next | ----------------------> | next | --> ...
+----------+ +----------+
... <-- | prev | <---------------------- | prev | <-- ...
+----------+ +----------+
+----------+
| next |
+----------+
| prev |
+----------+
^
|
ins
First, you execute cur->next = ins;, which does this:
curr
|
v
+----------+ +----------+
| next | -----------+ | next | --> ...
+----------+ | +----------+
... <-- | prev | <----------+----------- | prev | <-- ...
+----------+ v +----------+
+----------+
| next |
+----------+
| prev |
+----------+
^
|
ins
Notice that we no longer have a pointer to the element that was originally after curr - oops! That'll be a problem later.
Now, we do ins->prev = curr;, which looks like this:
curr
|
v
+----------+ +----------+
| next | -----------+ | next | --> ...
+----------+ | +----------+
... <-- | prev | <----------+----------- | prev | <-- ...
+----------+ v +----------+
^ +----------+
| | next |
| +----------+
+---------- | prev |
+----------+
^
|
ins
Now, we write ins->next = curr->next;. But oops! Notice that curr->next points to ins, so we just added a cycle in here:
curr
|
v
+----------+ +----------+
| next | -----------+ | next | --> ...
+----------+ | +----------+
... <-- | prev | <----------+----------- | prev | <-- ...
+----------+ v +----------+
^ +----------+
| | next | --+
| +----------+ |
+---------- | prev | <-+
+----------+
^
|
ins
And finally, you write cur->next->prev = ins; But oops! curr->next is still prev, so we get another cycle:
curr
|
v
+----------+ +----------+
| next | -----------+ | next | --> ...
+----------+ | +----------+
... <-- | prev | <----------+----------- | prev | <-- ...
+----------+ v +----------+
+----------+
+-> | next | --+
| +----------+ |
+-- | prev | <-+
+----------+
^
|
ins
The issue here is that you lose track of the cell pointed at by curr->next after the first assignment, so you lose the ability to look in the right place.
What if you start off by writing something like this?
DList* next = curr->next;
and then use next instead of curr->next in some of these contexts?
Key is to not lose the values which are going to be used later on. Your first line of code cur->next = ins; makes you lose the original value (cur->next); thereafter you really don't know who will be next to ins.
Use this logic to insert a new node in middle. Lets say initially,
cur - cur->next
|
[ins]
Do,
ins->next = cur->next;
(cur) (ins) - (cur->next) {Assume - for next, and = for next and prev}
cur->next = ins;
(cur) - (ins) - (cur->next)
ins->prev = cur;
cur = ins - (cur->next)
ins->next->prev = ins;
cur = ins = (cur->next)