You are on page 1of 21

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 21/7/2021 Date Received 2nd submission 21/7/2021

Student Name Vuong Khanh Thanh Student ID GDH190702

Class GCH0902 Assessor name Do Hong Quan

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 THANH

Grading grid

P4 P5 P6 P7 M4 M5 D3 D4
 Summative Feedback:  Resubmission Feedback:
2.1

Grade: Assessor Signature: Date:


Internal Verifier’s Comments:

IV Signature:
Table of Contents
1. Introduction .......................................................................................................................................................... 4
2. Design and implementation of Stack ADT and Queue ADT (P4) .......................................................................... 4
2.1 Stack .............................................................................................................................................................. 4
2.2 queue .................................................................................................................................................................. 7
3. Application .............................................................................................................................................................. 10
4. Implement error handling and report test results (P5) ...................................................................................... 10
4.1 testing ............................................................................................................................................................... 10
4.2 Evaluation ......................................................................................................................................................... 12
5. Discuss how asymptotic analysis can be used to assess the effectiveness of an algorithm (P6) ....................... 12
6.1 Time complexity ................................................................................................................................................ 16
6.2 Space complexity ............................................................................................................................................. 18
7. References .......................................................................................................................................................... 20

Table of figure
Figure 1 LIFO ................................................................................................................................................................. 4
Figure 2 FIFO ................................................................................................................................................................. 7
Figure 3 test table ....................................................................................................................................................... 12
Figure 4 Big Oh Notation, Ο ........................................................................................................................................ 13
Figure 5 function f(n) 1 ............................................................................................................................................... 13
Figure 6 Omega Notation, Ω ....................................................................................................................................... 14
Figure 7 function f(n) 2 ............................................................................................................................................... 14
Figure 8 Theta Notation, θ .......................................................................................................................................... 14
Figure 9 function f(n) 3 ............................................................................................................................................... 14
Figure 10 number of items in the collection............................................................................................................... 15
Figure 11 example O(n) – Linear time complexity ...................................................................................................... 16
Figure 12 example O(n2) – Quadratic time complexity .............................................................................................. 16
Figure 13 exmaple time complexity ............................................................................................................................ 17
Figure 14 example Space complexity.......................................................................................................................... 19
Figure 15 example Space complexity 2....................................................................................................................... 19

List of table
Table 1 Common Asymptotic Notations ..................................................................................................................... 15
1. Introduction
A data structure is a programmatic way of storing data so that it can be used efficiently. Almost every
enterprise application uses various types of data structures in one way or another. This tutorial will give
you a great understanding of Data Structures needed to understand complexity of enterprise-grade
applications and the needs of algorithms and data structures. In this report, I have reused ADT such as
Queues and Stacks.

2. Design and implementation of Stack ADT and Queue ADT (P4)


2.1 Stack
LIFO (Last in First Out) In this method, the most recently entered or last entered numbers are output
first. The new number is used first, taking precedence over the old number.

Figure 1 LIFO

package demo;

public class Mystack<T> {

private class Node<T>{


T data;
Node nextNode;

public Node(T data) {


this.data = data;
this.nextNode = null;
}
}
Node head = null;
Node tail = null;
int size = 0;

public void push(T data) {


Node aNode = new Node(data);
if(head == null) {
head = aNode;
tail = aNode;
}
else{
tail.nextNode = aNode;
tail = aNode;
}
}
I put a conditional sentence if head is null then head is aNode and tail aNode else tail concatenate
with next aNode

public void push (int index, T data ) {


Node aNode = new Node(data);
int count = 0;
if(head != null) {
Node current = head;
while(current.nextNode != null && count < index - 1) {
current = current.nextNode;
count++;
}
aNode.nextNode = current.nextNode;
current.nextNode = aNode;
}
}
aNode concatenate curent and conect to next node and curent is aNode.
public int pop(){
Node temp = head;
head = head.nextNode;
temp.nextNode = null;
return 0;
}
change head to temp then connect head to next node finally cut up temp connection.

public void removeLast() {


//go to the last Node
Node current = head;
Node pre = head;
while(current.nextNode.nextNode != null){
pre = current;
current = current.nextNode;
}
// pre: the second-last Node
pre.nextNode = null;
}
I take pre is second-last Node if pre is a null node then it is deleted.
public int size() {
int count = 0;
Node current = head;
while(current.nextNode != null) {
current = current.nextNode;
count++;
}
System.out.print("size is:");
return count +1 ;
}
we get current by head current will concatenate until it is null, then it will count the number of elements
in the row.

public void print(){


{
Node p = head;
while (p != null) {
System.out.print(p.data + ",");
p = p.nextNode;
}
System.out.println("");
}
}
we take p as head if p is not null then it will print until null then stop
public void reverse(){
Node pre, current, succ;
current = pre = head;
current = current.nextNode;
pre.nextNode = null;
while (current != null) {

succ = current.nextNode;
current.nextNode = pre;
pre = current;
current = succ;
}
head = pre;
}
current is not null, then succ is equal to current and current is connected to pre instead of current
because pre is the beginning of head, so it will run backwards.

public static void main(String[] args) {


// TODO Auto-generated method stub
Mystack s = new Mystack();
s.push(2);
s.push(7);
s.push(4);
s.push(5);
s.push(8);
s.push(9);
s.push(1);

s.print();
System.out.println(s.size());
s.push(2,99);
s.print();
System.out.println(s.size());

s.push(8);
s.print();
System.out.println(s.size());

s.removeLast();
s.print();
System.out.println(s.size());

s.reverse();
s.print();
System.out.println(s.size());

s.pop();
s.print();

}
}
2.2 queue
FIFO(First in First out) With this method, the first number of the input row will be the first number
output.

