How to implement this create function correctly? - c

I'm trying to make a function to make a priority queue. My structs are this:
struct node {
char *item;
struct node *next;
};
struct queue {
struct node *start;
struct node *end;
};
struct priority_queue {
struct queue **aoq;
int x;
};
My function that I want to implement is:
struct priority_queue *priority_queue_create(int x);
Here, as seen in the structure priority_queue, struct queue **aoq is essentially an array of queues, which is want I want. int x, as in the function header, is the number of queues in the array.
My take on it is this:
struct priority_queue *priority_queue_create(int x) {
struct priority_queue *pq = malloc(sizeof(struct priority_queue));
pq->x = x;
pq->aoq = malloc(x* sizeof(struct queue));
return pq;
}
Where I put the 3 comment lines in the code above is where I suspect my error to be. I want to be able to do something like: if x = 3, there should be an array with 3 queues in it, and I suppose I'll be able to access it by doing something like
pq->aoq[0] /// to access the first queue in the array, or
pq->aoq[2] /// to access the third queue in the array
Can anyone help my fix my implementation? Thanks in advance.
Edit:
Other implementation I've tried:
struct priority_queue *priority_queue_create(int x) {
struct priority_queue *pq = malloc(sizeof(struct priority_queue *));
pq->x = x;
pq->aoq = malloc(x * sizeof(struct queue *));
for (int i = 0; i < x; ++i) {
pq->aoq[i] = malloc(sizeof(struct queue));
}
return pq;
}

From the code that you posted, I think this is the functionality that you want. You were missing the allocation of memory for the queues inside struct priority_queue, that is:
pq-> aoq = malloc( x * sizeof(struct queue *));
Adding this and keeping what you already had (with a sample main function to test the function priority_queue_create()), we have the following code:
#include <stdio.h>
#include <stdlib.h>
struct node {
char *item;
struct node *next;
};
struct queue {
struct node *start;
struct node *end;
};
struct priority_queue {
struct queue **aoq;
int x;
};
struct priority_queue *priority_queue_create(int x) {
struct priority_queue *pq = malloc(sizeof(struct priority_queue *));
pq->x = x;
pq-> aoq = malloc( x * sizeof(struct queue *));
for (int i = 0; i < x; ++i) {
pq->aoq[i] = malloc(sizeof(struct queue)); ///
}
return pq;
}
int main(){
struct priority_queue * pq = priority_queue_create(3);
char * str1 = "abcd";
char * str2 = "abcde";
pq->aoq[0]->start = malloc(sizeof(struct node));
pq->aoq[2]->start = malloc(sizeof(struct node));
pq->aoq[0]->start->item = str1;
pq->aoq[2]->start->item = str2;
printf("%s - %s\n", pq->aoq[0]->start->item, pq->aoq[2]->start->item);
free(pq->aoq[0]->start);
free(pq->aoq[2]->start);
free(pq->aoq[0]);
free(pq->aoq[1]);
free(pq->aoq[2]);
free(pq->aoq);
free(pq);
}

Related

C Language - Rehashing a Separate Chaining Hash Table

