Lecture Notes - Towson University

advertisement
COSC 600: Advanced Data structures and Algorithm Analysis
Dr.Yanggon Kim
Chapter 3-Lecture Notes
Lists, Stacks and Queues
Abstract Data Types(ADTs):
An ADT is a set of objects together with a set of operations. These data types are mathematical
abstractions and nowhere in ADT’s definition, there is no description of how the set of operations are
implemented.
Abstract Data types can be two types based on their access to the data.
1.
Linear ADT: Linear ADTs can be two types again
● Direct Access Linear ADT which can be either a homogenous or a heterogenous one EX: Arrays,
Sets
● Sequential Access Linear ADT EX: List(stacks-FILO , Queues-FIFO)
2.
Non- Linear ADT.
NOTE: When the relationships between the elements is logical then is called a linear relationship.
The following are the few other examples of the ADTs.
1. Graphs which have vertices and edges.
2. Tree (it is a speciality graph and it does not have cycle).
3. Binary tree is a speciality case of Tree
4. Binary search tree is a speciality of Binary tree
5. Balanced binary search tree is a speciality of Binary Search tree.
LISTS:
List is a homogenous collection of elements with linear relationship between the elements. Lists can be of
two types.
●
●
Sorted Lists
Unsorted Lists
Operations that can be performed on Lists are as follows:
● Printlist()
● makeempty()
● find()/Search()/
● Insert()
●
Delete/remove()
IMPLEMENTATION OF THE OPERATIONS ON LIST:
Lists can be implemented in using the following data types
1.
2.
Arrays
Linked lists (single or double)
Implementing the lists using arrays:
All the operations on the list can be performed using arrays. All the capacity of the arrays is fixed , we can
create the arrays with double the capacity when needed. The maximum size estimate for the array is not
required in java or any other modern programming language.
1. Arrays become a good option to implement lists where the lists are build up by insertions at the
high end.
2. If insertions and deletions occur throughout the list then array is not a good option.
Operation
Sorted Array
Unsorted array
Printlist()
O(N)
O(N)
Find()
O(logN)
O(N)
Insert()
O(N)
O(1)
Delete()
O(N)
O(N)
Implementing the lists using the Linked lists:
Linked lists consists of series of nodes which are not necessarily adjacent in memory. Each node
contains the element and a link to the next node. The last cell next reference would be null. Linked lists
help in reducing the time complexity for the insert and delete operations.
Operation
sorted linked list
unsorted linked list
print()
O(N)
O(N)
find()
O(N)
O(N)
insert()
O(1)
O(1)
delete()
O(N)/O(1)
O(N)/O(1)
Note : If the previous node information is present then the time complexity for the delete option becomes
O(1).
STACKS(LIFO):
A stack is a list where the insertions and deletions can be performed at one position, that is at the end of
the list, called the top. They are Last In First Out lists.
Operations performed on a Stack:
1.
2.
3.
push - equivalent to insert
pop - equivalent to delete
top- which finds the element just inserted.
In stack, only the top element is visible and that is the only element accessible to perform any operation
on stack. All the stack operations are constant time operations.
Since stacks are lists , they can either be implemented by arraylists or linked lists.
Linked List implementation of Stacks:
It uses single linked lists.
1.
2.
3.
4.
5.
Push - inserts an element at the front of the list
Pop- deletes an element at the front of the list
top - examines the element at the front of the list
IsEmpty()-returns boolean
Isfull()-returns boolean.
Array implementation of the stacks:
Here, the operations are performed at a very fast constant time. Each stack array has theArray() and the
topOfstack which is -1 for the empty stack.
To insert some element into the stack, we incerement the topOfstack and then set the
thearray[topOfstack]=x.
To pop, we set the return value to the theArray[topOfArray] and then decrement the topOfstack.
Applications of Stacks:
Compilers, game theory, all machines and back-tracking[maze problem]
Also converting the infix arithmetic expressions to the postfix arithmetic expressions uses stack ADT.
Priorities of the operators:(decreasing order of priority)
1.
2.
3.
4.
5.
6.
7.
unary operator(--,++)
*,/,%,div
+,==,!=,<,>,<=,>=
Boolean
and/OR
(
Algorithm to convert an infix expression to a post fix expression:
1.
2.
Initialize a stack
While (error/endof the stack)
● read the token
● if the token is operand display it.
● if the token is ( push it into the stack
● if the token is ) pop and display the stack element until ( is encounered.
● if the token is an operator
❖ if the stack is empty or the token as higher priority, then pop the top
stack element out , and push the token into the stack
❖ else(otehrwise)
pop and display the top item.
(the above 2 steps should be done repeatedly)
3.
At the end of the expression, pop and display stack items until stack is empty.
Example: (((A-B)-C)-D*E/F+(G-H)) this is an infix expression
AB-C-DE*F/-GF-+ Post fix expression.
Below figure shows how the algorithm is implemented to convert the expression from infix to post fix.
STEP:1 First token is (, its an operator so , its pushed into the stack
-
(
(
(
(
(
(
(
*
+
_
(
(
(
(
(
(
(
(
.Step 2: Second token is again (, priority is compared with the elements in the stack. if its equal or less
its pushed into the stack.
Step 3: Third token is again (, again priority is matched and its pushed into the stack
Step 4: Fourth token is an operand , so it is displayed.
A
Step 5: Fifth token is -, its an operator and its priority is higher than elements in stack and its pushed into
the stack
Step 6: token 6 is operand B, so its displayed
AB
Step 7: token 7 is ), now all the elements in the stack are popped out until an ( is encountered and its
deleted from the stack
ABStep 8: 8th token is operator -, so its priority is checked and pushed into stack
Step 9: 9th token is C, its an operand and its displayed
AB-C
Step 10: Next token is ), so the elements in the stack are check and all are displayed untill (, is
encountered. so the expression becomes
AB-CStep 11: Next token is D, an operand so it is displayed
AB-C-D
Step 12: Next token is * , an operand so priority is compared and since its higher, push into the stack
Step 13: Next token is E , its an operand so display it.
AB-C-DE
Step 14: Next token is /, it has same priority as *, pop the top element out and then display this operator
also..
AB-C-DE*
Step 15: Next token is F , its operand so display it
AB-C-DE*F
Step 16: Next token is +, compared to the existing elements and \ is popped out
AB-C-DE*F/
Step 17: Next token is (, so it should be pushed into the stack and - is popped out.
AB-C-DE*F/Step 18: next token is G , and its displayed
AB-C-DE*F/-G
Step 19 : Next token is - , its operator and pushed into the stack
Step 20: Next token is H , its operand and displayed.
AB-C-DE*F/-GH
Step 21: Next token is ), so the elements in the stack are popped out until ( is encountered.
AB-C-DE*F/-GH-+
When the above procedure is followed for the entire infix expression, it gets converted to postfix
expression.
STACKS(LIFO):
With a queue, however, insertion is done at one end and deletion is done at the other end.
Operations:
Enqueue ………….. O(1)
Dequeue ………….. O(1)
IsEmpty …………... O(1)
IsFull ……………….O(1)
Queue
Dequeue
<----------------
enqueue
<-------------------
Enqueue : which inserts an element at the end of the list called the rear.
Dequeue: which deletes the element at the start of the list called as front.
Array implementation of Queues: Array vs Single Linked list
Like stacks, both the linked list and array implementation gives O(1) running times for
every operation.
Example: For each queue data structure we keep an array “thearray”
positions “front” and “back” which represents the ends of the queue
number of elements in the queue “currentsize”
To enqueue an element “X”, increment currentsize and back then set thearray[back]= X
To dequeue an element , set the return value to thearray[front], decrement currentsize and
then increment front
There appears a problem with this implementation, after several enqueues the queue will
be full since back is now at the last array index, the next queue would be in a non existing
position.
The simple solution to this problem is that whenever front or back gets to the end of array,
it is wrapped around to the beginning, known as circular array implementation.
Initial state
2
4
↑
front
↑
back
After enqueue(1)
1
↑
back
2
↑
front
4
After enqueue(3)
1
3
↑
back
2
↑
front
4
2
4
↑
front
2
4
2
4
2
4
After dequeue, which returns 2
1
3
↑
back
After dequeue, which returns 4
1
3
↑
↑
front
back
After dequeue, which returns 1
1
3
↑
back
front
After dequeue, which returns 3 and makes queue empty
1
3
↑
↑
back front
How to distinguish queue is empty to queue is full?
For array, use upto n-1 items
Use “counter” to keep track of # number of items in a queue
If counter=0 array is full
If counter=1 array is empty
Applications of Queues:
There are many algorithms to give efficient running times.
1)Virtually every real life line is a queue. For instance, lines at ticket counters are queues,
becoz it is first come first served
2)calls to large companies are generally placed on a queue when all operators are busy
3)Jobs sent to a line printer are placed on a queue
Download