I have to sort my linked list in specific way for example I have such linked list: 3->1->4->1->7->5->7 it has to be sorted like that: 7->1->7->1->3->4->5. So from what I understand I have to find for example max element in list and add it at beginning and delete such element from current list, I'm curious if is there any way to for example swap place of element in middle of list to beginning of list. Or I have to delete such element and allocate memory with given data and add it at beginning? Overall it's task from programming class test and has to be done using singly linked lists.
Here's one approach: given an unsorted list P:
Create a list Q.
Append max(P) to Q.
Remove max(P) from P.
Append min(P) to Q.
Remove min(P) from P.
Repeat steps 2 through 5 one more time.
Sort P in ascending order.
Append P to Q.
Q is now your sorted list.
This approach assumes P's length is no less than 4.
You can swap nodes in singly-linked lists if you have pointers to the list's node pointers, i.e the head pointer and the nodes' next pointers. You can do that because you have to walk the list anyway to find the maximum. (Be careful to chose the last maximum, otherwise subseqnet calls to this function will alwas bring the same 7 to the front.)
void max_to_front(struct node **head)
{
struct node **mp = head; // current max pointer address
struct node **pv = head; // iterator pointer address
struct node *nd = *head; // iterator pointer
int mx;
// nothing to do for empty or single-node lists
if (*head == NULL || (*head)->next == NULL) return;
// find maximum
mx = nd->value;
while (nd) {
if (nd->value >= mx) {
mp = pv;
mx = nd->value;
}
pv = &nd->next;
nd = nd->next;
}
// move max to the front, i.e. update the pointers
nd = *mp;
*mp = (*mp)->next;
nd->next = *head;
*head = nd;
}
Note how pv always contains the address of the node pointer via which you have come to the current node, nd, so you can update it; mp is just the pointer address for the node with the maximum value.
Related
I got a list of string data. 10,20,30 are the line numbers
10. string 1
20. string 2
30. string 3
and if user types in "23 string data". 23 is the line number user wants to insert into. The data should become like that
10. string 1
20. string 2
23. string data
30. string 3
and if user types in "40 string data". The data should become like that
10. string 1
20. string 2
23. string data
30. string 3
40. string data
I'm relatively new in C's data structure. Which data structure should I use to store this kind of data efficiently? My current direction is to implement dynamic array or linked list. However, below are the list of problems I experienced.
Problem with dynamic array:
Using the line number as the index and create sufficient space to
ensure array length is always more or equal to the highest line
number. Waste a lot of memory for un-used index.
Printing out the data would be an issue.
Eg. index 0-9 doesn't have memory allocated. Accessing it will cause an error. Need to find ways to know which index are used?
Problem with linked list:
I cannot jump index(not sure). How do identify which line comes after
another and insert line in between easily?
Let's assume the following about your requirements:
No strong real time. (I.e. it's not for high frequency trading, or controlling machinery.)
It runs on a relatively contemporary PC (RAM measured in GB, CPU frequency in GHz). In particular it does not run on an embedded system.
The data is no more than a few ten thousand lines.
Then you can use almost any data structure you like; it won't matter with respect to memory or run time behavior.
For example, in order to find the point of insertion in a linked list, just iterate that list. PCs are fast enough to iterate tens of thousands of times before you finished blinking.
Or just allocate an array of 100,000 lines of 80 characters each. No problem whatsoever. Or of a million lines. Still no problem. Or of 10 million lines, still no problem. You see my point? (In an array you'll need a marker to mark unused lines. I would use a struct line { bool used; char text[80]; } or the like. You can also cater to arbitrarily long lines — and save memory — by having just a char *text member and allocating dynamically, or defining the text as a linked list of chunks.)
The choice therefore boils down to what's easiest for you to use. Could be the array.
I'll give the two solutions I could come up with, but this question is possibly open-ended.
Use a hash table. Keys are line numbers. Values are (string, pointer to next line's value). This makes both random and linear access fast. Edit: Insertion is still O(n) with this. It'll only help with access time, which will be O(1). The second solution has O(1) insertion.
Assuming you don't have wildly spaced out line numbers: Use a singly linked list L to store strings. Also create a separate array P containing a pointer to every k-th node in the list. To access line i, check P[floor(i/k)], jump to the node it points to in L, and jump forward i mod k times to reach your string. Access time is therefore O(k). Insertion time is O(1). Space usage for n strings is O(n + max{i}/k).
The one thing that makes this relevant to C... is that there's no built-in hash table, of course! So #2 may be easier to implement.
I know you're looking for a specialized data structure, but how about instead using a simple data structure but sorting it lazily? You could append new lines to a dynamic array and then sort the array (with qsort) when you need to print them.
I think that this would be better because printing all lines is probably done much less frequently than adding/inserting lines. Therefore you should make adding lines cheap (in this case, O(1) amortized), and printing can be more expensive (in this case, O(n log n)). This also keeps your data structures simple and lets the C standard library handle the complicated parts.
You could make this a bit better still by keeping a flag that tracks whether all of the data is already known to be sorted; that way repeatedly printing (or, presuming you're trying to write a BASIC interpreter, repeatedly running) will be cheap too. Such a flag also might be helpful if you expect that lines are usually entered in order; then as each line is added:
alreadySorted = alreadySorted && (new_line_number > last_line_number)
I'll note that you have not specified what happens if a line is added that reuses an existing line number. If you wish to replace the old line, then you could tweak this approach by using a stable sort and afterward iterating over the lines to remove lines with duplicate numbers, keeping only the last one.
(If you want to make qsort stable for this case, instead of storing just a string for each line, you could store some extra metadata with it (any monotonically increasing counter would do, such as the current time, or just the total number of lines at the time the line was added). Then the comparison function you give to qsort would just need to use that extra data to resolve ties from duplicate line numbers.)
One disadvantage to this approach is that removing lines either won't be fast or won't reclaim memory immediately. However, you haven't specified whether line removal is a requirement; even if it is, it is likely to be a rare operation (so being a bit more time-inefficient or a bit more space-inefficient might be acceptable).
The best solution for this task is to use dictionary data type.
Of course, depending on nature of keys (number of lines) you can perform optimization via appropriate hash table.
Of course, c library don't have implementation of dictionary. But you can create your own, based on red black tree. Cormen explained such data structure easily https://www.amazon.com/Introduction-Algorithms-3rd-MIT-Press/dp/0262033844
Note: if your collection has small size or you will rarely modify structure, then you can just use linked list.
My suggestion is to use linked list and insertion sort to insert whenever needed ,
Here is the code modified on originally taken from geeksforgeeks.org,
I haven't tested code , this is just modified code as taken from the site.
/* C program for insertion sort on a linked list */
#include<stdio.h>
#include<stdlib.h>
/* Link list node */
struct node
{
int lineNumber;
char *str;
struct node* next;
}node;
// Function to insert a given node in a sorted linked list
void sortedInsert(struct node**, struct node*);
// function to sort a singly linked list using insertion sort
void insertionSort(struct node **head_ref)
{
// Initialize sorted linked list
struct node *sorted = NULL;
// Traverse the given linked list and insert every
// node to sorted
struct node *current = *head_ref;
while (current != NULL)
{
// Store next for next iteration
struct node *next = current->next;
// insert current in sorted linked list
sortedInsert(&sorted, current);
// Update current
current = next;
}
// Update head_ref to point to sorted linked list
*head_ref = sorted;
}
/* function to insert a new_node in a list. Note that this
function expects a pointer to head_ref as this can modify the
head of the input linked list (similar to push())*/
void sortedInsert(struct node** head_ref, struct node* new_node)
{
struct node* current;
/* Special case for the head end */
if (*head_ref == NULL || (*head_ref)->lineNumber >= new_node->lineNumber)
{
new_node->next = *head_ref;
*head_ref = new_node;
}
else
{
/* Locate the node before the point of insertion */
current = *head_ref;
while (current->next!=NULL &&
current->next->lineNumber < new_node->lineNumber)
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
/* BELOW FUNCTIONS ARE JUST UTILITY TO TEST sortedInsert */
/* Function to print linked list */
void printList(struct node *head)
{
struct node *temp = head;
while(temp != NULL)
{
printf("%d %s \n", temp->lineNumber,temp->str);
temp = temp->next;
}
}
/* A utility function to insert a node at the beginning of linked list */
void push(struct node** head_ref, int new_data, char *line)
{
/* allocate node */
struct node* new_node = (struct node *)malloc(sizeof(struct node));
int len = strlen(line)+1;
/* put in the data */
new_node->lineNumber = new_data;
new_node->str = malloc(len);
strcpy(new_node->str,line);
new_node->str[len] = '\0';
/* 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(int argc,char *argv[])
{
struct node *a = NULL;
push(&a, 5 , "TestLine");
push(&a, 1 , "SecondTest");
push(&a, 1 , "SecondTest");
push(&a, 3 , "SecondTest");
insertionSort(&a);
printf("\nLinked List after sorting \n");
printList(a);
return 0;
}
I' d advice you to use linked list.
// Define your list like this
typedef struct node {
int line; // To hold the line number
char * data;
struct node * next;
} node_t;
// To insert
node_t* insert(node_t *head, const char * data, int line) // n is line from beginning
{
// Node to be inserted in given line
node_t *newNode;
// Allocating Memory
newNode = malloc(sizeof(node_t));
// Filling the Data to New Node
newNode->data = malloc(strlen(data)+1); // Allocate memory to store data
strcpy(newNode->data, data);
newNode->line = line;
newNode->next = NULL;
// It might be our First Node in Linked List
if(head == NULL) {
//Address of New Node Becomes our head
return (head = newNode);
}
// Node Might be inserted At Head
else if(line == 0) {
// Joining previous Linked List After new Node
newNode->next = head;
// Address of New Node Becomes our head
return (head = newNode);
}
// Inserting At the line next to line
else {
// Pointer to store intermediate address of node
// To be used in Traversing
node_t * current = head;
// Go through to insert at Nth line
while(current != NULL) {
node_t * next = current->next; //The next Node
if((line >= current->line && line < next->line) || (line >= current->line && NULL == next->line)) { // Test if we are at some point between current line and next line or if there is no next
// If we are, point newNode to the next node of current
newNode->next = current->next;
// Now point current towards our New Node
current->next = newNode;
// Return Head as soon as we have inserted our new node
return head;
}
current = next; // Point current to the next node to continue
}
}
}
If there's guarantee that the line numbers will always be greater, you could also store a pointer to the node with greatest line number in every node. This will increase space but achieve the result in n(0) time.
I'm trying to just reverse a singly linked list, but with a bit of a twist. Rather than having the pointer to the next node be the actual next node, it points to the pointer in that next node.
struct _Node
{
union
{
int n;
char c;
} val;
void *ptr; /* points to ptr variable in next node, not beginning */
int var;
};
typedef struct _Node Node;
I know how to reverse a normal singly linked list and I think I have the general idea of how to go about solving this one, but I'm getting a segfault when I'm trying to access head->ptrand I don't know why.
Node *reverse(Node *head)
{
Node * temp;
Node * prev = NULL;
while(head != NULL)
{
temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
head->ptr = prev;
prev = head;
head = temp;
}
return prev;
}
Even if I try and access head->ptr without adding 4, I get a segfault.
The driver that I have for this code is only an object file, so I can't see how things are being called or anything of the sort. I'm either missing something blatantly obvious or there is an issue in the driver.
First, I'll show you a major problem in your code:
while (head) // is shorter than while(head != NULL)
{
// Where does the 4 come from?
// And even if: You have to substract it.
// so, definitively a bug:
// temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
size_t offset_ptr = (char*)head->ptr - (char*)head;
// the line above should be moved out of the while loop.
temp = head->ptr - offset_ptr;
Anyways, your algorithm probably won't work as written. If you want to reverse stuff, you are gonna have to work backwards (which is non-trivial in single linked lists). There are two options:
count the elements, allocate an array, remember the pointers in that array and then reassign the next pointers.
create a temporary double linked list (actually you only need another single reversely linked list, because both lists together form a double linked list). Then walk again to copy the next pointer from your temporary list to the old list. Remember to free the temporary list prior to returning.
I tried your code and did some tweaking, well in my opinion your code had some logical error. Your pointers were overwritten again and again (jumping from one node to another and back: 1->2 , 2->1) which were leading to suspected memory leaks. Here, a working version of your code...
Node *reverse(Node *head)
{
Node *temp = 0;
//Re-ordering of your assignment statements
while (head) //No need for explicit head != NULL
{
//Here this line ensures that pointers are not overwritten
Node *next = (Node *)head->ptr; //Type casting from void * to Node *
head->ptr = temp;
temp = head;
head = next;
}
return temp;
}
Hi guys I'm new to linked lists, but I'm pretty sure that I know how they work, theoretically, I think I might having a syntax misunderstanding, or memory management error.
edit: My main purpose is to make a collection of lists which are indexed by the array, each array element is a head(or root) node. I'm having issues allocation dynamically this struct array.
What I'm doing is the following:
typedef struct item_list{
int item_name;
int item_supplier;
int item_price;
struct item_list *next
}node;
int i;
node ** shop_1 = (node **)malloc(shop_items_elements * sizeof(node));
for (i=0;i<=shop_items_elements;i++)
{
shop_1[i]->next=NULL;
}
I'm getting a segmentation fault while I try to give next at the element i the value of NULL.
The problem is that you are trying to allocate the memory for 20000 items as a contiguous block. Which implies that you actually haven't understood linked lists yet.
I think you are mixing up random access array functionality with pure linked lists which do not allow accessing individual items without traversing the list.
A linked list usually has a head and tail node which are initially NULL when there are no elements in the list:
node* head = NULL;
node* tail = NULL;
When adding a new node you first allocate it by using malloc with the size of a single node struct:
node* the_new_node = (node*)malloc(sizeof(node));
Initialize the struct members, specifically set next to NULL for each new node. Then use this append_node() function to append the node to the linked list:
void append_node(node** head, node** tail, node* the_new_node)
{
if(*tail == NULL)
{ // list was empty
*head = *tail = the_new_node;
}
else
{
(*tail)->next = the_new_node; // link previous tail node with new one
*tail = the_new_node; // set the tail pointer to the new node
}
Please note the pointer to pointers which are needed to update the head and tail pointers. Call the function like this for any given n you want to add:
append_node(&head, &tail, n);
Repeat this for every new node.
A much better way of encapsulating a linked list is putting the head and tail pointers into another struct
typedef struct linked_list
{
node* head;
node* tail;
} list;
and using an instance of that as first argument to append_node() (which I'll leave to you as an exercise ;)
When using such a linked list it is not possible to conveniently access the Nth node in less than O(n) since you have to follow all next pointers starting from the head node until you arrive at the Nth node.
EDIT: If you want to have the possibility to index the shop items and build a linked list from each of the elements I would suggest the following solution:
node** shop_1 = (node**)malloc(shop_items_elements * sizeof(node*));
int i;
for(i = 0; i < shop_items_elements; ++i)
{
node* n = (node*)malloc(sizeof(node));
n->next = NULL;
shop_1[i] = n;
}
You first allocate an array of pointers to node pointers which have to be allocated individually of course. Take a look at this diagram for reference:
The actual node instances may be larger than a pointer's size (unlike drawn in the diagram) which is the reason why you allocate N * sizeof(node*) in a block instead of N * sizeof(node).
Your code needs to look like this
int i;
node * shop_1 = (node *)malloc(shop_items_elements * sizeof(node));
for (i=0;i<shop_items_elements;++i)
{
shop_1[i].next=NULL;
}
Your malloc statement has allocated an array of nodes, not an array of pointers to nodes. (If that is what you wanted instead, then you would have had to initialize each pointer with a further malloc call before trying to assign a value to a field within the node pointed to.)
I need to make a program that has (at most) 50 linked lists. Basically, my program generates some messages and based on a indicator that comes in the front of my string, I need to put the message in the right linked list.
I don't know if it is clear enough but I will try to show part of my code (the important part). The function I made to add a new element (on the top) of the linked list is the following:
void InsertLL (News p, char M[]) {
char * text = malloc(strlen(M)+1);
strcpy(text, M);
News s,t;
t = malloc(sizeof(struct List));
t-> Text = text;
s = p;
p = t;
p-> next = s;
}
My struct List (the type of the elements of my lists) contains a char pointer (called text) and a pointer to the next element of the list.
Simulating my program, suppose that I received a message that needs to be put in the linked list where the begin is pointed by the pointer p[0]. So I create a new element (forget the case that the list is empty, I already made this one) and add in the top of my list using the function I've shown.
Now, suppose that I received another message that needs to be put in the next pointer p[1]. If I print p[0] -> Text, I get the text of p[1]->Text.
I mean, if I add a new element in the list pointed by p[i], all the previous texts p[i] -> Texts gets the new text of this new element. I have no idea what am I doing wrong.
I don't know if it is enough to help me, if more information is needed, just tell me.
Problem with your code is you are not maintaining the list of nodes. you are overwriting the same node again and again.
Algo:
If no node then create one.
If nodes are present then navigate to end node.(You can use tail
pointer for quicker access).
Append the node at the end.
Creating Node:
Declare a node type pointer.
Allocate memory to it.
Update the content of the node.
Set the next pointer of the node to null.
Add this node to end of the list.
ex. inserting node 3 in the list of node 1 and node 2.
This is the general approach that you can use
typedef struct node{
int val; //you can use your text here
struct node* next;
}NODE;
struct node* head=0;
int addNode(int v){
if(head==0){ //checking for empty node.
struct node* n=malloc(sizeof(NODE));
n->val=v;
head=n;
}
else{
struct node* temp=head;
while(temp->next != 0) //Navigating till end
{
temp=temp->next;
}
struct node* n=malloc(sizeof(NODE)); //allocating memory
n->val=v; //you need to use strcpy for string here.
temp->next=n; //adjusting pointers
n->next=0;
}
}
You can check this demo created by me for Double linked List http://ideone.com/s6TtUX
typedef struct Node
{
int data;
Node *next;
Node *other;
};
Node *pHead;
pHead is a singly linked list. The next field points to the next element in the list. The other field may point to any other element (could be one of the previous nodes or one of the nodes ahead) in the list or NULL.
How does one write a copy function that duplicates the linked list and its connectivity? None of the elements (next and other) in the new list should point to any element in the old list.
Create a new node for every node in the old list, copy the corresponding data and make the next pointer of the nodes in the new list point to their successor in the new list, forgetting the other pointer for time being. At the time of creating a new node remember the mapping of node address something like:
Old_list New_list
-------------------
0x123 0x345 [ addresses of the first node]
0xabc 0xdef [ addresses of the second node]
...
In the second pass pass for every node in the new list consider its other pointer and find its corresponding node in the new list from the map and use it as the other pointer of this node (node in the new list).
Came across this. Hope it helps!
Citing one solution from this link, below.
1) Create the copy of 1 and insert it between 1 & 2, create the copy of 2 and insert it between 2 & 3.. Continue in this fashion, add the copy of N to Nth node
2) Now copy the arbitrary link in this fashion
if original->arbitrary is not NULL
original->next->arbitrary = original->arbitrary->next; /*TRAVERSE TWO NODES*/
else
original->next->arbitrary=NULL;
This works because original->next is nothing but copy of original and Original->arbitrary->next is nothing but copy of arbitrary.
3) Now restore the original and copy linked lists in this fashion in a single loop.
original->next = original->next->next;
copy->next = copy->next->next;
4) Make sure that last element of original->next is NULL.
Sample code, Time Complexity O(N), Space Complexity O(1)
pNode copy_list(pNode head) {
// pre-condition: node->other either points into the list or NULL
if (!head) return NULL;
pNode node = head, copied = NULL, cnode = NULL;
for ( ; node; node = node->next->next) {
// make copy
cnode = newnode(node->next, node->data);
cnode->other = node->other;
if (node == head)
copied = cnode;
// insert the copy between originals
node->next = cnode;
// node -> cnode -> (orig)node->next
}
for (node = head; node && node->next;
node = node->next->next /* only original nodes */)
if (node->other)
node->next->other = node->other->next;
else
node->next->other = NULL;
// restore lists
node = head; cnode = copied;
for ( ; cnode && cnode->next; node = node->next, cnode = cnode->next) {
node->next = node->next->next;
cnode->next = cnode->next->next;
}
node->next = NULL;
return copied;
}
Complete program is at http://gist.github.com/349630
I like the solution of Codaddict, but this would be my answer:
iterate over the linked list.
a. store the data in an array (position i for the i'th node of course)
b. replace data with i to create an id (this way you'll definitely know which node you are talking about)
create the 2nd linked list the size of the first (ignore the other pointer for now)
*. maybe use a temporary array to find each node quickly
iterate over the first linked list.
a. find out which id other points to (which is in that nodes data)
b. recreate this link in the 2nd linked list (the temporary array could really help here)
iterate over both linked list simultaneously and replace the ids in data with the stored data
Of course you could collapse some processing and iterating here. But this would roughly be what I would do/think of.