You are on page 1of 11

Topic 1

Basic Data Structures


Abstract Data Types (ADT) List ADT Binary Tree ADT Stack ADT Queue ADT Priority Queue ADT

I. Abstract Data Types


An Abstract Data Type (ADT) is a data structure declaration with operations of creation, destruction, manipulation, and interrogation of instances of ADT. ADT should maintain the encapsulation principle meaning that data should be accessible only through a small set of well defined operations.

ADT
Public Part with Specifications Private Part & Implementation Details

The specifications of an ADT describe logical relationships among the public parts, which are usually operations and constants. A significant advantage of designing with ATDs is that the client can develop a logically correct algorithm knowing only the ADT specifications, without committing to a specific implementation for the ADT. Specifications include both preconditions and postconditions for each operation. The preconditions are statements that are assumed to be true when the operation is called. If the operation has parameters it is important that the preconditions be stated in terms of these parameter names. It is the client responsibility to meet the preconditions before calling any operation. The postconditions are statements that the client may assume to be true when the operation returns. Again, if the operation has parameters it is important that the postconditions be stated in terms of these parameter names. Constuctors (create a new object and return a reference to it) Access functions or Accessors (return information about an object, but do not modify it)

ADT operations:

Modification procedures or modifiers (modify an object, but do not return information) Since access functions do not modify the state of an object, it is normally unnecessary to have postconditions for them. On the other hand constructors and modifiers need to have postconditions. We will formulate pre- and postconditions in terms of public constants and access functions. For that the set of access functions should be sufficient to express pre- and post- conditions for all operations. A set of such access functions can be considered as a generalized value of an object. We will adopt a minimalist view, by including only the necessary operations in an ADT itself; these are the operations that need to know how the objects are implemented. All other manipulating operations by an ADT could be placed outside of the ADT and implemented based on the operations declared within the ADT. Thus, an ADT should not be library of procedures that might be convenient; such a library might be supplied as an additional class, if desired. An ADT is recursive if any of its access functions returns an object of the same class as the ADT. In other words, some part of the object (as returned by the access function) is of the same type as the object itself. In such cases the ADT usually also has a constructor that has a parameter of the same class as the ADT. Such an ADT should have a non recursive constructor as well. List and binary tree are recursive data structures most commonly used.

Armen Kostanyan, 2011

II. List ADT


A list is a set of elements, that is empty or else satisfies the following: There is a distinguished element called the first. The set of remaining elements again is a list and called the rest.

Public Part with Specifications


(1) IntList nil\/ //constant\\\\\\\\\\\\\\\\\\\\\\\ Constant denoting the empty list. (2) int first(IntList aList) Precondition: aList<>nil. (3) IntList rest(IntList aList) Precondition: aList<>nil. (4) IntList cons(int element, IntList aList) //constructor Precondition: none. Postconditions: If x=cons(element, aList), then a) x refers to a newly created object; b) xnil; c) first(x)=element; d) rest(x)=aList. //accessor //accessor

Client code examples


1. Length computation public static int length(IntList aList) //returns length of aList { int result; if(aList==IntList.nil) result=0; else { IntList restElements=IntList.rest(aList); result=1+length(restElements); } return result; } 2. Addition at the end //returns the result of addition element at the end of aList public static IntList append(IntList aList, int element) { IntList result; if(aList==IntList.nil) result=IntList.cons(element, IntList.nil); else { int firstElement=IntList.first(aList); IntList restElements=IntList.rest(aList); result=IntList.cons( firstElement, Armen Kostanyan, 2011 3

append(restElements, element) ); } return result; } 3.Reversion //returns reversion of the list public static IntList reverse(IntList aList) { IntList result; if(aList==IntList.nil) result=IntList.nil; else { int firstElement=IntList.first(aList); IntList restElements=IntList.rest(aList); result=append( reverse(restElements), firstElement ); } return result; }

Implementation
PRIVATE PART: L=[a, b , c] int IntList first_; rest_:
first_ rest_

first_ public class IntList{ private int first_; private IntList rest_; /*1*/ public static final IntList nil=null; /*2*/ public static int first(IntList aList) { return aList.first_;} /*3*/ public static intList rest(IntList aList) { return aList.rest_; } /*4*/ public static IntList cons(int element, IntList aList) { IntList result=new IntList(); result.first_=element; result.rest_=aList; return result; } } Armen Kostanyan, 2011

