Linked list search function modifying list - c++

I am trying to implement a doubly linked list in C++ and the add function is working properly but the find node function is modifying the list.
All other function like insertAfter, delete depend on this find function and hence they are also not working as expected.
I am new to C++, so I don't completely understand pointers. I simply tried to replicate my Java program in C++. I know for sure that in the find function the pointer to the head node is causing the problem but I don't completely understand how.
Below is my code :
struct Node{
int data;
Node* next;
Node* prev;
Node(int d) {
data = d;
};
};
struct DLL {
Node* head;
Node* tail;
int size;
//Adding a Node to the Doubly LL
void addNode(Node* n) {
//If LL is empty add the first Node
if (tail == NULL) {
tail = n;
head = n;
}
//Else add add node to the tail. Connect n to the tails next and make n the tail
else {
tail->next = n;
n->prev = tail;
tail = n;
tail->next = NULL;
}
size++;
};
//Finding a random Node in the linked List
//It will return the Node with the FIRST occurrence where data = d
Node* findNode(int d) {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
//While next pointer is not null, traverse to search for a match.s
while (start->next != NULL) {
start = start->next;
if (start->data == d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
}
cout << "No node found with matching data = " << d <<endl;
return NULL;
};
};

start->data = d
This line in your second if block is assigning d to start->data rather than comparing the two.

This is a good time to learn about constness.
Node* findNode(int d) {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
This function has write access to the list, and you don't want that. Unfortunately, you abuse this access in the last if statement:
if (start->data = d) {
this code assigns the value of d to start->data and then tests if the value assigned to it was not null.
We can mark this function as const easily:
//////////////////////vvvvv/////////////////
Node* findNode(int d) const {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
and now the if will generate a compiler error.
A cleaned up version of your code might look something like the following:
#include <iostream>
struct Node {
int data_;
Node* next_ { nullptr };
Node* prev_ { nullptr };
Node(int data) : data_(data) {}
};
struct DLL {
Node* head_ { nullptr };
Node* tail_ { nullptr };
int size_ { 0 };
//Adding a Node to the Doubly LL
void addNode(Node* node) {
//If LL is empty add the first Node
if (tail_ == nullptr) {
tail_ = node;
head_ = node;
node->prev_ = node->next_ = nullptr;
}
//Else add add node to the tail. Connect n to the tails next and make n the tail
else {
tail_->next_ = node;
node->prev_ = tail_;
tail_ = node;
node->next_ = nullptr;
}
size_++;
}
//Finding a random Node in the linked List
//It will return the Node with the FIRST occurrence where data = d
Node* findNode(int data) const {
//We will start at the head and then traverse through the entire list to find a Node where data = d
//While next pointer is not null, traverse to search for a match.s
for (Node* start = head_; start != nullptr; start = start->next_) {
if (start->data_ == data) {
std::cout << "Node found with matching data : " << start << '\n';
return start;
}
}
std::cout << "No node found with matching data = " << data << '\n';
return nullptr;
}
};
int main()
{
DLL dll;
Node n1(1), n3(3), n5(5);
dll.addNode(&n1);
dll.addNode(&n3);
dll.addNode(&n5);
if (dll.findNode(1) != &n1)
std::cerr << "wrong result for findNode(1)\n";
if (dll.findNode(2) != nullptr)
std::cerr << "wrong result for findNode(2)\n";
if (dll.findNode(3) != &n3)
std::cerr << "wrong result for findNode(3)\n";
if (dll.findNode(4) != nullptr)
std::cerr << "wrong result for findNode(4)\n";
if (dll.findNode(5) != &n5)
std::cerr << "wrong result for findNode(5)\n";
}
Live demo: http://ideone.com/X34EgY

Related

Unhandled exception thrown: read access violation. "this" was 0x8 - Using Shared_ptr

Node.h
#pragma once
#include <iostream>
#include<memory>
class Node
{
public:
Node();
Node(int k, int d);
int key;
int data;
std::shared_ptr<Node> next;
std::shared_ptr<Node> previous;
//Node* next;
//Node* previous;
};
doubleLinkedList.h
#pragma once
/*! \class Double Linked List
\brief A double linked list data structure
*/
#include <iostream>
#include "../Node.h"
#include <string>
#include<memory>
class DoubleLinkedList : public Node
{
private :
std::shared_ptr<Node> head; //Node* head;
std::shared_ptr<Node> temp; //Node* temp;
std::shared_ptr<Node> mypointer; //Node* ptr;
std::shared_ptr<Node> nextNode;
std::shared_ptr<Node> prevNode;
public :
DoubleLinkedList();
DoubleLinkedList(std::shared_ptr<Node> n);
std::shared_ptr<Node> checkNodeExsits(int k); //Node*
void addNodeToFront(std::shared_ptr<Node> n); //Node*
void addNodeToEnd(std::shared_ptr<Node> n); //Node*
void addNodeAfter(int k, std::shared_ptr<Node> n); //Node*
void UpdateNode(int k , int d);
void deleteNode(int k);
void printList();
void printInfo(std::string Info);
};
Node.cpp
#include "Node.h"
#include <iostream>
Node::Node()
{
key = 0;
data = 0;
next = nullptr;
previous = nullptr;
}
Node::Node(int k, int d)
{
key = k;
data = d;
}
doubleLinkedList.cpp
#include <iostream>
#include "include\doubleLinkedList.h"
DoubleLinkedList::DoubleLinkedList()
{
head = nullptr;
}
DoubleLinkedList::DoubleLinkedList(std::shared_ptr<Node> n)
{
head = n;
}
std::shared_ptr<Node> DoubleLinkedList::checkNodeExsits(int k)
{
temp = nullptr;
mypointer = head;
while (mypointer != nullptr) {
if (mypointer -> key == k) {
temp = mypointer;
}
mypointer = mypointer-> next;
}
return temp;
}
void DoubleLinkedList::addNodeToFront(std::shared_ptr<Node> n)
{
if (checkNodeExsits(n->key) != nullptr)
{
printInfo("Node Already exist with key Number ");
}
else {
if (head == nullptr) {
head = n;
printInfo("Node Added as Head Node");
}
else {
head->previous = n;
n->next = head;
head = n;
printInfo("Node Added To The Begining");
}
}
}
void DoubleLinkedList::addNodeToEnd(std::shared_ptr<Node> n)
{
if (checkNodeExsits(n->key) != nullptr)
{
printInfo("Node Already exist with key Number");
}
else {
if (head == nullptr)
{
head = n; // if there isnt any node in the list.
printInfo("Node Has Been Added As Head Node");
}
else {
mypointer = head;
while (mypointer ->next != nullptr)
{
mypointer = mypointer->next;
}
mypointer->next = n;
n->previous = mypointer;
printInfo("Node Has Been Added To The End");
}
}
}
void DoubleLinkedList::addNodeAfter(int k, std::shared_ptr<Node> n)
{
mypointer = checkNodeExsits(k);
if (mypointer == nullptr) {
printInfo("No Node Exists With The Key Value");
}
else {
if (checkNodeExsits(n -> key) != nullptr) {
printInfo("Node Already exist with key Number");
}
else {
nextNode = mypointer-> next;
// inserting at the end
if (nextNode == nullptr) {
mypointer-> next = n;
n -> previous = mypointer;
printInfo("Node Inserted at the END");
}
//inserting in between
else {
n -> next = nextNode;
nextNode -> previous = n;
n -> previous = mypointer;
mypointer-> next = n;
printInfo("Node Inserted in Between");
}
}
}
}
void DoubleLinkedList::UpdateNode(int k, int d)
{
mypointer = checkNodeExsits(k);
if (mypointer != nullptr) {
mypointer-> data = d;
std::cout << "Node Data Updated Successfully" << std::endl;
}
else {
std::cout << "Node Doesn't exist with key value : " << k << std::endl;
}
}
void DoubleLinkedList::deleteNode(int k)
{
mypointer = checkNodeExsits(k);
if (mypointer == nullptr) {
std::cout << "No node exists with key value: " << k << std::endl;
}
else {
if (head -> key == k) {
head = head -> next;
std::cout << "Node UNLINKED with keys value : " << k << std::endl;
}
else {
nextNode = mypointer-> next;
prevNode = mypointer-> previous;
// deleting at the end
if (nextNode == nullptr) {
prevNode -> next = nullptr;
std::cout << "Node Deleted at the END" << std::endl;
}
//deleting in between
else {
prevNode -> next = nextNode;
nextNode -> previous = prevNode;
std::cout << "Node Deleted in Between" << std::endl;
}
}
}
}
void DoubleLinkedList::printList()
{
if (head == nullptr) {
std::cout << "No Nodes in Doubly Linked List";
}
else {
std::cout << std::endl << "Doubly Linked List Values : ";
temp = head;
while (temp != nullptr) {
std::cout << "[Key: " << temp->key << ", Data: " << temp->data << "] <___> " << std::endl;
temp = temp -> next;
}
}
}
void DoubleLinkedList::printInfo(std::string Info)
{
std::cout << Info << std::endl;
}
main.cpp
#include <iostream>
#include "../include/doubleLinkedList.h"
#include"../Node.h"
void Print(std::string info)
{
std::cout << info << std::endl;
}
int main() {
DoubleLinkedList myNode;
//Node* newNode = new Node(2,7);
std::shared_ptr<Node> newNode = std::make_shared<Node>(2, 7); // enter key number and data number
std::shared_ptr<Node> newNode1 = std::make_shared<Node>(3, 9);// enter key number and data number
newNode->key;
newNode->data;
myNode.addNodeToFront(newNode);
newNode->key;
newNode->data;
myNode.addNodeAfter(2, newNode1); // enter the key number of existing node and then to add new key number and new data
myNode.printList();
system("pause");
return 0;
}
The error I'm getting is:
unhandled exception thrown: read access violation. "this" was 0x8
To elaborate -
( Access violation reading location 0x0000000000000010. Unhandled exception thrown: read access violation).
So, the code would work when I use raw pointers. The only thing I can deduce from this is that there's a chain of shared pointers in the method of void DoubleLinkedList::addNodeAfter() nextNode = mypointer-> next;
or that shared pointers can't have nullptr assigned to it.
I'm clueless as to why this is happening.
Let's take a walk through addNodeAfter
mypointer = checkNodeExsits(k);
We've checked to make sure value k exists in the list and gotten a pointer to it if it does. If it doesn't, we get a null pointer.
if (mypointer == nullptr) {
Tests whether or not k was found. Let's assume it was, mypointer isn't null, and we enter the else
else {
if (checkNodeExsits(n -> key) != nullptr) {
Here we check to make sure the node we're inserting isn't a duplicate. Again let's take the else branch
else {
nextNode = mypointer-> next;
Should be safe, right? We know that mypointer was not null because we tested it earlier. But when we look... Holy smurf! The program crashed! It was null. How did it become null?
The answer lies in another question: Where did mypointer come from? It's not defined within this function, so it has wider scope. Turns out it is a DoubleLinkedList member variable. Is someone else messing with it? We don't have multiple threads, so it must be another function that was called by addNodeAfter.
That would have to be checkNodeExsits, so let's take a look at it.
std::shared_ptr<Node> DoubleLinkedList::checkNodeExsits(int k)
{
temp = nullptr;
mypointer = head; // well lookie here!
while (mypointer != nullptr) {
if (mypointer -> key == k) {
temp = mypointer;
}
mypointer = mypointer-> next; // and here!
}
return temp;
}
We can see that if the inserted node's value does not exist, thus putting us in the else case back in addNodeAfter where we're going to insert the new node, mypointer can only be null!
The overly broad scope of mypointer turns the member variable into a boobytrap. It needs to be a variable local to checkNodeExsits and addNodeAfter to prevent these functions from trashing the state of the functions that use them. This leads to questioning whether mypointer should be a member variable anywhere it is found. That leads to wondering about temp, nextnode and prevnode. They all sound like temporary holders of local state information. Likely the only DoubleLinkedList member that should be a member is head.
To fix: Remove the definition of mypointer from the class and resolve the compiler errors that result. If a use of mypointer can be easily replaced with a local variable, do so. For any that remain and are needing of longer-term storage for resuse, you'll have to get a bit more creative. Make the call on how long-lived and how wide a scope is necessary to get the behaviour you want and then give this new variable an appropriately descriptive name to help you remember the circumstances in which it should be used.
I recommend repeating this process for the other member variables except for head where the necessary scope and lifetime is obvious.
General rule of thumb: Keep the scope of variables as tight as possible. Only widen the scope of a variable if you absolutely need to reuse its state later, and if you find you must be very careful with it to ensure you don't cause unintended side-effects.

I read the input number with getchar(), why is the number reversed in the linked list?

I typed 1234, but the list has 4,3,2,1 in it. I suspect the problem is getchar() itself, or a function in the class, but I have no way to find out.
The link class is responsible for some linked list operations, such as deletion, insertion, etc., while the node class is responsible for creating and assigning nodes.
The createlist class is responsible for the creation of the linked list, which is the main source of the problem. I wrote the debug statement in it, so you can run it and see the results for yourself
using namespace std;
class Node
{
public:
int data;
Node *next;
Node()
{
next = nullptr;
}
Node(int data)
{
this->data = data;
}
Node(const Node &temp)
{
this->data = temp.data;
}
};
class Link
{
public:
Node *head;
int length = 0;
Link()
{
head = new Node();
}
~Link()
{
while (head != nullptr)
{
Node *p = head->next;
free(head);
head = p;
}
}
void insert(const Node &cache)
{
Node *temp = new Node(cache);
temp->next = head->next;
head->next = temp;
length++;
}
};
void Creatlist(Link &link)
{
char cache;
while (1)
{
cache = getchar();
if (cache == '\n')
break;
link.insert(Node(cache - '0'));
cout << cache << " ";
}
cout<<endl;
Node *p = link.head->next;
cout << "in the linklist:";
while (p != nullptr)
{
cout << p->data << " ";
p = p->next;
}
}
int main()
{
Link link;
cout<<"inut numbers:"<<endl;
Creatlist(link);
}```
With the insert you inserted to the FRONT of the list. So you had "1", then "2->1" ... If you want to insert to the end, don't insert at the head, but hake a Node* tail in the class Link and an insert_end function as
//...
Node* temp;
void insert_end(const Node &cache){
Node *temp = new Node(cache);
tail->next=temp;
tail=tail->next;
length++;
}
Alsoin the constructor set tail=head

How to use head while iterating through a linked list?

We are iterating through the linked list with the help of head, that is, we are updating our head as we move forward towards i th position. Please have a look at the fuction insertIthnode. I am inserting my Node at i th position are returning head - and it's still able to print the linked list. I don't know how? head is no longer pointing towards the first node then how is it still able to return a full linked list?
here's the code:
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node *next;
Node(int data) {
this->data = data;
next = NULL;
}
};
int length(Node *head) {
int x = 0;
Node *temp = head;
while (temp != NULL) {
x += 1;
temp = temp->next;
}
return x;
}
void printIthnode(Node *head, int i) {
int n = length(head);
if (i < 0 || i > n - 1) {
cout << -1 << endl;
return;
}
int count = 1;
while (count <= i) {
head = head->next;
count++;
}
if (head) {
cout << head->data << endl;
} else {
cout << "-1" << endl;
}
}
Node *takeinput() {
int data;
cin >> data;
Node *head = NULL;
Node *tail = NULL;
while (data != -1) {
Node *n = new Node(data);
if (head == NULL) {
head = n;
tail = n;
} else {
tail->next = n;
tail = n;
}
cin >> data;
}
return head;
}
void PrintLL(Node *head) {
Node *temp = head;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
}
Node *insertIthnode(Node *head, int i, int data) {
if (i < 0) {
return head;
} else if (i == 0) {
Node *n = new Node(data);
n->next = head;
head = n;
return head;
}
int count = 1;
while (count <= i - 1 && head != NULL) {
head = head->next;
count++;
if (count == i - 1) {
Node *n = new Node(data);
n->next = head->next;
head->next = n;
return head;
}
return head;
}
}
int main() {
/*Node n1(1);
Node *head=&n1;
Node n2(2);
Node n3(3);
Node n4(4);
Node n5(5);
Node n6(6);
n1.next=&n2;
n2.next=&n3;
n3.next=&n4;
n4.next=&n5;
n5.next=&n6;
*/
Node *head = takeinput();
insertIthnode(head, 3, 7);
PrintLL(head);
}
In the main() function you are creating a head when you are taking input from the user with the help of the "takeInput()" function.
After that, you are calling the function "insertIthnode(head,3,7)" which is returning the head (since the return type is Node) but you are not receiving it in any variable so the head returned from the "insetIthnode" is lost.
Your original head remains the same as per of "takeInput()" function.
If you try to insert ith Node at Index 0 it won't print according to the inserted node.
The problem is that you consider the Node as the linked list. While this is valid, the whole point of the linked list is that you don't lose track of the head. You could use two approaches:
Don't iterate over the head. Instead, use a temporary reference to the head.
Implement a Linked List wrapper. You can keep a constant reference to the head while performing operations over the node.
You pass head by value. Any changes you do to the variable receiving the value of head inside the functions are made to the local variable inside the function only and will not be visible from the call site.
Take your PrintLL function as an example:
void PrintLL(Node *head) { // head is here a local variable
Node *temp = head;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
}
This could be rewritten without the extra variable temp. The name head doesn't make it the same head you used to call the function with:
void PrintLL(Node* head) {
while (head != nullptr) {
cout << head->data << ' ';
head = head->next;
}
}
and it would not affect the head you passed in as a parameter.
Similarly:
void foo(int x) {
++x;
//
}
int main() {
int x = 10;
foo(x);
std::cout << x << '\n'; // prints 10
}

Will there be a memory leak in this Linked list

Hi Currently i have been honing my skill in the data structure so that i can become a good Game Developer, i am Learning Linked list and made a Linked list Program with Insertion ,Deletion and Recursive insertion and Reversing a Linked list Can you Guys tell me am i here Clearing the assigned memory Created with the new Operator Correctly, I am getting the Desired Output but i am worried about memory leak. ... please be gentle aim Still learning.
class Node
{
int data;
Node *Next;
public:
int GetData()
{
return data;
}
void SetData(int Data)
{
data = Data;
}
Node *GetNext()
{
return Next;
}
void SetNext(Node *next)
{
Next = next;
}
};
void Insert(Node **Head, int x)
{
Node *temp = new Node();
temp->SetData(x);
temp->SetNext(*Head);
*Head = temp;
}
void InsertAt(Node **Head, int x, int n)
{
if (n == 0)
{
std::cout << "The Given data at 'n' cannot be assigned to null\n";
}
Node *temp = new Node();
temp->SetData(x);
if (n == 1)
{
temp->SetNext(nullptr);
*Head = temp;
return;
}
Node *temp2 = *Head;
if (Head == nullptr)
{
std::cout << "The Given data cannot be assigned to null\n";
}
for (int i = 0; i < n - 2; i++)
{
temp2 = temp2->GetNext();
}
temp->SetNext(temp2->GetNext());
temp2->SetNext(temp);
}
void AppendList(Node **Head, int Data)
{
Node *temp = new Node();
Node *LastNode = *Head;
temp->SetData(Data);
temp->SetNext(nullptr);
if (*Head == nullptr)
{
*Head = temp;
return;
}
// else Traverse till last node.
while (LastNode->GetNext() != nullptr)
{
LastNode = LastNode->GetNext();
}
LastNode->SetNext(temp);
}
void DeleteNode(Node **Head, int n)
{
Node *temp = *Head;
if (n == 1)
{
*Head = temp->GetNext();
std::cout << "\nAfter Deletion of Head Node\n";
return;
}
for (int i = 0; i < n - 2; i++)
{
temp = temp->GetNext();
}
Node *temp2 = temp->GetNext();
temp->SetNext(temp2->GetNext());
std::cout << "After Deletion of Node\n";
}
Node *ReverseList(Node *Head)
{
Node *Current, *Prev, *next;
Current = Head;
Prev = nullptr;
while (Current != nullptr)
{
next = Current->GetNext();
Current->SetNext(Prev);
Prev = Current;
Current = next;
}
Head = Prev;
return Head;
}
int LinkedList_Count(Node *Head)
{
int count = 0;
Node *Current = Head;
while (Current != nullptr)
{
count++;
Current = Current->GetNext();
}
std::cout << "Number of Elements: " << count;
return count;
}
void PrintList(Node *Head)
{
std::cout << "Data list : ";
while (Head != nullptr)
{
std::cout << " " << Head->GetData();
Head = Head->GetNext();
}
std::cout << "\n";
}
//Print Listed using Recursion
void Recursion_Print(Node *Head)
{
if (Head == nullptr)
{
return;
}
std::cout << ' ' << Head->GetData(); //comment to Do Reverse the Linked list
Recursion_Print(Head->GetNext());
//std::cout << ' ' << Head->GetData();//unComment to Reverse the linked List recursively
}
Node *RecursiveRevList(Node *Head)
{
Node *temp;
if (Head->GetNext() == nullptr)
{
temp = Head;
return temp;
}
temp = RecursiveRevList(Head->GetNext());
Head->GetNext()->SetNext(Head);
Head->SetNext(nullptr);
return temp;
}
void RunList()
{
Node *Head = NULL;
//AppendList(&Head, 16);
Insert(&Head, 6);
Insert(&Head, 7);
Insert(&Head, 8);
InsertAt(&Head, 18, 2);
std::cout << "Data list : \n";
Recursion_Print(Head);
std::cout << '\n';
LinkedList_Count(Head);
DeleteNode(&Head, 1);
//Head = ReverseList(Head);
Head = RecursiveRevList(Head);
PrintList(Head);
LinkedList_Count(Head);
delete Head;
}
You're writing C-style code; in C++, you should avoid explicit calls to new. Your example is far too long to rewrite, but here is a very small start:
#include <memory>
class Node
{
int data;
std::shared_ptr<Node> Next;
// ...
void Insert(std::shared_ptr<Node>& Head, int x)
{
auto temp = std::make_shared<Node>();
// ...
}
(Note that std::unique_ptr is probably a better choice than std::shared_ptr, but that incurs complications with copying Node of which you're "blissfully" unaware right now.)
And, in practice, you should really use std::list (in your case, std::list<int>) rather than writing your own. Once you're proficient using std::list (and friends like std::vector), you'll be better able to "roll your own."
As pointed out in the comments by many learned people, you have a memory leak in your program. When you are deleting the nodes, you're not actually freeing the allocated memory locations. The correct way? Use delete to deallocate the memory from the program.
I would recommend you to learn it as a rule of thumb, when programming in C or C++, if you're allocating dynamic memory somewhere in your program, then in all certainty you'd have some deletion method where you should use free() or delete to deallocate the memory from the heap.
Here are some resources which might help you.
https://www.geeksforgeeks.org/g-fact-30/
https://www.geeksforgeeks.org/delete-in-c/
https://en.cppreference.com/w/cpp/language/delete (might be a little hard if you're a beginner)

Linked lists problems

I am trying to teach myself c++ and I am really confused with linked lists. I have been reading through textbooks and looking online but I am really confused how they work. I found an exercise online that I have been trying to figure out, but I am not getting anywhere with it.
Here is the list.h file:
struct node
{
int val;
struct node *next;
};
int length(struct node *);
void push(struct node **, int); //add to front of list
void append(struct node **, int); //add to rear of list
void print(struct node *, int);
I am having a hard time trying to write the functions for length, push and append.
A linked list is simply a string of Node classes string together, with each owning a pointer whose address is that of the next Node class in the list.
It's pretty simple if you think of it this way:
Coliru: http://coliru.stacked-crooked.com/a/5e71c5e31b58673c
#include <iostream>
//Proper encapsulation is not included for terseness and clarity
class Node {
public:
int num;
Node* next;
Node(int n) :
num(n),
next(nullptr) {
};
~Node() {}
};
int main() {
Node a(0);
Node b(1);
Node c(2);
Node d(3);
Node e(4);
//String the nodes together, linking like metal chainlinks
a.next = &b;
b.next = &c;
c.next = &d;
d.next = &e;
//Can you see how the "link" actually works here?
//Each Node's "next" pointer points to the next node.
std::cout << "Node a: " << a.num << std::endl;
std::cout << "Node b: " << a.next->num << std::endl;
std::cout << "Node c: " << a.next->next->num << std::endl;
std::cout << "Node d: " << a.next->next->next->num << std::endl;
std::cout << "Node e: " << a.next->next->next->next->num << std::endl;
//What if I were to point e to the start of a?
e.next = &a;
std::cout << "Node e->next: " << e.next->num << std::endl;
//It's node a!
//Node e.next is now accessible through the linked list:
std::cout << "Node e->next = a.next->next->next->next->next: " << a.next->next->next->next->next->num << std::endl;
//Usually people just use a for loop for this type of stuff.
//Let's use a lambda function to write one right here:
auto index_func = [](Node* head, size_t index) {
Node* current = head;
Node* next = head->next;
for (int i = 0; i < index; ++i) {
if (next != nullptr) {
//Hey, look at the pointers fly!
current = next;
next = current->next;
} else {
std::cout << "Whoops, we hit a null pointer before we got to our index!" << std::endl;
break;
}
}
return current->num;
};
//This is the same as finding the 6th node in the list (but since it's zero-indexing it's [5])
std::cout << index_func(&a, 5) << std::endl;
//We can also continue to do this:
std::cout << index_func(&a, 499) << std::endl;
//This is me accessing the 500th element, which, since our back links to our head node, it's the 4th node, d.
return 0;
}
You can probably imagine the other shenanigans we can do with linked lists if we decide to insert a Node between a or e simply be reassigning pointers.
void push(struct node **list, int newVal) { //add to front of list
struct node* firstNode = *list;
struct node* newNode = (struct node*) malloc(sizeof(struct node));
if (newNode == NULL) abort();
newNode->val = newVal;
newNode->next = firstNode;
*list = newNode;
}
void append(struct node **list, int newVal){ //add to rear of list
if (*list == NULL) {
push(list, newVal);
return;
}
/* Locate last node in list*/
struct node* curNode = *list;
while (curNode->next != NULL)
curNode = curNode->next;
/* Add a new node at the end of the list */
struct node* newNode = (struct node*) malloc(sizeof(struct node));
if (newNode == NULL) abort();
newNode->val = newVal;
newNode->next = NULL;
curNode->next = newNode;
}