I have a project about linked lists but I'm having a hard time doing it. The teacher wants me to read a .txt file and create singly linked list from it. After that, I need to reverse odd numbers of every line. Then print it. Here is the code which I used for printing the linked list. But I need help to reverse the odd numbers of each line.
This is the code which I used to print the list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main(void) {
FILE *fp;
char line[10];
LIST *current, *head;
head = current = NULL;
fp = fopen("data.txt", "r");
while(fgets(line, sizeof(line), fp)){
LIST *node = malloc(sizeof(LIST));
node->string = strdup(line);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
}
fclose(fp);
for(current = head; current ; current=current->next){
printf("%s", current->string);
}
return 0;
}
Here is the content of the .txt file:
10
9,6,11,7,12,18,19,14,15,13
13,14,9,12,15,3,18,20,1,2
4,11,8,17,12,15,20,10,3,16
19,4,11,1,13,17,12,16,20,18
1,6,20,11,13,9,7,16,10,2
12,4,11,16,3,20,9,19,17,15
20,3,10,12,18,2,5,14,15,16
18,19,15,2,6,9,1,3,17,4
7,6,20,1,11,4,3,5,8,16
1,2,16,13,17,10,12,9,4,15
"But I need help to reverse the odd numbers of each line."
There are several other parts that need to be considered before this step can be developed.
Following are suggestions for a functions approach implementation using your problem description. A few items are simply suggestions to simplify the existing code. And a few other steps, are not mentioned as necessary, but should be considered:
Since you are not mandated to use char *string; in your problem description, choose to use a reasonable string length variable that does not require an additional layer of dynamic allocation, such as char string[260]; (or even smaller to fit your input file.) This will greatly simplify the code.
Because the input file is sized with lines ~30 char long, declare the variable line to be at least large enough to contain one line, eg 80 would allow larger values, and still allow enough space, but since memory is cheap, go with the same size as is used in the string member of your linked list.
Move the work of populating each new node to a function. It also will greatly simplify the program, and provide greater readability. Eg: void insert(LIST **head_ref, char *str);
Always test the return of fopen() before attempting to use the file descriptor.
To manipulate the contents of each odd row (eg 1, 3, 5, 7, 9), as numbers, the contents of each line read in from a file as a string, needs to first be converted to a collection of numbers. This suggests an additional member be added to the struct. For example int num[10].
The previous observation implicitly suggests the need of an additional function to parse and convert each comma delimited string into discrete integer values. Perhaps with the prototype: void parseIntArray(LIST **list);
The next and final task also suggests an additional function to reverse the contents of selected array member integer arrays. This one might use a prototype such as: void reverse_odd(LIST **list, size_t size);
Finally, because each node of LIST created required dynamically allocated memory, once finished using LIST, the memory must be given back to the OS to prevent memory leaks. An additional function to do this could be prototyped: void freeList(LIST **head);
Following are the main() function and preceding support declarations etc. It is intended here to illustrate the above suggested steps, and the benefits of breaking down a bigger problem into smaller problems, then implementing each smaller solution to support the whole. Benefits include for example readability and maintainability and potential re-use of code-base, (Note the similarity of argument lists in each supporting function.):
#define MAX_STRLEN 260 //use mnemonic values to avoid magic numbers in code
struct list {
char string[MAX_STRLEN];
int arr[10];
struct list *next;
};
typedef struct list LIST;
//Prototypes of 'smaller' solutions
void insert(LIST **head_ref, char *str);
void parseIntArray(LIST **list);
void reverse_odd(LIST **list, size_t size);
void freeList(LIST **head);
int main(void)
{
FILE *fp;
char line[MAX_STRLEN];
LIST *current, *head;
char *convPtr = NULL;
head = current = NULL;
fp = fopen("data.txt", "r");
if(fp)
{
//consume 1st line
if(fgets(line, sizeof(line), fp));//10
{
sizeArray = strtol(line, &convPtr, 10);
if(errno != ERANGE)
{
while(fgets(line, sizeof(line), fp))
{
//(see implementations of each below)
//create new node, insert num string
insert(¤t, line);
//convert new->string to integers, place in new->array
parseIntArray(¤t);
//reverse 'odd' contents of each array
reverse_odd(¤t, sizeArray);
}
}else{//handle error and leave}
}
fclose(fp);
}else{//handle error and leave}
//At this point in code, entire file is captured into nodes of list.
//use list as needed
//When finished using list, memory must be freed to prevent memory leaks
head = current;
freeList(&head);
return 0;
}
The remaining code segments are the function implementations used above:
void freeList(LIST **head)
{
LIST *tmp;
while (*head != NULL)
{
tmp = (*head);
(*head) = (*head)->next;
free(tmp);
}
}
//create new node, insert num string
void insert(LIST **head_ref, char *str)
{
int *arr = malloc(numNodes * sizeof(*arr));
//allocate node
LIST* new = calloc(1, sizeof(*new));
//put in the data
strcpy(new->string, str);
//Make next of new node as head
new->next = (*head_ref);
//Move the head to point to the new node
(*head_ref) = new;
}
//convert new->string to integers, place in list->array
void parseIntArray(LIST **list)
{
char *tok = NULL;
int i = 0;
int tmp = 0;
char *sArray = strdup((*list)->string);
tok = strtok(sArray, ",\n ");
while(tok)
{
errno = 0;
tmp = atoi(tok);
if(errno == ERANGE)
{
printf("Error converting string to number\nExiting.");
return;
}
(*list)->arr[i] = tmp;
i++;
tok = strtok(NULL, ",\n ");
}
}
//reverse 'odd' contents of list->array
void reverse_odd(LIST **list, size_t size)
{
int *ptr = &((*list)->arr[0]);
int *tmp = malloc(size * sizeof(*tmp));
memset(tmp, -1, size*sizeof(*tmp));
for(int i=0;i<size;i++)
{
if(ptr[i]%2 != 0)
tmp[size-1-i] = ptr[i];
}
for(int i=0;i<size;i++)
{
if(tmp[i] < 0)
{
while((*ptr)%2 != 0 ) ptr++;
tmp[i] = *ptr;
ptr++;
}
}
memcpy((*list)->arr, tmp, size*sizeof(int));
}
This hope this code will do the job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct line {
struct num *first;
struct line *next;
} LineNode;
typedef struct num {
int num;
int order;
struct num *next;
} NumNode;
int main() {
FILE *fp;
char ch;
int counter = 0;
NumNode *curr_num, *even_ptr, *odd_ptr, *odd_head, *even_head;
LineNode *curr_line, *line_head;
curr_num = even_head = odd_head = even_ptr = odd_ptr = NULL;
line_head = curr_line = NULL;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
ch = fgetc(fp);
while(ch != EOF){
if (ch >= 48 && ch <= 57)
{
int n = 0;
while (ch != EOF && ch != '\n' && ch >= 48 && ch <= 57)
{
int x = ch - 48;
n = n * 10 + x;
ch = fgetc(fp);
}
NumNode *node = malloc(sizeof(NumNode));
node->num = n;
node->order = counter;
node->next =NULL;
if (n % 2 == 0){
if(even_head == NULL){
even_head = even_ptr = node;
} else {
even_ptr = even_ptr->next = node;
}
}else{
if(odd_head == NULL){
odd_head = node;
} else {
node->next = odd_head;
odd_head = node;
}
}
counter++;
}
if (ch == '\n' || ch == EOF)
{
NumNode *num_node, *head;
num_node = head = NULL;
even_ptr = even_head;
odd_ptr = odd_head;
counter = 0;
if (even_head != NULL && even_head->order == counter){
head = num_node = even_ptr;
even_ptr = even_ptr->next;
} else {
head = num_node = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
while (even_ptr != NULL)
{
if (even_ptr->order == counter) {
num_node = num_node->next = even_ptr;
even_ptr = even_ptr->next;
}
else if (odd_ptr != NULL) {
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
}
while (odd_ptr != NULL)
{
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
LineNode *node = malloc(sizeof(LineNode));
node->next =NULL;
node->first = head;
if (line_head == NULL)
line_head = curr_line = node;
else
curr_line = curr_line->next = node;
odd_head = even_head = NULL;
counter = 0;
}
ch = fgetc(fp);
}
fclose(fp);
for(curr_line = line_head; curr_line != NULL ; curr_line=curr_line->next) {
for(curr_num = curr_line->first; curr_num != NULL ; curr_num=curr_num->next) {
printf("%d", curr_num->num);
if (curr_num->next != NULL)
printf(",");
}
printf("\n");
}
return 0;
}
I'm trying to setup a graph in C. I tried the graph with user input and it works perfectly. However, i am trying to implement a read from file. The last else statement is where the error is coming from because when i commented it out it compiles without any problems. I have included a comment over the block i think that has the problem. Please let me know if there is anything else needed for this question.
#include <stdio.h>
#include <stdlib.h>
struct node{
int data;
struct node* next;
};
//int counter and mainVertex would be used to determine if graph is connected.
// void graphConnection(){
//
//
//
//
//
//
// }
char* deblank(char* input)
{
int i,j;
char *output=input;
for (i = 0, j = 0; i<strlen(input); i++,j++)
{
if (input[i]!=' ')
output[j]=input[i];
else
j--;
}
output[j]=0;
return output;
}
struct node *G[1000];
int counter = 0;
char *mainVertex;
void readingEachLine(){
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
//Read file and exit if fail
fp = fopen("test.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
line = deblank(line);
int i = 0;
struct node* cursor = malloc(sizeof(struct node));
struct node* secondcursor = malloc(sizeof(struct node));
struct node* tempitem;
while(line[i] != '\n'){
//If its the first of the line look into the array and set struct cursor to the corresponding
//array position
if (i == 0){
mainVertex[counter] = line[0];
int convertor = line[i] - '0';
cursor = G[convertor];
counter++;
}
//if its not the first, then set a struct with that number as data
else{
tempitem = malloc(sizeof(struct node));
int convertor = line[i] - '0';
tempitem->data = convertor;
tempitem->next = NULL;
}
//if there is no element connected to the struct in array, connect the tempitem
if (cursor->next == NULL){
cursor->next = tempitem;
}
//If there are already connected elements, loop until the end of the linked list
//and append the tempitem
//ERROR: I GET SEGMENTATION FAULT FROM HERE. TRIED AFTER COMMENTING IT OUT
else{
secondcursor = cursor;
while(secondcursor->next != NULL){
secondcursor = secondcursor->next;
}
secondcursor->next = tempitem;
}
i++;
}
printf("\n");
}
}
int main(void){
for (int i = 1; i < 1000; i++)
{
G[i]= malloc(sizeof(struct node));
G[i]->data = i;
G[i]->next = NULL;
}
readingEachLine();
}
EDIT: This is how the text file looks like:
1 3 4
2 4
3 1 4
4 2 1 3
Your code has several misconceoptions:
Apparently, you can have a maximum of 1,000 nodes. You have an array G of 1,000 head pointers to linked lists. Don't allocate memory for all 1,000 nodes at the beginning. At the beginning, all lists are empty and an empty linked list is one that has no node and whose head is NULL.
In your example, cursor is used to iterate oer already existing pointers, so don't allocate memory for it. If you have code like this:
struct node *p = malloc(...);
// next use of p:
p = other_node;
you shouldn't allocate. You would overwrite p and lose the handle to the allocated memory. Not all pointers have to be initialised with malloc; allocate only if you create a node.
Your idea to strip all spaces from a line and then parse single digits will fail if you ever have more then 9 nodes. (But you cater for 1,000 node.) Don't try to parse the numbers yourself. There are library functions for that, for example strtol.
It is not clear what mainVertex is supposed to be. You use it only once, when you assign to it. You treat it like an array, but it is a global pointer, initialised to NULL. When you dereference it, you get undefined behaviour, which is where your segmentation fault probably comes from.
Here's a program that does what you want to do. (It always inserts nodes at the head for simplicity and it should have more allocation checks.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
maxNodes = 1000
};
struct node{
int data;
struct node* next;
};
struct node *G[maxNodes];
size_t nnode = 0;
int read_graph(const char *fn)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
fp = fopen(fn, "r");
if (fp == NULL) return -1;
while (getline(&line, &len, fp) != -1) {
char *p;
char *end;
int id;
int n;
id = strtol(line, &end, 10);
if (end == line) continue;
if (id < 1 || id > maxNodes) break;
if (id > nnode) nnode = id;
id--;
p = end;
n = strtol(p, &end, 10);
while (p != end) {
struct node *nnew = malloc(sizeof(*nnew));
nnew->data = n - 1;
nnew->next = G[id];
G[id] = nnew;
p = end;
n = strtol(p, &end, 10);
}
}
fclose(fp);
free(line);
return 0;
}
int main(void)
{
if (read_graph("test.txt") < 0) {
fprintf(stderr, "Couldn't gread raph.\n");
exit(1);
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
if (p) {
printf("%d:", i + 1);
for (; p; p = p->next) {
printf(" %d", p->data + 1);
}
puts("");
}
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
while (p) {
struct node *old = p;
p = p->next;
free(old);
}
}
return 0;
}
So I'm trying to insert data into a trie, and my code works fine. But then I change my insert function a little bit and it doesn't work anymore and also causes the memory leak. To me, both versions of insert do the same thing but obviously, they are not. Can someone please explain to me why? Thanks in advance.
Here is the code that works
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 26
#define hash(c) (tolower(c) - (int)'a')
typedef struct node{
bool endWord;
struct node* children[SIZE];
} node;
void freeTrie(node* root){
if(root == NULL) return;
for (size_t i = 0; i < SIZE; i++) {
freeTrie(root->children[i]);
}
free(root);
}
node* newNode(){
node* new = NULL;
new = (node*) malloc(sizeof(node));
if(new != NULL){
new->endWord = false;
for(int i = 0; i < SIZE; i++)
new->children[i] = NULL;
}
return new;
}
void insert(node* root, const char* data){
node* temp = root;
for (size_t i = 0, len = strlen(data); i < len; i++) {
int index = hash(data[i]);
if(temp->children[index] == NULL){
temp->children[index] = newNode();
if (temp->children[index] /*still*/ == NULL){
printf("Something went wrong\n");
return;
}
}
temp = temp->children[index];
}
temp->endWord = true;
}
bool search(node* root, const char* data){
node* temp = root;
for (size_t i = 0, len = strlen(data); i < len; i++) {
int index = hash(data[i]);
temp = temp->children[index];
if (temp == NULL){
printf("search end here\n");
return false;
}
}
return (temp != NULL && temp->endWord);
}
int main() {
char data[][8] = {"fox", "foo", "dog", "do"};
node* root = newNode();
if(root == NULL){
printf("Something went wrong\n");
return 1;
}
for (size_t i = 0, dataSize = sizeof(data)/sizeof(data[0]); i < dataSize; i++) {
insert(root, data[i]);
}
printf("Check: \n");
char output[][32] = {"not found", "found"};
// char s[5];
// fscanf(stdin, "%s", s);
printf("%s\n", output[search(root, "fox")]);
freeTrie(root);
printf("Done\n");
return 0;
}
Here is the insert that makes me confused
void insert(node* root, const char* data){
node* temp = root;
for (size_t i = 0, len = strlen(data); i < len; i++) {
int index = hash(data[i]);
temp = temp->children[index];
if(temp == NULL){
temp = newNode();
if (temp /*still*/ == NULL){
printf("Something went wrong\n");
return;
}
}
}
temp->endWord = true;
}
PS: I do this for a problem set of the CS50x course, in which I have to load a dictionary of 143091 words (in alphabetical order) into my trie. My program takes about 0.1s to load and 0.06s to unload when the staff's does the same job with just 0.02s and 0.01s. I am not allowed to see the staff's source code but I guess they used a trie to store data. How can I improve my code for faster runtime? Would it run faster if I store data in an array and then binary search instead?
When you write
temp = temp->children[index];
you copy value contained in temp->children[index] (I'll call it A) into a completely independent variable named temp. When you later modify temp, you modify temp only, not A. That is, all new nodes do not get inserted into the trie.
So I am trying to sort a linked list, from small to big based on the name. It sorts it but it is sorting it the reverse or wrong way.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
//declaring a struct
struct node {
char *name;
int num;
struct node *next;
};
struct node *list=NULL;
/*
* insert()
*/
struct node *insert(char word2[], int val){
struct node *tmp;
tmp = malloc(sizeof(struct node));
if(tmp ==NULL){
fprintf(stderr, "out of memory\n");
exit(1);
}
tmp->name = strdup(word2);
tmp->num = val;
tmp->next = list;
list = tmp;
return tmp;
}//read string
void print(){
struct node *ptr;
for(ptr= list; ptr!=NULL; ptr = ptr->next){
printf(" %s/%d\n", ptr->name, ptr->num);
}//for loop
}//print
void sort(){
struct node *ptr1, *ptr2;
char *tmp;
for(ptr1 = list; ptr1!=NULL; ptr1 = ptr1->next){
for(ptr2 = ptr1->next; ptr2!=NULL; ptr2 = ptr2->next){
if(strcmp(ptr1->name, ptr2->name)>0){
//ptr1->name is "greater than" ptr2->name - swap them
tmp = ptr1->name;
ptr1->name = ptr2->name;
ptr1->name = tmp;
}
}
}
}//sort
int main (){
char buff[81];
int status=0;
int len;
char word1[20];
char word2[20];
int val;
// char word3[20];
while(fgets(buff, 81, stdin)>0){
len = strlen(buff);
if(buff[len-1]!='\n'){
fprintf(stderr,"Error: string line length was too long\n");
exit(1);
}
sscanf(buff, "%s %s %d", word1, word2, &val);
if(strcmp(word1, "insert")==0){
insert(word2, val);
sort();
}else if(strcmp(word1, "print")==0){
print();
}else{
}
}//while loop
return status;
}
This is what my input looks like when I run it.
"insert a 1"
"insert b 2"
"insert c 3"
"print"
output
c/3
b/2
a/1
If have tried changing my sort method condition but it keeps sorting it the wrong way. I can't seem to find the bug. Any help will be greatly appreciated. But my output is supposed to look like this
Desired output
a/1
b/2
c/3
If you sort after every insert you do not need the full bubble-sort, just one round.
void sort()
{
struct node *ptr1 = list;
char *tmpname;
int tmpnum;
while (ptr1->next != NULL) {
if (strcmp(ptr1->name, ptr1->next->name) > 0) {
// swap content in this case, not the nodes
tmpname = ptr1->name;
tmpnum = ptr1->num;
ptr1->name = ptr1->next->name;
ptr1->num = ptr1->next->num;
ptr1->next->name = tmpname;
ptr1->next->num = tmpnum;
}
ptr1 = ptr1->next;
}
}
I have been working on this little project for quite some time and I can't figure out why I'm not getting the results that are expected. I am a beginner to C programming so my understanding with pointers and memory allocation/deallocation is novice. Anyways, I have constructed this segment of code by originally building a hash function, then adding a count to it. However, when I test it, sometimes the count works, sometimes it doesn't. I'm not sure whether it's the fault of the hash function, or the fault of the way I set up my count. The text file is read one line at a time and is a string consisting of a hexadecimal.
struct node {
char *data;
struct node *next;
int count; /* Implement count here for word frequencies */
};
#define H_SIZE 1024
struct node *hashtable[H_SIZE]; /* Declaration of hash table */
void h_lookup(void)
{
int i = 0;
struct node *tmp;
for(i = 0; i < H_SIZE; i++) {
for(tmp = hashtable[i]; tmp != NULL; tmp = tmp->next) {
if(tmp->data != 0) {
printf("Index: %d\nData: %s\nCount: %d\n\n", i,
tmp->data, tmp->count);
}
}
}
}
/* self explanatory */
void h_add(char *data)
{
unsigned int i = h_assign(data);
struct node *tmp;
char *strdup(const char *s);
/* Checks to see if data exists, consider inserting COUNT here */
for(tmp = hashtable[i]; tmp != NULL; tmp = tmp->next) {
if(tmp->data != 0) { /* root node */
int count = tmp->count;
if(!strcmp(data, tmp->data))
count= count+1;
tmp->count = count;
return;
}
}
for(tmp = hashtable[i]; tmp->next != NULL; tmp = tmp->next);
if(tmp->next == NULL) {
tmp->next = h_alloc();
tmp = tmp->next;
tmp->data = strdup(data);
tmp->next = NULL;
tmp->count = 1;
} else
exit(EXIT_FAILURE);
}
/* Hash function, takes value (string) and converts into an index into the array of linked lists) */
unsigned int h_assign(char *string)
{
unsigned int num = 0;
while(*string++ != '\0')
num += *string;
return num % H_SIZE;
}
/* h_initialize(void) initializes the array of linked lists. Allocates one node for each list by calling h_alloc which creates a new node and sets node.next to null */
void h_initialize(void)
{ int i;
for(i = 0; i <H_SIZE; i++) {
hashtable[i] = h_alloc();
}
}
/* h_alloc(void) is a method which creates a new node and sets it's pointer to null */
struct node *h_alloc(void)
{
struct node *tmp = calloc(1, sizeof(struct node));
if (tmp != NULL){
tmp->next = NULL;
return tmp;
}
else{
exit(EXIT_FAILURE);
}
}
/* Clean up hashtable and free up memory */
void h_free(void)
{
struct node *tmp;
struct node *fwd;
int x;
for(x = 0; x < H_SIZE; x++) {
tmp = hashtable[x];
while(tmp != NULL) {
fwd = tmp->next;
free(tmp->data);
free(tmp);
tmp = fwd;
}
}
}
I assume that the count is not being incremented when it does not work. It is possible that strdup is not able to allocate memory for the new string and is returning NULL. You should check the return value to and exit gracefully if it fails.