Linked List Interview Code methods, runtime, and edge cases Refactored code here

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 &lt;iostream&gt;

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-&gt;next != nullptr) {
            if (current-&gt;id == newId) {
                return;
            }
            else {
                current = current-&gt;next;
            }
        }
        current-&gt;next = new Node(newId);
    }

    void remove(int targetId) {
        Node* current = this;
        Node* previous;
        while (current-&gt;id != targetId) {
            if (current-&gt;next != nullptr) {
                previous = current;
                current = current-&gt;next;
            }
            else {
                cout &lt;&lt; "node not found :(\n";
                return;
            }
        }
        if (current-&gt;next == nullptr) {
            delete current;
            previous-&gt;next = nullptr;
        }
        else {
            Node* danglingPtr = current-&gt;next;
            current-&gt;id = current-&gt;next-&gt;id;
            current-&gt;next = current-&gt;next-&gt;next;
            delete danglingPtr;
        }
    }

     void print() {
        if (this-&gt;next == nullptr) {
            cout &lt;&lt; this-&gt;id &lt;&lt; endl;
        }
        else {
            cout &lt;&lt; this-&gt;id &lt;&lt; " ";
            this-&gt;next-&gt;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);
}
The namespace std is not designed for wholesale importation, see &quot;Why is “using namespace std” considered bad practice?&quot; 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 Nodes. Consider putting it all into a List 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 write pointer in a boolean context. Respectively for pointer == nullptr and !pointer. Yes, Java needs that, but this is C++.

When you return from the if-branch, putting the alternative in an else-branch is superfluous.

A function for printing an object should allow the caller to specify the stream, and be called operator&lt;&lt;.

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.