Insert function hash table C - c

I am having trouble implementing my insert function for my hash table.
So I implement some test calls where I just call the function separately. For actual use, I call the function inside a while loop. For testing purpose, I only run the loop 4 times.
I post some outputs below. The reason the table looks weird is because of my hash function. It hashes the words such that A = 1, B = 2, C = 3, and so on. The position of the letter in the word is irrelevant, since I will consider permutations of the word. Moreover, the case of the letter will be irrelevant in this problem as well, so the value of a = the value of A = 1.
And for strings, abc = 1 + 2 + 3 = 6, bc = 2 + 3 = 5, etc.
Overall, the hash function is fine. The problem is the insert function.
The first 4 words of my local dictionary are A, A's, AA's, AB's.
My expected output should be (I got the same output when I run the test calls):
0:
1: [W: A, Len:1]
2:
3:
...
18:
19:
20: [W: A's, Len:3]
21: [W: AA's, Len:4]
22: [W: AB's, Len:4]
But when I call the function inside a loop, whatever is last on the list will overwrite other entries. If I run the loop 100 times, then the last entry still replaces the previous ones (Notice how the lengths of the words are unchanged, but only the words are replaced):
0:
1: [W: AB's, L:1]
2:
3:
...
18:
19:
20: [W: AB's, Len:3]
21: [W: AB's, Len:4]
22: [W: AB's, Len:4]
Below is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int hash(char *word)
{
int h = 0;
while(*word != '\0')
{
if(*word >='A' && *word < 'A'+26) {
h=h+(*word -'A' + 1);
}
else if(*word >='a' && *word < 'a'+26) {
h=h+(*word -'a' + 1);
}
//else { // special characters
// return -1;
//}
word++;
}
return h;
}
typedef struct Entry {
char *word;
int len;
struct Entry *next;
} Entry;
#define TABLE_SIZE 1000 // random numbers for testing
Entry *table[TABLE_SIZE] = { NULL }; // an array of elements
void init() {
int i;
for (i = 0; i < TABLE_SIZE; i++) {
// initialize values
struct Entry *en = (struct Entry *)malloc(sizeof(struct Entry));
en->word = "";
en->len = 0;
en->next = table[i];
table[i] = en;
}
}
//Insert element
void insertElement(char *word, int len) {
int h = hash(word);
int i;
// because all words are different so there is no need to check for duplicates
struct Entry *en = (struct Entry *)malloc(sizeof(struct Entry));
en->word = word;
en->len = len;
en->next = table[h];
table[h] = en;
}
void cleanTable()
{
struct Entry *p, *q;
int i;
for( i=0; i<TABLE_SIZE; ++i )
{
for( p=table[i]; p!=NULL; p=q )
{
q = p->next;
free( p );
}
} // for each entry
}
int main() {
init(); // create hash table
// test calls produce correct output
//insertElement("A", (int)strlen("A"));
//insertElement("A's", (int)strlen("A's"));
//insertElement("AA's", (int)strlen("AA's"));
//insertElement("AB's", (int)strlen("AB's"));
int i;
i = 0;
FILE* dict = fopen("/usr/share/dict/words", "r"); //open the dictionary for read-only access
if(dict == NULL) {
return;
}
// Read each line of the file, and insert the word in hash table
char word[128];
while(i < 4 && fgets(word, sizeof(word), dict) != NULL) {
size_t len = strlen(word);
if (len > 0 && word[len - 1] == '\n') {
word[len - 1] = '\0'; // trim the \n
}
insertElement(word, (int)strlen(word));
i++;
}
for ( i=0; i < 50; i++)
{
printf("%d: ", i);
struct Entry *enTemp = table[i];
while (enTemp->next != NULL)
{
printf("[W: %s, Len:%d] ", enTemp->word, enTemp->len);
enTemp = enTemp->next;
}
printf("\n");
}
cleanTable();
return 0;
}

Try to reallocate the memory in each loop in this part of code:
char* word = malloc(sizeof(char)*128);
while(i < 4 && fgets(word, sizeof(word), dict) != NULL) {
size_t len = strlen(word);
if (len > 0 && word[len - 1] == '\n') {
word[len - 1] = '\0'; // trim the \n
}
insertElement(word, (int)strlen(word));
word = malloc(sizeof(char)*128);
i++;
}
You forgot to reallocate memory to every string which causes all pointers points at same point
Note: Not tested

notice that your insertElement get a pointer to a string, and assign that pointer to the current Entry, but its the main function, you pass the word argument(a pointer) that point the stack allocated string, and that string is changed after each read of a word. you must use malloc so that each word point to its own memory area

Related

Why does the printf function affect my speller program?

I am using a hash function from the internet and when I use a print function before the return statement, it makes my program correct, but if I remove it, it becomes bugged again... like literally frustrating because I can do printf("asfasfnasfnk\n"); and it would output correctly but the moment I delete the printf function its bugged again...
unsigned int hash(const char *word)
{
/* credits to...
*https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/
*/
unsigned long hash = 0;
int n = strlen(word);
for (int i = 0; i < n; i++)
{
hash = (hash << 2) ^ word[i];
}
return hash % N;
}
Output:
MISSPELLED WORDS
A
is
not
a
caterpillar
WORDS MISSPELLED: 5
WORDS IN DICTIONARY: 2
WORDS IN TEXT: 6
TIME IN load: 0.00
TIME IN check: 0.00
TIME IN size: 0.00
TIME IN unload: 0.00
TIME IN TOTAL: 0.00
unsigned int hash(const char *word)
{
/* credits to...
*https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/
*/
unsigned long hash = 0;
int n = strlen(word);
for (int i = 0; i < n; i++)
{
hash = (hash << 2) ^ word[i];
}
printf("%s -> %lu\n", word, hash%N);
return hash % N;
}
Output:
cat -> 1984
caterpillar -> 109622
MISSPELLED WORDS
a -> 97
A
cat -> 1984
is -> 471
is
not -> 1832
not
a -> 97
a
caterpillar -> 109622
WORDS MISSPELLED: 4
WORDS IN DICTIONARY: 2
WORDS IN TEXT: 6
TIME IN load: 0.00
TIME IN check: 0.00
TIME IN size: 0.00
TIME IN unload: 0.00
TIME IN TOTAL: 0.00
The words in the dictionary is cat and caterpillar, the words in the text is "A cat is not a caterpillar"
Functions:
// Implements a dictionary's functionality
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 200000;
// Hash table
node *table[N];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
// TODO
int len = strlen(word);
char *copy = malloc(sizeof(char) * len + 1);
// change into lowercase the word
for (int i = 0; i < len; i++)
{
copy[i] = tolower(word[i]);
}
// get the index by using the hash function
int index = hash(copy);
node *tmp = table[index];
// check if the word is in the hash table
while (tmp != NULL)
{
if (strcmp(tmp->word, copy) == 0)
{
free(copy);
return true;
}
tmp = tmp->next;
}
free(copy);
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
/* credits to...
*https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/
*/
unsigned long hash = 0;
int n = strlen(word);
for (int i = 0; i < n; i++)
{
hash = (hash << 2) ^ word[i];
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
// TODO
char *words = malloc(sizeof(char) * (LENGTH + 1));
if (words == NULL)
{
return 1;
}
// initialize the hash table to NULL
for (int i = 0; i < N; i++)
{
table[i] = NULL;
}
// open dictionary file
FILE *indata = fopen(dictionary, "r");
// 1 character for '\0' and another for '\n' because fgets takes a trailing new line
// when it reads 'man' the value of words will be "man\n\0" so meaning 2 extra characters
while (fgets(words, LENGTH + 2, indata) != NULL)
{
// get rid of the trailing new line from fgets
words[strlen(words) - 1] = '\0';
// allocate memory for the newNode
node *newNode = malloc(sizeof(node));
if (newNode == NULL)
{
return false;
}
// get the index by using the hash function
int index = hash(words);
strcpy(newNode->word, words);
// make the newNode the head of the list
newNode->next = table[index];
table[index] = newNode;
}
// free memory and close the opened file
free(words);
fclose(indata);
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
// TODO
// counter of words loaded
unsigned int counter = 0;
// loop through the hash table
for (int i = 0; i < N; i++)
{
node *tmp = table[i];
while (tmp != NULL)
{
counter++;
tmp = tmp->next;
}
}
return counter;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
// TODO
// loop through the whole hash table
for (int i = 0; i < N; i++)
{
while (table[i] != NULL)
{
node *tmp = table[i]->next;
free(table[i]);
table[i] = tmp;
}
}
return true;
}
// TODO
int len = strlen(word);
char *copy = malloc(sizeof(char) * len + 1);
// change into lowercase the word
for (int i = 0; i < len; i++)
{
copy[i] = tolower(word[i]);
}
// get the index by using the hash function
int index = hash(copy);
Notice that + 1 in the malloc. Why is that there? It's to allow space for the terminating zero byte that marks the end of the string.
Say the string is "test". Then strlen will return 4. Your loop will iterate from 0 to 3 inclusive, copying the four letters in the word.
But you will not copy the terminating zero byte on the end of the string. When hash calls strlen on copy, who knows what value it will get since what you have passed it is not a legal string.
Change the condition in the for loop to i <= len.

Getting the count of each word in a sorted array

The goal of the following code segment is to take a sorted array of strings, and count how many of each word there is.
That information is then put into a struct called reduceNode that holds a string and a count for the given string.
The reduceNode structs are put into another array.
Once all of the words and their counts are found and put into the intermediate array, they are inserted into a global array of reduceNode structs.
This method is called by threads, which is why I am storing the results into a global array.
Anytime I run this part of the program, I am getting a segfault.
I assume I am accessing an array out of bounds, but I am having trouble narrowing down where I am doing so.
void* reduce(void* num) //Reduce function
{
int index = *(int*)num;
int curSize = 0; //Size of the current linked list
struct HashNode *head = HashTable[index]; //Get the head of the linked list from the hashtable
struct HashNode *linkedList = head; //Pointer to the head to traverse the linked list
while(linkedList != NULL) //Gets the size of the current linked list
{
curSize++;
linkedList = linkedList->next;
}
linkedList = head;
int linkedListTraverse = 0; //Array index for each linked list node
int numSort[curSize];
char* wordSort[curSize];
while(linkedList != NULL)
{
if(app == 1)
numSort[linkedListTraverse] = linkedList->num; //Copy the data from the linked list into an array
else
{
wordSort[linkedListTraverse] = (char*) malloc(sizeof(linkedList->string));
strcpy(wordSort[linkedListTraverse],linkedList->string); //Copy the data from the linked list into an array
}
linkedList = linkedList->next;
linkedListTraverse++;
}
if(app == 1)
{
qsort(numSort, curSize, sizeof(int), numCmpFunc); //Sort the current node
int i, j = 0;
reduceNode* numSortArray[curSize];
reduceNode* curNum;
for(i = 0; i < curSize; i++)
{
curNum = (reduceNode*) malloc(sizeof(reduceNode));
curNum->num = numSort[i];
numSortArray[i] = curNum;
}
i = 0;
while(sortedArray[i] != NULL)
{
i++;
}
for(j = 0; j < curSize; j++, i++)
{
sortedArray[i] = numSortArray[j];
}
return (void*) 0;
}
else
{
int i = 0;
while(i < curSize) //Convert all of the words to lowercase
{
char* str = wordSort[i];
char *p;
for (p = str; *p != '\0'; p++)
*p = (char)tolower(*p);
i++;
}
qsort(wordSort, curSize, sizeof(char*), stringCmpFunc); //Sort the current node
}
int curWordIndex = 0; //Exclusively for wordcount
int checkWordIndex = 1;
int curArrayIndex = 0;
reduceNode *curWord;
reduceNode* wordCountArray[curSize];
while(curWordIndex < curSize)
{
curWord = malloc(sizeof(reduceNode));
curWord->word = wordSort[curWordIndex]; //Set the word
curWord->count = 1; //Start the count out at 1
while(strcmp(wordSort[curWordIndex], wordSort[checkWordIndex]) == 0) //While the two words are equal
{
checkWordIndex++; //Advance the leading index check
curWord->count++;
if(checkWordIndex >= curSize) //If the leading index goes beyond the array bounds
{
break;
}
}
if(checkWordIndex <= curSize)
{
curWordIndex = checkWordIndex;
checkWordIndex = curWordIndex + 1;
}
else if(checkWordIndex >= curSize) //If the leading index goes beyond the array bounds
{
if(strcmp(curWord->word, wordSort[curWordIndex]) != 0)
{
curWord->word = wordSort[curWordIndex]; //Set the word
curWord->count = 1; //Start the count out at 1
curArrayIndex++;
wordCountArray[curArrayIndex] = curWord;
}
else
{
wordCountArray[curArrayIndex] = curWord;
curArrayIndex++;
}
break;
}
wordCountArray[curArrayIndex] = curWord;
curWord = NULL;
curArrayIndex++;
}
int i,j = 0;
while(sortedArray[i] != NULL)
{
i++;
}
for(j = 0; j < curSize; j++, i++)
{
sortedArray[i] = wordCountArray[j];
}
return (void*) 0;
}
reduceNode is defined as
typedef struct reduceNode
{
int count;
char *word;
int num;
} reduceNode;
sortedArray is declared globally as
reduceNode **sortedArray;
and later initialized as
sortedArray = (reduceNode **)calloc(1,sizeof(reduceNode*)*inputCount);
Input count is the number of words that are read into the program
An example input would be an array: [alpha, alpha, bravo, charlie, charlie, charlie, delta]. The expected result would be [alpha 2, bravo 1, charlie 3, delta 1].
1.
You checkWordIndex reaches exactly curSize and strcmp(wordSort[curWordIndex], wordSort[checkWordIndex] will go out of bounds. I recomment printing the indicies for debugging.
if(checkWordIndex < curSize)
{
curWordIndex = checkWordIndex;
checkWordIndex = curWordIndex + 1;
}
this code will still lead to checkWordIndex == curSize
2.
You allocate new memory, remember to free it.
3.
For thread safety lookup mutex in C.
I recommend using only one indicie and iterating like
while(index < cursize-1)
{
...
++index;
}
your fist indicie is index and your second is index+1.

Count occurrences and associate with given array in C

I'm having issues to correct my code so that it works as I want it.
I have three arrays given in this example:
char arr[MAX_ELEMENTS][MAX_LENGTH] = {"ABS","ABS","ABS","ACT","ACT","PPB","PPB","QQQ","QQQ"};
char race[MAX_ELEMENTS][MAX_LENGTH] = {"PARI", "PARI", "LOND", "PARI", "PARI", "CYKA", "LOND", "CYKA", "PARI"};
int freq[MAX_ELEMENTS];
I wish to create a function that can count the amount of occurrences of string elements in arr[] and store them in freq[]. Apart from that I also wish to know in what race[] there have been the most occurrences of given arr[].
To demonstrate this here is an example of what output I wish to receive when the function works:
In Race [PARI] the highest occurence was [ABS] with 3 occurences!
In Race [LOND] the highest occurence was [ACT] with 1 occurences!
.....
Currently, I am able to count the occurrences of arr[] in freq[] but I can't associate them with their respective race[] and give that output..
for(i=0; i<size; i++)
{
count = 1;
for(j=i+1; j<size; j++)
{
/* If duplicate element is found */
if(strcmp(arr[i], arr[j])==0)
{
count++;
/* Make sure not to count frequency of same element again */
freq[j] = 0;
}
}
/* If frequency of current element is not counted */
if(freq[i] != 0)
{
freq[i] = count;
}
}
Giving me currently :
ABS occurs 3 times.
ACT occurs 2 times.
etc. etc...
But I don't know how I can associate them with the race[] and only count them if a given race.
You probably have to use struct here to format your data.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define true 1
#define len 100
#define elms 10
struct NODE;
#define Node struct NODE
struct NODE {
unsigned long int val;
int count;
char name[len];
Node *left;
Node *right;
};
Node * makeNode(char * str, unsigned long int val){
Node * tmp = (Node *)malloc(sizeof(Node));
strcpy(tmp->name, str);
tmp->val = val;
tmp->left = NULL;
tmp->right = NULL;
tmp->count = 1;
return tmp;
}
unsigned long int getHash(char * name){
int prime = 19;
int i = 0;
unsigned long int val = 0;
while(name[i]!='\0'){
val += (name[i] * pow(prime, i) );
++i;
}
return val;
}
void insert(Node * root, char * name){
Node * newnode;
int val = getHash(name);
Node * tmp = root;
while(tmp != NULL) {
if ( tmp->val == val){
tmp->count += 1;
break;
}
if (val > tmp->val){
if( tmp->right != NULL)
tmp = tmp->right;
else{
tmp->right = makeNode(name, val);
break;
}
}else {
if( tmp->left != NULL)
tmp = tmp->left;
else{
tmp -> left = makeNode(name, val);
break;
}
}
}
}
Node * find(Node * root, char * name){
int val = getHash(name);
Node * tmp = root;
while(tmp != NULL){
if(tmp -> val == val){
return tmp;
}else if (val > tmp->val){
tmp = tmp->right;
}else{
tmp = tmp->left;
}
}
return NULL;
}
struct Race {
char name[len];
char elements[elms][len];
};
char arr[elms][len] = {"ABS","ABS","ABS","ACT","ACT","PPB","PPB","QQQ","QQQ"};
char race[elms][len] = {"PARI", "PARI", "LOND", "PARI", "PARI", "CYKA", "LOND", "CYKA", "PARI"};
int freq[elms];
void copyArray(char dest[elms][len], char src[elms][len] ){
int i = 0;
while(strlen(src[i]) > 0){
strcpy(dest[i],src[i]);
++i;
}
}
int main(){
Node * root = makeNode("root", 0);
int i = 0;
while(strlen(arr[i]) > 0){
insert(root,arr[i]);
++i;
}
i = 0;
while(strlen(arr[i]) > 0){
Node * r = find(root,arr[i]);
printf("found %s, count = %ld\n", r->name, r->count);
++i;
}
// make representation of race
struct Race r1, r2;
strcpy(r1.name, "PARI");
{
char tmp[elms][len] = { "ABS", "PPB", "QQQ" };
copyArray(r1.elements, tmp);
}
strcpy(r2.name, "LOND");
{
char tmp[elms][len] = { "ACT" };
copyArray(r2.elements, tmp);
}
struct Race races[2] = {r1, r2};
i = 0;
while(i < 2){
struct Race * current = &races[i];
printf("for %s", current->name);
Node * max = NULL;
int m = -1;
int j = 0;
while(strlen(current->elements[j]) > 0){
Node * tmp = find(root, current->elements[j]);
if( tmp != NULL && tmp->count > m) {
max = tmp;
m = tmp->count;
}
++j;
}
if (max != NULL){
printf(" max is %s : %d\n", max->name, max->count);
}else{
printf(" max is None\n");
}
++i;
}
return 0;
}
Basically you have to format you data, and specify link between them. Here I used Binary tree and Rabin karp hashing technique to store data efficiently.
Binary tree is best way to solve counting problem, since the search operation fairly cheap. and Rabin karp hashing technique will avoid string comparison every time.
And I create a struct called Race to store all related elements of that race. so the algorithm is going to be.
let arr be array of elements
let races be array of races
for each race in races
define related element
#find occurrence now
#Binary tree will increment count if element already exist.
let binary_tree be a Binary Tree
for each element in arr
add element to binary_tree
# now we have all the elements with it's count
# let's iterate through races now
for each race in races
m = null
for element in race.elements
node = find_element_in_binary_tree(element)
if node is not null
m = max(m, node)
if m is not null then
print m
else
print not found
First, initializations, note the []s
char arr[][MAX_LENGTH] = {"ABS","ABS","ABS","ACT","ACT","PPB","PPB","QQQ","QQQ"};
char race[][MAX_LENGTH] = {"PARI","PARI","LOND","PARI","PARI","CYKA","LOND","CYKA","PARI"};
int freq[MAX_ELEMENTS];
int n = sizeof(arr)/sizeof(*arr); // get actual number of used items
int i,j;
int max = 0; // init max to 0
The main loop goes through arr and race, and whenever a dupe is found at [j] (after [i]), "invalidate" the dupe ("already processed") by setting its first char to 0 (empty string).
Note that j starts from i and not i+1 to ensure freq is at least 1, even for the first non-dupes items.
for(i=0 ; i<n ; i++) {
freq[i] = 0; // ensure freq is 0 for any item
if ( ! *arr[i]) continue; // skip already processed items
for(j=i ; j<n ; j++) { // j=i, not i+1!
if (!strcmp(arr[i],arr[j]) && !strcmp(race[i],race[j])) {
freq[i]++; // update max if necessary
if (freq[i] > max) max = freq[i];
if (j > i) *arr[j] = 0; // invalidate that arr element
}
}
}
Finally display the max appearances, including ties
printf("Items at max=%d:\n", max);
for(i=0 ; i<n ; i++) {
if (freq[i] == max) { // skipped items are never displayed (max cannot be 0)
printf("%s / %s\n", arr[i],race[i]);
}
}
(no need to check for "invalidation" as max will be >0, and all invalidated items have freq[i] == 0)

Function that extracts words from text ( array of chars ) and put them in 2 dimensions array

I'm learning C and have some struggles.I have to make a program , which becomes a text (max 80 chars) and put the words from text in a char words[80][80] (every word must be only single time in this array! it is also defined as global) and count of times every word comes in the text in a int count[] (Index must be same as this from words[][]).
The function is called int extract_and_count(char *source,int *count).
I wrote some code ,but I'm not sure how exactly to implement this function.Can someone help me?
I'm also new to stackoverflow so if I have made any mistake, sorry.
Thats some of the code but its not to the end:
int extract_and_count(char *source,int *count){
char token[80][80];
char *p;
int i = 0;
p = strtok(source, " ");
while( p != NULL ){
strcpy(token[i],p);
printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " ");
}
char word;
int value = 0, j;
for(i = 0 ; i < 80 ; i++){
word = token[i];
for(j = 0 ; j < 80 ; j++){
if(strcmp(word,token[i])==0){
value++;
}
}
}
return 1;
}
You need to check if a word has been found already. If so, just increment the global counter. Otherwise, copy the new word to the global array of strings.
Something like:
#include <stdio.h>
#include <string.h>
// Global variables to hold the results
char word[80][81];
int count[80] = { 0 };
int extract_and_count(char *source,int *strings_cnt){
char token[80][81];
char *p;
int i = 0;
// Find all words in the input string
p = strtok(source, " ");
while( p != NULL ){
strcpy(token[i],p);
// printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " ");
}
// Find unique words and count the number a word is repeated
*strings_cnt = 0;
int j,k;
// Iterator over all words found in the input string
for(j = 0 ; j < i ; j++){
// Check if the word is already detected once
int found = 0;
for(k = 0 ; k < *strings_cnt ; k++){
if (strcmp(word[k], token[j]) == 0)
{
// The word already exists - increment count
found = 1;
count[k]++;
break;
}
}
if (!found)
{
// New word - copy it and set count to 1
strcpy(word[*strings_cnt], token[j]);
count[*strings_cnt] = 1;
(*strings_cnt)++;
}
}
return 1;
}
int main(void)
{
char s[] = "c language is difficult c is also fun";
int c, i;
printf("Searching: %s\n", s);
extract_and_count(s, &c);
printf("Found %d different words\n", c);
for (i=0; i<c; i++)
{
printf("%d times: %s\n", count[i], word[i]);
}
return 0;
}
Output:
Searching: c language is difficult c is also fun
Found 6 different words
2 times: c
1 times: language
2 times: is
1 times: difficult
1 times: also
1 times: fun
Above I tried to follow your codes style but I like to add these comments:
1) You don't really need the token array. The first loop can be changed so that it updates the final result directly.
2) Don't use global variable
3) The code can't handle normal separators like , . : and so on
4) You should put the word and the count into a struct.
Taken comment 1,2 and 4 in to consideration, the code could be:
#include <stdio.h>
#include <string.h>
// Global variables to hold the results
struct WordStat
{
char word[81];
int count;
};
int extract_and_count(char *source,int *strings_cnt, struct WordStat* ws, int max){
char *p;
int i = 0;
int k;
*strings_cnt = 0;
// Find all words in the input string
p = strtok(source, " ");
while( p != NULL ){
// Check if the word is already detected once
int found = 0;
for(k = 0 ; k < *strings_cnt ; k++){
if (strcmp(ws[k].word, p) == 0)
{
// The word already exists - increment count
found = 1;
ws[k].count++;
break;
}
}
if (!found)
{
// New word - copy it and set count to 1
strcpy(ws[*strings_cnt].word, p);
ws[*strings_cnt].count = 1;
(*strings_cnt)++;
}
i++;
p = strtok(NULL , " ");
}
return 1;
}
#define MAX_WORDS 80
int main(void)
{
struct WordStat ws[MAX_WORDS];
char s[] = "c language is difficult c is also fun";
int c, i;
printf("Searching: %s\n", s);
extract_and_count(s, &c, ws, MAX_WORDS);
printf("Found %d different words\n", c);
for (i=0; i<c; i++)
{
printf("%d times: %s\n", ws[i].count, ws[i].word);
}
return 0;
}
while( p != NULL ){
strcpy(token[i],p);
printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " "); --> here you are just splitting the words
}
Now token will contain all the words in splitted manner, not as per your requirement of "each word only once". You can compare and copy the unique words to another array and in the same loop, you can count and update the count array.
Note: You should not use one counter variable on the whole, the array of counter only shall be used to count the words.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NUM_OF_WORDS_MAX 80
#define MAX_WORD_LENGTH 79
#define S_(x) #x
#define S(x) S_(x) //number literal convert to string
char words[NUM_OF_WORDS_MAX][MAX_WORD_LENGTH+1];
int Words_entry = 0;
static inline int hash(const char *str){
return (tolower(*str) - 'a')*3;//3:(NUM_OF_WORDS_MAX / 26), 26 : a-z
}
char *extract(char **sp){//extract word
char *p = *sp;
while(*p && !isalpha(*p))//skip not alpha
++p;
if(!*p)
return NULL;
char *ret = p;//first word
while(*p && isalpha(*p))//skip alpha
++p;//*p = tolower(*p);
if(!*p){
*sp = p;
} else {
*p = '\0';
*sp = ++p;//rest
}
return ret;
}
int extract_and_count(char *source, int *count){
char *sp = source;
char *word;
int word_count = 0;
while(word = extract(&sp)){
if(Words_entry == NUM_OF_WORDS_MAX){
fprintf(stderr, "words table is full.\n");
return word_count;
}
int index = hash(word);
while(1){
if(*words[index]){
if(strcasecmp(words[index], word) == 0){//ignore case
++count[index];
break;
}
if(++index == NUM_OF_WORDS_MAX){
index = 0;
}
} else {
strcpy(words[index], word);
count[index] = 1;
++Words_entry;
break;
}
}
++word_count;
}
return word_count;
}
int main(void){
int count[NUM_OF_WORDS_MAX] = {0};
char text[MAX_WORD_LENGTH+1];
while(1==scanf("%" S(MAX_WORD_LENGTH) "[^\n]%*c", text)){//end if only enter press.
extract_and_count(text, count);
}
//print result
for(int i = 0; i < NUM_OF_WORDS_MAX; ++i){
if(*words[i]){
printf("%s : %d\n", words[i], count[i]);
}
}
return 0;
}

Huffman encoding in C

I am trying to write a module which assigns huffman encoded words to the input symbols, but the given codes differ from what they should look like.
For example, if I run it with following symbol probabilities:
(1st column: probabilities; 2nd column: my huffman codes; 3rd column: correct huffman codes)
0,25 --> 01 --> 10
0,15 --> 101 --> 111
0,15 --> 110 --> 110
0,1 --> 1111 --> 010
0,1 --> 000 --> 001
0,05 --> 0010 --> 0110
0,05 --> 0011 --> 0001
0,05 --> 1000 --> 0000
0,05 --> 1001 --> 01111
0,05 --> 1110 --> 01110
I think the problem might be caused in my function for generating huffman codes, since strcat() function's behaviour was initially not good for my idea, so I combined it with strcat(). Not sure if it is good that way tho.
I am providing you with two functions responsible for codes assign, build_huffman_tree() and generate_huffman_tree(), hopefully you can help me out with this, and point out where the problem could be.
Generate guffman tree:
void generate_huffman_tree(node *n, char *code){
if(n->left== NULL && n->right== NULL){
SYMBOLS[code_counter] = n->symbol; // this 3 lines just store current code, not important
CODES[code_counter] = strdup(code);
code_counter += 1;
}
if(n->left!= NULL){
char temp[100];
strcpy(temp, code);
strcat(temp, "0");
generate_huffman_tree(n->left, temp);
}
if(n->right!= NULL){
char temp[100];
strcpy(temp, code);
strcat(temp, "1");
generate_huffman_tree(n->right, temp);
}
Build Huffman tree:
node *build_huffman_tree(double *probabilities){
int num_of_nodes = NUM_SYMBOLS;
int num = NUM_SYMBOLS;
// 1) Initialization: Create new node for every probability
node *leafs = (node*) malloc(num_of_nodes*sizeof(node));
int i;
for(i=0; i<num_of_nodes; i+=1){
node c;
c.probability= *(probability+ i);
c.symbol= *(SYMBOLS + i);
c.left= NULL;
c.right= NULL;
*(leafs+i) = c;
}
node *root= (node*) malloc(sizeof(node)); // Root node which will be returned
while(num_of_nodes> 1){
// 2) Find 2 nodes with lowest probabilities
node *min_n1= (node*)malloc(sizeof(node));
node *min_n2 = (node*)malloc(sizeof(node));
*min_n1 = *find_min_node(leafs, num, min_n1);
leafs = remove_node(leafs, min_n1, num);
num -= 1;
*min_n2= *find_min_node(leafs, num, min_n2);
leafs = remove_node(leafs, min_n2, num);
num -= 1;
// 3) Create parent node, and assign 2 min nodes as its children
// add parent node to leafs, while its children have been removed from leafs
node *new_node = (node*) malloc(sizeof(node));
new_node->probabilty= min_n1->probability + min_n2->probability;
new_node->left= min_n1;
new_node->right= min_n2;
leafs = add_node(leafs, new_node, num);
num += 1;
num_of_nodes -= 1;
root = new_node;
}
return root;
I have tested functions for finding 2 min nodes, removing and adding nodes to leafs structure, and it is proven to work fine, so I guess the problem should be something about this here.
I didn't look at your source code, but there's nothing wrong with the Huffman code you generated. There is also nothing wrong with what you are calling "correct huffman codes". There is more than one valid Huffman code possible with that set of probabilities. If you take the sum of the probabilities times the bit lengths for both Huffman codes, you will find that those sums are exactly the same. Both Huffman codes are optimal, even though they're different.
The way this happens is that when you look for the two lowest frequencies, there is more than one choice. Depending on which choice you make, you will get a different tree.
This code below is an implementation of Mark Allen Weiss's Algorithm. Give it a try!
It offers routines similar to yours, in addition to a function that displays the result according to the previously constituted codes for each letter.
The compiler used is MinGW 2.95 (C-Free 4.0).
Prerequisites:
An input file with a text (any, but remember, it deals with alphabet characters only, no punctuation, no space, no numbers).
The constant IN_PATH is the one you should modify to point at the right location of your text file to run the program successfully.
The image shows a sample text, the letters proportions and the result of huffman code interpretation (letters separated by one space).
Good luck!
//*******************************************************************
// File: HuffmanEncoding - Tree.c
// Author(s): Mohamed Ennahdi El Idrissi
// Date: 14-Aug-2012
//
// Input Files: in.txt
// Output Files: out.txt
// Description: CSC 2302 - <Data Structures>
// <Struct, Array, File I/O, Recursion, Pointers, Binary Tree>
// This program covers the Huffman Encoding concept.
// We first read a file, from we which we count the number of characters, and then reckon the frequency
// of each letter individually. Each letter's frequency is stored in a node with its respective character.
// This node is stored in an array of 26 elements (element 0 -> 'A', element 1 -> 'B'...element 25 -> 'Z').
// Each element is a pointer, and each pointer is supposed to be a root of a tree (sub tree).
// After processing all characters of the text (read from a file), we end up with an array with
// 25 NULL elements. The only element that is not NULL is the root of the tree that gathers the different
// nodes of each letter.
// Deducing the encoding of each letter if performed with intermediary of the prefix traversal.
// To summarize, the pseudo-code is:
// - Initialize the letters array
// - Read file
// - Increment each letter frequency + compute the number of characters in the file
// - Store in the array's node the frequency of each letter
// - Compute the number (N) of involved characters (Sometimes, texts don't include all letters. In our case 'Q' and 'Z' are absent).
// - Loop N times
// - find Minimum and second minimum
// - create a new node, its left child contains the minimum and the right child contains the second minimum
// - minimum position points on the new node, and the second minimum's array position points on NULL
// - Browse the array till the unique non NULL element is encountered
// - invoke prefix traversal function
// - build the encoding of each character
// - display the letter and its characteristics when found.
// - Finally, read the output file to interpret its content
// - if root contains a character (A - Z), display character
// - else, if the current character is '0', browse the left leaf
// - else, if the current character is '1', browse the right leaf
//
//*******************************************************************
#include <stdio.h>
#define NBR_OF_LETTERS 26
#define LEFT 'L'
#define RIGHT 'R'
#define CODE_SIZE 128
#define TYPED_ALLOC(type) (type *) malloc( sizeof(type) )
#define BYTE_SIZE 8
#define IN_PATH "./files/in.txt"
#define OUT_PATH "./files/out.txt"
typedef struct tree_node_s {
float frequency;
char c;
char code[CODE_SIZE];
struct tree_node_s *left;
struct tree_node_s *right;
} tree_node_t;
tree_node_t *arr[NBR_OF_LETTERS], *letters[NBR_OF_LETTERS];
void findMinAndSecondMin(tree_node_t **, float *, int *, float *, int *);
void printTree(tree_node_t *);
void interpret(char *, int *, tree_node_t *);
void printTree(tree_node_t *);
void encode(tree_node_t *, tree_node_t **, char, short, char*);
/*
*
*/
int main() {
char str[CODE_SIZE];
int fileReadingVerdict;
int i, j, k, index, n;
float min, secondMin;
int minIndex, secondMinIndex;
int numberOfCharacters = 0;
tree_node_t *tree;
FILE *in = fopen(IN_PATH, "r");
FILE *out;
if ( in == NULL ) {
printf("\nFile not found");
return 0;
} else {
/*
* Begin: Array Initialization
*/
for (i = 'A'; i <= 'Z'; i++) {
index = i - 'A';
arr[index] = NULL;
}
/*
* End: Array Initialization
*/
numberOfCharacters = 0;
fileReadingVerdict = fgets(str, CODE_SIZE, in) != NULL;
while(!feof(in) || fileReadingVerdict) {
n = strlen(str);
printf("\n%s", str);
for (i = 0; i < n ; i ++ ) {
str[i] = toupper(str[i]);
if (str[i] >= 'A' && str[i] <= 'Z') {
numberOfCharacters ++;
index = str[i] - 'A';
if (arr[index] == NULL) {
arr[index] = TYPED_ALLOC(tree_node_t);// malloc(sizeof(tree_node_t));
arr[index]->c = str[i];
arr[index]->frequency = 1;
arr[index]->left = arr[index]->right = NULL;
} else {
arr[index]->frequency += 1;
}
}
}
if (fileReadingVerdict) {
fileReadingVerdict = fgets(str, CODE_SIZE, in) != NULL;
}
}
}
fclose(in);
for ( i = 0, n = 0 ; i < NBR_OF_LETTERS ; i ++ ) {
letters[i] = arr[i];
if (arr[i] != NULL) {
arr[i]->frequency /= numberOfCharacters; // Computing the frequency.
n ++; // n is the number of involved letters which is going to be consumed in the do while loop's condition
}
}
j = 1;
do {
findMinAndSecondMin(arr, &min, &minIndex, &secondMin, &secondMinIndex);
if (minIndex != -1 && secondMinIndex != -1 && minIndex != secondMinIndex) {
tree_node_t *temp;
tree = TYPED_ALLOC(tree_node_t);// malloc(sizeof(tree_node_t));
tree->frequency = arr[minIndex]->frequency + arr[secondMinIndex]->frequency;
tree->c = j;
tree->left = arr[minIndex];
temp = TYPED_ALLOC(tree_node_t);// malloc(sizeof(tree_node_t));
temp->c = arr[secondMinIndex]->c;
temp->frequency = arr[secondMinIndex]->frequency;
temp->left = arr[secondMinIndex]->left;
temp->right = arr[secondMinIndex]->right;
tree->right = temp;
arr[minIndex] = tree;
arr[secondMinIndex] = NULL;
}
j ++;
} while( j < n );
for ( i = 0 ; i < NBR_OF_LETTERS ; i ++ ) {
if (arr[i] != NULL) {
char code[CODE_SIZE];
strcpy(code, "");
encode(tree = arr[i], letters, 0, 0, code);
puts("\nSuccessful encoding");
printTree(arr[i]);
break;
}
}
in = fopen(IN_PATH, "r");
out = fopen(OUT_PATH, "w");
fileReadingVerdict = fgets(str, CODE_SIZE, in) != NULL;
while(!feof(in) || fileReadingVerdict) {
n = strlen(str);
for (i = 0; i < n ; i ++ ) {
str[i] = toupper(str[i]);
if (str[i] >= 'A' && str[i] <= 'Z') {
index = str[i] - 'A';
fputs(letters[index]->code, out);
}
}
if (fileReadingVerdict) {
fileReadingVerdict = fgets(str, CODE_SIZE, in) != NULL;
}
}
fclose(in);
fclose(out);
printf("\nFile size (only letters) of the input file: %d bits", numberOfCharacters * BYTE_SIZE);
out = fopen(OUT_PATH, "r");
fileReadingVerdict = fgets(str, CODE_SIZE, out) != NULL;
numberOfCharacters = 0;
while(!feof(out) || fileReadingVerdict) {
numberOfCharacters += strlen(str);
if (fileReadingVerdict) {
fileReadingVerdict = fgets(str, CODE_SIZE, out) != NULL;
}
}
fclose(out);
printf("\nFile size of the output file: %d bits", numberOfCharacters);
printf("\nInterpreting output file:\n");
out = fopen(OUT_PATH, "r");
fileReadingVerdict = fgets(str, CODE_SIZE, out) != NULL;
while(!feof(out) || fileReadingVerdict) {
n = strlen(str);
i = 0 ;
while(i < n) {
interpret(str, &i, tree);
}
if (fileReadingVerdict) {
fileReadingVerdict = fgets(str, CODE_SIZE, out) != NULL;
}
}
fclose(out);
puts("\n");
return 0;
}
/*
*
*/
void encode(tree_node_t *node, tree_node_t **letters, char direction, short level, char* code) {
int n;
if ( node != NULL ) {
if ((n = strlen(code)) < level) {
if (direction == RIGHT) {
strcat(code, "1");
} else {
if (direction == LEFT) {
strcat(code, "0");
}
}
} else {
if (n >= level) {
code[n - (n - level) - 1] = 0;
if (direction == RIGHT) {
strcat(code, "1");
} else {
if (direction == LEFT) {
strcat(code, "0");
}
}
}
}
if (node->c >= 'A' && node->c <= 'Z') {
strcpy(node->code, code);
strcpy(letters[node->c - 'A']->code, code);
}
encode(node->left, letters, LEFT, level + 1, code);
encode(node->right, letters, RIGHT, level + 1, code);
}
}
void printTree(tree_node_t *node) {
int n;
if ( node != NULL ) {
if (node->c >= 'A' && node->c <= 'Z') {
printf("\t%c - frequency: %.10f\tencoding: %s\n", node->c, node->frequency, node->code);
}
printTree(node->left);
printTree(node->right);
}
}
/*
* Begin: Minimum and second minimum
*/
void findMinAndSecondMin(tree_node_t *arr[], float *min, int *minIndex, float *secondMin, int *secondMinIndex) {
int i, k;
k = 0;
*minIndex = -1;
/*
* Skipping all the NULL elements.
*/
while (k < NBR_OF_LETTERS && arr[k] == NULL) k++;
*minIndex = k;
*min = arr[k]->frequency;
for ( i = k ; i < NBR_OF_LETTERS; i ++ ) {
if ( arr[i] != NULL && arr[i]->frequency < *min ) {
*min = arr[i]->frequency;
*minIndex = i;
}
}
k = 0;
*secondMinIndex = -1;
/*
* Skipping all the NULL elements.
*/
while ((k < NBR_OF_LETTERS && arr[k] == NULL) || (k == *minIndex && arr[k] != NULL)) k++;
*secondMin = arr[k]->frequency;
*secondMinIndex = k;
if (k == *minIndex) k ++;
for ( i = k ; i < NBR_OF_LETTERS; i ++ ) {
if ( arr[i] != NULL && arr[i]->frequency < *secondMin && i != *minIndex ) {
*secondMin = arr[i]->frequency;
*secondMinIndex = i;
}
}
/*
* End: Minimum and second minimum
*/
}
void interpret(char *str, int *index, tree_node_t *tree) {
int n = strlen(str);
if (tree->c >= 'A' && tree->c <= 'Z') {
printf("%c ", tree->c);
return ;
} else {
if ( *index < n ) {
if (str[*index] == '0') {
(*index) ++;
interpret(str, index, tree->left);
} else {
if (str[*index] == '1') {
(*index) ++;
interpret(str, index, tree->right);
}
}
}
}
}

Resources