Qsort with Huffman tree - c

I have a problem where I have to build a Huffman tree, I understand the concept behind the problem and I know how to do it. However, I have discovered the reason for my code going wrong is to do with my sorting functions. I want to sort the list every time I input a new node into the list. I have a structure and in it I have frequency, I have made an array of structures that character are store in from a file.
My problem lies with creating the nodes, a new node is created and points to two characters in the array. However, when I then sort this list the pointers change to other characters. This happens for every step and causes it to be completely wrong.
My qsort call in main is like this:
qsort(list, n, sizeof(Node), intcompare);
and my intcompare function is like this:
int intcompare(const void *a, const void *b)
{
int freq1 = ((Node*)a)->frequency;
int freq2 = ((Node*)b)->frequency;
if (freq1 == freq2){
return 0;
}
else if (freq1 < freq2){
return 1;
}
else{
return -1;
}
}
my structure is like this:
typedef struct node{
char character;
int frequency;
struct node *left;
struct node *right;
}Node;
My list gets sorted correctly by frequency each time I call qsort.
Why would this change where the pointers in my struct are pointing to?
Thanks in advance!!

qsort moves the structures around in the array. So if there exists a left pointer that points to character X at index 3 in the array, and qsort moves X to index 16, then the left pointer won't be pointing at X anymore. It will be pointing to whatever qsort put at index 3. Long story short, you need to recompute all of the left and right pointers after every sort.

Related

Lists and Pointers in C