Figure 2 FIFO

package demo;

public class Myqueue<T> {

private class Node<T>{


T data;
Node nextNode;
Node prev;

public Node(T data) {


this.data = data;
this.nextNode = null;
this.prev = null;
}
}
Node head = null;
Node tail = null;

public void enqueue(T data) {// add a new node into the end of the list
Node aNode = new Node(data);
if(head == null) {
head = aNode;
tail = aNode;
}
else {
//connect the new node to tail
tail.nextNode = aNode;
aNode.prev = tail;
tail = aNode;
}
}
tail connects to aNode aNode connects prev and tail with anode so it will be printed at the end
public void enqueue (int index, T data ) {
Node aNode = new Node(data);
int count = 0;
if(head != null) {
Node current = head;
current = tail;
while(current.prev != null && count < index - 1) {
current = current.prev;
count++;
}
aNode.prev = current.prev;
current.prev = aNode;
}
}

aNode connection with current and current is aNode.


public int dequeue(){
Node temp =head.nextNode ;
head.nextNode.prev = null;
head = head.nextNode;
return 0; }
temp by tail tail connect to the next node then cut the link of temp so that temp is deleted.
public void dequeue( int index) {
int count = 0;
Node current = head;
current = tail;
while(current.prev !=null && count <index-2) {
current = current.prev;
count++;
}
Node temp = current.prev;
current.prev = temp.prev;
temp.prev = null;
}
temp is current, current connects to other notes then disconnects temp, so delete temp

public void queuelast() {


Node temp = tail;
Node pre = tail;
tail = tail.prev;
temp.prev = null;
pre.prev = null;
}
take first as the tail, tail connects to other nodes then disconnects pre from the temporary.
public int size() {
int count = 0;
Node current = head;
current = tail;
while(current.prev != null) {
current = current.prev;
count++;
}
System.out.print("size is:");
return count +1 ;
}
we get current by tail current will concatenate until it is null, then it will count the number of elements in
the row.
public void print(){
{
Node p = tail;
while (p != null) {
System.out.print(p.data + ",");
p = p.prev;
}
System.out.println("");
}
}

we take p as tail if p is not null then it will print until null then stop
public static void main(String[] args) {
Myqueue<Integer> s = new Myqueue<>();
s.enqueue(2);
s.enqueue(7);
s.enqueue(4);
s.enqueue(5);
s.enqueue(8);
s.enqueue(9);
s.enqueue(1);

s.print();
System.out.println(s.size());

s.enqueue(4,55);
s.print();
System.out.println(s.size());

s.dequeue();
s.print();
System.out.println(s.size());

s.dequeue(2);
s.print();
System.out.println(s.size());

s.queuelast();
s.print();
System.out.println(s.size());

}
}

3. Application
reverse a Stack: we let current ,pre and head, current connects to the next current, pre connects to null.
Current is not null, then succ connects to current, current with pre finally replaces pre with head.

public void reverse(){


Node pre, current, succ;
current = pre = head;
current = current.nextNode;
pre.nextNode = null;
while (current != null) {

succ = current.nextNode;
current.nextNode = pre;
pre = current;
current = succ;
}
head = pre;
}

using recursion will help the computer to calculate faster, but the algorithm will be more complicated,
reverse the unused stack so that it is easier to understand for coders, Although this is not as optimal as
recursion, but I still prefer to use this method over not using it to reverse stack.

4. Implement error handling and report test results (P5)


