0% found this document useful (0 votes)
169 views54 pages

Assignment 2 Front Sheet: Qualification BTEC Level 5 HND Diploma in Computing

This document contains an assignment front sheet for a unit on data structures and algorithms. It includes information like the student's name and ID, assessor name, unit details, and requires the student's signature to confirm the work is their own. The grading grid lists the pass criteria that will be used to evaluate the assignment and spaces for assessor and internal verifier feedback and signatures.

Uploaded by

Đỗ Hoài Nam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
169 views54 pages

Assignment 2 Front Sheet: Qualification BTEC Level 5 HND Diploma in Computing

This document contains an assignment front sheet for a unit on data structures and algorithms. It includes information like the student's name and ID, assessor name, unit details, and requires the student's signature to confirm the work is their own. The grading grid lists the pass criteria that will be used to evaluate the assignment and spaces for assessor and internal verifier feedback and signatures.

Uploaded by

Đỗ Hoài Nam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 54

ASSIGNMENT 2 FRONT SHEET

Qualification BTEC Level 5 HND Diploma in Computing

Unit number and title Unit 19: Data Structures and Algorithms

Submission date Date Received 1st submission

Re-submission Date Date Received 2nd submission

Student Name Student ID

Class Assessor name

Student declaration

I certify that the assignment submission is entirely my own work and I fully understand the consequences of plagiarism. I understand that
making a false declaration is a form of malpractice.

Student’s signature

Grading grid

P4 P5 P6 P7 M4 M5 D3 D4

Page. 1
Page. 2
Summative Feedback: Resubmission Feedback:

Grade: Assessor Signature: Date:


Internal Verifier’s Comments:

IV Signature:

Page. 3
Table of Contents

Table of Contents ................................................................................................................................ 4

Table of Figures .................................................................................................................................. 6

Introduction........................................................................................................................................ 8

1. Design and implementation of Stack ADT and Queue ADT (P4) ........................................................ 9

1.1. Stack ADT .............................................................................................................................................. 9

1.2. Queue Data Structure .......................................................................................................................... 11

1.3. Code Implementation .......................................................................................................................... 13

2. Application ................................................................................................................................... 16

2.1. Introduction ........................................................................................................................................ 16

2.2. Implementation ................................................................................................................................... 16

2.3. Explanation ......................................................................................................................................... 19


2.3.1. Time Complexity of Operations in Abstract Data Types ............................................................................................. 19
2.3.2. Problem 1: Reverse a Queue using a Stack................................................................................................................. 21
2.3.3. Problem 2: Compare Two Stacks Preserve Data ........................................................................................................ 26

3. Error Handling .............................................................................................................................. 30

3.1. Testing Plan ......................................................................................................................................... 30

3.2. Evaluation ........................................................................................................................................... 43

4. How Asymptotic Analysis Measure Effectiveness of an Algorithm .................................................. 44

4.1. Theta Notation (Θ-notation)................................................................................................................. 45

4.2. Big-O Notation (O-notation) ................................................................................................................. 46

4.3. Omega Notation (Ω-notation) .............................................................................................................. 47

5. Two Ways Measuring Efficiency of An Algorithm ....................................................................... 48

Page. 4
5.1 Time efficiency...................................................................................................................................... 48

5.2 Space efficiency .................................................................................................................................... 50

6. Trade-off When Specifying an ADT ................................................................................................ 51

7. Benefits of Using Implementation Independent Data Structures..................................................... 52

Conclusion ........................................................................................................................................ 53

References ........................................................................................................................................ 54

Page. 5
Table of Figures
Figure 1. Stack Data Structure ........................................................................................................................ 9
Figure 2. Stack push operation ..................................................................................................................... 10
Figure 3. Stack pop operation ...................................................................................................................... 10
Figure 4. Stack top operation ....................................................................................................................... 10
Figure 5. Queue ............................................................................................................................................ 11
Figure 6. enqueue ......................................................................................................................................... 12
Figure 7. dequeue ......................................................................................................................................... 12
Figure 8. Stack class ...................................................................................................................................... 14
Figure 9. Queue Class ................................................................................................................................... 15
Figure 10. Main program .............................................................................................................................. 18
Figure 11. Linked List .................................................................................................................................... 19
Figure 12. Doubly Linked List ........................................................................................................................ 20
Figure 13. Reverse Queue Function ............................................................................................................. 21
Figure 14. Move items from a Queue to a Stack .......................................................................................... 21
Figure 15. Dequeue process ......................................................................................................................... 22
Figure 16. Stack after dequeued .................................................................................................................. 22
Figure 17. Reverse a queue by moving data from FIFO -> LIFO -> FIFO....................................................... 23
Figure 18. A Stack is created for storing elements ....................................................................................... 24
Figure 19. De-queue all elements form the Queue and Push them to the Stack ........................................ 24
Figure 20. Pop all items from Stack and Push them back into the Queue ................................................... 24
Figure 21. Popping out items from Stacks and Push them to temporary stacks ......................................... 26
Figure 22. Pop out remaining elements and push them to the temporary stack ........................................ 26
Figure 23. Push elements back to their original Stack ................................................................................. 27
Figure 24. Checking for length and top value of two stacks. ....................................................................... 28
Figure 25. Initiate two Blank Stacks (Temporary Stacks) ............................................................................. 28
Figure 26. Checking elements in two Stacks ................................................................................................ 28
Figure 27. Push back elements to their original stacks ................................................................................ 29
Figure 28. Theta Notation Expression .......................................................................................................... 45

Page. 6
Figure 29. Theta Notation factors ................................................................................................................ 45
Figure 30. Big-O Notation Expression........................................................................................................... 46
Figure 31. Big-O Notation Factors ................................................................................................................ 46
Figure 32. Omega Notation Expression ........................................................................................................ 47
Figure 33. Omega Notation Factors ............................................................................................................. 47

