Corning Community College
CSCS2320 Data Structures
~~TOC~~
======Project: DLT0======
=====Errata=====
This section will document any updates applied to the project since original release:
* __revision 1__: various bugfixes and new unit tests added (20141120)
* unit-addnode had a typo where it was expecting an empty tree to have a height of 0 instead of -1 (FIXED)
* unit-traverse had a number of typos, and saw some increased ruggedness:
* missing semi-colon on line 15 (FIXED)
* invalid variable reference (myList instead of tmpList) on/around line 52 (FIXED)
* extra checks to test invalid modes
* unit-set_mode has been deployed
* valid and invalid modes are checked
* unit-searchtree has been deployed
* NULL, empty, and non-empty tree searches of valid and invalid values
* verify-node.sh has been updated to show absolute totals
* verify-tree.sh has been updated to include new unit tests in its totals
* __revision 2__: new unit tests and verify scripts added (20141121)
* unit-balance has been deployed
* corresponding verify-balance.sh has been deployed
* the unit test for balance assumes a pivot based on the middle (if odd), or left-middle (if even) node
* unit-grabnode has been deployed
* corresponding verify-grabnode.sh has been deployed
* the grabnode unit test, like traverse, tests all 3 implementations in one unit test
* verify-tree.sh has been updated to include new unit tests
* __revision 3__: tweaks, comments, and new unit tests and verify script added (20141122)
* unit-grabnode may not have been working truly as intended. FIXED
* also added a specific test for grabbing from a single node tree
* performs 6 tests in total- NULL, empty, one-node, one node grabbed, two nodes grabbed, three nodes grabbed
* unit-addnode has added tests (looks like 35 in total when correctly implemented)
* unit-copytree has been deployed
* corresponding verify-copytree.sh has been deployed
* we perform 6 tests- NULL, empty, one-node, populated, populated and balanced, populated and node grabbed
* verify-tree.sh has been updated to include new unit test
=====Objective=====
In this project, we continue our conceptual journey and explore yet another data structure: trees.
=====Background=====
A **tree** is our first foray into the realm of non-linear data structures. While it still consists of nodes, and those nodes are linked together, they are not linked together in a way providing a contiguous succession as we have in a list.
Truth be told, the simplest tree is merely a list- the underlying data structure we've worked with all semester. But now, under this new light, we can start to look at a list as merely a procession of singled childed nodes. The more things are different, the more they are the same...
The simplest conceptual tree is one which has a minimum of two connections- often these are called 'binary' trees. We will use that description interchangeably here with 'doubly linked trees'.
The premise behind a tree is that of a parent-child relationship. Each node can be a parent (of two child nodes), and each child can in turn be its own parent (of two child nodes). This creates a connection between relationships of nodes (ie a parent and its descendants), and ends up offering some interesting and useful algorithmic opportunities, along with implementation experience.
====A slight Node modification====
With the tree project, we see a modification to our venerable node struct, used consistently and throughout our adventures this semester.
We keep all the existing content (and changes) that have endured up to this point, and we merely add one additional member: an **other** pointer that can see use in our //iterative// and //stack-based// implementations of various tree operations.
This will also mean mild tweaks are needed to the node library functions, and updated unit tests that will need to be verified.
====conceptualizing a tree====
It is common to think of a tree as a vertical structure, upside-down from the item we commonly think of in nature. Here we will have the tree's "root" at the very top, and all the branches (nodes, and connections to nodes) growing down and out from the root.
====the tree====
The tree data structure presents certain advantages that encourages its use in solving problems, and we accomplish that by its compositional definition:
* a tree has a **root** which forms the basis or starting point of the structure; all nodes are reachable (and related through means of descendance) from the root.
* to put an item on the tree, we insert or **add** it (which you will have 3 separate opportunities to implement in this project!). When we add a node to the tree, it gets positioned in the appropriately available child link off some parent node.
* to get an item off of the tree, we **grab** it (my term- often times it is referred to as deleting a node from the tree). Just as with a list, disconnecting a node from the structure can involve a lot of work to maintain connections and overall integrity of the data structure.
* an important action that will be taking place is the traversal of the tree- ie walking it in certain orders to achieve desired results (sorting/ordering of values, among other things).
As we will be placing nodes in the tree according to their stored values (lesser values go to the left, or previous; greater values go to the right, or next), our tree will naturally sort our data for us.
====tree height can matter====
For some operations (specifically, visualization), the height of the tree can be very important, and constraining the tree height can impose a nice upper bound on the total number of nodes put into the tree.
Just as with the stack's size and the queue's buffer, our tree will have the ability to set a **max_height**, which our adding logic needs to respect.
=====Project Overview=====
For this project, we're going to be implementing the tree data structure utilizing nodes.
====In inc/node.h====
#ifndef _NODE_H
#define _NODE_H
#include
struct node {
char data;
struct node *other;
struct node *prev;
struct node *next;
};
typedef struct node Node;
Node *mknode(char ); // allocate new node containing value
Node *rmnode(Node *); // deallocate node
Node *cpnode(Node *); // duplicate node
#endif
====In inc/tree.h====
#ifndef _TREE_H
#define _TREE_H
#include "list.h"
#define RECURSIVE 0
#define STACK_BASED 1
#define ITERATIVE 2
#define INORDER 0
#define PREORDER 1
#define POSTORDER 2
typedef struct tree Tree;
struct tree {
Node *root; // pointer to root node of tree
char max_height; // max height of tree (0 = unlimited)
char height; // current height of tree
Tree *(*copy)(Tree * ); // copy tree (function pointer)
Tree *(*grab)(Tree *, Node **); // grab node (function pointer)
List *(*walk)(Tree *, char ); // walk tree (function pointer)
};
Tree *mktree (char ); // create new tree
Tree *set_mode (Tree *, char ); // set tree mode (recurse, stack, iter)
Tree *addnode (Tree *, Node * ); // add given node to tree
Tree *copytree_s(Tree * ); // copy/duplicate given tree (stack-based)
Tree *copytree_r(Tree * ); // copy/duplicate given tree (recursion)
Tree *copytree_i(Tree * ); // copy/duplicate given tree (iterative)
Node *searchtree(Tree *, char ); // find node in tree
Tree *grabnode_s(Tree *, Node **); // stack-based grabnode
Tree *grabnode_r(Tree *, Node **); // recursive grabnode
Tree *grabnode_i(Tree *, Node **); // iterative grabnode
List *traverse_s(Tree *, char ); // stack-based traverse (in mode)
List *traverse_r(Tree *, char ); // recursive traverse (in mode)
List *traverse_i(Tree *, char ); // iterative traverse (in mode)
Tree *rmtree (Tree * ); // purge and deallocate tree
Tree *balance (Tree * ); // rebalanced tree (root is midpoint)
void print_tree(Tree * ); // display tree structure (to STDOUT)
#endif
====tree library====
In **src/tree/**, you will find skeletons of the above prototyped functions, hollowed out in anticipation of being made operational.
Figure out what is going on, the connections, and make sure you understand it.
Of particular focus in this project, in addition to the important tree concepts, is that of implementation. Until now we've only bothered to just get it done and working... now, not only do we care about functionality, but we look more closely at how we got there (ie the three different implementation approaches).
====Reference Implementation====
As the layers and complexities rise, narrowing down the source of errors becomes increasingly important.
If your tree copytree_i() isn't working, is it because of a problem with your tree? Or might it be in an underlying node operation it relies upon?
To aid you in your development efforts, you now have the ability to import functioning node, list, and stack implementations into your project for the purposes of testing your tree functionality.
This also provides another opportunity for those who have fallen behind to stay current, as they can work on this project without having needed to successfully get full node or stack (and by extension- list) functionality.
===Using the test reference implementation===
You'll notice that, upon running **make help** in the base-level Makefile, the following new options appear (about halfway in the middle):
** **
** make use-test-reference - use working implementation object files **
** make use-your-own-code - use your node/list implementation code **
** **
In order to make use of it, you'll need to run **make use-test-reference** from the base of your **dlt0** project directory, as follows:
lab46:~/src/data/dlt0$ make use-test-reference
...
NODE, LIST, and STACK reference implementation in place, run 'make' to build everything.
lab46:~/src/data/dlt0$
You'll see that final message indicating everything is in place (it automatically runs a **make clean** for you), and then you can go ahead and build everything with it:
lab46:~/src/data/dlt0$ make
...
**__Debugging__:** When using the test reference implementation, you will not be able to debug the contents of the node, list, and stack functions (the files provided do not have debugging symbols added), so you'll need to take care not to step into these functions (it would be just like stepping into **printf()**. You can still compile the project with debugging support and debug (as usual) those compiled functions (ie the stack functions).
===Reverting back to using your code===
If you were trying out the reference implementation to verify tree functionality, and wanted to revert back to your own code, it is as simple as:
lab46:~/src/data/dlt0$ make use-your-own-code
Local node/list/stack implementation restored, run 'make clean; make' to build everything.
lab46:~/src/data/dlt0$
====Node Library unit tests====
As a result of the addition of **other**, an updated set of unit tests will be released (this will mean a different number of total tests for list than previously).
Be sure to run the various node unit tests and verification scripts to see which functions have fallen out of compliance with the node struct specification changes issued in this project. The **verify-node.sh** script can be especially useful in getting a big picture view of what work is needed.
====Tree library unit tests====
In **testing/tree/unit/**, you will //eventually// find these files:
* **unit-addnode.c** - unit test for the various **addnode()** library function
* **unit-searchtree.c** - unit test for the various **searchtree()** library function
* **unit-grab.c** - unit test for **grabnode_*()** library functions
* **unit-set_mode.c** - unit test for **set_mode()** library function
* **unit-balance.c** - unit test for **balance()** library function
* **unit-treeops.c** - unit test for {mk,rm}tree library functions
* **unit-traverse.c** - unit test for **traverse_*()** library functions
* **unit-print.c** - unit test for displaying tree structure
**NOTE**: At the time of project release, unit tests are not yet available; so they will be released as they are developed.
There are also corresponding **verify-FUNCTION.sh** scripts that will output a "MATCH"/"MISMATCH" to confirm overall conformance with the pertinent tree functionality.
These are complete runnable programs (when compiled, and linked against the tree library, which is all handled for you by the **Makefile** system in place).
Of particular importance, I want you to take a close look at:
* the source code to each of these unit tests
* the purpose of these programs is to validate the correct functionality of the respective library functions
* follow the logic
* make sure you understand what is going on
* ask questions to get clarification!
* the output from these programs once compiled and ran
* analyze the output
* make sure you understand what is going on
* ask questions to get clarification!
=====Expected Results=====
To assist you in verifying a correct implementation, a fully working implementation of the node and tree libraries should resemble the following (when running the respective verify script):
====node library====
Here is what you should get for node:
lab46:~/src/data/dlt0$ bin/verify-node.sh
====================================================
= Verifying Doubly-Linked Node Functionality =
====================================================
[mknode] Total: 6, Matches: 6, Mismatches: 0
[cpnode] Total: 8, Matches: 8, Mismatches: 0
[rmnode] Total: 2, Matches: 2, Mismatches: 0
====================================================
[RESULTS] Total: 16, Matches: 16, Mismatches: 0
====================================================
lab46:~/src/data/dlt0$
With the addition of the **other** pointer, we have a few additional combinations to test.
====tree library====
Here is what you should get for tree:
lab46:~/src/data/dlt0$ bin/verify-tree.sh
coming soon
lab46:~/src/data/dlt0$
=====Submission Criteria=====
To be successful in this project, the following criteria must be met:
* Project must be submit on time, by the posted deadline.
* As this project is due at the very end of the semester, there is no late window. It is either submitted, or it is not.
* All code must compile cleanly (no warnings or errors)
* all requested functions must be implemented in the related library
* all requested functionality must conform to stated requirements (either on this project page or in comment banner in source code files themselves).
* Executed programs must display in a manner similar to provided output
* output formatted, where applicable, must match that of project requirements
* Processing must be correct based on input given and output requested
* Output, if applicable, must be correct based on values input
* Code must be nicely and consistently indented (you may use the **indent** tool)
* Code must be commented
* Any "to be implemented" comments **MUST** be removed
* these "to be implemented" comments, if still present at evaluation time, will result in points being deducted.
* Sufficient comments explaining the point of provided logic **MUST** be present
* Track/version the source code in a repository
* Submit a copy of your source code to me using the **submit** tool (**make submit** will do this) by the deadline.