I have a problem with lists and pointers, let me explain.
let's define a list like this:
typedef struct list *LIST;
typedef struct node *link;
struct list{link head; int n;};
struct node{Item val;link next;};
where n is the number of nodes and LIST is a pointer to struct list (I have to do that because I want to make an opaque pointer to the list, in my homework the LIST pointer would go in the .h and the structs would go in the .c but that's not the problem, and I know I should avoid declaring pointer like that).
The Item type is also declared via an opaque pointer, so I have something like this:
typedef struct info *Item;
struct info{char *name;int N};
my problem is that I don't understand how to insert stuff in this lists. (so I would like to add an Item type to the lists, but I can't because Item is a pointer so, for example if a try to do this:
//the lists is already initialized and let's say we want to add 3 nodes
Item x=malloc(sizeof(*x));`
x->name=calloc(10,sizeof(char));
for(int i=0;i<3;i++){
fscanf("%s",x->name);
fscanf("%d",x->N);//this is a random number
ListInsert(L,x);
}
this is what i have in ListInsert():
void ListInsert(LIST L, Item x){
link z,p;
if(L->head==NULL)
L->head=newNode(x,L->head);
else{
for(z=L->head->next, p=L->head;z!=NULL;p=x, z=z->next);//i know a tail would help
p->next=newNode(x,z);
}
}
And this is what I have in newNode():
link newNode(item x,link next){
link z=malloc(sizeof(*z));//should control the allocation was successful I know
z->val=x;
z->next=next;
return z;
}
Whenever I modify the value x, I'm actually modifying what the head and everything points to, that's my problem, what could be a solution? maybe make an array? pointers can sometimes be so hard to understand, for example should I allocate z->val->name?
When you say ...
Whenever I modify the value x, I'm actually modifying what the head and everything points to, that's my problem, what could be a solution?
... I think you're talking about this code:
Item x=malloc(sizeof(*x));`
x->name=calloc(10,sizeof(char));
for(int i=0;i<3;i++){
fscanf("%s",x->name);
fscanf("%d",x->N);//this is a random number
ListInsert(L,x);
}
Indeed, you have allocated only one struct info and assigned x to point to it. You have added that one struct info to your linked list three times, and also modified it several times.
Supposing that your objective is to add three distinct objects to the list, the solution starts with allocating three distinct objects (else where would they come from?). Since each one has a pointer to a dynamically allocated array, you will also want to allocate a separate array for each of those. The easiest way to achieve that would be simply to move the allocations into the loop:
for (int i = 0; i < 3; i++) {
Item x = malloc(sizeof(*x));
x->name = calloc(10, sizeof(char));
fscanf("%s", x->name);
fscanf("%d", x->N); //this is a random number
ListInsert(L, x);
}
If you are permitted to modify the structures involved then you could also consider making the name element of struct info an array of suitable length instead of a pointer. That's a little less flexible, but it would mean that you need only one allocation for each item, not two.

C: Indexed Array for Hashtable which itself consists of N “struct nodes”: Struggling to access single elements

The problem is about an indexed array for a hashtable, which itself consists of a number N of “struct nodes”, see code below.
Each field of the array is the head of a linked list which is to be filled in later.
I am trying to access a field (as defined below in “struct node”) of a specific indexed element of this array.
LENGTH and N are previously defined constant integers, that is they are 45 and 5, respectively.
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Hash table
node *table[N];
// initialize hashtable
for (int i = 0; i < N; i++)
{
(table[i]->next) = NULL;
}
The code (which is obviously part of a larger program) compiles correctly but I receive a “segmentation fault” on debug here
(table[i]->next) = NULL
My guess is that it might be a syntax problem because I can't really see any other possible issue here.
Can somebody help? Thanks in advance!
The declaration
node* table[N];
is not declaring an array of node structs, it is declaring an array of pointers to node structs. As I don't see any place where you initialize the array itself, I assume you are not doing it.
The correct way would be (modernizing your code a little):
struct node
{
char word[LENGTH + 1];
struct node *next;
};
// Hash table
std::array<node,N> table;
// initialize hashtable
for ( node& n: table )
{
n.next = nullptr;
}
The answer from Jellyboy pointed me in the right direction: I need to declare an "array of node structs", and not an "array of pointers to node structs".
So the solution is simply:
node table[N]; // (instead of: node* table[N];
And then I am also able to initialize my hashtable, i.e. this array ("table") of N "struct nodes", and these will later serve as start points for linked lists.
I was led in the wrong direction because in the distributed "raw code" that I received in this online course, there was this statement (which included the *), and I didn't think I would have to change it.
And, as I said, I am really very new to coding, so bear with me.

C: List sort code not working

I cannot figure out why my code to sort the elements of a list does not work, it sort the first 5 element of the list and then just stop. I know it may be a stupid problem but I checked the loop a lot of time and can't understand why it reach the exit before the end of the list.
typedef enum { NOTORD=0, TIME=1, POSITION=2 } ord_t;
typedef struct elem {
double position;
double time;
flag_t flag;
struct elem * next;
} elem_t;
typedef struct {
elem_t * head;
int nelem;
ord_t ord;
} lista_t;
void set_ordinata_time (lista_t * l)
{
if (l->ord!=TIME) {
elem_t * aux, *corr, *succ;
int scambio;
corr=l->head;
succ=l->head->next;
aux=malloc(sizeof(elem_t));
scambio=1;
while(scambio==1)
{
corr=l->head;
succ=l->head->next;
scambio=0;
while(succ != NULL)
{
if (corr->time > succ->time)
{
aux=corr;
aux->next=succ->next;
corr=succ;
succ=aux;
corr->next=succ;
scambio=1;
}
corr=succ; succ=succ->next;
}
}
l->ord=TIME;
}
}
When you are swapping, if corr is l->head then you need to reset l->head to succ. If you don't, then the next time through the loop, the sort will start from wherever l->head ended up after the first pass.
Also, you will be losing (and leaking) part of the list.
EDIT: More from comments
It applies generally to corr's previous element (it needs to be reset to succ when swapping)
There's no need to allocate an instance of elem_t. When swapping elements, there's an issue if one of the elements is the first one in the list, or if the elements are adjacent elements. Using pointers to pointer to element can reduce the number of if statements. A pointer to pointer could be the address of head or the address of a nodes next pointer.
To implement a swap, first swap whatever points to the two nodes (could be head or a node next pointer), then swap the two nodes next pointers. This will take care of the adjacent versus not adjacent node issue.

C: Pointer to pointer issue when I try to group a lot of things in it

I have a function that adds one item to a list that I created. If it's the first time and the list points to NULL, it allocates the list and completes it, returning the address. If it's not the first time, it adds another item and again returns the first item (by now I could disregard this return). The list and the function WORKS fine, here is the prototype:
typedef struct structTAppConsoleList {
char *text;
void (*cbFunction)(int);
int number; // This is the item number
struct structTAppConsoleList *previous;
struct structTAppConsoleList *next;
} TList;
TList *AppConsoleListAddItem(TList *p_list, const char *p_string, void (*p_funcPtr)(int));
So, somewhere in my code I have to create a lot of them and I'm trying to make it as the code below shows. Thing is, I can't make it work... I want to create something to group the lists I want to create and then use it in the function. The code below is an idea of what I'm trying to do. Consider only the part where I try to allocate the 3 lists, the rest is not important for this example.
TList *list1;
TList *list2;
TList *list3;
int main(void)
{
int i,j;
TList **groupMyLists;
TList *temp;
groupMyLists=malloc(sizeof(TList)*3);
*groupMyLists =(TList*)&list1;
*(groupMyLists+1)=(TList*)&list2;
*(groupMyLists+2)=(TList*)&list3;
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
**groupMyLists=temp; // my make won't let me do this
groupMyLists++;
}
}
I'm pretty sure that this would do it, but I can't compile it.
In my head, (*groupMyLists) would be the same as (&list1), (&list2), (&list3), the same way that (**groupMyLists) would be the same as (list1), (list2) and (list3). So why I can't do (**groupMyLists=temp)? Anyone?
I hope I made myself clear!! I's not easy to explain this madness I'm trying to do...
Change this line, you are using the wrong indirection.
*groupMyLists=temp;
In addition to the above two answers about the incorrect indirection of **groupMyLists you probably also want to assign the list1,list2,list3 pointers correct pointer values instead of writing garbage values into the allocated memory in groupMyLists i.e.
TList * groupMyList = malloc(sizeof(TList)*3);
list1 = &groupMyList[0];
list2 = &groupMyList[1];
list3 = &groupMyList[2];
but, this does not really match the rest of your code as it seems that AppConsoleAddListItem allocates the temp list so in that case your malloc would be incorrect as it should allocate the space for the pointers instead of space for the lists as in:
TList ** groupMyList = (TList **)malloc(sizeof(TList *)*3);
TList * temp;
if (!groupMyList) {
/* Print allocation error warning or handle in some proper fashion */
exit(1);
}
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
groupMyLists[j]=temp; // Here you now assign the pointer in temp to the memory for // pointers that you allocated above
}
list1 = groupMyList[0]; // Here we need to assign the list1,2,3 after the AppConsole calls
list2 = groupMyList[1]; // as these calls will changes the pointer addresses written into
list3 = groupMyList[2]; // groupMyList
Although I can not be sure exactly what you are trying to do there are several inconsistencies of pointers and indirections in your original code and the above two examples can hopefully be of some guidance
This would do the job:
**groupMyLists = *temp;
of copying one struct referenced by temp to another struct referenced by *groupMyLists.
But only if *groupMyLists would reference any valid memory, which is does not do - at least not from the source you posted.