Page. 7
Introduction
As a developer, the author has been requested for design and implement the two data structures that
were briefly described in the previous assignment, Stack and Queue. Two data structures should
demonstrate some important operations of these data structures.

The project is built for these two questions:

• Write a function to reverse the order of a queue using a stack. Include proper annotations in your
solution. What is the worst-case run time complexity of this function if there are n elements in the
queue? Explain.
• Write a function that takes two stacks and returns true if the stacks contain the same elements in
the same order. After the operations, both stacks must remain in the original order. You are
allowed to use additional stacks or queues as needed, but no other data structures. What is the
worst-case run time complexity of your code if each stack contains n elements? Explain.

In this document, the author is going to show the implementation and explanation about the development
process of the project.

Page. 8
1. Design and implementation of Stack ADT and Queue ADT (P4)
1.1. Stack ADT

Figure 1. Stack Data Structure

A stack is a container of objects that are inserted and removed according to the last-in first-out (LIFO)
principle. In the pushdown stacks only two operations are allowed: push the item into the stack, and pop
the item out of the stack. A stack is a limited access data structure - elements can be added and removed
from the stack only at the top. push adds an item to the top of the stack, pop removes the item from the
top. A helpful analogy is to think of a stack of books; you can remove only the top book, also you can add
a new book on the top. (S.Adamchik, 2009)

In simplified, stack is a linear data structure in a one-way order. The order can be in LIFO or FILO (however,
they seem to be similar).

Stack is a concept which is used mainly in data structures. It comes along with array concept for data
inserting, modifying and recovering.

Main operations of Stack are:

• Push (Item i) : Boolean


• Pop () : Boolean
• Top () : Item i

Page. 9
Push (Item i) : Boolean

Push operation is used for inputting an item to the Stack. The operation
will add or insert an item to the Stack whether the Stack is still empty.
Once the Stack is full (full of memory), the operation will notice that the
Stack is overflow so cannot add anymore.

Figure 2. Stack push operation

Pop () : Boolean

Pop operation is used for taking (or removing) an item from the Stack.
Pop operation will be performed no matter the stack is full or not, but
will not perform when the Stack is empty.

Once the Stack is empty, it will notice that the Stack has no item in it.

Figure 3. Stack pop operation


Top () : Item i

Top operation is about taking the topmost item from the Stack.

Figure 4. Stack top operation

Some more operations that can perform on a Stack:

- isFull: Checking whether Stack is full or not.


- isEmpty: Checking whether Stack is empty or not.
- Destroy: Delete all items of Stack.

There’re many applications of Stack in real life. The simplest application of Stack is for reversing a word by
putting it in a Stack.

Page. 10
1.2. Queue Data Structure
A queue is a Data Structure that’s similar to the ticket queue in fast-food store where the first entering
person will be the first one who get the goods.

Queue follows FIFO rule – item goes in first will be firstly processed.

Figure 5. Queue

Queue operations work as follows:

• two pointers FRONT and REAR


• FRONT track the first element of the queue
• REAR track the last elements of the queue
• initially, set value of FRONT and REAR to -1

Main operations of Queue are:

• bool enqueue([in] item)


Append an item to the rear of the queue, returning true if successful, false if not.
• bool dequeue()
Remove the item from the front of the queue, returning item if successful and null otherwise.
• item front()
Return the item at the front of the queue to the caller without removing it
• int size()
Return the number of items in the queue.

Page. 11
Enqueue Operation

• check if the queue is full


• for the first element, set value of FRONT to 0
• increase the REAR index by 1
• add the new element in the position pointed to by REAR

Figure 6. enqueue

Dequeue Operation

• check if the queue is empty


• return the value pointed by FRONT
• increase the FRONT index by 1
• for the last element, reset the values of FRONT and REAR
to -1

Figure 7. dequeue

Page. 12
1.3. Code Implementation

1. package com.nstudio.assignment_2;
2.
3. public class Stack<T> {
4. private class Node {
5. T data;
6. Node prev;
7.
8. Node(T data) {
9. this.data = data;
10. }
11. }
12.
13. private int maxLen;
14. private int curLen;
15. private Node top;
16.
17. public Stack(int maxLen) {
18. this.maxLen = maxLen;
19. this.curLen = 0;
20. }
21.
22. public boolean push(T data) {
23. if (curLen < maxLen) {
24. curLen ++;
25. Node newNode = new Node(data);
26. if (top != null) newNode.prev = top;
27. top = newNode;
28. return true;
29. } else {
30. return false;
31. }
32. }
33.
34. public T pop() {
35. Node r = top;
36. top = top.prev;
37. curLen--;
38. return r.data;
39. }
40.
41. public T top() {
42. return top.data;
43. }
44.
45. public int countItems() {
46. return curLen;
47. }
48.
49. public boolean isEmpty() {
50. return top == null;
51. }
52.
53. public void printAllNodes() {
54. Node curNode = top;
55. while (curNode != null) {

Page. 13
56. System.out.print(curNode.data + " -> ");
57. curNode = curNode.prev;
58. }
59. System.out.println();
60. }
61. }

Figure 8. Stack class

1. package com.nstudio.assignment_2;
2.
3. public class Queue<T> {
4. private class Node {
5. T data;
6. Node next;
7. Node prev;
8. Node(T data) {
9. this.data = data;
10. next = null;
11. prev = null;
12. }
13. }
14.
15. Node head;
16. Node tail;
17. int maxLen;
18. int curLen;
19. public Queue (int maxLen) {
20. this.head = null;
21. this.tail = null;
22. this.maxLen = maxLen;
23. this.curLen = 0;
24. }
25. public boolean enQueue(T data) {
26. if (curLen < maxLen) {
27. Node newNode = new Node(data);
28. if (head == null || tail == null) {
29. head = tail = newNode;
30. head.next = tail;
31. tail.prev = head;
32. head.prev = null;
33. tail.next = null;
34. } else {
35. newNode.prev = tail;
36. tail.next = newNode;
37. tail = newNode;
38. }
39. curLen ++;
40. return true;
41. } else
42. return false;
43. }
44. public T deQueue() {
45. if (hasItem()) {
46. Node r = head;
47. head = head.next;
48. if (head != null) head.prev = null;

Page. 14
49. curLen --;
50. return r.data;
51. } else {
52. return null;
53. }
54. }
55. public boolean hasItem() {
56. return head != null && tail != null;
57. }
58. public int getLength() { return curLen; }
59.
60. public void printAllNodes() {
61. Node curNode = head;
62. while (curNode != null) {
63. System.out.print(" <- " + curNode.data);
64. curNode = curNode.next;
65. }
66. System.out.println();
67. }
68. }

