448dfca95c4e2d2368233c32c5e6c0c37311e588
haas/fall2026/data/projects/dlt0.md
| ... | ... | @@ -0,0 +1,195 @@ |
| 1 | +# PROJECT: DOUBLY LINKED TREES (dlt0) |
|
| 2 | + |
|
| 3 | +## OBJECTIVE |
|
| 4 | + |
|
| 5 | +Re-using our doubly-linked node structure to construct an ordered |
|
| 6 | +binary search tree. |
|
| 7 | + |
|
| 8 | +## TASK |
|
| 9 | + |
|
| 10 | +By the deadline, please do the following: |
|
| 11 | + |
|
| 12 | + * create a `Tree` management struct, containing a `root` `Node` |
|
| 13 | + pointer and a `height` integer |
|
| 14 | + * `root` points to the node at the top of the tree (NULL if empty) |
|
| 15 | + * `height` tracks the maximum height of the tree (an empty tree has |
|
| 16 | + a height of 0) |
|
| 17 | + * implement the following Tree functions (each takes a `Tree *` as |
|
| 18 | + its first parameter, and returns the updated `Tree *`): |
|
| 19 | + * `Tree *mktree ();` |
|
| 20 | + * `Tree *cleartree (Tree *);` |
|
| 21 | + * `Tree *rmtree (Tree *);` |
|
| 22 | + * `Tree *addnode (Tree *, Node *);` |
|
| 23 | + * `Tree *grabnode (Tree *, Node **);` |
|
| 24 | + * `Tree *traverse (Tree *, List *, int);` |
|
| 25 | + * `Tree *findnode (Tree *, Node **, int);` |
|
| 26 | + * test your tree implementation |
|
| 27 | + |
|
| 28 | +## BACKGROUND |
|
| 29 | + |
|
| 30 | +A binary search tree is an ordered, hierarchical data structure: each |
|
| 31 | +node has up to two children, and the position of a node is determined |
|
| 32 | +by its value relative to the nodes already in the tree. |
|
| 33 | + |
|
| 34 | +We will not be creating a new node type. The `Node` you built for your |
|
| 35 | +doubly-linked list already has two pointers, and we will simply give |
|
| 36 | +them new meaning: |
|
| 37 | + |
|
| 38 | + * `prev` will point to the **left** child |
|
| 39 | + * `next` will point to the **right** child |
|
| 40 | + |
|
| 41 | +The ordering rule is as follows: |
|
| 42 | + |
|
| 43 | + * values **less than or equal** to a node's value go into its left |
|
| 44 | + subtree |
|
| 45 | + * values **greater than** a node's value go into its right subtree |
|
| 46 | + |
|
| 47 | +Note that there is no parent pointer: your `Node` struct does not need |
|
| 48 | +to (and should not) change for this project. Functions that need to |
|
| 49 | +know about a node's parent will need to track it as they descend. |
|
| 50 | + |
|
| 51 | +You are expected to continue making use of your existing node |
|
| 52 | +functions (`mknode()`, `rmnode()`) wherever they apply. |
|
| 53 | + |
|
| 54 | +### TREE STRUCT |
|
| 55 | + |
|
| 56 | +Much like `List` and the others before it, `Tree` is a management layer: |
|
| 57 | +it gives us a reliable point of access to the tree (`root`), and a place |
|
| 58 | +to keep track of information about the tree as a whole (`height`). |
|
| 59 | + |
|
| 60 | +### MKTREE() |
|
| 61 | + |
|
| 62 | +Like `mklist()`, except it focuses on creating a new, empty Tree |
|
| 63 | +instance. Allocate memory and do any error checking, ensuring `root` is |
|
| 64 | +set to `NULL` and `height` to 0. |
|
| 65 | + |
|
| 66 | +On error, `mktree()` should return `NULL`. |
|
| 67 | + |
|
| 68 | +### CLEARTREE() |
|
| 69 | + |
|
| 70 | +The purpose of `cleartree()` is to effectively and reliably empty a |
|
| 71 | +tree: every node in the tree is removed and deallocated (this will |
|
| 72 | +require visiting every node, so think carefully about the order in which |
|
| 73 | +you do it: don't free a node before you are done with its children). |
|
| 74 | + |
|
| 75 | +An empty tree is returned, or `NULL` on some sort of error state. |
|
| 76 | + |
|
| 77 | +### RMTREE() |
|
| 78 | + |
|
| 79 | +The purpose of `rmtree()` is to empty and deallocate a given tree, |
|
| 80 | +returning a `NULL` value at the end. |
|
| 81 | + |
|
| 82 | +You can pass a populated tree to `rmtree()`: it is `rmtree()`'s job to |
|
| 83 | +call other routines to clear out the tree before deallocating the tree |
|
| 84 | +pointer in question. |
|
| 85 | + |
|
| 86 | +### ADDNODE() |
|
| 87 | + |
|
| 88 | +Places the provided node into its proper position in the tree, according |
|
| 89 | +to the ordering rule (value <= goes to the left subtree, otherwise |
|
| 90 | +right). |
|
| 91 | + |
|
| 92 | +The parameters are as follows: |
|
| 93 | + |
|
| 94 | + * pointer to the tree |
|
| 95 | + * pointer to the node you'd like to add to the tree |
|
| 96 | + |
|
| 97 | +A pointer to the tree is returned. |
|
| 98 | + |
|
| 99 | +Be sure to handle the case of an empty tree (the new node becomes the |
|
| 100 | +`root`), and to keep `height` up to date as the tree grows. |
|
| 101 | + |
|
| 102 | +You should check to make sure the involved things are not NULL (as a |
|
| 103 | +segmentation fault would occur otherwise). |
|
| 104 | + |
|
| 105 | +### GRABNODE() |
|
| 106 | + |
|
| 107 | +To get a node out of the tree, we `grabnode()` it. This function will |
|
| 108 | +isolate a node from the tree, maintaining the ordering of the remaining |
|
| 109 | +nodes. |
|
| 110 | + |
|
| 111 | +The parameters are as follows: |
|
| 112 | + |
|
| 113 | + * pointer to the tree |
|
| 114 | + * double pointer to the node you'd like to grab |
|
| 115 | + |
|
| 116 | +A pointer to the tree is returned, and the double pointer will reference |
|
| 117 | +the now-isolated node (its `prev` and `next` pointers should be NULL). |
|
| 118 | + |
|
| 119 | +There are three scenarios you will need to account for: |
|
| 120 | + |
|
| 121 | + * the node has no children (a leaf): simply remove it |
|
| 122 | + * the node has one child: that child takes its place |
|
| 123 | + * the node has two children: the **greatest value in its left |
|
| 124 | + subtree** is promoted to take its place |
|
| 125 | + |
|
| 126 | +Be mindful of grabbing the `root` node (the tree's `root` pointer must be |
|
| 127 | +updated), and be sure to keep `height` accurate afterwards. |
|
| 128 | + |
|
| 129 | +### TRAVERSE() |
|
| 130 | + |
|
| 131 | +Recursively traverses the tree, building a **list** of the nodes in the |
|
| 132 | +order visited. |
|
| 133 | + |
|
| 134 | +The parameters are as follows: |
|
| 135 | + |
|
| 136 | + * pointer to the tree |
|
| 137 | + * pointer to the list the results will be placed into |
|
| 138 | + * integer indicating the traversal order: |
|
| 139 | + * inorder (left, node, right) |
|
| 140 | + * preorder (node, left, right) |
|
| 141 | + * postorder (left, right, node) |
|
| 142 | + |
|
| 143 | +You will want to define meaningful names for the three traversal orders |
|
| 144 | +(constants, `#define`, or `enum`) instead of passing around bare |
|
| 145 | +numbers. |
|
| 146 | + |
|
| 147 | +A pointer to the tree is returned. |
|
| 148 | + |
|
| 149 | +Because the nodes in the tree are using their `prev` and `next` pointers |
|
| 150 | +for tree connections, the list must be populated with **new** nodes |
|
| 151 | +(created with `mknode()`) containing copies of each value. The tree |
|
| 152 | +itself must remain unchanged by a traversal. The caller is responsible |
|
| 153 | +for the resulting list. |
|
| 154 | + |
|
| 155 | +Hint: an inorder traversal of a binary search tree will yield its values |
|
| 156 | +in sorted order. |
|
| 157 | + |
|
| 158 | +### FINDNODE() |
|
| 159 | + |
|
| 160 | +Locates the first match of a value within the tree. |
|
| 161 | + |
|
| 162 | +The parameters are as follows: |
|
| 163 | + |
|
| 164 | + * pointer to the tree |
|
| 165 | + * double pointer which will be set to the node found |
|
| 166 | + * the integer value being sought |
|
| 167 | + |
|
| 168 | +A pointer to the tree is returned. If the value is not found, the node |
|
| 169 | +double pointer is set to `NULL`. The tree is not modified, and the |
|
| 170 | +located node is **not** removed from the tree. |
|
| 171 | + |
|
| 172 | +Remember that the tree's ordering rule means you do not need to look at |
|
| 173 | +every node: use it to decide which way to descend. |
|
| 174 | + |
|
| 175 | +## SUBMISSION |
|
| 176 | + |
|
| 177 | +``` |
|
| 178 | +208:dlt0:final tally of results (208/208) |
|
| 179 | +*:dlt0:create tree struct [13/13] |
|
| 180 | +*:dlt0:create and test mktree function [13/13] |
|
| 181 | +*:dlt0:create and test cleartree function [13/13] |
|
| 182 | +*:dlt0:create and test rmtree function [13/13] |
|
| 183 | +*:dlt0:create and test addnode function [26/26] |
|
| 184 | +*:dlt0:create and test grabnode function [26/26] |
|
| 185 | +*:dlt0:create and test traverse function [26/26] |
|
| 186 | +*:dlt0:create and test findnode function [26/26] |
|
| 187 | +*:dlt0:tree height is accurately maintained [26/26] |
|
| 188 | +*:dlt0:code is well commented, organized, functional [26/26] |
|
| 189 | +``` |
|
| 190 | + |
|
| 191 | +Additionally: |
|
| 192 | + * Solutions not abiding by spirit of project will be subject to a 25% overall deduction |
|
| 193 | + * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction |
|
| 194 | + * Solutions not utilizing indentation to promote scope and clarity will be subject to a 25% overall deduction |
|
| 195 | + * Solutions not organized and easy to read (assume a terminal at least 90 characters wide, 40 characters tall) are subject to a 25% overall deduction |