lab11

advertisement
Laboratory 11
Array Processing
Introduction
In this lab we will learn how to perform basic processing of arrays. These are the most
commonly performed tasks on arrays. We will see that for loops are designed to work
hand-in-hand with array processing.
Arrays have the potential to be very large, unlike the more fundamental types such as ints
and floats. This has a number of ramifications, but primarily it means that we should be
careful when writing functions that process arrays since a little bit of inefficiency will be
multiplied many times over.
Key Concepts







Arrays as parameters
Finding the maximum element
Finding the minimum element
Determine whether two arrays are equal
Verifying order
Deleting an element
Inserting an element
Before the Lab
Read Sections 9.1 – 9. 3 in Cohoon and Davidson.
Preliminary





Turn the PC on, if necessary.
Access the network.
Copy the source files from the Lab 11 Source Files folder.
Start up CodeWarrior.
Open the LabWork project.
Arrays as Function Parameters
The array-processing algorithms that we will study in this lab are among the most
commonly used procedures in programming. Therefore, we would like to write each one
as a function that may be called on at any time and receive any array of any size. In other
words, we want to write these functions in full generality. To accomplish this, we must
pass the arrays as parameters to the functions. Furthermore, we must also pass the
number of elements in the array since that information is necessary for the correct
processing of the array and it is not passed automatically as part of the array. Thus, a pair
of parameters that will be common to all of the functions in this lab is
(int a[], int size)
The first represents an array of integers. The second is the number of elements to be
processed in the array (not necessarily the capacity of the array). Note the use of the
empty square brackets []. This indicates that the parameter is the name of an array. It
permits us to write expressions such as a[i] within the function.
How do we indicate whether to pass an array by value or by reference? It turns out that
arrays are always passed by reference! We have no choice. This is done for reasons of
efficiency. Arrays have the potential to be huge. If one were passed by value, then the
function would have to make a copy of the array before processing it. That would be a
tremendous waste of time.
So how do we protect an array if we do not want the function to change it? The standard
method is to pass it as a constant reference. That is, the parameter should be written as
const int a[]
Then the elements of the array cannot be modified by the function.
Finding the Maximum or Minimum Element of an Array
Often we need to locate the largest element in an array. For example, in Lab 12 we will
look at a sorting algorithm that repeatedly locates the largest element in an array as a way
of putting the elements in order.
What algorithm should we follow to accomplish this? In this case, the best guide is to
imagine that you have a very long list of random numbers in front of you and you need to
find the largest number in the list.

How would you do it? Give this some thought. You cannot just “pick out” the
largest number because there are too many of them to tell at a glance which is
largest.
The best method is to begin with the first number and consider it to be the largest number
found so far. Then compare it to the other numbers in the list, beginning with the second
number. If they are smaller, then ignore them. But if we find one that is larger, then it
becomes the new “largest so far,” replacing the previous largest so far. When we reach
the end of the list, the number that is then the largest so far is actually the largest in the
list.
Observation




Open the file FindMax.cpp.
Read the function findMax() and be sure you see how it applies the above
algorithm.
Add FindMax.cpp to the LabWork project.
Run FindMax.cpp to see that it works.
Experimentation
The algorithm for finding the smallest member of an array is similar except that we keep
track of the “smallest so far” rather then the “largest so far.”
In the file FindMax.cpp,







Write a function findMin() that will find the minimum element in an array.
Add lines in the main function that will call findMin() and print the result.
Run FindMax.cpp to be sure that findMin() works.
Will the findMax() and findMin() functions work properly if the array contains
only one element?
Run FindMax.cpp with an array of one element to see if it works.
What will the findMax() and findMin() functions do if the array is empty? Is
this acceptable?
Run FindMax.cpp with an empty array to see what happens.
Determining Whether Two Arrays are Equal
The C++ language does not automatically permit us to compare arrays using the ==
operator. That is, if array1 and array2 are names of arrays, then the expression
array1 == array2
will not tell us whether the two arrays contain the same values. It is a legal expression,
but it compares the addresses of the arrays, not the contents of the arrays. Therefore, it
will be true only when the arrays are one and the same array.
To compare the contents of two arrays, we must use a for loop to compare the individual
elements. The condition for equality of arrays is that their sizes be equal and that for
every value of i from 0 to size - 1, the i-th values be equal. If follows that if the sizes
are not equal or if a single pair of elements fails to be equal, then the arrays are not equal.
That means that as soon as we find an unequal pair, we may exit the function and return
false.
Observation