4.1 testing
Scope Operation Testing Input Expected Output Actual Status
Output
Stack Push() Normal Mystack[2,7,4]; MyStack: [2,7,4,5] The Passed
ADT: s.push(5); same
MyStack As
expected
output
Push() Normal Mystack[2,7,4,5]; MyStack:[2,99,7,4 The Passed
s.push(2,99); ,5] same
As
expected
output
Push() Data MyStack:[2,7,4,5] Invalid input program Failed
validation ; stop
s.Pop(*);
Pop() Normal Mystack[2,99,7,4, MyStack:[99,7,4,5 The Passed
5]; ] same
s.Pop(); As
expected
output
size() Normal Mystack[2,7,4,5]; MyStack: [2,7,4,5] The Passed
System.out.printl Size is: 4 same
n(s.size()); As
expected
output
removefirst() Normal Mystack[2,7,4,5]; MyStack: [2,7,4] The Passed
s.removefirst(); same
As
expected
output
Queue enqueue () Normal Myqueue[4,7,2]; Myqueue:[5,4,7,2 The Passed
ADT: s.enqueue(5); ] same
MyQueue As
expected
output
enqueue () Normal Myqueue:[5,4,7,2 Myqueue:[5, The Passed
]; ,4,7,15,2] same
s.enqueue(2,15); As
expected
output
enqueue () Data Myqueue:[2,7,4,5 Invalid input program Failed
validation ]; stop
s.Myqueue (*);
dequeue () Normal Myqueue:[2,7,4,5 Myqueue:[ 2,7,4] The Passed
]; same
s. dequeue(); As
expected
output
dequeue () Normal Myqueue:[4,7,5,6 Myqueue:[ 4,5,6] The Passed
]; same
s.dequeue(3); As
expected
output
size() Normal Myqueue:[5,4,7,2 Myqueue:[5,4,7,2 The Passed
]; ] same
System.out.printl Size: 4 As
n(s.size()); expected
output

dequeuelast Normal Myqueue:[5,4,7,2 Myqueue:[4,7,2] The Passed


() ]; same
s.dequeuelast(): As
expected
output
Reverse a reverse() Normal MyStack:[ MyStack:[ 5,4,2,7] The Passed
Stack 7,2,4,5]; same
without s.reverse(); As
Recursion expected
output
Figure 3 test table

4.2 Evaluation
I have tested 14 cases all of the above gives the expected pop() result and Stack ADT push is also
properly executed queue ADT. My code is basically completed but there are still a few flaws such as the
interface is still hard to see, the code is not fully optimized, and there are a few cases like changing the
number in the stack. I will try to fix it in the future.

5. Discuss how asymptotic analysis can be used to assess the


effectiveness of an algorithm (P6)
Asymptotic analysis of an algorithm refers to defining the mathematical boundation/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.

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 (Anon., 2021).

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.

Following are the commonly used asymptotic notations to calculate the running time complexity of
an algorithm.

Big-O Notation (O-notation): The notation Ο(n) is the formal way to express the upper bound of an
algorithm's running time. It measures the worst case time complexity or the longest amount of time
an algorithm can possibly take to complete.

Figure 4 Big Oh Notation, Ο

For example, for a function f(n) :

Figure 5 function f(n) 1

Omega Notation, (Ω-notation): The notation Ω(n) is the formal way to express the lower bound of an
algorithm's running time. It measures the best case time complexity or the best amount of time an
algorithm can possibly take to complete.
Figure 6 Omega Notation, Ω

For example, for a function f(n):

Figure 7 function f(n) 2

Theta Notation, (θ-notation): The notation θ(n) is the formal way to express both the lower bound and
the upper bound of an algorithm's running time. It is represented as follows

Figure 8 Theta Notation, θ

Figure 9 function f(n) 3


Common Asymptotic Notations:

Table 1 Common Asymptotic Notations

constant − Ο(1)
logarithmic − Ο(log n)
linear − Ο(n)
n log n − Ο(n log n)
quadratic − Ο(n2)
cubic − Ο(n3)
polynomial − nΟ(1)
exponential − 2Ο(n)

Figure 10 number of items in the collection


For example:

Figure 11 example O(n) – Linear time complexity

this function prints all the numbers from 0 to n-1. As n grows, execution of print statement increases as
well. For n=0, it will not execute at all, for n=1, it will execute once, for n=2, it executes two times and for
n=n, it executes n times. So, the loop is directly proportional to the value of n, hence complexity of loop
is O(n).

Figure 12 example O(n2) – Quadratic time complexity

Typical quadratic complexity function has the format, a loop with an in the loop and both loops depends
on the size of the input

