4
\$\begingroup\$

This is a classic linked list problem.

  1. Deleting a node from linked list given the data to be deleted.
  2. Inserting a node in a sorted linked list.

I saw various versions of this and here is my version. Could you check and let me know if this code is efficient?

void deletenode(struct node *&first, int data) 
{
    struct node * current = first;// first will have the node after deletion

    struct node * prev = (node *)malloc(sizeof(node));
    while(current!=NULL)
    {
        if(current->data!=data)
        {
            prev=current;
            current = current->next;
        }
        else
        {
            prev->next = current->next;
            delete current;
            break;
        }
    }
}

void insertinsortedlist(struct node *& first, int data)
{
    struct node * current = first;// first will have the node after insertion
    struct node * newnode = (node *)malloc(sizeof(node));
    newnode->data = data;
    struct node * temp = (node *)malloc(sizeof(node));
    while(current)
    {
        if((current->data < data) && (current->next->data > data))
        {
            temp = current->next;
            current->next = newnode;
            newnode->next = temp;
            break;
        }
        current = current->next;
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ In the future please indent your code by 4 spaces or use the code button (the one with the ones and zeros) to properly format your code. \$\endgroup\$ Commented Mar 27, 2011 at 2:06
  • \$\begingroup\$ It's difficult to give a full review of your code without seeing your definition of node. \$\endgroup\$ Commented Mar 27, 2011 at 10:26
  • \$\begingroup\$ This program has a bug. If I want to delete the first node, it will not work. This is wrong: prev=first=null prev.next !=null \$\endgroup\$ Commented Mar 8, 2013 at 5:09
  • \$\begingroup\$ This code looks more like C than C++ to me \$\endgroup\$ Commented Dec 4, 2016 at 16:03

4 Answers 4

13
\$\begingroup\$

The efficiency of your algorithm is fine, however there are a couple of other things you should take care of:

First of all do not free memory, which was allocated with malloc, using delete. delete is for freeing memory which was allocated with new. To free malloced memory use free. Using delete on malloced memory is wrong and leads to undefined behavior.

Also there is almost never a good reason to use malloc in C++ code. If in doubt use new and delete.

Another serious bug in your code is that you allocate memory for prev in deletenode, but never free it.


I also don't see why you pass in first as a reference to a pointer. Since you never change first, I see no reason not to pass it in as a plain pointer (or a plain reference which you then take the address of to assign to current).


Another thing is that you don't need to add the struct keyword when declaring variables or parameters containing structs in C++ - that's a C thing. You should remove it as it only adds noise.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ All the above, and don't malloc a new node for previous in the first place. Instead, check if previous is still NULL once you found the node to be deleted. If it is, assign first = first->next to delete the first node. \$\endgroup\$ Commented Mar 27, 2011 at 7:31
  • \$\begingroup\$ To back this point up it's worth noting that if you use malloc to create instances of C++ objects, then the constructors won't get called. You'd have to use "placement new" manually after allocating the memory. This holds for destruction as well (ie. free doesn't call destructors). \$\endgroup\$ Commented Mar 8, 2013 at 22:27
2
\$\begingroup\$

To improve your deletenode function, I would strongly suggest you use a loop to advance forward to the node you need to delete. If there is no node, current will be NULL at the end of the loop. If the node to select is the first, prev will be NULL.

Of course, if we are deleting the first node, the calling code likely no longer has any way to access the first node. This is where we might use the fact that the first node pointer is being passed in as a reference.

Ideally your linked list would have a first node, but would not be defined as a node.

void deletenode(node *&first, int data) 
{
    node *current = first;
    node *prev = NULL;

    for (
        ; 
        current && current->data != data; 
        current = (prev = current)->next
    );

    // Node to delete not found.
    if (!current) {
        return;
    }

    // Node to delete is first node.
    if (!prev) {
        first = current->next;
        delete current;
        return;
    }

    // Node to delete is somewhere in the middle.
    prev->next = current->next;
    delete current;
}

We can employ a similar strategy to deal with inserting a node, which appears from your implementation to insert into the list to maintain a sorted order.

Note that your condition to check is odd and probably broken in that it only successfully inserts the node if the value to insert is both less than the current node and greater than the next node's value.

Imagine inserting 4 into {4, 5, 6, 7}: this condition is never met.

Also, in this condition, we never check that current->next is not NULL before accessing current->next->data. This is a one way, first-class, express ticket to undefined behavior.

void insertinsortedlist(struct node *& first, int data)
{
    node *current = first;
    node *prev = NULL;

    for (
        ; 
        current && current->data < data; 
        current = (prev = current)->next
    );

    node *newnode = new node;
    newnode->data = data;

    // New node gets inserted at the head of the list.
    if (!prev) {
        newnode->next = current;
        first = newnode;
        return;
    }

    prev->next = newnode;
    newnode->next = current;
}
\$\endgroup\$
2
\$\begingroup\$

You know the Single Responsibility Principle?

Every function should do only one thing, and do it well.

Currently, you have two functions which first find the proper position, and then respectively add or remove a node.
Splitting that simplifies the code and gives additional options.

I changed deletenode to also assume an ordered list.

struct node*& findpos(struct node*& first, int data) {
    auto* node = &first;
    while (*node && (*node)->data < data)
        node = &(*node)->next;
    return *node;
}
void deletenode(struct node*& node) {
    assert(node);
    delete std::exchange(node, node->next);
}
void deletenode(struct node*& first, int data) {
    auto& node = findpos(first, data);
    if (node && node->data == data)
        deletenode(node);
}
void inserthere(struct node*& node, int data) {
    node = new node { .next = node, .data = data };
}
void insertinsortedlist(struct node*& first, int data) {
    auto& node = findpos(first, data);
    inserthere(node, data);
}
\$\endgroup\$
2
\$\begingroup\$

I am not happy with missing spaces but that probably is a personal preference.

However the naming would definitely be better with underscores or camelCase.

The use should either be current != NULL (or better null_ptr) or !current

Your usage of struct node *& is fine, an alias of a pointer. As you then still are hesitating to utilize aliases, here an explicit aliasing version with struct node **, to better clarify what happens.

You pass the address of a list.

void delete_node_sorted(struct node **list, int data) 
{
    struct node **current = list;
    while(*current && (*current)->data < data)
    {
        current = &(*current)->next;
    }
    if(*current && (*current)->data==data)
    {
        struct node * match = *current;
        *current = (*current)->next;
        delete match;
    }
}

void delete_node(struct node **list, int data) 
{
    struct node **current = list;
    while(*current && (*current)->data != data)
    {
        current = &(*current)->next;
    }
    if(*current && (*current)->data==data)
    {
        struct node * match = *current;
        *current = (*current)->next;
        delete match;
    }
}

struct node *list = ...;
delete_node(&list, 13);
// list may have become NULL.

As you seem the alias current is either the address of the list, the first node, or the address of a next field. Hence deletion or insertion will work fine. This is an extra potency of C and C++.

Notice that there is no need to create a dummy node, remember a prev. And the code is clear for handling found/not found data.

// void insert_sorted(...)
void insert_in_sorted_list(struct node ** list, int data)
{
    struct node **current = list;
    while(*current && (*current)->data < data)
    {
        current = &(*current)->next;
    }
    if(!(*current && (*current)->data == data))
    {
        struct node * newnode = new node;
        newnode->data = data;
        newnode->next = *current;
        *current = newnode;
    }
}

Insertion here does not allocate in vain, when the data already is present. By *current = newnode; either the list header or some next field is filled.

\$\endgroup\$

You must log in to answer this question.