Chapter 9
Chapter 9
but conceptually, this information is all related. In this chapter we will introduce a data structure
which allows us to group together all such information into a single structure | a two dimensional
array. For a data base application, we can think of this 2D organization as an array of arrays. As
another example of where such a structure is convenient, consider an array of names. We have
seen that we can store a name in a string, which is an array of characters. Then an array of strings
is also an array of arrays, or a two dimensional array.
In this chapter we will discuss how we can declare two dimensional arrays, and use them in
applications. We will see how we can access the data in such a structure using indices and pointers,
and see how this concept can be extended to multi-dimensional arrays. We will present examples
of 2 dimensional arrays for data base applications, string sorting and searching, and solutions
to systems of simultaneous linear algebraic equations, useful in scientic, engineering, and other
applications, e.g. electronic circuit analysis, economic analysis, structural analysis, etc. The one
restriction in the use of this data type is that all of the data stored in the structure must be of
the same type. (We will see how we can remove this restriction in the next chapter).
each element of this array is, itself, an array of 5 integer elements. Here is the declaration of an
array of integer arrays:
int scores10]5]
The rst range says the array has ten elements: scores0], scores1], : : : scores9]. The
second range says that each of these ten arrays is an array of ve elements. For example, scores0]
has ve elements: scores0]0], scores0]1], : : : scores0]4]. Similarly, any other element
may be referenced by specifying two appropriate indices, scoresi]j]. The rst array index
references the ith one dimensional array, scoresi]
the second array index references the j th
element in the one dimensional array, scoresi]j].
A two dimensional array lends itself to a visual display in rows and columns. The rst in-
dex represents a row, and the second index represents a column. A visual display of the array,
scores10]5], is shown in Figure 9.1. There are ten rows, (0-9), and ve columns (0-4). An
element is accessed by row and column index. For example, scores2]3] references an integer
element at row index 2 and column index 3.
We will see in the next section that, as with one dimensional arrays, elements of a two dimen-
sional array may be accessed indirectly using pointers. There, we will see the connection between
two dimensional arrays and pointers. For now, we will use array indexing as described above and
remember that arrays are always accessed indirectly. Also, just as with one dimensional arrays, a
2D array name can be used in function calls, and the called function accesses the array indirectly.
We can now easily set down the algorithm for our task:
9.1. TWO DIMENSIONAL ARRAYS 343
We can easily write the top level program driver using functions to do the work of reading scores,
getting the weights, computing the weighted averages, printing scores, averaging each set of scores,
and printing the averages. The driver is shown in Figure 9.2.
We have declared an array, scores]], with MAX rows and COLS columns, where these macro
values are large enough to accommodate the expected data. We have used several functions,
which we will soon write and include in the same program le. Their prototypes as well as those
of other functions are declared at the head of the le. In the driver, getwts() reads the weights
for the exams into an array, wts], returning the number of exams. The function, read scores(),
reads the data records into the two dimensional array, scores]], and returns the number of
data records. The function, wtd avg(), computes the weighted averages of all exam scores, and
avg scores() computes an average of each exam score column as well as that of the weighted
average column. Finally, print scores() and print avgs() print the results including the input
data, the weighted averages, and the averages of the exams.
Let us rst write getwts(). It merely reads the weight for each of the exams as shown in Figure
9.3. The function prompts the user for the number of exam scores, and reads the corresponding
number of oat values into the wts] array. Notice that the loop index, i begins with the value
1. This is because the element wts0], corresponding to the student id column, does not have a
weight and should be ignored. After the weights have been read, we ush the keyboard buer of
any remaining white space so that any kind of data (including character data) can be read from
344 CHAPTER 9. TWO DIMENSIONAL ARRAYS
/* File: wtdavg.c
Other Source Files: avg.c
Header Files: avg.h
This program computes weighted averages for a set of exam scores for
several individuals. The program reads scores from a file, computes
weighted averages for each individual, prints out a table of scores,
and prints averages for each of the exams and for the weighted average.
*/
#include <stdio.h>
#define MAX 20
#define COLS 5
int getwts(float wts])
FILE *openinfile(void)
int read_scores(int ex]COLS], int lim, int nexs)
void wtd_avg(int ex]COLS], int lim, int nexs, float wts])
void avg_scores(int ex]COLS], int avg], int lim, int nexs)
void print_scores(int ex]COLS], int lim, int nexs)
void print_avgs(int avg], int nexs)
main()
{ int no_of_stds, no_of_exams
int avgCOLS]
int scoresMAX]COLS]
float wtsCOLS]
fp = fopen(infile, "r")
/* Opens the input file and reads scores for nexs exams returns
the number of individual student records.
*/
int read_scores(int ex]COLS], int stds, int nexs)
{ int row, col, n, x
FILE * fp
fp = openinfile()
for (row = 0 row < stds row++)
for (col = 0 col <= nexs col++) {
x = fscanf(fp, "%d", &n)
if (x == EOF) {
fclose(fp)
return row
}
exrow]col] = n
}
fclose(fp)
return row
}
We convert the percent weight to the actual weight multiply by the score, and accumulate it in
the sum, wtdavg yielding a oat value. The wtdavg will be stored in the integer array, ex]],
after rounding to a nearest integer. If we simply cast wtdavg to an integer, it will be truncated.
To round to the nearest integer, we add 0.5 to wtdavg and then cast it to integer:
exrow]nexs + 1] = (int) (0.5 + wtdavg)
The weighted average is stored into the column of the array after the last exam score. The entire
function is shown in Figure 9.5
Computing average of each of the exams and the weighted average is simple. We just sum
each of the columns and divide by the number of items in the column, and is also shown in Figure
9.5. For each exam and for the weighted average column, the scores are added and divided by
lim, the number of rows in the array, using oating point computation. The result is rounded to
the nearest integer and stored in the array, avg]. Figure 9.6 shows the nal two functions for
printing the results.
Running the program with data le,wtdin.dat as follows:
3 70 76
52 92 80
53 95 56
54 48 52
55 98 95
57 100 95
61 100 65
62 95 76
63 86 65
70 100 90
71 73 73
75 94 79
printf("ID #\t")
for (j = 1 j <= nexs j++)
printf("Ex%d\t", j) /* print the headings */
printf("WtdAvg\n")
for (i = 0 i < lim i++) { /* print the scores and wtd avg */
printf("\n")
}
}
/* Prints the averages of exams and the average of the weighted average
of exams.
*/
void print_avgs(int avg], int nexs)
{ int i
Number of exams: 2
Percent Weight for Exam 1: 50
Percent Weight for Exam 2: 50
Input File, RETURN to quit: wtdin.dat
ID # Ex1 Ex2 WtdAvg
3 70 76 73
52 92 80 86
53 95 56 76
54 48 52 50
55 98 95 97
57 100 95 98
61 100 65 83
62 95 76 86
63 86 65 76
70 100 90 95
71 73 73 73
75 94 79 87
Average for Exam 1 = 88
Average for Exam 2 = 75
Average of the weighted average = 82
In this program, we have assumed that the input le contains only the data to be read, i.e. the
student id numbers and exam scores. Our read scores() function is written with this assumption.
However, the input le might also contain some heading information such as the course name and
column headings in the rst few lines of the le. We can easily modify read scores() to discard
the rst few lines of headings.
As a second example of application of two dimensional arrays, consider our previous payroll
example. In this case, the data items in a pay data record are not all of the same data type. The
id numbers are integers, whereas all the other items are oat. Therefore, we must use an array of
integers to store the id numbers, and a two dimensional oat array to store the rest of the data
record. The algorithm is no dierent from the program we developed in Chapter 7 that computed
pay. The dierence is that now we use a two dimensional array for all oat payroll data instead
of several one dimensional arrays. The id numbers are still stored in a separate one dimensional
array. Since the data structures are now dierent, we must recode the functions to perform the
tasks of getting data, calculating pay, and printing results, but still using the same algorithms.
The program driver and the header les are shown in Figure 9.7. The program declares an
integer array for id numbers and a two dimensional oat array for the rest of the data record. The
successive columns in the two dimensional array store the hours worked, rate of pay, regular pay,
overtime pay, and total pay, respectively. We have dened macros for symbolic names for these
index values. As in the previous version, the program gets data, calculates pay, and prints data.
The dierence is in the data structures used. Functions to perform the actual tasks are shown
in Figure 9.8 and 9.9 and included in the same program source le. Each function uses a two
dimensional array, payrec]]. The row index species the data record for a single id, and the
column index species a data item in the record. The data record also contains the total pay. A
sample interaction with the program, pay2rec.c, is shown below.
9.1. TWO DIMENSIONAL ARRAYS 351
/* File: pay2rec.c
Program calculates and stores payroll data for a number of id's.
The program uses a one dimensional array for id's, and a two
dimensional array for the rest of the pay record. The first column
is hours, the second is rate, the third is regular pay, the fourth
is overtime pay, and the fifth is total pay.
*/
#include <stdio.h>
#define MAX 10
#define REG_LIMIT 40
#define OT_FACTOR 1.5
#define HRS 0
#define RATE 1
#define REG 2
#define OVER 3
#define TOT 4
main()
{ int n = 0, idMAX]
float payrecMAX]TOT + 1]
if (idn] <= 0)
return n
else {
payreci]REG] = REG_LIMIT * payreci]RATE]
payreci]OVER] = (payreci]HRS] - REG_LIMIT) *
OT_FACTOR * payreci]RATE]
}
/* Prints a table of payroll data for all id's. Id's in one array,
and the rest of the records in a two dim. array.
*/
void print2data(int id], float payrec]TOT + 1], int n)
{ int i, j
printf("\n")
}
}
Figure 9.9: Code for Payroll Program Functions | calc2pay() and print2data()
354 CHAPTER 9. TWO DIMENSIONAL ARRAYS
Sample Session:
***Payroll Program - Records in 2 D arrays***
ID <zero to quit>: 5
Hours Worked: 30
Rate of Pay: 10
ID <zero to quit>: 10
Hours Worked: 50
Rate of Pay: 12
ID <zero to quit>: 0
***PAYROLL: FINAL REPORT***
ID HRS RATE REG OVER TOT
5 30.00 10.00 300.00 0.00 300.00
10 50.00 12.00 480.00 180.00 660.00
AA0]
AA1]
...
AAMAX-1]
AA0] &AA0]0]
AA1] &AA1]0]
AA2] &AA2]0]
AAk] &AAk]0]
AA0] + 1 &AA0]1]
AA0] + j &AA0]j]
AAk] + j &AAk]j]
* AA0] AA0]0]
AAk] AAk]0]
(AA0] + 1) AA0]1]
(AA0] + j) AA0]j]
(AAk] + j) AAk]j]
* AA AA0] &AA0]0]
AA + 1 AA0] + 1 &AA0]1]
AA + j AA0] + j &AA0]j]
(AA + 1) AA1] &AA1]0]
(AA + k) AAk] &AAk]0]
(* AA) *AA0] AA0]0]
(* (AA + 1)) *AA1] AA1]0]
(* (AA + k) + j) *(AAk] + j) AAk]j]
however, the pointer references are equally valid as seen in the gure.
The relationships between dierent pointers for a two dimensional array is further illustrated
with the program shown in Figure 9.12. The two-dimensional array, a, is not an integer pointer,
it points to the array of integers, a0]. However, *a is an integer pointer
it points to an integer
object, a0]0]. To emphasize this point, we initialize an integer pointer, intptr to *a, i.e. a0].
The initial value of intptr is the address of a0]0]. We next print the values of a and *a, which
are the same address even though they point to dierent types of objects. In the for loop, we
print the value of a + i, which is the same as that of ai] even though they point to dierent
types of objects. In the inner for loop, we print the address of the ith row and the j th column
element of the row major array using pointers:
*a + COL * i + j
The same value is printed using the address of operator, &ai]j]. Finally, the value of ai]j]
is printed using array indices as well as by dereferencing pointers, i.e. *(*(a + i) + j).
The value of intptr, initialized to *a, is incremented after each element value is printed
? ? ?
AA - AA0] -
AA + 1 - AA1] -
AA + 2 - AA2] -
..
a + 1 = 65480
*a + COL * 1 + 0 = 65480 intptr = 65480
&a1]0] = 65480
a1]0] = 23 *(*(a + 1) + 0) = 23
358 CHAPTER 9. TWO DIMENSIONAL ARRAYS
/* File: ar2ptr.c
Other Source Files: ar2util.c
Header Files: ar2def.h, ar2util.h
Program shows relations between arrays and pointers for 2 dimensional
arrays.
*/
#include <stdio.h>
#define ROW 2
#define COL 3
print2aray(int x]COL], int r, int c)
main()
{ int i, j, *intptr, aROW]COL] =
{ {12, 24, 29}, {23, 57, 19} }
As we mentioned in the last section, when a two dimensional array is passed to a function,
the parameter declaration in the function must include the number of columns. We can now see
why this is so. The number of columns in a row species the size of each row in the array of rows.
Since the passed parameter is a pointer to a row object, it can be incremented and dereferenced,
as shown in Table 9.3, to access the elements of the two dimensional array. The compiler must
know the size of the row in order to be able to increment the pointer to the next row.
As we stated earlier, multi-dimensional arrays are arrays of arrays just like two dimensional
arrays. An n dimensional array is an array of n ; 1 dimensional arrays. The same general approach
applies as for two dimensional arrays. When passing an n dimensional array, the declaration of
the formal parameter must specify all index ranges except for the rst index.
As was seen in the program in Figure 9.12, multi-dimensional arrays may also be initialized in
declarations by specifying constant initializers within braces. Each initializer must be appropriate
for the corresponding lower dimensional array. For example, a two dimensional array may be
initialized as follows:
int x2]3] = { {10, 23}, {0, 12, 17} }
The array has two elements, each of which is an array of three elements. The rst initializer
initializes the rst row of x. Since only the rst two elements of the row are specied, the third
element is zero. The second element initializes the second row. Thus, x is initialized to the array:
10 23 0
0 12 17
names0] - J o h n n0
names1] - S u e n0
names2] -
...
namesMAX-1] -
We will organize the program in several source les since we will be using some of the functions
in several example programs. The program driver and the header le are shown in Figure 9.14.
The program reads character strings into an array, in this case, names. The program can, of
course, serve to read in any strings. The for loop in main() reads strings into an array using
gets() to read a string into namesn], the nth row of the array. That is, the string is stored
where namesn] points to. The variable n keeps track of the number of names read. The loop is
terminated either if the number of names equals MAX, or when gets() returns NULL indicating end
of le has been reached. Next, the program calls on printstrtab() to print the names stored in
the two dimensional array, names. The arguments passed are the array of strings and the number
of strings, n.
The function, printstrtab() is included in the le strtab.c and its prototype is included
in the le strtab.h. Remember, the second range of the two dimensional array of strings must
be specied in the formal parameter denition, otherwise the number of columns in a row are
unknown and the function cannot access successive rows correctly. A sample interaction for the
compiled and linked program is shown below:
Sample Session:
***Table of Strings - Names***
9.3. ARRAYS OF STRINGS 361
/* File: strtab.h */
#define SIZE 31 /* maximum size of a name plus a NULL */
void printstrtab(char strtab]SIZE], int n)
/* File: names.c
Other Source Files: strtab.c
Header Files: strtab.h
This program reads a set of names or strings into a two
dimensional array. It then prints out the names.
*/
#include <stdio.h>
#define MAX 10
#include "strtab.h"
main()
{ int n /* number of names */
char namesMAX]SIZE] /* 2-d array of names */
if (n == MAX)
printf("\n**Table full - input terminated\n")
printstrtab(names, n)
}
/* File: strtab.c */
#include <stdio.h>
#include "strtab.h"
printf("Names are:\n")
for (k = 0 k < n k++)
puts(strtabk])
}
The value returned is assigned to an integer variable, k. If successful, srchstrtab() returns the
index where the string was found
otherwise, it returns -1. The function is shown in Figure 9.16. In
the for loop, the string that strtabi] points to is compared with the string that key points to. If
they are equal, strcmp() returns zero and the value of i is returned by the function. Otherwise, i
is incremented, and the process is repeated. The loop continues until the valid array is exhausted,
in which case -1 is returned. Again, the formal parameter denition for the two dimensional array,
x, requires the size of the second dimension, SIZE. A sample run of the program is shown below:
9.3. ARRAYS OF STRINGS 363
/* File: strsrch.c
Other Source Files: strtab.c
Header Files: strtab.h
This program searches for a string (key) in a set of strings
in a two dimensional array. It prints the index where key is found.
It prints -1, if the string is not found.
*/
#include <stdio.h>
#define MAX 10
#include "strtab.h"
main()
{ int k
char namesMAX]SIZE] = { "John Jones", "Sheila Smith",
"John Smith", "Helen Kent"}
/* File: strsort.c
Other Source Files: strtab.c
Header Files: strtab.h
This program sorts a set of strings in a two dimensional array.
It prints the unsorted and the sorted set of strings.
*/
#include <stdio.h>
#define MAX 10
#include "strtab.h"
main()
{ int n
char namesMAX]SIZE] = { "John Jones", "Sheila Smith",
"John Smith", "Helen Kent"}
sortstrtab(names, 4)
printf("Sorted ")
printstrtab(names, 4)
}
strcpy(tmp, strtabmaxpos])
strcpy(strtabmaxpos], strtabeff_size-1])
strcpy(strtabeff_size - 1], tmp)
}
}
is similar to the numeric selection sort function, except that we now use strcmp() to compare
strings and strcpy() to swap strings. A sample session is shown below:
In our example program, the entire strings are compared. If we wish to sort by last name, we
could modify our function to nd and compare only the last names.
366 CHAPTER 9. TWO DIMENSIONAL ARRAYS
9.4 Arrays of Pointers
As seen in the last example, sorting an array of strings requires swapping the strings which can
require copying a lot of data. For eciency, it is better to avoid actual swapping of data whenever
a data item is large, such as a string or an entire data base record. In addition, arrays may be
needed in more than one order
for example, we may need an exam scores array sorted by Id
number and by weighted scores
or, we may need strings in both an unsorted form and a sorted
form. In either of these cases, we must either keep two copies of the data, each sorted dierently,
or nd a more ecient way to store the data structure. The solution is to use pointers to elements
of the array and swap pointers. Consider some examples:
int data1, data2, *ptr1, *ptr2, *save
We could swap the values of the data and store the swapped values in data1 and data2 or we
could simply swap the values of the pointers:
save = ptr1
ptr1 = ptr2
ptr2 = save
p1 = name1
p2 = name2
Pointers p1 and p2 point to strings name1 and name2. We can now swap the pointer values so p1
and p2 point to name2 and name1, respectively.
In general, an array of pointers can be used to point to an array of data items with each
element of the pointer array pointing to an element of the data array. Data items can be accessed
either directly in the data array, or indirectly by dereferencing the elements of the pointer array.
The advantage of a pointer array is that the pointers can be reordered in any manner without
moving the data items. For example, the pointer array can be reordered so that the successive
elements of the pointer array point to data items in sorted order without moving the data items.
Reordering pointers is relatively fast compared to reordering large data items such as data records
or strings. This approach saves a lot of time, with the additional advantage that the data items
remain available in the original order. Let us see how we might implement such a scheme.
STRPTRS: Given an array of strings, use pointers to order the strings in sorted form, leaving
the array unchanged.
We will use an array of character pointers to point to the strings declared as follows:
9.4. ARRAYS OF POINTERS 367
0 - j o h n n0
1 - y u r i n0
2 - s u e n0
3 - c a r o l n0
4 - d a v i d n0
..
MAX-1
captionUnsorted Pointers to Strings
char * nameptrMAX]
The array, nameptr], is an array of size MAX, and each element of the array is a character pointer.
It is then possible to assign character pointer values to the elements of the array
for example:
nameptri] = "John Smith"
The string "John Smith" is placed somewhere in memory by the compiler and the pointer to the
string constant is then assigned to nameptri]. It is also possible to assign the value of any string
pointer to nameptri]
for example, if s is a string, then it is possible to assign the pointer value
s to nameptri]:
nameptri] = s
In particular, we can read strings into a two dimensional array, names]], and assign each string
pointer, namesi] to the ith element of the pointer array, nameptr]:
for (i = 0 i < MAX && gets(namesi]) i++)
nameptri] = namesi]
The strings can then be accessed either by namesi] or by nameptri] as seen in Figure 9.4. We
can then reorder the pointers in nameptr] so that they successively point to the strings in sorted
order as seen in Figure 9.4. We can then print the strings in the original order by accessing them
through namesi] and print the strings in sorted order by accessing them through nameptri].
Here is the algorithm:
while not end of file and array not exhausted,
read a string
store it in an array of strings and
assign the string to an element of a pointer array
0 - j o h n n0
1 - y u r i n0
2 - s u e n0
3 - c a r o l n0
4 - d a v i d n0
..
MAX-1
captionSorted Pointers to Strings
The program driver, including a prototype for sortptrs() is shown in Figure 9.19. It declares
a two dimensional array of strings, names]], and an array of character pointers, nameptr].
It then reads strings into names]], and assigns each string pointer namesi] to nameptri].
The function sortptrs() is then called to reorder nameptr] so the successive pointers of the
array point to the strings in sorted order. Finally, strings are printed in original unsorted order
by accessing them through namesi] and in sorted order by accessing them through nameptri].
The function sortptrs() uses the selection sort algorithm modied to access data items
through pointers. It repeatedly moves the pointer to the largest string to the highest index of an
eective array. The implementation of sorting using pointers to strings is shown in Figure 9.20.
The algorithm determines maxpos, the index of the pointer to the largest string. The pointer at
maxpos is then moved to the highest index in the eective array. The array size is then reduced,
etc.
Sample Session:
***Arrays of Pointers - Sorting by Pointers***
/* File: ptraray.c
This program uses an array of pointers. Elements of the array
point to strings. The pointers are reordered so that they
point to the strings in sorted order. Unsorted and sorted
strings are printed out.
*/
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define MAX 10 /* max number of names */
#define SIZE 31 /* size of names plus one for NULL */
void sortptrs(char * nameptr], int n)
main()
{ int i /* counter */
int n /* number of names read */
char namesMAX]SIZE] /* 2-d array of names */
char *nameptrMAX] /* array of ptrs - used to point to names */
if (n == MAX)
printf("\n***Only %d names allowed***\n", MAX)
if (strcmp(nameptri],nameptrmaxpos]) > 0)
maxpos = i
tmpptr = nameptrmaxpos]
nameptrmaxpos] = nameptreff_size-1]
nameptreff_size-1] = tmpptr
}
}
Reordering of pointers, so they point to data items in sorted order, is referred to as sorting by
pointers. When the data items are large, such as data records or strings, this is the preferred way
of sorting because it is far more ecient to move pointers than it is to move entire data records.
The unknowns and the right hand side are assumed to be elements of one dimensional arrays:
x0], x1],: : :, xm - 1] and y0], y1],: : :, yn - 1], respectively. The coecients are
assumed to be elements of a two dimensional array: ai]j] for i = 0 : : : n ; 1 and j =
0 : : : m ; 1. The coecients of each equation correspond to a row of the array. For our discussion
in this section, we assume that m equals n. With this assumption, it is possible to nd a unique
solution of these equations unless the equations are linearly dependent, i.e. some equations are
linear combinations of others.
A common method for solving such equations is called the Gaussian elimination method.
The method eliminates (i.e. makes zero) all coecients below the main diagonal of the two
dimensional array. It does so by adding multiples of some equations to others in a systematic way.
The elimination makes the array of new coecients have an upper triangular form since the lower
triangular coecients are all zero.
The modied equivalent set of n equations in m = n unknowns in the upper triangular form
have the appearance shown below:
a0]0]*x0]+ a0]1]*x1] + ... + a0]n-1] *xn-1] = y0]
a1]1]*x1] + ... + a1]n-1] *xn-1] = y1]
a2]2]*x2]..+ a2]n-1] *xn-1] = y2]
an-1]n-1]*xn-1]= yn-1]
The upper triangular equations can be solved by back substitution. Back substitution rst solves
the last equation which has only one unknown, xn-1]. It is easily solved for this value | xn-1]
= yn-1]/an-1]n-1]. The next to the last equation may then be solved | since xn-1] has
been determined already, this value is substituted in the equation, and this equation has again only
one unknown, xn-2]. The unknown, xn-2], is solved for, and the process continues backward
to the next higher equation. At each stage, the values of the unknowns solved for in the previous
equations are substituted in the new equation leaving only one unknown. In this manner, each
equation has only one unknown which is easily solved for.
Let us take a simple example to see how the process works. For the equations:
1 * x0] + 2 * x1] + 3 * x2] = 6
2 * x0] + 3 * x1] + 1 * x2] = 6
1 * x0] + 0 * x1] + 2 * x2] = 3
We rst reduce to zero the coecients in the rst column below the main diagonal (i.e. array index
zero). If the rst equation is multiplied by -2 and added to the second equation, the coecient in
the second row and rst column will be zero:
1 * x0] + 2 * x1] + 3 * x2] = 6
0 * x0] - 1 * x1] - 5 * x2] = -6
1 * x0] + 0 * x1] + 2 * x2] = 3
Similarly, if the rst equation is multiplied by -1 and added to the third equation, the coecient
in the third row and rst column will be zero:
9.5. AN EXAMPLE: LINEAR ALGEBRAIC EQUATIONS 373
1 * x0] + 2 * x1] + 3 * x2] = 6
0 * x0] - 1 * x1] - 5 * x2] = -6
0 * x0] - 2 * x1] - 1 * x2] = -3
Coecients in the rst column below the main diagonal are now all zero, so we do the same for
the second column. In this case, the second equation is multiplied by a multiplier and added to
equations below the second
thus, multiplying the second equation by -2 and adding to the third
makes the coecient in the second column zero:
1 * x0] + 2 * x1] + 3 * x2] = 6
0 * x0] - 1 * x1] - 5 * x2] = -6
0 * x0] + 0 * x1] + 9 * x2] = 9
We now have equivalent equations with an upper triangular form for the non-zero coecients.
The equations can be solved backwards | the last equation gives us x2] = 1. Substituting the
value of x2] in the next to the last equation and solving for x1] gives us x1] = 1. Finally,
substituting x2] and x1] in the rst equation gives us x0] = 1.
From the above discussion, we can see that a general algorithm involves two steps: modify
the coecients of the equations to an upper triangular form, and solve the equations by back
substitution.
Let us rst consider the process of modifying the equations to an upper triangular form. Since
only the coecients and the right hand side values are involved in the computations that modify
the equations to upper triangular form, we can work with these items stored in an array with n
rows and n + 1 columns (the extra column contains the right hand side values).
Let us assume the process has already reduced to zero the rst k ; 1 columns below the main
diagonal, storing the modied new values of the elements in the same elements of the array. Now,
it is time to reduce the kth lower column to zero (by lower column, we mean the part of the column
below the main diagonal). The situation is shown in below:
a0]0] a0]1] ... a0]k] ... a0]n]
0 a1]1] ... a1]k] ... a1]n]
0 0 a2]2]... a2]k] ... a2]n]
... ... ... ... ... ...
0 0 0... ak]k] ak]k+1].. ak]n]
0 0 0... ak+1]k] ... ak+1]n]
0 0 0... ak+2]k] ... ak+2]n]
... ... ... ... ... ...
0 0 0... an-1]k] ... an-1]n]
The nth column represents the right hand side values with ai]n] equal to yi]. We multiply the
kth row by an appropriate multiplier and add it to each row with index greater than k. Assuming
that ak]k] is non-zero, the kth row multiplier for addition to the ith row (i > k) is:
-ai]k] / ak]k]
The kth row multiplied by the above multiplier and added to the ith row will make the new ai]k]
zero. The following loop will reduce to zero the lower kth column:
374 CHAPTER 9. TWO DIMENSIONAL ARRAYS
/* Algorithm: process_column
Reduces lower column k to zero.
*/
for (i = k + 1 i < n i++) { /* process rows k+1 to n-1 */
m = - ai]k] / ak]k] /* multiplier for ith row */
/* File: gauss.h */
typedef enum {ERROR, OK} status
#define DEBUG
#define MAX 10 /* maximum number of equations */
Recall, in our representation, the right hand side is the nth column of the two dimensional array.
For each index i, we must sum all contributions from those unknowns already solved for, i.e. those
xi] with index greater than i. This is the following sum:
We then subtract this sum from the right hand side, ai]n], and divide the result by ai]i]
to determine the solution for xi]. The algorithm is shown below:
/* Algorithm: Back_Substitution */
for (i = n - 1 i >= 0 i--) { /* go backwards */
sum = 0
We can now write the function gauss() that solves a set of equations by the Gaussian elim-
ination method which rst calls on uptriangle() to convert the coecients to upper triangular
form. If this succeeds, then back substitution is carried out to nd the solutions. As with other
functions, gauss() returns OK if successful, and ERROR otherwise. The code is shown in Figure
9.24. The code is straight forward. It incorporates the back substitution algorithm after the
function call to uptriangle(). If the function call returns ERROR, the equations cannot be solved
376 CHAPTER 9. TWO DIMENSIONAL ARRAYS
/* File: gauss.c */
#include <stdio.h>
#include "gauss.h"
if (ak]k] == 0) {
j = findnonzero(a, k, n)
if (j < 0)
return ERROR
else
swaprows(a, k, j, n)
#ifdef DEBUG
printf("Rows %d and %d swapped\n", k, j)
#endif
}
return OK
}
/* Scans the rows with index >= k for the first non-zero element
in the kth column of the array 'a' of size n.
*/
int findnonzero(double a]MAX + 1], int k, int n)
{ int i
/* Swaps the kth and the jth rows in the array 'a' with n rows. */
void swaprows(double a]MAX + 1], int k, int j, int n)
{ int i
double temp
if (uptriangle(a, n) == ERROR) {
printf("Dependent equations - cannot be solved\n")
return ERROR
}
if (ai]i])
xi] = (ai]n] - sum) / ai]i]
else
return ERROR
}
return OK
}
All these functions use data of type double. The code is shown in Figure 9.25.
Finally, we are ready to write a program driver as shown in Figure 9.26. The driver rst reads
coecients and the right hand side values for a set of equations and then calls on gauss() to
solve the equations. During the debug phase, both the original data and the transformed upper
triangular version are printed. Finally, if the equations are solved with success, the solution is
printed. Otherwise, an error message is printed. During debugging, the macro DEBUG is dened
in gauss.h so that we can track the process. The program loops as long as there are equations
to be solved. In each case, it gets coecients using getcoeffs() and solves them using gauss().
During debug, the program uses pr2adbl() to print the original array and the array after gauss
transformation. If the solution is possible, the program prints the solution array using pr1adbl().
Here are several example equation solutions:
Sample Session:
***Simultaneous Equations - Gauss Elimination Method***
Solution is:
-1.00
1.00
380 CHAPTER 9. TWO DIMENSIONAL ARRAYS
if (n)
printf("Type coefficients and right side of each row\n")
printf("\n")
}
}
/* File: gauss.c
Header Files: gauss.h
This program solves a number of simultaneous linear algebraic
equations using the Gauss elimination method. The process repeats
itself until number of equations is zero.
*/
main()
{ double aMAX]MAX + 1] /* coefficients and right hand side */
double xMAX] /* solution */
int n /* number of equations */
status soln /* status of solution, OK or ERROR */
printf("***Simultaneous Equations***\n\n")
while (n = getcoeffs(a)) {
printf("\nOriginal equations are:\n")
#ifdef DEBUG
pr2adbl(a, n)
#endif
if (soln == OK) {
printf("\nSolution is:\n")
pr1adbl(x, n)
}
Error! aray2 is a pointer to a two dimensional array, i.e. it points to an object that is a
one-dimensional array, aray20]. Without a knowledge of the size of the object, aray20],
384 CHAPTER 9. TWO DIMENSIONAL ARRAYS
it is not possible to access aray21], aray22], etc. Consequently, one must specify the
number of integer objects in aray20]:
init2(int aray2]COLS])
{ ...
}
Correct! aray20] has COLS objects. It is possible to advance the pointer, aray2 correctly
to the next row, etc.
2. Failure to pass arguments correctly in function calls:
init2(aray2MAX]COLS])
init2(aray2]COLS])
init2(aray2]])
All of the above are errors. A two dimensional array name is passed in a function call:
init2(aray2)
3. Confusion between pointers to dierent types of objects. For example, in the above, aray2
points to an array object, aray20], whereas aray20] points to an int object. The
expression aray2 + 1 points to aray21], whereas aray20] + 1 points to aray20]1].
In the rst case the pointer is increased by COLS integer objects, whereas in the second case
the pointer is increased by one integer object.
4. Confusion between arrays of character strings and arrays of character pointers:
char tableMAX]SIZE], *ptrarayMAX]
The rst declares table to be a two dimensional array that can be used to store an array of
strings, one each in table0], table1], tablei], etc. The second declares ptraray to be
an array, each element of which is a char *. Read the declaration from the end: MAX] says
it is an array with MAX elements
ptraray is the name of the array
char * says each element
of the array is a char *. Properly initialized with strings stored in table]], tablei] can
point to a string. Properly initialized with pointers to strings, ptrarayi] can also point
to a string. However, tableMAX]SIZE] provides memory space for the strings, whereas
ptrarayMAX] provides memory space only for pointers to strings. Both pointers may be
used in a like manner:
puts(tablei])
puts(ptrarayi])
2. x + i
3. *(x + i)
4. *(x + i) + j
5. *(*(x + i) + j)
6. x0]
7. xi]
8. xi] + j
9. *(xi] + j)
Find and correct errors if any. What does the program do in each case?
10. main()
{ int x5]10]
init(x]])
}
11. main()
{ int x5]10]
init(x]])
}
12. main()
{ char s5]100]
read_strings(s)
print_strings(s)
}
read_strings(char s]100])
{
for (i = 0 i < 5 i++) {
gets(*s)
s++
}
}
print_strings(char s]100])
{
while (*s) {
puts(s)
s++
}
}
388 CHAPTER 9. TWO DIMENSIONAL ARRAYS
9.9 Problems
1. Read id numbers, project scores, and exam scores in a two dimensional array from a le.
Compute the averages of each project and exam scores
compute and store the weighted
average of the scores for each id number.
2. Repeat 1, but sort and print the two dimensional array by weighted average in decreasing
order. Sort and print the array by id numbers in increasing order. Use an array of pointers
to sort.
3. Repeat 2, but plot the frequency of each weighted score.
4. Combine 1-3 into a menu-driven program with the following options: read names, id numbers,
and scores from a le
add scores for a new project or exam
save scores in a le
change
existing scores for a project or an exam for specied id numbers
delete a data record
add
a data record
compute averages
sort scores in ascending or descending order by a primary
key, e.g. id numbers, weighted scores, etc.
compute weighted average
plot frequency of
weighted scores
help
quit.
5. Write a function that uses binary search algorithm to search an array of strings.
6. Write a function that sorts strings by selection sort in either increasing or decreasing order.
7. Write a program that takes a string and breaks it up into individual words and stores them.
8. Repeat 7 and keep track of word lengths. Display the frequency of dierent word lengths.
9. Repeat 7, but store only new words that occur in a string. If the word has already been
stored, ignore it.
10. Write a function that checks if the set of words in a string, s, represents a subset of the set
of words in a second string, t. That is, the words of s are all contained in t, with t possibly
containing additional words.
11. Write a menu-driven spell check program with the following options: read a dictionary from
a le
spell check a text le
add to dictionary
delete from dictionary
display text buer
17. Modify the Gauss Method so that a pivot with the largest magnitude is used in converting
the array of coecients to an upper triangular form.
18. Modify 17 to a menu-driven program that allows the following commands: Get coecients,
display coecients, solve equations, display solution, verify solution, help, and quit. Write
and use functions get coeffs(), display coeffs(), solve eqns(), display soln(), verify soln(),
help().
20. Modify 19 so that display coecients displays equations in the above form.
390 CHAPTER 9. TWO DIMENSIONAL ARRAYS
21. Write a simple menu driven editor which allows the following commands: text insert, display
text, delete text, delete lines, insert lines, nd string, nd word, replace string, replace word,
help, and quit. A window should display part of the text when requested.
PART II
391
392 CHAPTER 9. TWO DIMENSIONAL ARRAYS
Chapter 10
Sorting and Searching
One very common application for computers is storing and retrieving information. For example,
the telephone company stores information such as the names, addresses and phone numbers of its
customers. When you dial directory assistance to get the phone number for someone, the operator
must look up that particular piece of information from among all of data that has been stored.
Taken together, all of this information is one form of a data base which is organized as a collection
of records. Each record consists of several elds, each containing one piece of information, such as
the name, address, phone number, id number, social security number or part number, etc..
As the amount of information to be stored and accessed becomes very large, the computer
proves to be a useful tool to assist in this task. Over the years, as computers have been applied to
these types of tasks, many techniques and algorithms have been developed to eciently maintain
and process information in data bases. In this chapter, we will develop and implement some of
the simpler instances of these algorithms. The processes of \looking up" a particular data record
in the data base is called searching. We will look at two dierent search algorithms
one very
easy to implement, but inecient, the other much more ecient. As we will see, in order to do
an ecient search in a data base, the records must be maintained in some order. For example,
consider the task of nding the phone number in the phone book of someone whose name you
know, as opposed to trying to nd the name of someone whose phone number you know in the
same book.
The process of ordering the records in a data base is called sorting. We will discuss three sorting
algorithms and their implementation in this chapter, as well. Sorting and searching together
constitute a major area of study in computational methods. We present some of these methods
here to introduce this area of computing as well as to make use of some of the programming
techniques we have developed in previous chapters.
As we develop these algorithms, we use a very simple data base of records consisting of single
integers only. We conclude the chapter by applying the searching and sorting techniques to our
payroll data base with records consisting of multiple numeric elds. In Chapter 9 we will see how
these same algorithms can be applied to string data types described in Chapter 11.
key == ?
? ? ? ?
index 0 1 2 MAX
if the array is small, there aren't many elements to search and the amount of time it takes is not
even noticed by the user. Thus, for many situations, linear search is a perfectly valid approach.
Here is a linear search algorithm which returns the index in the array where key is found or -1 if
key is not found in the array:
initialize index i to 0
traverse the array until exhausted
if arrayi] matches key
return i
return -1.