Data Structur and Algorithm
Data Structur and Algorithm
SCHOOL OF TECHNOLOGY
NJALA UNIVERSITY
NJALA CAMPUS
The following lessons introduce the topic of data structures by comparing how data
is actually stored in a computer with the abstract structures that programmers use. To
illustrate this comparison, several basic data structures such as lists, stacks, queues,
arrays, trees, graphs, sets sorting are described
Imagine that you are hired by techLinks Systems to organize all of their records into
an automated database system. The first thing you are asked to do is create a database
of names with all the company's management and employees. To start your work, you
Name Position
Aaron Manager
Charles VP
George Employee
Jack Employee
Janet VP
2|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
John President
Kim Manager
Larry Manager
Martha Employee
Patricia Employee
Rick Secretary
Sarah VP
Susan Manager
Thomas Employee
Zack Employee
But this list only shows one view of the company. You also want your database to
represent the relationships between management and employees at techLinks
Systems. Although your list contains both name and position, it does not tell you
which managers are responsible for which workers and so on. After thinking about
the problem for a while, you decide that a tree diagram is a much better structure for
showing the work relationships at techLinks Systems.
These two diagrams are examples of different data structures. In one of the data
structures, your data is organized into a list. This is very useful for keeping the names
of the employees in alphabetical order so that we can locate the employee's record
very quickly. However, this structure is not very useful for showing the relationships
between employees. A tree structure is much better suited for this purpose.
3|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
COMPUTER MEMORY
To fully understand how a computer can represent large data structures like our tree
diagram, we first need to understand some basic facts about computer memory. Every
piece of data that is stored in a computer is kept in a memory cell with a specific
address. We can think of these memory cells as being a long row of boxes where each
box is labelled with an address. If you have ever used a computer spreadsheet before,
you know that spreadsheets also can hold data. Computer memory is similar to this
with the exception that computer memory labelled boxes is linear.
The computer can store many different types of data in its memory like integers, real
numbers and characters. Once the computer stores data in the memory cells, it can
access the data by using the address of the data cells. For example, consider the
following instructions for adding two integers together.
4|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Notice how the computer performs operations by referring to the address of the
memory cells. These addresses are a very important component in creating various
data structures in computer memory. For example, suppose we want a data structure
that can store a group of characters as a word. In many computer languages, this data
structure is called a string. If we store the characters for the string 'apple' in the
computer's memory, it might look something like this.
In order for the computer to recognize that 'apple' is a string, it must have some way
of identifying the start and end of the characters stored in memory. This is why the
addresses of the memory cells are important. By using the addresses to refer to a
group of memory cells as a string, the computer can store many strings in a row to
create a list. This is one way that we could create a data structure to represent our list
of employees at techLinks Systems.
But what happens when we try to represent our tree diagram of techLinks Systems?
It doesn't make sense to store the names one after the other because the tree is not
linear. Now we have a problem. We want to represent a nonlinear data structure using
computer memory that is linear. In order to do this, we are going to need some way
5|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
of mapping nonlinear structures like trees or spreadsheet tables onto linear computer
memory. This problem will lead us to the next section.
In our last section, we discovered a problem with representing data structures that are
not linear. We needed some way to map these data structures to the computer's linear
memory. One solution is to use pointers. Pointers are memory locations that are
stored in memory cells. By using a pointer, one memory cell can "point" to another
memory cell by holding a memory address rather than data. Let's see how it works.
In the diagram above, the memory cell at address 2003 contains a pointer, an address
of another cell. In this case, the pointer is pointing to the memory cell 2005 which
contains the letter 'c'. This means that we now have two ways of accessing the letter
'c' as stored data. We can refer to the memory cell which contains 'c' directly or we
can use our pointer to refer to it indirectly. The process of accessing data through
pointers is known as indirection.
We can also create multiple levels of indirection using pointers. The diagram below
shows an example of double indirection. Notice that we must follow two pointers this
time to reach the stored data.
6|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
As you can see, pointers can become very complex and difficult to use with many
levels of indirection. In fact, when used incorrectly, pointers can make data structures
very difficult to understand. Whenever we use pointers in constructing data structures,
we have to consider the trade-off between complexity and flexibility.
The idea of pointers and indirection is not exclusive to computer memory. Pointers
appear in many different aspects of computer use. A good example is hyperlinks in
web pages. This links are really pointers to another web page. Perhaps you have even
experienced "double indirection" when you went to visit a familiar web site and found
the site had moved. Instead of the page you expected, you saw a notice that the web
pages had been moved and a link to the new site. Rather than clicking a single link,
you had to follow two links or two pointers to reach the web page.
In the previous section, we saw that it is very simple to create data structures that are
organized similar to the way the computer's memory is organized. For example, the
list of employee's from techLinks Systems is a linear data structure. Since the
computer's memory is also linear, it is very easy to see how we can represent this list
with the computer. Any data structure which organizes the data elements one after
the other is a linear data structure. So far we have seen two examples of linear data
structures: the string data structure (a list of characters) and the techLinks Systems list
(a list of strings).
Example String
7|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Example List
You may have noticed that these two examples of linear data structures resemble each
other. This is because they are both really different kinds of lists. In general, all linear
data structures look like a list. However, this does not mean that all linear data
structures are exactly the same.
Suppose we want to design a list to store the names of the techLinks employees in
the computer. One possible design is to organize the names similar to the example
picture above. Another possible design is to use the pointers we learned about in the
last section. While these two designs provide the same functionality (i.e. a list that can
hold names), the way they are implemented in the computer is much different. This
means that there is an abstract view of a list which is distinct from any particular
computer implementation.
You may have also noticed that the example picture of Name
George
list to the right. When we make a list of names, we
Jack
tend to organize this list in a column rather than a row.
Janet
In this case, the conceptual or logical representation of John
a row of strings. For most data structures, the way that Martha
Patricia
we think about them is far different from the way they
Rick
are implemented in the computer. In other words, the
Sarah
physical representation is much different than the Susan
8|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The most common linear data structure is the list. By now you are already pretty
familiar with the idea of a list and at least one way of representing a list in the
computer. Now we are going to look at a particular kind of list: an ordered list. Ordered
lists are very similar to the alphabetical list of employee names for techLinks Systems.
These lists keep items in a specific order such as alphabetical or numerical order.
Whenever an item is added to the list, it is placed in the correct sorted position so
that the entire list is always sorted.
Before we consider how to implement such a list, we need to consider the abstract
view of an ordered list. Since the idea of an abstract view of a list may be a little
confusing, let's think about a more familiar example. Consider the abstract view of a
television. Regardless of who makes a television, we all expect certain basic things like
the ability to change channels and adjust the volume. As long as these operations are
available and the TV displays the shows we want to view, we really don't care about
who made the TV or how they chose to construct it. The circuitry inside the TV set
may be very different from one brand to the next, but the functionality remains the
same. Similarly, when we consider the abstract view of an ordered list, we don't worry
about the details of implementation. We are only concerned with what the list does,
not how it does it.
Suppose we want a list that can hold the following group of sorted numbers: [2 4 6
7]. What are some of the activities that we might want to perform with our list? Well,
since our list is in order, we will need some way of adding numbers to the list in the
proper place, and we will need some way of deleting numbers we don't want from
the list. To represent these operations, we will use the following notation:
9|Page
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Each operation has a name and a list of parameters the operation needs. The
parameter list for the AddListItem operation includes a list (the list we want to add
to) and an item (the item we want to add). The RemoveListItem operation is very
similar except this time we specify the item we want to remove. These operations are
part of the abstract view of an ordered list. They are what we expect from any ordered
list regardless of how it is implemented in the computer.
In this section, we are going to look at two different ways of creating an ordered list
data structure to hold the following list [2 4 6 7]. First, we will create a list using an
array of memory cells. Next, we will create the same list using pointers. Finally,
we will compare these two approaches to see the advantages and disadvantages.
Array Implementation
One approach to creating a list is simply to reserve a block of adjacent memory cells
large enough to hold the entire list. Such a block of memory is called an array. Of
course, since we will want to add items to our list, we need to reserve more than just
four memory cells. For now, we will make our array large enough to hold six numbers.
The diagram below shows a graphical representation of our array in memory with the
list numbers.
2 4 6 7
9
10 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Our list
To add a number to the ordered list we must first locate the correct position in the array for
the number and then insert the new number.
In the diagram, you saw that there were two disadvantages to using an array to
implement an ordered list. First, you saw that the elements in the list must be kept in
sequence, that is, there must not be gaps in the list. If gaps are allowed, the computer
will not be able to determine which items are part of the list and which items are not.
For this reason, the ordered list structures that are implemented with arrays are known
as sequential lists.
The second disadvantage that you saw was that arrays have a fixed size and therefore
limit the number of items the list can contain. Of course we could try to increase the
size of the array, but it may not always be the case that the adjacent memory cells in
the computer are available. They could be in use by some other program. However, it
is quite likely that the computer does have available memory at some other non-
adjacent location. To take advantage of this memory, we need to design our list so
that the list items do not have to be adjacent.
Pointer Implementation
A second approach to creating a list is to link groups of memory cells together using
pointers. Each group of memory cells is called a node. With this implementation every
node contains a data item and a pointer to the next item in the list. You can picture
this structure as a chain of nodes linked together by pointers. As long as we know
where the chain begins, we can follow the links to reach any item in the list. Often
this structure is called a linked list.
11 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Notice that the last memory cell in our chain contains a symbol called "Null". This
symbol is a special value that tells us we have reached the end of our list. You can
think of this symbol as a pointer that points to nothing. Since we are using pointers
to implement our list, the list operations AddListItem and RemoveListItem will work
differently than they did for sequential lists. The diagram below shows how these
operations work and how they provide a solution for the two problems we had with
arrays.
2 275
4 342 6 103
7 230 8 Null
200 201 275 276 342 343 103 104 230 231
When we were working with arrays we discover two problems. First we have to shift
many items to insert new items in there correct positions, secondly the size of our
array was limited so our list was also limited. The linked list provides solution for this
Suppose we want to add number 3 to the ordered list above. To do this we only need to
change the position in our list. First we get an available memory location for the new item.
12 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Next we set the pointer for 3 to point to 4, the item that should follow 3 in the list. Finally
we change the pointer for 2 to point to the new item we just added.
Now suppose we need to remove the item 7 from the list. To do this we must point the
pointer of the preceding item, 6, to point to the next item, 8. Then we simply delete the 7
from the list.
Notice that with both the add and remove operations. We only needed to make a few
changes to update our linked list. But with an array, we often had to shift many items
to have other items added or removed. Linked list also provide us with flexible list
size. Unlike the array item, linked list items need not be in adjacent memory location,
to lengthen our list we first find another available memory location, store the new
item and then link this new item to our list. As long as we have free memory in the
computer, we can increase the size of our list.
By implementing our list with pointers, we are able to avoid the two disadvantages
we discovered with using sequential lists. However, this does not mean that linked
lists are the perfect solution. Whenever we use indirection in building a data structure,
it becomes much harder to find mistakes. For example, it is very easy to assign the
wrong address to a pointer and "short circuit" our list. In general, sequential lists are
simpler than linked lists but they are also more limited. Linked lists give us a great
amount of flexibility but this comes at the cost of increased complexity.
13 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Another common linear data structure is the stack. Just like we did with the ordered
list, we will examine the abstract view of a stack first and then look at a couple of
ways a stack can be implemented. In one way, a stack is very similar to a list except
that a stack is more restricted. The diagram below should give you a good idea of the
abstract view of a stack.
STACK
The columns on the left represent stack data structure. Notice that the stack has only one
operation. The stack data structures only allow items to add from one end. The process of
adding items unto a stack is known as PUSH. In a stack, Items can only be added from the
top of the stack.
Removing an item from the stack, this operation is known as popping items off from the
stack. Notice that items can only be removed from the top of the stack. Just like PUSH
operation, the POP operation is only valid for the top item in the stack.
From the stack, you will notice that items are always removed from the stack opposite the
order they were added. Also notice that the last item added to the stack is always the first
item to be removed from the stack.
14 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Now that you know how a stack works, you can see that this data structure is really a
restricted list. With the stack, we have restricted the access to one end of the list by
using the pop and push operations. The result of this restriction is that items in the
list pile one on top of the other. To get to the bottom item, we must first remove all
the items above it. This behaviour is sometimes described as "last-in, first-out" or LIFO
since the last item to enter the stack is the first item to leave the stack. With the stack,
the top item is always the last item to enter the stack and it is always the first item to
leave the stack since no other items can be removed until the top item is removed.
Let's take another look at the operations that can be performed on a stack. We will
represent these two operations with the following notation:
The PushStackItem operation has two parameters which are a stack and an item. This
operation adds the item to the top of the specified stack. The PopStackItem operation
only takes one parameter which is a stack. However, notice that this operation has the
keyword Item listed to the left. This keyword represents the item that is removed from
the top of the stack when the PopStackItem operation is done. These two operations
are part of the abstract view of a stack. They are what we expect from any stack
regardless of how it is implemented in the computer.
As we did with the ordered list, we are going to look at two implementations of a
stack. The first implementation uses an array to create the stack data structure, and
the second implementation uses pointers.
Array Implementation
15 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Notice that our array implementation retains one of the problems we saw with the
array implementation of an ordered list. Since our array is a fixed size, our stack can
only grow to a certain size. Once our stack is full, we will have to use the PopStackItem
operation before we can push any more items onto the stack. To make the size of our
stack more flexible, we can use pointers to implement the stack.
Pointer Implementation
16 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Notice that the stack operations can get a little tricky when we use pointers. To push
an item onto the stack, we need to find a free memory location, set the pointer of the
new location to the top of the stack, and finally set the stack pointer to the new
location. The order of these operations is very important. If we set the stack pointer
to the location of the new memory first, we will lose the location of the top of our
stack. This example shows the same trade-off that we saw earlier with the ordered list
implementations. While the array implementation is simpler, the added complexity of
the pointer implementation gives us a more flexible stack.
17 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The final linear data structure that we will examine is the queue. Like the stack, the
queue is a type of restricted list. However, instead of restricting all the operations to
one end of the list as a stack does, the queue allows items to be added at one end
of the list and removed at the other end. The diagram below should give you a good
idea of the abstract view of a queue.
Head tail
Queue
The row across the top represents the queue data structure. Notice that the queue has two
open ends labelled “Head” and “Tail”. The tail of the queue is where items are added to the
queue. Items are added onto the queue from it tail and this process is called ENQUEUE
operation.
In a queue, Items can only be added to the tail of the queue, not in the middle or from the
head.
Items are removed from the queue by dragging them from the Head of the queue. This
operation is known as DEQUEUE operation. Notice items can only be removed from the HEAD
of the queue. Items at the middle of the queue cannot be removed.
Notice that items are always removed from the queue in the same order they were added.
Also notice that the first items added to the queue is always the first to be removed from the
queue. This restriction placed on a queue causes this structure to be a "first-in, first-out" or
FIFO structure. This idea is similar to customer lines at a grocery store. When customer X is
18 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
ready to check out, he or she enters the tail of the waiting line. When the preceding customers
have paid, then customer X pays and exits the head of the line. The check-out line is really a
queue that enforces a "first come, first serve" policy.
Now let's take another look at the operations that can be performed on a queue. We
will represent these two operations with the following notation:
These two operations are very similar to the operations we learned for the stack data
structure. Although the names are different, the logic of the parameters is the same.
The EnqueueItem operation takes the Item parameter and adds it to the tail of
Queue. The DequeueItem operation removes the head item of Queue and returns
this as Item. Notice that we represent the returned item with a keyword located to
the left of the operation name. These two operations are part of the abstract view of
a queue. Regardless of how we choose to implement our queue on the computer, the
queue must support these two operations.
When we looked at the ordered list and stack data structures, we saw two different
ways to implement each one. Although the implementations were different, the data
structure was still the same from the abstract point of view. We could still use the
same operations on the data structures regardless of their implementations. With the
queue, it is also possible to have various implementations that support the operations
EnqueueItem and DequeueItem. However, in this section, we are only going to focus
on one implementation in order to highlight another distinction: the distinction
between the logical representation of a queue and the physical representation of a
queue. Remember that the logical representation is the way that we think of the data
19 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
being stored in the computer. The physical representation is the way the data is
actually organized in the memory cells.
To implement our queue, we will use an array of eight memory cells and two pointers
to keep track of the head and tail of the queue. The diagram below shows a snapshot
of a queue in the computer's memory. The queue currently contains five letter items
with 'L' at the head of the queue and 'O' at the tail of the queue.
Now let's consider how the EnqueueItem and DequeueItem operations might be
implemented. To enqueue letters into the queue, we could advance the tail pointer
one location and add the new letter. To dequeue letters, we could remove the head
letter and increase the head pointer one location. While this approach seems very
straightforward, it has a serious problem. As items are added and removed, our queue
will march straight through the computer's entire memory. We have not limited the
size of our queue.
Perhaps we could limit the size of the queue by not allowing the tail pointer to
advance beyond a certain location. This implementation would stop the queue from
traversing the entire memory, but it would only allow us to fill the queue one time.
Once the head and tail pointers reached the stop location, our queue would no longer
work.
20 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
A tree is just one example of a nonlinear data structure. Two other examples are
multidimensional arrays and graphs. In the next few sections, we will examine these
data structures to see how they are represented using the computer's linear memory.
For each of the data structures we examine, we will look at a simple implementation
for the data structure to see how it can be represented in physical memory. Then we
will compare this physical representation with the logical representation of the data
structure.
MULTIDIMENSIONAL ARRAYS
Let's return to our example of techLinks Systems. After you finish creating a list of the
company's employees, you are asked to make an electronic time sheet that shows the
number of hours that each employee worked during the week. You begin by
associating each data point (hours worked) with two labels: employee's name and day
of the week. To organize the data, you use the following logical representation.
21 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Now we have a new data structure called a two-dimensional array. We can see how
the data structure gets its name by comparing it with a typical array. All the arrays we
have seen so far were simply a group of contiguous memory cells. Since computer
memory is linear, the arrays were also linear or one-dimensional. Notice, however, that
each row of the table above looks like a typical, linear array. Our table is really a
collection of one-dimensional arrays with five memory cells. Each memory cell
represents a day of the week, and each array in the table represents an employee.
Just like a one-dimensional array is a collection of memory cells, a two-dimensional
array is a collection of one-dimensional arrays.
You may be wondering how we can represent our two-dimensional array in the
computer's memory. The diagram below shows the answer to this question by
comparing the logical representation (the table) with the physical representation in
memory
22 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
As you saw in the diagram, it is common to refer to array locations by specifying the
row and column numbers after the array name. If we named our table above "Hours"
then we could find the number of hours that Aaron worked on Wednesday at the
following array location: Hours[1, 3].
We can also have higher dimensional arrays that are collections of lower dimensional
arrays. For example, we could organize the time sheet for one month by making a
three-dimensional array. This array would be a collection of four weeks of time sheets
which are two-dimensional arrays.
Then we could find the number of hours that Aaron worked during the second
Wednesday of the month at the following array location: Hours[2, 1, 3]. The '2'
represents the second week of the month, the '1' represents the employee Aaron, and
the '3' represents Wednesday, the third day of the week.
23 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
TREES
Another common nonlinear data structure is the tree. We have already seen an
example of a tree when we looked at the employee hierarchy from techLinks Systems.
Let's take another look at this diagram with some of the important features of trees
highlighted.
In this diagram, we can see that the starting point, or the root node, is circled in blue.
A node is a simple structure that holds data and links to other nodes. In this case, our
root node contains the data string "John" and three links to other nodes. Notice that
the group of nodes circled in red does not have any links. These nodes are at the end
of the branches and they are appropriately called leaves or leaf nodes. In our diagram,
the nodes are connected with solid black lines called arcs or edges. These edges show
the relationships between nodes in the tree. One important relationship is the
parent/child relationship. Parent nodes have at least one edge to a node lower in the
tree. This node is called the child node. Nodes can have more than one child, but
children can only have a single parent. Notice that the root node has no parent, and
the leaf nodes have no children. The final feature to note in our diagram is the subtree.
At each level of the tree, we can see that the tree structure is repeated. For example,
the two nodes representing "Charles" and "Rick" compose a very simple tree with
"Charles" as the root node and "Rick" as a single leaf node.
24 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Now let's examine one way that trees are implemented in the computer's memory.
We will begin by introducing a simple tree structure called a binary tree. Binary trees
have the restriction that nodes can have no more than two children. With this
restriction, we can easily determine how to represent a single binary node in memory.
Our node will need to reserve memory for data and two pointers.
Using our binary node, we can construct a binary tree. In the data cell of each node,
we will store a letter. The physical representation of our tree might look something
like this:
Although the diagram above represents a tree, it doesn't look much like the tree we
examined from techLinks Systems. Because our tree uses pointers, the physical
representation is much different than the logical representation.
Definition of Trees
25 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Trees are as common and important as lists. And like lists there are many variations -
binary search trees, balanced trees, and heaps are the main ones we will look at.
A tree is very similar: it has property (1) but (2) is slightly relaxed:
If there is no limit on the number of successors that a node can have, the tree is called
a general tree. If there is a maximum number N of successors for a node, then the
tree is called an N-ary tree. In particular a binary (2-ary) tree is a tree in which each
node has 0, 1, or 2 successors.
The unique node with no predecessor is called the root of the tree. A node with no
successors is called a leaf - there will usually be many leaves in a tree. The successors
of a node are called its children; the unique predecessor of a node is called its parent.
If two nodes have the same parent, they are called brothers or siblings. In a binary
tree the two children are called the left and right.
26 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Drawing Trees
The root is at the top; below it are its children. An arc connects a node to each of its
children: we sometimes draw arrowheads on the arc, but they are optional because
the direction parent->child is always top->bottom.
Then we continue in the same manner, the children of each node are drawn below
the node.
In general, each child of a node is the root of a tree ``within the big tree''. For example,
B is the root of a little tree (B,D,E), so is C. These inner trees are called subtrees. The
subtrees of a node are the trees whose roots are the children of the node. e.g. the
subtrees of A are the subtrees whose roots are B and C. In a binary tree we refer to
the left subtree and the right subtree.
Path in a Tree
A path is any linear subset of a tree, e.g. A-B-E and C-F are paths. The length of a
path could be counted as either the number of nodes or the number of edges on the
path - in the lectures we will count the nodes; e.g. A-B-E has length 3. But be careful:
there is no agreed definition!
There is a unique path from the root to any node. The depth or level of a node is the
length of this path. When you draw a tree, it is very useful if all the nodes in the same
27 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
level are drawn as a neat horizontal row. The depth or height of a tree is the maximum
depth of the nodes in the tree.
Ordered Trees
A tree is ordered if there is some significance to the order of the subtrees. For example,
consider this tree:
If this is a family tree, there could be no significance to left and right. In this case the
tree is unordered, and we could redraw the tree exchanging subtrees without affecting
the meaning of the tree. On the other hand, there may be some significance to left
and right - maybe the left child is younger than the right... or (as is the case here)
maybe the left child has the name that is earlier in the alphabet. Then, the tree is
ordered and we are not free to move around the subtrees.
For now we will restrict ourselves to ordered trees. Like lists, ordered N-ary trees have
a nice recursive structural definition:
pre-order traversal
in-order traversal
post-order traversal
In each case, the algorithms for traversal are recursive - they call themselves.
28 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Pre-order traversal
DBACFEG
In-order traversal
ABCDEFG
Post-order traversal
ACBEGFD
29 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
E.g. let us look at what order the nodes will get processed given this tree:
The preceding diagrams show us the order in which the nodes will get written:
Pre-Order: A-B-D-C-E-F
In-Order: B-D-A-E-C-F
Post-Order: D-B-E-F-C-A
These three are certainly not the only possible traversal orders. Another very natural traversal
order is ``level by level'' - the root is processed first, all its children are processed next, then
all of their children, etc. down to the bottom level. This is called breadth first traversal. In the
above example, it would process nodes in the order: A-B-C-D-E-F. It is not difficult to write a
breadth-first traversal, but is not quite as simple as the traversal orders just described.
Expression Trees
Algebraic expressions such as
The terminal nodes (leaves) of an expression tree are the variables or constants in the
expression (a, b, c, d, and e). The non-terminal nodes of an expression tree are the
operators (+, -, , and ). Notice that the parentheses which appear in Equation do
30 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
not appear in the tree. Nevertheless, the tree representation has captured the intent
of the parentheses since the subtraction is lower in the tree than the multiplication.
The common algebraic operators are either unary or binary. For example, addition,
subtraction, multiplication, and division are all binary operations and negation is a
unary operation. Therefore, the non-terminal nodes of the corresponding expression
trees have either one or two non-empty subtrees. That is, expression trees are usually
binary trees.
31 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
GRAPHS
The last data structure that we will study in this section is the graph. Graphs are similar
to trees except they do not have as many restrictions. In the previous section, we saw
that every tree has a root node, and all the other nodes in the tree are children of
this node. We also saw that nodes can have many children but only one parent. When
we relax these restrictions, we get the graph data structure. The logical representation
of a typical graph might look something like this:
Notice that our graph does not have a root node like the tree data structure did.
Instead, any node can be connected with any other node. Nodes do not have a clear
parent/child relationship like we saw in the tree. Instead nodes are called neighbours
if they are connected by an edge. For example, node A above has three neighbours:
B, C, and D.
It is not hard to imagine how the graph data structure could be useful for representing
data. Perhaps each of the nodes above could represent a city and the edges
connecting the nodes could represent roads. Or we could use a graph to represent a
computer network where the nodes are workstations and the edges are network
connections. Graphs have so many applications in computer science and mathematics
32 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
that several algorithms have been written to perform standard graph operations such
as searching the graph and finding the shortest path between nodes of a graph.
Now that you have a basic idea of the logical representation of graphs, let's take a
look at one way that graphs are commonly represented in computers. The
representation is called an adjacency matrix, and it uses a two-dimensional array to
store information about the graph nodes. The adjacency matrix for our graph is given
below.
A B C D E F
A -- 1 1 1 -- --
B 1 -- 1 -- 1 --
C 1 1 -- -- -- --
D 1 -- -- -- 1 1
E -- 1 -- 1 -- --
F -- -- -- 1 -- --
Notice that the matrix has six rows and six columns labelled with the nodes from the
graph. We mark a '1' in a cell if there exists an edge from the two nodes that index
that cell. For example, since we have an edge between A and B, we mark a '1' in the
cells indexed by A and B. These cells are marked with a dark gray background in the
adjacency matrix. With our adjacency matrix, we can represent every possible edge
that our graph can have.
33 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
We can extend the idea of data types to include more than just the basic data types.
Our definition of data types consists of two parts: 1) values, and 2) operations. Now
suppose we extended our definition so that the first part included data structures. This
extension makes sense because our data structures are really just novel ways of
organizing values. We have already seen several examples of these extended or
abstract data types (ADTs). The examples that we studied are listed below.
AddListItem(List, Item)
Ordered List
RemoveListItem(List, Item)
34 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Notice that each of the ADTs above has the two elements required by our definition:
1) a particular data structure, and 2) operations related to the data structure. We call
these data types "abstract" because we have said nothing about how they are
implemented. Instead, we have defined an interface for using the data type which
consists of certain operations. The interface for an ADT remains the same regardless
of how the data structure and operations are implemented. For example, we can use
the stack ADT in a computer program by using the two operations defined in the
stack interface. We do not need to know whether these operations were implemented
using a linked list or an array.
35 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
In mathematical texts the type of a variable is usually deducible from the typeface
without consideration of context; this is not feasible in computer programs. Usually
there is one typeface available on computer equipment (i.e., Latin letters). The rule is
therefore widely accepted that the associated type is made explicit in a declaration
of the constant, variable, or function, and that this declaration textually precedes the
application of that constant, variable, or function. This rule is particularly sensible if
one considers the fact that a compiler has to make a choice of representation of the
object within the store of a computer.
1. A data type determines the set of values to which a constant belongs, or which
may be assumed by a variable or an expression, or which may be generated by an
operator or a function.
3. Each operator or function expects arguments of a fixed type and yields a result of
a fixed type. If an operator admits arguments of several types (e.g., + is used for
addition of both integers and real numbers), then the type of the result can be
determined from specific language rules.
Since constituent types may again be structured, entire hierarchies of structures may
be built up, but, obviously, the ultimate components of a structure are atomic.
Therefore, it is necessary that a notation is provided to introduce such primitive,
36 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
With this tool in hand, it is possible to define primitive types and to build
conglomerates, structured types up to an arbitrary degree of nesting. In practice, it is
not sufficient to have only one general method of combining constituent types into
a structure. With due regard to practical problems of representation and use, a
general-purpose programming language must offer several methods of structuring.
In a mathematical sense, they are equivalent; they differ in the operators available to
select components of these structures.
Variables and data types are introduced in a program in order to be used for
computation. To this end, a set of operators must be available. For each
standard data type a programming language offers a certain set of primitive,
standard operators, and likewise with each structuring method a distinct
operation and notation for selecting a component. The task of composition of
operations is often considered the heart of the art of programming. However, it
will become evident that the appropriate composition of data is equally
fundamental and essential.
The most important basic operators are comparison and assignment, i.e., the test for
equality (and for order in the case of ordered types), and the command to enforce
equality. The fundamental difference between these two operations is emphasized
by the clear distinction in their denotation throughout this text.
37 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
These fundamental operators are defined for most data types, but it should be
noted that their execution may involve a substantial amount of computational effort,
if the data are large and highly structured. For the standard primitive data types, we
postulate not only the availability of assignment and comparison, but also a set of
operators to create (compute) new values. Thus we introduce the standard
operations of arithmetic for numeric types and the elementary operators of
propositional logic for logical values.
A new, primitive type is definable by enumerating the distinct values belonging to it.
Such a type is called an enumeration type. Its definition has the form:
T is the new type identifier, and the ci are the new constant identifiers.
Examples
The definition of such types introduces not only a new type identifier, but at the
same time the set of identifiers denoting the values of the new type. These
identifiers may then be used as constants throughout the program, and they
enhance its understandability considerably. If, as an example, we introduce variables
s, d, r, and b.
VAR s: sex
VAR d: weekday
38 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
VAR r: rank
s := male
d := Sunday
r := major
b := TRUE
s := 1 d := 7 r := 6 b := 2
which are based on the assumption that c, d, r, and b are defined as integers and
that the constants are mapped onto the natural numbers in the order of their
enumeration.
Standard primitive types are those types that are available on most computers as
built-in features. They include the whole numbers, the logical truth values, and a set
of printable characters. On many computers fractional numbers are also
incorporated, together with the standard arithmetic operations. We denote these
types by the identifiers
Integer types
The type INTEGER comprises a subset of the whole numbers whose size may vary
among individual computer systems. If a computer uses n bits to represent an
integer, then the admissible values x must satisfy -2n-1 ≤ x < 2n-1.
It is assumed that all operations on data of this type are exact and correspond to
the ordinary laws of arithmetic, and that the computation will be interrupted in the
case of a result lying outside the representable subset. This event is called overflow.
39 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The standard operators are the four basic arithmetic operations of addition (+),
subtraction (-), multiplication (*), and division (/).
The type REAL denotes a subset of the real numbers. Whereas arithmetic with
operands of the types INTEGER is assumed to yield exact results, arithmetic on
values of type REAL is permitted to be inaccurate within the limits of round-off
errors caused by computation on a finite number of digits. This is the principal
reason for the explicit distinction between the types INTEGER and REAL, as it is
made in most programming languages.
The standard operators are the four basic arithmetic operations of addition (+),
subtraction (-), multiplication (*), and division (/). It is an essence of data typing that
different types are incompatible under assignment. An exception to this rule is made
for assignment of integer values to real variables, because here the semantics are
unambiguous. After all, integers form a subset of real numbers. However, the inverse
direction is not permissible: Assignment of a real value to an integer variable
requires an operation such as truncation or rounding.
The two values of the standard type BOOLEAN are denoted by the identifiers TRUE
and FALSE. The Boolean operators are the logical conjunction, disjunction, and
negation whose values are defined in the Table below. The logical conjunction is
denoted by the symbol &, the logical disjunction by OR, and negation by “~”. Note
that comparisons are operations yielding a result of type BOOLEAN. Thus, the result
of a comparison may be assigned to a variable, or it may be used as an operand of
a logical operator in a Boolean expression. For instance, given Boolean variables p
and q and integer variables x = 5, y = 8, z =10, the two assignments
p := x = y
q := (x ≤ y) & (y < z)
yield p = FALSE and q = TRUE.
40 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
p q p&q p OR q ~p
The standard type CHAR comprises a set of printable characters. Unfortunately, there
is no generally accepted standard character set used on all computer systems.
Therefore, the use of the predicate "standard" may in this case be almost
misleading; it is to be understood in the sense of "standard on the computer system
on which a certain program is to be executed."
The character set defined by the International Standards Organization (ISO), and
particularly its American version ASCII (American Standard Code for Information
Interchange) is the most widely accepted set. It consists of 95 printable (graphic)
characters and 33 control characters, the latter mainly being used in data
transmission and for the control of printing equipment.
41 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
INTRODUCTION TO ALGORITHMS
42 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Each operation in an algorithm must be sufficiently clear so that it does not need to
be simplified. Given a list of numbers, you can easily order them from largest to
smallest with the simple instruction "Sort these numbers." A computer, however,
needs more detail to sort numbers. It must be told to search for the smallest
number, how to find the smallest number, how to compare numbers together, etc.
Each operation in an algorithm must be doable, that is, the operation must be
something that is possible to do. Suppose you were given an algorithm for planting
a garden where the first step instructed you to remove all large stones from the soil,
this instruction may not be doable if there is a four ton rock buried just below
ground level. For computers, many mathematical operations such as division by zero
or finding the square root of a negative number are also impossible. These
operations are not effectively computable so they cannot be used in writing
algorithms.
43 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
you have no way of determining the effect of your command. The same is true with
algorithms. Only algorithms which produce results can be verified as either right or
wrong.
While our algorithm seems to be pretty clear, we have two problems. First, the
algorithm must have an infinite number of steps because there are an infinite
number of integers greater than one. Second, the algorithm will run forever trying to
count to infinity. These problems violate our definition that an algorithm must halt
in a finite amount of time. Every algorithm must reach some operation that tells it to
stop.
Specifying Algorithms
When writing algorithms, we have several choices of how we will specify the
operations in our algorithm. One option is to write the algorithm using plain English
although plain English may seem like a good way to write an algorithm, it has some
problems that make it a poor choice. First, plain English is too wordy. When we write
in plain English, we must include many words that contribute to correct grammar or
style but do nothing to help communicate the algorithm. Second, plain English is
too ambiguous. Often an English sentence can be interpreted in many different
ways. Remember that our definition of an algorithm requires that each operation be
unambiguous.
44 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
int a = 1;
int b = 0;
while (a <= 10)
{
b += a;
a++;
}
[Link] (b);
This algorithm sums the numbers from 1 to 10 and displays the answer on the
computer screen. However, without some special knowledge of the Java
programming language, it would be difficult for you to know what this algorithm
does. Using a programming language to specify algorithms means learning special
syntax and symbols that are not part of Standard English. For example, in the code
above, it is not very obvious what the symbol "++" or the symbol "+=" does. When
we write algorithms, we would rather not worry about the details of a particular
programming language.
What we would really like to do is combine the familiarity of plain English with the
structure and order of programming languages. A good compromise is structured
English. This approach uses English to write operations, but groups operations by
45 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
indenting and numbering lines. An example of this approach is the directions for
changing motor oil illustrated below.
Each operation in the algorithm is written on a separate line so they are easily
distinguished from each other. We can easily see the advantage of this organization
by comparing the structured English algorithm with the plain English algorithm.
For the remainder of this study, we will write our algorithms using the structured
English approach.
46 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
SORTING ALGORITHMS
Now that we have a definite way of writing our algorithms, let's look at some
algorithms for solving the problem of sorting. Sorting is a very common problem
handled by computers. For example, most graphical email programs allow users to
sort their email messages in several ways: date received, subject line, sender, priority,
etc. Each time you reorder your email messages, the computer uses a sorting
algorithm to sort them. Since computers can compare a large number of items
quickly, they are quite good at sorting.
47 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
available.
Our list of numbers will be stored in an array of memory cells like the diagram
below.
One important quality of a good algorithm is that it solves a class of problems and
not just one particular problem. A good sorting algorithm should provide a solution
to the problem of sorting for many types of items. Imagine if you were trying to
design an email program that allowed users to sort their messages by date received,
subject line, and sender. Would you want to write a new algorithm for each different
sort, or would you prefer to write a single algorithm that handled all three? Of
course you would prefer the latter approach, so when you designed your algorithm,
you would design it to solve a class of problems (e.g. sorting) rather than an
individual problem (e.g. sorting emails by date).
BASIC OPERATIONS
The sorting algorithms we will learn in the next few lessons share two basic
operations in common. These operations are the comparison operation and the
swap operation. We will look at each one in more detail before we examine our
sorting algorithms.
The comparison operation is simply a way of determining which item in a list should
come first. If we are sorting a list of numbers from smallest to largest, the
comparison operation tells us to place the number with the least value first. If we
are sorting a list of letters alphabetically, the comparison operation tells us to place
48 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
'a' before 'b', 'b' before 'c', and so on. We will see that a sorting algorithm must
usually perform many comparisons in order to correctly sort a list.
The swap operation is one way we move items as we are sorting. By swapping small
items with large ones, we can place all the items in the correct order. When we use
computers for sorting, the swap operation can be a little tricky because of the way
computers copy data from one memory location to another. Using the example
below, see if you can correctly determine the algorithm for the swap operation
5 4
Cell A Cell B
Use the mouse to swipe the content of the cells what problem do you discover?
How do you solve this problem?
Notice that this operation requires three copies. It is important to remember that
the swap operation is really a combination of copy operations. In our next lesson,
we will learn a sorting algorithm called the Simple Sort that uses just the copy
operation rather than the swap operation.
49 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Simple Sort can be used by a computer. Let's see what this algorithm looks like and
how it can be used to sort numbers in a computer.
For our algorithm to work, we must replace our original number with a special
marker so it will not be considered again. The steps below illustrate how the Simple
Sort algorithm works on a computer.
1. First, we give the computer a list of unsorted numbers. These numbers are
stored in a group of contiguous memory cells called an array. Each memory
cell of the array holds a single number.
2. As the computer sorts these numbers, it will repeatedly compare them to find
the smallest number. This is similar to the comparisons made when sorting
our hand of cards. Each time we compared two cards and kept the smaller of
50 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
the two. Then we compared this card to the remaining cards until we found a
smaller one or checked all the cards. The computer uses the same process
only with numbers rather than cards.
3. Once the smallest number is found, the computer will copy this number to a
new array of memory cells and replace the old number with a special number
called MAX. MAX is the largest number a single memory cell can hold. None
of the remaining numbers can be larger than MAX, so this number is a good
choice for marking memory cells that have already been sorted.
Unsorted Array
Sorted Array
4. Next, the computer begins searching for the smallest number in the unsorted
list. Although it is easy for us to scan the numbers and select the 2 as
smallest, the computer must compare all the memory cells in the unsorted
array to be certain which number is smallest. This means the computer must
perform six comparisons: (7 < 8), (7 > 5), (5 > 2), (2 < 4), (2 < 6), and finally
(2 < 3) Once the comparisons are done, the computer copies 2 to the sorted
array and replaces the original 2 with MAX.
Unsorted Array
Sorted Array
51 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
5. Now the computer begins searching for the smallest number again. Six more
comparisons are required to determine that 3 is smallest: (7 < 8), (7 > 5),
(5 < MAX), (5 > 4), (4 < 6), and finally (4 > 3). Now we can see the
importance of replacing 2 with MAX in our previous step. If we had not made
this change, then 2 would have been selected as the smallest number again.
After copying 3 to the sorted array, the computer also replaces the original
with MAX.
Unsorted Array
Sorted Array
6. With six more comparisons, the computer selects 4 as the smallest number,
copies it to the sorted array, and replaces the original with MAX.
Unsorted Array
Sorted Array
Unsorted Array
52 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Sorted Array
Now let's look at how the Insertion Sort algorithm would work inside a computer.
Below is our modified algorithm for sorting a list of numbers.
The steps below illustrate how the Insertion Sort algorithm works on a computer.
1. First, we give the computer a list of unsorted numbers and store them in an
array of memory cells
53 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
2. To begin the sort, the computer divides the sorted and unsorted sections of
the list by placing a marker after the first number. To sort the numbers, it will
repeatedly compare the first unsorted number with the numbers in the sorted
section. If the unsorted number is smaller than its sorted neighbour, the
computer will swap them.
4. Now the first number in the unsorted section is 5. 5 is less than 8, so the
computer swaps these numbers.
54 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
6. Now 5 is in the correct order, so the computer advances the marker one
position. This time two comparisons and two swaps were needed to sort the
number.
7. Now the first number in the unsorted section is 2. 2 is less than 8, 7, and 5,
so after three comparisons and three swaps, 2 arrives at the correct sorted
position, and the computer advances the sort marker.
8. Now the first number in the unsorted section is 4. 4 is less than 8, 7, and 5
but it is not less than 2. This time the computer performs four comparisons
and three swaps to put the 4 in the correct order. Only three swaps were
needed since the 2 and the 4 did not need to be switched. After these
comparisons and swaps, the computer advances the sort marker.
9. Now 6 is the first number in the unsorted section. After three comparisons
and two swaps, the computer places the 6 in the correct position between 5
and 7. Notice that the computer did not need to compare the 6 with the 2 or
the 4 since it already knows these numbers are less than 5. Once the
computer finds a number in the sorted section less than 6, it knows it has
found the correct position for 6 and it can advance the sort marker.
55 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
10. The final unsorted number is 3. To find the correct position for 3, the
computer must compare it with every number in the unsorted section.
However, only five swaps are required since the first number (2) is less than 3.
After moving 3 to the correct position and advancing the sort marker, the
Insertion Sort is complete since the unsorted section is empty.
Let’s see how the computer would perform this sort with numbers. Below is our
modified algorithm for sorting a list of numbers.
56 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The steps below illustrate how the Selection Sort algorithm works on a computer.
1. First, we give the computer a list of unsorted numbers and store them in an
array of memory cells.
2. To begin the sort, the computer divides the sorted and unsorted sections of
the list by placing a marker before the first number. To sort the numbers, the
computer will repeatedly search the unsorted section for the smallest number,
swap this number with the first number in the unsorted section, and update
the sort marker.
3. To find the smallest number in the unsorted section, the computer must make
six comparisons: (7 < 8), (7 > 5), (5 > 2), (2 < 4), (2 < 6), and (2 > 3). After
these comparisons, the computer knows that 2 is the smallest number, so it
swaps this number with 7, the first number in the unsorted section, and
advances the sort marker.
4. Now five more comparisons are needed to find the smallest number in the
unsorted section: (8 > 5), (5 < 7), (5 > 4), (4 < 6), and (4 > 3). After these
comparisons, the computer swaps 3, the smallest number in the unsorted
section, with 8, the first number in the unsorted section, and advances the
sort marker.
57 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
5. This time four comparisons are needed to determine that 4 is the smallest
number in the unsorted section: (5 < 7), (5 > 4), (4 < 6), and (4 < 8). After
these comparisons, the computer swaps 4 with 5 and then advances the sort
marker.
7. This time only two comparisons are needed to determine that 6 is the
smallest number: (7 > 6) and (6 < 8). After these two comparisons, the
computer swaps 6 with 7 and then advances the sort marker.
8. Now we only need a single comparison to find the right position for 7:
(7 < 8). Since 7 is the smallest number and it is also the first number in the
unsorted section, the computer does not need to swap this number. It only
needs to advance the sort marker. Now there is only one number in the
58 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
unsorted section, so the list of numbers is sorted and the Selection Sort
algorithm is complete.
In this section, we introduce the concept of algorithms and discuss the issues of
fundamental analysis of algorithms. An algorithm is a clearly specified set of simple
instructions to be followed to solve a problem. In other words, it is a step-by-step
procedure for taking any instance of a problem and producing a correct answer for
that instance.
Problem Given a non-empty set of numbers, what is the minimum element of the
set?
Instance:
What is the minimum element of (2, 5, 8, 3) entered on a single line from the
keyboard?
Algorithm 1.1 is a solution for solving the problem of finding the minimum of a set
of data that are input from the keyboard. The algorithm should give a correct
answer for any set of data.
INPUT: nothing
OUTPUT: the minimum
59 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
1: read min;
2: while not eoln do
3: read x
4: if x < min then
5: min ← x
6: end if
7: end while
8: print min;
Implementation
import [Link];
int min() {
Scanner input = new Scanner( [Link] );
[Link]("x=? (999 to end) ");
int x = [Link]();
int min = x;
while (x!=999) {
[Link]("x=? (999 to end) ");
x = [Link]();
if (x < min) {
min = x;
}
}
return min;
}
60 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Efficiency
In this module, our goal is not only to develop a working algorithm, but also an
efficient algorithm for a given problem. The speed of hardware computation of basic
operations has been improved dramatically, but efficiency matters more than ever
today. This is because our ambition for computer applications has grown with
computer power. Many areas demand a great increase in speed of computation.
Examples include the simulation of continuous systems, high resolution graphics,
and the interpretation of physical data, medical applications, and information
systems.
On the other hand (and more importantly), an algorithm may be so inefficient that,
even with computation speed vastly increased, it would not be possible to obtain a
result within a useful period of time. The time that many algorithms take to execute
is a non-linear function of the input size. This can reduce their ability to benefit from
the increase in speed when the input size is large.
61 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Measures of performance
An algorithm consists of a set of ordered instructions and the time complexity, that
is, the number of execution steps in an algorithm, is irrelevant to the real time. Note
our main interest here is in an algorithm instead of a program. A program is an
implementation of an algorithm. The execution time of a program depends on the
implementation including not only the operating system but also the speed of the
computer itself.
The same program may run faster on a computer with a faster CPU, but the same
algorithm should perform the same number of algorithmic steps to accomplishment
a task. Normally we are concerned with the time complexity rather than space
complexity of an algorithm. The reasons are that firstly it becomes easier and
cheaper to obtain space. Secondly techniques to achieve space efficiency by
spending more time are available.
Observation
62 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The complexity of an algorithm normally depends on the size of input. The number
of operations may depend on a particular input.
Solution
For different sets of input data, we analyse the performance of an algorithm in the
worst case or in the average case. For different algorithms, we focus on the growth
rate of the time taken by the algorithms as the input size increases. The time
complexity of an algorithm can be expressed by a function of input size: T (n). We
are normally interested in the behaviour of T (n) as n grows large.
ALGORITHM ANALYSIS
63 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Secondly, algorithms may behave differently for different input sizes, and we would
like to estimate their computational complexity for large inputs. For some problems
we simply have not found an efficient algorithm yet, but we may find those
algorithms feasible and useful for input within some limited range.
But what makes an algorithm good? This is the question we are going to discuss in
this section.
Evaluating Algorithms
What makes an algorithm good? The three most important criteria are correctness,
efficiency, and simplicity.
Correctness is clearly the most important criterion for evaluating an algorithm. If the
algorithm is not doing what it is supposed to do, it is worthless. Unfortunately, it is
not always easy to establish the correctness of an algorithm; it may actually require
complicated mathematical proofs to do this.
The focus of the section will be on efficiency. We would like our algorithms to make
efficient use of the computer’s resources. In particular, we would like them to run as
fast as possible using as little memory as possible. To keep things simple, we will
concentrate on the runtime of the algorithms and will only occasionally look at the
space (i.e., amount of memory) they need.
The third important criterion is simplicity. We would like our algorithms to be easy
to understand and easy to implement. Unfortunately, there often (but not always) is
a trade-off between efficiency and simplicity: More efficient algorithms tend to be
more complicated. It depends on the particular task which of the two criteria is more
64 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
important. If a program is used only once or few times, the cost of implementing a
very efficient, but also very complicated algorithm may exceed the cost of running a
simple, but inefficient algorithm. On the other hand, if a program will be used very
often, the cost of running an inefficient algorithm over and over again may by far
exceed the cost of implementing an efficient complicated algorithm. It is up to the
programmer to decide which way to go in any particular situation.
Counting steps
Given two algorithms A1 and A2, which one is more efficient? In other words, which
one has lower computational complexity? To answer this question, one way is to
simply count the execution steps of each algorithm and compare the numbers of
the steps of the two.
Example
INPUT: n
OUTPUT: sum
1: sum ← 0
2: for k ← 1, k ≤ n, k ← k + 1 do
3: sum ← sum+ k
4: end for
5: print sum
From the fact
we have
𝑛(𝑛−1)
∑𝑛𝑘=1 k =
2
INPUT: n
OUTPUT: sum
1: print n(n + 1)/2
65 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Implementation
We look at the time complexity by counting the steps taken in execution. Let any
assignment, arithmetic computation +,-,*,/, read, print be all counted as one step. So
for Algorithm 1.2, it takes 1 + n × 1 + 1 = n + 2 steps; and for Algorithm 1.3, it
takes 1 + 1 + 1 = 3 steps to execute. Obviously, Algorithm 1.3 is more efficient in
terms of execution time.
How about the space efficiency? Let a simple variable require one unit of storage.
Algorithm 1.2 needs three units since it involves three variables sum, k and n, and
Algorithm 1.3 only needs two units since it involves only two variables n and sum.
We can therefore conclude that Algorithm 1.3 is more efficient in terms of storage,
too.
In fact, Algorithm 1.3 has an important advantage, that is, it takes constant time to
execute no matter how large n is. This means that it takes the same amount of time
to run no matter how many such numbers need to be added up. We have done an
analysis. Had we, however, to undertake all the counting every time to analyse an
66 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
algorithm, the task would be tedious and quickly become infeasible. We need some
easier approach.
Asymptotic behaviour
We are often interested in the rate of growth of the time required for an algorithm
when the input size gets larger. So the lower order terms of the time complexity T
(n) could be ignored, where n is a positive integer. In other words, we only need to
master the asymptotic behaviour of T (n). Here the term asymptotic means
approximate in a specific way.
Big O notations
The behaviour of an algorithm usually depends not only on the size of the input, but
also on the input itself. Look at Algorithm 1.4 which determines whether integer x is
an element of array Y [0..n − 1], where n is a non-negative integer.
Example
67 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
We therefore need to consider the behavior of the algorithm for two special cases,
namely the worst case and the average case.
In terms of time complexity, the worst case is the situation where the algorithm
would take the longest time. The average case is the case where the average
behavior is estimated after every instance of the problem has been taken into
consideration. We define two functions of n, the input size, for the two cases
respectively.
Let Ti(n) be the time complexity of the algorithm when given the ith instance, for 1
≤ i ≤ k. Let pi be the probability that this instance occurs. Then the time complexity
for The worst case:
W(n) = max
1≤i≤k
Ti(n)
The average case:
A(n) =
Xk
i=1
piTi(n)
68 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
The worst case analysis could help to provide an estimate for a time limit for a
particular implementation of an algorithm. It is particularly useful in real time
applications. The average case analysis is more meaningful in providing an overall
picture because it computes the number of steps performed for each possible input
instance of size n and then takes the (probability-weighted) average.
RECURSIVE ALGORITHM
A recursive algorithm calls itself which usually passes the return value as a
parameter to the algorithm again. This parameter is the input while the return value
is the output.
One or more simple cases of the problem (called stopping cases) have a
simple, non-recursive solution.
For the other cases, there is a process (using recursion) for substituting one or
more reduced cases of the problem that are closer to a stopping case.
Eventually the problem can be reduced to stopping cases only, all of which
are relatively easy to solve.
The recursive algorithms that we write will generally consist of an if statement with
the form shown below:
if (the stopping case is reached)
{
Solve it
}
69 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
else
{
Reduce the problem using recursion
}
Let's assume that for a particular problem of size N, we can split this problem into
one involving a problem of size 1, which we can solve (a stopping case), and a
problem of size N - 1, which we can split further. If we split the problem N times, we
will end up with N problems of size 1, all of which we can solve.
Recursive Multiplication
Problem 1. Multiply 6 by 2.
Because we know the addition tables, we can solve problem 2 but not problem 1.
However, problem 1 is simpler than the original problem. We can split it into the
two problems 1.1 and 1.2, leaving us three problems to solve, two of which are
additions.
Problem 1. Multiply 6 by 2.
70 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Even though we don't know the multiplication tables, we are familiar with the simple
rule that, for any M, M x 1 is M. By solving problem 1.1 (the answer is 6) and
problem 1.2, we get the solution to problem 1 (the answer is 12). Solving problem 2
gives us the final answer, 18.
Factorial
To find N!:
1. If N = 1 then N! = 1;
2. Otherwise N! = N x (N - 1)!
We have defined the “!” operation in terms of “!”. Notice that the definition is not
circular, because the “!” is applied to a smaller and smaller number each time until it
is applied to 1. Here is the definition applied to calculate 5!.
Recursive Calculation of 5!
5! = 5 x 4!
= 5 x (4 x 3!)
= 5 x (4 x (3 x 2!))
= 5 x (4 x (3 x (2 x 1!)))
= 5 x (4 x (3 x (2 x 1)))
= 5 x (4 x (3 x 2))
= 5 x (4 x 6)
= 5 x 24
= 120
71 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
Try the definition on some other numbers to make sure you understand how the
recursion works. You will discover that N! gets very large very quickly: Even an
innocent-looking calculation, such as 10!, produces a rather large number
(3,628,800). In fact, if you were to write a program to calculate N! and run it on a
computer using 16 bits to represent an integer, your program could not calculate
factorials larger than 7!, because 8! > 32767. On a computer with a 32-bit integer
representation, your program would fail to compute 14!.
if (n == 1) //terminating case
return 1;
return iRet;
This is a more elegant solution than our procedural method. Two key points in this
recursive method are the terminating case and the recursive call. The recursive call
occurs when the method calls itself.
The method above will wait for the recursive call to return before continuing. The
recursive call will also make another recursive call and wait for that to finish... and so
on. The methods are stacking until one method finally returns with a value. That's
where the terminating case comes in. The terminating case stops the function from
making any further calls to itself. The problem is divided to the point where we are
72 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Data Structure and Algorithm BIT/CS YR2 Njala University
computing 1!. Since that answer is trivial, there are no more recursive calls and it
returns immediately. This triggers all of the previous recursive calls to return, one-
by-one, and in order.
73 | P a g e
A. Kallon Lecture Notes First Semester - 2014 / 2015 AY.
Stacks and queues differ in their data handling operations primarily in the ends where data can be added or removed. In a stack, items can only be added or removed from one end, referred to as the top. The operations are known as PUSH and POP, respectively. Stacks follow a Last In, First Out (LIFO) principle . In contrast, queues allow items to be added at the tail and removed from the head, following a First In, First Out (FIFO) approach. This distinction in operation illustrates how stacks restrict operations to one end while queues use both ends for adding and removing items .
The Simple Sort algorithm differs from the Selection Sort primarily in how it manages and marks unsorted elements during sorting. Simple Sort repeatedly finds and removes the smallest unsorted number, moving it to a new sorted list, and replacing it in the original list with a marker like MAX to avoid re-sorting . In contrast, the Selection Sort swaps the smallest unsorted number with the first unmarked (unsorted) position until the list is fully sorted, using a marker to incrementally delimit the sorted section . The fundamental difference lies in the replacement versus swapping approach and marker usage for sorted sections.
Advantages of using a linked list for stack implementation include flexibility in size, as the stack can grow as long as there is free memory, and no predetermined size limits like those with array-based stacks. This is particularly useful when the number of elements cannot be predicted in advance . However, linked list implementations introduce complexity due to pointer management, requiring careful handling to ensure the stack's integrity. In contrast, array-based stacks are simpler but limited by a fixed size and require resizing when limits are reached, which is often resource-intensive .
Well-ordered operations enhance the effectiveness of algorithms by ensuring that the sequence in which instructions are executed is clear and correct, preventing errors in execution. This clarity is critical, especially in computing, where ambiguous order can lead to incorrect operations being performed or uncertainty about which operation should occur next. The well-ordered nature of algorithms allows for systematic and predictable problem-solving, which is essential for their execution by computers .
A pointer-based list is more advantageous than an array-based list in scenarios where memory constraints inhibit the allocation of contiguous blocks and when data manipulation requires frequent insertions and deletions. Because pointer-based lists do not require contiguous memory, they can leverage fragmented memory efficiently and expand dynamically as long as there is available memory, whereas array-based lists face size limitations and require time-consuming shifts for element management. This makes pointer-based lists preferable in applications where data entities are constantly being altered .
Linked lists overcome the limitations of arrays by allowing memory cells to be linked together using pointers, instead of requiring them to be contiguous. This flexibility solves two primary problems associated with arrays: the need to shift many items to insert new items at the correct positions, and the fixed size limitation of an array. With linked lists, as long as there is free memory, the list can be expanded by adding nodes at any non-adjacent memory location, circumventing the contiguous memory restriction of arrays .
The Selection Sort algorithm determines the smallest number in an unsorted section by iterating through all items in that section and performing a series of comparisons to identify the smallest element. Each pass reduces the problem size by moving the smallest found number to the front of the unsorted section and then marking it as sorted . Although it is simple to understand and implement, Selection Sort is considered inefficient due to its high time complexity of O(n^2), making it unsuitable for large datasets as it performs unnecessary comparisons and swaps regardless of the initial order of elements .
Queues manage data entry and removal by allowing insertions at the tail and removals from the head, following a First In, First Out (FIFO) method. This contrasts with stacks, where both entry (PUSH) and removal (POP) are restricted to the top, following a Last In, First Out (LIFO) order. Queues ensure that the first elements added are the first to be processed, which is ideal for tasks requiring fair and sequential processing. In contrast, stacks prioritize the most recently added elements for processing, which suits tasks like undo functionalities .
It is important for each operation in an algorithm to be unambiguous to ensure clarity and precision in instructions. Unambiguous operations guarantee that the algorithm can be correctly and consistently executed without misunderstandings or errors during processing. For computers, which require a detailed breakdown of even simple instructions, ambiguity can lead to incorrect operations or failure to execute. Thus, algorithms must use primitive operations that computers can execute unambiguously to achieve desired outcomes effectively .
When using pointers in implementing data structures such as linked lists and stacks, challenges include increased complexity and potential errors in managing memory addresses. Mistakes, such as assigning incorrect addresses, can easily cause problems like "short circuiting" the list. This complexity contrasts with arrays, which are simpler since they do not involve pointer manipulation; their elements are stored in contiguous memory locations, making management straightforward but limiting flexibility in terms of size and insertion/deletion operations .