rest_

first_

rest_

null

III. Binary Tree ADT


A binary tree T is a set of elements, called nodes, that is empty or else satisfies the following: - There is a distinguished node r called the root. - The remaining nodes are divided into two disjoint subsets, L and R, each of which again is a binary tree. L is called the left sub tree of T and R is called the right sub tree of T.

Public Part with Specifications


(1) IntTree nil\ //constant\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Constant denoting the empty tree. (2) int root(IntTree aTree) \ Precondition: aTree<>nil. (3) IntTree leftSubtree(IntTree aTree) \ Precondition: aTree<>nil. (4) IntTree rightSubtree(IntTree aTree) \//accessor\\\\\\\\\\\\\\\\\\\\\\ Precondition: aTree<>nil. (5) IntTree buildTree(int element, IntTree firstTree, IntTree secondTree) //constructor Precondition: none. Postcondition: If x=buildTree(element, firstTree, secondTree), then a) x refers to a newly created object; b) xnil; c) root(x)=element; d) leftSubtree(x)=firstTree; e) rightSubtree(x)=secondTree. //accessor\\\\\\\\\\\\\\\\\\\\\\ //accessor\\\\\\\\\\\\\\\\\\\\\\

Client code examples


1. Size calculation public static int size(IntTree aTree) //returns number of nodes in aTree { int result; if(aTree==IntTree.nil) result=0; else { IntTree firstTree=IntTree.leftSubtree(aTree); IntTree secondTree=IntTree.rightSubtree(aTree); result=1+size(firstTree)+size(secondTree); } return result; }

2. Height computation
public static int height(IntTree aTree) //returns the height of { int result; if(aTree==IntTree.nil) result=-1; aTree

Armen Kostanyan, 2011

else { int h1=height(IntTree.leftSubtree(aTree)); int h2=height(IntTree.rightSubtree(aTree)); result=1 + ((h1>=h2)? h1: h2); } return result; }

Implementation
PRIVATE PARTS:

int IntTree IntTree

root_; left_; right_;

left_

root_

right_

left_ nil

root_

right_ nil

left_ nil

root_

right_ nil

class IntTree{ private int private IntTree private IntTree

root_; left_; right_;

/*1*/ public static final IntTree nil=null; /*2*/ public static int root(IntTree aTree) { return aTree.root_; } /*3*/ public static IntTree leftSubtree(IntTree aTree) { return aTree.left_; } /*4*/ public static 6

Armen Kostanyan, 2011

IntTree rightSubtree(IntTree aTree) { return aTree.right_; } /*5*/ public static IntTree buildTree(int element, IntTree firstTree, IntTree secondTree) { IntTree result=new IntTree(); result.root_=element; result.left_=firstTree; result.right_=secondTree; return result; } }

Armen Kostanyan, 2011

III. Stack ADT


A stack is a linear structure in which insertions and deletions are always made at one end, called the top. This updating policy is called LIFO (Last In, First Out). The top item in a stack is the one most recently inserted, and only this element can be inspected.

Public Part with Specifications


(1) boolean isEmpty(Stack s)\`````//accessor\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Precondition: none. (2) int top(Stack s) \ //accessor\\\\\\\\\\\\\\\\\\\\\\

Precondition: isEmpty(s)=false. (3) Stack create() \ //constructor\\\\\\\\\\\\\\\\\\\\\\

Precondition: none. Postconditions: If s=create(), then a) s refers to a newly created object; b) isEmpty(s)=true. (4) void push(Stack s, int e) \````````//modifier\\\\\\\\\\\\\\\\\\\\\\ Precondition: none. Postconditions: a) top(s)=e; b) isEmpty(s)=false; (5) void pop(Stack s) //modifier

Precondition: isEmpty(s)=false. Postconditions: See Explanation below.

Explanation: Following create, any legal sequence of push and pop operations yields the same stack state as a certain sequence consisting only of push operations. To obtain this sequence repeatedly find any pop that is immediately preceded by a push and delete this pair of operations from the sequence.

Armen Kostanyan, 2011

IV. Queue ADT


A queue is a linear structure in which insertions are done at one end, called the rear, and all deletions are done at the other end, called the front. Only the front element can be inspected. This updating policy is called FIFO (First In, First Out).

