- •Data Structures and Algorithms Topics for the 1st part of the course Prepared by Dr. Alex Ossyka
- •- Representation of objects in computer memory :
- •Iterative implementation (by loop)
- •1St print: 5
- •2Nd print: 9 3d print: 7
- •2Nd print: 5
- •1St print: 9 3d print: 7
- •Balanced Trees
- •Height-Balanced Binary Search Trees
- Representation of objects in computer memory :
If 2 bytes are used to present an integer number, then MAX_INT = 215 – 1 = 32,767.
If 4 bytes are used to present an integer number, then MAX_INT = 231 –1;
A decision should be made about the machine code to represent integer numbers: 1’complement, 2’complement, BCD, excess-3 etc.
- implementation of operations:
Any operation can be performed by many different algorithms. It is necessary to choose the most suitable algorithm for each operation.
The same decisions should be made about any other data type (standard or user-defined) at the stage of design of a given data type.
Design of any data type goes through 3 stages:
specification of a set of items + specification of a set of operations ;
choice of representation of items in computer memory;
choice of implementation of operations;
At the first stage a data type is designed as an abstract data type.
DEFINITION: an abstract data type (ADT) is a data type that is organized so that the specification of items and operations is separated from details about memory representation of items and implementation of operations.
In an ADT a specification of an operation consists of:
a name of the operation;
the number of arguments and their types;
the type of the result;
a precise description of what the operation does, without saying how the operation does its job. Such a description can be done in terms of a precondition and postcondition.
All standard data types are given to us (users) as ADTs, because we do not care about details of their representation and implementation. We just use them!
One of the aims of this course is to learn how to design our user-defined data types so that other programmers might use them as ADTs (=as if they are standard data types).
Our task is to implement our data structures in Java (or other language) using object-oriented approach. The methods of a class (for example, Java class ) represent the operations of an ADT. Data members of the class contain data items of our ADT. In this way, the process of moving from an ADT to a data structure leads directly to a class definition in some object-oriented programming language, like Java, C++, etc .
The second part of this course is devoted to study of some classic algorithms, their design and analysis methods. These algorithms are based on different data structures.
Topic 2. List ADT.
In many application programs it is necessary to deal with all kinds of list, such as:
A list of universities in Ukraine;
A list of students in section# CE-16-1;
A list of books in a library, etc.
We do same things (operations) with any kind of a list: create an empty list, add items to a list, search a list for some data item, delete some item from a list and some other operations. In many cases it is convenient to have a new user-defined data structure List in order to manipulate lists easily.
Definition: List ADT is a set of lists + set of operations to manipulate lists.
Definition: A list is a sequence of objects (data items) of the same type.
Examples:
List1 of names = ( Sally, Ali, Tom, Amjad); (unsorted)
List2 of numbers = (2, 4, 7, 25, 30, 52); (sorted)
List3 of subjects = (Mathematical calculus, C++, Discrete mathematics, English); (unsorted)
Note: A list and an array are different concepts: an array is a sequence of memory locations, while a list is a sequence of data items. A list may be stored in an array.
For now, we shall consider only unsorted lists and call them just lists. (An unsorted list can be made sorted by applying a sorting method.)
Definition: An unsorted list is an unsorted collection of data items of the same type.
Now that we understood what are the objects, it necessary to specify operations.
Specification of some operations for List ADT :
makeEmpty()
Function: Initializes a list to an empty state.
Precodition: None. (It is a statement that must be true before the operation.)
Postcondition: List is empty. (It is a statement that must be true after the operation.)
Boolean isFull()
Function: Determines if a list is full.
Precodition: List was initialized.
Postcondition: Function value is true if a list is full and false otherwise.
Boolean isEmpty()
Function: Determines if a list is empty.
Precodition: List was initialized.
Postcondition: Function value is true if a list is empty and false otherwise.
addItem(Object item)
Function: add item to a list.
Precodition: List was initialized and not full.
Postcondition: item is in the list.
deleteItem(Object item)
Function: Remove item from list .
Precodition: item is in the list.
Postcondition: item is not in the list.
Boolean search(Object item)
Function: Determines if item is in a list.
Precodition: List was initialized.
Postcondition: Returns true if item is in list and false otherwise.
Object retrieve(Object item)
Function: Find, read and return item without deleting it from a list.
Precodition: List was initialized.
Postcondition: the function value is item if it is found and an empty value otherwise. List is unchanged.
These are examples of formal specifications of operations for objects. Later in C++ code you may find some other operations (length, print, etc.).
At this stage we finished the definition of List ADT as two sets. In this definition there is nothing about programming.
Before designing an algorithm for each operation and wring a C++ code it is necessary to decide how to represent a list in computer memory. There different methods to store a list in memory. Now we shall choose the simplest one.
Representation of a lists object by an array.
A list will be represented by three variables: array (reference to an array of references to actual list items ), max (with a required array size) and last (with the array index of the last list item).
List
A = (25, 11, 17, 30) is represented as a List object A with Java
language so:
|
array |
max |
last |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 |
|
|
|
|
|
|
|
null |
null |
null |
25
11
17
30
To simplify a picture such a list is usually presented so:
|
array |
max |
last |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 |
|
|
|
25 |
11 |
17 |
30 |
|
|
|
List items garbage
Now it we can write a code for the new data structure: Java class List.
In the following code the class name is UnsortedList (not just List as it should be). It was done for instruction purpose only. We use this name to remind you that it is an array implementation of the List ADT. List items are not sorted. It determines the algorithms of class methods.
Here are some data about time efficiency of operations as they are coded in the class UnsortedList.
Operation name |
Time efficiency |
constructor |
O(1) |
isFull() |
O(1) |
isEmpty() |
O(1) |
makeEmpty() |
O(1) |
addItem() |
O(1) |
deleteItem() |
O(n) |
search() |
O(n) |
retrieve() |
O(n) |
length() |
O(1) |
printList() |
O(n) |
Note: In Java, Object is the base class for all other classes. Definition of the name item, Object item, means that item can be of any type.
Note: For two items of Object class we can check only if they are equal or not. For such a check we have to use a member function of the class Object equals(). For example, if(item.equals(B)) …
Other relations (> and < ) are not defined for instances (objects) of the class Object.
H ere is a picture that explains the logic of deleteItem() operation. Given UnsortedList A.
|
array |
max |
last |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 |
|
|
|
25 |
11 |
17 |
30 |
? |
? |
? |
List items garbage
A .deleteItem(25); This operation changes list A as follows:
|
array |
max |
last |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 2 |
|
|
|
2 5 30 |
11 |
17 |
30 |
? |
? |
? |
List items garbage
Java class UnsortedList for Object items (simplified)
Prepared by Dr. Alex Ossyka
//file UnsorteList.java
package unsortedobjectlist;
public class UnsortedList {
protected int max; //array size.
protected int last;//Index of the array element with the last list item;
protected Object[] array;//Reference to the array with list elements.
//*********************************************************************
public UnsortedList(int n)
{ max = n; //Array size.
last = -1;
array = new Object[max]; //Array of references to any class objects is created.
}//An empty list is constructed.
//*********************************************************************
public UnsortedList()
{ max = 100; //Array size = 100 by default
last = -1;
array = new Object[max]; //Array is created assuming that dynamic memory is available.
}//An empty list is constructed.
//*********************************************************************
public boolean isFull()
{ return (last == max - 1); }// isFull
//*********************************************************************
public boolean isEmpty()
{ return (last == -1); }// isEmpty
//*********************************************************************
public int length()//Returns the number of items in list
{ return (last+1); }// length
//*********************************************************************
public void makeEmpty()
{ last = -1; }// makeEmpty
//*********************************************************************
public boolean search(Object item) //If found, true is returned, otherwise false.
{ for(int i = 0; i <= last; i++)
if(item.equals(array[i])) return true;
return false;
}//searchUsorted
//*********************************************************************
public void addItem(Object item) //Add at the end of list.
{ if(isFull())
{ System.out.println("List overflow");
System.exit(1);
}
else array[++last] = item; //item is always added at the end of unsorted list.
}// addItem
//*********************************************************************
public Object retrieve(Object item)
{ if(isEmpty())
{ System.out.println("List underflow");
return null; //Any object is represented by a reference variable.
//null value denotes that the required list item object does not exist.
}//if
for(int i = 0; i <= last; i++)
{ if(item.equals(array[i]))
{ /* Object dataReturned = new Object();
dataReturned = array[i];//A new copy of data is created for data safety.
return dataReturned; //The new data copy is returned.
*/
return array[i];
}//if
}//for
System.out.println("Invalid argument");
return null; //Object item was not found.
}//retrieve
//*********************************************************************
public void deleteItem(Object item)
{ if(isEmpty())
{ System.out.println("List underflow");
System.exit(1);
}
int i;
for(i = 0; i <= last; i++) //searching for an item to be deleted
if(item.equals(array[i]))
break; //item is found. The search is stopped.
if(i > last) //search was stopped after the last list item.
{ System.out.println("Item was not found and was not deleted");
return;
}
array[i] = array[last--]; //Overwriting the deleted item by the last item.
}//deleteItem
//*********************************************************************
public void printList()
{ System.out.println("\nList contents:");
if(last == -1) System.out.println("\nPinting failed: list is empty");
else
{ for(int i = 0; i <= last; i++)
System.out.print(", " + array[i]);
System.out.println("; ");
}//else
}//print_List
//*********************************************************************
}//class UnsortedList
////////////////////////////////////////////////////////////////////////////////////////////////////////
//file UnsortedListTest.java
package unsortedlisttest;
import unsortedobjectlist.*; //to use methods of classes from package unsortedobjectlist
public class UnsortedListTest {
public static void main(String args[] )
{
System.out.println("\nExperimenting with an UNSORTED list of INTEGER numbers:");
UnsortedList B = new UnsortedList(20);
//Adding items after checking preconditions
if(!B.isFull()) //checking the precondition of addItem operation.
B.addItem(15); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
B.printList();
if(!B.isFull()) //checking the precondition of addItem operation.
B.addItem(10); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
B.printList();
if(!B.isFull()) //checking the precondition of addItem operation.
B.addItem(25); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
B.printList();
if(!B.isFull()) //checking the precondition of addItem operation.
B.addItem(20); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
B.printList();
//Deleting items after checking preconditions
if(B.search(10)) //checking the precondition of deleteItem operation.
B.deleteItem(10);
else System.out.println("Item is not on the list. It cannot be deleted.");
B.printList();
if(B.search(35)) //checking the precondition of deleteItem operation.
B.deleteItem(35);
else System.out.println("Item is not on the list. It cannot be deleted.");
B.printList();
Integer A = (Integer)B.retrieve(10);
if( A!= null) //checking if any object was retrieved.
System.out.println("the retrieved data = " + A);
else System.out.println("the item was not found, the data is not retrieved");
B.printList();
System.out.println("\nExperimenting with an UNSORTED list of STRING items:");
UnsortedList C = new UnsortedList(10);
//Adding items after checking preconditions
if(!C.isFull()) //checking the precondition of addItem operation.
C.addItem("Sally"); //inserting string item
else System.out.println("List is full. Item cannot be added.");
C.printList();
if(!C.isFull()) //checking the precondition of addItem operation.
C.addItem("Abeer"); //inserting string item
else System.out.println("List is full. Item cannot be added.");
C.printList();
if(!C.isFull()) //checking the precondition of addItem operation.
C.addItem("Tom"); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
C.printList();
if(!C.isFull()) //checking the precondition of addItem operation.
C.addItem("Alex"); //inserting integer item
else System.out.println("List is full. Item cannot be added.");
C.printList();
//Deleting items after checking preconditions
if(C.search("Abeer")) //checking the precondition of deleteItem operation.
C.deleteItem("Abeer");
else System.out.println("Item is not on the list. It cannot be deleted.");
C.printList();
if(C.search("Ali")) //checking the precondition of deleteItem operation.
C.deleteItem("Ali");
else System.out.println("Item is not on the list. It cannot be deleted.");
C.printList();
String D = (String)C.retrieve("Tom");
if( D!= null) //checking if any object was retrieved.
System.out.println("the retrieved data = " + D);
else System.out.println("the item was not found, the data is not retrieved");
C.printList();
}//main
}//class UnsortedListTest
The program output:
Experimenting with an UNSORTED list of INTEGER numbers:
List contents:
, 15;
List contents:
, 15, 10;
List contents:
, 15, 10, 25;
List contents:
, 15, 10, 25, 20;
List contents:
, 15, 20, 25;
Item is not on he list.It cannot be deleted.
List contents:
, 15, 20, 25;
Invalid argument
the item was not found, the data is not retrieved
List contents:
, 15, 20, 25;
Experimenting with an UNSORTED list of STRING items:
List contents:
, Sally;
List contents:
, Sally, Abeer;
List contents:
, Sally, Abeer, Tom;
List contents:
, Sally, Abeer, Tom, Alex;
List contents:
, Sally, Alex, Tom;
Item is not on the list. It cannot be deleted.
List contents:
, Sally, Alex, Tom;
the retrieved data = Tom
List contents:
, Sally, Alex, Tom;
Topic 3. Implementation of Unsorted Linked List ADT by Linked Structures.
Problems with lists (and other data structures) implemented by arrays:
overflow situation if the actual number of data items is larger than the array size;
memory is wasted if the array size is much larger than the actual number of data items.
That is why lists (and other data structures) are often implemented by linked structures using references to objects in dynamic memory.
Unsorted Linked List ADT = set of linked unsorted lists + set of operations for such lists.
An unsorted linked list object of 5 integer numbers (10, 7, 6, 2, 34) can be pictured as follows :
f |
|
1 |
|
|
7 |
|
|
6 |
|
|
2 |
|
|
34 |
null |
data (data part of each node) next (reference, link part of each node)
reference to the first element (node) nodes of a linked list
So a linked list consists of:
a refernce first which is a private data member of each List object;
linked nodes which are allocated memory in the dynamic part of a RAM (in a heap) during the execution of a program.
A Random Access Memory (RAM) consists of such parts:
Byte address |
#0 |
#1 |
#2 |
… |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#MAX |
RAM: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OS (Opeating system) UP1 UP2 … UPk heap (dynamic memory) SS (System Stack)
User Program 1…User Program k
Usually a heap is the largest part of the RAM. When a program needs some memory for an object in the heap it asks the OS to allocate this memory by the operator new. If the memory is found the operator new returns the reference to this memory (its address). If the OS cannot find enough memory for the object then new returns the reference value null.
Note: Lists represented by arrays and lists represented by linked nodes are different implementations of the same List ADT. Operations for such lists are the same (their algorithms are different). A user programmer does not care how stacks are saved in memory. He/she just declares an objects of the class List and calls the member functions of this class. That is why, in application programs, we should use the same class name List for both representations of List objects. But when we study new data structures we prefer to use two class names: List for lists in arrays and ListLinked for lists implemented as linked nodes.
Operations: isFull(), isEmpty(), makeEmpty(), addItem(), deleteItem(), search(), retrieve(), printList(), etc.
A picture that illustrates the logic of the operation deleteItem(2):
p
prev
f irst |
|
10 |
|
|
7 |
|
|
6 |
|
|
2 |
|
|
34 |
null |
Deleting is done by: prev.next
Here are some pictures that illustrate operations for linked lists of integer data items. first
List A = new List(); |
(empty) |
A: |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.addItem(20); |
|
A: |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A. addItem (15); |
|
A: |
|
|
15 |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int b = A.retrieve(20); |
|
A: |
|
|
15 |
|
|
20 |
null |
b=20; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.addItem(30); |
|
A: |
|
|
30 |
|
|
15 |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
A.deleteItem(15); |
|
A: |
|
|
30 |
|
|
20 |
null |
|
|
|
Note:
addItem() always inserts a new node with a new item at the beginning of a list.
deleteItem() at first finds a node with the item in a linked list, then it deletes the node from list by changing one link (the value of next in the previous node).
retrieve() finds the argument item in the list and returns it (and all related data) without deleting the node (item) from a list. (retrieve = find, read, do not delete)
The algorithms of each operation for linked lists are simple and quite clear from the Java code.
In the following code the class name is UnsortedLinkedList (not just List as it should be). We use this name to remind you that it is a linked implementation of the List ADT. List items are not sorted. It determines the algorithms of class methods.
Java class UnsortedLinkedList and its related classes.
Prepared by Dr. Alex Ossyka
//file Node.java
package unsortedlinkedlist_consistent;
public class Node {
protected Object data;
protected Node next;
public Node(Object d, Node ref)
{
data = new Object();
data = d;
next = ref;
}//constructor wiht parameters
public Node()
{
data = new Object();
next = null;
}//constructor
}//class Node = type of a node in an unsorted linked list
/////////////////////////////////////////////////////////////////////////////////////////
//file UnsortedLinkedList.java
package unsortedlinkedlist_consistent;
public class UnsortedLinkedList {
protected Node first;
//*****************************************************************
public UnsortedLinkedList()
{ first = null; }//constructor
//*****************************************************************
public boolean isEmpty()
{ return (first == null); }
public boolean isFull()
{
Node new_node = new Node();
return (new_node == null);
}
//*****************************************************************
public void makeEmpty()
{ first = null; }
//*****************************************************************
public void addItem(Object item) //Item is always added at the beginning of a list.
{
if(isFull())
{ System.out.println("List overflow");
System.exit(1);
}
first = new Node(item, first); //Using Node constructor with parameters
}//addItem
//*****************************************************************
public void deleteItem(Object item)
{
Object result = new Object();
if(isEmpty())
{ System.out.println("List underflow");
` System.exit(1);
}
Node prev = null, p = first;
while(p != null && !item.equals(p.data)) //searching for item
{ prev = p;
p = p.next;
}
if(p == null) //the last node was tried unsuccessfully
{ System.out.println("Item was not found: it cannot be deleted\n");
System.exit(1);
}
result = p.data;// p references the data to be deleted
if(prev == null) //first node is excluded from list
first = first.next;
else //not the first node is excluded from list
prev.next = p.next;
} //deleteItem
//*****************************************************************
public Object retrieve(Object item)
{
Object result = new Object();
if(isEmpty())
{ System.out.println("List underflow");
System.exit(1);
}
Node p = first;
while(p != null && !item.equals(p.data)) //searching for item
p = p.next;
if(p == null) //The last node was tried uncussesfully.
{ System.out.println("Item was not found: it cannot be deleted\n");
System.exit(1);
}
result = p.data;// p references the data to be returned
return result;//reference to memory location not connecte to list
} //retrieve
//*****************************************************************
public boolean search(Object item)
{
if(isEmpty())
{ System.out.println("List underflow");
System.exit(1);
}//if
Node p = first;
while(p != null)
{
if(item.equals(p.data))
return true;
p = p.next;
}//while
return false;
} //search
//*****************************************************************
public int length()
{ int count = 0; //to count nodes
Node p = first;
while(p != null)
{ count++;
p = p.next;
}//while
return count;
} //length
//*****************************************************************
public void printList()
{ System.out.println("\nList contents:");
if(first == null)
System.out.println("\nPrinting failed: list is empty\n");
else
{ Node p = first;
while(p != null)
{ ` System.out.print(" " + p.data);
p = p.next;
}//while
System.out.println("; ");
}//else
}//printList
}//class UnsortedLinkedList
///////////////////////////////////////////////////////////////////////////////////
//file UnsortedLinkedListTest.java
package unsortedlinkedlisttest;
import unsortedlinkedlist_consistent.*;
public class UnsortedLinkedListTest {
public static void main(String[] args)
{
System.out.println("\nExperimenting with a list of integer numbers:");
UnsortedLinkedList B = new UnsortedLinkedList();
if(!B.isFull()) //checking the precondition for adding item
B.addItem(15); //inserting integer item
else
System.out.println("List is full: item was not added");
B.addItem(10); //adding item without checking the precondition
B.addItem(25); //adding integer item
B.printList();
Integer a; //a is object of the rapper class Integer
if(B.search(10)) //checking the precondition for retrieving and deleting.
{
a = (Integer)B.retrieve(10);
B.deleteItem(10);
System.out.println("Retrieved and deleted item = " + a);
}
else
System.out.println("Item is not in the list: it cannot be deleted");
System.out.println("The NUMBER of ITEMS in list is = " + B.length());
B.printList();
System.out.println("\nExperimenting with a list of strings:");
UnsortedLinkedList A = new UnsortedLinkedList();
A.addItem("Ali"); //inserting String item
A.addItem("Sam");
A.addItem("Bob");
System.out.println("The NUMBER of ITEMS in list is = " + A.length());
A.printList();
String b;
if(A.search("Ali"))
{
b = (String)A.retrieve("Ali");
A.deleteItem("Ali");
System.out.println("Retrieved and deleted item = " + b);
A.printList();
}
else
System.out.println("Item is not in the list: it cannot be deleted");
}//main
}// class UnsortedLinkedListTest
/////////////////////////////////////////////////////////////////////////////////////
Program output:
Experimenting with a list of integer numbers:
List contents:
25 10 15;
Retrieved and deleted item = 10
The NUMBER of ITEMS in list is = 2
List contents:
25 15;
Experimenting with a list of strings:
The NUMBER of ITEMS in list is = 3
List contents:
Bob Sam Ali;
Retrieved and deleted item = Ali
List contents:
Bob Sam;
Topic 4. Stack ADT.
Definition: Stack ADT is a set of stacks + seat of operations that manipulate stacks.
Definition: A stack is a special type of a list where items are added and deleted from one end called the top of a stack. It is a Last In First Out (LIFO) type of a list.
Operations: isFull(), IsEmpty(), makeEmpty(), push() (==addItem), pop() (==deleteItem).
Here are some pictures to illustrate push(item) and pop() operations:
index |
Empty |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
Push(17) |
|
|
Push(7) |
|
|
Pop() |
|
|
Push(5) |
|
|
1 |
|
|
|
|
|
|
7 |
|
|
|
|
|
5 |
0 |
|
top = 0 |
|
17 |
top = 1 |
|
17 |
t op = 0 |
|
17 |
top =1 |
|
17 |
top = -1
Though a stack data structure is very simple it is widely used in computers:
For processing function calls;
For parsing and evaluating evaluating different expressions;
For designing algorithms with backtracking, etc.
Representation of stacks in computer memory. For now we shall represent stacks by arrays.
Each stack object will be represented in memory by an array and three variables: max (array size), top (array index of the top stack item), array (reference to an array with stack items).
A statck object A can be viewed so:
|
array |
max |
top |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 |
|
|
|
15 |
20 |
10 |
25 |
? |
? |
? |
stack items garbage
int b = A.pop(); This operation removes and returns the item in position #top and decrements the index top : return array[top--]; The result of this operation is presented on the following picture:
|
array |
max |
top |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
3 2 |
|
|
|
15 |
20 |
10 |
25 |
? |
? |
? |
stack items garbage
25 (retrned)
The picture below explains the logic of push(item) operation. It increments the index top and writes in the top position the value of the argument: array[++top] = item;
A .push(17):
|
array |
max |
top |
|
|
|
0 |
1 |
2 |
3 |
4 |
5 |
6=max-1 |
A: |
|
7 |
2 3 |
|
|
|
15 |
20 |
10 |
17 |
? |
? |
? |
List items garbage
Class Stack and its related classes
Prepared by Dr. Alex Ossyka
//File Stack.java
package stack;
public class Stack {
protected int max; //array size.
protected int top;//Index of the array element with the last list item;
protected Object array[];//Reference to an array with Object elements.
//*********************************************************************
public Stack(int n)
{
max = n; //Array size.
top = -1;
array = new Object[max]; //assuming that memory is available.
}//constructor with parameter
//*********************************************************************
public Stack()
{ max = 100; //Array size = 100.
top = -1;
array = new Object[max];
}//constructor without parameter
//*********************************************************************
public boolean isFull()
{ return (top == max - 1); }// isFull
//*********************************************************************
public boolean isEmpty()
{ return (top == -1); }// isEmpty
//*********************************************************************
public void makeEmpty()
{ top = -1; }// makeEmpty
//*********************************************************************
public void push(Object item)
{ if(isFull())
{ System.out.println("Stack overflow\n");
System.exit(1);
}
else array[++top] = item;
}// push
//*********************************************************************
public Object pop()
{ Object result = new Object();
if(isEmpty())
{ System.out.println("Stack underflow\n");
System.exit(1);
}// if(!isEmpty())
else result = array[top--];
return result; //a copy of an array element is returned to keep safe private data.
}//pop
//*********************************************************************
}//class Stack
// file StackTest
import stack.*;
public class StackTest {
public static void main(String args[] )
{ System.out.println("\nExperimenting with a stack of integer numbers:");
Stack A = new Stack(30);
A.push(15); //inserting integer item
A.push(10); //inserting integer item
A.push(25); //inserting integer item
A.push(20); //inserting integer item
Integer a; //a is object of the wrapper class Integer
a = (Integer)A.pop();
System.out.println("popped item = " + a);
//Printing the contents of stack B without changing it.
Integer c;
Stack D = new Stack(30);
while(!A.isEmpty()) //copying from A to D and printing.
{ c = (Integer)A.pop();
System.out.println(" " + c);
D.push(c);
}
while(!D.isEmpty()) //Copying back from D to A.
A.push(D.pop());
System.out.println("\nExperimenting with a stack of strings:");
Stack B = new Stack(30);
B.push("Ali"); //inserting String item
B.push("Sam");
B.push("Bob");
String b;
b = (String)B.pop();
System.out.println("popped item = " + b);
}//main
}//class StackTest
Program output:
Experimenting with a stack of integer numbers:
popped item = 20
25
10
15
Experimenting with a stack of strings:
popped item = Bob
/////////////////////////////////////////////////////////////////////////////////////////////////////////
A pplication of stack data structure for expression evaluation.
Expressions: x+y, A&&B, !w.
Operands: x, y, A, B, w.
Operators: +, && (binary, require 2 operands)
! (unary, requires 1 operand).
Infix notation: x + y# (operator between operands); # is End Of Line symbol.
Prefix notation: +xy# (operator before operands);
Postfix: xy-# (operator after operands).
Converting an expression from infix to postfix expression:
Put all parentheses (brackets) into an infix expression.
Move each operator to its right parenthesis (to its left parenthesis for prefix).
Remove all parentheses.
Example. Given: x + y * z / 3 – 2 * x (infix)
((x + ((y * z) / 3)) – (2 * x))
( (x + ((y * z) / 3) ) – (2 * x) ) (for postfix)
* /+ * -
( (x + ( (y * z) / 3)) – (2 * x)) (for prefix)
- + / * *
x yz * 3 / +2x * - # (postfix) - + x / * yz3 * 2x# (prefix)
Example. Given: a < b && b != 5 || ! c && a > b
(((a < b) && (b != 5)) || ((! c) && (a > b)))
(((a < b) && (b != 5) ) || ((! c) && (a > b) ) ) (for postfix)
< != && ! > && ||
( ( (a < b) && (b != 5)) || ( (! c) && (a > b))) (for prefix)
|| && < != && ! >
a b < b5 != && c ! ab> && ||# (postfix)
| | && < ab != b5 && ! c > ab# (prefix)
Evaluating postfix expressions.
ExpressionType evaluatePostfix(string PostfixExpression) //Algorithm
{ int k = o;
char ch = PostfixExpression[k]; //read 1st symbol from input expression
while(ch != “#”)
{ if(ch is operand) //if ch is not in a list of operators…
push value of ch in stack;
else
if(ch is a binary operator)
{ pop two values from stack;
apply the operator to the popped values; ( before_top_value operator top_value )
push the result on stack;
}
else //ch is unary operator
{ pop one value from stack;
apply the operator to the popped value;
push the result on stack;
}
ch =PostfixExpression[++k]; //read a next symbol
}//while
pop result from stack;
return result;
}// evaluatePostfix
Example. Given infix expression a – b * c + d / a, where a = 7, b = 2, c = 4, d = 21. It is necessary to evaluate it in its postfix notation: abc * - da / + # using evaluatePostfix algorithm.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ch = ‘a’ |
|
|
ch = ‘b’ |
|
|
ch = ‘c’ |
|
4 |
ch = ‘*’ |
|
|
|
push(7) |
|
|
push(2) |
|
2 |
push(4) |
|
2 |
(pop,evaluate,push) |
|
8 |
|
|
|
7 |
|
|
7 |
|
|
7 |
(4,2 2*4 8 ) |
|
7 |
top = -1(empty) top = 0 top = 1 top = 2 top = 1
|
|
|
|
|
|
|
|
|
|
|
|
||
ch = ‘-‘ |
|
|
ch = ‘d’ |
|
|
ch = ‘a’ |
|
7 |
ch = ‘/’ |
|
|
||
(pop, evaluate,push) |
|
|
push(21) |
|
21 |
push(7) |
|
21 |
(pop,evaluate,push) |
|
3 |
||
(8,7 7-8 -1 ) |
|
-1 |
|
|
-1 |
|
|
-1 |
(7,21 21/7 3 ) |
|
-1 |
||
top = 0 top = 1 top = 2 top = 1
|
|
|
|
|
|
|
ch = ‘+’ |
|
|
ch = ‘#’ |
|
|
|
(pop,evaluate,push) |
|
|
pop() |
|
|
|
(3,-1 (-1)+3 2 ) |
|
2 |
|
|
|
returned result = 2 |
top = 0 top = -1 (empty)
Home assignment 1:
Evaluate the same infix expression via its prefix notation using a modified evaluatePostfix algorithm. Hint: Process the prefix expression from right to left.
Home assignment 2:
Apply the evaluatePostfix algorithm to evaluate the postfix expression: ab < bd && cd>|| #.
Topic 5. Linked stacks.
Problems with lists, stacks, queues and other data structures implemented by arrays:
overflow situation if the actual number of data items is larger than the array size;
memory is wasted if the array size is much larger than the actual number of data items.
That is why stacks (and other data structures) are often implemented by linked structures using references to objects in dynamic memory.
For example a stack of 5 integer numbers: top 10, 7, 6, 2, 34 can be pictured as follows :
t op |
|
1 0 |
|
|
7 |
|
|
6 |
|
|
2 |
|
|
34 |
null |
data (data part of each node) next (reference, link part of each node)
reference to the top element (node) nodes of a linked stack
So a linked stack consists of:
a refernce top which is a private data member of each Stack object;
linked nodes which are allocated memory in the dynamic part of a RAM (in a heap) during the execution of a program.
A Random Access Memory (RAM) consists of such parts:
Byte address |
#0 |
#1 |
#2 |
… |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#MAX |
RAM: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OS (Opeating system) UP1 UP2 … UPk heap (dynamic memory) SS (System Stack)
User Program 1…User Program k
Usually a heap is the largest part of the RAM. When a program needs some memory for an object in the heap it asks the OS to allocate this memory by the operator new. If the memory is found the operator new returns the reference to this memory (its address). If the OS cannot find enough memory for the object then new returns the reference value null.
Note: Stacks represented by arrays and stacks represented by linked nodes are different implementations of the same Stack ADT. Operations for such stacks are the same (their algorithms are different). A user programmer does not care how stacks are saved in memory. He/she just declares the objects of the class Stack and calls the member functions of this class. That is why, in application programs, we should use the same class name Stack for both representations of stack objects. But when we study new data structures we prefer to use two class names: Stack for stacks in arrays and StackLinked for stacks implemented as linked nodes.
Here are some pictures that illustrate operations for linked stacks of integer data items. top
Stack A = new Stack(); |
empty) |
A: |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.push(20); |
|
A: |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.push(15); |
|
A: |
|
|
15 |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
int b = A.pop(); |
|
A: |
|
|
20 |
null |
|
b = |
15; |
|
|
|
|
|
|
|
|
|
|
|
|
A.push(30); |
|
A: |
|
|
30 |
|
|
20 |
null |
|
Note:
push() is performed by adding a new node with the data idem at the beginning of a linked stack;
pop() is performed by deleting the very first (top) node of a linked stack.
The algorithms of each operation for linked stacks are simple and quite clear from the Java code.
Class StackLinked and its related classes in Java.
Prepared by Dr. Alex Ossyka
/////////////////////////////////////////
// file Node.java
/////////////////////////////////////////
package stacklinked;
public class Node {
protected Object data;
protected Node next;
public Node(Object d, Node ref)
{ data = new Object();
data = d;
next = ref;
}//constructor with parameters
//*********************************************
public Node()
{ data = new Object();
next = null;
}//constructor
}//Type of a node in a linked stack
///////////////////////////////////////////////
//file StackLinked.java
//////////////////////////////////////////////
package stacklinked;
public class StackLinked {
protected Node top;
//*********************************************
public StackLinked()
{ top = null; }//constructor
//*********************************************
public boolean isEmpty()
{ return (top == null); }
//*********************************************
public boolean isFull()
{ Node new_node = new Node();
return (new_node == null);
}
public void makeEmpty()
{ top = null; }
//*********************************************
public void push(Object item)
{ if(isFull())
{ System.out.println("Stack overflow");
System.exit(1);
}
top = new Node(item, top);
}//push
/* Here is a version of push() function which is easier to trace:
public void push(Object item)
{ if(isFull())
{ System.out.println("Stack overflow");
System.exit(1);
}
// The following 4 statements produce the same result as top = new Node(item, top);
Node p = new Node(); //(1)
p.data = item; //(2)
p.next = top; //(3)
top = p; //(4)
}//push() clarified;
/* At first we explain the meaning of Java notation used in statements (1), (2), (3), (4).
G
iven
stack S: #AB16 top. #7C3D
|
top |
# AB16 |
|
15 |
# 7C3D |
|
20 |
null |
|
top.data top.next (top.next).data (top.next).next
So variable top and next members of each node are reference variables that keep addresses of some objects of class Node. (Address is a hexadecimal number.) We reach members (data and next) of a Node object as it is shown above. Instead of writing (top.next). data = 20; we can simplify the notation:
Node p = top.next;
p.data = 40;
Let us trace the call S.push(30); for the above stack S.
|
t op |
# AB16 |
|
15 |
|
|
20 |
null |
|
(4) #ABCD (4) (3)
p |
# ABCD |
|
30 |
#AB16 |
(1) |
(2) (3)
# ABCD
As a result, the new node is inserted at the beginning of the linked stack.
*/
//*********************************************
public Object pop()
{ if(isEmpty())
{ System.out.println("Stack underflow");
System.exit(1);
}
Object result = new Object(); //Object result; is enough
result = top.data;
top = top.next;
return result; // we return a reference to data part of the deleted node.
} //pop()
}//class StackLinked
///////////////////////////////////////////
//file StackLinkedTest.java
//////////////////////////////////////////
package stacktest;
import stacklinked.*;
public class StackLinkedTest {
public static void main(String[] args)
{
System.out.println("\nExperimenting with a stack of integer numbers:");
StackLinked B = new StackLinked();
B.push(15); //inserting integer item
B.push(10); //inserting integer item
B.push(25); //inserting integer item
Integer a; //a is object of the rapper class Integer
if(!B.isEmpty()) //popping an item from the stack after checking the precondition
{ a = (Integer)B.pop(); // item of class Object is casted into an item of class Integer.
System.out.println("popped item = " + a);
}
else System.out.println("Stack is empty: no item can be popped from it.");
System.out.println("\nExperimenting with a stack of strings:");
StackLinked A = new StackLinked();
A.push("Ali"); //inserting String item
A.push("Sam");
A.push("Bob");
String b;
b = (String)A.pop();
System.out.println("popped item = " + b);
b = (String)A.pop();
System.out.println("popped item = " + b);
}//main
}// class StackLinkedTest
/////////////////////////////////////////////////
// Output of main() function
////////////////////////////////////////////////
Experimenting with a stack of integer numbers:
popped item = 25
Experimenting with a stack of strings:
popped item = Bob
popped item = Sam
Home assignment1. Write a part of a user function main() that reverses the content of a non-empty linked stack. You should use only methods of the class StackLinked.
Home assignment2. Write a Java code of a new member function of the class StackLinked that reverses the sequence of items n a linked stack.
Home assignment3. Write a part of a user function main() that deletes all odd items from a non-empty linked stack with integer data items. Even numbers should follow in the same sequence. You should use only methods of the class StackLinked.
Topic 6. Queue ADT.
Definition: Queue ADT is a set of queues + seat of operations that manipulate queues.
Definition: A queue is a special type of a list where items are added at one end ( called rear) and are deleted at another end ( called front ) of a queue. It is a First In First Out (FIFO) type of a list.
Operations: isFull(), IsEmpty(), makeEmpty(), enqueue() (== enter queue, addItem), dequeue() (==deleteItem).
Representation of objects (queues) in computer memory. Each queue will be represented by an array and three variables:
max which keeps the array size;
front which is the array index of an item for deleting;
rear which is the array index of the last item entered into a queue.
There are some issues to be discussed here. Here are some pictures to illustrate operations enqueue() and dequeue().
|
0 |
1 |
2 |
|
front |
rear |
max |
Empty queue |
|
|
|
|
0 |
-1 |
3 |
enqueue(6); |
6 |
|
|
|
0 |
0 |
3 |
enqueue(9); |
6 |
9 |
|
|
0 |
1 |
3 |
enqueue(7); (Queue is full.) |
6 |
9 |
7 |
|
0 |
2 |
3 |
Note: With each enqueue() only rear must be incremented. |
|||||||
dequeue(); |
|
9 |
7 |
|
1 |
2 |
3 |
dequeue(); |
|
|
7 |
|
2 |
2 |
3 |
dequeue(); (Queue is empty.) |
|
|
|
|
3 |
2 |
3 |
Note: With each dequeue() only front must be incremented. |
|||||||
Now we have a strange situation: there are no items in the queue and not a single item can be added: a next enqueue() operation will write in position #3 (outside the array!!).
Basically with a linear use of an array (when we increment indexes so: rear = rear+1; and front = front +1;) we can enter only 3 (=max) items in a queue. If we want to use the same positions for other items we have to:
shift queue items to the left after each dequeue() operation;
update (decrement) the values of front and rear indexes after each dequeue() operation.
dequeue() operation would be in O(n). It would be slow compared to pop() for stacks (in O(1)).
Note: Below we shall use a different representation of queues by arrays.
In order to use the same array positions for different queue items we shall use an array in a circular way: array[max-1] is followed by array[0]. Now front and rear indexes are incremented so:
front = (front + 1)% max;
rear = (rear + 1)% max;
Note: a % b returns the remainder of integer division of a by b. Examples: 7%3 = 1, 10%3 = 1, 8%3 = 2, 8%5 = 3.
In order to check easily if a queue is empty or full we shall agree:
front points at position before the first queue item. This position is always kept vacant (no useful value there);
rear points at the last queue item.
Examples of operations for circular array queues. array (circular)
|
0 |
1 |
2 |
3 |
|
front |
rear |
max |
Empty queue front == rear is true |
|
|
|
|
|
3 |
3 |
4 |
enqueue(9) |
9 |
|
|
|
|
3 |
0 |
4 |
enqueue(7) |
9 |
7 |
|
|
|
3 |
1 |
4 |
enqueue(8) |
9 |
7 |
8 |
|
|
3 |
2 |
4 |
Queue is full: only array[front] is free. The condition (rear+1)%max == front is true. |
||||||||
dequeue() |
|
7 |
8 |
|
|
0 |
2 |
4 |
dequeue() |
|
|
8 |
|
|
1 |
2 |
4 |
enqueue(5) |
|
|
8 |
5 |
|
1 |
3 |
4 |
enqueue(12) |
12 |
|
8 |
5 |
|
1 |
0 |
4 |
Queue is full again: only array[front] is free. The condition (rear+1)%max == front is true. |
||||||||
Note:
initial values of indexes front and rear are max – 1 if we want to start writing in array from position #0;
enqueue() operation changes only index rear: rear = (rear+1)%max; For example, enqueu(12); { rear = (3+1)%4 = 0; array[rear] = 12; }
dequeue() operation changes only index front: front = (front+1)%max;
a queue is empty if and only if front == rear is true;
a queue is full if and only if (rear + 1)%max == front is true;
a queue is full when only array[front] is vacant. If we enter a new queue item into that position then rear is incremented and front == rear becomes true. It becomes impossible to check if a queue is full or empty.
The algorithm for each circular array queue operation is quite clear from the Java code given below:
Generic class Queue and its related classes
Prepared by Dr. Alex Ossyka
//File Queue.java
package queue;
public class Queue {
protected int max; //array size.
protected int front;//Index of the array position before the first queue item;
protected int rear;//Array index of the last queue item;
protected Object array[];//Reference to an array with Object elements.
//*****************************************************************
public Queue(int n)
{ max = n; //Array size.
front = rear = max - 1;
array = new Object[max]; //assuming that memory is available.
}//constructor with parameter
//*****************************************************************
public Queue()
{ max = 100; //Array size = 100.
front = rear = max - 1;
array = new Object[max];
}//constructor without parameter
//****************************************************************
public boolean isFull()
{ return ((rear + 1)% max == front); }// isFull
//*****************************************************************
public boolean isEmpty()
{ return (front == rear); }// isEmpty
//****************************************************************
public void makeEmpty()
{ front = rear = max - 1; }// makeEmpty
//****************************************************************
public void enqueue(Object item)
{ if(isFull())
{ System.out.println("Queue overflow\n");
System.exit(1);
}
else
{ rear = (rear+1)%max;
array[rear] = item;
}
}// enqueue
//***************************************************************
public Object dequeue()
{ Object result = null;
if(isEmpty())
{ System.out.println("Queue underflow\n");
System.exit(1);
}// if(!isEmpty())
else
{ result = new Object();
front = (front + 1)%max;
result = array[front];
}
return result;
}//dequeue
}//class Queue
//////////////////////////////////////////////////////////////////////////////////// file QueueTest.java
package queue;
public class QueueTest {
public static void main(String args[] )
{
System.out.println("\nExperimenting with a queue of integer numbers:");
Queue A = new Queue(30);
A.enqueue(15); //inserting integer item
A.enqueue(10); //inserting integer item
A.enqueue(25); //inserting integer item
A.enqueue(20); //inserting integer item
Integer a; //a is object of the rapper class Integer
If(!A.isEmptu()) //checking the precondition.
{ a = (Integer)A.dequeue();
System.out.println("deleted item = " + a);
}
else System.out.println("Queue is full: item cannot be deleted. ");
//Printing the contents of queue A without changing it.
Integer c;
Queue D = new Queue(30);
while(!A.isEmpty()) //copying from A to D and printing.
{
c = (Integer)A.dequeue();
System.out.println(" " + c);
D.enqueue(c);
}
while(!D.isEmpty()) //Copying back from D to A.
A.enqueue(D.dequeue());
System.out.println("\nExperimenting with a queue of strings:");
Queue B = new Queue(30);
B.enqueue("Ali"); //inserting String item
B.enqueue("Sam");
B.enqueue("Bob");
String b;
b = (String)B.dequeue();
System.out.println("deleted item = " + b);
}//main
}//class QueueTest
Output of the program:
Experimenting with a queue of integer numbers:
deleted item = 15
10
25
20
Experimenting with a queue of strings:
deleted item = Ali
//////Note: All circular array queue operations require time in O(1).
Home assignment1. Write a part of a user function main() that reverses the sequence of items in a non-empty queue.
Home assignment2. Write a part of a user function main() that deletes all odd items from a non-empty queue with integer items. All even items follow in the same sequence.
Home assignment3. Write a new member function retrieve() of the class Queue that only reads and returns the front item without deleting it.
Topic 7. Linked queues.
Problems with queues implemented by arrays:
problem of overflow (a queue can become larger than the size of its array);
waste of memory (if the size of the array is large but the size of a queue is small).
That is why queues are often implemented by linked structures using references to objects in a heap. For example a queue of 5 integer numbers: front 10, 7, 6, 2, 34 rear can be pictured as follows :
f ront |
|
10 |
|
|
7 |
|
|
6 |
|
|
2 |
|
|
34 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rear |
So a linked queue consists of two reference variables front and rear and linked nodes . The nodes are allocated memory outside the program , in dynamic memory called a heap.
In a linked queue data items are placed in nodes ( objects of the class NodeQ). Such nodes are added ( one by one using enqueue() ) after the node referenced by the rear variable. Each dequeue() operation deletes the node which is currently referenced by the front variable.
In order to implement operations for linked queues (enqueue, dequeue, makeEmpty, isFull, isEmpty, etc.) we have to define the type of a node (class NodeQ) and the class Queue for linked implementation. In this paper the name of this class is QueueLinked. For real application programs we should use the same name of the class Queue for both array queues and linked queues. In such a case, a user program does not need any changes if the representation of queue objects and/or algorithms of operations are changed (by system programmers).
P pictures that illustrate operations for linked queues of integer data items. front data nodes rear
Queue A = new Queue(); |
(empty) |
A: |
null |
|
|
|
|
|
|
|
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.enqueue(20); |
|
A: |
|
|
20 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.enqueue(15); |
|
A: |
|
|
20 |
|
|
15 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int b = A.dequeue(); |
|
A: |
|
|
15 |
null |
|
b = |
20; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int c = A.dequeue(); |
(empty) |
A: |
null |
|
|
|
|
c = |
15; |
|
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.enqueue(25); |
|
A: |
|
|
25 |
null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A.enqueue(30); |
|
A: |
|
|
25 |
|
|
30 |
n ull |
|
|
|
Note:
enqueue() changes the value of only rear reference variable. front is also changed only when the first item (node) is added to a queue.
dequeue() changes only the value of the front reference variable. rear is also changed (to null) when the last data item (node) is deleted from a queue.
The algorithms of each operation for linked queues are simple and quite clear from the Java code.
Java class QueueLinked and its related classes
Prepared by Dr. A. Ossyka
//NodeQ.java
package queuelinked;
public class NodeQ {
protected Object data;
protected NodeQ next;
public NodeQ(Object d, NodeQ ref)
{ data = new Object();
data = d;
next = ref;
}//constructor wiht parameters
public NodeQ()
{ data = new Object();
next = null;
}//constructor
}//Type of a node in a linked queue
///////////////////////////////////////////////////////////////////////////////////
//file QueueLinked.java
package queuelinked;
public class QueueLinked {
protected NodeQ front;
protected NodeQ rear;
//**************************************************************
public QueueLinked()
{ front = rear = null; }//constructor
//**************************************************************
public boolean isEmpty()
{ return (front == null); }
//**************************************************************
public boolean isFull()
{ NodeQ new_node = new NodeQ();
return (new_node == null);
}
//**************************************************************
public void makeEmpty()
{ front = rear = null; }
//**************************************************************
public void enqueue(Object item)
{ if(isFull())
{ System.out.println("Queue overflow");
System.exit(1);
}
if(rear == null) //queue was epty
front = rear = new NodeQ(item, null);
else //queue was not empty
{ rear.next = new NodeQ(item, null);
rear = rear.next;
}//else
}//enqueue
//**************************************************************
public Object dequeue()
{ Object result = new Object();
if(isEmpty())
{ System.out.println("Queue underflow");
System.exit(1);
}
result = front.data;
front = front.next; //We exlude the front node.
if(front == null) //The only node was excluded.
rear = null;
return result; //Reference to the copy of the deleted data.
} //dequeu
}//class QueueLinked
////////////////////////////////////////////////////////////////////////////////////
//file QueueLinkedTest.java
import queuelinked.*;
import javax.swing.JOptionPane; //to use input dialog
public class QueueLinkedTest {
public static void main(String[] args)
{ System.out.println("\nExperimenting with a queue of integer numbers:");
QueueLinked B = new QueueLinked();
// Reading data items from a keyboard to enter them to a queue.
String str = JOptionPane.showInputDialog("Enter number(-1 to finish) ==>");
int n = Integer.parseInt(str);
while(n != -1)
{ B.enqueue(n);
str = JOptionPane.showInputDialog("Enter number(-1 to finish) ==>");
n = Integer.parseInt(str);
}
Integer b; //b is object of the rapper class Integer
while(!B.isEmpty())
{ b = (Integer)B.dequeue();
System.out.println("deleted item = " + b);
}
System.out.println("\nExperimenting with a linked queue of strings:");
QueueLinked A = new QueueLinked();
A.enqueue("Ali"); //inserting String item
A.enqueue("Sam");
A.enqueue("Bob");
String c;
c = (String)A.dequeue();
System.out.println("deleted item = " + c);
c = (String)A.dequeue();
System.out.println("deleted item = " + c);
}//main
}// class StackLinkedTest
Program output:
Experimenting with a queue of integer numbers:
deleted item = 10
deleted item = 25
Experimenting with a linked queue of strings:
deleted item = Ali
deleted item = Sam
Home assignment1. Write a part of main() function that copies a non-empty linked queue A into queue B with items following in the same sequence. Use only methods of the class QueueLinked.
Home assignment2. Write a part of main() function that reverses the sequence of data items in a non-empty linked queue of integer items. Use only methods of the class QueueLinked. Hint: use additional data structure StackLinked.
Home assignment3. Write a new member function size() of the class QueueLinked that counts the number of nodes in a linked queue.
About priority qeueus.
Definition: A priority queue is a queue where items have an additional integer field called priority. The item with the highest priority value is the first to be deleted from the queue (= first served by some process).
Three main methods to implement a priority queue:
array queue which is kept sorted by priority values.
linked queue which is kept sorted by priority values.
A queue stored in a maxheap where the first item (root) has the highest priority value. Other items are not sorted. The root item is deleted and the maxheap structure is restored out of remaining items. We shell discuss this method when we study heapsort algorithm.
Topic 8: Time efficiency of algorithms. The concept of Big-O notation.
We need only fast operation in any data type or data structure. It is important to estimate the run time of a program that implements an operation without writing a computer program and experimenting with it. The concept of Big-O notation gives an idea about an approximate time of execution of a program at the stage of algorithm design.
Definition: The time-efficiency function TA(n) returns an approximate number of time-consuming instructions that algorithm A has to execute in order to produce the result for n input items.
Note: A time-consuming instruction is an instruction that takes (consumes, eats) much time compared to other instructions.
Definitin: O(f(n)) (also called Big-O) gives the smallest upper-bound function f(n) for TA(n). In mathematical terms it is defined so : if TA(n) C’ f(n) then TA(n) O(f(n)). C’ is some constant. (It means: algorithm A belongs to the set of algorithms that execute the number of instructions proportional to f(n).
Big-O gives an idea about the approximate number of instructions to be executed by A. The number of instructions gives some knowledge about the time required by A to perform its task.
Examples should clarify the above definitions.
Example 1: Given algorithm A1. Find its time-efficiency function TA1(n) and its O(f(n)).
x
= x + y; It is “algorithm” A1. In any case, only 3 instruction
are performed here. So TA1(n)
= 3;
y = y +1; Here n represents the initial values of x and y. A1 belongs to the set of fastest
x = x + y + 7; algorithms that execute only a constant number of instructions: it does not matter what is the constant. This set of algorithms is denoted by O(1). So TA1(n) O(1).
Example 2: Given algorithm A2. Find its time-efficiency function TA2(n) and its O(f(n)).
f
or(int
j = 1; j <= n; j++) We assume that only
3 instructions of the loop body and 1
{
x = x + y; instruction after the loop are
time-consuming instructions.
y = y + 1; 3 3n So TA2(n) = 3n +1 O(n).
x = x + y + 7; 3n + 1 We took the fastest growing term 3n from 3n + 1,
} discarded its coefficient 3, and used function n as f(n).
x = x*5; A2 belongs to the set of linear algorithms.
Example 3: Given algorithm A3. Find its time-efficiency function TA3(n) and its O(f(n)).
f
or(int
j = 1; j <= n; j++)
{
for(int
k = 1; k <= n + 3; k++)
{ y
= y + 1;
x = x + y; 3 3(n + 3) 3(n + 3) + 1
y = x + y + 7; n (3(n + 3)+1) n(3(n + 3) + 1) +1
}
x = x*4;
}
y = x + y +4;
So TA3(n) = n(3(n + 3) + 1) +1 = n(3n + 9 + 1) + 1 = 3n2 + 10n + 1 O(n2);
We removed the brackets from the formular, took the fastest growing term 3n2 from 3n2 + 10n + 1, discarded its coefficient 3, and used the function n2 as f(n).
Here are typical Big-O notations (sets of algorithms united by their speed):
O(1) O(log n) O(n) O(n*log n) O(n2) O(n3) … O(2n) O(n!) O(nn) ……
If input size is large algorithms from the set O(1) are faster than algorithms from the set O(log n) – O(1), algorithms from the set O(log n) are faster than the algorithms from the set O(n) – O(log n), and so on.
Algorithms with T(n) in O(1), O(log n), O(n), O(n*log n) are very fast. They can be used as operations for computer data structures. Algorithms from other set are slow. For example, algorithms (coded as programs for the fastest modern computers) with T(n) in O(2n) may need centuries if input size n 100. Here is the graph of rates of growth of typical upper bound functions f(n) used in O(f(n)). It reflects the run time of typical sets of algorithms, implemented as computer programs.
Function value
O(n)
O(n*log n)
O(n2)
O(N3)
O(2n) ……..
O(log n)
O(1)
Input size
Topic 9. Recursion.
A recursive function is a function that calls itself.
1
,
if n= 0;
n! =
n*(n-1)*(n-2)*…*2*1, if n 1.
Recursive definition:
1
,
if n= 0; //base case
n! =
n*(n-1)!, if n 1. //inductive step

irst
0