I am trying to make a program for Santa! My knowledge is limited; I am lost in pointers and loops etc, I've been thinking this for a couple hours.
I have an array of pointers to singly linked lists. Each array index indicates a list of children in age groups 0: 0-3, 1: 4-7, 2: 8-11, 3: 11 - 15.
Each child is a struct, now after each year I want to go through all the lists, increment their age by 1, and if they need to change an age group, I have to move the node to the appropriate list that holds that age group. If although the child gets over the age of 15 then it has to delete the node instead. My code is incomplete because I am new to linked lists and I got confused.
My main issue is that I make changes to the lists as I move through them, so if I check for the first node and I remove it, I have to check the first node again because now its a new one, so I keep checking until the Head is okay, is this the right approach? I am not sure my code works, I cant test it yet.
the part from my Santa_Claus.h:
/*Structure defining a node of the children list*/
struct child {
int cid; /*The identifier of the child.*/
int age; /*The age of the child.*/
int did; /*The identifier of the child.*/
int present_choices[M]; /*The array in which the preferences of the child for presents are stored*/
struct child *next; /* Singly-linked, sorted by id */
};
the part from Santa_Claus.c
#define N 4 /*Number of children's age categories*/
struct child *Age_categories[N];
int new_season(void) {
int i;
struct child *childP = NULL;
struct child *prev = NULL;
struct child *childptr = NULL;
int MaxAges[N] = {3,7,11.15};
//Increment Age Loop
for(i = 0; i < N; i++){
childP = Age_categories[i];
while(childP != NULL){
childP->age = childP->age + 1;
childP = childP->next;
}
}
//Remove or Move Loop
for(i = 0; i < N; i++){
childP = Age_categories[i];
//while the first is still > than the max age of this category
while(childP->age > MaxAges[i]){
if(i != (N-1)){
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
childP = childP->next;
}
prev = Age_categories[i];
childP = prev->next;
while(childP != Null){
if(childP->age > MaxAges[i]){
if(i != (N-1)){
prev->next = childP->next;
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
}
prev = childP;
childP = childP->next;
}
}
return 1;
}
My main issue is that I make changes to the lists as I move through them, so if I check for the first node and I remove it, I have to check the first node again because now its a new one, so I keep checking until the Head is okay, is this the right approach?
In place edits will bite you at the edge-cases. You'll have problems with the head and the tail nodes. If you remove the head, then whatever was keeping track of your head now points to nullspace.
In short, you just have to be careful. There are a lot of ways to screw it up. And for newbies, I'd suggest you stay away from pointers in C. Powerful, but a pain in the ass. Stick to fixed arrays unless you really need this thing to scale.
You don't need to re-check the head so much as have a special case to check the head, and do head-safe edits. Also checking if the set is empty, and checking the tail are typically good ideas. You can try and figure out something clever to avoid that sort of thing and have smooth code that handles it all... But clever code often bites you in the ass just as much.
My main issue is that I make changes to the lists as I move through
them, so if I check for the first node and I remove it, I have to
check the first node again because now its a new one, so I keep
checking until the Head is okay, is this the right approach?
In principle it's the right approach, but you have to do that not only for the head, but for every node in each list. Your code works not in all cases, e. g.
//while the first is still > than the max age of this category
while(childP->age > MaxAges[i]){
if(i != (N-1)){
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
childP = childP->next;
}
constitutes an endless loop if let's say the head child in age group 0 has become 4 years old.
Consider this correct code, which is shorter and therefore gives less opportunity for mistakes:
// Remove or Move Loop
for (i = 0; i < N; i++)
{
struct child **chPA; // address of pointer to node under check
// for each node in age group i
for (chPA = &Age_categories[i]; childP = *chPA; )
if (childP->age > MaxAges[i])
{
*chPA = childP->next; // remove node from this list
if (i != N-1)
{
struct child **chPA; // address of pointer to node in next group
// find where in next group to insert the aged node
for (chPA = &Age_categories[i+1]; childptr = *chPA;
chPA = &childptr->next)
if (childptr->cid > childP->cid) break;
(*chPA = childP)->next = childptr;
}
else
free(childP); // delete the grown out node
}
else
chPA = &childP->next; // proceed to next node
}
(It inserts a node in the next higher age group according to its cid because of your code comment
struct child *next; /* Singly-linked, sorted by id */
Related
I want to implement a droplist function that creates a new list with the elements of the input list exept the first n elements. My approach his to go trough the list and increment the counter so I know at which node I'am and start creating the new list at that node. Whats the issue here?
// Create a list consisting of nodes of list, except the first n.
Node* drop_list(Node* list, int n) {
Node* result;
int counter = 0; //sets counter to 0
if (counter == n ){
Node* result = new_node(list->value, NULL); //creates the head of the note starting at the nth
node
for (Node* ntf = result; list->next != NULL; ntf = ntf->next) {
list = list->next; //updates list->value
ntf->next = new_node(list->value, NULL); //creates the rest of the new list
}
return result;
}
else {
list = list->next; //sets list value to the next node
counter++; //increments counter
}
}
If counter < n, you will never enter the part where you copy the nodes. Note that, though you increase counter in the else-part, you leave the function afterwards.
A simple solution would be to call drop_list recursively in the else-part, e.g. like
}
else {
drop_list(list->next, n-1);
}
Note that you'll have to check corner cases like list being NULL, n being greater than the number of nodes in the list and so on. I leave this up to you.
Generally I'd prefer a solution where you move to the node in question with a loop and then start copying.
BTW: Instead of copying nodes before dropping them afterwards you could simply return the n'th node as new head of the list, could you?
The issue here is if n will be greater than zero then it will never enter if (counter == n). It will go to the else block and after doing stuff written there in the else block, it will simply return.
I am trying to solve the Josephus problem using a linked list - https://en.wikipedia.org/wiki/Josephus_problem. Basically we have a specified number of people - let's say 6 with k being 3. I start counting from the start of the line/ circle of people and when k is reached, that person is eliminated. I then move onto the next person and k is reset and increased until the next person is reached. We do this until one person is remaining. Note - when counting people, we skip people that have already been eliminated. So in my example of 6 people and k = 3, the first person to be eliminated would be person 3 (or in array terms, index 2), with the last person to survive being person 1 (or index = 0).
I have already solved this problem using an array and for loops. However, now I want to try to solve using a linked list as that is what I am learning. I have already created my linked list. I think the logic here is the same, count through the nodes of the linked list and eliminate the node when k is reached and so on. My problem is that I don't know how to traverse through the linked list multiple times and how to successfully eliminate until there is one (node)remaining.
Firstly, creating the linkedlist with the specified number of people (valn):
int counter = 0;
ListNode * head = NULL;
head = malloc(sizeof(ListNode));
head -> value = counter;
ListNode *current = &head;
while ( valn - 1 > counter)
{
current -> next = malloc(sizeof*(current -> next));
current = current -> next;
current -> value = counter;
current -> next = NULL;
counter++;
}
Head is then returned to main (which is already provided so I am certain it is correct)
I then have a function that is supposed to count through the linked list and figure out which one to eliminate.
This is what I have so far (with valk being the number to eliminate), head and valkn are passed into this function (which is void):
int counter = 0;
ListNode * p = head;
while ( p != NULL)
{
if (counter == valk)
{
ListNode * todelete = p;
printListNode (todelete); // Print function that was already provided (already correct) - basically print the value of the node to be eliminated.
deleteNode(head,todelete); // passed on to another function to delete - not sure how to delete yet
counter = 0;
}
p = p -> next;
counter++;
}
The header file (all the relevant libraries are included. Header file was also given):
typedef struct node {
int value;
struct node * next;
} ListNode;
void printListNode(ListNode * head);
ListNode * createList(int valn);
void eliminate(ListNode * head, int valk);
ListNode * deleteNode(ListNode * head, ListNode * todelete);
Expected results are as described above, if my inputs are 6 and 3, I go through the linked list until the 1st (with value 0) node is remaining.
struct hashLink
{
KeyType key; /*the key is what you use to look up a hashLink*/
ValueType value; /*the value stored with the hashLink, an int in our case*/
struct hashLink *next; /*notice how these are like linked list nodes*/
};
struct hashMap
{
hashLink ** table; /*array of pointers to hashLinks*/
int tableSize; /*number of buckets in the table*/
int count; /*number of hashLinks in the table*/
};
I'm trying to iterate through a hashMap with hashLinks. Is this the correct approach? The hashLinks are in an array and may have more hashLinks linked to them in a linked list. I just do not understand how to work pointers to pointers. tableSize is the amount of elements in the array. At each array position there may be more hashLinks linked to the first there.
for(int i = 0; i < ht->tableSize; i ++)
{
hashLink *current;
if (ht->table[i] != 0)
{
current = ht->table[i];
while(current->next !=0)
{
hashLink *next;
next = current->next;
free(current->key);
free(current);
current = next;
}
free(current->key);
free(current);
}
else
{
continue;
}
counter++;
}
}
Yes, this does work, but you end up with a hashtable that contains dangling pointers. Also, as Joachim noted, it works as long as you assume that the values contained in the structs are sane, i.e., tableSize contains the number of entries in table and the hashLinks have been correctly allocated.
Your iteration through the links is fine and correclty frees all the hashLinks in the table. However, consider the state of ht after the iteration. You do not change the values of ht->table[i] at all, so after you leave the loop, the pointers will still be stored in the table. If you want to reuse the table, you should set the pointers to 0 when you do not need them anymore, i.e., add ht->table[i] = 0 somewhere after current = ht->table[i];.
If this method is part of the "destructor" of the table (i.e., some method like hashmap_delete(...)), then you can simply free the hashmap after you finished your iteration, i.e., add free(ht); after the for-loop.
Simplified:
for(int i=0; i < ht->tableSize; i++)
{
hashLink *current;
while (ht->table[i] != NULL) {
current = ht->table[i];
ht->table[i] = current->next;
free(current->key);
free(current);
}
}
It can be further simplified to only one loop, but that is left as an exercise to the reader ...
Note: as a side effect, this will set all the pointers in ht->table[] to NULL; which is good, since after freeing the linked lists they have become stale anyway.
i have a full linked list with data in it, what i want is to fill another linked list with the same data but with a condition, so let say that this is the linked list :
char cl;
int time;
int lng;
C 0 1
D 0 2
B 2 1
A 2 2
i wan to copy from this list to a new empty one but only if (time>0), so the new one will be like this :
B 2 1
A 2 2
i have tried this code but it doesn't work :
void insert(const node * n )// this where i put the pointer of the full node
{
node *head=n;
node *sec; // new node
sec=(node*)malloc(sizeof(node));
do{
if(head->time>0){
strcpy(sec->cl,head->cl);
sec->time= head->time;
sec->lng= head->lng;
head=head->next;
sec=sec->next;
}
else
head=head->next;
}while(head!=NULL);
print(sec) // this will print the new node
}
Help me please. Thank you
I combined all the suggestions from the comments as well as some additional fixes.
Here's the resulting code:
const node* insert(const node * head)
{
node *sec = NULL; // start of the new, second list
node *last = NULL; // points to the last inserted node
while(head!=NULL){
if(head->time > 0){
node* newsec=(node*)malloc(sizeof(node));
newsec->cl = head->cl;
newsec->time = head->time;
newsec->lng = head->lng;
newsec->next = NULL;
//Add the new node to the list:
if(last == NULL){ //This is the first element in the new list
sec = newsec;
}else{
last-> next = newsec;
}
last = newsec;
}
head=head->next;
}
print(sec); // this will print the new node
return sec;
}
Your mistakes:
Wrong memory allocation (You only allocated memory once)
strcpy is not needed (char's don't require a string-copy)
while must be at the beginning of the loop (your code would fail if the given list is empty)
missing semicolon
wrong concatination of the new list
wrong const-correctness (Missing const in node *head=n;)
the internal head-variable is not neccessary (And the parameter-naming n is also not ideal. If you name it "start"/"head"/"begin", the comment wouldn't be neccessary)
Another suggestion: Use an upper case name for your struct, since it makes it easier to distinguish types from variables (should be Node, not node).
Note that you might want to remove the const from the return value type.
// Note: a better function name might be: printNodesWithTime
void insert(const node * pOld )
{
// note: since nothing is done with the new linked list,
// there is no need to actually create it
while( NULL != pOld )
{
if(pPld->time > 0)
{
print(pOld) // this will print the node
}
// step to next old node
pOld = pOld->next;
} // end while
} // end function: insert
This is homework for my first class in c. It focuses on dynamic allocation in c, in the form of a bst.
I have to have a dynamically allocated BST, recursively implemented. I know that my traversal works correctly, and am having trouble inserting nodes. I only ever have the root node, and every other node seems to be set to NULL. I think that I can't print the rest of the nodes when traversing, because I am trying to access the data member of a NULL struct. My code so far is as follows:
void insert_node(TreeNode** root, int toInsert){
if(*root==NULL){
TreeNode *newnode = (TreeNode *)malloc(sizeof(TreeNode));
newnode->data = toInsert;
newnode->left = NULL;
newnode->right = NULL;
}
else if(toInsert > (*root)->data){ //if toInsert greater than current
struct TreeNode **temp = (TreeNode **)malloc(sizeof(struct TreeNode*));
*temp = (*root)->right;
insert_node(temp, toInsert);
}
else{ //if toInsert is less than or equal to current
struct TreeNode **temp = (TreeNode **)malloc(sizeof(struct TreeNode*));
*temp = (*root)->left;
insert_node(temp, toInsert);
}
}
void build_tree(TreeNode** root, const int elements[], const int count){
if(count > 0){
TreeNode *newroot = (TreeNode *)malloc(sizeof(TreeNode));
newroot->data = elements[0];
newroot->left = NULL;
newroot->right = NULL;
*root = newroot;
for(int i = 1; i < count; i++){
insert_node(root, elements[i]);
}
}
I'm sure it's only one of many problems, but I get segmentation faults on any line that uses "(*root)->data", and I'm not sure why.
As a side note, despite getting segmentation faults for the "(*root)->data" lines, I'm still able to printf "(*root)->data". How is it possible to print the value, but still get a segmentation fault?
It's messy. Some things that might help
1) Don't need to use TreeNode*, pointer to pointer, as argument. Use jsut the TreeNode. (something went wrong here, as it's some feature from the text editor, consider and additional * after each TreeNode in this line)
2) Not a strict rule, but as best practice avoid using the first node of a linked list to store actual values. Use just as the header of your list. Reason is, if you need to delete this node, you don't lose the list. Just a tip
3) In your first function, if *root==NULL, I'd rather make the function fail than adding it to a temporary list (that's being lost in the current code, see that it adds the value to a list that is not being passed outside the function.
4) Well, you are actually making it go to the right if the new value is greater than the node, to the left if it's smaller than the node, but it never stops. See this example:
Suppose you have the list 1->3->4. Now you want to insert 2. What the algorithm will do? keep trying to insert in the 1 node and 3 node, switching between them, but never actually inserting anything.
Solution: as you will build this list bottom up, your list will always be sorted (inf you insert nodes correctly). So you just need to check if the next node is higher, and if it is, insert right where you are.
5) If you're passing a TreeNode *root as argument (on the 2nd function), you shouldn't have to recreate a new list and make root=newlist. Just use the root.
All of this would result in (didn't test, might be some errors):
void insert_node(TreeNode* root, int toInsert){
if(root==NULL){
printf("Error");
return;
}
TreeNode* temp = root; //I just don't like to mess with the original list, rather do this
if(temp->right!=NULL && toInsert > temp->right->data){ //if toInsert greater than next
insert_node(temp->right, toInsert);
}
else{ //if toInsert is less or equal than next node
TreeNode* temp2 = temp->right; //grabbing the list after this node
temp->right=(TreeNode*)malloc(sizeof(TreeNode)); //making room for the new node
temp->right->right=temp2; //putting the pointer to the right position
temp->right->left=temp; //setting the left of the next node to the current
temp->right->data=toInsert;
}
}
void build_tree(TreeNode* root, const int elements[], const int count){
if(count > 0){
for(int i = 0; i < count; i++){
insert_node(root, elements[i]);
}
}
}