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).
Related
I am trying to create a dynamic array of simple linked lists. The MAX_SIZE is 5. I would like to insert
a new node, for example the character 'c', at (link[5]). For every new character, i would like to allocate a new entry at the bottom of the array.
When i execute my code, I get this output above. I think there is a problem with realloc
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
but the expected output is below:
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c
My code is here
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node *next;
} Node;
int push_front( Node **head, char data )
{
Node *new_node = malloc( sizeof( Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output( Node **head )
{
for( Node *current =*head; current != NULL && current->data != -1 ; current = current->next )
{
printf( "%c ", current->data );
}
printf( "%s", "NULL" );
}
void display( Node **set, int n )
{
for (int i = 0; i < n; i++){
printf("[%d] ", i);
output(&(set[i]));
printf("\n");
}
}
#define N 5
int main(void)
{
//Node * link[N] = { 0 };
Node **link;
link = calloc(N , sizeof (Node*));
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
push_front( &link[0], 'b' );
push_front( &link[0], 'a' );
push_front( &link[1], 'c' );
push_front( &link[2], 'd' );
push_front( &link[2], 'e' );
push_front( &link[2], 'f' );
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
push_front( &link[5], 'c' );
display(link, N);
return 0;
}
OP is doing some serious mistake while allocating memory to list and the accepted answer is not pointing out those mistakes. Hence, I am posting this answer.
I am trying to create a dynamic array of simple linked lists....
The way you are allocating memory, initially, you are creating array of array's of Node type objects.
From calloc(): [emphasis added]
void* calloc( size_t num, size_t size );
Allocates memory for an array of num objects of size and initializes all bytes in the allocated storage to zero.
So, this loop:
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
will end up allocating memory for an array of N objects of size of Node type to link[i] pointer, where value of i lies in [0, 5).
The in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----------------------
link[0] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[1] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[2] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
----
After calling push_front() on link array elements, the in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----- ----- ----------------------
link[0] | | -> |a:n| -> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----------------------
| |
---- ----- ----------------------
link[1] | | -> |c:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----------------------
| |
---- ----- ----- ----- ----------------------
link[2] | | -> |f:n| -> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----- ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
----
Here:
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
the link pointer will be reallocated the memory of new size N + 1 (assuming realloc() was success).
and, this loop:
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
will end up reallocating link[i] pointer to memory of size N + 1 objects of Node type. You don't need to realloc() the link[i] pointers. The link[i] pointers are head of their respective linked list.
Now, you must be thinking - why printing of list giving expected output?
Because the reallocation is done by either:
a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined.
b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.
So, link[i] pointers were pointing to a memory of a Node type object and realloc() will maintain the content of this memory after reallocation.
After reallocation, the in-memory view would be something like this:
(read `n` as `next` pointer, x as some garbage value
and `:` is separator between `data` and `next` member of a node)
---- ------------------------- ----- ----------------------
link[0] | | -> |a:n|x:n|x:n|x:n|x:n|x:n| |-> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----------------------
| | \----------------------/
---- ------------------------- ----------------------
link[1] | | -> |c:n|x:n|x:n|x:n|x:n|x:n| |-> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----------------------
| | \----------------------/
---- ------------------------- ----- ----- ----------------------
link[2] | | -> |f:n|x:n|x:n|x:n|x:n|x:n| |-> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----- ----------------------
| | \----------------------/
---- --------------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- --------------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- -------------------------
link[5] | | -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | -------------------------
| |
----
You can see the next pointers are preserved after reallocation and that's why when you are printing the list you are getting output as expected. I hope this giving you clear picture of mistakes you are making while allocating memory.
Now, after this statement
push_front( &link[5], 'c' );
the in-memory view of link[5] would be something like this:
---- ----- -------------------------
link[5] | | -> |c|n| -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | ----- -------------------------
| |
----
Note that your output() may not work as expected while printing the link[5] list because the output() function for loop works on condition current != NULL && current->data != -1 and neither realloc() initialise memory to 0 (as the calloc() does) and nor you are assigning -1 to list[5]->data.
You don't need to allocate memory to link[i] pointers. The push_front() function is allocating the memory and creating a new node and adding that node in the array member list whose address passed as argument to push_front() function. Also, you should take care of freeing the list once done with them i.e. free the dynamically allocated memory before exiting.
Putting these altogether:
#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct Node {
char data;
struct Node *next;
} Node;
int push_front (Node **head, char data) {
Node *new_node = malloc (sizeof (Node));
int success = new_node != NULL;
if (success) {
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output (Node **head) {
for (Node *current = *head; current != NULL; current = current->next) {
printf ("%c ", current->data);
}
printf ("%s", "NULL");
}
void display (Node **set, int n) {
for (int i = 0; i < n; i++) {
printf ("[%d] ", i);
output (&(set[i]));
printf ("\n");
}
}
Node ** reallocate_list (Node **list, size_t old_size, size_t new_size) {
if (old_size == new_size) {
return list;
}
Node **temp = realloc (list, new_size * sizeof(Node*));
if (temp == NULL) {
printf ("Failed to allocate memory.\n");
/* do cleanup stuff and handle the allocation failure
* the way you want. I am simply exiting on allocation failure.
*/
exit (EXIT_FAILURE);
}
if (old_size < new_size) {
/* Initialise the newly added elements in the array
*/
for(size_t i = old_size; i < new_size; i++){
list[i] = NULL;
}
}
return temp;
}
void free_list (Node **head) {
Node *current = *head;
while (current) {
Node *x = current->next;
free (current);
current = x;
}
*head = NULL;
}
void free_all_list (Node **list, size_t num_of_lists) {
for (size_t i = 0; i < num_of_lists; i++) {
free_list (&list[i]);
}
}
int main (void) {
Node **link;
link = calloc(N , sizeof (Node*));
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
printf ("link array size : %d\n", N);
/* handle the push_front() return value
*/
push_front (&link[0], 'b');
push_front (&link[0], 'a');
push_front (&link[1], 'c');
push_front (&link[2], 'd');
push_front (&link[2], 'e');
push_front (&link[2], 'f');
display (link, N);
printf ("Reallocating link array, new size : %d\n", N + 1);
link = reallocate_list (link, N, N + 1);
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
push_front (&link[5], 'c');
display (link, N + 1);
free_all_list (link, N + 1);
free (link);
return 0;
}
Output:
# ./a.out
link array size : 5
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
Reallocating link array, new size : 6
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c NULL
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!
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.
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 |
--------------- --------------- ---------------
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