What were K&R doing here with this linked list? - c

I was doing a hackerRank challenge with dictionaries in C and I tore some code out of the K&R book. I dont understand how they are establishing a bucket of linked lists inside a hashtable with this?? It appears to me they are linking the next pointer to the head of the linked list. Are they creating a bucket in in some way im not catching? np is a three member struct containing a strings(name,defn) and the pointer to the next,lookup finds if np is exists in the dictionary.
if ((np = lookup(name)) == NULL){ // file not found
np = (struct nlist *) malloc(sizeof(*np));
if (NULL == np || (np->name = strdup(name)) == NULL){
return NULL;
}
hashval = hash(name);
np->next = hashtab[hashval]; // WHAT THE HECK ARE THEY DOING HERE?!?!
hashtab[hashval] = np;
}else{ // already there
free((void *)np->defn);
}
if((np->defn = strdup(defn)) == NULL){
return NULL;
}
return np;
I modified the code as follows to get it to work, but I have a nagging feeling that a missed a point they were trying to make.
if ((np = lookup(name)) == NULL) { // not found
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
phE->next = NULL; //if first entry set next to NULL, MOD HERE
tmpNode = hashtab[hashval];
if (tmpNode == NULL){ // EMPTY SPOT IN HASHTABLE
hashtab[hashval] = np;
}else{ //HASH COLLISION, ADD NODE TO LIST END
while (tmpNode->next != NULL){
tmpNode = tmpNode->next;
}
tmpNode->next = np;
}
}else{
free((void *) np->defn);
}
if ((np->defn = strdup(defn)) == NULL){
return NULL;
}
return np;

Let's trace through this code to see what it does:
np->next = hashtab[hashval]; // WHAT THE HECK ARE THEY DOING HERE?!?!
hashtab[hashval] = np;
Initially, our hash table looks something like this:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
Here's np:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | | +----> | head | -> | ... |
+-----+ +------+ +-----+
Now, we set np->next = hashtab[hashval]. Now things look like this:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
Now, both the newly-created cell's next pointer and hashtab[hashval] point to the same thing. From the perspective of np, it now points to the list formed by prepending the new cell, then using all existing cells.
Finally, we do hashtab[hashval] = np, which looks like this:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
This splices the new element to the front of the linked list.
In other words, this is a pretty typical list prepend made a bit trickier by the use of arrays of linked list pointers.

What you see is the basic idiom for inserting a new node as the first node in the list.
If head points to the current beginning of the list (null pointer for empty list) and node points to the new node, then all you need to do is
node->next = head;
head = node;
and you are done.
These two lines is exactly what you see in the K&R code you quoted.
Your version of the code insists on inserting the new node at the end of the list. In a basic implementation of a hash set the order of elements in a bucket does not really matter, which is why the K&R implementation simply inserts the new nodes at the start of each bucket. It is very simple and efficient, as you can see.
If you want to store each bucket's nodes in the order of their arrival, then you have to add the new nodes at the end of the list, which is notably less efficient in your implementation. But if you insist on it, you can use another idiomatic way of doing it, which allows you to avoid special if branch for empty buckets
struct nlist **pnext = &hashtab[hashval];
for (; *pnext != NULL; pnext = &(*pnext)->next);
*pnext = np;
np->next = NULL;
Of course, a more efficient way of doing all this would be to store two pointers for each bucket: to both the first and the last element of the list.

Related

