101 2014 3 B
101 2014 3 B
101 2014 3 B
Semesters 1 and 2
School of Computing
BARCODE
CONTENTS
Page
Dear Student
The contents of this module follow on the first-year programming module, COS1512. You will
learn to write more complex C++ programs, to implement and use data structures and
recursion. You will also learn sorting and searching algorithms and how to do complexity
analysis of algorithms.
COS2611 is a practical module in the sense that you constantly have to write, debug and run
programs on your computer. There is only one way to learn to program and that is to sit down
and write programs!
We hope that you will enjoy this module. All the best.
All study material for this module will be available on myUnisa. It is thus very important that you
register on myUnisa and access the module site on a regular basis. You must be registered on
myUnisa to be able to access your learning material, submit your assignments, and gain access
to various learning resources and to participate in online discussion forums.
Because this is an online module, you need to go online to see your study material and read
what to do for the module. Go to the website here: https://my.unisa.ac.za and login with your
student number and password. You will see COS2611-20-S1/S2 in the row of modules in the
orange blocks across the top of the webpage. Remember to check in the ‘More Sites’ tab if you
cannot find it in the orange blocks. Click on the module you want to open.
You can access all the study material on myUnisa. You should NOT wait for the printed
document to arrive to start studying.
Please consult the Study @ Unisa publication for more information on the activation of your
myLife email address as well as obtaining access to the myUnisa module site.
3
2 PURPOSE AND OUTCOMES
2.1 Purpose
Students who successfully complete this module will have the knowledge, skills and
competencies to apply Data Structures and Algorithm Analysis knowledge and strategies in
solving real-world programming problems, according to industry-approved processes within
African, South African and global contexts.
2.2 Outcomes
For this module, there are several outcomes that we hope you will be able to accomplish by the
end of the course:
Demonstrate an understanding of Abstract Data Types (ADTs) and how they are
stored on computers.
The details of the lecturers will also be communicated in a COSALL tutorial letter.
When you contact the lecturers, please do not forget to always include your student number and
module code. This will help the lecturers to assist you.
3.2 Department
You can contact the School of Computing as follows:
4
COS2611/101/3/2020
3.3 University
To contact the University, follow the instructions in the brochure Study @ Unisa. Remember to
have your student number available whenever you contact the University.
4 RESOURCES
4.1 Prescribed book(s)
MALIK, D.S., Data structures using C++, International Edition, 2nd Edition, Cengage Learning,
2009 (ISBN: 978 143 904 0232).
We refer to this book, in the tutorial letters, as Malik. You should purchase this prescribed
text from one of the official university booksellers.
5
for research support and services (e.g. Personal Librarians and literature search
services), go to http://www.unisa.ac.za/sites/corporate/default/Library/Library-
services/Research-support
6
COS2611/101/3/2020
connection. If you live close to a Unisa regional centre or a Telecentre contracted with Unisa,
please feel free to visit any of these to access the Internet. E-tutoring takes place on myUnisa
where you are expected to connect with other students in your allocated group. It is the role of
the e-tutor to guide you through your study material during this interaction process. For you to
get the most out of online tutoring, you need to participate in the online discussions that the e-
tutor will be facilitating.
5.2 Downloading study material and software
One of the requirements for studying at the School of Computing is to have regular access to
myUnisa. You are therefore expected to download any study material from the Internet that, for
whatever reason, is not available on paper in time. You may download it from myUnisa. The
study material is updated regularly, thus you need to check the COS2611 website at least once
a week on myUnisa.
The software should also be downloaded from myUnisa under Additional Resources for
COS2611. Please note that you only need to download Code::Blocks only for COS2611.
6 STUDY PLAN
Use your Study@unisa brochure for general time management and planning. For this
module we recommend that you use the study programme given below as a starting point.
You will probably need to adapt this schedule, taking into account your other modules
together with your personal circumstances. You are expected to spend at least 8 hours per
week on COS2611. Keep in mind that the skills you will learn in this course are progressive,
i.e. you will not be able to understand or do the exercises in the later chapters if you
have skipped the earlier ones.
Week Activity
1 Install software
Study Chapter 1
Study Appendix A
2 Study Chapter 3
3 Study Chapter 5
4 Study Chapter 6
5 Study Chapter 7
6 Study Chapter 8
7 Study Chapter 9
7
8 Study Chapter 10
9 Study Chapter 11
Do Assignment 2
10 Submit Assignment 2
11 Study Chapter 12
12 Do Self-Assessment Assignment
Do NOT submit.
13-15 Revision
7 PRACTICAL WORK
7.1 Prescribed compiler
The prescribed C++ compiler for COS2611 is the GNU C++ compiler.
Note that this prescribed compiler is the one used for COS1511 and COS1512 and can
be downloaded from myUnisa under Additional Resources.
8 ASSESSMENT
8.1 Assessment criteria
Assessment for COS2611 is in the form of 2 assignments and a final exam.
The final mark is made up of the semester mark and the exam mark based on the following
assessment plan guidelines.
8
COS2611/101/3/2020
Determine the worst case time and space complexity of non-recursive algorithms.
Note that all concepts or sections which are part of the selected chapters above which
are not listed above are not examinable. You do not have to for instance study hashing
which is part of the chapter on sorting.
The concepts and sections which have not been included during the discussion of the
foregoing syllabus are NOT part of the syllabus of COS2611. Consequently you will NOT
be examined on these concepts. Hashing which is part of Chapter 10 should for example
NOT be studied.
Determine the worst case time and space complexity of non-recursive algorithms.
Range: This would involve performing Big-Oh analysis of short code fragments
10
COS2611/101/3/2020
Specific outcome 2:
Demonstrate an understanding of the basic properties of pointers and linked lists.
Assessment Criteria:
Specific outcome 3:
Demonstrate an understanding of how to use recursion to solve problems and how to think in
terms of recursion.
Assessment Criteria:
11
Specific outcome 5:
Demonstrate an understanding of search techniques used to retrieve data held in data
structures.
Range: Sequential Search and Binary Search
Assessment Criteria:
The combination of formative and summative assessments consists of the following structure:
12
COS2611/101/3/2020
Semester 1
Semester Assignment Unique assignment Weight Due date
number number
1 01 770462 25% 24 February 2020
1 02 669070 75% 14 April 2020
Semester 2
Semester Assignment Unique assignment Weight Due date
number number
2 01 549716 25% 17 August 2020
2 02 731876 75% 21 September 2020
13
8.6 The assignments
Maximum marks: 25
For each question, you are required to identify the letter of the choice that best completes the
statement or answers the question.
For Questions 1 – 15 please indicate the run time complexity of the respective code fragments:
Question 1
total = 0;
for (int i = 0; i < n; i++)
total += i;
1. O(1) 3. O(n)
2. O(log n) 4. O(n log n)
Question 2
1. O(1) 3. O(n2)
2. O(n) 4. O(n log n)
Question 3
1. O(n) 3. O(n3)
2. O(n2) 4. O(n log n)
14
COS2611/101/3/2020
Question 4
1. O(n) 3. O(3n)
2. O(2n) 4. O(6n)
Question 5
1. O(1) 3. O(n2)
2. O(n) 4. O(n3)
Question 6
1. O(3n) 3. O(3n3)
2. O(n3) 4. O(3 log n)
Question 7
1. O(1) 3. O(2n)
2. O(n) 4. O(log n)
Question 8
1. O(1) 3. O(n2)
2. O(n) 4. O(log n)
15
Question 9
1. O(1) 3. O(2n)
2. O(n) 4. O(n2)
Question 10
1. O(n) 3. O(n3)
2. O(n2) 4. O(1)
Question 11
1. O(n) 3. O(n3)
2. O(n2) 4. O(log n)
Question 12
1. O(n2) 3. O(2n)
2. O(n4) 4. O(4n)
Question 13
1. O(n) 3. O(2n)
2. O(n2) 4. O(2n2)
16
COS2611/101/3/2020
Question 14
1. O(log n) 3. O(n)
2. O(n/2) 4. O(2n)
Question 15
1. O(1) 3. O(n)
2. O(1000) 4. O(log n)
Question 16
An algorithm takes 10 seconds for an input size of 1000. How long will it take for an input size
of 10 000 if the running time is log n?
1. 1 second 3. 13 seconds
2. 10 seconds 4. 100 seconds
Question 17
An algorithm takes 1 second for an input size of 10. How long will it take for an input size of 50
if the running time is in O(1)?
1. 1 second 3. 50 seconds
2. 10 seconds 4. 500 seconds
Question 18
An algorithm takes 2 seconds for an input size of 5. How long will it take for an input size of 50
if the running time is exponential (2n)?
17
Question 19
An algorithm takes 10 seconds for an input size of 1000. What will the largest size of input be
that can be executed in 80 seconds if the running time is cubic?
1. 500 3. 4000
2. 2000 4. 8000
Question 20
An algorithm takes 5 seconds for an input size of 80. What will the largest size of input be that
can be executed in 15 seconds if the running time is linear (O(n))?
1. 512 3. 240
2. 480 4. 48
Question 21
Which one of the following functions has the fastest growth rate (for n > 1)?
1. N 3. n2 log n
2. n log n 4. n2
Question 22
Which one of the following functions has the slowest growth rate (for n > 1)?
1. N 3. 2n
2. n2 4. log n
Question 23
Order the following functions by growth rate from smallest to largest (in terms of their Big-O
values): 10 log n; 2n2; n log n
18
COS2611/101/3/2020
Consider the table below when answering questions 24 and 25. The table shows the
running time against input size (n) of 2 algorithms.
Algorithm n=10 n=20 n=30
Question 24
1. n2 log n 3. 2n
2. n2 4. n log n
Question 25
1. n2 log n 3. 2n
2. n2 4. n log n
19
Assignment 02 [ For First Semester Students Only]
Due date: 14 April 2020
Maximum mark: 80
Consider a linked list called list depicted below. Assume the node is in the usual info-link
form. (list and Ptr are pointers of type nodeType.)
Suppose calling exchange(2,4) on the list will rearrange the nodes such that the second
node is D and the last node is B.
Without changing the order of the nodes in the above diagram, adjust the links of this list below
to show the results after calling exchange(2,4):
(2)
Note that the exchange() function does not swap the elements of the nodes.
Write C++ code to implement (a). Do not provide the complete function.
(3)
20
COS2611/101/3/2020
suppose list1 points to the list containing the elements 2 6 7 and list2 points to the list
containing the elements 3 5 8. The statement:
newList.mergeLists(list1, list2);
creates a new linked list with the elements in the order 2 3 4 5 7 8 and the object
newLists points to this list. After the preceding statement have executed, list1 and list2
are empty.
2.2 Write the definition of the function template to implement the operation mergeLists. Write a
program to test your function thereafter.
Write a recursive function template, identicalS that checks whether two stacks provided as
parameters are identical. If that is the case, the function should return the Boolean value true
otherwise the Boolean value false should be returned.
21
Question 4: Stacks [6]
L={anbn} where n ≥1, is a language of all words with the following properties:
One way to test if a word w belong to this language is to use a stack to check if the number of
a’s balances the number of b’s. Use the provided header and write a function isInLanguageL
that uses a stack to test if any word belongs to L.
a) Write a function reverseQ that uses a local stack to reverse the contents of a queue. Use
the following header:
You can make use of any member function of class queueType. Note that this function
is not a member of class queueType. (6)
(10)
Describe how the binary search algorithm will search for 27 in the following list:
5, 6, 8, 12, 15, 21, 25, 31.
22
COS2611/101/3/2020
a) Sort the list using selection sort. Show the state of the list after each call to the swap
procedure.
b) Sort the list using insertion sort. For each iteration of the outer loop of the algorithm show
the state of the list.
c) Sort the list using quick sort with the middle element as pivot. For each iteration of the outer
loop of the algorithm show the state of the list.
You are not required to write code for this question. You need to trace through the different
sorting algorithms using the given list.
F
B
G
C E
List the node of this binary tree an in-order, pre-order and post-order sequence.
23
Assignment 01 [For Second Semester Students Only]
Due date: 17 August 2020
Maximum marks: 25
For each question, you are required to identify the letter of the option that best completes the
statement or answers the question.
For Questions 1 – 15 please indicate the run time complexity of the respective code fragments:
Question 1
1. O(1) 3. O(N)
2. O(Log N) 4. O(N log N)
Question 2
1. O(1) 3. O(N)
2. O(Log N) 4. O(N log N)
Question 3
1. O(1) 3. O(N3)
2. O(N2) 4. O(N log N)
24
COS2611/101/3/2020
Question 4
1. O(1) 3. O(N3)
2. O(N2) 4. O(N log N)
Question 5
1. O(N2) 3. O(N)
2. O(Log N) 4. O(N log N)
Question 6
1. O(1) 3. O(log N)
2. O(N2) 4. O(N log N)
Question 7
1. O(N2) 3. O(N3)
2. O(N5) 4. O(N log N)
Question 8
I = 1
while (i<=n){
for (j=1; j<10; j++)
doIt();
i++;}
25
1. O(N4) 3. O(N3)
2. O(N2) 4. O(N5)
Question 9
Question 10
1. O(1) 3. O(N3)
2. O(N2) 4. O(N log N)
Question 11
1. O(N4) 3. O(N3)
2. O(N2) 4. O(N log N)
26
COS2611/101/3/2020
Question 12
1. O(N) 3. O(N3)
2. O(N2) 4. O(N log N)
Question 13
1. O(1) 3. O(log N)
2. O(N) 4. O(N log N)
Question 14
1. O(N) 3. O(log N)
2. O(N2) 4. O(N log N)
Question 15
1. O(N) 3. O(log N)
2. O(N2) 4. O(N log N)
27
Question 16
An algorithm takes 0.5 seconds to execute for an input size of 100. How long will it take for an
input size of 500 if the running time is linear?
1. 5 sec 3. 0.5sec
2. 2.5sec 4. 1sec
Question 17
An algorithm takes 10 seconds for an input size of 100. How long will it take for an input size of
10 000 if the running time is log N?
1. 10 sec 3. 20 sec
2. 20.5sec 4. 40sec
Question 18
An algorithm takes 1 second for an input size of 10. How long will it take for an input size of 50
if the running time is exponential (2n)?
Question 19
An algorithm takes 1 second to execute for an input size of 10. What will the largest size of
input be that can be executed in 25 seconds if the running time is quadratic (N2)?
Question 20
An algorithm takes 10 seconds for an input size of 1000. What will the largest size of input be
that can be executed in 80 seconds if the running time is cubic?
28
COS2611/101/3/2020
Question 21
Which one of the following functions has the fastest growth rate (for n > 1)?
1. n2 3. n log n
2. n3/100 4. n3
Question 22
Which one of the following functions has the slowest growth rate (for n > 1)?
1. n2 3. n
2. N log n 4. log n
Question 23
Which one of the following sets of functions is ordered according to growth rate from the
smallest to the largest?
Questions 24 and 25 are based on the table below. The table displays the running time of two
algorithms for an input size of n..
Algorithm n=4 N=8 N = 16
A 16 256 65536
B 6 11 20
Question 24
1. O(n2) 3. O(2n)
2. O(n) 4. O(log n)
29
Question 25
1. O(1) 3. O(1/n)
2. O(n) 4. O(log n)
30
COS2611/101/3/2020
Maximum mark: 80
Draw what is produced by the following C++ code. Assume the node is in the usual info-link
form with info of type int. (list and ptr are pointers of type nodeType.)
Given two ordered linked lists L1 and L2, write a function that deletes every element of L2
contained in L1. Your function should be a friend function of the class
orderedLinkedListType.
Template<class Type>
Void deleteOc (orderedLinkedList<Type> & L1, const
ordereLinkedList<Type> & L2)
31
Question 3: Recursion [6]
Operating systems use virtual memory to expand the amount of memory available for running
programs by swapping pages between RAM and virtual memory. One of the algorithms that can
be used to determine which page gets swapped out is the least recently used (LRU) algorithm.
One way of implementing this algorithm is to use a stack of page numbers, and when a page is
referenced it is removed from the stack and put on top. Thus the least recently used page
reference is at the bottom of the list. This is best implemented by using a linked list because
page references are removed from the middle of the list. However, for this exercise, assume
the LRU algorithm is implemented by using a stack.
Write a member function of class StackType that updates the stack when a page is
referenced. Assuming a stack that can hold 5 values, and the next page referenced is 7, then
the function searches the stack for page reference 7;
if it finds it, it removes it from the stack and places it at the top;
if there is no 7 in the list, the last page reference in the stack is removed and the 7 is
placed at the top of the stack.
Below is an example illustrating the contents of the stack before and after the function has run.
L={anb2n} where n ≥1, is a language of all words with the following properties:
32
COS2611/101/3/2020
One way to test if a word w belong to this language L is to use a stack to check if the number of
a’s balances the number of b’s. Use the following header and write a function
isInLanguageL2 that uses a stack to test if any word belongs to L. If w belongs to L then the
isInLanguageL2 should return true otherwise isInLanguageL2 should return false.
a) Write a function removeFirst that removes only the first occurrence of an item in a
circular queue without changing the order of the other elements in the queue. Use the
following header:
a) Sort the list using a selection sort. Show the state of the list after each call to the swap
procedure.
b) Sort the list using an insertion sort. Show the state of the list each time after the outer for
loop of the algorithm has iterated.
c) Sort the list using a quick sort with the middle element as pivot. Show the state of the list
after each call to the partition procedure.
You are not required to write code for this question. You need to trace through the different
sorting algorithms using the given list.
33
Question 7: Trees [6]
Trees can be used to express formulae. For the tree below, determine the result of the pre-
order, in-order, and post-order traversals. The edges showing empty sub-trees have not been
included.
34
COS2611/101/3/2020
10 SOURCES CONSULTED
See Appendix A.
11 IN CLOSING
Do not hesitate to contact your e-tutors or lecturers by email if you are experiencing problems
with the content of this tutorial letter or any aspect of the module.
We wish you a fascinating and satisfying journey through the learning material and trust that
you will complete the module successfully.
Enjoy the journey!
35
12 APPENDIX A: INTRODUCTION TO ALGORITHMS ANALYSIS
1. Introduction
An algorithm is a set of instructions to be followed (usually by a computer) to solve a
computational problem. By computational problem, we mean a problem for which a solution has
been designed and structured in the form of an algorithm which could be implemented using a
suitable programming language, and processed by a given computer.
Generally there can be more than one algorithm to solve a given computational problem and
these algorithms can be implemented using different programming languages on different
platforms. In order to cross-compare different algorithms, we need to analyse each of them in
terms of their space and time complexities. Space complexity refers to the theoretical
evaluation of how much space (memory load) is required by the algorithm if processed by the
computer, and time complexity refers to the time (processing time) taken by the algorithm to
complete (i.e. to solve the problem).
The aim of algorithm analysis is thus to assess the efficiency of an algorithm. Algorithm
efficiency entails not only the time required by the computer to process the corresponding
program, but also the memory resources required during its execution. However, a formal
algorithm analysis is usually performed theoretically without taking into account the underlying
architecture on which the corresponding program will be executed. This kind of formalism helps
evaluate the performance of the algorithm at design time before taking into account
programming language and hardware considerations.
Algorithm analysis is therefore a means by which the programmer may evaluate various
algorithms aiming at solving the same problem in order to choose the optimal solution. An
algorithm is said to be optimal if it is processed faster (time complexity) compared to its
counterparts, and utilizes fewer memory resources (space complexity) compared to the others.
Once we have an algorithm that correctly solves a problem, both its time and space
complexities should be evaluated in order to capture its efficiency compared to other algorithms
designed to solve the same problem.
This tutorial letter focuses on how to estimate the time and space complexities (requirements) of
an algorithm.
To analyse algorithms we first count the number of basic operations in a particular solution to
assess its efficiency. Then, we will express the efficiency of algorithms using growth functions.
A basic operation is an operation that takes one time unit to execute and is independent from
the programming language used. One unit time is simply the theoretical time taken by a basic
operation to complete on a given computer. In practice this time may vary from one computer to
36
COS2611/101/3/2020
An algorithm usually consists of basic operations that can be executed by the computer. Basic
operations include among others:
· Assignment operations (e.g.:today = “Monday”)
· Arithmetic operations (e.g.:salary = hoursWorked*hourlyRate)
· Comparison operations (e.g.:age == 20)
takes one unit of time to complete. Its evaluation depends on the unit time (in micro/
milliseconds) it takes the computer to execute a basic operation.
If for a given computer, a unit time is for example 1 micro-second, then approximately 1 micro-
second is required to complete the operation.
Total cost = 1 + 1 = 2
Instructions (1) and (2) are both basic operations. As for the first example, it will take
approximately 2 unit-times to complete the sequence.
Total cost = 1+ 1 = 2
For this simple conditional statement, only one branch is taken after the condition has been
evaluated.
37
sum = sum + i; n
}
2.2. Remarks
(a) Loops: The running time of a loop is at most the running time of the statements inside that
loop times the number of iterations.
(b) Nested Loops: The running time of a nested loop containing a statement in the inner most
loop is the running time of the statements multiplied by the product of the size of all loops.
(c) Consecutive Statements: The running time is the sum of the running time of each
statement.
(d) If/Else: The running time is that of the test instruction plus the larger of the running times of
both branches’ instructions
In many cases we can isolate a specific operation fundamental to the analysis of the algorithm,
and then ignore other basic operations (that have a negligible influence on the overall time
complexity, or are the same for all algorithms being considered to solve a particular problem).
Examples of such ignorable operations are initialization and incrementation of loop control
variables. The chosen basic operation may, for instance, be a very expensive operation
compared to the others, or it may be the only operation that really causes a growth of time
required. So then we only count the chosen basic operations. Of course we need to be careful
to make the right choice of basic operations. In our simple loop of example 4, we can safely
choose sum = sum + i as our basic operation, and use only this statement in our running
time calculations.
If the problem size of algorithm A (an arbitrary algorithm) is n then we can say, for example, that
Algorithm A requires 5*n2 time units to solve a problem of size n. The most important thing to
learn is how quickly an algorithm’s time requirement grows as a function of the problem size.
Algorithm A requires a running time that is proportional to n2. . An algorithm’s proportional time
requirement is known as growth rate. We can compare the efficiency of two algorithms by
comparing their growth rates.
If Algorithm A requires time proportional to f(n), Algorithm A is said to be order f(n), and it is
denoted as O(f(n)). The function f(n) is called the algorithm’s growth-rate function. Since the
capital O is used in the notation, this notation is known as the Big O notation.
If Algorithm A requires time proportional to n2, it is O(n2).
38
COS2611/101/3/2020
Consider the formal definition of the Big-O function, which is also given on page 15 of Malik
(2003):
3.1. Definition:
Let f and g be nonnegative real-valued functions in the input size n. We say that f(n) is Big-O of
g(n), written f(n) = O(g(n)), if there exists positive constants c and n0 such that f(n) cg(n) for all
nn0 .
Example:
An algorithm requires n2–3*n+10 seconds to solve a problem size of n. If constants c and n0
exist such that
c*n2 > n2–3*n+10 for all n ≥ n0 .
the algorithm is order n2(In fact, c is 3 and n0 is 2)
3*n2 > n2–3*n+10 for all n ≥ 2 .
Thus, the algorithm requires no more than c*n2 time units for n ≥ n0 ,
So it is O(n2)
If f1(n) is O(g1(n)) and f2(n) is O(g2(n)) then f1(n) f2(n) is O(g1(n) g2(n)) and
f1(n) + f2(n) is O(g1(n)+ g2(n)) .
e.g.: If an algorithm is O(n3)* O(4n2), it is also O(n3 *4n2) which is O(4n5) therefore
it is O(n5).If an algorithm is O(n3) + O(4n2), it is also O(n3 +4n2) therefore it is
O(n3).
Example 1:
If an algorithm takes 1 second to run with a problem size of 8, what is the time requirement
(approximately) for that same algorithm with a problem size of 16?
39
If its order is:
O(n) T(n) = (1*16) / 8 = 2 seconds
the basic operation is sum = sum + i, which has a cost of 1 unit-time and is executed n
times.
T(n)= n
Example 2:
In the following code fragment,
i=1;
sum = 0;
while (i <= n) {
j=1;
while (j <= n) {
sum = sum + i;
j = j + 1;
}
i = i +1;
}
the basic operation is sum = sum + i, which has a cost of 1 unit- time and is executed n*n
times.
T(n) = n*n
40
COS2611/101/3/2020
Example 3:
In the following code fragment,
the basic operation is x=x+1, which has a cost of 1 and is executed n3 times
T(n) = n3
4. What to analyse?
Consider a simple linear search where we are searching for a specific value (num) in an array of
size n:
inti=0;
int place=0;
while (num != arr[i] &&i<n)
i++;
if (i<n)
place=i;
Say we choose the comparison num != arr[i]as the basic operation. If num happens to be
equal to the first element in the array, then the comparison will be made once only. If num is not
equal to any element of the array, the comparison will be performed n+1 times. On average, if
we know that num occurs exactly once in the array, the comparison will be performed n/2 times.
As we can see an algorithm can require different times to solve different problems of the same
size.
Worst-Case Analysis – This is the maximum amount of time that an algorithm requires to solve
a problem of size n. This gives an upper bound for the time complexity of an algorithm. In the
worst case the linear search is O(n). This is achieved when the search item is not in the list or it
is the last item in the list.
Best-Case Analysis –This is the minimum amount of time that an algorithm requires to solve a
problem of size n. In our search the best case is when the search item is the first item in the list.
The best case time complexity is then O(1).
Average-Case Analysis –This is the average amount of time that an algorithm requires to
solve a problem of size n. The linear search is O(n) on average. We only focus in this course on
Worst-Case analysis of algorithms.
41
5. Space complexity
The analysis techniques used to measure space requirements are similar to those used to
measure time requirements. The asymptotic analysis of the growth rate in the time requirement
of an algorithm as a function of the input size, also applies to the measurement of the space
requirement of an algorithm. However, time requirements are measured in terms of basic
operations on the data structure performed by the algorithm, whereas space requirements are
determined by the data structure itself.
If we have an array with n integers and we want to keep the entire array in memory, the space
requirements will be O(n). If we have a two-dimensional array, then it will be O(n2). However, if
the two-dimensional array is not always filled up, and if we can find a way of only setting aside
the memory positions as and when we need them, we may save a lot on memory. In this
module you’ll study various data structures to accomplish such memory savings and various
algorithms that could accomplish the same task, but with different time complexities. As you get
acquainted with these data structures, you should also learn how to determine the space
complexity of each structure.
In the third-year level Artificial Intelligence module, COS3751, you will also encounter
algorithms that operate on implicit data structures, called search spaces. These data structures
are really huge and space complexity becomes an important issue when using them, even with
today's cheap memory. Search spaces are expanded dynamically; they are never generated
and kept in memory in totality. Different orders of expansions yield different space complexities.
6. Big-O Examples
//Code Fragment #1
for(int i = 0; i < 2n; i++)
sum++;
The problem size here is the value of n. Obviously, as the value of n gets bigger, the fragment
will take longer to run. Rather than trying to calculate exactly how long it will take to run for a
particular value of n, we are interested in how quickly the running time of this fragment
increases as n increases. The amount of time a program takes to run depends on how many
basic instructions must be executed. To obtain an estimate of this, we can examine the source
code and count how many assignment, increment, or conditional tests the fragment would
perform. There are four expressions in Fragment#1:
So for an input of size n, the number of basic operations this fragment executes is:
6n + 2
42
COS2611/101/3/2020
The constants in the formula above are not relevant - we are interested in the performance of
the fragment for large values of n- so we simply ignore the constant2 (See page 6 Section 3-2
of Tutorial Letter 102).
So →6n + 2 (ignoring 2)
→6n (ignoring the constant factor)
→n
This tells us, that the running time of the program is proportional to n. That is, if we double the
value of n we can expect the running time to be approximately double as well. This gives us an
indication of the scalability of the program – how well it will perform when the value of n is large.
This kind of analysis is actually quite exhausting. We can often analyse the running time of a
program by determining the number of times selected statements are executed. We can usually
get a good estimate of the running time by considering one type of statement such as some
statement within a looping structure. Provided the loop is iterating sequentially in a linear
fashion. Thus if we just consider sum++ we note that it runs 2n times. Ignoring the constant - the
Big O Notation of Fragment#1 is O(n). This works because Big O is just an estimate of the
running time.
//Code Fragment #2
for(inti = 0; i< n; i++)
for(int j = 0; j <i; j++)
sum++; //Line 3
We begin by analysing the number of times Line 3 is executed. When i = 1, Line 3 is executed
once. When i = 2, Line 3 is executed twice. In general, Line 3 is executed exactly i times.
Therefore, the total number of executions of Line 3 is: 1 + 2 + 3 + . . . + n - 1 times.
By mathematical proof, we know that:
1 + 2 + 3 + . . . + n = n(n + 1)/ 2
For mathematical buffs, this formula will be familiar. But if you have not seen this before, do not
worry about the proof, we will not expect you to know this and there are easier ways of finding
the Big O, which will be discussed shortly.
As you can see trying to count the exact number of times Line 3 is executed as a function of n
gets tricky, especially for this fragment. The following rule may be applied:
The Multiplication Rule: The total running time of a statement inside a group of nested
loops is the running time of the statements multiplied by the product of the sizes of all
the for loops. We need only look at the maximum potential number of repetitions of each
nested loop.
//Fragment #3
for(int i = 0; i < n*n; i++)
sum++;
for(int i = 0; i < n; i++)
for(int j = 0; j < n*n; j++)
sum++;
We consider only the number of times the sum++ expression within the loop is executed. The
first sum expression is executed n2 times. While the second sum++ expression is executed n *
n2 times. Thus the running time is approximately n2 + n3. As we only consider the dominating
term - the Big O Notation of this fragment is expressed by O(n3).
Note that there were two sequential statements therefore we added n2 to n3. This problem
illustrates another rule that one may apply when determining the Big O. You can combine
sequences of statements by using the Addition rule, which states that the running time of a
sequence of statements is just the maximum of the running times of each individual
statement.
/Fragment #4
We note that we have three nested for loops: the innermost, the inner and the outer for loops.
The innermost for loop potentially executes n*n times (the maximum value of j will be n*n-1).
The inner loop executes n*n times and the outer for loop executes n times. The statement
sum++; therefore executes not many times less than n*n * n*n * n, so the Big O Notation of
Fragment #4 is expressed by O(n5).
//Fragment #5
for(inti = 0; i<210; i++)
for(int j = 0; j < n; j++)
sum++;
Note that the outer loop runs 210times while the inner loop runs n times. Hence the running time
is:
210 * n. The Big O Notation of this Fragment is expressed by O(n)(ignoring constants).
//Fragment #6
for(int i = 1; i < n; i++)
for(int i = n; i > 1; i = i/2)
sum++;
The first loop runs n times while the second loops runs log n times. (Why? Suppose n is 32,
then sum++; is executed 5 times. Note 25 = 32 therefore log2 32 = 5. Thus the running time is
44
COS2611/101/3/2020
actually log2 n, but we can ignore the base.) Hence the Big O Notation of Fragment #6 is
expressed by O(n logn ) (Multiplication Rule.)
CONTACTED SOURCES
Baase, S. & Van Gelder, A. 2000. Computer Algorithms: Introduction to design and analysis. 3rd
Edition. Addison-Wesley: Massachusetts.
Malik, DS. 2003. Data structures using C++. Thompson: Canada.
Scaffer, CA. 1998. A practical introduction to data structures and algorithm analysis (Java
edition). Prentice-Hall: New Jersey.
45
13 APPENDIX B: Errata
13.1 Consider the following error that you may come across when compiling code from
Malik:
Execution terminated
For examination purposes inserting the this-> in front of inherited data members is not
necessary.
The following paragraph is not examinable and merely included for those students who
are interested in knowing why error (a) occurs.
When working with derived classes the compiler does not associate any inherited data
members as members of the derived class. The C++ standard says that unqualified names in a
template are generally non-dependent and must be looked up when the template is defined.
Since the definition of a dependent base class is not known at that time, unqualified names are
never resolved to members of the dependent base class. Where names in the template are
supposed to refer to base class members or to indirect base classes, they can be made
dependent by qualifying them.
13.2 This type of error occurs when friend functions are used in conjunction with template
classes.
You need to insert <> after the operator<< in the class definition only. This should get rid of
the warning above.
46
COS2611/101/3/2020
Please note that if you had switched to the compiler (mingw_gcc3.4.2.exe) which was supplied
to you this year then you will need to add the statements in bold below (or similar statements
depending on the class type) to any template class that overloads the ostream operator.
#ifndef H_MyList
#define H_MyList
#include <iostream>
#include <cassert>
using namespace std;
template<class Type>
class MyList;
template<class Type>
ostream& operator<<(ostream&, constlinkedListType<Type>&);
The friend declaration requires this additional syntax - the compiler will not be able to associate
the friend function with the class. As the class MyList is not fully declared yet. These lines of
code known as incomplete declarations are used to inform the compiler of the existence of a
class or function.
© 2020
Unisa
47