0

Iam currently trying an exercise from the "practical programming in C" from MIT opencourseware. the exercise is on huffman coding. it is lab2 part 2 where Iam having an Issue. Primarily the pq_insert() method. Iam getting pretty confused as to how the insertion of a node should be performed? I will post the whole .c file below. I think i need the sudo code for an insert operation.

my node is basically a struct (shown below)

struct tnode
{
struct  tnode* left; /*used when in tree*/
struct  tnode*right; /*used when in tree*/  
struct  tnode*parent;/*used when in tree*/
struct  tnode* next; /*used when in list*/ 
float   freq;
int     isleaf;
char     symbol;
};

Iam presuming the pointers "left" and "right" are not used in my PQ construction? I just use "parent" and "next" pointers when creating PQ and if the current "freq" value is less than the next checked nodes "freq" value, I add this to the queue before next ?? I may be wrong here but this is one of the areas in which Iam confused??

below is the full file.

#include <stdio.h>
#include <stdlib.h>

#define MAX_SYMBOLS 255
#define MAX_LEN     7

struct tnode
{
struct  tnode* left; /*used when in tree*/
struct  tnode*right; /*used when in tree*/  
struct  tnode*parent;/*used when in tree*/
struct  tnode* next; /*used when in list*/ 
float   freq;
int     isleaf;
char     symbol;
};


/*global variables*/
char code[MAX_SYMBOLS][MAX_LEN];
struct tnode* root=NULL; /*tree of symbols*/
struct tnode* qhead=NULL; /*list of current symbols*/
struct cnode* chead=NULL;/*list of code*/

/*
@function   talloc
@desc       allocates new node 
*/
struct tnode* talloc(int symbol,float freq)
{
struct tnode* p=(struct tnode*)malloc(sizeof(struct tnode));
if(p!=NULL)
{
    p->left=p->right=p->parent=p->next=NULL;
    p->symbol=symbol;
    p->freq=freq;
    p->isleaf=1;
}
return p;
}

/*
@function display_tnode_list
@desc     displays the list of tnodes during code construction
*/
void pq_display(struct tnode* head)
{
struct tnode* p=NULL;
printf("list:");
for(p=head;p!=NULL;p=p->next)
{
    printf("(%c,%f) ",p->symbol,p->freq);
}
printf("\n");
}

/*
@function pq_insert
@desc     inserts an element into the priority queue
NOTE:     makes use of global variable qhead
*/
void pq_insert(struct tnode* p)
{
struct tnode* curr=NULL;
struct tnode* prev=NULL;
 printf("inserting:%c,%f\n",p->symbol,p->freq);
 if(qhead==NULL) /*qhead is null*/
    {
       /*TODO: write code to insert when queue is empty*/

    //qhead = null means we nead to set something as the heeed!
    //possibly p???????


    qhead = p;

    return;

       //not too sure bout this.


 }
       /*TODO: write code to find correct position to insert*/


//potentially check if 'symbol' less
//than ?? 

if(curr==qhead)//curr is always set to null when method called????
 {
    /*TODO: write code to insert before the current start*/
    curr->parent = p;
    curr = p;



}
 else /*insert between prev and next*/
{
    /*TODO: write code to insert in between*/
}
}

/*
@function pq_pop
@desc     removes the first element
NOTE:     makes use of global variable qhead
*/

struct tnode* pq_pop()
{
struct tnode* p=NULL;

p = qhead;


if(qhead->next != NULL)
{   
    qhead = qhead->next;
}




/*TODO: write code to remove front of the queue*/
printf("popped:%c,%f\n",p->symbol,p->freq);
return p;
}

 /*
 @function build_code
 @desc     generates the string codes given the tree
 NOTE: makes use of the global variable root
 */
 void generate_code(struct tnode* root,int depth)
 {
 int symbol;
 int len; /*length of code*/
 if(root->isleaf)
 {
    symbol=root->symbol;
    len   =depth;
    /*start backwards*/
    code[symbol][len]=0;
    /*
        TODO: follow parent pointer to the top
        to generate the code string
    */
    printf("built code:%c,%s\n",symbol,code[symbol]);
}
else
{
    generate_code(root->left,depth+1);
    generate_code(root->right,depth+1);
}

}