How to delete a node in a linked list in c [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
what i need to do is to implement a function in c for deleting a node in a linked list, using a for and while loop with strcmp. The program compiles fine but the loop never stop, any suggestion on how to make it better?
The structure is this:
struct Test
{
char name[16];
int id;
};
typedef struct Node
{
struct Test structure;
struct Node * next;
}TNode;
typedef TNode * Nodo;
And the function i wrote is this:
void Delete(Nodo * pp)
{
Nodo i;
char temp[16], name[16];
printf("Insert the name to delete:");
scanf(" %s", name);
for(i = *pp; i->next != NULL ; i = i->next)
{
if(strcmp(i->structure.name, name) == 0)
{
while(i->next != NULL)
{
strcpy(temp,i->structure.name);
strcpy(i->structure.name, i->next->structure.name);
strcpy(i->next->structure.name, temp);
i = i->next;
}
}
}
}
UPDATE: The working function is this, thanks to Some programmer dude for the explanation:
void Delete(Nodo * pp)
{
Nodo i, prev;
char name[16];
printf("Insert the name to delete:");
scanf(" %s", name);
//case for the first node
if(strcmp((*pp)->structure.name, name) == 0)
{
Nodo old = *pp;
*pp = (*pp)->next;
free(old);
return;
}
//loop to delete node inside list
for(prev = *pp, i = prev->next; i != NULL ; prev = i, i = i->next)
{
if(strcmp(i->structure.name, name) == 0)
{
prev->next = i->next;
free(i);
break;
}
}
}
[Note: This doesn't actually answer the question, but it tells how to unlink nodes in a single-linked list, which IMO is the bigger problem the OP have.]
Lets say you have the following list
+---+ +---+ +---+ +---+
| A | -> | B | -> | c | -> | D |
+---+ +---+ +---+ +---+
If you want to remove node B then you don't copy the contents of the remaining list. Because that would give you the follow list:
+---+ +---+ +---+ +---+
| A | -> | C | -> | D | -> | D |
+---+ +---+ +---+ +---+
And that's not correct.
Instead you make the A node's next pointer point to C instead, so now the list becomes
+---+ +---+ +---+ +---+
| A | -\ | B | /-> | c | -> | D |
+---+ | +---+ | +---+ +---+
\-------/
Then you just free the node B, and you end up with
+---+ +---+ +---+
| A | -> | c | -> | D |
+---+ +---+ +---+
The trick to unlinking the node is to keep track of the previous node. It could be implemented something like this:
// Special case: Removing the first node in the list
if (strcmp((*pp)->structure.name, name) == 0)
{
TNode *old = *pp; // Save a pointer to the node that should be removed
*pp = (*pp)->next; // Actually unlink the node from the list
free(old); // Free the memory of the old node
return;
}
TNode *prev; // To keep track of previous node
TNode *curr; // The "current" node
for (prev = *pp, curr = prev->next; curr != NULL; prev = curr, curr = curr->next)
{
if (strcmp(curr->structure.name, name) == 0)
{
// Found the node to remove
prev->next = curr->next; // Unlink the current node from the list
free(curr); // Free the node
break; // No need to iterate anymore
}
}
The "special case" of the first node being removed could also be solved inside the loop, by initializing prev to NULL, and start curr with *pp instead. Then you need to check if prev is NULL or not inside the if in the loop.
The code might look like more, and it might be more lines (even after removing some or all of the empty lines), but I think most people would say it's easier to follow and see what's going on.
Of course, you also need to add a check for an empty list and that pp itself is not null (like pp != NULL && *pp != NULL).

Using fgets to read lines from a file into Binary Search Tree

Currently i'm trying to store each separate line in my file into a string, and then store it in a binary search tree, but a problem occurs. For some reason when I print my BST only the last line is outputted and not the first 3. Below is my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
int count;
char* key;
struct node* left;
struct node* right;
};
struct node *newNode(char* item)
{
struct node* temp = (struct node*)malloc(sizeof(struct node));
temp->key = item;
temp->left = NULL;
temp->right = NULL;
temp->count = 1;
return temp;
};
void printInorder(struct node* root)
{
if(root != NULL)
{
printInorder(root->left);
printf("%s \n", root->key);
printInorder(root->right);
}
}
struct node* insert(struct node* node, char* key)
{
if(node == NULL)//When tree is empty
return newNode(key);
if(strcmp(key, node->key) < 0)
node->left = insert(node->left, key);
if(strcmp(key, node->key) > 0)
node->right = insert(node->right, key);
return node;
};
int main()
{
struct node *root = NULL;
int i = 0;
char str[100];
FILE* fp;
fp = fopen("textFile.txt", "r");
if ((fp = fopen("textFile.txt","r")) == NULL)
{
printf("Could not open textFile.txt\n");
exit(1);
}
while(fgets(str, 100, fp) != NULL)
{
++i;
root = insert(root, str);
printf("%3d: %s", i, str);
}
printf("bst printed\n");
printInorder(root);
return 0;
}
textFile.txt contains
bob is working.
david is a new hire.
alice is bob's boss.
charles doesn't like bob.
And when the bst is printed the only line that is outputted is the last one
Charles doesn't like bob.
Any help would really be appreciated.
Notice that when you create a node with newNode, you store a copy of the pointer passed into it, rather than a copy of the string being pointed at. This means that every time you insert a value into the tree, it stores a pointer to the str buffer in main. In other words, after you do your first insertion, things look like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | b | o | b | | i | | 0 |
+------------+ +---+---+---+---+---+...+---+
When you then read the next line of the file, you're overwriting str with the contents of that line, so the picture looks like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | d | a | v | i | d | | 0 |
+------------+ +---+---+---+---+---+...+---+
Notice that your BST now acts as though it contains "david is a new hire" even though you never inserted that value. As a result, when you try inserting "david is a new hire" into the BST, nothing happens.
The same thing happens for the next few reads, until eventually you read the final line of the file, when things look like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | c | h | a | r | l | | 0 |
+------------+ +---+---+---+---+---+...+---+
This is why you're only seeing the line about Charlie at the end - the BST is directing you to the single shared copy of the buffer.
To fix this, make your BST store copies of the strings passed into it, or copy the strings before storing them in the tree. For example, you might have the newNode function call strdup to get its own copy of the string to store:
struct node *newNode(char* item)
{
struct node* temp = (struct node*)malloc(sizeof(struct node));
temp->key = strdup(item); // <--- here!
/* TODO: Error-handling! */
temp->left = NULL;
temp->right = NULL;
temp->count = 1;
return temp;
};
That should fix your issue. Just make sure to deallocate everything when you're done!

How does head connected to tail when deleting the list

I have a function which creates a list based on a given array,
this is the function:
typedef struct Item
{
int num;
struct Item* next;
}*PItem;
int main()
{
int Arr[N] = { 3, 4, 1, 0, 8 }, i;
PItem list = NULL, tail = NULL;
CreateListFromArray(&list, &tail, Arr);
}
void CreateListFromArray(PItem* head, PItem* tail, int *Arr)
{
int i;
PItem temp;
for (i = 0; i<N; i++)
{
temp = (PItem)malloc(sizeof(struct Item));
if (temp == NULL)
{
DeleteList(head);
Error_Msg("Memmory!");
}
temp->num = Arr[i];
temp->next = NULL;
if (*head == NULL)
*head = temp;
else
(*tail)->next = temp;
*tail = temp;
}
}
I understand that if List is empty, then head's null is initialized to the first allocated temp (arr[0]). But after that, for these arrays arr[1],..,arr[N], I update only the tail, meaning that all the tails from arr[1] to arr[N] are connected. but how does the head (arr[0]) POINTS/connected to arr[1]?
I ask this because, when I try to print the list, I use temp = head, and advance head until temp is null, but when I advance head, how does it know that it has to advance to arr[1]?
Here's the full code: http://pastebin.com/VPCfMU4X
After the first iteration of the loop, head and tail point to the same element which contains arr[0]. After the second iteration, (*tail)->next (which is the same as (*head)->next) point to the new element that contains arr[1], and tail is moved up to this value. Subsequent iterations keep appending to the end of the list.
So after one iteration, you have this:
head tail
| |
v v
---------------
| 3 | NULL |
---------------
After the second iteration, you have this:
head tail
| |
v v
--------------- ---------------
| 3 | .-----|--->| 4 | NULL |
--------------- ---------------
And the third:
head tail
| |
v v
--------------- --------------- ---------------
| 3 | .-----|--->| 4 | .-----|--->| 1 | NULL |
--------------- --------------- ---------------

Reverse direction of pointers in doubly linked list

I have a circular doubly linked list and I want to change the direction of all the next and prev pointers. I can't really figure out what the source of the error is. When I print the reversed list, it gets the first two numbers correct but past that point the links list stops printing.
struct Link
{
TYPE value;
struct Link * next;
struct Link * prev;
};
struct CircularList
{
int size;
struct Link* sentinel;
};
static void init(struct CircularList* list)
{
list->sentinel = (struct Link *) malloc(sizeof(struct Link));
list->sentinel->next = list->sentinel;
list->sentinel->prev = list->sentinel;
list->size = 0;
}
struct CircularList* circularListCreate()
{
struct CircularList* list = malloc(sizeof(struct CircularList));
init(list);
return list;
}
void circularListAddFront(struct CircularList* list, TYPE value)
{
struct Link *newLink = (struct Link *) malloc(sizeof(struct Link));
newLink->value = value;
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next = newLink;
if(circularListIsEmpty(list)) {
list->sentinel->prev = newLink;
}
list->size++;
}
void circularListRemoveFront(struct CircularList* list)
{
struct Link *temp = list->sentinel->next;
temp->next->prev = list->sentinel;
list->sentinel->next = temp->next;
free(temp);
list->size--;
}
void circularListRemoveBack(struct CircularList* list)
{
struct Link *temp = list->sentinel->prev;
temp->prev->next = list->sentinel;
list->sentinel->prev = temp->prev;
free(temp);
list->size--;
}
void circularListReverse(struct CircularList* list)
{
struct Link *link = list->sentinel->next;
while(link != list->sentinel) {
struct Link *nextTemp = link->next;
struct Link *prevTemp = link->prev;
link->prev = link->next;
link->next = prevTemp;
link = nextTemp;
}
struct Link *temp = list->sentinel->next;
list->sentinel->next = list->sentinel->prev;
list->sentinel->prev = temp;
}
If I run the following to test this I get the output 5 4 1 2 2 1 and then terminates with no errors.
struct CircularList *deque = circularListCreate();
circularListAddBack(deque, 1);
circularListAddBack(deque, 2);
circularListAddBack(deque, 3);
circularListAddFront(deque, 4);
circularListAddFront(deque, 5);
circularListAddFront(deque, 6);
circularListRemoveFront(deque);
circularListRemoveBack(deque);
circularListPrint(deque);
circularListReverse(deque);
circularListPrint(deque);
There's a bug in your circularListAddFront function, and (though not shown) probably also in circularListAddBack:
Assume this state of the list:
p+SENTINEL+n
| A |
-----|------
Now, let's say you add 42 to the front. You first allocate a new node and set its value and pointers. Also you set the sentinel next pointer:
struct Link *newLink = (struct Link *) malloc(sizeof(struct Link));
newLink->value = value;
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next = newLink;
This leads to the following state:
p+SENTINEL+n
| A |
-----| |
| |
------ |
| | |
p+42+n |
A |
| |
---------
Which is not fine, because the prev pointer of the sentinel still points to itself. You fix that directly after that:
if(circularListIsEmpty(list)) {
list->sentinel->prev = newLink;
}
list->size++;
This gives the desired result:
p+SENTINEL+n
--| A |
| | |
| ------ |
| | | |
| p+42+n |
| A |
| | |
--------------
This is fine. Now let's add a 21 to the glorious list:
----------
| |
| V
| p+SENTINEL+n
| --| A |
| | | |
| | ------ |
| | | | |
| | p+42+n |
| | A |
| | | |
| -----| |
| | |
| p+21+n |
--| A |
| |
-----------
That's the state right before that if, and it has the same issue as before: There's one prev pointer wrong, this time it's not sentinels but the one of node 42: It should point to its previous node, which is now 21, and not to the sentinel.
Since the if is not taken, the sate remains. You don't notice it until reversing the list because you don't use the prev pointers until then.
To fix that, get rid of the if and correct the logic unconditionally:
When you insert a new node to the front ("after the sentinel"), then you need to change the prev pointer of the node that was at the front before and point it to the new node:
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next->prev = newLink; // ADDITION
list->sentinel->next = newLink;
The code for reversing the list seems fine, though. And for now, I'm done with "line art ASCII graphics" :D

need assistance with singly linked list

I need some help with a singly linked list. I am trying to insert a new node for each word read in from my text file and compares it to the words in a dictionary file.From there, the new node is inserted into a hashtable.I feel that I am close(maybe wistful thinking),but I keep getting a segmentation error everytime I run my program. From the looks of my code, does anyone have any idea on what maybe wrong?
typedef struct Node {
char word[LENGTH+1];
struct Node *Next;
} Node;
hash_table_t *create_hash_table(int size)
hashtable = malloc(sizeof(hash_table_t));
if (hashtable == NULL)
{
return NULL;
}
hashtable->table= malloc(size* sizeof(struct Node *) ) ;
if (hashtable->table== NULL)
{
return NULL;
}
for(int i=0; i<size; i++)
{
hashtable->table[i]=NULL;
hashtable->size =size;
}
return hashtable;
typedef struct hash_table_t{
int size; /* the size of the table */
struct Node **table; /* the table elements */
} hash_table_t;
File *inptr;
Node* TempNode=NULL;
Node* new_node=NULL;
Node* Head=NULL;
char buffer[46];
unsigned int hashval;
int j=0;
int count=0;
int update_counter=0;
inptr= fopen(dictionary, "r");
if (inptr == NULL)
{
printf("Could not open dictionary file");
printf("\n");
return 0;
}
int ch = fgetc(inptr);
for ( ;; )
{
if ( ch == EOF ) //determines how many words are in the file
{
break;
}
if (isalpha(ch) || isdigit(ch) || ispunct(ch))
{
update_counter = 1;
}
if (isspace(ch) && update_counter )
{
count++;
update_counter = 0;
}
}
if (update_counter)
{
count++;
}
sizeOfDictionary=count;
rewind(inptr);
hashtable=create_hash_table(sizeOfDictionary);
while(fread(buffer,sizeof(buffer),1,inptr)!=0)
{
if(Head==NULL)
{
hashval = hash(buffer);
Head = malloc(sizeof(Node));
strcpy(&Head->word[j],buffer);
Head->Next = hashtable->table[hashval];
hashtable->table[hashval]=Head;
Head=Head->Next;
TempNode = Head;
}
else if(Head!=NULL)
{
new_node = malloc(sizeof(Node));
hashval = hash(buffer);
strcpy(&new_node->word[j],buffer);
new_node->Next = hashtable->table[hashval];
hashtable->table[hashval]=new_node;
TempNode=new_node->Next;
}
}
return true;
Let me try to explain what is happening in steps:
Head = malloc(sizeof(Node));
strcpy(&Head->word[j],buffer);
Head
----------
| |
| buffer |
| |
----------
Head->Next = hashtable->table[hashval];
Head=Head->Next;
TempNode = Head;
TempNode
Head
---------- -----------------
| | Next | |
| buffer | -----> | table[hashval] |
| | | |
---------- -----------------
^
This is now
lost forever
new_node = malloc(sizeof(Node));
strcpy(&new_node->word[j],buffer);
new_node->Next = hashtable->table[hashval];
TempNode=new_node->Next;
Head new_node TempNode
---------- ----------------- ---------- -----------------
| | Next | | | | Next | |
| buffer | -----> | table[hashval] | No link | buffer | -----> | table[hashval] |
| | | | here! | | | |
---------- ----------------- ---------- -----------------
^
This is now
lost forever
And so on.
I suspect that the "No link here!" part in my diagram above is causing your segfault. I also do not know how you intend to use your table[hashval], but here goes anyway. This solution just bridges the gaps in your linked list.
Solution:
Replace your while loop with this:
TempNode = Head = NULL;
while(fread(buffer,sizeof(buffer),1,inptr)!=0)
{
if(Head==NULL)
{
hashval = hash(buffer);
Head = malloc(sizeof(Node));
strcpy(&Head->word[j],buffer);
Head->Next = hashtable->table[hashval];
Head->Next->Next = NULL;
TempNode = hashtable->table[hashval] = Head;
}
else
{
new_node = malloc(sizeof(Node));
hashval = hash(buffer);
strcpy(&new_node->word[j],buffer);
new_node->Next = hashtable->table[hashval];
hashtable->table[hashval]=new_node;
new_node->Next->Next = NULL;
TempNode->Next = new_node;
TempNode = new_node;
}
TempNode = TempNode->Next;
}
Visually the differences are:
Head = malloc(sizeof(Node));
strcpy(&Head->word[j],buffer);
Head->Next = hashtable->table[hashval];
Head->Next->Next = NULL;
TempNode = hashtable->table[hashval] = Head;
TempNode
Head
---------- -----------------
| | Next | | Next
| buffer | -----> | table[hashval] | -----> NULL
| | | |
---------- -----------------
TempNode = TempNode->Next
Head TempNode
---------- -----------------
| | Next | | Next
| buffer | -----> | table[hashval] | -----> NULL
| | | |
---------- -----------------
new_node = malloc(sizeof(Node));
strcpy(&new_node->word[j],buffer);
new_node->Next = hashtable->table[hashval];
new_node->Next->Next = NULL;
Head TempNode new_node
---------- ----------------- ---------- -----------------
| | Next | | | | Next | |
| buffer | -----> | table[hashval] | No link | buffer | -----> | table[hashval] | ----> NULL
| | | | here! | | | |
---------- ----------------- ---------- -----------------
TempNode->Next = new_node;
Head TempNode new_node
---------- ----------------- ---------- -----------------
| | Next | | Next | | Next | |
| buffer | -----> | table[hashval] | -----> | buffer | -----> | table[hashval] | ----> NULL
| | | | | | | |
---------- ----------------- ---------- -----------------
TempNode = new_node;
TempNode
Head new_node
---------- ----------------- ---------- -----------------
| | Next | | Next | | Next | |
| buffer | -----> | table[hashval] | -----> | buffer | -----> | table[hashval] | ----> NULL
| | | | | | | |
---------- ----------------- ---------- -----------------
And so on.
Tips:
Free whatever memory you have malloced at the end.
Do not move your Head pointer (unless you need to delete the first element or for some other reason).

Resources