I am just wondering, in a linked list what is the difference between the two:
node1 = node2
and
node1->next = node2
The first line makes me a bit confused.
I'm assuming from context here that node1 and node2 are pointers to nodes (for example, they might have a type like Node *). If that's not the case, please let me know!
If node1 and node2 are pointers, remember that there is a difference between pointers to nodes and actual, honest-to-goodness node objects. The pointers are just a way of saying "look over there and you'll find a node." The nodes themselves are actual objects containing data and links to other nodes.
For example, if you have two node pointers node1 and node2 that point to nodes, perhaps it looks like this:
+----------+ +----------+
| | -----------> | data! |
+----------+ +----------+
node1 | next! | -----> ...
+----------+
+----------+ +----------+
| | -----------> | data! |
+----------+ +----------+
node2 | next! | -----> ...
+----------+
If you write node1->next = node2, you're saying "follow the pointer named node1 to see what node it points at, find the next pointer in that node, and change it to point to wherever node2 points." That makes things look like this:
+----------+ +----------+
| | -----------> | data! |
+----------+ +----------+
node1 | next! | -----> ...
+----------+
|
|
v
+----------+ +----------+
| | -----------> | data! |
+----------+ +----------+
node2 | next! | -----> ...
+----------+
Writing node1 = node2 means "change node1 to point to whatever node node2 is pointing at." That looks like this:
+----------+ +----------+
| | ------+ | data! |
+----------+ | +----------+
node1 | | next! | -----> ...
| +----------+
| |
| |
| v
+----------+ | +----------+
| | ------+----> | data! |
+----------+ +----------+
node2 | next! | -----> ...
+----------+
Fundamentally, there's no deep difference between these operations. They both change where some pointer is pointing. The difference is whether you're changing the next pointer inside of a node object or whether you're changing which node node1 points at.
Whenever you have a question about what a line of code does that involves pointers or linked lists or the like, I highly recommend drawing pictures like the ones shown here. Building a visual intuition for what links you're creating and what links you're breaking is one of the best ways to better understand how code works. Plus, it's great for debugging!
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.
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)
I am currently about to implement a multi-way tree in c++, but I am still not sure about what exactly they are. I have read a few documentations, but I am still confused because of the lack of pictures or visualization provided.
Lets say I want a 3 way tree, according to online web notes it means each node can have at most 3-1 = 2 elements and each node can have at most 3 children. Below I have drawn some trees that I am not sure if they are 3-way trees, can someone please verify I am understanding this correctly? Thank you!
Also, if I have a 2 way tree, does that mean I have a binary tree as well? O.o?
My understanding of a multi-way tree is the number of subtrees that can be traversed from a single node.
+---+
| D |
+---+
^
|
|
+---+ +------+ +---+
| A | <-- | Root | --> | B |
+---+ +------+ +---+
|
|
V
+---+
| C |
+---+
The diagram above shows a multi-way tree because the root has more than 1 child.
Usually 2 children per node (except leaf nodes) indicates binary trees.
There are many different kinds of binary trees.
See also B-Tree and B*Trees.
Edit 1:
Another view:
+------------------------+
| Root +
+------------------------+
| | | |
V V V V
+---+ +---+ +---+ +---+
| A | | B | | C | | D |
+---+ +---+ +---+ +---+
The C++ program I'm working on is designed to create a custom doubly Linked List class in C++ by utilizing an indefinite number of node struct pointers with a data value and two pointers inside. That's the premise of this program. Can't use the STL LinkedList class, have to make my own.
This being said, how do you create a class member variable that doesn't have a specific amount of pointers to nodes? I don't want to initialize fifty nodes when declaring the class, but then only use ten of them. and at the same time, I don't want to initialize only 5 and then have to use more than that 5. Is there a way to dynamically add node pointers to a linked list class in C++, when node pointers are considered a member variable?
Am I even going about this the right way? And if so, how will I go about doing this?
Picture this:
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
A node with two link fields and a data field.
(Linked lists are always easier to understand when you draw them.)
We could have two of them:
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
^ |
| V
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
The Previous Node field of the 2nd node points to the first. The first node has no predecessors, so it's Previous Node field is empty.
Similarly, the Next Node field of the first node points to the 2nd node. The 2nd node has no successor, so the Next Node field of the 2nd node is empty.
This is what I believe the requirements want: a doubly linked list using pointers.
Edit 1: Three nodes
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
^ |
| V
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
^ |
| V
+----------+-------+------+
| Previous | Data | Next |
| Node | Field | Node |
+----------+-------+------+
As you can see, to visit (traverse) the nodes in a forward manner, you follow the link field of one node to get to the next node. Similarly, to go in a backwards manner, you follow the Previous Node link to get to a node's predecessor.
A nice issue about the links is that you only need to change the link fields in order to insert a node in the middle of the list. Drawing of the insertion processes is left as an exercise for the reader.
Edit 2: The Container Class
The Linked List is a container of nodes. For simple implementations, the Container class should not be a Node.
The Container has-a pointer to the first node and optionally a pointer to the last node:
+------+-------+
| Last | First |
| Node | Node |
+------+-------+
| |
| +---------------+
| |
| V
| +----------+-------+------+
| | Previous | Data | Next |
| | Node | Field | Node |
| +----------+-------+------+
| ^ |
| | V
| +----------+-------+------+
| | Previous | Data | Next |
| | Node | Field | Node |
| +----------+-------+------+
| ^ |
| | V
| +----------+-------+------+
| | Previous | Data | Next |
+->| Node | Field | Node |
+----------+-------+------+
By using a container class, you don't need to worry about an using an empty node as the first node. Here, we use a simple pointer to point to the first node. Additionally, there is a pointer to the last node.
The pointer to the last node speeds up the operation of appending nodes to the list. Without this pointer, you would have to traverse all the nodes to find the last one, which takes a lot of time.