Java

324
1. Overview of Data Structures © 2008, University of Colombo School of Computing 1 Structures

description

This is consist of Graphs Linked lists and such a kind of things related to java field.

Transcript of Java

  • 1. Overview of Data Structures

    2008, University of Colombo School of Computing 1

    Structures

  • 1.1. Introduction to data structures A data structure is an arrangement of data in a computer's

    memory or even disk storage. An example of several common data structures are

    Arrays Linked lists Queues Stacks Binary trees

    2008, University of Colombo School of Computing 2

    Binary trees Hash tables

    Algorithms, on the other hand, are used to manipulate the data contained in these data structures as in searching and sorting.

    Many algorithms apply directly to a specific data structures. When working with certain data structures you need to know how to insert new data, search for a specified item, and deleting a specific item.

  • 1.1. Characteristics of Data StructuresData Structure Advantages DisadvantagesArray Quick inserts

    Fast access if index known

    Slow searchSlow deletesFixed size

    Ordered Array Faster search than unsorted array

    Slow insertsSlow deletes

    2008, University of Colombo School of Computing 3

    unsorted array Slow deletesFixed size

    Stack Last-in, first-out acces Slow access to other itemsLinked List Q

    ui

    Slow search Lin

    Quick insertsQuick deletes

    Queue First-in, first-out access Slow access to other items

  • 1.1. Characteristics of Data Structures cont

    Data Structure Advantages Disadvantages

    Binary Tree Quick searchQuick insertsQuick deletes(If the tree remains balanced)

    Deletion algorithm is complex

    Red-Black Tree Quick searchQuick inserts

    Complex to implement

    2008, University of Colombo School of Computing 4

    Quick insertsQuick deletes(Tree always remains balanced)

    2-3-4 Tree Quick searchQuick insertsQuick deletes(Tree always remains balanced)(Similar trees good for disk storage)

    Complex to implement

  • 1.1. Characteristics of Data Structures cont

    Data Structure Advantages Disadvantages

    Hash Table Very fast access if key is knownQuick inserts

    Slow deletesAccess slow if key is not knownInefficient memory usage

    Heap Quick insertsQuick deletes

    Slow access to other items

    2008, University of Colombo School of Computing 5

    Quick deletesAccess to largest item

    Graph Best models real-world situations

    Some algorithms are slow and very complex

    NOTE: The data structures shown above (with the exception of the array) can be thought of as Abstract Data Types (ADTs).

  • 1.1. Abstract Data Types An Abstract Data Type (ADT) is a way of looking at a

    data structure: focusing on what it does. A stack or a queue is an example of an ADT. It is important to understand that both stacks and queues

    can be implemented using an array. It is also possible to implement stacks and queues using

    2008, University of Colombo School of Computing 6

    It is also possible to implement stacks and queues usinga linked list.

    This demonstrates the "abstract" nature of stacks andqueues: how they can be considered separately fromtheir implementation.

    To best describe the term Abstract Data Type, it is bestto break the term down into "data type" and then"abstract".

  • 1.1. Abstract Data Types cont Data type

    Primitive data types refer to two things: a data itemwith certain characteristics and the permissibleoperations on that data.

    Eg: A short in Java, can contain any whole number valuefrom -32,768 to 32,767.

    2008, University of Colombo School of Computing 7

    from -32,768 to 32,767. It can also be used with the operators +, -, *, and /.

    The data type's permissible operations are aninseparable part of its identity; understanding the typemeans understanding what operations can beperformed on it.

  • 1.1. Abstract Data Types cont

    Data type cont.. In Java, any class represents a data type, in the

    sense that a class is made up of data (fields) and permissible operations on that data (methods).

    By extension, when a data storage structure like a

    2008, University of Colombo School of Computing 8

    By extension, when a data storage structure like a stack or queue is represented by a class, it too can be referred to as a data type.

    A stack is different in many ways from an int, but they are both defined as a certain arrangement of data and a set of operations on that data

  • 1.1. Abstract Data Types cont

    Abstract In Java, an Abstract Data Type is a class considered

    without regard to its implementation. It can be thought of as a "description" of the data in the class and a list of operations that can be carried out on that data and

    2008, University of Colombo School of Computing 9

    of operations that can be carried out on that data and instructions on how to use these operations.

    What is excluded though, is the details of how the methods carry out their tasks.

    End user (or class user), should be told what methods to call, how to call them, and the results that should be expected, but not how they work.

  • 1.1. Abstract Data Types cont

    Abstract cont Can further extend the meaning of the ADT when

    applying it to data structures such as a stack and queue. In Java, as with any class, it means the data and the operations that can be performed on it. In this

    2008, University of Colombo School of Computing 10

    and the operations that can be performed on it. In this context, although, even the fundamentals of how the data is stored should be invisible to the user.

    Users not only should not know how the methods work, they should also not know what structures are being used to store the data.

  • 1.1. Abstract Data Types cont

    The Interface The ADT specification is often called an

    interface. It's what the user of the class actually sees.

    2008, University of Colombo School of Computing 11

    It's what the user of the class actually sees. In Java, this would often be the public

    methods. Consider for example, the stack class - the public methods push() and pop() and similar methods from the interface would be published to the end user.

  • 1.2. Practical data storage structures

    Many of the structures and techniques considered here are about how to handle real-world data storage.

    By real-world data, we mean data that describes physical entities external to the computer.

    2008, University of Colombo School of Computing 12

    physical entities external to the computer.

    Examples: A personnel record describes a actual human being, an inventory record describes an existing car part or grocery item, and a financial transaction record describes, say, an actual check written to pay the electric bill.

  • 1.2. Practical data storage structures Cont

    A non-computer example of real-world data storage is a stack of index cards.

    These cards can be used for a variety of purposes. If each card holds a person's

    2008, University of Colombo School of Computing 13

    purposes. If each card holds a person's name, address, and phone number, the result is an address book. If each card holds the name location, and value of a household possession, the result is a home inventory.

  • 1.3. Programmers Tools for data storage

    Not all data storage structures are used to store real-world data.

    Typically, real-world data is accessed more or less directly by a program's user.

    Some data storage structures, however, are not

    2008, University of Colombo School of Computing 14

    Some data storage structures, however, are not meant to be accessed by the user, but by the program itself.

    A programmer uses such structures as tools to facilitate some other operation. Eg: Stacks, queues, and priority queues are often used in this way.

  • 1.3. Real- world Modeling for data storage

    Some data structures directly model a real-world situation.

    The most important data structure of this type is the graph.

    You can use graphs to represent airline routes between cities, connections in an electrical circuit,

    2008, University of Colombo School of Computing 15

    between cities, connections in an electrical circuit, or tasks in a project.

    Other data structures, such as stacks, queues, and priority queues, may also be used in simulations.

    A queue, for example, can model customers waiting in line at a bank.

  • 2. Stacks, Queues and Hashing

    2008, University of Colombo School of Computing 1

    Hashing

  • 2.1. Stacks A stack is a data structure in which all the

    access is restricted to the most recently inserted items.

    If we remove this item, then we can

    2008, University of Colombo School of Computing 2

    If we remove this item, then we can access the next-to-last item inserted, and etc.

    A stack is also a handy aid for algorithms applied to certain complex data structures.

  • Stack Model contd

    Input to a stack is by push

    Access is by top

    2008, University of Colombo School of Computing 3

    Access is by top

    Deletion is by pop

  • Example

    Stack contains 3, 4 Push item 9

    Now the stack contains 3,4,9

    2008, University of Colombo School of Computing 4

    Now the stack contains 3,4,9 If we pop now, we get 9 If we pop again, we get 4

  • Stack Properties The last item added to the stack is placed

    on the top and is easily accessible.

    Thus the stack is appropriate if we expect

    2008, University of Colombo School of Computing 5

    Thus the stack is appropriate if we expect to access on the top item, all other items are inaccessible.

  • 2.1.3. Important stack applications

    Compiler Design Mathematical Expression Evaluation Balanced Spell Checker Simple Calculator

    2008, University of Colombo School of Computing 6

    Simple Calculator

  • The stack operations clear() Clear the stack isEmpty() Check to see if the stack is empty push(el) Put the element el on top of the stack. pop() Take the topmost element from stack. topEl() Return the topmost element in the stack

    2008, University of Colombo School of Computing 7

    topEl() Return the topmost element in the stack without removing it.

  • 2.1.1. Implementation Of Stacks

    There are two basic ways to arrange for constant time operations. The first is to store the items contiguously in

    an array.

    2008, University of Colombo School of Computing 8

    an array. And the second is to store items non-

    contiguously in a linked list

  • Array Implementation To push an item into an empty stack, we

    insert it at array location 0. ( since all java arrays start at 0)

    To push the next item into the location 0

    2008, University of Colombo School of Computing 9

    To push the next item into the location 0 over the location to make room for new item.

  • This is easily done by defining an auxiliary integer variable known as stack pointer, which stores the array index currently being used as the top of the stack.

    A stack can be implemented with an array and

    Array Implementation contd

    2008, University of Colombo School of Computing 10

    A stack can be implemented with an array and an integer.

    The integer TOS (top of stack) provides the array index of the top element of the stack, when TOS is 1 , the stack is empty.

  • Stacks specifies two data fields such as The array (which is expanded as needed

    stores the items in the stack)

    TopOfStack (TOS) ( gives the index of

    2008, University of Colombo School of Computing 11

    TopOfStack (TOS) ( gives the index of the current top of the stack, if stack is empty, this index is 1.)

  • Algorithms For Pushing & Popping

    PUSH If stack is not full then Add 1 to the stack pointer.

    2008, University of Colombo School of Computing 12

    Add 1 to the stack pointer. Store item at stack pointer location.

  • Algorithms For Pushing & Popping contdPOP If stack is not empty then Read item at stack pointer location.

    2008, University of Colombo School of Computing 13

    Read item at stack pointer location. Subtract 1 from the stack pointer.

  • How to stack routines work:empty stack;push(a),push(b);pop

    Push (a)

    TOS (0)a

    Stack is empty TOS(-1)

    2008, University of Colombo School of Computing 14

    Push (a)

    Push (b)

    b

    a

    Stack is empty TOS(-1)

    TOS (1)

    pop

    TOS (0)a

  • Java implementation Zero parameter constructor for array

    based stack

    public stackar( )

    2008, University of Colombo School of Computing 15

    public stackar( )/* construct the stack{thearray= new object[default-capacity];Tos = -1;}

  • Isempty : returns true if stack is empty, false otherwise

    Isempty( )

    public Boolean Isempty( )

    2008, University of Colombo School of Computing 16

    public Boolean Isempty( ){return tos= = -1}

  • Isfull( ) : returns true if stack is full , false otherwise

    Isfull( )

    public Boolean isfull( ){

    2008, University of Colombo School of Computing 17

    {return tos= = stacksize-1; (default capacity)}

  • push method for array based stack

    Insert a new item into the stack

    public void push (object x){

    2008, University of Colombo School of Computing 18

    {If isfull( ) throw new stackexception ( stack is full)theArray[++tos]=x;}

  • Pop method for array based stack

    Remove the most recently inserted item from the stack

    Exception underflow if the stack is empty

    public void pop( ) throws underflow

    2008, University of Colombo School of Computing 19

    public void pop( ) throws underflow{If (isempty( )) throw new underflow (stackpop);tos - - ;}

  • Top method for array based stack

    Return the most recently inserted item from the stack

    Exception underflow if the stack is empty

    2008, University of Colombo School of Computing 20

    emptypublic object top( ) throws underflow{if (isEmpty( ))throw new underflow( stacktop)return theArray[tos];}

  • Top and pop method for array based stack

    Return and remove the most recently inserted item from the stack

    Exception underflow if the stack is empty

    public object topandpop( ) throws underflow

    2008, University of Colombo School of Computing 21

    public object topandpop( ) throws underflow{if isEmpty( ) )throw new underflow (stack topandpop);return theArray[tos - - ];}

  • Java Code for a Stackimport java.io.*; // for I/O

    class StackX{private int maxSize; // size of stack arrayprivate double[ ] stackArray;private int top; // top of stack//-------------------------------------------------------------

    2008, University of Colombo School of Computing 22

    //-------------------------------------------------------------

    public StackX(int s) // constructor{maxSize = s; // set array sizestackArray = new double[maxSize]; // create arraytop = -1; // no items yet}//-------------------------------------------------------------

  • Java Code for a Stackpublic void push(double j) // put item on top of stack {stackArray[++top] = j; // increment top, insert item}//-------------------------------------------------------------

    public double pop() // take item from top of stack {return stackArray[top--]; // access item, decrement top}//-------------------------------------------------------------

    2008, University of Colombo School of Computing 23

    public double peek() // peek at top of stack {return stackArray[top];}//--------------------------------------------------------------

    public boolean isEmpty() // true if stack is empty{return (top == -1);}

  • Java Code for a Stack

    //--------------------------------------------------------------

    public boolean isFull() // true if stack is full{return (top == maxSize-1);}

    2008, University of Colombo School of Computing 24

    }//--------------------------------------------------------------

    } // end class StackX

  • Java Code for a Stackclass StackApp {public static void main(String[] args){StackX theStack = new StackX(10); // make new stacktheStack.push(20); // push items onto stacktheStack.push(40);theStack.push(60);theStack.push(80);while( !theStack.isEmpty() ) // until it's empty,

    2008, University of Colombo School of Computing 25

    while( !theStack.isEmpty() ) // until it's empty,{ // delete item fromstack double value = theStack.pop();System.out.print(value); // display itSystem.out.print(" ");} // end whileSystem.out.println("");} // end main()} // end class StackApp

  • Java Code for a Stack The main() method in the StackApp class

    creates a stack that can hold 10 items, pushes 4 items onto the stack, and then displays all the items by popping them off the stack until it's empty.

    2008, University of Colombo School of Computing 26

    empty. Here's the output:

    80 60 40 20

  • StackX Class Methods The constructor creates a new stack of a size

    specified in its argument. The fields of the stack comprise a variable to hold

    its maximum size (the size of the array), the array

    2008, University of Colombo School of Computing 27

    its maximum size (the size of the array), the array itself, and a variable top, which stores the index of the item on the top of the stack.(Note that we need to specify a stack size only because the stack is implemented using an array. If it had been implemented using a linked list, for example, the size specification would be unnecessary.)

  • StackX Class Methods The push() method increments top so it points to the space just

    above the previous top, and stores a data item there. Notice that topis incremented before the item is inserted.

    The pop() method returns the value at top and then decrements top. This effectively removes the item from the stack; it's inaccessible, although the value remains in the array (until another

    2008, University of Colombo School of Computing 28

    inaccessible, although the value remains in the array (until another item is pushed into the cell).

    The peek() method simply returns the value at top, without changing the stack.

    The isEmpty() and isFull() methods return true if the stack is empty or full, respectively. The top variable is at 1 if the stack is empty and maxSize-1 if the stack is full.

  • Error Handling There are different philosophies about how to

    handle stack errors. What happens if you try to push an item onto a stack that's already full, or pop an item from a stack that's empty?

    2008, University of Colombo School of Computing 29

    We've left the responsibility for handling such errors up to the class user. The user should always check to be sure the stack is not full before inserting an item:

  • Error Handling

    if( !theStack.isFull() )insert(item);

    elseSystem.out.print("Can't insert, stack is full");

    2008, University of Colombo School of Computing 30

    In the interest of simplicity, we've left this code out of the main() routine (and anyway, in this simple program, we know the stack isn't full because it has just been initialized). We do include the check for an empty stack when main() calls pop().

  • 2.1.2. Efficiency of Stacks Items can be both pushed and popped

    from the stack implemented in the StackX class in constant O(1) time.

    That is, the time is not dependent on how

    2008, University of Colombo School of Computing 31

    That is, the time is not dependent on how many items are in the stack, and is therefore very quick.

    No comparisons or moves are necessary.

  • 2.2. Queues A queue is a special kind of list

    Items are inserted at one end (the rear) and(enqueue)

    deleted at the other end (the front).(dequeue)

    2008, University of Colombo School of Computing 32

    (dequeue) Queues are also known as FIFO lists

    Front

    Back

    InsertedRemoved A B

  • 2.2. Queues There are various queues quietly doing their job in our

    computer's (or the network's) operating system. There's a printer queue where print jobs wait for the

    printer to be available. A queue also stores keystroke data as we type at the

    keyboard.

    2008, University of Colombo School of Computing 33

    A queue also stores keystroke data as we type at the keyboard.

    This way, if we are using a word processor but the computer is briefly doing something else when we hit a key, the keystroke won't be lost; it waits in the queue until the word processor has time to read it.

    Using a queue guarantees the keystrokes stay in order until they can be processed.

  • 2.2.1. The Queue operations clear() Clear the queue isEmpty() Check to see if the queue is empty enqueue(el) Put the element el on top of the queue. dequeue() Take the first element from queue. firstEl() Return the first element in the queue without

    2008, University of Colombo School of Computing 34

    firstEl() Return the first element in the queue without removing it.

  • 2.2.2. A Circular Queue When we insert a new item in the queue in the

    Workshop applet, the Front arrow moves upward, towardhigher numbers in the array.

    When we remove an item, Rear also moves upward. we may find the arrangement counter-intuitive, because

    the people in a line at the movies all move forward,

    2008, University of Colombo School of Computing 35

    we may find the arrangement counter-intuitive, becausethe people in a line at the movies all move forward,toward the front, when a person leaves the line.

    We could move all the items in a queue whenever wedeleted one, but that wouldn't be very efficient.

    Instead we keep all the items in the same place andmove the front and rear of the queue.

  • 2.2.2. A Circular Queue contd

    To avoid the problem of not being able to insert more items into the queue even when it's not full, the Front and Rear arrows wrap around to the beginning of

    2008, University of Colombo School of Computing 36

    arrows wrap around to the beginning of the array. The result is a circular queue

  • 2.2.3. Java Code for a Queue The queue.java program features a Queue class

    with insert(), remove(), peek(), isFull(), isEmpty(), and size() methods.

    The main() program creates a queue of five cells, inserts four items, removes three items,

    2008, University of Colombo School of Computing 37

    cells, inserts four items, removes three items, and inserts four more. The sixth insertion invokes the wraparound feature. All the items are then removed and displayed.

    The output looks like this:40 50 60 70 80

  • The Queue.java Programimport java.io.*; // for I/Oclass Queue {private int maxSize;private int[] queArray;private int front;private int rear;private int nItems;//-------------------------------------------------------------

    2008, University of Colombo School of Computing 38

    //-------------------------------------------------------------

    public Queue(int s) // constructor{maxSize = s;queArray = new int[maxSize];front = 0;rear = -1;nItems = 0;}//-------------------------------------------------------------

  • The Queue.java Programpublic void insert(int j) // put item at rear of queue{if(rear == maxSize-1) // deal with wraparoundrear = -1;queArray[++rear] = j; // increment rear andinsertnItems++; // one more item}

    2008, University of Colombo School of Computing 39

    }//--------------------------------------------------------------

    public int remove() // take item from front of queue{int temp = queArray[front++]; // get value and incr frontif(front == maxSize) // deal with wraparoundfront = 0;nItems--; // one less itemreturn temp;}//-------------------------------------------------------------

  • The Queue.java Programpublic int peekFront() { // peek at front of queuereturn queArray[front];}//-------------------------------------------------------------public boolean isEmpty() { // true if queue is emptyreturn (nItems==0);}//-------------------------------------------------------------

    2008, University of Colombo School of Computing 40

    //-------------------------------------------------------------public boolean isFull() { // true if queue is fullreturn (nItems==maxSize);}//-------------------------------------------------------------public int size() { // number of items in queuereturn nItems;}//-------------------------------------------------------------} // end class Queue

  • The Queue.java Programclass QueueApp {public static void main(String[] args) {Queue theQueue = new Queue(5); // queue holds 5 itemstheQueue.insert(10); // insert 4 itemstheQueue.insert(20);theQueue.insert(30);theQueue.insert(40);theQueue.remove(); // remove 3 items

    2008, University of Colombo School of Computing 41

    theQueue.remove(); // remove 3 itemstheQueue.remove(); // (10, 20, 30)theQueue.remove();theQueue.insert(50); // insert 4 more itemstheQueue.insert(60); // (wraps around)theQueue.insert(70);theQueue.insert(80);while( !theQueue.isEmpty() ) { // remove and display all itemsint n = theQueue.remove(); // (40, 50, 60, 70, 80)System.out.print(n);

  • The Queue.java ProgramSystem.out.print(" ");}System.out.println("");} // end main()} // end class QueueApp

    2008, University of Colombo School of Computing 42

  • 2.2.3.1. Efficiency of Queues As with a stack, items can be inserted and

    removed from a queue in O(1) time.

    2008, University of Colombo School of Computing 43

  • 2.2.3.2. Deques A deque is a double-ended queue. We can insert items at either end and delete them from

    either end. The methods might be called insertLeft() and

    insertRight(), and removeLeft() and removeRight(). If we restrict ourself to insertLeft() and removeLeft() (or

    their equivalents on the right), then the deque acts like a

    2008, University of Colombo School of Computing 44

    their equivalents on the right), then the deque acts like a stack.

    If we restrict ourself to insertLeft() and removeRight() (or the opposite pair), then it acts like a queue.

    A deque provides a more versatile data structure than either a stack or a queue, and is sometimes used in container class libraries to serve both purposes.

    However, it's not used as often as stacks and queues, so we won't explore it further here.

  • 2.2.4. Priority Queues A priority queue is a more specialized data structure than

    a stack or a queue. However, it's a useful tool in a surprising number of

    situations. Like an ordinary queue, a priority queue has a front and

    a rear, and items are removed from the front.

    2008, University of Colombo School of Computing 45

    Like an ordinary queue, a priority queue has a front and a rear, and items are removed from the front.

    However, in a priority queue, items are ordered by key value, so that the item with the lowest key (or in some implementations the highest key) is always at the front.

    Items are inserted in the proper position to maintain the order.

  • 2.2.4.1. Efficiency of Priority Queues

    In the priority-queue implementation we show here, insertion runs in O(N) time, while deletion takes O(1) time.

    2008, University of Colombo School of Computing 46

  • 2.3. Hash Functions Choosing a good hashing function, h(k), is

    essential for hash-table based searching. The key criterion is that there should be a

    minimum number of collisions.

    2008, University of Colombo School of Computing 47

    minimum number of collisions. Sophisticated hash functions may avoid

    collisions,computational cost in determining h(k)can be prohibitive.

    Less sophisticated methods may be faster.

  • 2.3. Hash Functions contd The number of hash functions that can be used

    to assign positions to n items in a table of mpositions (for n

  • 2.3. Hash Functions contd Mid-Square Function

    The key is squared and the middle or mid part of the result is used as the address. If the key is a string, it has to be pre-processed to

    2008, University of Colombo School of Computing 49

    is a string, it has to be pre-processed to produce a number.

    e.g. if the key is 3121 then 31212 = 9740641 and h(3121) = 406.

  • 2.3. Hash Functions contd Extraction

    Only a part of the key is used to compute the address. For the key 123-45-6789 this method might use the

    2008, University of Colombo School of Computing 50

    method might use thefirst four digits 1234last four digits 6789the first two combined with last two 1289 orsome other combination

  • 2.3. Hash Functions contd- Radix Transformation

    The key is transformed into another number base.e.g. If K is the decimal number 345, then

    2008, University of Colombo School of Computing 51

    e.g. If K is the decimal number 345, then its value in base 9 is 423. This value is then divided modulo TSize, and the resulting number is used as the address of the location to which K should be hashed.

  • 3. Linked Lists

    2008, University of Colombo School of Computing 1

  • 3. Introduction to Linked Lists An array is a very useful data structure provided in programming languages. It has at least two limitations:

    (1) changing the size of the array requires creating a new array and then copying all data from the array with the old size to the array with the new size

    (2) The data in the array are next to each other sequentially in memory, which means that inserting an item inside the array requires shifting some other data in this array.

    These limitations can be overcome by using linked structures.

    2008, University of Colombo School of Computing 2

    These limitations can be overcome by using linked structures. A linked structure is a collection of nodes storing data and links to other

    nodes. In this way, nodes can be located anywhere in memory, and passing from

    one node of the linked structure to another is accomplished by storing the reference(s) to other node(s) in the structure.

    Although linked structures can be implemented in a variety of ways, the most flexible implementation is by using a separate object for each node.

  • 3.1. Singly linked lists

    If a node contains a data field that is a reference toanother node, then many nodes can be used togetherusing only one variable to access the entire sequence ofnodes.

    Such a sequence of nodes is the most frequently usedimplementation of a linked list, which is a data structurecomposed of nodes, each node holding some

    2008, University of Colombo School of Computing 3

    implementation of a linked list, which is a data structurecomposed of nodes, each node holding someinformation and a reference to another node in the list.

    If a node has a link only to its successor in thissequence, the list is called a singly linked list.

    Note that only one variable p is used to access any nodein the list.

    The last node on the list can be recognized by the nullreference field.

  • 3.2. Doubly Linked Lists Each node has a reference or pointer back

    to the previous nodes

    2008, University of Colombo School of Computing 4

  • 3.2. Doubly Linked Lists If we wish to traverse a list both forwards and backwards efficiently,

    or if we wish, given a list element, to determine the preceding and following elements quickly, then the doubly-linked list comes in handy. A list element contains the data plus pointers to the next and previous list items as shown in the picture below.

    2008, University of Colombo School of Computing 5

  • 3.2. Doubly Linked List contdExample:The full example can be found in the directory:/home/331/tamj/examples/lists/doublyLinked

    class ListManager{

    private Node head;

    2008, University of Colombo School of Computing 6

    private Node head;private int length;private int currentDataValue = 10;private static final int MAX_DATA = 100;: : : :

    }

  • 3.2. More about Doubly Linked List

    A doubly linked list provides a natural implementation of the List ADT

    Nodes implement Position and store: element

    2008, University of Colombo School of Computing 7

    element link to the previous node link to the next node

    Special trailer and header nodes

  • 3.2. More about Doubly Linked List contd.

    2008, University of Colombo School of Computing 8

  • 3.2.1. Adding a new node at the end of a doubly linked list

    To add a node to a list, the node has to be created, its fields properly initialized, and then the node needs to be incorporated into the list.

    The process of Inserting a node at the end of a doubly linked list is performed in six steps:

    1. A new node is created, and then its three fields are initialize as exaples info,el and prev.

    2008, University of Colombo School of Computing 9

    2. the info field to the number el being inserted 3. the next field to null4. and the prev field to the value of tail so that this field points to the last

    node in the list. But now, the new node should become the last node; therefore,

    5. tail is set to reference the new node. But the new node is not yet accessible from its predecessor; to rectify this,

    6. the next field of the predecessor is set to reference the new node.

  • 3.2.1. Adding a new node at the end of a doubly linked list

    A special case concerns the last step. It is assumed in this step that the newly created node has a predecessor, so it accesses its prev field.

    It should be obvious that for an empty linked list, the new node is the only node in the list and it has no predecessor.

    2008, University of Colombo School of Computing 10

    predecessor. In this case, both head and tail refer to this node, and the

    sixth step is now setting head to refer to this node. Note that step foursetting the prev field to the value of

    tailis executed properly because for an initially empty list, tail is null. Thus, null becomes the value of the prev field of the new node.

  • 3.2.1. Doubly Linked List: Adding To The End

    public void addToEnd (){

    Node anotherNode = new Node (currentDataValue);Node temp;

    if (isEmpty() == true)head = anotherNode;

    else {temp = head;

    2008, University of Colombo School of Computing 11

    temp = head;while (temp.next != null){

    temp = temp.next;}temp.next = anotherNode;anotherNode.previous = temp;

    }currentDataValue += 10;length++;

    }

  • 3.2.2. Doubly Linked List: Adding Anywhere(1)

    public void addToPosition (int position){

    Node anotherNode = new Node (currentDataValue);Node temp;Node prior;Node after;int index;

    2008, University of Colombo School of Computing 12

    if ((position < 1) || (position > (length+1))){

    System.out.println("Position must be a value between 1-" +(length+1));

    }

  • 3.2.2. Doubly Linked List: Adding Anywhere(2)

    else{

    // List is emptyif (head == null){

    if (position == 1){

    currentDataValue += 10;

    2008, University of Colombo School of Computing 13

    currentDataValue += 10;length++;head = anotherNode;

    }elseSystem.out.println("List empty, unable to add node to " +"position " + position);}

  • 3.2.2. Doubly Linked List: Adding Anywhere(3)

    // List is not empty, inserting into first position.else if (position == 1){head.previous = anotherNode;anotherNode.next = head;head = anotherNode;currentDataValue += 10;

    2008, University of Colombo School of Computing 14

    currentDataValue += 10;length++;}

  • 3.2.2. Doubly Linked List: Adding Anywhere (4)

    // List is not empty inserting into a position other than the firstelse{

    prior = head;index = 1;// Traverse list until current is referring to the node in front// of the position that we wish to insert the new node into.while (index < (position-1))

    2008, University of Colombo School of Computing 15

    while (index < (position-1)){

    prior = prior.next;index++;

    }after = prior.next;

  • 3.2.2. Doubly Linked List: Adding Anywhere (5)

    // Set the references to the node before the node to be// inserted.prior.next = anotherNode;anotherNode.previous = prior;

    // Set the references to the node after the node to be// inserted.

    2008, University of Colombo School of Computing 16

    // inserted.if (after != null)

    after.previous = anotherNode;anotherNode.next = after;currentDataValue += 10;length++;}

    }}

  • 3.2.3. Deleting the last node from the doubly linked list

    Deleting the last node from the doubly linked list is straightforward because there is direct access from the last node to its predecessor, and no loop is needed to remove the last node.

    When deleting a node from the list, the temporary variable el is set to the value in the last node, then tail is set to its predecessor, and the last node is cut off from the list by setting the next field of the next to last node to null.

    2008, University of Colombo School of Computing 17

    In this way, the next to last node becomes the last node, and the formerly last node is abandoned.

    Although this node accesses the list, the node is inaccessible from the list; hence, it will be claimed by the garbage collector.

    The last step is returning the value stored in the removed node.

  • 3.2.3. Deleting the last node from the doubly linked list

    An attempt to delete a node from an empty list may result in a program crash.

    Therefore, the user has to check whether the list is not empty before attempting to delete the last

    2008, University of Colombo School of Computing 18

    is not empty before attempting to delete the last node.

    As with the singly linked list's deleteFromHead(), the caller should have an if statement

    if (!list.isEmpty())n = list.deleteFromTail();

    else do not remove;

  • 3.2.3. Deleting the last node from the doubly linked list

    The second special case is the deletion of the only nodefrom a single-node linked list. In this case, both head andtail are set to null.

    Because of the immediate accessibility of the last node,both addToTail () and deleteFromTail () execute inconstant time O(l).

    2008, University of Colombo School of Computing 19

    constant time O(l). Methods for operating at the beginning of the doubly

    linked list are easily obtained from the two methodsdiscussed by changing head to tail and vice versa,changing next to prev and vice versa, and exchangingthe order of parameters when executing new.

  • 3.2.3. Doubly Linked List: Deleting A Node(1)

    public void delete (int key){

    int indexToDelete;int indexTemp;Node previous;Node toBeDeleted;Node after;

    2008, University of Colombo School of Computing 20

    Node after;indexToDelete = search(key);// No match, nothing to delete.if (indexToDelete == -1){

    System.out.println("Cannot delete element with a data value of "

    + key + " because it was not found.");}

  • 3.2.3. Doubly Linked List: Deleting A Node(2)

    else{

    // Deleting first element.if (indexToDelete == 1){

    head = head.next;length--;

    }else{

    previous = null;toBeDeleted = head;indexTemp = 1;

    2008, University of Colombo School of Computing 21

    indexTemp = 1;while (indexTemp < indexToDelete){

    previous = toBeDeleted;toBeDeleted = toBeDeleted.next;indexTemp++;}previous.next = toBeDeleted.next;after = toBeDeleted.next;after.previous = previous;length--;: : :

  • 3.2.4. Pros Of Doubly Linked Lists

    Pros Traversing the list in reverse order is now

    possible. One can traverse a list without a trailing

    2008, University of Colombo School of Computing 22

    One can traverse a list without a trailingreference (or by scanning ahead)

    It is more efficient for lists that requirefrequent additions and deletions near the frontand back

  • 3.2.5. Cons Of Doubly Linked Lists

    Cons An extra reference is needed Additions and deletions are more complex

    (especially near the front and end of the list)

    2008, University of Colombo School of Computing 23

    (especially near the front and end of the list)

  • 3.2.6. An implementation of a doubly linked list

    /***************************IntDLLNode.java********************************/public class IntDLLNode {

    public int info;public IntDLLNode next, prev;public IntDLLNode (int el) {

    this (el,null,null);

    2008, University of Colombo School of Computing 24

    }public IntDLLNode (int el, IntDLLNode n, IntDLLNode p) {

    info = el; next =n; prev=p;}

    }

  • 3.2.6. An implementation of a doubly linked list(2)/***************************IntDLList.java********************************/public class IntDLList {

    private IntDLLNode head, tail;public IntDLList ( ) {

    head = tail = null; }public boolean isEmpty( ) {

    return head == null; }public void addToTail (int el) {

    if (!isEmpty ( )) {tail = new IntDLLNode (el, null, tail);tail.prev.next = tail; }

    else head = tail = new IntDLLNode(el);

    2008, University of Colombo School of Computing 25

    else head = tail = new IntDLLNode(el);}public int removeFromTail ( ) {

    int el = tail.info;if (head == tail) // if only one node in the list;

    head = tail =null;else { // if more than one node in the list;

    tail = tail.prev;tail.next = null; }

    return el;}. }

  • 3.3. Circular Lists

    2008, University of Colombo School of Computing 26

    A circular list. The large yellow object represents the circular list as such. The circular green nodes represent the elements of the list. The rectangular nodes are instances of a class similar to LinkedListNode, which connect the constituents of the list together.

  • 3.3. Circular Lists A circular list is needed in which nodes form a ring: The

    list is finite and each node has a successor. An example of such a situation is when several

    processes are using the same resource for the same amount of time, and we have to assure that each process has a fair share of the resource.

    2008, University of Colombo School of Computing 27

    process has a fair share of the resource. Therefore, all processeslet their numbers be 6, 5, 8,

    and 10, are put on a circular list accessible through current.

    After one node in the list is accessed and the process number is retrieved from the node to activate this process, current moves to the next node so that the next process can be activated the next time.

  • 3.3. Circular Lists In an implementation of a circular singly

    linked list, we can use only one permanent reference, tail, to the list even though operations on the list require access to the

    2008, University of Colombo School of Computing 28

    operations on the list require access to the tail and its successor, the head.

    To that end, a linear singly linked list uses two permanent references, head and tail.

  • 3.3. Circular Linked Lists An extra link from the end of the list to the

    front forms the list into a ring

    2008, University of Colombo School of Computing 29

  • 3.3.1. Uses Of A Circular List e.g., Memory management by operating

    systems

    2008, University of Colombo School of Computing 30

  • 3.3.2. Searches With A Circular Linked Lists

    Cannot use a null reference as the signal that the end of the list has been reached.

    Must use the list reference as a point reference (stopping point) instead

    2008, University of Colombo School of Computing 31

    reference (stopping point) instead

  • 3.3.3. Traversing A Circular Linked List

    Cannot use a null reference as the signal that the end of the list has been reached.

    Must use the list reference as a point reference (stopping point) instead

    2008, University of Colombo School of Computing 32

    reference (stopping point) instead

  • 3.3.4. An Example Of Traversing A Circular Linked List

    public void display (){

    Node temp = list;System.out.println("Displaying list");if (isEmpty() == true){

    System.out.println("Nothing to display, list is empty.");

    2008, University of Colombo School of Computing 33

    System.out.println("Nothing to display, list is empty.");}do{

    System.out.println(temp.data);temp = temp.next;

    } while (temp != list);System.out.println();

    }

  • 3.3.5. Worse Case Times For Circular Linked Lists

    2008, University of Colombo School of Computing 34

  • 3.4. Skip Lists Linked lists have some serious drawbacks:

    They require sequential scanning to locate a searched-for element.

    The search starts from the beginning of the list and stops when either a searched-for element is found or the end of the list is reached without finding this element.

    2008, University of Colombo School of Computing 35

    reached without finding this element. Ordering elements on the list can speed up searching, but a

    sequential search is still required. Therefore, we may think about lists that allow for skipping certain nodes to avoid sequential processing.

    A skip list is an interesting variant of the ordered linked list that makes such a nonsequential search possible (Pugh 1990).

  • 3.4. Skip Lists contd In a skip list of n nodes, for each k and i such that 1
  • 3.5. Self-organizing lists The introduction of skip lists was motivated by the need to speed up the

    searching process. Although singly and doubly linked lists require sequential search to locate

    an element or to see that it is not in the list, we can improve the efficiency of the search by dynamically organizing the list in a certain manner.

    This organization depends on the configuration of data; thus, the stream of data requires reorganizing the nodes already on the list.

    There are many different ways to organize the lists, and this section

    2008, University of Colombo School of Computing 37

    There are many different ways to organize the lists, and this section describes four of them:

    Move-to-front method. After the desired element is located, put it at the beginning of the list.

    Transpose method. After the desired element is located, swap it with its predecessor unless it is at the head of the list.

    Count method. Order the list by the number of times elements are being accessed.

    Ordering method. Order the list using certain criteria natural for the information under scrutiny.

  • 3.5. Self-organizing lists contd In the first three methods, new information

    is stored in a node added to the end of the list;

    In the fourth method, new information is

    2008, University of Colombo School of Computing 38

    In the fourth method, new information is stored in a node inserted somewhere in the list to maintain the order of the list.

  • 3.5. Self-organizing lists contd With the first three methods, we try to locate the elements most likely to be

    looked for near the beginning of the list, most explicitly with the move-to-front method and most cautiously with the transpose method.

    The ordering method already uses some properties inherent to the information stored in the list. For example, if we are storing nodes pertaining to people, then the list can be organized alphabetically by the name of the person or the city or in ascending or descending order using, say, birthday or salary.

    2008, University of Colombo School of Computing 39

    ascending or descending order using, say, birthday or salary. This is particularly advantageous when searching for information that is not

    in the list, because the search can terminate without scanning the entire list. Searching all the nodes of the list, however, is necessary in such cases

    using the other three methods. The count method can be subsumed in the category of the ordering

    methods if frequency is part of the information. In many cases, however, the count itself is an additional piece of

    information required solely to maintain the list; hence, it may not be considered "natural" to the information at hand.

  • 3.5. Self-organizing lists contd Analyses of the efficiency of these methods

    customarily compare their efficiency to that of optimal static ordering.

    With this ordering, all the data are already ordered by the frequency of their occurrence in

    2008, University of Colombo School of Computing 40

    ordered by the frequency of their occurrence in the body of data so that the list is used only for searching, not for inserting new items.

    Therefore, this approach requires two passes through the body of data, one to build the list another to use the list for search alone.

  • 4. Recursion

    2008, University of Colombo School of Computing

  • 4.1. Recursive Definitions There are many programming concepts

    that define themselves. As it turns out, formal restrictions imposed

    on definitions such as existence and

    2008, University of Colombo School of Computing

    on definitions such as existence and uniqueness are satisfied and no violation of the rules takes place. These definitions are called recursive definitions.

    Recursive definitions are used primarily to define infinite sets.

  • 4.1. Recursive Definitions contd

    When defining such as set, giving a complete list of elements is impossible, and for large finite sets, it is inefficient.

    A recursive definition consists of 2 parts :

    2008, University of Colombo School of Computing

    A recursive definition consists of 2 parts : In the first part, called the anchor or ground case

    the basic elements that are building blocks of all other elements of the set are listed.

    In the second part, rules are given that allow for the construction of new objects out of basic elements or objects that have already been constructed.

  • 4.1. Recursive Definitions contd

    These rules are applied again and again to generate new objects.

    Example : To construct the set of natural numbers, one basic element, 0, is singled out,

    2008, University of Colombo School of Computing

    numbers, one basic element, 0, is singled out, and the operation of incrementing by 1 is given as : 0 N; if n N, then (n+1) N; there are no other objects in the set N.N consists of the following items : 0,1,2,3,4,5,6,7,9

  • 4.1. Recursive Definitions contd

    Recursive Definitions serve two purposes: Generating new elements Testing whether an element belongs to a set.

    Recursive definitions are frequently used

    2008, University of Colombo School of Computing

    Recursive definitions are frequently used to define functions and sequence of numbers.

  • 4.2. Method calls and recursion implementation

    What happens when a method is called? If the method has formal parameters, they have to be initialized to the values passed as actual parameters.

    2008, University of Colombo School of Computing

    In addition, the system has to know where to resume execution of the program after the method has finished.

    The method can be called by other methods or by the main program (main ()).

  • 4.2. Method calls and recursion implementation contd

    The information indicating where it has been called from has to be remembered by the system.

    This could be done by storing the return address in main memory in a place set aside for return addresses, but we do not know in advance how much space might be needed, and allocating too much space for that purpose

    2008, University of Colombo School of Computing

    needed, and allocating too much space for that purpose alone is not efficient.

    For a method call, more information has to be stored than just a return address. Therefore, dynamic allocation using the run-time stack is a much better solution.

  • 4.2. Method calls and recursion implementation contd

    What information should be preserved when a method is called? First, automatic (local) variables must be stored. If method f1(), which contains a declaration of an automatic variable

    x, calls method f2(), which locally declares the variable x, the system has to make a distinction between these two variables x.

    If f2 () uses a variable x, then its own x is meant; if f2 () assigns a value to x, then x belonging to f1 () should be left unchanged.

    2008, University of Colombo School of Computing

    value to x, then x belonging to f1 () should be left unchanged. When f2 () is finished, f1 () can use the value assigned to its private

    x before f2 () was called. This is especially important in the context of the present chapter,

    when f1 () is the same as f2 (), when a method calls itself recursively.

  • 4.2. Method calls and recursion implementation contd

    The state of each method, including main ( ), is characterized by the contents of all automatic variables, the values of the method's parameters, and the return address indicating where to restart its caller.

    The data area containing all this information is called an activation record or a stack frame and is allocated on the run-time stack.

    An activation record exists for as long as a method owning it is

    2008, University of Colombo School of Computing

    An activation record exists for as long as a method owning it is executing.

    This record is a private pool of information for the method, a repository that stores all information necessary for its proper execution and how to return to where it was called from.

    Activation records usually have a short lifespan because they are dynamically allocated at method entry and deallocated upon exiting.

    Only the activation record of main ( ) outlives every other activation record.

  • 4.2. Method calls and recursion implementation contd

    An activation record usually contains the following information: Values for all parameters to the method, location of the first cell if

    an array is passed or a variable is passed by reference, and copies of all other data items.

    Local (automatic) variables that can be stored elsewhere, in which case, the activation record contains only their descriptors

    2008, University of Colombo School of Computing

    which case, the activation record contains only their descriptors and pointers to the locations where they are stored.

    The return address to resume control by the caller, the address of the caller's instruction immediately following the call.

    A dynamic link, which is a pointer to the caller's activation record. The returned value for a method not declared as void. Because

    the size of the activation record may vary from one call to another, the returned value is placed right above the activation record of the caller.

  • 4.2. Method calls and recursion implementation contd

    If a method is called either by main () or by another method, then its activation record is created on the run-time stack.

    Creating an activation record whenever a method is called allows the system to handle recursion properly.

    Recursion is calling a method that happens to have the

    2008, University of Colombo School of Computing

    Recursion is calling a method that happens to have the same name as the caller.

    Therefore, a recursive call is not literally a method calling itself, but rather an instantiation of a method calling another instantiation of the same original.

    These invocations are represented internally by different activation records and are thus differentiated by the system.

  • 4.3. Anatomy of a Recursive Call The function that defines raising any number x to a

    nonnegative integer power n is good example of a recursive function.

    The most natural definition of this functions given by:

    2008, University of Colombo School of Computing

    Xn = 1 if n =0

    x.xn-1 if n > 0

  • 4.3. Anatomy of a Recursive Call contd

    A Java method for computing xn can be written directly from the definition of a power:

    double power (double x, int n) {if (n == 0)

    2008, University of Colombo School of Computing

    if (n == 0)return 1.0;

    elsereturn x* power (x, n-1);

    }

  • 4.4. The implementation of recursion

    Thee are several implementations of recursions such as Tail recursion NonTail Recursion

    2008, University of Colombo School of Computing

    NonTail Recursion Indirect recursion Nested Recursion Excessive recursion

  • 4.4.1. Tail recursion All recursive definitions contain a reference to a

    set or function being defined. There are, however, a variety of ways such a

    reference can be implemented.

    2008, University of Colombo School of Computing

    reference can be implemented. This reference can be done in a straightforward

    manner or in an intricate fashion, just once or many times.

    There may be many possible levels of recursion or different levels of complexity.

  • 4.4.1. Tail recursion contd Tail recursion is characterized by the use

    of only one recursive call at the very end of a method implementation.

    In other words, when the call is made,

    2008, University of Colombo School of Computing

    In other words, when the call is made, there are no statements left to be executed by the method; the recursive call is not only the last statement but there are no earlier recursive calls, direct or indirect.

  • 4.4.1. Tail recursion contd Example : The method tail() defined as

    void tail (int i) {if (I > 0) {

    System.out.print (I + );

    2008, University of Colombo School of Computing

    System.out.print (I + );tail (i-1);

    }}

  • 4.4.1. Tail recursion contd Tail recursion is simply a glorified loop and

    can be easily replaced by one. In this example, it is replaced by

    substituting a loop for the if statement and

    2008, University of Colombo School of Computing

    substituting a loop for the if statement and incrementing or decrementing the variable i in accordance with the level of recursion.

    In this way, tail ( ) can be expressed by an iterative method:

  • 4.4.1. Tail recursion contdvoid iterativeEquivalentOfTail ( int i ) {

    for ( ; i > 0; i-- ) System. out. print (i+ "");

    }

    2008, University of Colombo School of Computing

    }

  • 4.4.1. Tail recursion contd Is there any advantage in using tail recursion

    over iteration? For languages such as Java, there may be no

    compelling advantage, but in a language such as Prolog, which has no explicit loop construct (loops are

    2008, University of Colombo School of Computing

    Prolog, which has no explicit loop construct (loops are simulated by recursion), tail recursion acquires a much greater weight.

    In languages endowed with a loop or its equivalents, such as an if statement combined with a goto statement or labeled statement, tail recursion should not be used.

  • 4.4.2. NonTail Recursion Another problem that can be implemented in

    recursion is printing an input line in reverse order.

    Here is a simple recursive implementation:

    2008, University of Colombo School of Computing

    Here is a simple recursive implementation:void reverse() {

    char ch = getChar();if (ch != '\n') {

    reverse() ;System.out.print(ch);

    }}

  • 4.4.3. Indirect recursion Direct recursion - where a method f ( ) called itself. f ( ) can call itself indirectly via a chain of other calls.

    For example, f ( ) can call g(), and g ( ) can call f ( ) . This is the simplest case of indirect recursion.

    The chain of intermediate calls can be of an arbitrary length, as in:

    2008, University of Colombo School of Computing

    length, as in:f () f1() f2() .. fn() f()

    There is also the situation when f ( ) can call itself indirectly through different chains.

    Thus, in addition to the chain just given, another chain might also be possible. For instancef() g1 () g2() gm() f()

  • 4.4.3. Indirect recursion This situation can be exemplified by three methods used

    for decoding information. receive () stores the incoming information in a buffer decode () converts it into legible form store () stores it in a file

    receive () fills the buffer and calls decode (), which in

    2008, University of Colombo School of Computing

    receive () fills the buffer and calls decode (), which in turn, after finishing its job, submits the buffer with decoded information to store ().

    After store () accomplishes its tasks, it calls receive () to intercept more encoded information using the same buffer.

    Therefore, we have the chain of callsreceive() decode() store() receive()

    decode()

  • 4.4.3. Indirect recursion contd Above three methods work in the following

    manner:receive (buffer)

    while buffer is not filled upif information is still incoming

    2008, University of Colombo School of Computing

    if information is still incomingget a character and store it in buffer;

    else exit( );decode (buffer);

    decode (buffer)decode information in buffer;store (buffer);

    store (buffer)transfer information from buffer to file;receive (buffer);

  • 4.4.3. Indirect recursion contd As usual in the case of recursion, there has to

    be an anchor in order to avoid falling into an infinite loop of recursive calls.

    2008, University of Colombo School of Computing

  • Nested Recursion A more complicated case of recursion is found in

    definitions in which a function is not only defined in terms of itself, but also is used as one of the parameters. The following definition is an example of such a nesting:

    0 if n = 0

    2008, University of Colombo School of Computing

    h(n) =

    0 if n = 0

    N if n > 4

    h(2 +h(2n)) if n

  • 4.4.4. Nested Recursion contd

    Function h has a solution for all n >= 0. This fact is obvious for all n > 4 and n = 0, but it has to be proven for n = 1,2,3, and 4. Thus, h(2) = h(2 + h(4)) = h(2 + h(2 +

    2008, University of Colombo School of Computing

    Thus, h(2) = h(2 + h(4)) = h(2 + h(2 + h(8))) = 12. (What are the values of h(n) for n = 1,3, and 4?)

  • 4.4.4. Nested Recursion contd Another example of nested recursion is a very important

    function originally suggested by Wilhelm Ackermann in 1928 and later modified by Rozsa Peter:

    m+1 if n = 0

    2008, University of Colombo School of Computing

    A(n,m) =

    m+1 if n = 0

    A(n-1,1) if n > 0, m = 0

    A(n-1, A(n,m-1)) otherwise

  • 8.4.4. Nested Recursion contd Above function is interesting because of its remarkably rapid growth. It grows so fast that it is guaranteed not to have a representation by a

    formula that uses arithmetic operations such as addition, multiplication, and exponentiation.

    To illustrate the rate of growth of the Ackermann function, we need only show that

    A(3,m) = 2m+3 -3

    2008, University of Colombo School of Computing

    A(4,m) = 22:216 - 3with a stack of m 2s in the exponent; A(4,l) = 2216 - 3 = 265536 - 3, which exceeds even the number of atoms in the universe (which is 1080 according to current theories).

    The definition translates very nicely into Java, but the task of expressing it in a nonrecursive form is truly troublesome.

  • 4.4.4. Excessive recursion Logical simplicity and readability are used as an argument

    supporting the use of recursion. The price for using recursion is slowing down execution time and

    storing on the run-time stack more things than required in a nonrecursive approach.

    If recursion is too deep (for example, computing 5.6100'000), then we can run out of space on the stack and our program terminates

    2008, University of Colombo School of Computing

    can run out of space on the stack and our program terminates abnormally by raising an unrecoverable StackOverflowError.

    But usually, the number of recursive calls is much smaller than 100,000, so the danger of overflowing the stack may not be imminent. However, if some recursive function repeats the computations for some parameters, the run time can be prohibitively long even for very simple cases.

  • 4.4.4. Excessive recursion contd Consider Fibonacci numbers. A sequence of Fibonacci

    numbers is defined as follows:

    Fib(n) = n if n = 0

    Fib(n-2)+Fib(n-1) otherwise

    2008, University of Colombo School of Computing

    The definition states that if the first two numbers are 0 and 1, then any number in the sequence is the sum of its two predecessors. But these predecessors are in turn sums of their predecessors, and so on, to the beginning of the sequence. The sequence pro-duced by the definition is

    0,1,1,2,3, 5,8,13,21, 34, 55,89,...

  • 4.4.4. Excessive recursion contd

    How can this definition be implemented in Java? It takes almost term-by-term translation to have a recursive version, which is

    int Fib (int n) {if (n < 2)

    2008, University of Colombo School of Computing

    if (n < 2) return n;

    else return Fib(n-2) + Fib(n-l); }

    The method is simple and easy to understand but extremely inefficient fibonacci heap.

  • 5. Trees

    2008, University of Colombo School of Computing 1

    Part -1

  • 5.1. Trees, Binary trees and Binary Search trees

    What Is a Tree?

    A tree consists of nodes connected by edges.

    2008, University of Colombo School of Computing 2

    In the above picture of the tree, the nodes are represented as circles, and the edges as lines connecting the circles.

    Trees have been studied extensively as abstract mathematical entities, so there's a large amount of theoretical knowledge about them.

    A tree is actually an instance of a more general category called a graph.

  • 5.1. Trees, Binary trees and Binary Search trees contd

    What Is a Tree? contd In computer programs, nodes often represent

    such entities as people, car parts, airline reservations, and etc; in other words, the

    2008, University of Colombo School of Computing 3

    reservations, and etc; in other words, the typical items we store in any kind of data structure.

    The lines (edges) between the nodes represent the way the nodes are related.

  • 5.1. Trees, Binary trees and Binary Search trees contd

    There are different kinds of trees Binary Tree : each node in a binary tree has a

    maximum of two children. Multiway trees : more general trees, in which

    2008, University of Colombo School of Computing 4

    Multiway trees : more general trees, in which nodes can have more than two children, are called

  • 5.1. Trees, Binary trees and Binary Search trees contd

    Why Use Binary Trees? Why might you want to use a tree? Usually, because it combines the advantages

    of two other structures:

    2008, University of Colombo School of Computing 5

    an ordered array and a linked list.

    You can search a tree quickly, as you can an ordered array, and you can also insert and delete items quickly, as you can with a linked list.

  • 5.2. Implementation of Binary trees

    The Node Class First, we need a class of node objects. These objects contain the data representing

    the objects being stored (employees in an

    2008, University of Colombo School of Computing 6

    the objects being stored (employees in an employee database, for example) and also references to each of the node's two children. Here's how that looks:

  • 5.2. Implementation of Binary trees contd

    class Node{

    int iData; // data used as key valuefloat fData; // other datanode leftChild; // this node's left child

    2008, University of Colombo School of Computing 7

    node rightChild; // this node's right child

    public void displayNode(){// method body}

    }

  • 5.2. Implementation of Binary trees contd

    There are other approaches to designing class Node. Instead of placing the data items directly into the node, you could use a reference to an object representing the data item:class Node{

    person p1; // reference to person

    2008, University of Colombo School of Computing 8

    person p1; // reference to person objectnode leftChild; // this node's left childnode rightChild; // this node's right child

    }class person{

    int iData;float fData;

    }

  • 5.2. Implementation of Binary trees contd

    The Tree Class We'll also need a class from which to instantiate the

    tree itself; the object that holds all the nodes. We'll call this class Tree. It has only one field: a Node

    variable that holds the root.

    2008, University of Colombo School of Computing 9

    variable that holds the root. It doesn't need fields for the other nodes because

    they are all accessed from the root. The Tree class has a number of methods: some for

    finding, inserting, and deleting nodes, several for different kinds of traverses, and one to display the tree.

  • 5.2. Implementation of Binary trees contd

    Here's a skeleton version:class Tree{

    private Node root; // the only data field in Treepublic void find(int key){

    2008, University of Colombo School of Computing 10

    {}public void insert(int id, double dd){}public void delete(int id){}// various other methods

    } // end class Tree

  • 5.2. Implementation of Binary trees contd

    The TreeApp Class Finally, we need a way to perform operations

    on the tree. Here's how you might write a class with a

    2008, University of Colombo School of Computing 11

    Here's how you might write a class with a main() routine to create a tree, insert three nodes into it, and then search for one of them.

    We'll call this class TreeApp:

  • 5.2. Implementation of Binary trees contd

    class TreeApp{

    public static void main(String[] args){

    Tree theTree = new Tree; // make a treetheTree.insert(50, 1.5); // insert 3 nodes

    2008, University of Colombo School of Computing 12

    theTree.insert(50, 1.5); // insert 3 nodestheTree.insert(25, 1.7);theTree.insert(75, 1.9);node found = theTree.find(25); // find node with key 25if(found != null)System.out.println("Found the node with key 25");elseSystem.out.println("Could not find node with key 25");

    } // end main()} // end class TreeApp

  • 5.3. Searching a Binary tree Finding a Node

    Finding a node with a specific key is the simplest of the major tree operations, so let's start with that.

    Remember that the nodes in a binary search tree correspond to objects containing information.

    They could be person objects, with an employee number as the

    2008, University of Colombo School of Computing 13

    They could be person objects, with an employee number as the key and also perhaps name, address, telephone number, salary, and other fields.

    Or they could represent car parts, with a part number as the key value and fields for quantity on hand, price, and so on.

    However, the only characteristics of each node that we can see in the Workshop applet are a number and a color. A node is created with these two characteristics and keeps them throughout its life.

  • 5.3. Searching a Binary tree contd

    Java Code for Finding a NodeHere's the code for the find() routine, which is a method of the Tree class:

    public Node find(int key) // find node with given key{ // (assumes non-empty tree)Node current = root; // start at rootwhile(current.iData != key) // while no

    2008, University of Colombo School of Computing 14

    while(current.iData != key) // while no match,{if(key < current.iData) // go left?current = current.leftChild;elsecurrent = current.rightChild; // or go right?if(current == null) // if no child,return null; // didn't find it}return current; // found it}

  • 5.3. Searching a Binary tree contd

    This routine uses a variable current to hold the node it is currently examining.

    The argument key is the value to be found. The routine starts at the root. (It has to; this is the only node it can access directly.) That is, it sets current to the root.

    Then, in the while loop, it compares the value to be

    2008, University of Colombo School of Computing 15

    Then, in the while loop, it compares the value to be found, key, with the value of the iData field (the key field) in the current node. If key is less than this field, then current is set to the node's left child.

    If key is greater than (or equal) to the node's iData field, then current is set to the node's right child.

  • 5.4. Ways of traversing a tree Tree-traversal refers to the process of

    visiting each node in a tree data structure, exactly once, in a systematic way. Such traversals are classified by the order in

    2008, University of Colombo School of Computing 16

    traversals are classified by the order in which the nodes are visited.

  • 5.4. Ways of traversing a tree contd

    Traversal methodsCompared to linear data structures like linked lists and one dimensional arrays, which have only one logical means of traversal, tree structures can be traversed in many different ways. Starting at the root of a binary tree, there are three main steps that can be performed and

    2008, University of Colombo School of Computing 17

    there are three main steps that can be performed and the order in which they are performed define the traversal type. These steps (in no particular order) are: performing an action on the current node (referred to as "visiting" the node), traversing to the left child node, and traversing to the right child node.

  • 5.4. Ways of traversing a tree contd

    To traverse a non-empty binary tree in preorder, perform the following operations recursively at each node, starting with the root node:

    1. Visit the node. 2. Traverse the left subtree. 3. Traverse the right subtree. (This is also called Depth-first traversal.)

    To traverse a non-empty binary tree in inorder, perform the following operations recursively at each node, starting with the root node:

    1. Traverse the left subtree.

    2008, University of Colombo School of Computing 18

    1. Traverse the left subtree. 2. Visit the node. 3. Traverse the right subtree.

    To traverse a non-empty binary tree in postorder, perform the following operations recursively at each node, starting with the root node:

    1. Traverse the left subtree. 2. Traverse the right subtree. 3. Visit the node.

    Finally, trees can also be traversed in level-order, where we visit every node on a level before going to a lower level. This is also called Breadth-first traversal.

  • 5.4. Ways of traversing a tree contd

    Example

    In this binary search tree,

    2008, University of Colombo School of Computing 19

    In this binary search tree, Preorder traversal sequence: F, B, A, D, C, E, G, I, H Inorder traversal sequence: A, B, C, D, E, F, G, H, I

    Note that the inorder traversal of this binary search tree yields an ordered list

    Postorder traversal sequence: A, C, E, D, B, H, I, G, F Level-order traversal sequence: F, B, G, A, D, I, C, E, H

  • 5.4.1. Breadth-first search breadth-first search (BFS) is a graph

    search algorithm that begins at the root node and explores all the neighboring nodes. Then for each of those nearest

    2008, University of Colombo School of Computing 20

    nodes. Then for each of those nearest nodes, it explores their unexplored neighbor nodes, and so on, until it finds the goal.

  • 5.4.1. Breadth-first search How it works

    Breadth-first search (BFS) is an uninformed search method that aims to expand and examine all nodes of a graph systematically in search of a solution. In other words, it exhaustively searches the entire graph without considering the goal until it finds it. It does not

    2008, University of Colombo School of Computing 21

    without considering the goal until it finds it. It does not use a heuristic.

    From the standpoint of the algorithm, all child nodes obtained by expanding a node are added to a FIFO queue. In typical implementations, nodes that have not yet been examined for their neighbors are placed in some container (such as a queue or linked list) called "open" and then once examined are placed in the container "closed".

  • 5.4.1. Breadth-first search How it works

    2008, University of Colombo School of Computing 22

  • 5.4.1. Breadth-first search Applications of BFS

    Breadth-first search can be used to solve many problems in graph theory, for example:

    Finding all connected components in a graph. Finding all nodes within one connected component

    2008, University of Colombo School of Computing 23

    Finding all nodes within one connected component Copying Collection, Cheney's algorithm Finding the shortest path between two nodes u and v

    (in an unweighted graph) Testing a graph for bipartiteness (Reverse) CuthillMcKee mesh numbering

  • 5.4.2. Depth-first search Depth-first search (DFS) is an algorithm for traversing

    or searching a tree, tree structure, or graph. One starts at the root (selecting some node as the root in the graph case) and explores as far as possible along each branch before backtracking.

    Formally, DFS is an uninformed search that progresses

    2008, University of Colombo School of Computing 24

    Formally, DFS is an uninformed search that progresses by expanding the first child node of the search tree that appears and thus going deeper and deeper until a goal node is found, or until it hits a node that has no children. Then the search backtracks, returning to the most recent node it hadn't finished exploring. In a non-recursive implementation, all freshly expanded nodes are added to a LIFO stack for exploration.

  • 5.4.2. Depth-first search How it works

    2008, University of Colombo School of Computing 25

    See next slide:

  • 5.4.2. Depth-first search a depth-first search starting at A, assuming that the left edges in the shown graph are

    chosen before right edges, and assuming the search remembers previously-visited nodes and will not repeat them (since this is a small graph), will visit the nodes in the following order: A, B, D, F, E, C, G.

    Performing the same search without remembering previously visited nodes results in visiting nodes in the order A, B, D, F, E, A, B, D, F, E, etc. forever, caught in the A, B, D, F, E cycle and never reaching C or G.

    Iterative deepening prevents this loop and will reach the following nodes on the following depths, assuming it proceeds left-to-right as above:

    2008, University of Colombo School of Computing 26

    following depths, assuming it proceeds left-to-right as above: 0: A 1: A (repeated), B, C, E

    (Note that iterative deepening has now seen C, when a conventional depth-first search did not.)

    2: A, B, D, F, C, G, E, F (Note that it still sees C, but that it came later. Also note that it sees E via a different

    path, and loops back to F twice.) 3: A, B, D, F, E, C, G, E, F, B

    For this graph, as more depth is added, the two cycles "ABFE" and "AEFB" will simply get longer before the algorithm gives up and tries another branch.

  • 5.4.3. Stackless Depth-First Traversal

    Threaded trees allow you traverse the tree by following pointers stored within the tree

    Each node would store pointers to its predecessor and successor

    2008, University of Colombo School of Computing 27

    predecessor and successor This would create a lot of overhead with

    the additional two pointers for a total of 4 pointers per node

  • 5.5. Insertion and deletion

    Inserting a Node To insert a node we must first find the place to insert

    it. This is much the same process as trying to find a node that turns out not to exist, as described in the section on Find.

    2008, University of Colombo School of Computing 28

    section on Find. We follow the path from the root to the appropriate

    node, which will be the parent of the new node. Once this parent is found, the new node is connected

    as its left or right child, depending on whether the new node's key is less than or greater than that of the parent.

  • 5.5. Insertion and deletioncontd

    Java Code for Inserting a Node The insert() function starts by creating the new node,

    using the data supplied as arguments. Next, insert() must determine where to insert the new

    node. This is done using roughly the same code as

    2008, University of Colombo School of Computing 29

    node. This is done using roughly the same code as finding a node, described in the section on find(). The difference is that when you are simply trying to find a node and you encounter a null (nonexistent) node, you know the node you are looking for doesn't exist so you return immediately. When you're trying to insert a node you insert it (creating it first, if necessary) before returning.

  • 5.5. Insertion and deletioncontd

    The value to be searched for is the data item passed in the argument id.

    The while loop uses true as its condition because it doesn't care if it encounters a node with the same value as id; it treats another node

    2008, University of Colombo School of Computing 30

    with the same value as id; it treats another node with the same key value as if it were simply greater than the key value. (We'll return to the subject of duplicate nodes later in this chapter.)

  • 5.5. Insertion and deletioncontd

    A place to insert a new node will always be found (unless you run out of memory); when it is, and the new node is attached, the while loop exits with a return

    2008, University of Colombo School of Computing 31

    the while loop exits with a return statement.

    Here's the code for the insert() function:

  • 5.5. Insertion and deletioncontd

    public void insert(int id, double dd){Node newNode = new Node(); // make new nodenewNode.iData = id; // insert datanewNode.dData = dd;if(root==null) // no node in root

    root = newNode;

    2008, University of Colombo School of Computing 32

    root = newNode;else // root occupied {Node current = root; // start at rootNode parent;while(true) // (exits internally) {

    parent = current;if(id < current.iData) // go left?{

  • 5.5. Insertion and deletioncontd

    current = current.leftChild;if(current == null) // if end of the line,{ // insert on leftparent.leftChild = newNode;return;}

    } // end if go leftelse // or go right?{

    2008, University of Colombo School of Computing 33

    {current = current.rightChild;if(current == null) // if end of the line

    { // insert on rightparent.rightChild = newNode;return;}

    } // end else go right} // end while

    } // end else not root} // end insert()

  • 5.5. Insertion and deletioncontd

    Deletion The algorithm to delete an arbitrary node from a

    binary tree is deceptively complex, as there are manyspecial cases. The algorithm used for the deletefunction splits it into two separate operations,searching and deletion. Once the node which is to be

    2008, University of Colombo School of Computing 34

    searching and deletion. Once the node which is to bedeleted has been determined by the searchingalgorithm, it can be deleted from the tree. Thealgorithm must ensure that when the node is deletedfrom the tree, the ordering of the binary tree is keptintact.

    Special Cases that have to be considered:

  • 5.5. Insertion and deletioncontd

    Deletion1. The node to be deleted has no children.

    In this case the node may simply be deleted

    2008, University of Colombo School of Computing 35

    In this case the node may simply be deleted from the tree.

  • 5.5. Insertion and deletioncontd

    Deletion contd2. The node has one child.

    The child node is appended to its

    2008, University of Colombo School of Computing 36

    The child node is appended to its grandparent. (The parent of the node to be deleted.)

  • 5.5. Insertion and deletioncontd

    Deletion contd3. The node to be deleted has two children.

    This case is much more complex than the previous two, because the order of the binary

    2008, University of Colombo School of Computing 37

    previous two, because the order of the binary tree must be kept intact. The algorithm must determine which node to use in place of the node to be deleted:

  • 5.5. Insertion and deletioncontd

    (i)Use the inorder successor of the node to be deleted.

    2008, University of Colombo School of Computing 38

  • 5.5. Insertion and deletioncontd

    (ii) Else if no right subtree exists replace the node to be deleted with the it's left child.

    2008, University of Colombo School of Computing 39

  • 5.5. Insertion and deletioncontd

    Deletion of the root node is also a special case. It can be accomplished using the methods described above, checking for the separate cases with no children, two

    2008, University of Colombo School of Computing 40

    the separate cases with no children, two children, or one.

    Complexity Average case is O(log2n). Worst case is O(n).

  • 5. Trees

    2008, University of Colombo School of Computing 1

    5. TreesPart -2

  • 5.6. Balancing a tree BSTs where introduced because in theory

    they give nice fast search time. We have seen that depending on how the

    data arrives the tree can degrade into a

    2008, University of Colombo School of Computing 2

    data arrives the tree can degrade into a linked list

    So what is a good programmer to do. Of course, they are to balance the tree

  • 5.6. Balancing a tree -ideas One idea would be to get all of the data

    first, and store it in an array Then sort the array and then insert it in a

    tree

    2008, University of Colombo School of Computing 3

    tree Of course this does have some drawbacks

    so we need another idea

  • 5.6.1. DSW Trees Named for Colin Day and then for Quentin F.

    Stout and Bette L. Warren, hence DSW. The main idea is a rotation rotateRight( Gr, Par, Ch )

    2008, University of Colombo School of Computing 4

    rotateRight( Gr, Par, Ch ) If Par is not the root of the tree

    Grandparent Gr of child Ch, becomes Chs parent by replacing Par;

    Right subtree of Ch becomes left subtree of Chs parent Par;

    Node Ch aquires Par as its right child

  • Maybe a picture will help

    2008, University of Colombo School of Computing 5

  • 5.6.1.1. More of the DSW So the idea is to take a tree