affiliate marketing
Showing posts with label data structure in tree. Show all posts
Showing posts with label data structure in tree. Show all posts

Monday, 12 December 2011

6. Layered Software


Recall the picture that I showed you earlier:
It illustrates an important, general idea: the idea of layered software. In this picture there are two layers: the application layer and the implementation layer. The critical point - the property that makes these truly separate layers - is that the functionality of the upper layer, and the code that implements that functionality, are completely independent of the code of the lower layer. Furthermore the functionality of the lower layer is completely described in the specification.
We have already discussed how this arrangement permits very rapid, bug-free changes to the code implementing an abstract data type. But this is not the only advantage.
Reusability

5. Specification (Sections 3.3 & 3.5)


5. Specification (Sections 3.3 & 3.5)
Handout Stack Specification.
Handout: Stack Specification
Let us now look in detail at how we specify an abstract datatype. We will use `stack' as an example.
The data structure stack is based on the everyday notion of a stack, such as a stack of books, or a stack of plates. The defining property of a stack is that you can only access the top element of the stack, all the other elements are underneath the top one and can't be accessed except by removing all the elements above them one at a time.
The notion of a stack is extremely useful in computer science, it has many applications, and is so widely used that microprocessors often are stack-based or at least provide hardware implementations of the basic stack operations.
We will briefly consider some of the applications later. First, let us see how we can define, or specify, the abstract concept of a stack. The main thing to notice here is how we specify everything needed in order to use stacks without any mention of how stacks will be implemented.
5.1. Pre & Post Conditions
Preconditions:

4. Abstract Data Type


4.1. Alternative Implementations Of Fractions
Returning to our example of the fraction data type, how might we actually implement this datatype in C?
Implementation 1
typedef struct { int numerator,denominator; } fraction;

main()
{
  fraction f;
  f.numerator   = 1;
  f.denominator = 2;
  ...
}
Implementation 2
#define numerator   0
#define denominator 1
typedef int fraction[2];

main()
{
  fraction f;
  f[numerator]   = 1;
  f[denominator] = 2;
  ...
}
These are just 2 of many different possibilities. Obviously, these differences are in some sense extremely trivial - they do not affect the domain of values or meaning of the operations of fractions.

The List ADT


The List ADT

The List ADT is one of the most basic data structures in computer science. It is the basic foundation for more complex data types (such as Trees, Stacks, Queues, etc). Even an entire programming language, Lisp, has been built around the list as the only data structure. Lisp stands for "list processing" and is a commonly used language in Artificial Intelligence.
The List ADT is known as a recursive ADT. An ADT is recursive if it has access methods that return the same class as the ADT itself. In this case, the List ADT will return a List with it's access function "rest," as you will see shortly.
An interesting fact about the List ADT is that is has no manipulator (mutator) methods available. Remember that there are 4 types of operations on an ADT - constructors, interrogators (accessors), manipulators (mutators), and destructors. We can take advantage of the recursive definition and simply create access methods to make the List ADT surprisingly simple.
Read on ...

3. Common Structures


3. Common Structures (Section 3.1)
Let us stick with structural definitions for the moment, and briefly survey the main kinds of data types, from a structural point of view.
3.1. Atomic Data Types
First of all, there are atomic data types. These are data types that are defined without imposing any structure on their values. Boolean, our first example, is an atomic type. So are characters, as these are typically defined by enumerating all the possible values that exist on a given computer.
3.2. Structured Data Types
The opposite of atomic is structured. A structured data type has a definition that imposes structure upon its values. As we saw above, fractions normally are a structured data type.
In many structured data types, there is an internal structural relationship, or organization, that holds between the components. For example, if we think of an array as a structured type, with each position in the array being a component, then there is a structural relationship of `followed by': we say that component N is followed by component N+1.

2. Basic Definitions


2.1. Data Types and Structured Data Types
Let me begin the course by giving definitions for the terms data type and structured data type.
A data type consists of
  • a domain (= a set of values)
  • a set of operations.
Example 1: boolean or logical data type provided by most programming languages.
  • two values: true, false.
  • many operations, including: AND, OR, NOT, etc.
Example 2: As a second example, consider the datatype fraction. How can we specify the domain and operations that define fractions? It seems straightforward to name the operations; fractions are numbers so all the normal arithmetic operations apply, such as addition, multiplication, comparison. In addition there might be some fraction-specific operations such as normalizing a fraction by removing common terms from its numerator and denominator - for example, if we normalized 6/9 we'd get 2/3.
But how do we specify the domain for fractions, i.e. the set of possible values for a fraction?

Breadth First Traversal



These three are certainly not the only possible traversal orders. Another very natural traversal order is ``level by level'' - the root is processed first, all its children are processed next, then all of their children, etc. down to the bottom level. This is called breadth first traversal. In the above example, it would process nodes in the order: A-B-C-D-E-F. It is not difficult to write a breadth-first traversal, but is not quite as simple as the traversal orders just described.

2. Tree Traversal


What I've just called ``scanning through'' a tree is actually called traversing a tree.
General Definition: to traverse a data structure is to process, however you like, every node in the data structure exactly once.
Note: You may ``pass through'' a node as many times as you like but you must only process the node once.
E.g. we can talk about ``traversing a list'', which means going through the list and processing every node once. We had a special name for this: map.
For a specific data structure, we talk about the different orders in which it might be traversed. For a list there are two common traversal orders: first-to-last (the most common) and last-to-first.
The general recursive pattern for traversing a (non-empty) binary tree is this: At node N you must do these three things:

Structural Definition of Binary Trees


A binary tree is either empty or it has 3 parts:
  • a value
  • a left subtree
  • a right subtree
Whenever a data structure has a recursive definition like this, most of the `properties' of the data structure can be computed in a recursive manner which exactly mirrors the definition.
For example, here is a function to compute the number of nodes in a binary tree:
        int size(binary_tree *t)
        {
          return is_empty(t) ? 0 : 1 + size(t->left) + size(t->right);
        }
With lists we had an alternative to recursion - we could scan through a list as easily with normal loops (while, do ... while) as with recursion. This is not true for trees. It is possible to scan through a tree non-recursively, but it is not nearly as easy as scanning recursively.

Drawing Trees



Here is how we draw a tree:
The root is at the top; below it are its children. An arc connects a node to each of its children: we sometimes draw arrowheads on the arc, but they are optional because the direction parent->child is always top->bottom.
Then we continue in the same manner, the children of each node are drawn below the node.
For example, here is a binary tree:



       
In general, each child of a node is the root of a tree ``within the big tree''. For example, B is the root of a little tree (B,D,E), so is C. These inner trees are calledsubtrees. The subtrees of a node are the trees whose roots are the children of the node. e.g. the subtrees of A are the subtrees whose roots are B and C. In a binary tree we refer to the left subtree and the right subtree.

1. Introduction To Trees


We will now turn to tree structures, which is the subject of most of the rest of the course.
Definition of Trees
Trees are as common and important as lists. And like lists there are many variations - binary search trees, balanced trees, and heaps are the main ones we will look at.
Recall that a list is a collection of components in which
1.     each component (except one, the first) has exactly 1 predecessor.
2.     each component (except one, the last) has exactly 1 successor.
a tree is very similar: it has property (1) but (2) is slightly relaxed:
(2') each component has some number of successors.
If there is no limit on the number of successors that a node can have, the tree is called a general tree.