Figure 9. Queue Class

Page. 15
2. Application
2.1. Introduction
Problem 1: Write a function to reverse the order of a queue using a stack. Include proper annotations in
your solution. What is the worst-case run time complexity of this function if there are n elements in the
queue? Explain.

Problem 2: Write a function that takes two stacks and returns true if the stacks contain the same elements
in the same order. After the operations, both stacks must remain in the original order. You are allowed to
use additional stacks or queues as needed, but no other data structures. What is the worst-case run time
complexity of your code if each stack contains n elements? Explain.

2.2. Implementation
Code implement in Java:

Page. 16
1. package com.nstudio.assignment_2;
2.
3. public class Main {
4. static int dataLength = 20;
5.
6. public static void main(String[] args) {
7.
8. Queue<Integer> myQueue = new Queue<Integer>(dataLength);
9.
10. myQueue.enQueue(1);
11. myQueue.enQueue(2);
12. myQueue.enQueue(3);
13. myQueue.enQueue(4);
14. System.out.println("Current Queue:");
15. myQueue.printAllNodes();
16. System.out.println("Reversed Queue is:");
17. reverseQueue(myQueue);
18. myQueue.printAllNodes();
19.
20. Stack<Integer> s = new Stack<Integer>(20);
21. s.push(2);
22. s.push(3);
23. s.push(4);
24.
25. System.out.println("Stack s:");
26. s.printAllNodes();
27.
28. Stack<Integer> s1 = new Stack<Integer>(20);
29. s1.push(2);
30. s1.push(3);
31. s1.push(4);
32. s1.push(5);
33. s1.push(6);
34.
35. System.out.println("Stack s1:");
36. s1.printAllNodes();
37.
38. System.out.println(compareStacks(s, s1) ? "stack is equal" : "stack is not equal");
39. s.printAllNodes();
40. s1.printAllNodes();
41.
42. }
43.
44. public static void reverseQueue(Queue<Integer> myQueue) {
45. Stack<Integer> myStack = new Stack<Integer>(myQueue.getLength());
46. while (myQueue.hasItem()) {
47. int item = myQueue.deQueue();
48. myStack.push(item);
49. }
50. while (!myStack.isEmpty()) {
51. int item = myStack.pop();
52. myQueue.enQueue(item);
53. }
54. }
55.
56. public static boolean compareStacks(Stack<Integer> stack1, Stack<Integer> stack2) {
57. if (stack1.countItems() != stack2.countItems()) return false;
58. if (!stack1.top().equals(stack2.top())) return false;
59. Stack<Integer> tempStack1 = new Stack<>(dataLength);
60. Stack<Integer> tempStack2 = new Stack<>(dataLength);
61.

Page. 17
62. boolean isEqual = true;
63. while (!stack1.isEmpty()) {
64. int poppedValue1 = stack1.pop();
65. int poppedValue2 = stack2.pop();
66. if (poppedValue1 == poppedValue2) {
67. tempStack1.push(poppedValue1);
68. tempStack2.push(poppedValue2);
69. } else {
70. isEqual = false;
71. break;
72. }
73. }
74.
75. while (!stack1.isEmpty()) tempStack1.push(stack1.pop());
76. while (!stack2.isEmpty()) tempStack2.push(stack2.pop());
77.
78. while (!tempStack1.isEmpty()) stack1.push(tempStack1.pop());
79. while (!tempStack2.isEmpty()) stack2.push(tempStack2.pop());
80. return isEqual;
81. }
82. }

Figure 10. Main program

Page. 18
2.3. Explanation
By applying the above source code, the author has successfully achieved the requirements of the topic in
this document.

2.3.1. Time Complexity of Operations in Abstract Data Types


Before implementing algorithm into the problem, this section will be used to discuss about time
complexity of ADTs before using them in the algorithms. The time complexity of ADT affects significantly
on the performance of the Algorithm since it’s a part of the algorithm used in this document.

2.3.1.1. Stack ADT


Firstly, the Stack is using Linked List. The benefits of using Linked List is that the program will have dynamic
structure.

Figure 11. Linked List

Not same as previously discussed in “Assignment 1 Document”, instead of saving head node, for better
performance, the ADT will save the last node of the Stack (top node). And each node saves the link to the
previous node of that node.

This will makes Pop Operation and Push Operation have Time Complexity O (1) since the program only
has to append a node and removing last node of the Stack instead of looping through the Stack from the
first node to last node as in previous document.

There’s no more Space used for temporary value, so the Space Complexity is O (1).

Page. 19
2.3.1.2. Queue ADT
Same as Stack ADT, Queue ADT implemented in this project also using Linked List to store data. However,
different from Stack, the Queue ADT uses Doubly Linked List.

Doubly Linked List has all nodes save both pointer of previous node and next node.

Figure 12. Doubly Linked List

By using Doubly Linked List, the program can easily navigate to the first and last item (in the queue) without
looping through data structure’s length to find the needed position.

The Queue ADT initialized with front and rear node saved as default. This preserves that Time Complexity
of enqueue and dequeue Operations are O (1).

