Program
/**************** stack.c **********************/ #include #define SUCCESS 0 #define ERROR -1 struct tnode; typedef struct tnode *stype; typedef struct snode snode; struct snode { stype data; snode *next; }; snode *stop = NULL; // signifies empty stack. int sPush( stype data ) { /* * push data at stop. */ snode *ptr = (snode *)malloc( sizeof(snode) ); ptr->data = data; ptr->next = stop; stop = ptr; return SUCCESS; } stype sPop() { /* * returns data at stop. */ stype data; snode *ptr; if( sEmpty() ) { fprintf( stderr, "ERROR : popping from empty stack." ); return (stype)NULL; } data = stop->data; ptr = stop; stop = stop->next; free(ptr); return data; } int sEmpty() { return ( stop == NULL ); } /**************** queue.c **********************/ #include #define SUCCESS 0 #define ERROR -1 struct tnode; typedef struct tnode *qtype; typedef struct qnode qnode; struct qnode { qtype data; qnode *next; }; qnode *front = NULL, // signifies empty queue. *rear = NULL; int qInsert( qtype data ) { /* * inserts data at rear. */ qnode *ptr = (qnode *)malloc( sizeof(qnode) ); ptr->data = data; ptr->next = NULL; if( qEmpty() ) front = ptr; else rear->next = ptr; rear = ptr; return SUCCESS; } qtype qRetrieve() { /* * retrieve data from front and remove it from queue. */ qtype data: if( qEmpty() ) { fprintf( stderr, "ERROR : retrieving from empty queue." ); return (qtype)NULL; } data = front->data; front = front->next; if( qEmpty() ) // last node removed. rear = NULL; return data; } int qEmpty() { return ( front == NULL ); } /**************** main.c **********************/ #include #include #include "queue.c" #include "stack1.c" typedef int ttype; typedef struct tnode tnode; struct tnode { ttype data; tnode *left; tnode *right; }; tnode *tree = NULL; int tInsert( ttype data ) { /* * insert data into global tree. */ tnode *ptr = (tnode *)malloc( sizeof(tnode) ); ptr->data = data; ptr->left = NULL; ptr->right = NULL; tInsertPtr( &tree, ptr ); } int tInsertPtr( tnode **tree, tnode *ptr ) { /* * inserts ptr into tree recursively. */ if( *tree != NULL ) { if( ptr->data < (*tree)->data ) tInsertPtr( &((*tree)->left), ptr ); else tInsertPtr( &((*tree)->right), ptr ); } else *tree = ptr; return SUCCESS; } void tPrint( tnode *tree ) { /* * prints tree in inorder recursively. */ if( tree != NULL ) { printf( "going left of %d... ", tree->data ); tPrint( tree->left ); printf( "%d ", tree->data ); printf( "going right of %d... ", tree->data ); tPrint( tree->right ); printf( "tree of %d is over. ", tree->data ); } } void tIterBFS( tnode *tree ) { /* * prints tree in breadth-first manner iteratively using a queue. */ if( tree != NULL ) { qInsert( tree ); while( !qEmpty() ) { tree = qRetrieve(); printf( "[[ %d ]] ", tree->data ); if( tree->left != NULL ) qInsert( tree->left ); if( tree->right != NULL ) qInsert( tree->right ); } } } void tIterPreorder( tnode *tree ) { /* * prints tree in preorder iteratively using a stack. */ if( tree != NULL ) { sPush( tree ); while( !sEmpty() ) { tree = sPop(); printf( "[[ %d ]] ", tree->data ); if( tree->right != NULL ) sPush( tree->right ); if( tree->left != NULL ) sPush( tree->left ); } } } int main() { tInsert(4); tInsert(2); tInsert(6); tInsert(1); tInsert(3); tInsert(5); tInsert(7); tPrint(tree); tIterPreorder(tree); }
Explanation
The preorder traversal is 12, 7, 3, 9, 8, 11, 18, 14, 13, 15.
The stepwise run of the algorithm is as follows.
step |
node |
stack |
output |
---|---|---|---|
0 |
nil |
empty |
nil |
1 |
12 |
18 7 |
12 |
2 |
7 |
18 9 3 |
12 7 |
3 |
3 |
18 9 |
12 7 3 |
4 |
9 |
18 11 8 |
12 7 3 9 |
5 |
8 |
18 11 |
12 7 3 9 |
6 |
11 |
18 |
12 7 3 9 8 11 |
7 |
18 |
14 |
12 7 3 9 8 11 18 |
8 |
14 |
15 13 |
12 7 3 9 8 11 18 14 |
9 |
13 |
15 |
12 7 3 9 8 11 18 14 13 |
10 |
15 |
empty |
12 7 3 9 8 11 18 14 13 15 |
Points to Remember
Program
/******************** stack.c *********************/ #include #define SUCCESS 0 #define ERROR -1 struct tnode; typedef struct tnode *stype; typedef struct snode snode; typedef struct snode *stack; struct snode { stype data; snode *next; }; void sInit( stack *s ) { *s = NULL; } int sPush( stack *s, stype data ) { /* * push data in stack s. */ snode *ptr = (snode *)malloc( sizeof(snode) ); ptr->data = data; ptr->next = *s; *s = ptr; return SUCCESS; } stype sPop( stack *s ) { /* * returns data at top of stack s. */ stype data; snode *ptr; if( sEmpty(*s) ) { fprintf( stderr, "ERROR : popping from empty stack. " ); return (stype)NULL; } data = (*s)->data; ptr = *s; (*s) = (*s)->next; free(ptr); return data; } int sEmpty( stack s ) { return ( s == NULL ); } /******************** main.c *********************/ #include #include #include "stack.c" typedef int ttype; typedef struct tnode tnode; struct tnode { ttype data; tnode *left; tnode *right; }; tnode *tree = NULL; int tInsert( ttype data ) { /* * insert data into global tree. */ tnode *ptr = (tnode *)malloc( sizeof(tnode) ); ptr->data = data; ptr->left = NULL; ptr->right = NULL; tInsertPtr( &tree, ptr ); } int tInsertPtr( tnode **tree, tnode *ptr ) { /* * inserts ptr into tree recursively. */ if( *tree != NULL ) { if( ptr->data < (*tree)->data ) tInsertPtr( &((*tree)->left), ptr ); else tInsertPtr( &((*tree)->right), ptr ); } else *tree = ptr; return SUCCESS; } void tPrint( tnode *tree ) { /* * prints tree in inorder recursively. */ if( tree != NULL ) { printf( "going left of %d... ", tree->data ); tPrint( tree->left ); printf( "%d ", tree->data ); printf( "going right of %d... ", tree->data ); tPrint( tree->right ); printf( "tree of %d is over. ", tree->data ); } } void tIterPostorder( tnode *tree ) { /* * prints tree in postorder iteratively using 2 stacks. */ stack s1, s2; sInit(&s1); sInit(&s2); if( tree != NULL ) { sPush( &s1, tree ); while( !sEmpty(s1) ) { tnode *t = sPop(&s1); sPush( &s2, t ); if( t->left != NULL ) sPush( &s1, t->left ); if( t->right != NULL ) sPush( &s1, t->right ); } while( !sEmpty(s2) ) printf( "%d ", sPop(&s2)->data ); } } int main() { tInsert(4); tInsert(2); tInsert(5); tInsert(1); tInsert(3); tInsert(6); //tInsert(7); tPrint(tree); tIterPostorder(tree); }
Explanation
The postorder traversal of this tree is 3, 8, 11, 9, 7, 13, 15, 14, 18, 12. The stepwise run of the algorithm is shown next:
step |
node |
stack 1 |
stack 2 |
---|---|---|---|
0 |
nil |
empty |
empty |
1 |
12 |
7 18 |
12 |
2 |
18 |
7 14 |
12 18 |
3 |
14 |
7 13 |
15 12 18 14 |
4 |
15 |
7 13 |
12 18 14 15 |
5 |
13 |
7 12 |
18 14 15 13 |
6 |
7 |
3 9 |
12 18 14 15 13 7 |
7 |
9 |
3 8 11 |
12 18 14 15 13 7 9 |
8 |
11 |
3 8 |
12 18 14 15 13 7 9 11 |
9 |
8 |
3 |
12 18 14 15 13 7 9 11 8 |
10 |
3 |
empty |
12 18 14 15 13 7 9 11 8 3 |
Stack 2 can now be output to get the required postorder traversal of the tree.
Points to Remember
Write a function to traverse an inorder threaded binary tree in preorder.
Program
#include #define SUCCESS 0 #define ERROR -1 typedef int type; typedef struct node node; typedef enum {FALSE, TRUE} bool; struct node { type data; node *lchild, *rchild; bool lthread, rthread; }; /* NOTE: since this is a threaded binary tree, there wont be any condition */ /* of type (ptr == NULL). */ node tree; void tInit() { tree.lchild = tree.rchild = &tree; tree.lthread = TRUE; tree.rthread = FALSE; tree.data = 99999999; } node *insucc( node *t ) { /* * find inorder successor of t. */ node *temp = t->rchild; if( t->rthread == FALSE ) while( temp->lthread == FALSE ) temp = temp->lchild; return temp; } node *inpred( node *t ) { /* * find inorder predecessor of t. */ node *temp = t->lchild; if( t->lthread == FALSE ) while( temp->rthread == FALSE ) temp = temp->rchild; return temp; } int tInsertRight( node *s, node *t ) { /* * insert t as right child of s. */ node *temp; t->rchild = s->rchild; t->rthread = s->rthread; t->lchild = s; t->lthread = TRUE; s->rchild = t; s->rthread = FALSE; if( t->rthread == FALSE ) { temp = insucc(t); temp->lchild = t; } return SUCCESS; } int tInsertLeft( node *s, node *t ) { /* * insert t as left child of s. */ node *temp; t->lchild = s->lchild; t->lthread = s->lthread; t->rchild = s; t->rthread = TRUE; s->lchild = t; s->lthread = FALSE; if( t->lthread == FALSE ) { temp = inpred(t); temp->rchild = t; } return SUCCESS; } node *tGetNewNode( type data ) { /* * returns a new node containing the data. */ node *ptr = (node *)malloc( sizeof(node) ); ptr->data = data; ptr->lchild = ptr->rchild = NULL; ptr->lthread = ptr->rthread = FALSE; return ptr; } int tInsert( node *t, type data ) { /* * insert data in t recursively. */ if( data < t->data ) if( t->lthread == TRUE ) tInsertLeft( t, tGetNewNode(data) ); else tInsert( t->lchild, data ); else if( t->rthread == TRUE ) tInsertRight( t, tGetNewNode(data) ); else tInsert( t->rchild, data ); return SUCCESS; } void tPrint( node *t ) { /* * prints t inorder recursively without using threads. */ if( t != &tree ) { if( t->lthread == FALSE ) tPrint( t->lchild ); printf( "%d ", t->data ); if( t->rthread == FALSE ) tPrint( t->rchild ); } } void tPrintPreorder( node *t ) { /* * prints tree preorder (no use of threads). */ if( t != &tree ) { printf( "%d ", t->data ); if( t->lthread == FALSE ) tPrintPreorder( t->lchild ); if( t->rthread == FALSE ) tPrintPreorder( t->rchild ); } } void tPrintInorder( node *tree ) { /* * prints tree inorder using threads. */ node *temp = tree; do { temp = insucc(temp); if( temp != tree ) printf( "%d ", temp->data ); } while( temp != tree ); } int main() { tInit(); tInsert( &tree, 4 ); tInsert( &tree, 2 ); tInsert( &tree, 1 ); tInsert( &tree, 3 ); tInsert( &tree, 6 ); tInsert( &tree, 5 ); tInsert( &tree, 7 ); tPrint( tree.lchild ); printf( " " ); tPrintPreorder( tree.lchild ); return 0; }
Explanation
Points to Remember
Implement union() and find() operations on sets represented as trees.
Program
#include #define N 100 // max no of elements together in all sets. typedef int type; typedef struct node node; struct node { type val; // this is value of member. int parent; // this is index of parent in the array. }; node sets[N]; // all sets are contained in it. int setsindex = 0; // total no of elements in sets. int insertRoot( type val ) { /* * insert val in sets as a root of a new tree. */ sets[setsindex].val = val; sets[setsindex].parent = -1; setsindex++; return setsindex-1; } void insertElement( int rootindex, type val ) { /* * insert element val in set whose root is indexed at rootindex. */ sets[setsindex].val = val; sets[setsindex].parent = rootindex; setsindex++; } int buildSet( type a[], int n ) { /* * repeated calls to this fun with diff arrays will insert diff set in sets. * forms a tree representation of elements in a. * n is number of elements in the set. * empty set(n==0) cannot be represented here. * returns index of root. */ int i, rootindex; if( n <= 0 ) { fprintf( stderr, "n should be > 0. " ); return -1; } // check whether there is enough space for n elements. if( setsindex+n > N ) { fprintf( stderr, "ERROR: set overflow. " ); return -1; } // a[0] becomes the root. rootindex = insertRoot( a[0] ); for( i=1; i
Explanation
Points to Remember
Write a program to find Huffman codes for a set of characters, given the frequency of occurrence of each of these characters.
Program
/********************** huffman.q.h ************************/ #include typedef struct qnode qnode; typedef tree qtype; typedef struct { qnode *front; qnode *rear; } queue; struct qnode { qtype op; qnode *next; }; bool qEmpty( queue *q ) { return (q->front == NULL); } void qPushBack( queue *q, qtype op ) { /* * pushes op at q.rear. */ qnode *ptr = (qnode *)malloc( sizeof(qnode) ); ptr->op = op; ptr->next = NULL; if( qEmpty(q) ) // first element in q. q->front = ptr; else q->rear->next = ptr; q->rear = ptr; } void qInsertSorted(queue *q, qtype op) { /* * inserts val in sorted q and keeps new q sorted. */ qnode *ptr = (qnode *)malloc(sizeof(qnode)); qnode *curr, *prev; ptr->op = op; for(prev=NULL, curr=q->front; curr && curr->op->w < op->w; prev=curr, curr=curr->next) ; if(!curr && !prev) // q empty. ptr->next = NULL, q->rear = q->front = ptr; else if(!curr) // op is the max value. ptr->next = NULL, prev->next = q->rear = ptr; else if(!prev) // op is the min value. ptr->next = curr, q->front = ptr; else { // if prev and ptr both exist. ptr->next = curr; prev->next = ptr; } } qtype qPopFront( queue *q ) { /* * pops op from q->front. */ qnode *ptr = q->front; qtype op; if( qEmpty(q) ) return (qtype)NULL; q->front = q->front->next; if( qEmpty(q) ) q->rear = NULL; op = ptr->op; free(ptr); return op; } /********************* huffman.c *************************/ #include #define MAXLEN 80 typedef struct node node; typedef char type; typedef node *tree; typedef enum {FALSE, TRUE} bool; struct node { int w; type val; node *left, *right; }; #include "huffman.q.h" int compare(const void *e1, const void *e2) { /* * compare the two elements in e1 and e2. * each element is a vector of two elements. */ return ((int *)e1)[1] > ((int *)e2)[1]; } void printTree(tree t, char *outputstr) { /* * print the huffman codes for each element of t. * outputstr contains huffman code for t (NOT parent of t). * assumes t!=NULL. */ char str[2] = "1"; if(t->right) { strcat(outputstr, str); printTree(t->right, outputstr); outputstr[strlen(outputstr)-1] = 0; // restore. } if(t->left) { str[0] = '0'; strcat(outputstr, str); printTree(t->left, outputstr); outputstr[strlen(outputstr)-1] = 0; // restore. } else if(!t->right) printf("%c=%d=%s. ", t->val, t->w, outputstr); } tree buildTree(int a[][2], int n) { /* * build a huffman tree using frequency in a[i][1] where a[0][j] indicates * the character * for that sort a on frequency. * n is the size of a. */ int i; tree t = NULL; queue sortedq = {NULL}; // sort a on frequency. qsort(a, n, sizeof(a[0]), compare); // insert each element in tree. for(i=0; iw = a[i][1]; temp->val = (type)a[i][0]; qPushBack(&sortedq, temp); } // assume n>0. while(TRUE) { tree t2 = NULL, t1 = qPopFront(&sortedq); if(!qEmpty(&sortedq)) t2 = qPopFront(&sortedq); else { t = t1; break; } t = (tree)malloc(sizeof(node)); t->w = t1->w + t2->w; t->left = t1; t->right = t2; { qnode *ptr; for(ptr=sortedq.front; ptr; ptr=ptr->next) printf("%d ", ptr->op->w); printf(" "); } printf("insertsorted=%d. ", t->w); qInsertSorted(&sortedq, t); } return t; } int main() { int a[][2] = { {'a', 20}, {'b', 23}, {'c', 14}, {'d', 56}, {'e', 35}, {'f', 29}, {'g', 5 } }; char outputstr[MAXLEN] = ""; tree t = buildTree(a, sizeof(a)/2/sizeof(int)); if(t) printTree(t, outputstr); return 0; }
Explanation
Note that this is not the minimum weighted path length. The minimum weighted path length can be obtained by restructuring the tree as shown here.
The path length of this tree is 1*15 + 2*5 + 3*2 + 3*4 = 43, which is minimum. The numbers 0 and 1 of the edges will be clear in the following description.
Points to Remember
Write procedures for insertion and deletion in a B-tree.
Program
/************************* b.q.c *****************************/ #include #include typedef struct qnode qnode; typedef btree qtype; typedef struct { qnode *front; qnode *rear; } queue; struct qnode { qtype op; qnode *next; }; bool qEmpty( queue *q ) { return (q->front == NULL); } void qPush( queue *q, qtype op ) { /* * pushes op at q.rear. */ qnode *ptr = (qnode *)malloc( sizeof(qnode) ); ptr->op = op; ptr->next = NULL; if( qEmpty(q) ) // first element in q. q->front = ptr; else q->rear->next = ptr; q->rear = ptr; } qtype qPop( queue *q ) { /* * pops op from q->front. */ qnode *ptr = q->front; qtype op; if( qEmpty(q) ) return (qtype)-1; q->front = q->front->next; if( qEmpty(q) ) q->rear = NULL; op = ptr->op; free(ptr); return op; } /*************************** b.c ****************************/ #include #define K 5 typedef struct node node; typedef int type; typedef node *btree; typedef enum {FALSE, TRUE} bool; #include "b.q.c" struct node { type val[K]; // data in the node. // actually first K-1 vals are valid. Kth entry is used // during breaking of the node. btree ptr[K+1]; // pointers to other nodes in the node. // one extra ptr to be used in some loops. int nptr; // no of pointers in the node = non-null ptrs. // this can also be used to see whether a node is a leaf. int nval; // no of values in the node. // this is reqd even when nptr is present: for leaf nodes. // for non-leaf nodes: nptr = nval+1. // for leaf nodes: nptr = 0. }; btree bNew() { /* * returns a new initialized node. */ btree tree = (btree)malloc(sizeof(node)); tree->nval = tree->nptr = 0; return tree; } void insertVal(btree tree, type val, btree ptr) { /* * insert (val, ptr) in node pointed to by tree without any checks. */ int i, j; for(i=0; inval && tree->val[i]val[i]. // shift-next the next values by one position. for(j=tree->nval-1; j>=i; -j) { tree->val[j+1] = tree->val[j]; tree->ptr[j+2] = tree->ptr[j+1]; } // insert val now at i. tree->val[i] = val; tree->nval++; tree->ptr[i+1] = ptr; if(ptr != NULL) // tree is NOT a leaf. tree->nptr++; printf(" val %d inserted at position %d, tree->nptr=%d tree- >nval=%d. ", val, i, tree->nptr, tree->nval); } btree getSplitNode(btree tree) { /* * returns a new node containing vals and pointers from tree. */ int i, j; btree ptr = bNew(); // copy vals. for(i=(K-1)/2+1, j=0; ival[j] = tree->val[i]; ptr->nval = K/2; tree->nval = K-K/2-1; // temporarily this node contains an extra val. // copy ptrs. for(i=(K-1)/2+1, j=0; i<=K; ++i, ++j) ptr->ptr[j] = tree->ptr[i]; if(tree->nptr > 0) { // non-leaf nodes. ptr->nptr = K/2+1; tree->nptr = K-K/2; } else // leaf nodes. tree->nptr = ptr->nptr = 0; return ptr; } btree bMakeChanges(btree tree, btree ptr, btree parent) { /* * last val of tree contains an extra val which should be * inserted in parent. * if parent == NULL, then tree was root. * tree = parent->ptr[i] if parent is NOT NULL. */ // extract the last value from tree. type val = tree->val[tree->nval]; printf("in bMakeChanges(). "); if(parent == NULL) { parent = bNew(); parent->ptr[0] = tree; parent->nptr = 1; } if(parent->nval < K-1) { // parent has space. insertVal(parent, val, ptr); } else { // parent full. printf("parent is full. "); insertVal(parent, val, ptr); return getSplitNode(parent); } return parent; } btree bInsert(btree tree, type val) { /* * calls insert to insert val in tree. * if the return node is diff from tree, that means a new node has * been created. Thus calls bMakeChanges(). */ btree insert(btree tree, type val); btree ptr = insert(tree, val); if(ptr != tree) // node was split. return bMakeChanges(tree, ptr, NULL); return tree; } btree insert(btree tree, type val) { /* * inserts val in tree. * returns tree if there is no change. * if there is creation of a new node then the new node is returned. */ int i; btree ptr; if(tree->nptr > 0) { // non-leaf. for(i=0; inval && tree->val[i]ptr[i]. printf(" val should be inserted in tree->ptr[%d]. ", i); ptr = insert(tree->ptr[i], val); if(ptr != tree->ptr[i]) return bMakeChanges(tree->ptr[i], ptr, tree); return tree; } else { // tree is a leaf. if(tree->nval < K-1) { // space is available in the leaf. insertVal(tree, val, NULL); return tree; } else { // leaf full! printf(" leaf is full. "); insertVal(tree, val, NULL); // now break the leaf node. return getSplitNode(tree); } } } btree getAdjTree(btree tree, int offset, btree parent, int treeindex) { /* * returns parent[treeindex+offset] if exists else NULL. */ int newindex = treeindex+offset; if(newindex >= 0 && newindex < parent->nptr) return parent->ptr[newindex]; return NULL; } void combineNodes(btree left, btree right) { /* * left += right. */ int i; int nptrleft = left->nptr; for(i=0; inval; ++i) left->val[left->nval++] = right->val[i]; for(i=0; inptr; ++i) left->ptr[nptrleft+i] = right->ptr[i]; if(left->nptr > 0) // non-leaf. left->nptr += i; } type getNextVal(btree ptr) { /* * return the first value in the first leaf accessible from ptr. */ if(ptr->nptr > 0) // still this is a non-leaf. return getNextVal(ptr->ptr[0]); return ptr->val[0]; // got it! } btree deleteVal(btree tree, int i) { /* * remove (val, ptr) at position i from tree without any checks. * and return tree->ptr[i+1]. */ btree rightptr = tree->ptr[i+1]; // ptr being removed along with val. if(i == -1) // special case for a dummy call to this function. return; for(++i; inval; ++i) { tree->val[i-1] = tree->val[i]; tree->ptr[i] = tree->ptr[i+1]; } tree->nval-; if(tree->nptr > 0) // if it is a non-leaf. tree->nptr-; return rightptr; } btree bApplyChanges(btree tree, btree parent, int treeindex) { /* * apply changes: tree is a non-leaf and tree = parent- >ptr[treeindex]. * also tree->nval < K/2. */ int parentvalindex; int adjtreevalindex; btree returntree; btree adjtree; int offset = -1; // predecessor. btree adjtreeleft; if(parent && tree->nval >= K/2) return tree; else if(parent == NULL && tree->nval == 0) { free(tree); return tree->ptr[0]; } else if(parent == NULL) return tree; // parent is NOT NULL. adjtreeleft = adjtree = getAdjTree(tree, offset, parent, treeindex); // predecessor. if(!adjtree || (adjtree && adjtree->nval <= K/2)) { // no extra val. offset = 1; // successor. adjtree = getAdjTree(tree, offset, parent, treeindex); // successor. if(!adjtree || (adjtree && adjtree->nval <= K/2)) { // no extra val here too. btree parentparent; int parentindex; type parentval; btree returntree; //printf("combine tree, parent median val, adjtree. "); //printf("also check parent for having val[parentvalindex]; deleteVal(parent, parentvalindex); // the return val is tree/adjtree. Hence dont worry. if(offset == -1) { // combine adjtree, parentval and tree. adjtree->val[adjtree->nval++] = parentval; combineNodes(adjtree, tree); free(tree); returntree = adjtree; } else { // offset == 1: right sibling. // combine tree, parentval and adjtree. tree->val[tree->nval++] = parentval; combineNodes(tree, adjtree); free(adjtree); returntree = tree; } return returntree; } else { adjtreevalindex = 0; returntree = adjtree; } } else { adjtreevalindex = adjtree->nval-1; returntree = tree; } // adjtree has an extra val. parentvalindex = (2*treeindex+offset)/2; // insert parent val in tree. insertVal(tree, parent->val[parentvalindex], returntree->ptr[0]); // now promote val in adjtree to parent. parent->val[parentvalindex] = adjtree->val[adjtreevalindex]; returntree->ptr[0] = deleteVal(adjtree, adjtreevalindex); return tree; } btree delete(btree tree, type val, int i, btree parent, int treeindex) { /* * delete val from tree. val == tree->val[i]. * and tree == parent->nptr[treeindex]. */ int parentvalindex; int adjtreevalindex; btree adjtree; btree bDelete(btree tree, type val, btree parent, int treeindex); if(tree->nptr == 0) { // leaf. deleteVal(tree, i); // find two adjacent nodes to tree(succ and pred) and // see whether any of them has >K/2 vals. if yes then bring // the parent value between tree and the node into tree and // promote the value in the adjacent node into the parent. // if no such adjacent node exists, combine tree, parent val and // the adjacent node. // if this leaves parent node having ptr[i+1]); // since tree->val[i] exists // tree->val[i+1] exists. tree->val[i] = nextval; bDelete(tree->ptr[i+1], nextval, tree, i+1); } return bApplyChanges(tree, parent, treeindex); } btree bDelete(btree tree, type val, btree parent, int treeindex) { /* * delete val from tree if exists. * tree == parent[treeindex]. */ int i; for(i=0; inval && tree->val[i]val[i] == val) { printf("val=%d found: to be deleted. ", val); return delete(tree, val, i, parent, treeindex); } else if(tree->nptr > 0) { // the val should be in a tree pointed to by tree->ptr[i]. //printf("val=%d may be in tree->ptr[%d]. ", val, i); bDelete(tree->ptr[i], val, tree, i); //printf("now check tree for <50% values. "); return bApplyChanges(tree, parent, treeindex); } else { // leaf reached. printf("val=%d does NOT exist. ", val); return tree; } } void bPrintFormatted(btree tree) { btree ptr; int i; queue q = {NULL}; qPush(&q, tree); qPush(&q, NULL); printf("--------------------------------------------------------------------------- "); while(qEmpty(&q) == FALSE) { ptr = qPop(&q); if(ptr) { for(i=0; inval-1; ++i) printf("%d-", ptr->val[i]); if(inval) printf("%d ", ptr->val[i]); for(i=0; inptr; ++i) qPush(&q, ptr->ptr[i]); } else { printf(" "); if(qEmpty(&q) == FALSE) qPush(&q, NULL); } } printf("--------------------------------------------------------------------------- "); } int main() { btree tree = bNew(); tree = bInsert(tree, 1); tree = bInsert(tree, 2); tree = bInsert(tree, 4); tree = bInsert(tree, 3); tree = bInsert(tree, 5); tree = bInsert(tree, 6); tree = bInsert(tree, 7); tree = bInsert(tree, 8); tree = bInsert(tree, 9); tree = bInsert(tree, 10); tree = bInsert(tree, 11); tree = bInsert(tree, 12); tree = bInsert(tree, 13); tree = bInsert(tree, 14); tree = bInsert(tree, 15); tree = bInsert(tree, 16); tree = bInsert(tree, 17); bPrintFormatted(tree); tree = bDelete(tree, 6, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 9, NULL, -320); bPrintFormatted(tree); return 0; }
Explanation
It is easier to insert and delete nodes into a B-tree retaining the B-tree properties than it is to maintain the best possible K-way search tree at all times. Thus, the reasons for using B-trees rather than optimal K-way search trees for indices are the same as those for using AVL trees, as opposed to optimal binary search trees, when maintaining dynamic internal tables.
typedef struct node *btree; struct node { type val[K]; btree ptr[K+1]; int nptr; int nval; };
nptr is the number of non-null pointers and nval is the number of valid values in a node. For a leaf-node, nptr == 0, while for all other nodes, nptr == nval + 1.
Examples: Orignal tree of order K = 3
Examples:
Note that in the deletion of value 40, since it is a non-leaf node, its next value in the search order, 45, is promoted to its place so that 45 appears in the root. Then 45 is deleted from the leaf node r. After deletion, r has less than K/2 values. So its sibling s is checked for any extra values. s, with a value of 55, has only K/2 values. So the values of r, s, and 50 (the value of the parent node t between the two children) are combined to get a node with the values (50, 55). Now t contains less than K/2 values. Since it is a non-leaf node, it checks its adjacent node to see if it has more than K/2 values. The node u, with the values (10, 20), actually has more than K/2 values. So the last value of u, 20, is promoted to the root and the value 45 is demoted to t. The rightmost sibling of u (the node containing a value of 28) becomes the leftmost sibling of t. 20, the rightmost sibling of u, becomes the leftmost sibling of t. A symetric transformation is done when the adjacent node appears in the right. If none of the adjacent nodes of t contain any extra values, then t, u, and the value in their parent node between these two pointers are combined to form one node. Then their parent node is checked to see if it has less than K/2 values, and the procedure continues.
For deletion, the main driver function is bDelete(), which calls itself recursively until it reaches a node that contains the value being deleted. It then calls delete(), which checks for the node to be a leaf or a non-leaf node and takes appropriate action as just described.
During insertion, bApplyChanges() is called from bDelete() and delete(). bApplyChanges is similar to bMakeChanges, and does the job of demotion and propagation of values in the current node and its siblings. It uses a function called getAdjTree(), which returns a node adjacent to a node with the same parent. combineNodes(), a procedure complementary to getSplitNode(), combines the second node with the first. In order to get the next value in the search order from the tree, the function getNextVal() is called. deleteVal() is a function that is complementary to the function insertVal(), and is used to delete a value, along with its right pointer, from a node. It then returns the pointer.
Points to Remember
Write procedures for insertion and deletion in a B+ tree.
Program
/*********************** bplus.q.c **************************/ #include #include typedef struct qnode qnode; typedef btree qtype; typedef struct { qnode *front; qnode *rear; } queue; struct qnode { qtype op; qnode *next; }; bool qEmpty( queue *q ) { return (q->front == NULL); } void qPush( queue *q, qtype op ) { /* * pushes op at q.rear. */ qnode *ptr = (qnode *)malloc( sizeof(qnode) ); ptr->op = op; ptr->next = NULL; if( qEmpty(q) ) // first element in q. q->front = ptr; else q->rear->next = ptr; q->rear = ptr; } qtype qPop( queue *q ) { /* * pops op from q->front. */ qnode *ptr = q->front; qtype op; if( qEmpty(q) ) return (qtype)-1; q->front = q->front->next; if( qEmpty(q) ) q->rear = NULL; op = ptr->op; free(ptr); return op; } /************************** bplus.c *************************/ #include #include #define K 5 typedef struct node node; typedef int type; typedef node *btree; typedef enum {FALSE, TRUE} bool; #include "bplusq.c" struct node { type val[K]; // data in the node. // actually first K-1 vals are valid. Kth entry is used // during breaking of the node. btree ptr[K+1]; // pointers to other nodes in the node. // one extra ptr to be used in some loops. int nptr; // no of pointers in the node = non-null ptrs. // this can also be used to see whether a node is a leaf. int nval; // no of values in the node. // this is reqd even when nptr is present: for leaf nodes. // for non-leaf nodes: nptr = nval+1. // for leaf nodes: nptr = 0. node *right; // if this is a leaf node, this points to its sibling. }; btree bNew() { /* * returns a new initialized node. */ btree tree = (btree)malloc(sizeof(node)); tree->nval = tree->nptr = 0; tree->right = NULL; return tree; } void insertVal(btree tree, type val, btree ptr) { /* * insert (val, ptr) in node pointed to by tree without any checks. */ int i, j; for(i=0; inval && tree->val[i]val[i]. // shift-next the next values by one position. for(j=tree->nval-1; j>=i; --j) { tree->val[j+1] = tree->val[j]; tree->ptr[j+2] = tree->ptr[j+1]; } // insert val now at i. tree->val[i] = val; tree->nval++; tree->ptr[i+1] = ptr; if(ptr != NULL) // tree is NOT a leaf. tree->nptr++; printf(" val %d inserted at position %d, tree->nptr=%d tree- >nval=%d. ", val, i, tree->nptr, tree->nval); } btree getSplitNode(btree tree) { /* * returns a new node containing vals and pointers from tree. */ int i, j; btree ptr = bNew(); // copy vals. for(i=(K-1)/2+1, j=0; ival[j] = tree->val[i]; ptr->nval = K/2; if(tree->nptr > 0) // non-leaf node. tree->nval = K-K/2-1; // temporarily this node contains an extra val. else // leaf node. tree->nval = K-K/2; // copy ptrs. //for(i=(K-1)/2, j=0; i<=K; ++i, ++j) for(i=(K-1)/2+1, j=0; i<=K; ++i, ++j) ptr->ptr[j] = tree->ptr[i]; if(tree->nptr > 0) { // non-leaf nodes. ptr->nptr = K/2+1; tree->nptr = K-K/2; } else { // leaf nodes. tree->nptr = ptr->nptr = 0; tree->right = ptr; // bplus tree: list of leaves. } return ptr; } btree bMakeChanges(btree tree, btree ptr, btree parent) { /* * last val of tree should be inserted in parent. * if parent == NULL, then tree was root. * tree = parent->ptr[i] if parent is NOT NULL. */ // extract the last value from tree. type val = (tree->nptr>0 ? tree->val[tree->nval] : tree->val[tree- >nval-1]); printf("in bMakeChanges(). "); if(parent == NULL) { parent = bNew(); parent->ptr[0] = tree; parent->nptr = 1; } if(parent->nval < K-1) { // parent has space. insertVal(parent, val, ptr); } else { // parent full. printf("parent is full. "); insertVal(parent, val, ptr); return getSplitNode(parent); } return parent; } btree bInsert(btree tree, type val) { /* * calls insert to insert val in tree. * if the return node is diff from tree, that means a new node has * been created. This calls bMakeChanges(). */ btree insert(btree tree, type val); btree ptr = insert(tree, val); if(ptr != tree) // node was split. return bMakeChanges(tree, ptr, NULL); return tree; } btree insert(btree tree, type val) { /* * inserts val in tree. * returns tree if there is no change. * if there is creation of a new node then the new node is returned. */ int i; btree ptr; if(tree->nptr > 0) { // non-leaf. for(i=0; inval && tree->val[i]ptr[i]. printf(" val should be inserted in tree->ptr[%d]. ", i); ptr = insert(tree->ptr[i], val); if(ptr != tree->ptr[i]) return bMakeChanges(tree->ptr[i], ptr, tree); return tree; } else { // tree is a leaf. if(tree->nval < K-1) { // space is available in the leaf. insertVal(tree, val, NULL); return tree; } else { // leaf full! printf(" leaf is full. "); insertVal(tree, val, NULL); // now break the leaf node. return getSplitNode(tree); } } } tree = bInsert(tree, 6); tree = bInsert(tree, 7); tree = bInsert(tree, 8); tree = bInsert(tree, 9); tree = bInsert(tree, 10); tree = bInsert(tree, 11); tree = bInsert(tree, 12); tree = bInsert(tree, 13); tree = bInsert(tree, 14); tree = bInsert(tree, 15); tree = bInsert(tree, 16); tree = bInsert(tree, 17); bPrintFormatted(tree); tree = bDelete(tree, 6, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 5, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 7, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 8, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 9, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 10, NULL, -320); bPrintFormatted(tree); tree = bDelete(tree, 11, NULL, -320); bPrintFormatted(tree); printf("The linked list of leaf nodes: "); for(; ptr; ptr=ptr->right) { for(i=0; inval; ++i) printf("%d-", ptr->val[i]); printf(" "); } printf(" "); return 0; }
Explanation
typedef struct node *btree; struct node { type val[K]; btree ptr[K+1]; int nptr; int nval; node *right; };
nptr is the number of non-null pointers and nval is the number of valid values in a node. For a leaf node, nptr == 0 while for all other nodes, nptr == nval + 1. The pointer right points to the node to the right of this node.
Examples:
Note that for a value v in a node and its two adjacent pointers on left and right, all values on left are £ v and all values on right are > v.
Examples
Note that the value 45 is deleted from the leaf node r. After deletion, r has less than K/2 values. So its sibling s is checked for any extra values. s, with a value of 55, has only K/2 values. So the values of r, s, and 50 (the value of the parent node t between the two children), are combined to get a node with the value 55, and the value 45 is deleted from t, along with its right pointer. r's right pointer is appropriately updated. Now t contains less than K/2 values. Since it is a non-leaf node, it checks its adjacent node to see if it has more than K/2 values. The node u, with the values (10, 15) actually has more than K/2 values. So the last value of u, 15, is promoted to the root and the value 28 is demoted to t. The rightmost sibling of u (the node containing a value of 25) becomes the leftmost sibling of t. A symmetric transformation is done when the adjacent node appears in the right. If none of the adjacent nodes of t contain any extra values, then the values of r, u, and the value in their parent node between these two pointers are combined to form one node. Then their parent node is checked to see if it has less than K/2 values, and the procedure continues.
For deletion, the main driver function is bDelete(), which calls itself recursively until it reaches the leaf node that contains the value being deleted. It then calls delete(), which takes appropriate action as just described. During insertion, bApplyChanges() is called from bDelete() and delete(). bApplyChanges is similar to bMakeChanges, and does the job of demotion and propagation of values in the current node and its siblings. It uses a function called getAdjTree(), which returns a node adjacent to a node with the same parent. combineNodes(), a procedure complementary to getSplitNode(), combines the second node with the first. In order to get the next value in the search order from the tree, the function getNextVal() is called. deleteval() is a function that is complementary to the function insertVal(), and is used to delete a value, along with its right pointer, from a node. It then returns the pointer.
Points to Remember
Part I - C Language
Part II - Data Structures
Part III - Advanced Problems in Data Structures