Just want to know if there are any flaws/inconsistencies/memory leaks in this implementation of deleting a linked list:
// Function to delete the entire linked list
void deleteList(Node** head) {
Node* current = *head;
Node* next;
while (current != 0) {
next = current->next;
delete current;
current = next;
}
*head = 0;
}
Edit:
struct Node {
int data;
Node* next;
Node(int data) : data(data){}
};
It would be more C++ if you passed head pointer by reference, not by pointer:
void deleteList(Node * & head)
{
// (...)
head = nullptr; // NULL in C++ pre-11
}
Also, to keep code a little more tidy, you can move declaration of next inside the loop:
while (current != 0)
{
Node * next = current->next;
delete current;
current = next;
}
My only worries about memory leak would concern properly freeing node's contents, but since you store a simple int, there shouldn't be any problems there.
Assuming, that your list have valid pointers to nodes and that head pointer is valid too, everything else seems fine.
Related
I am writing my own implementation of a single linked list. I wrote a popBack() method, but it seems to me that it is too overloaded with conditional operators, which should affect performance (and in general, it looks so-so). Is it possible to optimize it somehow so that it does not look so terrible?
void popBack() {
if (head == nullptr) {
return;
}
if (head->next == nullptr) {
delete head;
head = nullptr;
}
else {
Node* prev = nullptr;
Node* curr = head;
while(curr->next != nullptr) {
prev = curr;
curr = curr->next;
}
if(prev != nullptr) {
delete curr;
prev->next = nullptr;
}
}
size--;
}
The 2nd if block is not necessary. Also, the else block can be simplified if you use a Node** pointer to point at the Node* variable which needs to be null'ed when the Node object it is pointing at is delete'd, eg:
void popBack() {
if (!head) return;
Node *curr = head, **prev = &head;
while (curr->next) {
prev = &(curr->next);
curr = *prev;
}
*prev = nullptr;
delete curr;
--size;
}
Of course, if you really want to optimize the popping, you should implement a double-linked list instead, and add a tail member to your list class to point at the last Node in the list, then you won't need a while loop to hunt for the trailing Nodes in the list, eg:
void popBack() {
if (!tail) return;
Node *curr = tail;
tail = tail->previous;
Node **n = (tail) ? &(tail->next) : &head;
*n = nullptr;
delete curr;
--size;
}
One technique that can clean the code up a lot is to ensure the list always includes one node that you basically treat as empty. You never use the value it contains, but it eliminates many of the corner cases, so most of the code never had to deal with them.
Using this, pop_back can end up something like this:
void pop_back() {
if (head->next == nullptr)
return; // empty list--nothing to do.
// traverse to just before the sentinel node:
node* loc;
for (loc = head; loc->next->next != nullptr; loc = loc->next)
;
// at this point, `loc` points to the last "real" node in the list
// delete the old sentinel node
// then turn this into the sentinel node by setting its next pointer
// to a null pointer:
delete loc->next;
loc->next = nullptr;
}
Of course, to make this work, we have to assure the list always contains the sentinel node:
LinkedList() {
// node with its next pointer set to a null pointer:
head = new node(nullptr);
}
...and (of course) push_back has to be aware of the sentinel as well:
void push_back(T t) {
if (head->next == nullptr) {
head = new node(t, head);
return;
}
node* loc;
for (loc = head; loc->next->next != nullptr; loc = loc->next)
;
loc->next = new node(t, loc->next);
}
We can also carry this a step further by saving the location of the sentinel node. Although we still need to traverse the list to do a pop_back, we can avoid it when we want to do a push_back (we put the new value into the current sentinel node, then allocate a new sentinel node, and update our tail pointer to its location.
This general approach is, however, restricted to types that we don't mind creating a default-constructed item that we don't (currently) use. In my experience that's true a lot more often than not, but there are exceptions for which this design isn't suitable.
In place of *head_ref = temp->next;, why can't I assign it as *head_ref = *head_ref->next?
Why should I use temp? Aren't they pointing to the same place?
class Node{
public:
int data;
Node* next;
};
void deleteNode(Node** head_ref, int key){
Node* temp = *head_ref;
Node* prev = NULL;
if(temp!=NULL && temp->data==key){
*head_ref = temp->next;
delete temp;
return;
}
else{
while(temp!=NULL && *head_ref->data!=key){
prev = temp;
temp = temp->next;
}
}
Your code does not compile, *head_ref->data should be (*head_ref)->data.
The reason why you should use temp is that you want to modify *head_ref only if the element you want to delete is the head element. If you delete any other element of the list, the head pointer must stay the same.
But your code is wrong anyway. You're doing things in the wrong order. You must first find the element you want to delete, and then handle the deletion.
Your code handles the deletion first and then finds the element to delete which is absurd.
You want this:
void deleteNode(Node** head_ref, int key) {
Node* current = *head_ref;
Node* previous = NULL;
// find element to delete
while (current && current->data != key)
{
previous = current;
current = current->next;
}
// if current is NULL here then the element has not been found
if (current != NULL)
{
// element found,
// current points to element found
// previous points to previous element or NULL if current is head
if (previous == NULL)
{
// deleting head element -> we need to update head_ref
*head_ref = current->next;
}
else
{
// deleting any other element -> update next pointer of previous element
previous->next = current->next;
}
delete current;
}
}
That being said, this is rather C code than C++ code. You should use standard containers rather than making your own, or at least use C++ idioms such as constructors.
I'm trying to test if a linked list is palindrome. To do this, I'm just going to reverse the linked list, and then check each node one by one in a loop. However, I don't know how to pass the original linked list by value. I think right now I'm passing the linked list by its reference, which modified the original structure after I reverse it.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head) return NULL;
ListNode* current = head;
ListNode* pre = NULL;
ListNode* nextTemp = NULL;
while(current!=NULL) {
nextTemp = current->next;
current->next = pre;
pre = current;
current = nextTemp;
}
head = pre;
return head;
}
bool isPalindrome(ListNode* head) {
ListNode* original = head;
ListNode* reverse = reverseList(head);// is there a way that I can pass it as a copy?
while(original!=NULL){
if(reverse->val == original->val){
reverse=reverse->next;
original=original->next;
}else{
return false;
}
}
return true;
}
};
When you do this:
ListNode* original = head;
Your just copying the pointer which means you have two pointer variable pointing to the same area of memory so changing one will change the other. Could you not just de-reference the pointer and make a copy of that. You could then get a pointer to that new variable if need be.
ListNode original = *head;
ListNode* original2 = &original;
You can then modify original2 without changing the head.
While in the process of implementing a Linked List in C++, I ran into an issue with my function for inserting a new element at the tail end of the list.
struct LinkedNode
{
int data;
LinkedNode* next;
};
class LinkedList
{
private:
LinkedNode* head;
LinkedNode* tail;
int size;
public:
LinkedList();
void insert_back(int element);
int at(int index);
int length();
};
Originally, my insert_back function was as follows:
void LinkedList::insert_back(int element)
{
LinkedNode node = {element, 0};
if(size == 0)
{
head = &node;
tail = head;
}
else
{
tail->next = &node;
tail = tail->next;
}
++size;
}
However, when I iterated through a four-element list, printing out each element as such:
LinkedList myList;
myList.insert_back(5);
myList.insert_back(6);
myList.insert_back(7);
myList.insert_back(8);
for(int i = 0; i < myList.length(); ++i)
{
cout << myList.at(i) << endl;
}
the last element in the list printed first and then I would receive three garbage values such as in:
8
-403642776
-403642776
-403642776
I fixed this error by changing the way I create a new node in insert_back. This time, I used the new keyword.
void LinkedList::insert_back(int element)
{
LinkedNode* node = new LinkedNode;
node->data = element;
node->next = 0;
if(size == 0)
{
head = node;
tail = head;
}
else
{
tail->next = node;
tail = tail->next;
}
++size;
}
This fixed the error, but I don't really understand why. For whatever reason, in my old code, head and tail were always pointing at the same address, but they are no longer when I use new. Why could this be?
The issue is that the code LinkedNode node = {element, 0}; ... head = &node; of your first version stored the address of a local variable, which becomes invalid as soon as the insert-function finishes. This leads to undefined behaviour once you access this pointer later (e.g. when traversing your list).
In the second version, with LinkedNode* node = new LinkedNode;, you allocate memory dynamically, and such an object is valid until you explicitly delete it. Hence, the pointer remains valid and you may access it later on.
I was writing a simple function to insert at the end of a linked list on C++, but finally it only shows the first data. I can't figure what's wrong. This is the function:
void InsertAtEnd (node* &firstNode, string name){
node* temp=firstNode;
while(temp!=NULL) temp=temp->next;
temp = new node;
temp->data=name;
temp->next=NULL;
if(firstNode==NULL) firstNode=temp;
}
What you wrote is:
if firstNode is null, it's replaced with the single node temp which
has no next node (and nobody's next is temp)
Else, if firstNode is not null, nothing happens, except that the temp
node is allocated and leaked.
Below is a more correct code:
void insertAtEnd(node* &first, string name) {
// create node
node* temp = new node;
temp->data = name;
temp->next = NULL;
if(!first) { // empty list becomes the new node
first = temp;
return;
} else { // find last and link the new node
node* last = first;
while(last->next) last=last->next;
last->next = temp;
}
}
Also, I would suggest adding a constructor to node:
struct node {
std::string data;
node* next;
node(const std::string & val, node* n = 0) : data(val), next(n) {}
node(node* n = 0) : next(n) {}
};
Which enables you to create the temp node like this:
node* temp = new node(name);
You've made two fundamental mistakes:
As you scroll through the list, you roll off the last element and start constructing in the void behind it. Finding the first NULL past the last element is useless. You must find the last element itself (one that has its 'next' equal NULL). Iterate over temp->next, not temp.
If you want to append the element at the end, you must overwrite the last pointer's NULL with its address. Instead, you write the new element at the beginning of the list.
void InsertAtEnd (node* &firstNode, string name)
{
node* newnode = new node;
newnode->data=name;
newnode->next=NULL;
if(firstNode == NULL)
{
firstNode=newnode;
}
else
{
node* last=firstNode;
while(last->next != NULL) last=last->next;
last->next = newnode;
}
}
Note, this gets a bit neater if you make sure never to feed NULL but have all lists always initialized with at least one element. Also, inserting at the beginning of list is much easier than appending at the end: newnode->next=firstNode; firstNode=newnode.
The last element in your list never has it's next pointer set to the new element in the list.
The problem is that you are replacing the head of the linked list with the new element, and in the process losing the reference to the actual list.
To insert at the end, you want to change the while condition to:
while(temp->next != null)
After the loop, temp will point to the last element in the list. Then create a new node:
node* newNode = new node;
newNode->data = name;
newNode->next = NULL;
Then change temps next to this new node:
temp->next = newNode;
You also do not need to pass firstNode as a reference, unless you want NULL to be treated as a linked list with length 0. In that case, you will need to significantly modify your method so it can handle the case where firstNode is NULL separately, as in that case you cannot evaluate firstNode->next without a segmentation fault.
If you don't want to use reference pointer, you could use pointer to pointer. My complete code goes like below:
void insertAtEnd(struct node **p,int new_data)
{
struct node *new_node=(struct node *)malloc(sizeof(struct node));
new_node->data=new_data;
new_node->next=NULL;
if((*p)==NULL)//if list is empty
{
*p=new_node;
return;
}
struct node* last=*p;//initailly points to the 1st node
while((last)->next != NULL)//traverse till the last node
last=last->next;
last->next=new_node;
}
void printlist(struct node *node)
{
while(node != NULL);
{
printf("%d->",node->data);
node=node->next;
}
}
int main()
{
struct node *root=NULL;
insertAtEnd(&root,1);
insertAtEnd(&root,2);
insertAtEnd(&root,3);
insertAtEnd(&root,4);
insertAtEnd(&root,5);
printlist(root);
return 0;
}
Understanding the need of the below two variables is key to understanding the problem:
struct node **p: Because we need to link it from the root node created in the main.
struct node* last: Because if not used, the original content will be changed with the contents of the next node inside the while loop. In the end only 2 elements will be printed, the last 2 nodes, which is not desired.
void addlast ( int a)
{
node* temp = new node;
temp->data = a;
temp->next = NULL;
temp->prev=NULL;
if(count == maxnum)
{
top = temp;
count++;
}
else
{
node* last = top;
while(last->next)
last=last->next;
last->next = temp;
}
}
#include <bits/stdc++.h>
using namespace std;
class Node
{
public:
int data;
Node *next;
};
void append(Node *first, int n)
{
Node *foo = new Node();
foo->data = n;
foo->next = NULL;
if (first == NULL)
{
first = foo;
}
else
{
Node *last = first;
while (last->next)
last = last->next;
last->next = foo;
}
}
void printList(Node *first)
{
while (first->next != NULL)
{
first = first->next;
cout << first->data << ' ';
}
}
int main()
{
Node *node = new Node();
append(node, 4);
append(node, 10);
append(node, 7);
printList(node);
return 0;
}
Output: 4 10 7
You can use this code:
void insertAtEnd(Node* firstNode, string name)
{
Node* newn = new Node; //create new node
while( firstNode->next != NULL ) //find the last element in yur list
firstNode = firstNode->next; //he is the one that points to NULL
firstNode->next = newn; //make it to point to the new element
newn->next = NULL; //make your new element to be the last (NULL)
newn->data = name; //assign data.
}
void InsertAtEnd (node* &firstNode, string name){
node* temp=firstNode;
while(temp && temp->next!=NULL) temp=temp->next;
node * temp1 = new node;
temp1->data=name;
temp1->next=NULL;
if(temp==NULL)
firstNode=temp1;
else
temp->next= temp1;
}
while loop will return at temp==null in your code instead you need to return last node pointer from while loop like this
while(temp && temp->next!=NULL) temp=temp->next;
and assign a new node to next pointer of the returned temp node will add the data to the tail of linked list.