home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 155_01 / readme < prev    next >
Text File  |  1987-10-09  |  16KB  |  396 lines

  1.                       B-Tree Library
  2.                        Developed by
  3.                         Ray Swartz
  4.  
  5.     The programming routines that make up this b-tree library
  6. are hereby put into the public domain.  Any use, either
  7. private or commercial, is allowed upon the condition that
  8. a notice appear within the program documentation or on the terminal
  9. screen (during execution of the program)
  10. that these b-tree routines were developed by Ray Swartz.  Such notice
  11. must be clear and obvious, appearing at the beginning of written documentation
  12. or displayed on the terminal screen during the initial phases of the program.
  13.  
  14.     Use of these routines without proper attribution is not allowed.
  15. This violates the spirit within which these routines were
  16. developed and put into the public domain.  The author reserves the right
  17. to charge royalties of 10% of the gross revenue of any computer program
  18. using these routines WITHOUT the required notice.
  19.  
  20.  
  21.     These routines create and manage a balanced binary tree.
  22. The advantages of these trees are well-known and will not be described
  23. here.  However, for an excellent discussion of this, and a number
  24. of related topics, see D. Knuth, The Art of Computer Programming -
  25. Volume 3  (Sorting and Searching) pages 422 - 471.  This book is
  26. published by Addison-Wesley, ISBN: 0-201-03803-X.  
  27.  
  28.     The keyfile created to do this has two distinct parts.
  29. The first part holds the header information on the keyfile itself.
  30. This takes up the first 19 (DATA_LENGTH) bytes of the file and
  31. are arranged as follows:
  32.  
  33.   1) A Tilde (~) - used to denote a keyfile
  34.   2) The length of this file's key field (2-byte integer (ascii))
  35.   3) Node number of next available key node (5-byte integer (ascii))
  36.   4) Node number of first node in list (5-byte integer (ascii))
  37.   5) Number of non-deleted nodes in the list (5-byte integer (ascii))
  38.   6) A Tilde
  39.  
  40. This information is stored in the structure KEYINFO which is declared
  41. in the file BTREE.H.
  42.  
  43.     The second part of a keyfile is the key nodes.  Each node holds:
  44.  
  45.   1) A delete flag (YES if deleted, NO if active) (2 bytes)
  46.   2) A balance factor (-1 if unbalanced to left, 0 if balanced,
  47.          1 if unbalanced to right). (2 bytes)
  48.   3) The node number just to the left of this one. (5 bytes)
  49.   4) The node number just to the right of this one. (5 bytes)
  50.   5) The record number of the data containing this key. (5 bytes)
  51.   6) The key this node references (length depends on keyfile initialization)
  52.  
  53. This information is stored in the structure NODE which is declared in the
  54. file BTREE.H.
  55.  
  56.     Before information can be stored in a keyfile, the file must
  57. be initialized by the TREEINIT program included with this library.
  58. The user must declare the length of the keys this file will hold.
  59. This length is a maximum that must remain constant.  Changing the
  60. size of the key requires rebuilding the tree stored in the keyfile.
  61.  
  62.     No provisions are made for managing the data records referenced by
  63. these keys.  This is the responsibility of the programmer using
  64. these routines.
  65.  
  66.  
  67.     The heart of this system is the INSERT routine which
  68. not only inserts new keys into the tree but maintains the tree's
  69. balance as well.  This routine, in it entirety, was implemented by
  70. following the algorithm in Knuth, The Art of Computer Programming
  71. Volume 3, Pages 455 - 457.  No attempt should be made to modify
  72. the INSERT function without a thorough understanding of this material.
  73.  
  74.  
  75.     The b-tree library consists of 3 program files:
  76.  
  77.         BTREE1.C
  78.         BTREE0.C
  79.         TERMCTRL.C.
  80.  
  81. The file BTREE1.C contains the INSERT, DELETE_KEY, GET_NEXT,
  82. GET_PREVIOUS, and FIND_KEY functions.  BTREE0.C contains support
  83. functions called by BTREE1.C.  TERMCTRL.C contains all the routines
  84. that use functions that directly manipulate the terminal.
  85. Generally, these terminal control functions are not required for
  86. proper usage, however the routines will not work without them.
  87. They are included for 2 reasons.  First, when the
  88. routines were written, I integrated them into the program.  Removing them
  89. would be a pain.  Second, once the appropriate changes are made to the
  90. CURSOR routine (addressing the cursor) and the CLEAR_END_OF_PAGE macro
  91. (in BTREE.H), an elementary screen driver is enabled for whatever terminal
  92. is used.
  93.  
  94.     To create a linking library, these files should be organized so
  95. that BTREE1 can reference routines in BTREE0 and TERMCTRL and those
  96. in BTREE0 can reference routines in TERMCTRL.
  97.  
  98.  
  99.     Inserting keys into a balanced binary tree is a straight
  100. forward process.  Deleting keys from such a tree and maintaining
  101. the balance is another thing entirely.  In fact, deleting tree nodes
  102. may require several adjustments to maintain tree balance.  In an
  103. attempt to avoid all the complexities introduced by performing
  104. true node deletion, I opted for a conceptually simpler approach.
  105. Instead of actually deleting nodes from the tree, I mark them as
  106. deleted without removing them from the tree.  In this way, the tree's
  107. structure never changes except during insertion.  A "deleted" node
  108. remains in the tree except it can't be "found."
  109.  
  110.     Like all shortcuts, this one has a cost.  As more and more
  111. nodes are deleted, the search to find an "active" node takes longer
  112. since an increase number of nodes are being read but "ignored."
  113. However, searching a binary tree is quite efficient.  Even if 10%
  114. of a tree was deleted, the search would not take much longer in
  115. real time.  The obvious solution is to re-build the tree when the
  116. performance slows unacceptability due to an overabundance of deleted nodes.
  117.  
  118.  
  119.     This set of routines uses three tricks.  The first one is relying on
  120. someone else's algorithm to design the insertion function.  The second
  121. one is to mark deleted nodes in place of actually removing them from the
  122. tree.  The last one is
  123. the use of a stack to find the next and previous nodes from the current
  124. one.  I call this a trick because how it works is not obvious.
  125.  
  126.     The problem is this: Given that we have searched for and found
  127. a specific node, how do we get to the key that is just below
  128. (previous) or just above (next) this one?  Consider the balanced
  129. binary tree shown below that has 15 nodes - all active:
  130.  
  131.  
  132.                            8
  133.                          /   \
  134.                       /         \
  135.                     4             12
  136.                  /     \         /   \
  137.                2         6     10      14
  138.               / \       / \    / \     / \
  139.              1   3     5   7  9   11 13   15
  140.  
  141. Suppose node 12 is the current node and we want to find the next larger
  142. key.  That key is the one at the end of the left branch of the node
  143. one to the right of the current node - in this case, node 13.  The
  144. exact opposite is true of the key that is the next smaller or previous
  145. node.  It is the one at the end of the right branch of the node
  146. one to the left of the current node - in this case node 11.  This is the
  147. easy possibility.  What happens if we go the the next one - making
  148. node 13 the current node - and now want to know what the next node is?
  149. The problem is that node 13 is the end of a tree branch and we
  150. can't go further "down" the tree.  Instead, we must go "up" a node
  151. to 14.  How do we keep treck of which node is up one from here?
  152.  
  153. We do this by building a stack as we look through the tree.  In
  154. reality, we use two identical stacks - a right one and a left one.
  155. In the right stack we "push" values each time we move down and to the
  156. right in the tree.  We do the same on the left stack when we move
  157. down and to the left.  As an example, to search for node 13, we begin
  158. at the head of the list (node 8) and move to the right (since 13 > 8).
  159. After moving right, we push 8 onto the right stack.  At 12 we again move
  160. right (13 > 12) to node 14.  We then push 12 onto the right stack.
  161. At 14 we move left (13 < 14).  This time we push 14 onto the left stack
  162. since we moved left here.  The search now terminates since we are at node
  163. 13.  The stacks look like:
  164.  
  165.  
  166.     Left      Right
  167.      14         8
  168.                12
  169.  
  170. If we are asked to get the previous node, we determine that we can't
  171. go down and must go up.  Since it is the previous one, we "pop" the
  172. right stack (since this was the last place we found a node with
  173. a lower key value), and move to node 12.  The stacks now look like:
  174.  
  175.     Left      Right
  176.      14         8
  177.  
  178. But there is something wrong.  The left stack has bogus information in it.
  179. We went to 14 AFTER we went to 12.  Thus, if we pop 12 off the right
  180. stack (in effect going up 2 levels in the tree) then we should take
  181. 14 off the left stack, as well.  How would the computer know this
  182. without some inherent knowledge of the tree structure which is impossible.
  183. We can do this if we include an additional piece of data in the stack -
  184. the tree level of the element number on the stack.  The tree level
  185. is defined as the number of nodes that exist between this node and
  186. the head of the tree.  The diagram below shows the same tree with the
  187. tree levels marked:
  188.  
  189. level 0                    8
  190.                          /   \
  191.                       /         \
  192. level 1             4             12
  193.                  /     \         /   \
  194. level 2        2         6     10      14
  195.               / \       / \    / \     / \
  196. level 3      1   3     5   7  9   11 13   15
  197.  
  198. Thus, the actual stacks look like this after 13 is found:
  199.  
  200.      Left            Right
  201.   Node   Level    Node   Level
  202.    14      2        8      0
  203.                    12      1
  204.  
  205. Now, when we pop 12 off the right stack, we see that we have gone up
  206. to tree level 1.  This means that any data on the left stack
  207. below level 1 is no longer of any use and should be removed.
  208. After jumping up to node 12 from node 13, the stacks look like this:
  209.  
  210.      Left            Right
  211.   Node   Level    Node   Level
  212.                     8       0
  213.  
  214. The stacks would return to their previous values if at 12 we moved
  215. to the next node (13).
  216.  
  217.     If a stack is empty when we try to pop a value off of it, then
  218. we are at the end of the list.  As an example, if we search for node
  219. 15, we move right 3 times leaving the left stack empty.  A request to
  220. go the next one fails since we can't move down and the left stack
  221. is empty.
  222.  
  223.  
  224.  
  225.  
  226. FILE *open_keyfile(filename, fileinfo)   /* used to open keyfile */
  227. char *filename;  /* name of file to open */
  228. struct keyinfo *fileinfo;  /* where to store file header info */
  229.  
  230.  
  231. close_keyfile(fileinfo)   /* write out header information and close file */
  232. struct keyinfo *fileinfo;
  233.  
  234.  
  235. write_node(nbr, nodeinfo, fileinfo)   /* write a node's info to file */
  236. long nbr;   /* node to write at */
  237. struct node *nodeinfo;  /* node info to write */
  238. struct keyinfo *fileinfo;
  239.  
  240. print_node(node1)  /* display contents of a node on screen (debug) */
  241. struct node *node1;
  242.  
  243. push_left_stack(nbr)  /* moved left in tree - record this in stack */
  244. long nbr;  /* number of node to push onto stack */
  245.  
  246. push_right_stack(nbr)  /* moved right in tree - record this in stack */
  247. long nbr;  /* number of node to push onto stack */
  248.  
  249.   /* return the next one on the stack and keep left stack at proper level */
  250. long pop_right_stack() 
  251.  
  252.  
  253.   /* return the next one on the stack and keep right stack at proper level */
  254. long pop_left_stack()
  255.  
  256.  
  257. get_node(nbr, nodeinfo, fileinfo)  /* read the info stored in node NBR */
  258. long nbr;     /* node to retrieve */
  259. struct node *nodeinfo;   /* where to store the node's information */
  260. struct keyinfo *fileinfo;
  261.  
  262.  
  263. insert(argkey, recnbr, fileinfo) /* insert key (argkey) into tree */
  264. struct keyinfo *fileinfo;  /* file to insert key into */
  265. char *argkey;    /* key to insert */
  266. long recnbr;     /* record number of corresponding data record */
  267.  
  268.  
  269. link(alpha1, node1, alpha2, node2)
  270. int alpha1, alpha2;
  271. struct node *node1, *node2;
  272.     /* see definition of LINK(a, P) on Knuth pg. 455 (Vol. 3) */
  273.  
  274.  
  275. nbr_link(nbr, alpha, node1) /* set a record number according to alpha */
  276. long *nbr;   /*  this routine is used in insert when a record number */ 
  277. int alpha;   /*  needs to be set with  nbr = LINK(alpha, node1) */
  278. struct node *node1;
  279.  
  280.  
  281. link_nbr(alpha, node1, nbr) /* set a link according to alpha */
  282. int alpha;    /* used in insert when  LINK(alpha, node1) = nbr */
  283. struct node *node1;
  284. long nbr;
  285.  
  286. node_bal(alpha, node1, node2, node3)  /* node balancing in Step A9 */
  287. int alpha;
  288. struct node *node1, *node2, *node3;
  289.  
  290.  
  291. delete_key(node_nbr, current_node, fileinfo)  /* mark a node as deleted */
  292. long node_nbr;  /* node to delete */
  293. struct node *current_node;  /* deleted node's information */
  294. struct keyinfo *fileinfo;  /* keyfile */
  295.  
  296.  
  297.  
  298. get_next(node_nbr, current_node, fileinfo)  /* retrieve next higher key */
  299. struct keyfile *fileinfo;
  300. struct node *current_node;  /* where to store node's info */
  301. long *node_nbr;    /* node to retrieve */
  302.  
  303.  
  304.  
  305. find_key(key1, node_nbr, current_node, fileinfo) /* locate a key */
  306. char *key1;  /* key to find */
  307. struct keyinfo *fileinfo;
  308. long *node_nbr;  /* number of "found" node */
  309. struct node *current_node;  /* where "found" node is stored */
  310.  
  311.  
  312. get_previous(node_nbr, current_node, fileinfo) /* retrieve next lower key */
  313. long *node_nbr;  /* number of current node */
  314. struct node *current_node;  /* current node's information */
  315. struct keyinfo *fileinfo;   /* keyfile info */
  316.  
  317.  
  318. cursor(row, col)  /* cursor movement on a televideo 925 */
  319. int row;  /* row and column to more cursor to */
  320. int col;
  321.  
  322.  
  323. int main_prompt(prompt)   /* prompt used as database interface */
  324. char *prompt;
  325.  
  326. /* prints "Find Delete Next Previous Insert Quit" and
  327. returns the corresponding token - only reacts to the first character
  328. entered */
  329.  
  330.  
  331.    /* prompt for and read the key to FIND (from keyboard) */
  332. get_key(prompt, key, fileinfo) 
  333. struct keyinfo *fileinfo;
  334. char *prompt;  /* prompt to print */
  335. char *key;     /* key entered */
  336.  
  337.  
  338. print_msg(prompt)  /* go to line 20, clear window, ring bell, prompt */
  339. char *prompt;
  340.  
  341.  
  342. delay() /* a delay loop (counts to 10000) */
  343.  
  344.  
  345. yes_or_no(prompt)  /* print prompt and return YES or NO */
  346. char *prompt;      /* loops until Y/y/N/n is entered */
  347.  
  348.  
  349. Contents of BTREE.H file:
  350.  
  351.  
  352. #define NOT_FOUND -1
  353. #define AT_END -2
  354. #define YES 1
  355. #define NO 0
  356. #define TOP -1   /* flag to show if top of list = rotate node */
  357. #define END 0    /* end pointer in a node */
  358. #define QUIT 0          /* menu options */
  359. #define FIND 1
  360. #define INSERT 2
  361. #define NEXT 3
  362. #define PREVIOUS 4
  363. #define DELETE 5
  364. #define CLEAR_END_OF_PAGE    printf("%cY",27); /* for televideo 925 */
  365. #define BELL                 putchar(7);
  366. #define DATA_LENGTH 19  /* characters in first record of key file */
  367.  
  368. struct keyinfo {   /* Header information on each open keyfile */
  369.     FILE *file;          /* file pointer to keyfile */
  370.     int keylength;       /* Length of file key */
  371.     long next_avail;     /* Next free node in tree (nbr_in_list + 1) */
  372.     long list_head;      /* Node number at the head of the list */
  373.     long nbr_in_list;    /* Number of (active) nodes in the tree */
  374. };
  375.  
  376. struct node {      /* The composition of a tree-node */
  377.     long rec_nbr;        /* The pointer to the data file record */
  378.     long left_link;      /* The node to this one's immediate left */
  379.     long right_link;     /* The node to this one's immediate right */
  380.     char *key;           /* Pointer to this record's key */
  381.     int delete_flag;     /* 1 if deleted, 0 if live */
  382.     int balance;         /* -1 left subtree longer, 0 even, +1 right bigger */
  383. };
  384.  
  385.  
  386. #define STACK_LENGTH 50    /* length of history stacks */  
  387.  
  388. struct stack {    /* tree traversal stack */
  389.     long element[STACK_LENGTH];   /* Node number pushed onto history stack */
  390.     int level[STACK_LENGTH];      /* Stack level of this element */
  391.     int stack_cntr;               /* Top of stack */
  392. } right_history, left_history;
  393.  
  394. int stack_level;   /* tree level at present */
  395. char instr[256];   /* general string input */
  396.