Open the file EqualArrays.cpp.
Add EqualArrays.cpp to the LabWork project.
Read the function equal() to see that it applies the logic discussed above.
Run EqualArrays.cpp using arrays of different sizes. Does it work?
Run EqualArrays.cpp using arrays of the same size, but with a single pair of
different elements.
Note how the function was written so that it returns false as soon as an unequal
pair is found.
Run EqualArrays.cpp using two identical arrays.
Should two empty arrays be considered equal?
Run EqualArrays.cpp one more time using two empty arrays. Does it work?
Note the usefulness of for loops in the processing of arrays. The values taken on by the
control variable are exactly the consecutive values we wish the index of the array to have.
We thus sequence through the array from “left to right,” processing each element in turn.
The control value, in a sense, serves as a “pointer” to each array element.
Verifying that an Array is in Order
In Lab 13 we will encounter a search algorithm that is extremely efficient if the elements
of the array are in ascending order or descending order. Otherwise, the algorithm does
not work. Therefore, before using such an algorithm, it is important to know whether the
elements of the array are in order.
There are two possible orders: ascending and descending. We will first consider
ascending order. Later, a minor modification will allow us to treat the other case.
The elements of an array are in ascending order if each element is at least as large as its
predecessor in the array. (An array of one element is trivially in order.) Therefore, the
verification algorithm requires only that we inspect adjacent pairs of elements, verifying
that the second member is at least as large as the first member.
Note that if we are applying this algorithm to a user-define type, then it is essential that at
least one of the order relations <, >, <=, or >= be defined on the type. For example, in the
Rational class we overloaded the less-than operator.
Experimentation

Open the file VerifyOrder.cpp.

Add VerifyOrder.cpp to the LabWork project.
This program calls on a function verifyOrder() that returns true or false, depending
on whether the elements of the array are in ascending order.
As with the function equal() above, it is necessary that every pair of adjacent elements
have the proper order relation. Therefore, as soon as we find a single pair that is out of
order, we may exit the function and return false.

Write the function verifyOrder().
Note that if there are size elements in the array, then only size - 1 comparisons are
required. Therefore, the values of the index i in the for loop should go either from 0 to
size - 2 or from 1 to size - 1, but not from 0 to size - 1.





Run VerifyOrder.cpp to see if it works.
What should verifyOrder() return if the array contains only one element?
Run VerifyOrder.cpp with an array of size 1 to see if it works properly.
What should verifyOrder() return if the array is empty?
Run VerifyOrder.cpp with an empty array to see if it works properly.
Be sure that you understand why the function handled the special cases properly.
Deleting an Element from an Ordered Array
In many applications, it is necessary to maintain a list in order as new elements are added
and old elements are removed. Of course, if a list is ordered and an element is deleted,
then the remaining list is still ordered. But when we add a new element, it must be added
at the proper position or else the list will no longer be ordered. We will begin with
deletions since they are simpler than insertions.
Clearly, in order to delete an element from a list, we must first locate it. We will discuss
search algorithms in a later lab, but for now we will use a very simple method: start and
the beginning and compare to one element after another in the list. This is called a
sequential search.
But once we locate the element to be deleted, exactly how do we delete it? Consider an
example. We wish to delete the number 50 from the following list.
10
25
30
35
50
60
80
85
60
80
85
90
90
After deleting 50, the list will be
10
25
30
35
Notice that the values 60 through 90 have shifted one position to the left. That, in fact, is
our method of deleting the 50: simply shift the remainder of the list to the left one
position.
Observation