This implementation also doesn’t use more space to store temporary value, so the Space Complexity also
is O (1).

Page. 20
2.3.2. Problem 1: Reverse a Queue using a Stack
As previously discussed, the Queue follows FIFO while Stack is LIFO structured. In this Source Code, a
function has been created to solve the problem:

1. public static void reverseQueue(Queue<Integer> myQueue) {


2. Stack<Integer> myStack = new Stack<Integer>(myQueue.getLength());
3. while (myQueue.hasItem()) {
4. int item = myQueue.deQueue();
5. myStack.push(item);
6. }
7. while (!myStack.isEmpty()) {
8. int item = myStack.pop();
9. myQueue.enQueue(item);
10. }
11. }

Figure 13. Reverse Queue Function

2.3.3.1. Algorithm
The main working mechanism used here is to de-queue all items from the Queue and push them all to the
Stack. Since the working orientation of two Data Structure are opposite, so when the program enqueue
them again into the queue, all items will be in a reversed order.

Figure 14. Move items from a Queue to a Stack

As in this example, a Queue which is having value of “H E L L O” (in FIFO), will sequency take out items in
this order:

Page. 21
Figure 15. Dequeue process

So, the taken-out string will be the same as Queue displaying orders: H E L L O.

After dequeued, the Character is pushed into the stack right away in dequeue order (HELLO). So the Stack
now is:

Figure 16. Stack after dequeued

The data will stay in this order: O L L E H.

Page. 22
This order is also the orientation when Stack items are popped out. The FIFO keeps the orientation of the
item inserted into the data structure while LIFO reverses it. So, the final data of the Queue when the Stack
popped and enqueued all items:

Figure 17. Reverse a queue by moving data from FIFO -> LIFO -> FIFO

By moving data from FIFO to LIFO then back to FIFO, the author can achieve reversing elements in a queue
of data.

Page. 23
2.3.3.2. Code Explanation
Following the algorithm previously discussed, the program created a Stack for storing Queue elements
after popped them all.

1. Stack<Integer> myStack = new Stack<Integer>(myQueue.getLength());

Figure 18. A Stack is created for storing elements

The length of the Stack is same as the Queue length (actually, the program is setting max Length of the
queue. However, for strict purpose, the max length will have same length as queue length).

Then, the program will take out all elements in the queue and push them to the Stack.

1. while (myQueue.hasItem()) {
2. int item = myQueue.deQueue();
3. myStack.push(item);
4. }

Figure 19. De-queue all elements form the Queue and Push them to the Stack

In this part, the order of items has been reversed. First element previously stored in the Queue now is the
last item stored (and will be taken out) in the Stack.

Then, the program will pop out all items in the Stack and enqueue back to the Queue.

1. while (!myStack.isEmpty()) {
2. int item = myStack.pop();
3. myQueue.enQueue(item);
4. }

Figure 20. Pop all items from Stack and Push them back into the Queue

Since FIFO doesn’t affect on the order of elements, the data in the Queue is now same order as the way
they are stored in the Stack.

In this step, the Queue has been successfully reversed using a Stack.

Page. 24
2.3.3.3. Time Complexity & Space Complexity
It can be separated the algorithm into 2 steps:

- Enqueue out all items in the queue and push into the stack.
- Pop all the items form the stack and enqueue back into the queue.

The dequeue and push process takes n steps, while the pop process and enqueue also take n steps more.

So, the Time Complexity of the algorithm is O (n + n) = O (n).

Since the program uses a stack more to store value, the Space Complexity of the algorithm is O (n).

Page. 25
2.3.3. Problem 2: Compare Two Stacks Preserve Data
2.3.3.1. Algorithm
The method used is about getting out all data from the two Stacks and push them into two Temporary
Stacks. In popping out process, the program will also compare the value of popped out value. When found
a popped-out pair that is not having same value, it will return false and pop and push remaining elements
in two Stacks into those Temporary Stacks.

Figure 21. Popping out items from Stacks and Push them to temporary stacks

Figure 22. Pop out remaining elements and push them to the temporary stack

Page. 26
Finally, putting elements back to the two original Stacks.

Figure 23. Push elements back to their original Stack

In case the program cannot find any elements that’s not equal (all elements are equal), then return true.

For better performance, there’re something that will make the algorithm performs faster:

- Check the length of two stacks.


- Check the value of two top elements.

If the two stacks fail one of two requirements (or both), the algorithm stops since two stacks must have
same elements and size.

Page. 27
2.3.3.2. Code Explanation
Firstly, the program will check for the length of the two stacks. If they don’t have same length, the
algorithm will stop with false value returned.

1. if (stack1.countItems() != stack2.countItems()) return false;


2. if (!stack1.top().equals(stack2.top())) return false;

Figure 24. Checking for length and top value of two stacks.

Otherwise, the program will continue and check for the top element of two stack.

Next, the program initiates two Blank Stacks (temporary stacks – same size as the input stacks).

1. Stack<Integer> tempStack1 = new Stack<>(dataLength);


2. Stack<Integer> tempStack2 = new Stack<>(dataLength);

Figure 25. Initiate two Blank Stacks (Temporary Stacks)

Since the program cannot stop right away when it found an un-equal pair of values that taken from Stacks
(it will break the data and not preserve the two stacks as its original state), a variable is required to record
the final return of the function: boolean isEqual = true;

Because of the length of two stacks are equal (previously checked in the first step), the loop through the
data only has to traverse while one of two stacks are not blank. Every step, the program pops out value
from the two stacks and push them into temporary stacks before checking for the value. By pushing value
to temporary stacks before continue, the algorithm avoids breaking data or missing records.

Next, in case two value is not the same, the return variable will be set to false and the loop will be broken.

1. while (!stack1.isEmpty()) {
2. int poppedValue1 = stack1.pop();
3. int poppedValue2 = stack2.pop();
4. tempStack1.push(poppedValue1);
5. tempStack2.push(poppedValue2);
6. if (poppedValue1 != poppedValue2) isEqual = false;
7. }

