Below is the code for swapping nodes without changing data. I wonder whether swapping the next pointers of nodes required? Will swapping the current nodes doesn't swap the next pointers? Why?
void swapNodes(Node** head_ref, int x, int y)
{
// Nothing to do if x and y are same
if (x == y)
return;
Node **a = NULL, **b = NULL;
// search for x and y in the linked list
// and store therir pointer in a and b
while (*head_ref) {
if ((*head_ref)->data == x) {
a = head_ref;
}
else if ((*head_ref)->data == y) {
b = head_ref;
}
head_ref = &((*head_ref)->next);
}
// if we have found both a and b
// in the linked list swap current
// pointer and next pointer of these
if (a && b) {
swap(*a, *b);
swap(((*a)->next), ((*b)->next));
}
}
void swap(Node*& a, Node*& b)
{
Node* temp = a;
a = b;
b = temp;
}
Thank You.
whether swapping the next pointers of nodes required?
Yes it is required because the original nodes take place in different positions of the list.
Will swapping the current nodes doesn't swap the next pointers?
Yes, swapping current nodes doesn't swap the next pointers. Swapping current nodes means only swapping only pointers that point to the current nodes.
Consider for example the list
| A |next B| -> | B |next C| -> | C |next D| -> | D |next nullptr|
and let's assume that you need to swap nodes B and D. Then you'll get
---------------------
| |
| A |next D| ... | B |next C| -> | C |next B| ... | D |next nullptr|
| |
----------------------------------------------
So after the first swapping the node A points to node D but node D "points" to nullptr. Nodes B and C will be lost if not to swap their data members next.
Thus you need also to swap their data members next
--------------------------
| |
| A |next D| ... | B |next nullptr| | C |next B| ... | D |next C|
| |
---------------------------------------------------
and as result you'll get
| A |next D| -> | D |next C| -> | C |next B| -> | B |next nullptr|
Swapping the current nodes won't be enough.
When swapping a and b, their address is changed so their place in the list will be replaced
But you do not change the inner fields of each node.
Nodes Illustraion:
a - b - c - d
Let's take node a and c.
a->next == &b (True)
c->next == &d (True)
If we swap the nodes like this:
c - b - a - d
The address of node c and node a will change, but the list would look the same since their ->next values will not change
If we also swap the ->next values, the list will be really swapped
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.)
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.
The structure of the node is as followed:
struct Node {
int value;
Node *next;
};
// construct a new node
Node * cons( int x, Node* p) {
return new Node{x, p};
}
Now if I were to type in my main body:
Node *p;
p = cons(3,nullptr);
p = cons(2,p);
p = cons(1,p);
p = cons(4,p);
Is there a better value to start off with for my first node instead of nullptr?From what I understand this is 4 Nodes in order. Is that correct? Node value 3 is the first on the list. So this function would search my linklist in order as in goes to view node value 3, 2,1,4,then empty node.
//search linklist in order for value x
bool searchInOrder(int x, Node *p){
while(p->next != nullptr){
if(x == p->value)
return true;
p = p->next;
}
return false;
}
Question:
Is there a better value to start off with for my first node instead of nullptr?
Answer:
That is the best way to create the first node of a linked list.
Question:
From what I understand this is 4 Nodes in order. Is that correct? Node value 3 is the first on the list. So this function would search my linklist in order as in goes to view node value 3, 2,1,4,then empty node.
Answer:
Yes there are 4 Nodes. However, the Node with value 3 is not the first Node in the list.
After
p = cons(3,nullptr);
The list is:
+---+ +-----+
+ p + ---> | 3 | ---> NULL
+---+ +-----+
After
p = cons(2,p);
The list is:
+---+ +-----+ +-----+
| p | ---> | 2 | ---> | 3 | ---> NULL
+---+ +-----+ +-----+
After
p = cons(4,p);
The list is:
+---+ +-----+ +-----+ +-----+ +-----+
| p | ---> | 4 | ---> | 1 |---> | 2 | ---> | 3 | ---> NULL
+---+ +-----+ +-----+ +-----+ +-----+
class CSensor
{
public:
CSensor(int nVal1,char* pVal2,unsigned int nVal3);
CSensor(const CSensor& refMessage);
const CSensor& operator=(const CSensor& refMessage);
~CSensor(void);
private:
int m_nVal1;
char* m_pVal2;
unsigned int m_nVal3;
};
// vector erase
std::vector<CSensor> SensorList;
CSensor obj1(1,"Test1",10);
SensorList.push_back(obj1);
CSensor obj2(2,"Test2",11);
SensorList.push_back(obj2);
CSensor obj3(3,"Test3",12);
SensorList.push_back(obj3);
SensorList.erase (SensorList.begin()+1);
// map erase
std::map<int ,CSensor> ListSensor;
CSensor obj11(1,"Test1",10);
CSensor obj12(2,"Test2",11);
CSensor obj13(3,"Test3",12);
ListSensor.insert(std::pair<int,CSensor>(1,obj11));
ListSensor.insert(std::pair<int,CSensor>(2,obj12));
ListSensor.insert(std::pair<int,CSensor>(3,obj13));
ListSensor.erase(2);
I debugged both the case.
In both the cases i am deleting the second element.In case of vector
it is copying 3 element to 2nd poition and than it it deleting the 3 rd location.
So when u say
List.erase (List.begin()+1);
it is calling assignment operator(CSensor=) and then calling destructor.
In case of map when i do
ListSensor.erase(2);
it only calls the destructor.
I have gone through STL vector vs map erase.
It talks Iterator,couldn't explain the behavior.
My question is why erase behaves differently for these two STL container??
This is not a behaviour of .erase per se, but a consequence of how each respective container works.
Deleting from a vector
When you delete from a vector (List is a really poor name for a vector, btw), its contents must be shuffled along to fill the gap because the elements of a vector are always stored contiguously in memory.
This is generally done by copying (or moving) elements then chopping off the remainder:
Vector elements in memory:
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | e | f | g | h | i |
+---+---+---+---+---+---+---+---+---+
Erase 'e':
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | | f | g | h | i |
+---+---+---+---+---+---+---+---+---+
Fill the gap by copying/moving across:
<--
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | f | f | g | h | i |
+---+---+---+---+---+---+---+---+---+
<--
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | f | g | g | h | i |
+---+---+---+---+---+---+---+---+---+
<--
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | f | g | h | h | i |
+---+---+---+---+---+---+---+---+---+
<--
+---+---+---+---+---+---+---+---+---+
| a | b | c | d | f | g | h | i | i |
+---+---+---+---+---+---+---+---+---+
Resize the container:
+---+---+---+---+---+---+---+---+
| a | b | c | d | f | g | h | i |
+---+---+---+---+---+---+---+---+
Deleting from a map
This is not the case for a map, whose contents are not necessarily stored contiguously in memory (in fact, the complexity requirements mean it's almost always a tree structure filled with pointers to discontiguous data).
This is one reason to have different containers!
The vector has to copy the elements following the eased element to cover the "hole" that it left. Otherwise the elements wouldn't be contiguous anymore.
The map holds its elements in a tree structure and just has to adjust some pointers to rebalance the tree.
To the vector's defense: Copying a couple of elements might be cheaper than allocating tree nodes separately and keeping the tree balanced. There are always tradeoffs!