Open the file DeleteElement.cpp.
Add DeleteElement.cpp to the LabWork project.
Read the function delete() to see how it works.
Note the following techniques that were used:



To search for the value.
To determine whether the value was found.
To shift the values.
Be sure you understand these techniques. Variations of these techniques will appear in
the insertion function in the next section.

Run DeleteElement.cpp several times, testing the following cases:






Delete an element from a position somewhere in the middle of the array.
Delete an element from the first position in the array.
Delete an element from the last position in the array.
Delete an element an array containing only one element.
Attempt to delete an element that is not in the array.
Does the function work properly in all cases?
Inserting an Element into an Ordered Array
The insertion algorithm is roughly the reverse of the deletion algorithm, but there are a
few more details to be concerned with.
First, we must search for the location in which to insert the value. (Obviously, we do not
search for the number itself since we do not expect it to be there.) For example, if the list
is
10
25
30
35
60
80
85
90
and we wish to insert 50, then we must determine that it should be inserted between the
35 and the 60. However, in an array, the elements are stored contiguously; there is no
space between the elements in which to insert new elements.

What must we do to insert the number 50 into this list?
Clearly, we need to shift the elements 60 through 90 one position to the right, thereby
creating an open space for the number 50. Then we may copy 50 into that space.
Experimentation


Open the file InsertElement.cpp.
Add InsertElement.cpp to the LabWork project.
Note that the insert() function is empty. We will fill in the details.


Copy the search algorithm from delete() and paste it into insert().
Is it necessary to modify the search algorithm in order to adapt it to this situation?
After all, we are not searching for the value to be inserted since it is not yet in the
list.
It turns out that this algorithm need not be modified. This while loop will stop when it
encounters the first element of the array that is greater than or equal to the value to be
inserted, and that is exactly what we want.
We have now found the location where the insertion is to take place and have stored it as
the value of the integer pos. Next we need to shift the remaining elements to the right to
make room for the new value.

Copy the shift algorithm from delete() and paste it into insert() immediately
after the search algorithm.
This for loop must be modified. The most obvious modification is that the elements
must be shifted right instead of left. To shift them left, we wrote
a[i] = a[i + 1];
Therefore, to shift them right, we should write
a[i + 1] = a[i];

Make that change in the for loop.
Finally, we must copy the value into the open position and increment the size of the list.

Add the statements
a[pos] = value;
size++;
immediately after the shifting algorithm.
Let’s test our work.


Run InsertElement.cpp using a list of about 10 numbers and an insertion into
about the third position.
Did it work? What went wrong?
There are two problems. The simpler one is that the for loop did not go far enough. It
ended with the value size - 2, which makes the assignment
a[size - 1] = a[size - 2];
as the last assignment. After a moment’s reflection, we realize that the last assignment
should have been
a[size] = a[size - 1];
That is easy to fix. But there is a more serious problem: a single value was copied into
every position from the position just past the insertion to the end of the array.

Why did that happen?
To fix this problem, we should begin shifting at the end of the list and move towards the
beginning. In other words, the first assignment should be
a[size] = a[size - 1];
and the last assignment should be
a[pos + 1] = a[pos];
rather than the other way around. Thus, in the for loop, if the assignment is
a[i + 1] = a[i];
then the index i should range from size - 1 down to pos. In order to accomplish this,
we need to decrement i instead of increment it.


Write the for loop as it should be written.
Run InsertElement.cpp to see if it works. Test all cases.
Application
Open the MS Word document Lab 11 App.doc, found in the Lab 11 App subfolder, and
follow the instructions.
Summary
We have now learned how to do the most basic array processing. Some of these
algorithms will become the building blocks of more sophisticated array-processing
algorithms that we will study in the next two labs.
All of the processes we studied here run in linear time. That is, the time required for
them to execute is directly proportional to the size of the array; if the array is twice as
large, it takes twice as long to process it. In the next two labs we will see that some
important array-processing algorithms are slower than linear and others are faster. That
is, in some cases, if the array is twice as large, it takes four times larger to process it, and
in other cases, if the array is twice as large, it barely increases the run time.
Download