6. Two method that can measured the effectiveness of an algorithm(P7)


6.1 Time complexity
Time complexity is the amount of time taken by an algorithm to run, as a function of the length of the
input. It measures the time taken to execute each statement of code in an algorithm.

Time complexity of an algorithm signifies the total time required by the program to run till its
completion.

The time complexity of algorithms is most commonly expressed using the big O notation. It's an
asymptotic notation to represent the time complexity. We will study about it in detail in the next
tutorial.

Time Complexity is most commonly estimated by counting the number of elementary steps performed
by any algorithm to finish execution. Like in the example above, for the first code the loop will run n
number of times, so the time complexity will be n atleast and as the value of n will increase the time
taken will also increase. While for the second code, time complexity is constant, because it will never be
dependent on the value of n, it will always give the result in 1 step.

And since the algorithm's performance may vary with different types of input data, hence for an
algorithm we usually use the worst-case Time complexity of an algorithm because that is the maximum
time taken for any input size (Anon., 2021).

Types of Notations for Time Complexity:

• Big Oh denotes "fewer than or the same as" <expression> iterations.


• Big Omega denotes "more than or the same as" <expression> iterations.
• Big Theta denotes "the same as" <expression> iterations.
• Little Oh denotes "fewer than" <expression> iterations.
• Little Omega denotes "more than" <expression> iterations.

Figure 13 exmaple time complexity

In above code “Hello World!!!” print only once on a screen. So, time complexity is constant: O(1) i.e.
every time constant amount of time require to execute code, no matter which operating system or
which machine configurations you are using.

Lets see how many times count++ will run.

When i=0 , it will run 0 times.

When i-1, it will run 1 times.


When i=2, it will run 2 times and so on.

𝑁∗(𝑁−1)
Total number of times count++ will run is 0+1+2+…+(N-1)= . So the time complexity will be
2
O(N2).

6.2 Space complexity


Space complexity : is a function describing the amount of memory (space) an algorithm takes in terms of
the amount of input to the algorithm

The term Space Complexity is misused for Auxiliary Space at many places. Following are the correct
definitions of Auxiliary Space and Space Complexity. Auxiliary Space is the extra space or temporary
space used by an algorithm. Space Complexity of an algorithm is total space taken by the algorithm with
respect to the input size. Space complexity includes both Auxiliary space and space used by input. For
example, if we want to compare standard sorting algorithms on the basis of space, then Auxiliary Space
would be a better criteria than Space Complexity. Merge Sort uses O(n) auxiliary space, Insertion sort
and Heap Sort use O(1) auxiliary space. Space complexity of all these sorting algorithms is O(n) though.
Space complexity is a parallel concept to time complexity. If we need to create an array of size n, this will
require O(n) space. If we create a two dimensional array of size n*n, this will require O(n2) space
(hritikrommie, 2021).

In recursive calls stack space also counts.


Figure 14 example Space complexity

Each of these calls is added to call stack and takes up actual memory. So it take O(n) space. However just
because you have n calls total doesn’t mean it takes O(n) space.

Figure 15 example Space complexity 2


There will be roughly O(n) calls to pairSum. However, those calls do not exist simultaneously on the call
stack, so you only need O(1) space.

It’s necessary to mention that space complexity depends on a variety of things such as the programming
language, the compiler, or even the machine running the algorithm.

7. References
Anon., 2021. studytonight. [Online]
Available at: https://www.studytonight.com/data-structures/time-complexity-of-algorithms
[Accessed 4 7 2021].

Anon., 2021. tutorialspoint. [Online]


Available at: https://www.tutorialspoint.com/data_structures_algorithms/asymptotic_analysis.htm
[Accessed 4 7 2021].

hritikrommie, 2021. geeksforgeeks. [Online]


Available at: https://www.geeksforgeeks.org/g-fact-86/
[Accessed 4 7 2021].

Powered by TCPDF (www.tcpdf.org)


Index of comments

2.1 - P4: Most useful operations of Stack and Queue ADT have been implemented. The solution for size() in both Stack
and Queue can be better (i.e. O(1)).
One solution is provided for task (c), however this is not a good one since by doing that way, the concept
"Encapsulation" won't be met.
- P5: Most operations of Stack and Queue have been tested.
- P6: Some places in the discussion should be rewritten in your own words. Fortunately, the work has provided
examples to illustrate two common complexities.
- P7: When discussing two ways analyzing an algorithms, a number of examples are shown. Although they should
be rewritten in Java, fortunately their time and space complexity analyses are proper.

Powered by TCPDF (www.tcpdf.org)

You might also like