I want to write a very simple Linked List with only 3 methods Append, Remove and Print. This is not for production and is to be treated as code that could be used in an interview or a quick and dirty prototype. I'm really curious about the approach I've taken here to ensure no duplicates appear in my Linked List. I feel the data structure will be much easier to work with if remove any complexity around duplicate data and want to use this Linked List to implement a Stack, or Queue, or Binary Search Tree etc. I had int data before as the member data field for my Linked List and don't want to make this overly complex by introducing a concept of ids.
First I'd like to know if my member functions for the Linked List have any edge cases I am not catching and any improvements I can make to run time efficiency . Anyway I can simplify this code further with c++11 features, shorter variable names or any other suggestions would be appreciated too.
#include <iostream>
using namespace std;
struct Node
{
int id;
Node* next;
Node(int id) : id(id), next(nullptr) { }
void append(int newId) {
Node* current = this;
while (current->next != nullptr) {
if (current->id == newId) {
return;
}
else {
current = current->next;
}
}
current->next = new Node(newId);
}
void remove(int targetId) {
Node* current = this;
Node* previous;
while (current->id != targetId) {
if (current->next != nullptr) {
previous = current;
current = current->next;
}
else {
cout << "node not found :(\n";
return;
}
}
if (current->next == nullptr) {
delete current;
previous->next = nullptr;
}
else {
Node* danglingPtr = current->next;
current->id = current->next->id;
current->next = current->next->next;
delete danglingPtr;
}
}
void print() {
if (this->next == nullptr) {
cout << this->id << endl;
}
else {
cout << this->id << " ";
this->next->print();
}
}
};
int main()
{
Node list(1);
list.append(2);
list.append(3);
list.append(4);
list.print();
list.remove(3);
list.print();
list.remove(4);
list.print();
list.remove(1337);
}
1 Answer 1
The namespace
std
is not designed for wholesale importation, see "Why is "using namespace std" considered bad practice? " for more detail.You could instead do a
using std::cout;
or better qualify the three use-sites.You don't in any way encapsulate the list and abstract over the list. It's just a bunch of
Node
s. Consider putting it all into aList
owning and managing the whole lot.Trying to
remove()
the root-Node
uncovers a bug. Try to trace it through.pointer != nullptr
is just a long-winded way to writepointer
in a boolean context. Respectively forpointer == nullptr
and!pointer
. Yes, Java needs that, but this is C++.When you
return
from theif
-branch, putting the alternative in anelse
-branch is superfluous.A function for printing an object should allow the caller to specify the stream, and be called
operator<<
.There is no reason to rely on the compiler transforming recursion to iteration, especially as it might not always be able.
this
should rarely be used explicitly.
-
\$\begingroup\$ Thanks for the feedback Deduplicator. I'll refactor my code with these points, very helpful. In an interview scenario would you consider not encapsulating the Node, polluting the global namespace with
using namespace std
a bad reflection on the candidate? I want to write a solution as fast a possible, maybe in an interview I just mention that I should encapsulate my Node struct and not use std globally in a production code scenario?. \$\endgroup\$greg– greg2019年03月09日 21:05:23 +00:00Commented Mar 9, 2019 at 21:05 -
\$\begingroup\$ Just take a look how much the rest would change if you don't use
using namespace std;
. Huh, it's only used forstd::cout
, and that three times? And Considering that theNode
, as an internal implementation-detail ofList
, would at most need a single ctor for ease-of-use, it wouldn't lead to that much more code. Actually, when re-writing the rest you can actually shorten things, and get rid of the recursion too! \$\endgroup\$Deduplicator– Deduplicator2019年03月09日 22:33:02 +00:00Commented Mar 9, 2019 at 22:33 -
\$\begingroup\$ Should I replace my recursion with a while loop instead? In what cases will the compiler not be able to transform recursion to iteration? \$\endgroup\$greg– greg2019年03月10日 01:44:13 +00:00Commented Mar 10, 2019 at 1:44
-
1\$\begingroup\$ Well, if there are locals with non-trivial dtors, it might be non-trivial to prove, or need additional info. In this case, an optimising compiler should be able to. But writing it as iteration is more concise, clean, and always right. \$\endgroup\$Deduplicator– Deduplicator2019年03月10日 16:08:12 +00:00Commented Mar 10, 2019 at 16:08
-
1\$\begingroup\$ The root cause is that when you are trying to remove the first node, you don't have access to whatever owns that node. Fix point 2, and you can fix that. \$\endgroup\$Deduplicator– Deduplicator2019年03月10日 21:27:03 +00:00Commented Mar 10, 2019 at 21:27
Node
and aList
are different things. ANode
is a member of aList
(maybe a very simple member) but aList
does not need to have anyNodes
. There are several linked list implementations that we have reviewed here. Have a look at an existing one. \$\endgroup\$