java
DESCRIPTION
This is consist of Graphs Linked lists and such a kind of things related to java field.TRANSCRIPT
-
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