I'm trying to implement a hash table in C, and almost have it. I'm implementing collision resolution via chaining using a linked list in each array slot, and I'd like to be able to chain inside a function call.
Now, the problem is that in order for the modifications to be permanent I believe that I need an additional level of indirection. This is a problem in that when I try to traverse the list the previous element is overwritten by the next (see my comment inside the insert() function). I've tried to pass this array with an additional level of indirection specified a few different ways but I get compiler warnings and seg faults.
This may seem simple to some of you, but it's had me scratching my head for quite some time now, and this scenario (passing array of pointers for modification) is treated nowhere in my text and I can't seem to find this exact question asked (although it may be in a form I don't recognize). I'm not necessarily looking for a 'quick fix' to my code, but I want to understand what is the best practice to accomplish what I'm looking to do.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
//#include "list.c"
struct node{
int key;
struct node* next;
};
void createTable(struct node *table[], int numEls, int numSlots);
int hash(int key, int numSlots);
void insert(struct node *table[], int key, int slot, int numSlots);
void display(struct node *table[], int numEls);
int main(void){
srand(time(NULL));
int numEls, numSlots;
printf("Please enter the desired number of slots in the hash table: ");
scanf("%d", &numSlots);
struct node *table[numSlots];
printf("\nPlease enter the desired number of elements: ");
scanf("%d", &numEls);
printf("\nYour load factor will be %f", (float)numEls/numSlots);
createTable(table, numEls, numSlots);
}
void createTable(struct node *table[], int numEls, int numSlots){
for(int i = 0; i < numSlots; i++)
table[i] = NULL;
for(int j = 0; j < numEls; j++){
for(int k = 0; k < 99999999; k++){}//give the rand function time
int el = rand()%100;
insert(table, el, hash(rand()%100, numSlots), numSlots);
}
display(table, numSlots);
}
int hash(int key, int numSlots){
return((int)(pow(key, 2.819)) % numSlots);
}
void insert(struct node *table[], int key_, int slot, int numSlots){
printf("\nInserting %d into slot %d", key_, slot);
fflush(stdout);
struct node* new = malloc(sizeof(struct node));
(new)->key = key_;
(new)->next = NULL;
struct node** temp = &(table[slot]);
if((*temp) == NULL){
printf(" (*temp) == NULL");
(*temp) = new;
}
else{
printf(" %d", (*temp)->key);
while((*temp)->next != NULL){
printf(" %d", (*temp)->next->key);
(*temp) = (*temp)->next; //head is overwritten with head->next
}
(*temp)->next = new;
printf(" %d", (*temp)->next->key);
}
}
void display(struct node *table[], int numSlots){
for(int i = 0; i < numSlots; i++){
printf("\nSlot %d:", i);
struct node* temp = table[i];
while(temp != NULL){
printf(" %d", temp->key);
temp = temp->next;
}
}
}
In the line with the comment, you are overwriting the pointer to the head (inside the outermost array) with the next element in the linked list, which was probably not your intent.
The correction is to walk down the list of pointers until you found the last pointer, without modifying the main data structure during the walk.
Here is a corrected version of insert().
void insert(struct node *table[], int key_, int slot, int numSlots){
printf("\nInserting %d into slot %d", key_, slot);
fflush(stdout);
struct node* new = malloc(sizeof(struct node));
new->key = key_;
new->next = NULL;
// Here we make a copy of the pointer to the head node in the linked list.
// This way, we never overwrite the original copy which lives in the array itself.
struct node* head = table[slot];
if(head == NULL){
printf(" head == NULL");
table[slot] = new;
}
else{
while(head->next != NULL) {
head = head->next; //head is overwritten with head->next
}
head->next = new;
}
}
This isn't an answer but is too big for a comment... can you explain what this code is meant to be doing?
while((*temp)->next != NULL)
{
printf(" %d", (*temp)->next->key);
(*temp) = (*temp)->next; //head is overwritten with head->next
}
(*temp)->next = new;
I would expect that you want to append the new node at the end of the linked list of existing nodes in this slot. But this code actually updates the head to point to the last node in the list , just like your comment says (leaking memory - the earlier nodes in the list are now unreachable). Then it makes the last node (which is now the only node in the list) point to new.
So your "list" only ever has length 1 or 2, and it leaks memory each time you try to put a third entry in.
It seems to me that there is nothing wrong with your passing of table (which is a list of heads of linked lists) but the problem is that the code that maintains the list for which those are heads, is not right.
Related
I am trying to accomplish a function that grows a linked list while also putting them in ascending order at the same time, I have been stuck for a while and gained little progress. I believe my insertLLInOrder is correct it's just the createlinkedList that is messing it up.
Sometimes my output comes out fully and other times it only prints out some of the list.
Anything helps!
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *createlinkedList(int num);
node *insertLLInOrder(node * h, node * n);
void display(node * head);
int randomVal(int min, int max);
int
main()
{
int usernum = 0;
node *HEAD = NULL;
printf("How many Nodes do you want? ");
scanf("%d", &usernum);
srand(time(0));
HEAD = createlinkedList(usernum);
display(HEAD);
return 0;
}
node *
createlinkedList(int num)
{
int i;
int n = num;
node *head = NULL;
node *newNode;
node *temp;
for (i = 0; i < n; i++) {
newNode = (node *) malloc(sizeof(node));
newNode->next = NULL;
newNode->data = randomVal(1, 9);
temp = insertLLInOrder(head, newNode);
head = temp;
}
return head;
}
int
randomVal(int min, int max)
{
return min + (rand() % (max - min)) + 1;
}
node *
insertLLInOrder(node * h, node * n)
{
//h is the head pointer, n is the pointer to new node
node *ptr = h;
node *previous = NULL;
while ((ptr != NULL) && (ptr->data < n->data)) {
previous = ptr; // remember previous node
ptr = ptr->next; // check for the next node
}
if (previous == NULL) {
//h is an empty list initially
n->next = NULL;
return n; // return the pointer of the new node
}
else {
//if there are nodes in the linked list
// previous will point to the node that has largest value, but smaller than new node
n->next = previous->next; // insert new node between previous, and previous->next
previous->next = n;
return h; // return old head pointer
}
}
void
display(node * head)
{
node *p = head;
while (p != NULL) {
printf("%d, ", p->data);
p = p->next;
}
}
Obviously in your insertLLInOrder() if the first while loop gives previous == NULL it means that you must insert at list head, which is not what your are doing.
Just change n->next = NULL; to n->next = h; and it should improve behavior.
Taking a step back and perspective
This is a very simple error, but it is made harder to spot because of the way you wrote your code.
The bug in itself is not very interesting, but it can help to get a higher perspective on why it happened and how to avoid such bugs.
And, no, running a debugger is not very helpful for such cases!
Having to run a debugger happens sometimes, but it merely means that you have lost the control of your program. Like having a parachute can be a safety mesure for a pilot, but if he has to use it, it also means that the pilot lost control and his plane is crashing.
Do you know the story of the Three Ninjas Programmers?
The three Ninjas
The chief of ninjas orders three Ninja to show him their training level. There is a Noob, a Beginner and a Senior. He asks them to reach a small cabin, on the other side of a field, take some object inside and come back.
The first Ninja is a noob, he runs and jumps across the field with all his speed but soon enough he walks on a (plaster) mine. He goes back at the start line and confesses his failure, which is obvious because his previously black shirt is now covered by white plaster.
The second Ninja shows some practice. You can tell he failed like the Noob on a previous try and that now he is wary. He is very slow and very careful. He sneaks very slowly across the field watching closely everywhere at each step. He gets quite close to the cabin, and everybody believes he will succeed, but eventually, he is also blown by a mine at the last second. He also goes back disappointed to the starting point, but he somehow believes it will be hard for the third Ninja to do any better.
The third Ninja is a Senior. He walks calmly across the field in a straight line, enter the cabin, and goes back without any visible trouble, still merely walking across the field.
When he gets back to the starting point the other two Ninjas are stunned and ask him eagerly:
- How did you avoid the mines?
- Obviously, I didn't put any mines on my path in the first place; why did you put mines in yours?
Back to the code
So, what could be done differently when writing such a program?
First using random values in code is a bad idea. The consequence is that the code behavior can't be repeated from one run to the next one.
It is also important that the code clearly separate user inputs (data) and code manipulating that data.
In that case, it means that the createLinkedList() function should probably have another signature. Probably something like node *createlinkedList(int num, int data[]) where the data[] array will contains values to sort. It is still possible to fill input data with random values if it is what we want.
That way, we can easily create tests set and unit tests, like in code below:
Home made unit tests suite
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *createlinkedList(int num, int * data);
node *insertLLInOrder(node * h, node * n);
/* No need to have a test framework to write unit tests */
/* Check_LL is some helper function comparing a linked list with test data from an array */
int check_LL(node * head, int num, int * data)
{
node *p = head;
int n = 0;
for (; n < num ; n++){
if (!p){return 0;}
if (p->data != data[n]){return 0;}
p = p->next;
}
return p == NULL;
}
void test_single_node()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[1] = {1};
int expected[1] = {1};
node * HEAD = createlinkedList(1, input_data);
printf("%s\n", check_LL(HEAD, 1, expected)?"PASSED":"FAILED");
}
void test_insert_after()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[2] = {1, 2};
int expected[2] = {1, 2};
node * HEAD = createlinkedList(2, input_data);
printf("%s\n", check_LL(HEAD, 2, expected)?"PASSED":"FAILED");
}
void test_insert_before()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[2] = {2, 1};
int expected[2] = {1, 2};
node * HEAD = createlinkedList(2, input_data);
printf("%s\n", check_LL(HEAD, 2, expected)?"PASSED":"FAILED");
}
/* We could leave test code in program and have a --test command line option to call the code */
int
main()
{
test_single_node();
test_insert_after();
test_insert_before();
}
node *
createlinkedList(int num, int * data)
{
int i;
node *head = NULL;
for (i = 0; i < num; i++) {
node * newNode = (node *) malloc(sizeof(node));
newNode->next = NULL;
newNode->data = data[i];
head = insertLLInOrder(head, newNode);
}
return head;
}
node *
insertLLInOrder(node * h, node * n)
{
//h is the head pointer, n is the pointer to new node
node *ptr = h;
node *previous = NULL;
while ((ptr != NULL) && (ptr->data < n->data)) {
previous = ptr; // remember previous node
ptr = ptr->next; // check for the next node
}
if (previous == NULL) {
//h is an empty list initially
n->next = NULL;
return n; // return the pointer of the new node
}
else {
//if there are nodes in the linked list
// previous will point to the node that has largest value, but smaller than new node
n->next = previous->next; // insert new node between previous, and previous->next
previous->next = n;
return h; // return old head pointer
}
}
As you can see the third test spot the bug.
Of course, you could use some available third party Unit Test library, but the most important point is not the test library, but to write the tests.
Another point is that really you should interleave writing tests and writing implementation code.
This typically helps for writing good code and is what people call TDD. But my answer is probably already long enough, so I won't elaborate here on TDD.
I am trying to create an (ordered) linked list of (ordered) linked lists. The list-of-list links are carried by the first nodes of its member lists. I am trying to achieve this via the following code, but my program crashes right after I try to insert the second node into the list of lists.
Here's a schematic of the data structure I am trying to construct:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
struct node{
int number;
struct node*next;
struct node*lsnext;
};
typedef struct node Node;
Node* insertValue(Node * list, int value);
void display(Node*);
Node* insertArr(Node * list, int value);
int main()
{
Node *globalList = NULL, *lists,*start,*save;
int nbrOfLists, listNo, nbrOfVal, valNo, val;
printf("\n Enter the number of lists:");
scanf("%d", &nbrOfLists);
if(nbrOfLists < 0)
return -1;
for(listNo = 0; listNo < nbrOfLists; listNo++)
{
printf("\n\n Enter the number of inputs to the list %d: \n ",listNo+1);
scanf("%d", &nbrOfVal);
lists = NULL;
for(valNo = 0; valNo < nbrOfVal; valNo++)
{
printf("Enter node value %d:", valNo+1);
scanf("%d", &val);
// Here we insert the value in both lists
lists= insertValue(lists, val);
globalList = insertValue(globalList, val);
}
start=lists;
if(listNo==0){
save=start;
}
start=start->lsnext;
printf("\n The list %d is: ",listNo+1);
display(lists);
}
printf("\n\n The final list is: ");
display(globalList);
printf("The first list is");
display(save);
printf("The second list is");
display(save->lsnext); // CRASHES HERE
return 0;
}
Node* insertValue(Node * list, int value)
{
Node *newNode, *m;
newNode = malloc(sizeof(Node));
newNode->number=value;
if(list == NULL)
{
newNode->next=NULL;
return newNode;
}
if(value < list->number)
{
newNode->next = list;
return newNode;
}
m = list;
while(m->next)
{
if(value < m->next->number)
break;
m = m->next;
}
newNode->next = m->next;
m->next = newNode;
return list;
}
void display(Node*nodex){
while(nodex)
{
printf("%d ->",nodex->number);
nodex=nodex->next;
}
}
What is causing my error?
Your insertArr() function is wrong. Even its signature is wrong. Instead of linking together the first nodes of existing lists, it creates a perpendicular list of separate nodes. Note in particular that it accepts a value where it needs instead to accept the head node of a list.
Moreover, even the circumstances under which you call that function are wrong. You seem to try to link the initial head nodes of each list, but the head node may change as you add values. You must wait until you have a full list before you know what its final head node is; only then can you link that list into your list of lists.
Edit: I had first asserted that your problem was failure to initialize the lsnext members of your nodes. That would be correct if your insertArr() function were actually accepting nodes created by insertValue() as its second argument, as it should, but it is not correct for the code presented. The actual problem is a consequence of the issue described in my first paragraph, that insertArr() creates a separate list of separate nodes. In particular, those separate nodes do not have their next pointers initialized.
I am trying basic creation of linked list using C. I have written the following code which is working up until first node but fails eventually on second one. I think the issue is where I am trying to display the node values in list separated by arrow(->). I think my logic is right but please correct me. Thanks in advance
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
struct node
{
int number;
struct node *next;
};
typedef struct node NODE;
NODE *node1, *node2, *start, *save;
int main()
{
node1 = (NODE *)malloc(sizeof(NODE));
int i = 0;
start = NULL;
for(i = 0; i < 3; i++)
{
int inf;
printf("Enter node value:");
scanf("%d", &inf);
node1->number = inf;
node1->next = NULL;
if(start == NULL)
{
start = node1;
save = node1;
}
else
{
// save=start;
// start=node1;
// node1->next=save;
node1->next = start;
start = node1;
}
while(node1 != NULL)
{
printf("%d ->",node1->number);
node1 = node1->next;
}
}
return 0;
}
The issues are
How you're allocating your nodes for insertion (i.e. save for one, you're not).
How they're placed in the list once you fix the above.
Don't cast malloc in C programs (read here for why).
Fail to check the success of your scanf invoke.
Fail to check the success of your malloc invoke
Before you get discouraged, things you did correctly:
Did not mask a node pointer in a typedef
Properly included a MCVE for review
Prospected the things you may be doing wrong.
A very simple example of iterating three values into a linked list would look something like this:
#include <stdio.h>
#include <stdlib.h>
struct node
{
int number;
struct node *next;
};
typedef struct node NODE;
int main()
{
NODE *head = NULL, *p;
int i = 0;
for(i = 0; i < 3; i++)
{
int inf;
printf("Enter node value:");
if (scanf("%d", &inf) == 1)
{
p = malloc(sizeof *p);
if (p != NULL)
{
p->number = inf;
p->next = head;
head = p;
}
else
{
perror("Failed to allocate new node");
return EXIT_FAILURE;
}
}
else
{
// failed to read data. break
break;
}
// report current linked list
printf("%d", p->number);
for (p=p->next; p; p = p->next)
printf(" -> %d", p->number);
fputc('\n', stdout);
}
// cleanup the linked list
while (head)
{
p = head;
head = head->next;
free(p);
}
head = NULL;
return 0;
}
Input
The values 1 2 3 are input upon being prompted:
Output
Enter node value:1
1
Enter node value:2
2 -> 1
Enter node value:3
3 -> 2 -> 1
Best of luck.
You should use malloc() inside for loop.
Since it is outside, same memory is being used.
As said by Vamsi, you should use malloc to put the nodes on the heap. You also generally shouldn't cast the output of malloc, it isn't needed. And then you could play around with making a doubly-linked list, where you also have a prev pointer inside your struct.
I want to create a linked list.
The user adds numbers and the idea is that the numbers are inserted to the list in descending order.
Here goes what I did, but when rearranging, it just orders the first number...
int addInputNumber(numberList **node){
numberList *temp;
int userInput;
temp = (numberList*)malloc(sizeof(numberList));
//Memory Check
if ( temp == 0 )//out of memory, return 0
return 0;
//Get the users input
printf("Give me a Number!\n");
scanf("%d",&userInput);
//Add it to the list.
temp->numbero = userInput;
///Link to the list.
temp->next = *node;
*node = temp;
//Lets cycle through the list.
numberList *temp2;
int helpNumber;
temp2 = *node;
//Rearrange the list.
while(temp2 != 0){
if(temp->numbero < temp2->numbero){
//Switch position..
helpNumber= temp2->numbero;
temp2->numbero = temp->numbero;
temp->numbero = helpNumber;
temp2 = *node;// If we change number, we must cycle from the beginning AGAIN.
}//eof if
temp2 = temp2->next;
}//eof while
return 0;
}//eof addNUmber function.
Here's the structure just in case:
typedef struct dynamicNumberList {
int numbero;
struct dynamicNumberList *next;
}numberList;
I've got 2 quick questions.
Why would it only arrange the first number?
This list adds a space towards the left (visually), how could I make it so I can add a space to the right?
You need to get into the habit of creating one function per task, instead of cramming everything into a single one. It makes the code easier to read and reuse and reduces the chance of errors.
A correct implementation could look like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct s_List
{
int n;
struct s_List *next;
} List;
void print_list (List *head)
{
List *ptr;
for (ptr = head; ptr; ptr = ptr->next) {
printf ("%d\t", ptr->n);
}
putchar ('\n');
}
List * make_node (int n, List *next)
{
List * node = malloc (sizeof(List));
node->n = n;
node->next = next;
return node;
}
void insert_number_front (List **head, int n)
{
*head = make_node (n, *head);
}
void insert_number_after (List *prev, int n)
{
prev->next = make_node (n, prev->next);
}
// If HEAD is sorted, it will stay sorted after insertion
void insert_number_sorted (List **head, int n)
{
List *ptr;
List *ptr2;
// search for the rightmost node whose number is smaller than n.
ptr2 = NULL;
for (ptr = *head; ptr; ptr = ptr->next) {
if (ptr->n >= n)
break;
ptr2 = ptr;
}
// If such a node exists we insert the new node after it,
// otherwise we insert it at the front of the list.
if (ptr2) {
insert_number_after (ptr2, n);
}
else {
insert_number_front (head, n);
}
}
int input_number ()
{
int n;
printf ("enter a number: ");
scanf ("%d", &n);
return n;
}
int main ()
{
List *head = NULL;
int i;
// By adding elements exclusively with insert_number_sorted()
// we ensure the list is always sorted
for (i = 0; i < 5; i++) {
int n;
n = input_number ();
insert_number_sorted (&head, n);
}
print_list (head);
return 0;
}
To answer your second question, what you have here is a singly linked list, which can be described by a pointer to the first node. If you want to be able to insert nodes at the back you need to maintain an additional pointer to the last node. However this is not necessary in this case.
I am trying to implement the Linked List data structure for my college course, but on executing the code the following line produces an EXC_BAD_ACCESS(code=1, address=0x8) error.
temp->next = (ptrtonode) malloc(sizeof(struct node));
Following is the code in its entirety.
#include <stdio.h>
#include <stdlib.h>
typedef struct node *ptrtonode;
typedef ptrtonode header;
struct node
{
int data;
ptrtonode next;
};
ptrtonode create(int n)
{
int i;
header temphead = NULL;
ptrtonode temp = temphead;
for(i=0;i<n;i++)
{
temp->next = (ptrtonode) malloc(sizeof(struct node));
printf("Enter data for node %d: ", i+1);
scanf("%d", &temp->next->data);
temp = temp->next;
}
temp->next = NULL;
return temphead;
}
int main(int argc, const char * argv[])
{
header head;
int n;
printf("How many nodes do you wish to create?");
scanf("%d", &n);
head = create(n);
}
Any help would be appreciated.
Thanks all!
On first iteration of the for loop inside the create() function temp is NULL, which is then dereferenced causing the failure (not malloc() causing the failure). You will need to restructure the code slightly to prevent dereferencing a NULL pointer.
Other points:
casting the return value of malloc() is not required.
check the result of scanf() to ensure n was assigned a valid integer (and confirm that the int is positive):
/* scanf() returns the number of assignments made,
which in this case should be 1. */
if (1 == scanf("%d", &n) && n > 0)
{
/* 'n' assigned a sensible value. */
}