Figure 26. Checking elements in two Stacks

After have done the result, the program needs to put back the state of two stacks to their original. So, all
elements in temporary stacks will be push back to the original stacks:

Page. 28
1. while (!tempStack1.isEmpty()) {
2. stack1.push(tempStack1.pop());
3. stack2.push(tempStack2.pop());
4. }

Figure 27. Push back elements to their original stacks

And finally, the program only has to return the value: return isEqual;

2.3.3.3. Time Complexity & Space Complexity


The process of the algorithm can be separated into steps:

- Popping out all items from two stacks and push them to the temporary stacks.
- Push all items from the temporary stacks back to the original stacks.

First step takes O (n), second step also takes O (n).

So, this algorithm has Time Complexity = O (n + n) = O (n).

For Space Complexity, two more stacks are used to store values.

So, this algorithm has Space Complexity = O (n).

Page. 29
3. Error Handling
3.1. Testing Plan
Firstly, initialize a Stack (MyStack) with max size of 3 and a Queue (MyQueue) with max size of 3.

Input Output

N. Scope Operation Testing Type Data Expected Actual Status


MyStack = []; MyStack =
Push Normal MyStack = [3] Pass
Push (3) [3]
Test code:

Test result:

MyStack = [3]; MyStack = MyStack =


Push Normal Pass
Push (4) [3, 4] [3, 4]
Stack ADT: Test code:
MyStack

Test result:

MyStack =
Push Data Validation [3, 4, 5] False False Pass
3 Push (6)
Test code:

Page. 30
Test result:

MyStack =
Pop Normal 5 5 Pass
[3, 4, 5]
Test code:

Test result:

MyStack =
Pop Normal 3 3 Pass
[3]
Test code:

Test result:

Failed to
Pop Data Validation MyStack = [] null Fail
execute
Test code:

Page. 31
Test result:

MyStack = [3,
top Normal 4 4 Pass
4]
Test code:

Test result:

Failed to
top Data Validation MyStack = [] null Fail
execute
Test code:

Test result:

MyStack = [3,
countItems Normal 2 2 Pass
4]
Test code:

Test result:

countItems Data Validation MyStack = [] 0 0 Pass


10
Test code:

Page. 32
Test result:

MyStack = [3,
printAllNodes Normal 0 0 Pass
4]
Test code:

13

Test result:

This stack is Nothing


printAllNodes Data Validation MyStack = [] Fail
empty printed out
Test code:

14

Test result:

MyQueue = [] MyQueue = MyQueue =


enQueue Normal Pass
enQueue(3) [3] [3]
Test code:

Queue ADT:
15
MyQueue

Page. 33
Test result:

MyQueue = [3] MyQueue = MyQueue =


enQueue Normal Pass
enQueue(4) [3, 4] [3, 4]
Test code:

16

Test result:

MyQueue =
enQueue Data Validation [3, 4, 5] False False Fail
enQueue(1)
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(3);
myQueue.enQueue(3);
myQueue.enQueue(4);
17 myQueue.enQueue(5);
if (!myQueue.enQueue(1)) System.out.println("Failed to enqueue.");
myQueue.printAllNodes();
}
}
Test result:

MyQueue =
deQueue Normal 3 3 Pass
[3, 4]
Test code:

18

Test result:

Page. 34
deQueue Normal MyQueue = [4] 4 4 Pass
Test code:

19

Test result:

deQueue Data Validation MyQueue = [] null null Pass


Test code:

20

Test result:

getLength Normal MyQueue = [4] 1 1 Pass


Test code:

21

Test result:

getLength Data Validation MyQueue = [] 0 0 Pass


Test code:

22

Test result:

MyQueue =
23 hasItem Normal True True Pass
[3, 4, 5]

Page. 35
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(3);
myQueue.enQueue(3);
myQueue.enQueue(4);
myQueue.enQueue(5);
if (myQueue.hasItem()) {
System.out.println("The Queue has item.");
}
}
}

Test result:

hasItem Data Validation MyQueue = [] False False Pass


Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(3);
if (!myQueue.hasItem()) {
System.out.println("The Queue does not have item.");
24 }
}
}
Test result:

MyQueue =
printAllNodes Normal <- 3 <- 4 <- 3 <- 4 Pass
[3, 4]
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(3);
myQueue.enQueue(3);
25 myQueue.enQueue(4);
myQueue.printAllNodes();
}
}
Test result:

This queue There’s


printAllNodes Data Validation MyQueue = [] doesn’t have nothing Fail
any item printed
26 Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(3);
myQueue.printAllNodes();

Page. 36
}
}
Test result:

MyQueue = MyQueue = MyQueue =


reverseQueue Normal Pass
[1, 2, 3] [3, 2, 1] [3, 2, 1]
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(5);

myQueue.enQueue(1);
myQueue.enQueue(2);
myQueue.enQueue(3);
System.out.println("Current Queue:");
myQueue.printAllNodes();
27 System.out.println("Reversed Queue is:");
Main.reverseQueue(myQueue);
myQueue.printAllNodes();
}
}
Test result:

Reverse a MyQueue = MyQueue = MyQueue =


reverseQueue Normal Pass
queue [1, 2] [2, 1] [2, 1]
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(5);
myQueue.enQueue(1);
myQueue.enQueue(2);
System.out.println("Current Queue:");
myQueue.printAllNodes();
28 System.out.println("Reversed Queue is:");
Main.reverseQueue(myQueue);
myQueue.printAllNodes();
}
}
Test result:

Data MyQueue = MyQueue = MyQueue =


reverseQueue Pass
Validation [] [] []
29 Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(5);

Page. 37
System.out.println("Current Queue:");
myQueue.printAllNodes();
System.out.println("Reversed Queue is:");
Main.reverseQueue(myQueue);
myQueue.printAllNodes();
}
}
Test result:

Data MyQueue = MyQueue = MyQueue =


reverseQueue Pass
Validation [1] [1] [1]
Test code:
public class Test2 {
public static void main(String args[]) {
Queue<Integer> myQueue = new Queue<Integer>(5);
myQueue.enQueue(1);
System.out.println("Current Queue:");
myQueue.printAllNodes();
System.out.println("Reversed Queue is:");
30 Main.reverseQueue(myQueue);
myQueue.printAllNodes();
}
}
Test result:

True, True,
Stack1 = [1, 2, 3]
compareStacks Normal Preserve two Preserve two Pass
Stack2 = [1, 2, 3]
Stacks data Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
firstStack.push(1);
firstStack.push(2);
firstStack.push(3);
Compare two
31 System.out.println("First Stack:"); firstStack.printAllNodes();
stacks
Stack<Integer> secondStack = new Stack<Integer>(20);
secondStack.push(1);
secondStack.push(2);
secondStack.push(3);
System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}

Page. 38
Test result:

True,
Data Stack1 = [] Failed to
compareStacks Preserve two Fail
Validation Stack2 = [] execute
Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
System.out.println("First Stack:"); firstStack.printAllNodes();

Stack<Integer> secondStack = new Stack<Integer>(20);


System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
32 System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = [1, 2, 3]
compareStacks Preserve two Preserve two Pass
Validation Stack2 = [1, 2, 4]
Stacks data Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
firstStack.push(1);
firstStack.push(2);
33 firstStack.push(3);
System.out.println("First Stack:"); firstStack.printAllNodes();

Stack<Integer> secondStack = new Stack<Integer>(20);


secondStack.push(1);
secondStack.push(2);
secondStack.push(4);
System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();

Page. 39
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = [1, 2]
compareStacks Preserve two Preserve two Pass
Validation Stack2 = [1, 2, 3]
Stacks data Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
firstStack.push(1);
firstStack.push(2);
System.out.println("First Stack:"); firstStack.printAllNodes();

Stack<Integer> secondStack = new Stack<Integer>(20);


