In the code bellow, I'm searching for ways to optimize the speed of insertions when dealing with Millions of inserts at the time. the code runs fine but is very slow when performing large amounts of inserts. I already tried some ideas but is always slow. I guess the solution is to use Multi-threading to perform the inserts, and use a global variable "truct Node* nodeObj".
I have very few/no experience with Multi-threading and Synchronization in C, C++, If you could give me an example based on the code bellow I would be very appreciated.
Any additional Ideas are welcome.
The rules are:
1- In the end (after insert all numbers) the linked list must be ordered, meaning that each Node->data starts at lowest number up to the Largest number.
2 - The caller is using a for loop, this for loop cannot be started inside the Insert function.
3 - Any of the code, caller or insert function can be optimized as long as the insert function does not automatically add inserts by it self, that's the caller s job.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct Node {
int data;
struct Node* nextAddr;
};
struct Node* Insert(Node* p, int data);
void Print(struct Node* p);
void RevPrint(struct Node* p);
int main() {
struct Node* nodeObj = NULL;
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
nodeObj = Insert(nodeObj, i);
printf(" %d", i);
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
Print(nodeObj);
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
RevPrint(nodeObj);
printf("\nPress any key to continue...");
_getch();
}
struct Node* Insert(Node* _pToNode, int _nbr)
{
Node *newValue = (struct Node*)malloc(sizeof(struct Node));
newValue->data = _nbr;
newValue->nextAddr = NULL;
if (_pToNode == NULL) _pToNode = newValue;
else {
Node* pLocal = _pToNode;
while (pLocal->nextAddr != NULL) {
pLocal = pLocal->nextAddr;
}
pLocal->nextAddr = newValue;
}
return _pToNode;
}
void Print(struct Node* p) {
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->nextAddr);
}
void RevPrint(struct Node* p) {
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->nextAddr);
printf(" %d", p->data);
}
Thank you.
Linked lists are unwelcome on modern machines. The L1 and L2 caches love vectors and arrays. They utterly despise linked lists. Use a std::vector, not a linked list, (and certainly not a hand-rolled linked list). Try to .reserve() enough memory in the vector to accommodate all the new stuff. Just push everything onto the back of the vector, and then sort the vector using parallel execution. C++17 can do that painlessly. std::stable_sort parallels quite nicely.
Caveat: This works only for tail insertion/append:
int
main()
{
struct Node* nodeObj = NULL;
struct Node* nodeTail = NULL;
// your other stuff ...
for (int i = 1; i < 1000000; i++) {
nodeObj = Insert(nodeObj, &nodeTail, i);
printf(" %d", i);
}
// your other stuff ...
}
struct Node* Insert(Node* _pToNode, Node **tail,int _nbr)
{
Node *newValue = (struct Node*)malloc(sizeof(struct Node));
newValue->data = _nbr;
newValue->nextAddr = NULL;
if (_pToNode == NULL) {
_pToNode = newValue;
}
else {
(*tail)->nextAddr = newValue;
}
*tail = newValue;
return _pToNode;
}
You could clean this up with a "list" struct that has both the head and tail in it (vs. the separate args)
UPDATE:
Very cool/smart, but unfortunately is still slow...
malloc et. al. can be slow for a large number of small allocations. One way to speed things up is to use a subpool of allocations [as WeatherVane suggested].
As I mentioned, adding a "list" struct can make things tidier and I used it in two places. Once you commit to that, you can add other things besides head/tail, such as count. Also, it makes it easier to convert from a singly-linked list to a doubly-linked in the future, if you so choose.
Side note: With a doubly-linked list, insertions are [slightly] more complex, but insertions in the middle of the list are much faster because you don't have to traverse the list to find the previous pointer (e.g. the node would have a prev pointer in it). Also, RevPrintList would become as simple as PrintList.
Note that [on my system], the reverse print ran out of stack space [and segfaulted], so I recoded the print functions to not be recursive.
Here's a cleaned up version that should do the insertions faster because of the reduced number of individual malloc calls.
Side note: I didn't add the requisite checks for malloc, etc. returning null.
#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_* next;
} Node;
typedef struct List_ {
int count;
Node* head;
Node* tail;
} List;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
List freelist;
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist, i);
#if 0
printf(" %d", i);
#endif
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node*
NewNode(void)
{
Node *node;
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (freelist.count <= 0) {
freelist.count = 1000;
freelist.head = calloc(freelist.count,sizeof(Node));
}
node = freelist.head++;
freelist.count -= 1;
return node;
}
Node*
Insert(List* list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->next);
}
void
PrintList(List* list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d", node->data);
printf("\n");
}
void
RevPrint(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node**) * list->count);
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
UPDATE #2:
you could make freeList a list of lists (using a different "node" struct which would have a pointer to list instead of a number), so that memory could be released when the program is done.
Here is a modified version that does that:
#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_* next;
} Node;
typedef struct List_ {
int count;
Node* head;
Node* tail;
} List;
typedef struct Freelist_ {
int count;
Node* head;
Node* tail;
Node* avail;
} FreeList;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist, i);
// this printf will radically slow things down
#if 0
printf(" %d", i);
#endif
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
// release all nodes back to the malloc free pool
FreeAll();
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node*
NewNode(void)
{
Node *node;
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (freelist.count <= 0) {
freelist.count = 1000;
node = calloc(freelist.count,sizeof(Node));
// maintain linked list of nodes that are at the _start_ of a
// malloc area/arena
if (freelist.head == NULL)
freelist.head = node;
else
freelist.tail->next = node;
freelist.tail = node;
// burn the first node as a placeholder
freelist.avail = node + 1;
freelist.count -= 1;
}
node = freelist.avail++;
freelist.count -= 1;
return node;
}
void
FreeAll(void)
{
Node* node;
Node* next;
for (node = freelist.head; node != NULL; node = next) {
next = node->next;
free(node);
}
}
Node*
Insert(List* list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->next);
}
void
PrintList(List* list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d", node->data);
printf("\n");
}
void
RevPrint(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node**) * (list->count + 1));
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
UPDATE #3:
Here's a version that adds reuse of nodes by adding a reuse member to FreeList and a FreeOne function that can be called when a node is deleted.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_ *next;
} Node;
typedef struct List_ {
int count;
Node *head;
Node *tail;
} List;
typedef struct Freelist_ {
int count;
Node *head;
Node *tail;
Node *avail;
Node *reuse;
} FreeList;
Node *NewNode(void);
Node *Insert(List *p,int data);
void Print(Node *p);
void PrintList(List *list);
void RevPrint(Node *p);
void RevPrintList(List *list);
void FreeOne(Node *p);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n" "Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist,i);
// this printf will radically slow things down
#if 0
printf(" %d",i);
#endif
}
printf("\n---------------------------------\n" "Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n" "RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
// release all nodes back to the malloc free pool
FreeAll();
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node *
NewNode(void)
{
FreeList *list;
Node *node;
list = &freelist;
do {
// try to reuse a node that has been released by FreeOne
node = list->reuse;
if (node != NULL) {
list->reuse = node->next;
node->next = NULL;
break;
}
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (list->count <= 0) {
list->count = 1000;
node = calloc(list->count,sizeof(Node));
// maintain linked list of nodes that are at the _start_ of a
// malloc area/arena
if (list->head == NULL)
list->head = node;
else
list->tail->next = node;
list->tail = node;
// burn the first node as a placeholder
list->avail = node + 1;
list->count -= 1;
}
// grab one from the current allocation array
node = list->avail++;
list->count -= 1;
} while (0);
return node;
}
void
FreeOne(Node *node)
{
FreeList *list;
list = &freelist;
// push this node onto the front of the reuse list (i.e. it's fast)
node->next = list->reuse;
list->reuse = node;
}
void
FreeAll(void)
{
Node *node;
Node *next;
for (node = freelist.head; node != NULL; node = next) {
next = node->next;
free(node);
}
memset(&freelist,0,sizeof(FreeList));
}
Node *
Insert(List *list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node *p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d",p->data);
Print(p->next);
}
void
PrintList(List *list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d",node->data);
printf("\n");
}
void
RevPrint(Node *p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d",p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node **) * (list->count + 1));
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
I assume that the order of the elements that you mention is ordered by comparison. I.e. A < B => A is before B in the container.
If that assumption holds the best you can hope for is a log(N) insertion of each element. So if you want to insert an element among those millions of elements you should expect to do about 20+ comparisons. If the millions you want to insert have an order you can do better.
Now, a pointer based structure is never the answer to a fast data structure.
std::vector should be your go to.
To read this fast-insertion structure with a for statement you'd need a very peculiar iterator or you need to rearrange the inserted stuff in to another vector.
In conclusion, you want to insert stuff into a simple binary heap.
Multithreading would not make this faster unless you can devise a merge like scheme to your inserts.
Related
so i want to implement a queue data structure in c++ and use some special methods like the delete_at() putting into consideration the constraints of the queue data structure, so I made it using the dequeue method and took all the data that are not equal to the index the user want to delete and stored in array in order to enqueue it all back in but without the index that the user want to delete, however nothing gets deleted , so here is the code :
#include <list>
using namespace std;
class Queue{
private:
struct node {
int data;
struct node *next;
};
struct node* front = NULL;
struct node* rear = NULL;
public:
void Enqueue(int d) {
struct node* tmp=new node;
if (rear == NULL) {
tmp->next = NULL;
tmp->data = d;
rear=tmp;
front = rear;
}
else {
rear->next = tmp;
tmp->data = d;
tmp->next = NULL;
rear = tmp;
}
}
int Dequeue() {
struct node* tmp = front;
int data=front->data;
if (front == NULL) {
return 0;
}
else{
if (tmp->next != NULL) {
tmp=front;
front = front->next;
delete tmp;
}
else {
tmp=front;
delete tmp;
front = NULL;
rear = NULL;
}
}
return data;
}
void Display() {
struct node* temp = front;
if (front == NULL) {
cout<<"Queue is empty"<<endl;
return;
}
while (temp != NULL) {
cout<<temp->data<<"\n";
temp = temp->next;
}
}
int Size() {
struct node* temp = front;
int cnt=0;
while (temp != NULL) {
cnt++;
temp = temp->next;
}
return cnt;
}
void Delete_at(int index){
int i=0;
int ar_size=Size();
int data_arr[ar_size-1];
if(index > Size()){
cout<<"\n"<<"Error: out of bounds !";
return;
}
while(i<ar_size){
if (i==(index-1)){
Dequeue();
}
else{
data_arr[i]=Dequeue();
}
i++;
}
i=0;
while(i<ar_size){
Enqueue(data_arr[i]);
i++;
}
}
};
int main() {
int i=0;
Queue q;
q.Enqueue(2);
q.Enqueue(6);
q.Enqueue(7);
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(4);
q.Delete_at(2);
q.Display();
return 0;
}
You have a two primary problems with your code generally, and Delete_at() cannot simply call Dequeue(). As a general note, your code contains duplicated expressions that can simply be consolidated. For example, Enqueue() can be written succinctly as:
void Enqueue(int d) {
struct node *tmp = new node;
tmp->next = nullptr;
tmp->data = d;
if (rear == nullptr) {
front = rear = tmp;
size = 1;
}
else {
rear->next = tmp;
rear = tmp;
size += 1;
}
}
Your Dequeue() function will segfault checking front->data BEFORE checking if front == nullptr. You must check before you dereference, e.g.
int Dequeue() {
struct node *tmp = front;
int data;
if (front == nullptr) { /* (must check before dereference (front->data) */
return 0;
}
data = front->data;
size -= 1;
if (tmp->next != nullptr) {
front = front->next;
}
else {
front = nullptr;
rear = nullptr;
}
delete tmp;
return data;
}
Your Delete_at() function must remove the node at a specific index. This requires that you maintain your ->next links throughout your list, updating the prev->next before the deleted node to point to the node after the one you are deleting. You do that by iterating with both the address of the node and a pointer to node. When you reach the index to remove, you simply replace what is currently at the address of that node with the next node and delete the current, see: Linus on Understanding Pointers
void Delete_at (size_t index) {
struct node *pnode = front, /* pointer to node */
**ppnode = &front; /* address of node */
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
while (index--) { /* loop index times */
ppnode = &pnode->next; /* address of next node */
pnode = pnode->next; /* pointer to next node */
}
*ppnode = pnode->next; /* replace struct at address with next */
delete pnode; /* delete removed node */
size -= 1;
}
Your Size() function simply reduces to a "getter" function:
size_t Size() {
return size;
}
Updating your example a bit and being mindful of Why is “using namespace std;” considered bad practice? your full code could now be:
#include <list>
#include <iostream>
class Queue{
private:
struct node {
int data;
struct node *next;
};
struct node *front = nullptr;
struct node *rear = nullptr;
size_t size;
public:
void Enqueue(int d) {
struct node *tmp = new node;
tmp->next = nullptr;
tmp->data = d;
if (rear == nullptr) {
front = rear = tmp;
size = 1;
}
else {
rear->next = tmp;
rear = tmp;
size += 1;
}
}
int Dequeue() {
struct node *tmp = front;
int data;
if (front == nullptr) { /* (must check before dereference (front->data) */
return 0;
}
data = front->data;
size -= 1;
if (tmp->next != nullptr) {
front = front->next;
}
else {
front = nullptr;
rear = nullptr;
}
delete tmp;
return data;
}
void Display() {
struct node *temp = front;
if (front == nullptr) {
std::cout << "Queue is empty" << '\n';
return;
}
while (temp != nullptr) {
std::cout << temp->data << '\n';
temp = temp->next;
}
}
size_t Size() {
return size;
}
void Delete_at (size_t index) {
struct node *pnode = front, /* pointer to node */
**ppnode = &front; /* address of node */
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
while (index--) { /* loop index times */
ppnode = &pnode->next; /* address of next node */
pnode = pnode->next; /* pointer to next node */
}
*ppnode = pnode->next; /* replace struct at address with next */
delete pnode; /* delete removed node */
size -= 1;
}
};
int main() {
Queue q;
q.Enqueue(2);
q.Enqueue(6);
q.Enqueue(7);
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(4);
q.Display();
std::cout << "\nq.Delete_at(2)\n\n";
q.Delete_at(2);
q.Display();
}
Example Use/Output
$ ./bin/queue_delete_at
2
6
7
1
2
4
q.Delete_at(2)
2
6
1
2
4
Look things over and let me know if you have further questions.
Edit With Additional Constraints From Comments
Per you comments, you have constraints of only being able to use Dequeue() and Enqueue() in Delete_at() and no pointers, etc... You can do that, but understand it will be horribly inefficient compared to simply removing the node at the index. You will essentially have to save (Dequeue()) your entire queue data in an allocated block of memory, omitting the index to remove. You will then need to iterate over all saved values calling Enqueue() to repopulated your list.
You can do that as:
void Delete_at (size_t index) {
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
size_t nelem = Size();
int *arr = new int [nelem],
n = 0;
for (size_t i = 0; i < nelem; i++) {
int tmp = Dequeue();
if (i != index && tmp)
arr[n++] = tmp;
}
for (size_t i = 0; i < (size_t)n; i++)
Enqueue (arr[i]);
delete[] arr;
}
(same output)
For a less readable more C++'ized presentation, you can replace the first loop with:
for (int i = 0, j = Dequeue(); j; i++, j = Dequeue())
if (static_cast<size_t>(i) != index)
arr[n++] = j;
It would be nice to have utilized at least a separate list pointer, so you could build the new list while simultaneously deleting the old, but your class/struct isn't setup to use additional pointers. So you are basically left with buffering all values except the index to remove and then recreating your queue.
I am trying to implement merge sort in a linked list in c++. When I execute my code, it runs infinitely no. of time. When I debug it, I found that my mergesort function runs only for the left half infinite number no. of times.I mean it never comes out from left half. That function never called for the right half. I am pasting my whole code link here. Can anyone tell me why the function runs infinitely and what modifications I have to make to my code?
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
struct Node {
int data;
struct Node* link;
};
Node* head = NULL;
void ins(int data, int pos)
{
Node* ptr = new Node();
ptr->data = data;
ptr->link = NULL;
if (pos == 1) {
ptr->link = head;
head = ptr;
return;
}
Node* temp = new Node();
temp = head;
for (int i = 1; i < pos - 1; i++) {
temp = temp->link;
}
ptr->link = temp->link;
temp->link = ptr;
return;
}
Node* getmid(Node* temp)
{
if (temp == 0) {
return temp;
}
Node* a = temp;
Node* b = temp;
while (b->link != 0) {
b = b->link;
a = a->link;
if (b->link != 0) {
b = b->link;
}
else {
break;
}
}
return a;
}
Node* merge(Node* left, Node* right)
{
Node* res = NULL;
if (left == NULL || right == NULL) {
if (left == NULL) {
return right;
}
if (right == NULL) {
return left;
}
else {
return NULL;
}
}
else if ((left->data) < (right->data)) {
res = left;
res->link = merge(left->link, right);
}
else {
res = right;
res->link = merge(left, right->link);
}
return res;
}
Node* mergesort(Node* head)
{
if (head == NULL || (head->link) == NULL) {
return head;
}
Node* mid = getmid(head);
Node* left = head;
Node* right = mid->link;
mid->link = NULL;
left = mergesort(left);
right = mergesort(right);
Node* c = merge(left, right);
return c;
}
void display()
{
Node* t = head;
while (t != 0) {
cout << t->data << " ";
t = t->link;
}
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
ins(8, 1);
ins(23, 2);
ins(73, 3);
ins(4, 4);
ins(5, 5);
ins(7, 6);
mergesort(head);
display();
}
In the above code, ins function is used to insert the node. getmid function is used for getting the address of the middle node. merge function is used to sort the left and right half. And mergesort function is used to divide the list into 2 half and call each half.display function is used for print linked list
Maybe this can help you.
#include <bits/stdc++.h>
using namespace std;
/* Link list node */
class Node {
public:
int data;
Node* next;
};
/* function prototypes */
Node* SortedMerge(Node* a, Node* b);
void FrontBackSplit(Node* source,
Node** frontRef, Node** backRef);
/* sorts the linked list by changing next pointers (not data) */
void MergeSort(Node** headRef)
{
Node* head = *headRef;
Node* a;
Node* b;
/* Base case -- length 0 or 1 */
if ((head == NULL) || (head->next == NULL)) {
return;
}
/* Split head into 'a' and 'b' sublists */
FrontBackSplit(head, &a, &b);
/* Recursively sort the sublists */
MergeSort(&a);
MergeSort(&b);
/* answer = merge the two sorted lists together */
*headRef = SortedMerge(a, b);
}
/* See https:// www.geeksforgeeks.org/?p=3622 for details of this
function */
Node* SortedMerge(Node* a, Node* b)
{
Node* result = NULL;
/* Base cases */
if (a == NULL)
return (b);
else if (b == NULL)
return (a);
/* Pick either a or b, and recur */
if (a->data <= b->data) {
result = a;
result->next = SortedMerge(a->next, b);
}
else {
result = b;
result->next = SortedMerge(a, b->next);
}
return (result);
}
/* UTILITY FUNCTIONS */
/* Split the nodes of the given list into front and back halves,
and return the two lists using the reference parameters.
If the length is odd, the extra node should go in the front list.
Uses the fast/slow pointer strategy. */
void FrontBackSplit(Node* source,
Node** frontRef, Node** backRef)
{
Node* fast;
Node* slow;
slow = source;
fast = source->next;
/* Advance 'fast' two nodes, and advance 'slow' one node */
while (fast != NULL) {
fast = fast->next;
if (fast != NULL) {
slow = slow->next;
fast = fast->next;
}
}
/* 'slow' is before the midpoint in the list, so split it in two
at that point. */
*frontRef = source;
*backRef = slow->next;
slow->next = NULL;
}
/* Function to print nodes in a given linked list */
void printList(Node* node)
{
while (node != NULL) {
cout << node->data << " ";
node = node->next;
}
}
/* Function to insert a node at the beginging of the linked list */
void push(Node** head_ref, int new_data)
{
/* allocate node */
Node* new_node = new Node();
/* put in the data */
new_node->data = new_data;
/* link the old list off the new node */
new_node->next = (*head_ref);
/* move the head to point to the new node */
(*head_ref) = new_node;
}
/* Driver program to test above functions*/
int main()
{
/* Start with the empty list */
Node* res = NULL;
Node* a = NULL;
/* Let us create a unsorted linked lists to test the functions
Created lists shall be a: 2->3->20->5->10->15 */
push(&a, 15);
push(&a, 10);
push(&a, 5);
push(&a, 20);
push(&a, 3);
push(&a, 2);
/* Sort the above created Linked List */
MergeSort(&a);
cout << "Sorted Linked List is: \n";
printList(a);
return 0;
}
The issue was in getmid()
For lists of 2 elements it returned the second element, instead of the first.
Therefore with 2 elements left always pointed to the whole list, causing an infinite recursion.
Node* getmid(Node* temp)
{
if (temp == nullptr)
return nullptr;
if (temp->link == nullptr)
return temp;
Node* a = temp;
//---------------------------------
Node* b = temp->link; // Here!
//---------------------------------
while (b->link != nullptr) {
b = b->link;
a = a->link;
if (b->link != nullptr)
b = b->link;
else
break;
}
return a;
}
You can run it here: https://onlinegdb.com/3fhqQyBhy
I'm using nullptr instead of NULL or 0 and I'm passing the list as a parameter to the insert function
when I tried to implement a linked list in visual studio 2019 using c it produces heap error.
It was due to the free function.
However, the code works fine on online compilers which use the GCC compiler. https://www.jdoodle.com/c-online-compiler/
I can't able to figure it out..........................
here is the code:
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* head = NULL;
void append(int data)
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node*));
(*newNode).data = data;
(*newNode).next = NULL;
if (head == NULL)
{
head = newNode;
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
}
void insertAt(int position, int data)
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node*));
newNode->data = data;
newNode->next = NULL;
if (position == 1)
{
newNode->next = head;
head = newNode;
return;
}
struct Node* temp = head;
for (int i = 1; i < position - 1; i++)
{
temp = temp->next;
}
newNode->next = temp->next;
temp->next = newNode;
}
void deleteAt(int position)
{
struct Node* temp = NULL;
if (position == 1)
{
temp = head;
head = temp->next;
free(temp);
return;
}
struct Node* tempHead = head;
for (int i = 1; i < position - 1; i++)
{
tempHead = tempHead->next;
}
temp = tempHead->next;
tempHead->next = temp->next;
free(temp);
}
void print()
{
struct Node* temp = head;
while (temp != NULL)
{
printf("%d\n", temp->data);
temp = temp->next;
}
}
void main()
{
append(3);
append(4);
append(5);
append(6);
insertAt(3, 20);
insertAt(4, 50);
insertAt(2, 70);
deleteAt(4);
deleteAt(3);
print();
}
The sizes you're passing to malloc are wrong. You should pass sizeof(struct Node).
If you're compiling this as C++ you shouldn't be using malloc at all.
As #1201ProgramAlarm answered, the allocation size is wrong. sizeof(struct Node*) is the size of a pointer, not the size of the struct.
Instead of trying to match the type, use the size of the referenced data. Easy to code right, review and maintian.
Cast not needed in C.
// struct Node* newNode = (struct Node*)malloc(sizeof(struct Node*));
// instead...
// ptr = malloc(sizeof *ptr * N);
struct Node* newNode = malloc(sizeof *newNode);
Just try this code once down there .This code is written by me according to my understanding and still if u have any issue with the code you can further ask me .You can try this code out or just cross check it with your's.
Code:
Linked List:
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
}*first=NULL;
void create(int A[],int n)
{
int i;
struct Node *t,*last;
first=(struct Node *)malloc(sizeof(struct Node));
first->data=A[0];
first->next=NULL;
last=first;
for(i=1;i<n;i++)
{
t=(struct Node*)malloc(sizeof(struct Node));
t->data=A[i];
t->next=NULL;
last->next=t;
last=t;
}
}
void Display(struct Node *p)
{
while(p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
}
void RDisplay(struct Node *p)
{
if(p!=NULL)
{
RDisplay(p->next);
printf("%d ",p->data);
}
}
int Delete(struct Node *p,int index)
{
struct Node *q=NULL;
int x=-1,i;
if(index < 1 || index > count(p))
return -1;
if(index==1)
{
q=first;
x=first->data;
first=first->next;
free(q);
return x;
}
else
{
for(i=0;i<index-1;i++)
{
q=p;
p=p->next;
}
q->next=p->next;
x=p->data;
free(p);
return x;
}
}
int main()
{
int A[]={10,20,30,40,50};
create(A,5);
printf(“%d\n",Delete(first),2);
Display(first);
return 0;
}
In main function you can pass the function created int the program and also pass the argument according to you.
I have written two functions to insert nodes in a Linked List. While one function (insertNth) updates the head pointer, the second one (sortedInsert) does not update the head pointer across function calls. The push function is taking a reference to the head pointer.
struct node
{
int data;
node *next;
};
void printList(node *head)
{
node *current = head;
while(current!=NULL)
{
cout<<current->data<<" ";
current = current->next;
}
}
void push(node* &head, int data)
{
node *newNode = new node();
newNode->data = data;
newNode->next = head;
head = newNode;
}
void insertNth(node *&head, int index, int val)
{
node *current = head;
int cnt = 0;
while(current!=NULL)
{
if(cnt == index)
{
if(cnt==0)
{
push(head, val);
}
else
{
push(current->next, val);
}
}
current=current->next;
cnt++;
}
}
void sortedInsert(node *head, int val)
{
node *current = head;
if(head != NULL && val < head->data)
{
node *newNode = new node();
push(head,val);
return;
}
while(current!=NULL)
{
if(current->data < val && current->next->data > val)
{
push(current->next, val);
return;
}
current = current->next;
}
}
int main()
{
node *head;
push(head, 3);
cout<<"\n";
printList(head);
cout<<"\nInsertNth: ";
insertNth(head,0, 2);
printList(head);
cout<<"\nsortedInsert: ";
sortedInsert(head, 1);
printList(head);
return 0;
}
I'm getting following as output:
3
InsertNth: 2 3
sortedInsert: 2 3
Why is the third line not printing 1 2 3?
//
Update
//
The correct SortedInsert is as follows:
void sortedInsert(node *&head, node *newNode)
{
node *current = head;
if(head == NULL || newNode->data < head->data)
{
newNode->next = head;
head = newNode;
return;
}
while(current!=NULL && current->next != NULL)
{
if(current->data < newNode->data && current->next->data > newNode->data)
{
newNode->next = current->next;
current->next = newNode;
return;
}
current = current->next;
}
if(current->next == NULL)
{
current->next = newNode;
newNode->next = NULL;
}
}
A sample was requested. Note that I did it as a template, but you could skip the template business and instead of a T* you can use struct node *. It's not general purpose, but might be easier to understand.
template <class T>
class MyLinkedList {
class Entry {
public:
Entry * previous;
Entry * next;
T * node;
}
Entry * head;
Entry * tail;
void push(T * nodeToPush) { pushBefore(head, nodeToPush); }
void insertNth(int whereToInsert, T * nodeToInsert) {
... find the nth Entry pointer
pushBefore(head, nodeToPush);
}
private:
void pushBefore(Entry *entry, T * nodeToPush) {
Entry *newEntry = new Entry();
newEntry->node = nodeToPush;
if (entry != NULL) {
newEntry->previous = entry->previous;
}
newEntry->next = entry;
entry->previous = newEntry;
if (head == entry) {
head = newEntry;
}
if (tail == NULL) {
tail = newEntry;
}
}
// Other methods as necessary, such as append, etc.
}
Other than passing in a pointer to the objects you're inserting into your linked list, at no point do you have to pass pointers around in a fashion where your methods are also performing side effects on those pointer. The class should know how to manage a class, and no weird passing of variables all over.
Performing side effects on your arguments should be done with GREAT caution. If you're passing an object to a method, then it's fair to manipulate the object. But I really don't like passing pointers and having methods modify the pointers themselves.
That IS going to lead to (at best) confusion.
Note: I did NOT test this code. It might not quite be perfect.
Can anyone explain to me why the delete_min() function does not work? I have been trying to figure this out for hours now.
I have tried different variations of the function, but it seems to either delete the wrong thing, only alter data and not delete it, or delete the wrong thing. Also, the insertion function doesn't always work properly after delete_min() is called, so I'm sure this has something to do with pointers.
Here is the function:
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
delete node;
node = NULL;
return 0;
}
And here is a compilable example:
#ifndef _TREE_H_
#define _TREE_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TreeNode {
int val;
char *str;
TreeNode *left;
TreeNode *right;
};
class BinaryTree {
public:
BinaryTree();
~BinaryTree();
int insert_node(unsigned int val, char *str);
TreeNode *find_min();
int delete_min();
void print();
private:
int insert_node_helper(TreeNode *&node, unsigned int val, char *str);
int delete_min_helper(TreeNode *node);
void print_helper(TreeNode *node);
TreeNode *root;
};
#endif
BinaryTree::BinaryTree(){
this->root = NULL;
}
BinaryTree::~BinaryTree(){
}
int BinaryTree::insert_node(unsigned int val, char *str){
return insert_node_helper(this->root, val, str);
}
int BinaryTree::insert_node_helper(TreeNode *&node, unsigned int val, char *str){
if(node == NULL){
node = new TreeNode;
node->val = val;
node->str = strdup(str);
node->left = NULL;
node->right = NULL;
if(node != NULL){
return 0;
}else{
puts("inserted null node");
return 1;
}
}else if(val <= node->val){
return insert_node_helper(node->left, val, str);
}else if(val > node->val){
return insert_node_helper(node->right, val, str);
}
return 1;
}
void BinaryTree::print(){
print_helper(this->root);
}
void BinaryTree::print_helper(TreeNode *node){
if(node != NULL){
print_helper(node->right);
printf("%d occurrences of \"%s\"\n", node->val, node->str);
print_helper(node->left);
}
}
TreeNode *BinaryTree::find_min(){
TreeNode *temp = this->root;
while(temp->left != NULL){
temp = temp->left;
}
return temp;
}
int BinaryTree::delete_min(){
return delete_min_helper(root);
}
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
delete node;
node = NULL;
return 0;
}
#include <time.h>
int main(){
BinaryTree bt;
srand(time(NULL));
for(int i = 0; i < 10; i++){
bt.insert_node(rand() % 20, "test");
}
bt.print();
printf("min val = %d\n", bt.find_min()->val);
puts("#################################");
bt.delete_min();
bt.print();
printf("min val = %d\n", bt.find_min()->val);
return 0;
}
Two things come to mind right away:
a parent node is still pointing at the node which you deleted
the node you're deleting might have a node->right pointer hanging off of it, which you are lopping off the tree, instead of re-hooking it back into the tree.
There might be more problems, but you need to address these issues at a bare minimum.
Reference from his parent still point to some address in memory.
Try this:
int BinaryTree::delete_min_helper(TreeNode *node){
TreeNode *prev = NULL;
while(node->left != NULL){
prev = node;
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
prev->left = NULL;
delete node;
node = NULL;
return 0;
}