So I looked everywhere to get inspired but I didn't really find anything for rehashing a hash table using separate chaining method. So I tried myself, I think I know what I'm doing wrong, but I don't know how else to implement it, please help.
Everything works, except the new added function rehash()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
struct list_node
{
struct list_node *next;
char *key;
char *value;
};
struct hash_table
{
int table_size;
struct list_node **list_arr;
};
unsigned int hash(const char *key, unsigned int table_size);
struct hash_table *initialize(unsigned int table_size);
struct list_node *find(struct hash_table *H, const char *key);
void insert(struct hash_table *H, const char *key, const char *value);
void dump(struct hash_table *H);
void del(struct hash_table *H, const char *key);
struct hash_table *rehash(struct hash_table *H);
unsigned int
hash(const char *key, unsigned int table_size)
{
unsigned long int hashx = 0;
for(int i=0;key[i];i++)
{
hashx = (hashx<<5) + key[i];
}
return (hashx%table_size);
}
struct hash_table
*initialize(unsigned int table_size)
{
struct hash_table *H = malloc(sizeof(*H));
H->list_arr = malloc(sizeof(*H->list_arr)*table_size);
H->table_size = table_size;
for(unsigned int i = 0; i<table_size; i++)
{
H->list_arr[i] = malloc(sizeof(*H->list_arr[i]));
H->list_arr[i]->next = NULL;
}
return H;
}
void
insert(struct hash_table *H, const char *key, const char *value)
{
unsigned int index = hash(key, H->table_size);
struct list_node *head = H->list_arr[index];
struct list_node *current = head->next;
while(current!=NULL)
{
if(strcmp(current->key,key)==0)
{
free(current->value);
current->value = malloc(strlen(value)+1);
strcpy(current->value,value);
return;
}
current=current->next;
}
struct list_node *newNode = malloc(sizeof(*H->list_arr[index]));
newNode->next = head->next;
head->next = newNode;
newNode->key = malloc(strlen(key)+1);
newNode->value = malloc(strlen(value)+1);
strcpy(newNode->key,key);
strcpy(newNode->value,value);
}
void
dump(struct hash_table *H)
{
for( int i = 0; i<H->table_size; i++)
{
struct list_node *entry = H->list_arr[i]->next;
if(entry==NULL){continue;}
printf("Index[%d]: ", i);
while(entry!=NULL)
{
printf("\t%s|%s\t--> ", entry->key, entry->value);
entry = entry->next;
}
printf("\tNULL");
printf("\n");
}
}
void delete(struct hash_table *H, const char *key)
{
unsigned int index = hash(key,H->table_size);
struct list_node *prev = H->list_arr[index];
while(strcmp(prev->next->key,key)!=0)
{
if(prev->next==NULL){printf("Key not found!");return;}
prev=prev->next;
}
struct list_node *temp = prev->next;
prev->next = temp->next;
free(temp);
}
struct hash_table *rehash(struct hash_table *H)
{
unsigned int old_size = H->table_size;
struct list_node *old_entries = H->list_arr;
H = initialize(2*old_size);
for(unsigned int i = 0; i<old_size; i++)
{
while(old_entries[i]!=NULL)
{
insert(H,old_entries[i].key,old_entries[i].value);
old_entries[i] = old_entries[i]->next;
}
}
free(old_entries);
return H;
}
int main()
{
struct hash_table *H = initialize(20);
insert(H,"name1","David");
insert(H,"name2","Radka");
dump(H);
H = rehash(H);
dump(H);
return 1;
}
I think doing old_entries[i] is wrong, but nothing else comes to mind, please help me resolve this.
OK! After thinking about it for a while, I realized I created a struct list_node pointer variable that points to H->list_arr which is an array of pointers. That was my mistake. I was supposed to declare it as a double pointer.
Here's the modified rehash() function:
struct hash_table *rehash(struct hash_table *H)
{
unsigned int old_size = H->table_size;
struct list_node **old_entries = H->list_arr;
H = initialize(2*old_size);
for(unsigned int i = 0; i<old_size; i++)
{
old_entries[i] = old_entries[i]->next;
while(old_entries[i]!=NULL)
{
insert(H,old_entries[i]->key,old_entries[i]->value);
old_entries[i] = old_entries[i]->next;
}
}
free(old_entries);
return H;
}
with this code, you will have to return the address of the new hash_table to the pointer pointing to the old hash_table --> [H = rehash(H)] since passing the pointer H as a parameter will only change it locally. Therefore, I tried a second version (because I'm too lazy;) and inattentive and might forget to reassign it) where I don't have to return anything, I want to change it simply by calling the function and my pointer points to the new hash_table automatically -> [rehash(&H)], here's the other "lazy" alternative:
void
rehash(struct hash_table **H)
{
unsigned int old_size = (*H)->table_size;
struct list_node **old_entries = (*H)->list_arr;
*H = initialize(2*old_size);
for(unsigned int i = 0; i<old_size; i++)
{
old_entries[i] = old_entries[i]->next;
while(old_entries[i]!=NULL)
{
insert(*H,old_entries[i]->key,old_entries[i]->value);
old_entries[i] = old_entries[i]->next;
}
}
free(old_entries);
}
If I'm doing something that's inefficient (in terms of space and time), please let me know, as I am only in Bachelor's 3rd semester of CS and we have only started DSA this semester.
The thing you are doing by putting dummy elements at the beginning of each bin is a good idea, but you don't need to allocate such dummies with malloc(). You can just make the bin array an array of nodes instead of pointers to nodes. Then you have allocated the dummies when you have allocated the array. So you could define your hash table as
struct hash_table
{
int table_size;
struct list_node *list_arr;
};
(instead of using struct list_node **list_arr).
When you loop through the bins in the initialisation, you have to set the bins' next pointer to NULL, but not allocate them.
struct hash_table
*initialize(unsigned int table_size)
{
struct hash_table *H = malloc(sizeof(*H));
H->list_arr = malloc(sizeof(*H->list_arr)*table_size);
H->table_size = table_size;
for(unsigned int i = 0; i<table_size; i++)
{
// no malloc here!
H->list_arr[i].next = NULL;
}
return H;
}
Anyway, that is not pertinent to the rehashing, just a suggestion. But because you have the dummy elements as bins, you can refactor your code (that is the reason I think the dummies are such a good idea). You can get the bin from the table and work from there, without worrying about the table itself after that. You can get the relevant bin for a key with
struct list_node *get_bin(struct hash_table *H, const char *key)
{
unsigned int index = hash(key, H->table_size);
return &H->list_arr[index];
}
and you can find the node in a bin with
struct list_node *find_node(struct list_node *bin, const char *key)
{
for (struct list_node *current = bin->next;
current;
current = current->next) {
if(strcmp(current->key,key)==0) return current;
}
return 0;
}
and, for example, simplify insertion to
void prepend_node(struct list_node *node, struct list_node *bin)
{
node->next = bin->next;
bin->next = node;
}
void insert(struct hash_table *H, const char *key, const char *value)
{
struct list_node *bin = get_bin(H, key);
struct list_node *node = find_node(bin, key);
if (node) {
// update node
free(node->value);
node->value = malloc(strlen(value)+1);
strcpy(node->value,value);
} else {
// prepend new node
prepend_node(new_node(key, value), bin);
}
}
where the new_node() function looks like
struct list_node *new_node(const char *key, const char *value)
{
struct list_node *node = malloc(sizeof *node);
if (!node) abort(); // add some error handling here
node->key = malloc(strlen(key)+1);
if (!node->key) abort(); // add some error handling here
strcpy(node->key,key);
node->value = malloc(strlen(value)+1);
if (!node->value) abort(); // add some error handling here
strcpy(node->value,value);
return node;
}
Because the bins are embedded in the array, you can safely assume in all the functions that they aren't NULL, which can save you from testing some special cases.
It is not shorter code, because I split it into several functions, but in my opinion, it is more readable when each function does one simple thing. Here, getting the bin, finding the key in a bin, creating a node, pretending to a bin, etc. With "raw" malloc() and strcpy() and such, scattered through the code, it is harder to track that everything works correctly. The total lines of code grew, but each function is shorter and simpler. And you can get away with it, because you can work on bins as lists, without accessing the hash table array, exactly because all bins have a dummy head element.
You can now rewrite rehash() to just prepend to bins. You know that all the keys in the old bins are unique, so you don't need to check anything. You just put each node at the front of its new bin:
struct hash_table *rehash(struct hash_table *H)
{
unsigned int old_size = H->table_size;
struct list_node *old_entries = H->list_arr;
free(H); // You forgot to free this one!
H = initialize(2*old_size);
for(unsigned int i = 0; i<old_size; i++)
{
struct list_node *old_bin = &old_entries[i];
for (struct list_node *node = old_bin->next;
node; node = node->next) {
// just prepend to new bin; the key should be unique
prepend_node(node, get_bin(H, node->key));
}
}
free(old_entries);
return H;
}
I added a free(H) because you forgot to free memory for H, but it would be more efficient to update H without creating a new table. You can separate initialisation and allocation. But you do not gain terribly much as initialising the bins is the time-consuming part.
Speaking of freeing, though. Remember to write a function for freeing a hash table (that remembers to free the bins, including all the nodes). Don't use it with rehashing, of course, if you free H before you update it--you need to keep the nodes around--but you do want such a function.

Generric search function at generic link list

I have generic link list in C that know how to push struct to list.
The problem is the I can't implement generic search in those link list:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct Node
{
void *data;
struct Node *next;
};
void push(struct Node** head_ref, void *new_data, size_t data_size)
{
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = malloc(data_size);
new_node->next = (*head_ref);
int i;
for (i=0; i<data_size; i++)
*(char *)(new_node->data + i) = *(char *)(new_data + i);
(*head_ref) = new_node;
}
struct A
{
int a1;
long a2;
};
struct B
{
long b1;
int b2;
};
void find_a1_in_a_list (int desire_a1 , struct Node *a_list)
{
struct A *a;
while(NULL != a_list)
{
a = (struct A*) a_list->data;
if(a->a1 == desire_a1)
printf("found!\n");
a_list = a_list->next;
}
}
void find_b1_in_b_list (long desire_b1 , struct Node *b_list)
{
struct B *b;
while(NULL != b_list)
{
b = (struct B*) b_list->data;
if(b->b1 == desire_b1)
printf("found!\n");
b_list = b_list->next;
}
}
void find_generic (void* desire_value,int off,struct Node *list)
{
while(NULL != list)
{
void* check_value_void = list->data + off;
int check_value_cast = *(int *) check_value_void; //How to know if cast to int or long ?????
if(check_value_cast == *(int *)desire_value) //How to know if cast to int or long ?????
printf("found generic!\n");
list = list->next;
}
}
void main()
{
struct Node *a_list = NULL;
struct A a;
a.a1=1;
a.a2=2;
push(&a_list, &a, sizeof(struct A));
find_a1_in_a_list(1,a_list);
struct Node *b_list = NULL;
struct B b;
b.b1=1;
b.b2=2;
push(&b_list, &b, sizeof(struct B));
find_b1_in_b_list(1,b_list);
//tried to make it generic
int search = 3;
find_generic(&search,offsetof(struct A, a2),a_list);
}
As you can I tried to makes generic search in function find_generic by passing the offset to the value in struct, that code works but only for int
but how can I pass to this generic function if I want to search int or long ,so I will know how to makes cast ?
Is there any way to cast void * by size so I can pass sizeof(int) or sizeof(long) and makes the casting by this value? or maybe another way?
Passing the compare function directly instead of playing with offsetof/sizeof will be more flexible:
struct Node *find_generic (struct Node *list,
int (*fn_cmp)(void const *a, void const *b),
void const *data)
{
while (list) {
if (fn_cmp(list->data, data) == 0)
break;
list = list->next;
}
return list;
}
and then create custom compare functions
static int cmp_A(void const *a_, void const *b_)
{
struct A const *a = a_;
struct A const *b = b_;
if (a->a1 == b->a1 && a->a2 == b->a2)
return 0;
return 1;
}
and call it like
struct A key = {
.a1 = 23,
.a2 = 42,
};
find_generic(a_list, cmp_A, &key);

Keep segfaulting when trying to pass struct into function

I'm trying to pass a pointer to a queue into the createQueue function:
void createQueue(struct pqueue *queue){
queue = malloc( sizeof(struct pqueue) );
queue->root = malloc(sizeof(struct node));
queue->root->next = 0;
queue->root->taskID = 12;
queue->root->priority = 5000;
}
I also try to add to the newly created queue like this:
void add(struct pqueue *queue, int taskID, int priority){
struct node *conductor;
conductor = queue->root;
if ( conductor != 0 ) {
while ( conductor->next != 0)
{
conductor = conductor->next;
}
}
conductor->next = malloc( sizeof(struct node) );
conductor = conductor->next;
if ( conductor == 0 )
{
printf( "Out of memory" );
}
/* initialize the new memory */
conductor->next = 0;
conductor->taskID = taskID;
conductor->priority = priority;
}
from the main function:
int main()
{
struct pqueue *queue;
createQueue(queue);
add(queue, 234093, 9332);
}
...but I keep segfaulting. Any reason why this keeps happening?
EDIT:
The structs for pqueue and node are like this:
struct node {
int taskID;
int priority;
struct node *next;
};
struct pqueue{
struct node *root;
};
In C, everything is passed by value. Therefore, when you call createQueue(queue), you are passing a copy of the pointer to the function. Then, inside the function, when you say queue = malloc(...), you are setting that copy of the pointer equal to your newly allocated memory - leaving main()'s copy of that pointer unchanged.
You want to do something like this:
void createQueue(struct pqueue **queue)
{
(*queue) = malloc( ... );
}
int main(void)
{
struct pqueue *queue;
createQueue(&queue);
}
This question has a more detailed description of what's going wrong for you.

Passing struct to function and adding elements

I'm new to C. I'm trying to pass a struct list to a function and within that function fill the list. Code is as follows:
#include <stdio.h>
#include <stdlib.h>
struct Abc {
int test;
struct Abc *next;
};
void demo_fill(struct Abc *data);
int main(int argc, char **argv) {
struct Abc *db = NULL;
demo_fill(db);
printf("%d\n",db->test);
return 0;
}
void demo_fill(struct Abc *data) {
int i;
for( i = 0; i < 5; i++ ) {
struct Abc *new;
new = malloc(sizeof(struct Abc));
new->test = i;
new->next = data;
data = new;
}
}
When running this a 'Segmentation fault (core dumped)' error occurs because the struct is still NULL when I try to print the first element. What am I doing wrong?
You're passing the pointer by value. You need to pass a pointer to a pointer if you want the change the value of the caller's pointer:
int main(int argc, char **argv) {
struct Abc *db = NULL;
demo_fill(&db);
printf("%d\n",db->test);
return 0;
}
void demo_fill(struct Abc **data) {
int i;
for( i = 0; i < 5; i++ ) {
struct Abc *new;
new = malloc(sizeof(struct Abc));
new->test = i;
new->next = *data;
*data = new;
}
}
Assigning data to new will have no effect. data is a local copy of the pointer. Pass a double pointer to fix that. Something like this:
void demo_fill(struct Abc** data) {
int i;
for( i = 0; i < 5; i++ ) {
struct Abc *new;
new = malloc(sizeof(struct Abc));
new->test = i;
new->next = *data;
*data = new;
}
}
And of course you will have to pas the pointer to db in main:
demo_fill(&db)
You can pass a pointer to the pointer as the other two answers say, so i just point out an alternative you might also consider: Instead of using a single struct you could use one struct for the list and another for entry-links:
struct link {
int test;
struct link* next;
};
struct list {
struct link* first;
};
void demo_fill(struct list* data);
You can then modify the first entry of the list without thinking about ** syntax.

c programming: need fresh eyes to look at this [demo code != homework]

Essentially I want qPtr[0] to hold sPtr[0]
struct myQueue{
struct sample* node;
int front;
int size;
int numElements;
};
struct sample{
int field1[5];
char field2[10];
}
int main(){
struct myQueue* qPtr = malloc(10 * sizeof(struct myQueue);
struct sample* samplePtr = malloc(10 * sizeof(struct sample); //assume this array has been initialized
enqueue(qPtr, samplePtr[0]); //this does not work
}
//returns 1 if enqueue was successful
int enqueue(struct myQueue* qPtr, struct sample* sPtr){
qPtr->node[(qPtr->front + qPtr->numElements) % qPtr->size] = sPtr; //code pertains to circular array implementation of queues
return 1;
}
I've been at it for about 2 hours now and would appreciate some clarification on what I'm doing wrong conceptually. thank you!
samplePtr[0] gives the object itself, not a pointer to the object. Try sending &samplePtr[0] or samplePtr itself. enque function, second parameter expects a type of struct sample* and not struct sample.
How about:
enqueue(qPtr, &samplePtr[0]);
The second parameter to enqueue() takes a pointer to a struct sample.
Your code has 2 fundamental problems.
you're passing a struct sample object to enqueue() instead of a pointer to a struct sample. this should be caught by the compiler.
you're setting up an array of queue structures instead of having a single queue structure object that manages an array of pointers to the objects that are on the queue. This is a design problem.
Your code should probably look more like:
struct myQueue{
struct sample* node;
int front;
int size;
int numElements;
};
struct sample{
int field1[5];
char field2[10];
}
struct myQueue q = {0};
int enqueue(struct myQueue* qPtr, struct sample* sPtr);
int main(){
// get memory to hold a collection of pointers to struct sample:
q.node = calloc(10, sizeof(struct sample*));
q.size = 10;
// allocate a sample
struct sample* samplePtr = malloc(sizeof(*samplePtr));
// put the sample on the queue
enqueue(qPtr, samplePtr);
}
//returns 1 if enqueue was successful
int enqueue(struct myQueue* qPtr, struct sample* sPtr){
qPtr->node[(qPtr->front + qPtr->numElements) % qPtr->size] = sPtr; //code pertains to circular array implementation of queues
return 1;
}

Resources