C (beginner) : Why won't my qsort work? EDIT: From one error to another

I'm doing K&R exercise 6-4, which is:
6-4. Write a program that prints the distinct words in its input sorted into decreasing order of frequency of occurrence. Precede each word by its count.
What I decided to do is create a struct called dstncFreqNode6_4:
struct dstncFreqNode6_4
{
char *word;
int count;
struct dstncFreqNode6_4 *left;
struct dstncFreqNode6_4 *right;
};
I then parsed the input for words, and for each distinct word created one "dstncFreqNode6_4" node and two pointers to it: one to insert in to a BST (to add new words/update the counts of already encountered words), and one to insert in to a global array of "dsntFredNode6_4" pointers.
This was done so that I could update the counts of words (nodes) by traversing the BST (which contains pointers to all the words encountered so far). After the entire input has been parsed, the array of pointers would be sorted by the members' "count" variables, and then printed. Since the
The code the adding of new words/updating the counts is here:(I don't think anything is wrong with it,since the BST and array appear to be populated correctly, so you may ignore this):
//"wordCounts" is originally a global dstncFreqNode6_4** declared outside the function, numWords is the current length of the array
struct dstncFreqNode6_4 *addFreqNode6_4(struct dstncFreqNode6_4 *root, char *word)
{
if(root == NULL)
{
root = (struct dstncFreqNode6_4 *) malloc(sizeof(struct dstncFreqNode6_4));
root -> word = word;
root -> count = 1;
root -> left = NULL;
root -> right = NULL;
struct dstncFreqNode6_4 **p;
wordCounts = (struct dstncFreqNode6_4 **) realloc(wordCounts, sizeof(struct dstncFreqNode6_4*) * (numWords +1));
p = wordCounts + numWords++;
(*p) = root;
}
else if(strcmp(word, root -> word) < 0)
root -> left = addFreqNode6_4(root -> left, word);
else if(strcmp(word, root -> word) > 0)
root -> right = addFreqNode6_4(root -> right, word);
else
root -> count++;
return root;
}
So I've got everything working right except the sorting; it simply won't sort the array. That is to say... the order of the elements remains unchanged EDIT: I get a segmentation error. EDIT #2: No segmentation error now; the original problem still exists.
I'm using stlib.h's qsort method; the compare function i'm using is:
int qcmp6_4(const void *a, const void *b)
{
return (*(struct dstncFreqNode6_4 **)a) -> count - (*(struct dstncFreqNode6_4 **)b) -> count;
}
I can't seem to figure out why it won't sort correctly. I actually implemented my own quicksort algorithm and am getting the same results. I really don't know at this point.
It would be nice to get some fresh, and expert eyes to point me in the right direction. Thanks.
EDIT
Sorry, here's the call to qsort:
qsort(wordCounts, numWords, sizeof(struct dstncFreqNode6_4 *), qcmp6_4);
EDIT #2:
Following the suggestion, I've made "wordCounts" an array of pointers to nodes (all the code in this post has been updated to reflect this). So in essence, the BST and the array contain identical information (in fact the array pointers are initialized to the corresponding pointer in the BST), but their uses are different. The BST is used to add new words/update the counts of already encountered ones, and the array is sorted at the end (by the each of the words' counts) and printed. However, i'm running in to the same problem I originally ran in to: the order of the array remains the same after the call to qsort.
It looks to me like you're trying to sort an array of nodes which each contain pointers to other nodes in the array. After sorting, these pointers will of course point to the wrong elements of the array. Perhaps that is your problem?
By the way, I see that you're using realloc. The same problem with pointers to array elements will come up with realloc whenever it has to move the allocated array to a new location to satisfy the new size requirement, only much worse: the node pointers will now all point to invalid addresses and using them will result in undefined behavior.
One possible solution would be to never modify your original array of nodes, and instead make a second array of pointers to nodes and sort that array of pointers using a comparison function that compares the nodes they point to.
So i've managed to get it to sort with my own implementation of quicksort. The stlib.h's version still leaves my array unsorted. Can it only be used on arrays of primitive types? Because that is the only reason I can see that prevents it from working.
Here is my custom qsort code for reference:
void swapFreq6_4(struct dstncFreqNode6_4 **a, struct dstncFreqNode6_4 **b)
{
struct dstncFreqNode6_4 *t=*a; *a=*b; *b=t;
}
void **sortFreq6_4(struct dstncFreqNode6_4 **arr, int beg, int end)
{
if (end > beg + 1)
{
struct dstncFreqNode6_4 *piv = *(arr + beg);
int l = beg + 1;
int r = end;
while (l < r)
{
if ( (*(arr + l))-> count <= piv -> count)
l++;
else
swapFreq6_4((arr + l), (arr + --r));
}
swapFreq6_4((arr + --l), (arr + beg));
sortFreq6_4(arr, beg, l);
sortFreq6_4(arr, r, end);
}
}
Does anyone know why the stdlib's qsort code doesn't work with my struct? Am I using it wrong (my call to it, as well as the comparing function are in the original post)?

Resources