secondStack.push(1);
secondStack.push(2);
secondStack.push(3);
System.out.println("Second Stack:"); secondStack.printAllNodes();
34 System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = [1, 2, 4]
compareStacks Preserve two Preserve two Pass
Validation Stack2 = [1, 1]
Stacks data Stacks data
Test code:
public class Test2 {
35 public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
firstStack.push(1);
firstStack.push(2);
firstStack.push(4);
System.out.println("First Stack:"); firstStack.printAllNodes();

Page. 40
Stack<Integer> secondStack = new Stack<Integer>(20);
secondStack.push(1);
secondStack.push(1);
System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = [1]
compareStacks Preserve two Preserve two Pass
Validation Stack2 = [1, 2, 3]
Stacks data Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
firstStack.push(1);
System.out.println("First Stack:"); firstStack.printAllNodes();

Stack<Integer> secondStack = new Stack<Integer>(20);


secondStack.push(1);
secondStack.push(2);
secondStack.push(3);
System.out.println("Second Stack:"); secondStack.printAllNodes();
36 System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = []
compareStacks Preserve two Preserve two Pass
37 Validation Stack2 = [1, 2, 3]
Stacks data Stacks data
Test code:

Page. 41
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
System.out.println("First Stack:"); firstStack.printAllNodes();

Stack<Integer> secondStack = new Stack<Integer>(20);


secondStack.push(1);
secondStack.push(2);
secondStack.push(3);
System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
"Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

False, False,
Data Stack1 = [1, 2]
compareStacks Preserve two Preserve two Pass
Validation Stack2 = []
Stacks data Stacks data
Test code:
public class Test2 {
public static void main(String args[]) {
Stack<Integer> firstStack = new Stack<Integer>(20);
System.out.println("First Stack:"); firstStack.printAllNodes();
firstStack.push(1);
firstStack.push(2);

Stack<Integer> secondStack = new Stack<Integer>(20);


System.out.println("Second Stack:"); secondStack.printAllNodes();
System.out.println(Main.compareStacks(firstStack, secondStack) ? "Two stacks equal" :
38 "Two stacks not equal");
System.out.println("First Stack:"); firstStack.printAllNodes();
System.out.println("Second Stack:"); secondStack.printAllNodes();
}
}
Test result:

Page. 42
3.2. Evaluation
There’re 38 test cases have been implemented as in above table. However, 2 test cases failed due to lack
of validation.

Firstly, MyQueue.printAllNodes (1) function of Queue ADT is not an important feature of the Data Type.
In process of implementing, the author has missing a checking step before executing the print out loop.
This cause a “silent error” that the program doesn’t print out anything but in the right way it should show
a message to notice that there’s no element in the Queue.

Secondly, same as (1), MyStack.printAllNodes, it’s not a compulsory function that means the author hasn’t
implemented checking expression for this function. That cause the similar error as in the above issue.

Thirdly, a same issue, MyStack.pop() also doesn’t have checking for number of currently available elements
before taking out the value. Technically, this is not a Logic error since whenever the top is null, we cannot
take out any more element. However, in the runtime process it can break the program. As a result, this is
a big problem since it directly affects on the program.

These three fail tests are not really important. However, it directly affects on the quality of the program
since it doesn’t let the author know about the issue that occurred in the run time. The checking step before
print out will save un-necessary steps and improve the performance of the program. Further, in other
problems, strict checking will ensure the best performance for the algorithm and reduce the Time
Complexity for the program.

As a sequence, test 26 also failed since it takes advantage of MyStack.pop(). MyStack.pop only works if
there’re remaining elements in the Stack, otherwise, it will cause a fatal error. Due to it, test 26 failed
because two imported Stacks are blank (or also one of them will have same result of fatal error).

The solution for the problem is thinking about special cases before implementing any algorithms. Through
this step, in implementing, the developer will be noticed about issues that may occur and able to
implement an appropriate solution for the problem.

Page. 43
4. How Asymptotic Analysis Measure Effectiveness of an Algorithm
Asymptotic analysis of an algorithm refers to defining the mathematical bound/framing of its run-time
performance. Using asymptotic analysis, we can very well conclude the best case, average case, and worst-
case scenario of an algorithm. Asymptotic analysis is input bound i.e., if there's no input to the algorithm,
it is concluded to work in a constant time. Other than the "input" all other factors are considered constant.
(Garibel, 2018)

Asymptotic analysis refers to computing the running time of any operation in mathematical units of
computation. For example, the running time of one operation is computed as f(n) and may be for another
operation it is computed as g(n2). This means the first operation running time will increase linearly with
the increase in n and the running time of the second operation will increase exponentially when n
increases. Similarly, the running time of both operations will be nearly the same if n is significantly small.
(Garibel, 2018)

Usually, the time required by an algorithm falls under three types −

- Best Case − Minimum time required for program execution.


- Average Case − Average time required for program execution.
- Worst Case − Maximum time required for program execution.

Page. 44
4.1. Theta Notation (Θ-notation)
Theta notation encloses the function from above and below. Since it represents the upper and the lower
bound of the running time of an algorithm, it is used for analyzing the average case complexity of an
algorithm. (Swellson, 2019)

For a function g(n), Θ(g(n)) is given by the relation:

Θ(g(n)) = { f(n): there exist positive constants c 1, c2 and n0


such that 0 ≤ c1g(n) ≤ f(n) ≤ c2g(n) for all n ≥ n0 }

Figure 28. Theta Notation Expression

The above expression can be described as a function f(n) belongs to the set Θ(g(n)) if there exist positive
constants c1 and c2 such that it can be sandwiched between c1g(n) and c2g(n), for sufficiently large n.
(Swellson, 2019)

If a function f(n) lies anywhere in between c1g(n) and c2g(n) for all n ≥ n0, then f(n) is said to be
asymptotically tight bound. (Swellson, 2019)

Figure 29. Theta Notation factors

Page. 45
4.2. Big-O Notation (O-notation)
Big-O notation represents the upper bound of the running time of an algorithm. Thus, it gives the worst-
case complexity of an algorithm. (Swellson, 2019)

O(g(n)) = { f(n): there exist positive constants c and n 0


such that 0 ≤ f(n) ≤ cg(n) for all n ≥ n0 }

Figure 30. Big-O Notation Expression

The above expression can be described as a function f(n) belongs to the set O(g(n)) if there exists a positive
constant c such that it lies between 0 and cg(n), for sufficiently large n. (Swellson, 2019)

For any value of n, the running time of an algorithm does not cross time provided by O(g(n)). (Swellson,
2019)

Since it gives the worst case running time of an algorithm, it is widely used to analyze an algorithm as we
are always interested in the worst case scenario. (Swellson, 2019)

Figure 31. Big-O Notation Factors

Page. 46
4.3. Omega Notation (Ω-notation)
Omega notation represents the lower bound of the running time of an algorithm. Thus, it provides best
case complexity of an algorithm.

Ω(g(n)) = { f(n): there exist positive constants c and n 0


such that 0 ≤ cg(n) ≤ f(n) for all n ≥ n0 }

Figure 32. Omega Notation Expression

The above expression can be described as a function f(n) belongs to the set Ω(g(n)) if there exists a positive
constant c such that it lies above cg(n), for sufficiently large n.

For any value of n, the minimum time required by the algorithm is given by Omega Ω(g(n)).

Figure 33. Omega Notation Factors

Page. 47
5. Two Ways Measuring Efficiency of An Algorithm
There are two ways to measure the effectiveness of an algorithm:

• Space efficiency - the memory required, also called, space complexity.


• Time efficiency - the time required, also called time complexity.

5.1 Time efficiency


Time complexity estimates the time to run an algorithm. It's calculated by counting elementary operations.
An algorithm's time complexity is not the actual time needed to execute a specific code, as it depends on
other variables such as a programming language, operating software, computing power, etc. The principle
behind the complexity of time is that it can only calculate the algorithm's execution time in a way that
depends only on the algorithm itself and its input.

We use something called the "Big O notation" to express the time complexity of an algorithm. The Big O
notation is a language that we use to characterize an algorithm's time complexity. It's how the success of
multiple approaches to an issue is compared and helps us make decisions.

The runtime of an algorithm is expressed by the Big O notation in terms of how fast it grows relative to the
input (this input is called 'n'). Thus, if we suggest, for instance, that an algorithm's run time grows" on the
order of the input size, "we will state that as" O(n). If we suggest that an algorithm's run time grows "on
the order of the square of the input 's dimension," we will express it as "O(n2)."

The actual running time depends on many factors:

• The speed of the computer: CPU (not just clock speed), I/O, etc.
• The compiler, compiler options.
• The quantity of data - ex. search a long list or short.
• The actual data - ex. in the sequential search if the name is first or last.

We can take two approaches when evaluating time complexity:

Page. 48
• Order of magnitude / asymptotic categorization-This utilizes coarse categories and offers a general
performance idea. If algorithms fall into the same category, if the size of the data is small, or if
output is critical, then you can look more closely at the next method.
• Estimation of running time:
By analysis of the code we can do:
o Operation counts - select operation(s) that are executed most frequently and determine
how many times each is done.
o Step counts - determine the total number of steps, possibly lines of code, executed by the
program.
• By execution of the code we can:
o Benchmark - run the program on various data sets and measure the performance.
o Profile - A report on the amounts of time spent in each routine of a program, used to find
and tune away from the hot spots in it.

For any time, complexity consideration there are 3 cases:

o Worst case: the worst-case runtime complexity of the algorithm is defined by the maximum
number of steps taken on any instance of size n.
o Average case: the average case runtime complexity of the algorithm is defined by an average
number of steps taken on any instance of size n.
o Best case: the best-case runtime complexity of the algorithm is defined by the minimum number
of steps taken on any instance of size n.

One algorithm may run faster on some inputs than others of the same size. We may want to denote the
runtime of an algorithm as a function of the input size obtained by averaging on all possible inputs of the
same size. However, an average case analysis is often a challenge. Averaging case analysis also allows one
to measure estimated run times based on a given input distribution, and also involving complex probability
theory. The worst-case analysis is of special importance in real-time computation since it is important to
know how much worst-case time it will take to ensure that the algorithm will still be completed on time.
The word best-case output is used to define how an algorithm performs under optimum conditions.

Page. 49
5.2 Space efficiency
There are certain situations under which space/memory used must be evaluated. For example, for large
amounts of data or for the programming of embedded systems.

Components of space/memory use:

• Instruction space: Affected by the compiler, compiler options, target computer (CPU)
• Data space: Affected by the data size/dynamically allocated memory, static program variables,
• run-time stack space: Affected by the compiler, run-time function calls, and recursion, local
variables, parameters.

Memory limits provide details on the planned complexity of space. You can estimate the number of
variables you can declare in your programs. In short, if you have a constant number of variables, you also
have a constant complexity of space: in the Big-O notation, this is O (1). If you need to declare an array of
n-elements, you have linear space complexity — O(n). More precisely, the complexity of space is the
amount of memory used to conduct the computation. It contains all variables, both global and local,
dynamic pointer data structures and, in the case of recurrence, the contents of the stack. Input data can
also be used, depending on the convention. Space complexity is more difficult to quantify than time
complexity, since not all of these variables and data structures can be needed at the same time. Global
variables exist and occupy memory all the time; local variables (and additional information stored on the
stack) can only exist when the function is invoked.

Page. 50
6. Trade-off When Specifying an ADT
“Abstract data types introduce an abstraction barrier between those who implement a data type and those
who use it.” - (Clinger, 2010)

There’re a lot of well-known tradeoffs in computer development. Some example is: The time-space
tradeoff, where often you can use more memory to compute an answer faster. Or the time-accuracy
tradeoff, where you can compute an approximate answer quickly, and a precise one given more time.

Abstract Data Type brings a lot of benefits in development like: Representation independence, modularity,
interchangeability of Parts. However, for achieving those benefits, the program also has some trade-off
for that.

Data Encapsulation is one of the most important property of ADT, it’s used to hide the internal
representation, or state, of an object from the outside and can be called information hiding. However, by
applying Encapsulation, the program not just prevent unwanted accesses from outside but also restrict
any effort to accessing or modifying internal information. This can lead to an uncomfortable feeling in
development or need to open more methods for interacting with data inside the object.

Page. 51
7. Benefits of Using Implementation Independent Data Structures
Almost all required Data Structures are pre-defined in the programming language. However, in some
situation, the programmer has to define own Data Structure for special purposes.

Back to advantages and disadvantages of Abstract Data Types (Tradeoffs), for good purposes when using
own ADT, the programmer has to paid for the performance using memory space. Also, in Independent
Data Structures, the tradeoffs can be various kinds: Slower, lack of features, lack of testing, not well
defined, not well structured, …

However, to the bright side, Independent Data Structures bring big advantages.

When using Independent Data Structures, the program becomes independent from the representation of
the data, so the representation will be improved and avoid breaking the entire program. This often be
called as “Representation Independent”.

Furthermore, in the development, when the programmers work in team, parts of the program will be
developed independently and build dependencies modules for the whole project. Implementing
Independent Data Structure will represent the data independence and parts of the program will be less
depend on other parts and how they’re implemented. This is called “Modularity”.

One important advantage is “Interchangeability of Parts”. According to (Clinger, 2010), Different


implementations of an abstract data type may have different performance characteristics. With abstract
data types, it becomes easier for each part of a program to use an implementation of its data types that
will be more efficient for that particular part of the program.

And the final advantage in the author’s opinion is “customization”. Not using pre-defined Data Structures
enables Development Team a higher level of customization and reduce unnecessary parts of the system.
Higher customization also mean a higher controllable in the project when the management will be deep
down to the lowest level of the Data Structure.

Page. 52
Conclusion
Through this document, the author has discussed about Stack ADT, Queue ADT and their implementation
in the project. Problems are also discussed and two questions have been answered clearly:

• Write a function to reverse the order of a queue using a stack. Include proper annotations in your
solution. What is the worst-case run time complexity of this function if there are n elements in the
queue? Explain.
• Write a function that takes two stacks and returns true if the stacks contain the same elements in
the same order. After the operations, both stacks must remain in the original order. You are
allowed to use additional stacks or queues as needed, but no other data structures. What is the
worst-case run time complexity of your code if each stack contains n elements? Explain.

Through the report, the author has discussed in detail the implementation of two data structures and how
to measure the effectiveness of the algorithms involved. The report also assessed the use of ADT in design
and development, including complexity, tradeoffs and benefits.

Page. 53
References
S.Adamchik, V., 2009. Stacks and Queues. [Online]
Available at: https://www.cs.cmu.edu/~adamchik/15-
121/lectures/Stacks%20and%20Queues/Stacks%20and%20Queues.html

Garibel, J., 2018. Data Structures - Asymptotic Analysis. [Online]


Available at: https://www.tutorialspoint.com/data_structures_algorithms/asymptotic_analysis.htm

Swellson, M., 2019. Asymptotic Analysis. [Online]


Available at: https://www.programiz.com/dsa/asymptotic-notations

Clinger, W. D., 2010. Advantages of Abstract Data Types. [Online].

Clinger, W. D., 2010. Abstraction Barrier. [Online]


Available at: https://course.ccs.neu.edu/cs5010f17/InterfacesClasses2/abstractionBarrier1.html?

Page. 54

You might also like