Public Part with Specifications


(1) boolean isEmpty(Queue q)\ Precondition: none. (2) int front(Queue q) \ //accessor\\\\\\\\\\\\\\\\\\\\\\ //accessor\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Precondition: isEmpty(q)=false. (3) Queue create() \ Precondition: none. Postconditions: If q=create(), then a) q refers to a newly created object; b) isEmpty(q)=true. (4) void enqueue(Queue q, int e) \//modifier\\\\\\\\\\\\\\\\\\\\\\ Precondition: none. Postconditions: Let /q/ denotes the state of q before the operation. a) If isEmpty(/q/)=true, then front(q)=e, b) If isEmpty(/q/)=false, then front(q)=front(/q/). c) isEmpty(q)=false; (5) void dequeue(Queue q) //modifier //constructor\\\\\\\\\\\\\\\\\\\\\\

Precondition: isEmpty(q)=false. Postconditions: See Explanation below.

Explanation: Following create, any legal sequence of enqueue and dequeue operations yields the same queue state as a certain sequence consisting only of enqueue operations. To obtain this sequence repeatedly find the first dequeue and the first enqueue and delete this pair of operations from the sequence.

Armen Kostanyan, 2011

V. Priority Queue ADT


A priority queue is a structure with some aspects of a FIFO queue, but in which element order is related to each elements priority, rather than its chronological arrival time. Element priority (also called key) is a parameter supplied to the insert operation, not some innate property known to the ADT. We will assume it is type float for definiteness. We will also assume that elements are of type int.

Public Part with Specifications


Elementary Priority Queue.
(1) boolean isEmpty(PriorityQ pq)\ Precondition: none. (2) int getMin(PriorityQ pq) \ Precondition: isEmpty(pq)=false. (3) PriorityQ create() \ Precondition: none. Postconditions: If pq=create(), then a) pq refers to a newly created object; b) isEmpty(pq)=true. (4) void insert(PriorityQ pq, int id, float w) \//modifier\\\\\\\\\\\\\\\\\\\\\\ //constructor\\\\\\\\\\\\\\\\\\\\\\ //accessor\\\\\\\\\\\\\\\\\\\\\\ //accessor\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Precondition: If full version with getPriority and decreaseKey is implemented, then id must not already be in pq. Postconditions: The identifier of the element to be inserted is id and the priority is w. a) isEmpty(pq)=false; b) If getPriority is implemented, then getPriority(pq, id)=w; c) See Explanation below for value of getMin(pq). (5) void deleteMin(PriorityQ pq) Precondition: isEmpty(q)=false. Postconditions: a) If the number of deleteMins is less than the number of inserts since create(pq), then isEmpty(pq)=false, else it is true. b) See Explanation below for value of getMin(pq). Explanation: Think of /pq/ (the state of pq before the operation) abstractly as a sequence of pairs (id1, w1), ,(idk, wk), in nondecreasing order of the values of wi, which represent the priorities of the elements idi. Then insert(pq, id, w) effectively inserts(id, w) into this sequence in order, extending pq to k+1 elements in all. Also deleteMin(pq) effectively deletes the first element of the sequence /pq/, leaving pq with k-1 elements. Finally, getMin(pq) returns id1. //modifier

Armen Kostanyan, 2011

10

Full Priority Queue.


Only unique ids use. Besides the operations (1)-(5), the following additional operations are presumed. (6) float getPriority(PriorityQ pq, int id) Precondition: id is in pq. (7) void decreaseKey(PriorityQ pq, int id, float w) //modifier Precondition: id is in pq and w<getPriority(pq, id). Postconditions: a) isEmpty(pq) remains false; b) getPriority(id)=w; c) See Explanation below for value of getMin(pq). Explanation: As in the previous explanation, think of /pq/ abstractly as a sequence of pairs (id1, w1), ,(idk, wk), ordered by the values of wi. Also, all ids are unique. Then decreaseKey(pq, id, w) requires that id=idi for some 1ik, and effectively removes (idi, wi) from the sequence /pq/, then inserts (idi, w) into the sequence in order by w. The final sequence still has k elements. As before getMin(pq) returns id1. //accessor

Armen Kostanyan, 2011

11

You might also like