Basic Data Structures: Topic 1
Basic Data Structures: Topic 1
Basic Data Structures: Topic 1
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.
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
2. Height computation
public static int height(IntTree aTree) //returns the height of { int result; if(aTree==IntTree.nil) result=-1; aTree
else { int h1=height(IntTree.leftSubtree(aTree)); int h2=height(IntTree.rightSubtree(aTree)); result=1 + ((h1>=h2)? h1: h2); } return result; }
Implementation
PRIVATE PARTS:
left_
root_
right_
left_ nil
root_
right_ nil
left_ nil
root_
right_ nil
/*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
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; } }
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
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.
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\\\\\\\\\\\\\\\\\\\\\\
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.
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
10
11