/*
@func   dump_code
@desc   output code file
*/
void dump_code(FILE* fp)
{
int i=0;
for(i=0;i<MAX_SYMBOLS;i++)
{
    if(code[i][0]) /*non empty*/
        fprintf(fp,"%c %s\n",i,code[i]);
}
}

/*
@func   encode
@desc   outputs compressed stream
*/
 void encode(char* str,FILE* fout)
{
while(*str)
{
    fprintf(fout,"%s",code[*str]);
    str++;
}
}
 /*
  @function main
 */
int main()
 {
 /*test pq*/
 struct tnode* p=NULL;
struct tnode* lc,*rc;
float freq[]={0.01,0.04,0.05,0.11,0.19,0.20,0.4};
int   NCHAR=7; /*number of characters*/
int i=0;
const char *CODE_FILE="code.txt";
const char *OUT_FILE="encoded.txt";
FILE* fout=NULL;
/*zero out code*/
memset(code,0,sizeof(code));

/*testing queue*/
pq_insert(talloc('a',0.1));
pq_insert(talloc('b',0.2));
pq_insert(talloc('c',0.15));
/*making sure it pops in the right order*/
puts("making sure it pops in the right order");
while((p=pq_pop()))
{
    free(p);
}



qhead=NULL;
/*initialize with freq*/
for(i=0;i<NCHAR;i++)
{
    pq_insert(talloc('a'+i,freq[i]));
}
/*build tree*/
for(i=0;i<NCHAR-1;i++)
{
    lc=pq_pop();
    rc=pq_pop();
    /*create parent*/
    p=talloc(0,lc->freq+rc->freq);
    /*set parent link*/
    lc->parent=rc->parent=p;
    /*set child link*/
    p->right=rc; p->left=lc;
    /*make it non-leaf*/
    p->isleaf=0;
    /*add the new node to the queue*/
    pq_insert(p);
}
/*get root*/
root=pq_pop();
/*build code*/
generate_code(root,0);
/*output code*/
puts("code:");
fout=fopen(CODE_FILE,"w");
dump_code(stdout);
dump_code(fout);
fclose(fout);

/*encode a sample string*/
puts("orginal:abba cafe bad");
fout=fopen(OUT_FILE,"w");
encode("abba cafe bad",stdout);
encode("abba cafe bad",fout);
fclose(fout);
getchar();
/*TODO: clear resources*/
return 0;
}

i think i am over thinking this really and the left and right pointers are just used in tree construction after the priority queue is created. another thing that confused me was that "curr" is set to null whenever pq_insert() is called? Iam thinking maybe curr is set to the qhead. asuming its "freq" value is less than the "qhead" freq value? Also iam not doing this as homework or anything. anyway, any input is appreciated.

also not really sure how to use "curr" and "prev" pointers in the pq_insert method. if anyone cold point me in the direction of some pseudo code that would also be pretty helpful.

1 Answer 1

1

One of the common ways to implement a priority queue is as a heap, and a heap is commonly represented as a tree structure. However, it appears that the OpenCourseware Lab 2 Part B specifically refers to a linked-list implementation for this priority queue.

It is therefore likely that you are correct in assuming that the "left" and "right" pointers won't be used. Additionally, the "parent" pointer likely won't be used because the comments refer to it as being for the tree-based implementation. A "next" pointer is sufficient for a simple linked list, as each node points to the next node starting from the "head" node or start of the list.

What you're looking to do is called "insert in sorted order" and the general process is described here: inserting element into a sorted list

In general, "curr" will take the value of each node of the linked list as you iterate through it, and "prev" will take the value of the previous node (set this before you advance curr.) Setting curr to null at the start of pq_insert is fine because you'll want to start at the first element of the list when you go to insert a new element. You'll want to know what the previous node was in the case where you find that the current node belongs after the node you're trying to insert.

1
  • thanks hhauer. yes. I was overthinking it for sure. your input has helped me thanks very much! Aug 1, 2015 at 10:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.