Вы находитесь на странице: 1из 52

The College of Technology

Computer Technology Department

PROGRAMMING AND DATA STRUCTURES
COURSE NOTES
FALL 2003

Dr. Sayed A. Hussein

C O N T E N T S

 CHAPTER(1) FUNDAMENTALS 1 1.1 DATA TYPES 1 1.2 FUNCTIONS 2 1.3 FORMATTING OUTPUT 3 CHAPTER(2) ARRAYS AND STRINGS 4 2.1 ARRAYS OF STRUCTURES 5 2.2 STRUCTURES AND ARRAYS 6 2.3 ARRAYS AS CLASS MEMBER DATA 7 2.4 ARRAYS OF OBJECTS 8 2.5 STRINGS 9 2.6 ARRAY SEARCHING 10 2.6.1 Linear Search Algorithm 10 2.6.2 Binary Search Algorithm 11 2.7 SORTING ARRAYS 11 2.7.1 Selection Sort 11 2.7.2 Insertion Sort 12 2.7.3 Bubble Sort 12 2.7.4 Quicksort 13 CHAPTER(3) STACKS AND QUEUES 15 3.1 STACKS 15 3.1.1 Introduction to Stacks 15 3.1.2 Evaluation of Arithmetic Expressions 16 3.1.3 Array Implementation of the Stack 18 3.2 FIFO QUEUES 20 3.2.1 Introduction to Queues 20 3.2.2 Array Implementation of a FIFO Queue 21 CHAPTER(4) POINTERS 23 4.1 WHAT IS A POINTER? 23 4.2 CREATING A POINTER 23 4.3 USING POINTERS 25 4.4 POINTERS AND STRUCTURES 27 4.5 POINTERS AND OBJECTS 29 4.5.1 Declaration and Access 29 4.5.2 An array of Pointers to Objects 29 4.6 POINTERS AND FUNCTION ARGUMENTS 30 4.7 POINTERS AND ARRAYS 32 4.8 POINTER ARITHMETIC 33 4.9 DYNAMIC MEMORY ALLOCATION (NEW, DELETE) 35 4.9.1 The new Operator 35 4.9.2 The delete Operator 35 4.9.3 Dynamic Arrays 36 CHAPTER(5) FILE INPUT/OUTPUT 37 5.1 DISK FILE I/O WITH STREAMS 37 5.2 FORMATTED FILE I/O 37 5.2.1 Writing data 37 5.2.2 Reading Data 38 5.2.3 Character I/O 39 5.3 BINARY I/O 40 CHAPTER(6) LINKED LISTS 43 6.1 INTRODUCTION 43 6.2 CREATING A LINKED LIST 43 6.3 DELETING A NODE FROM A LINKED LIST 44 6.4 ADDING NODES TO A LINKED LIST (INSERTING A NODE) 44 6.5 TRAVERSING A LINKED LIST 45 6.6 WORKING WITH DYNAMICALLY CREATED NODES 46

CHAPTER(1)

FUNDAMENTALS

1.1 DATA TYPES

A data type is a set of values and a collection of operations on those values. All the data that we process on a computer ultimately decompose into individual bits, but writing programs that process bits would be tiresome indeed. Types allow us to specify how we will use particular sets of bits and functions allow us to specify the operations that we will perform on the data. The four basic data types in C++ are

INTEGER These are whole numbers, both positive and negative. Unsigned integers (positive values only) are supported. In addition, there are short and long integers. The keyword used to define integers is,

int An example of an integer value is 32. An example of declaring an integer variable called sum and assigning the value 20 to it is,

int sum; sum = 20;

To declare an integer constant we use the const keyword:

const int MaxMark=30;

In fact we can use const with all other data types, not only with integers.

FLOATING POINT These are numbers which contain fractional parts, both positive and negative. The keyword used to define float variables is, float An example of a float value is 34.12. An example of declaring a float variable called money and assigning the value 326.75 to it is, float money; money = 326.75;

DOUBLE

These are exponential numbers, both positive and negative. The keyword used to define double variables is double. An example of a double value is 3.0E2. An example of declaring a double variable called big and assigning the value 312E+27 to it is, double big; big = 312E+27;

CHARACTER These are single characters. The keyword used to define character variables is, char. An example of a character value is the letter A. An example of declaring a character variable called letter and assigning the value ‘A’ to it is, char letter; letter = 'A';

Note the assignment of the character A to the variable letter is done by enclosing the value in single quotes. Remember the golden rule: for single character - use single quotes.

Sample program illustrating each data type

#include <iostream.h> int main()

 { int sum, N=3; float money, average; char letter; double pi; sum = 10; money = 2.21; letter = 'A'; pi = 2.01E26; // assign integer value // assign float value // assign character value // assign a double value average = ((float) sum) / N; cout<<"value of sum = "<

Sample program output value of sum = 10 value of money = 2.21 value of letter = A value of pi = 2.01e+26 average = 3.333333

Operations are associated with types, not the other way around. When we perform an operation, we need to ensure that its operands and result are of the correct type. Neglecting this responsibility is a common programming error. In some cases C++ performs implicit type conversions; in others we use casts, or explicit type conversions. For example if sum and N are integers (see the above program), the expression

((float) sum) / N

includes both types of conversion: the (float) is a cast that converts the value of sum to float; then an implicit conversion is performed for N to make both arguments of the divide operator float.

Many of the operations associated with the standard data types are built into the C++ language. Other operations are found in the form of functions defined in standard function libraries or defined by us - programmers. This concept of data type can be extended to user- defined data types. The C++ language provides us with the tools required for defining our own data types.

In addition to the basic data types described above C++ allows us to define our own data types. We usually use functions in conjunction with user-defined data types or data structures.

1.2

Functions

Functions help us structure our programs effectively by subdividing those programs into subprograms, each of which can then be made to deal with one limited task or activity while the main program drives these individual components. Functions are also used within classes to define an object’s behavior. In C++ there are two types of functions:

(i)

functions which compute and return a value;

(ii) functions which simply carry out some task without returning a specific value.

For further information on functions refer to your textbook: OOP in C++ by Robert Lafore.

1.3 Formatting Output

(For further details see Lafore textbook pages 52-54, and pages 270-274.

Manipulators are operators used with the insertion operator (<<) to modify or manipulate the way data are displayed, e.g, endl, setw, setprecision, … The setw manipulator changes the field width of the output. Output is right justified within the specified field. The setprecision manipulator sets the accuracy (number of decimal places) for float data. The setiosflag manipulator (which is preceded by the class name, ios) works with the showpoint and fixed flags as follows:

The showpoint flag shows the decimal point. The fixed flag forces fixed decimal format (rather than the exponential format)

To use the above manipulator include the header file: <iomanip.h>

Example

The following example illustrates the use of some of the output manipulator. For more examples refer to your textbook.

#include<iostream.h>

#include<iomanip.h>

#include<math.h>

int main()

{

cout<<setw(10)<<"i"<<setw(12)<<"sqrt(i)"<<endl;

for(float i=1;i<=10;i+=0.5) cout <<setiosflags(ios::fixed)<<setiosflags(ios::showpoint)

<<setprecision(2)<<setw(10)<<i

<<setw(12)<<setprecision(3)<<sqrt(i)<<endl;

return 0;

}

10
12

CHAPTER(2)

ARRAYS AND STRINGS

The term data structure refers to a mechanism for organizing information to provide convenient and efficient mechanisms for accessing and manipulating it. The most fundamental data structure is the array, which is defined as a primitive type in C++ and in most other programming languages. The array is a linear data structure. An array is a fixed collection of same-type data that are stored contiguously and that are accessible by an index or a subscript. For example:

int a[10]; //array a has 10 integer elements. int b[]={21,8,20,14,24,29,12}; //array b is initialized with the shown numbers. char str[]=”The quick brown fox jumped over the lazy dogs”; //null-terminated string.

We can also use two-dimensional arrays. The following examples show how to declare and initialize two-dimensional array.

Example1:

A two-dimensional array b[2] [2] is initialized: int b[2] [2] = {{1,2},{3,4}}; which means element b[0] [0]= 1, b[0] [1]= 2, b[1] [0]= 3 and b[1] [1]= 4

Example 2:

// displays sales chart, initializes 2-d array #include <iostream.h>

#include <iomanip.h>

//for setprecision, etc.

const int DISTRICTS = 4; const int MONTHS = 3;

int main()

//array dimensions

{

int d, m;

//initialize array elements double sales[DISTRICTS][MONTHS] =

{

{ 1432.07,

{ 322.00, 13838.32, 17589.88 }, { 9328.34, 934.00, 4492.30 },

234.50, 654.01 },

{ 12838.29, 2332.63, cout << "\n\n";

cout << " cout << "

for(d=0; d<DISTRICTS; d++)

32.93 }

Month\n";

2

1

3";

};

{

cout <<"\nDistrict " << d+1; for(m=0; m<MONTHS; m++) cout << setw(10) << setiosflags(ios::fixed) << setiosflags(ios::showpoint) << setprecision(2) << sales[d][m]; //access array element

}

cout << endl; return 0;

}

2.1

Arrays of Structures

Arrays can contain structures as well as simple data types. Consider the following structure,

struct date {int month, day, year;};

Let us now create an array called birthdays of the same data type as the structure date

date birthdays[6];

This creates an array of 6 elements which have the structure of date. We may assign values to the second element of the array as:

birthdays[1].month = 3;

birthdays[1].day

birthdays[1].year = 1992;

= 8;

Or we may initialize the array birthdays as follows:

Example:

date birthdays[6] = {

{8, 14, 1989}, {3,

8, 1992},

{10, 12, 1994}, {9, 29, 1996}, {6, 20, 1999}, {6, 10, 2002}};

// Demonstrates using arrays of structures.

#include <iostream.h>

int main()

{

// Define a structure to hold entries. struct entry { char fname[20]; char lname[20]; char phone[10];

};

// Declare an array of structures. entry list[4];

int i;

// Loop to input data for four people.

for (i = 0; i < 4; i++)

{

cout<<"Enter first name: "; cin>>list[i].fname; cout<<"Enter last name: "; cin>>list[i].lname; cout<<"Enter phone in 123-4567 format: "; cin>>list[i].phone;

}

// Print two blank lines. cout<<"\n\n";

// Loop to display data. for (i = 0; i < 4; i++)

{

cout<<"Name: "<<list[i].fname<<" "<<list[i].lname; cout<<"\t\tPhone: "<<list[i].phone<<endl;

}

return 0;

}

Program input/output example

Enter first name: Salim Enter last name: Ahmed Enter phone in 123-4567 format: 555-1212 Enter first name: Samia Enter last name: Ibrahim Enter phone in 123-4567 format: 555-3434 Enter first name: Nasir Enter last name: Abdalla Enter phone in 123-4567 format: 555-1212 Enter first name: Hanan Enter last name: Adam Enter phone in 123-4567 format: 555-1234

Name: Salim Ahmed Name: Samia Ibrahim Name: Nasir Abdalla Name: Hanan Adam

Phone: 555-1212 Phone: 555-3434 Phone: 555-1212 Phone: 555-1234

2.2 Structures and Arrays

Example1:

You can define a structure that contains one or more arrays as members. The array can be of any C++ data type (int, char, and so on). For example, the statements

struct data{ int x[4]; char y[10];

};

define a structure of type data that contains a four-element integer array member named x and a 10-element character array member named y. You can then declare a structure named record of type data as follows:

data record;

The organization of this structure is shown in the figure below. Note that the elements of array x take up twice as much space as the elements of array y. This is because a type int typically requires two bytes of storage, whereas a type char usually requires only one byte.

record.x[0]
record.x[3]
record.y[9]

Record.y[6]

The organization of a structure that contains arrays as members.

You access individual elements of arrays that are structure members using a combination of the member operator and array subscripts:

record.x[2] = 100; record.y[1] = ‘x’;

Here is another example:

struct month {

int number_of_days; char name[4];

};

month this_month = { 31, "Jan" };

this_month.number_of_days = 31; strcpy( this_month.name, "Jan" ); cout<<"The month is "<< this_month.name;

Note that the array name has an extra element to hold the end of string null character ('\0').

2.3 Arrays as Class Member Data

Arrays can be used as data items in classes. Let’s look at an example:

#include <iostream.h> //to store a student name and marks of up to 29 subjects. class student

{

private:

char name[25]; //student name int marks[29]; //an array to store marks for 29 subjects

public:

student(); //constructor student(char s[]); //constructor void getdata(); void show();

};

student::student()

{

name[0]='\0';

for (int i=0;i<29;i++) marks[i]=-1;

}

student::student(char s[])

{

for (int i=0;s[i]!='\0';i++) name[i]=s[i];

name[i]='\0';

for ( i=0;i<29;i++) marks[i]=-1;

}

void student::getdata()

{

int m;

if(name[0]=='\0')

{

cout<<"Enter student name>";

cin.get(name,25);

}

cout<<"Enter marks for "<<name<<". To stop enter -1\n";

for (int i=0;i<29;i++)

{

cout<<"marks["<<i<<"]=";

cin>>m;

if(m<0)

else

break;

}

}

marks[i]=m;

void student::show()

{

}

cout<<"Name: "<<name<<endl; cout<<"subject\tMark\n"; for (int i=0;i<29;i++)

if(marks[i]>=0)

cout<<"

"<<i<<"\t"<<marks[i]<<endl;

int main()

{

student s1, s2("Sayed Ahmed Hussein");

s1.getdata();

s1.show();

s2.getdata();

s2.show();

return 0;

}

2.4 Arrays of Objects

We have seen how an object can contain an array. We can also create an array of objects. The following example creates an array of English distances.

// objects using English measurements #include <iostream.h>

////////////////////////////////////////////////////////////////

class Distance

//English Distance class

{

private:

int feet; float inches; public:

void getdist()

//get length from user

 { cout << "\n Enter feet: "; cin >> feet; cout << " Enter inches: "; cin >> inches; }

void showdist() const

//display distance

{ cout << feet << "\'-" << inches << '\"'; }

}; //////////////////////////////////////////////////////////////// int main()

{

const int MAX = 100;

Distance dist[MAX]; int n=0; char ans;

//array of distances //count the entries //user response ('y' or 'n')

cout << endl;

do {

//get distances from user

cout << "Enter distance number " << n+1;

dist[n++].getdist();

cout << "Enter another (y/n)?: "; cin >> ans;

//store distance in array

} while( ans != 'n' );

for(int j=0; j<n; j++)

{

//quit if user types 'n'

//display all distances

cout << "\nDistance number " << j+1 << " is "; dist[j].showdist();

}

cout << endl; return 0;

}

2.5

Strings

A string in C++ is an array of characters terminated by the ASCII NULL character ('\0'). A

string constant is null terminated by the compiler automatically.

‘Q’

‘A’

‘T’

‘A’

‘R’

\0’

Generally, there are two classes of functions (to use them include the header file <string.h>). The first category is concerned with varieties of string copying. When characters are

transferred to a destination string no checks are performed to ensure that the destination string

is sufficiently large, so be aware!

The second class of functions performs tests and returns a non-zero value representing TRUE

or zero for FALSE.

strcat()

char *strcat(char S1[], char S2[]);

Appends the content of the string S2 to the end of the string S1. The first character of S2 overwrites the null character which terminates S1.

Example:

#include <iostream.h> #include <string.h> int main()

{

char str[80]="This is "; strcat(str,"one line text "); cout <<str; return 0;

}

Program output:

This is one line text

strcmp()

int strcmp(const char S1[], const char S2[]);

Compares the two strings S1 and S2 returns a value that is:

<

0

if S1 is less than S2

 == 0 if S1 the same as S2 > 0 if S1 is greater than S2.

Example:

cout <<strcmp(“one”, “one”);

strcpy()

will print 0.

char *strcpy (char S1[], const char S2[]);

Overwrite the original content of the string S1 with the content of the string S2. You can create a string of length 0, strcpy(str,””);

strlen()

int strlen(const char S[]);

Returns the number of characters in S, not counting the null terminating character.

Example:

cout << strlen(“This is a string”);

will print 16.

2.6 Array Searching

A search algorithm is a method of locating a specific item of information in a larger collection of data. This section discusses two algorithms for searching the contents of an array: the linear search algorithm and the binary search algorithm.

2.6.1 Linear Search Algorithm

This is a very simple algorithm. It uses a loop to sequentially step through an array, starting with the first element. It compares each element with the value being searched for and stops when that value is found or the end of the array is reached.

The following function implements a linear (or sequential) search to return the location of element v in the array a between element l (ell) and element r. If the value v is not found in the array a then the function returns 1.

int search(int a[], int v, int l, int r)

{

for (int i = l; i <= r; i++) if (v == a[i]) return i; return -1;

}

It is easy to understand

Easy to implement

Does not require the array to be in order

If there are 20,000 items in the array and what you are looking for is in the 19,999th element, you need to search through the entire list.

2.6.2

Binary Search Algorithm

A more efficient search method for ordered arrays is given in the following function. This method is called the binary search method. The process compares element v with the middle element of the array, and returns successfully if the values match. If v is less than the middle element then the search is conducted only in the left half of the array. If it is greater then the search is made in the right half of the array. Now v is compared with the middle element of the subarray and the process is repeated till a value is found or a 1 is returned.

int search(int a[], int v, int l, int r)

 { while (r >= l) { int m = (l+r)/2; if (v == a[m]) return m; if (v < a[m]) r = m-1; else l = m+1; } return -1; }

2.7 Sorting Arrays

2.7.1 Selection Sort

One of the simplest sorting algorithms works as follows:

First, find the smallest element in the array, and exchange it with the element in the first position.

Then, find the second smallest element and exchange it with the element in the second position.

Continue in this way until the array is sorted.

This method is called selection sort because it works by repeatedly selecting the smallest

remaining element.

#include <iostream.h> #include <stdlib.h>

void exch(int &x, int &y); void selection(int a[], int l, int r);

int main(){ int t, x[100];

randomize();

for (int i=0; i<100; i++) x[i]=rand()%100; for (i=0; i<100; i++) cout<<x[i]<<" "; cout<<endl<<endl; selection(x, 0,99); for (i=0; i<100; i++) cout<<x[i]<<" "; return 0;

}

void selection(int a[], int l, int r) { for (int i = l; i < r; i++) { int min = i; for (int j = i+1; j <= r; j++) if (a[j] < a[min]) min = j; exch(a[i], a[min]);

}

}

void exch(int &x, int &y)

 { int temp=x; x=y; y=temp; }

2.7.2 Insertion Sort

In this sort method we find the smallest element and insert it in its appropriate position. We

need to make space for the element being inserted by moving larger elements to the right, and

then inserting the element into the vacated position.

#include <iostream.h> #include <stdlib.h> void exch(int &A, int &B)

{ int t = A; A = B; B = t; }

void compexch(int &A, int &B)

{ if (B < A) exch(A, B); }

void insertion(int a[], int l, int r)

 { for (int i = l+1; i <= r; i++) for (int j = i; j > l; j--) compexch(a[j-1], a[j]); }

int main(int argc, char *argv[])

{

argc = number of arguments argv[0] = program name argv[1] = N argv[2] = sw ( if sw==0 input elements

else choose random numbers.)

int i, N = atoi(argv[1]), sw = atoi(argv[2]); int *a = new int[N];

if (sw) for (i = 0; i < N; i++) a[i] = 1000*(1.0*rand()/RAND_MAX);

else

{ N = 0; while (cin >> a[N]) N++; }

insertion(a, 0, N-1); for (i = 0; i < N; i++) cout << a[i] << " "; cout << endl;

}

A more efficient version of the insertion sort algorithm is given below:

void insertion(int a[], int l, int r)

 { int i; for (i = r; i > l; i--) compexch(a[i-1], a[i]); for (i = l+2; i <= r; i++) { int j = i; int v = a[i]; while (v < a[j-1]) { a[j] = a[j-1]; j--; } a[j] = v; } }

2.7.3 Bubble Sort

The first sort that many people learn, because it is simple, is the bubble sort: Keep passing through the array, exchange adjacent elements that are out of order, continuing until the file is sorted. Bubble sort is easy to program, but it is slower than the other two sort methods.

#include <iostream.h> #include <stdlib.h>

void exch(int &x, int &y);

void compexch(int &A, int &B); void bubble(int a[], int l, int r); int main()

 { int t, x[100]; randomize(); for (int i=0; i<100; i++) x[i]=rand()%100; for (i=0; i<100; i++) cout<

void bubble(int a[], int l, int r) { for (int i = l; i < r; i++) for (int j = r; j > i; j--) compexch(a[j-1], a[j]);

}

void exch(int &x, int &y)

 { int temp=x; x=y; y=temp; }

void compexch(int &A, int &B) { if (B < A) exch(A, B); }

2.7.4 Quicksort

This sort method is used more than any other. It is a divide-and-conquer method for sorting. It works by partitioning an array into two parts, then sorting the parts independently. The crux of the method is the partitioning process, which rearranges the array to make the following three conditions hold:

The element a[i] is in its final place in the array for some i.

None of the elements in a[l],…,a[i-1] is greater than a[i].

None of the elements in a[i+1],…,a[r] is less than a[i].

 Less than or equal to v Greater than or equal to v v     l i j r

The following program illustrates the use of Quicksort (using recursion). The partition function is also given herein.

#include <iostream.h> #include <stdlib.h>

int partition(int a[], int l, int r); void exch(int &x, int &y); void quicksort(int a[], int l, int r); int main()

 { int t, x[100]; randomize(); for (int i=0; i<100; i++) x[i]=rand()%100; for (i=0; i<100; i++) cout<

void quicksort(int a[], int l, int r)

{

if (r <= l) return; int i = partition(a, l, r); quicksort(a, l, i-1); quicksort(a, i+1, r);

}

int partition(int a[], int l, int r) { int i = l-1, j = r; int v = a[r]; for (;;)

{

while (a[++i] < v) ; while (v < a[--j]) if (j == l) break; if (i >= j) break; exch(a[i], a[j]);

}

exch(a[i], a[r]); return i;

}

void exch(int &x, int &y)

 { int temp=x; x=y; y=temp; }

A carefully tuned version of quicksort is likely to run significantly faster on most computers than will any other sorting method, and quicksort is widely used as a library sort utility and for other serious applications.

CHAPTER(3)

STACKS AND QUEUES

3.1

Stacks

3.1.1 Introduction to Stacks

Stacks: Piles of objects of any kind; Add/Remove objects only at the top of the pile (LIFO).

A common computer data structure is the stack. The stack is a linear data structure. By linear

we mean a collection of components arranged in a straight line in which we restrict places where one

can add/remove. A stack works like the spring-loaded devices that hold trays in cafeterias. When a tray is put on top, the stack sinks down a little; when you take a tray off, it pops up. The last tray placed on the stack is always the first tray removed. This is called Last-In-First- Out discipline (LIFO).

Stacks are one of the cornerstones of the architecture of the microprocessors used in most modern computers. Functions pass their arguments and store their return addresses on the stack. This kind of stack is implemented partly in hardware and is most conveniently accessed

in assembly language. However stacks can be created completely in software. Software

stacks offer a useful storage device in certain programming situations, such as parsing

(analyzing) algebraic expressions.

A stack comprises two basic operations: push a new item (i.e. add or insert a new item), and

pop the item that was most recently pushed (i.e. remove it).

Adding a new object to the top: pushing it onto the stack.

Adding a new object to the top:

pushing it onto the stack.

Removing an object from the top:

popping it from the stack.

Add/Remove objects only at the top of the pile.

Only the top of the pile is accessible.

The top remains in a fixed position.

Pushing and popping are inverse operations:

Push object and immediately afterwards pop it.

Original stack remains unchanged.

The figure below shows how a sample stack evolves through a series of push and pop operations. Each push increases the size of the stack by 1 and each pop decreases it by 1.

Figure (S1):

This list shows the result of the sequence of operations in the left column (top to bottom), where a letter denotes push and an asterisk denotes pop. Each line displays the operation, letter popped for pop operations, and the contents of the stack after the operations, in order from least recently inserted to most recently inserted, left to right.

Exercises:

 L L A L A * A L S L S T L S T I L S T I * I L S T N L S T N * N L S T F L S T F I L S T F I R L S T F I R * R L S T F I S L S T F I S T L S T F I S T * T L S T F I S * S L S T F I O L S T F I O U L S T F I O U * U L S T F I O T L S T F I O T * T L S T F I O * O L S T F I * I L S T F * F L S T * T L S * S L * L

1. A letter means push and an asterisk means pop in the sequence E A S * Y * Q U E * * * S T * * * I O * N * * *. Give the sequence of values returned by the pop operations.

2. Using the conventions of exercise 1, give a way to insert asterisks in the sequence

E A S Y

(i) E A S Y ; (ii) Y S A E ; (iii) A S Y E ; (iv) A Y E S ; or, in each instance, prove that no such sequence exists.

so that the sequence of values returned by the pop operations is

3.1.2 Evaluation of Arithmetic Expressions

We need to find the value of a simple arithmetic expression involving multiplication and addition of integers, such as

( 5 * ( ( ( 9 + 8 ) * ( 4 * 6

) ) + 7 ) )

This form of the arithmetic expression is called infix notation. The calculation involves saving intermediate results: For example, if we calculate 9 + 8 first, then we have to save 17 while, say, we compute 4 * 6. A stack is the ideal mechanism for saving intermediate results in such calculations.

To solve the above problem we convert the infix expression to postfix (where each operator appears after its two operands). [The reverse of postfix is called prefix, or Polish notation]. In postfix or prefix we do not need parentheses.

The following figure shows the steps for converting an infix expression to its post fix form. The postfix equivalent of the above expression is

5 9 8 + 4 6 * * 7 + *

(

Figure (S2): Conversion of an Infix expression to postfix:

This sequence shows the use of a stack to

convert the infix expression shown above to its postfix form: 5 9 8 + 4 6 * * 7 + * . We proceed from left to right through the expression: If we encounter a number, we write it to the output; if we encounter

a left parenthesis, we ignore it; if we

encounter an operator, we push it on the stack; and if we encounter a right parenthesis; we write the operator at the top of the stack to the output.

 5 5 * * ( * ( * ( * 9 9 * + * + 8 8 * + ) + * * * * ( * * 4 4 * * * * * * 6 6 * * * ) * * * ) * * + * + 7 7 * + ) + * ) *

The following program stores the postfix form of the given expression in an array called p. It uses a class called STACK, defined as a template in a file called stack.cpp [Class templates allow us to use different data types with one class definition-see Lafore, page 690].

#include <iostream.h> #include <string.h> #include "stack.cpp" int main() { char a[] = "(5*(((9+8)*(4*6))+7))"; int N = strlen(a); char p[80]; STACK<char> ops;

for (int i = 0,

{

j=0; i < N; i++)

if (a[i] == ')')

{p[j++]=ops.pop();p[j++]=' ';} if ((a[i] == '+') || (a[i] == '*')) ops.push(a[i]); if ((a[i] >= '0') && (a[i] <= '9')) {p[j++]=a[i];p[j++]=' ';}

}

p[j]='\0';

cout << p<<endl; return 0;

}

Postfix notation and an associated stack give us a natural way to organize a series of computational procedures. Some calculators and some computer languages explicitly base their method of calculations on postfix and stack operations every operator pops its arguments from the stack and returns the results to the stack.

Evaluation of a postfix expression:

The following figure shows how to evaluate a postfix expression using a stack.

Figure (S3): This sequence shows the use of

a stack to evaluate the postfix expression 5 9

8 + 4 6 * * 7 + . Proceeding from left to right through the expression, if we encounter

a number, we push it on the stack; and if we

encounter an operator, we push the result of applying the operator to the top two numbers on the stack.

 5 5 9 5 9 8 5 9 8 + 5 17 4 5 17 4 6 5 17 4 6 * 5 17 24 * 5 408 7 5 408 7 + 5 415 * 2075

The following program evaluates a postfix expression stored in the array a. It uses a class called STACK, defined as a template in a file called stack.cpp

#include <iostream.h> #include <string.h> #include "stack.cpp" int main()

{ char a[] = "5 9 8 + 4 6 * * 7 + *"; int N = strlen(a); STACK<int> save; for (int i = 0; i < N; i++)

{

if (a[i] == '+') save.push(save.pop() + save.pop()); if (a[i] == '*') save.push(save.pop() * save.pop()); if ((a[i] >= '0') && (a[i] <= '9'))

save.push(0);

while ((a[i] >= '0') && (a[i] <= '9')) save.push(10*save.pop() + (a[i++]-'0'));

}

cout << save.pop() << endl;

return 0;

}

Exercises

1. Convert to postfix the expression

( 5 * ( ( 9 * 8

) + ( 7 * ( 4 + 6 ) ) ) )

.

2. Give the contents of the stack as the following expression is evaluated by the above program (Follow the steps shown in the figure which is given before the above program)

5 9 * 8 7 4 6 + * 2 1 3 * + * + *

.

3. Extend the above programs to include the ‘–‘ and ‘/’ operators.

3.1.3 Array Implementation of the Stack

In the above programs we used the stack without knowing how it is implemented. We just know the data structure and the set of operations defined on the data structure. This is the basic idea of Abstract Data Types (ADT) we abstract from the implementation details. The interface is given in a separate header file (stack.h) which shows information such as function prototypes, classes, … which are needed by any one who want to use the actual data structure the stack. The following program is a list of the stack interface. The line template <class Item> which precedes the class definition, indicates that the class can be used with any data type such as (int, char, ….) in place of Item. If we do not make use of class templates, then we have to define a separate class for each data type we have.

//file: stack.h template <class Item> class STACK

{

private:

enum Size {maxN=80};//a tricky way of defining a constant. Item s[maxN]; int N; public:

STACK(); int empty() const; void push(Item item); Item pop();

};

The actual implementation of the stack data structure is given in the program below. It uses the header file given above. By separating the implementation of the data structure from its clients (i.e. programs that use it), we can easily change the implementation without affecting those clients. //file: stack.cpp #include "stack.h"

template <class Item> STACK<Item>::STACK()

{

N

=

0; }

template <class Item>

int STACK<Item>::empty() const

{ return N == 0; }

template <class Item>

void STACK<Item>::push(Item item)

{ s[N++] = item; }

template <class Item>

Item STACK<Item>::pop()

{ return s[--N]; }

Example The following program show a client program using the stack ADT. #include <iostream.h> #include “stack.cpp” int main()

 { STACK s1; s1.push(11); s1.push(22); cout << "1: " << s1.pop() << endl; //22 cout << "2: " << s1.pop() << endl; //11 s1.push(33); s1.push(44); s1.push(55); s1.push(66); cout << "3: " << s1.pop() << endl; } //66 cout << "4: " << s1.pop() << endl; //55 cout << "5: " << s1.pop() << endl; //44 cout << "6: " << s1.pop() << endl; //33 return 0; Output:

1: 22

2: 11

3: 66

4: 55

5: 44

6: 33

To run the client program you put the client, the interface and the implementation modules in a folder and define a project that includes those files. You then need to build the project to produce the executable file.

3.2

FIFO Queues

3.2.1 Introduction to Queues

The first-in, first-out (FIFO) queue is another fundamental ADT that is similar to the stack, but uses the opposite rule to decide which element to remove. Rather than removing the most recently inserted element, we remove the element that has been in the queue the longest.

Buying Tickets on the Day of Play

On-Site Registration

Queues are abundant in everyday life. When we wait in line to see a movie, to register courses,

or to buy groceries, we are being processed according to a FIFO discipline. Perishable items in

a grocery are stored in a FIFO queue rather than in a stack. Similarly, FIFO queues are

frequently used within computer systems to hold tasks that are yet to be accomplished when we want to provide services on a first-come, first-served basis. To summarize, FIFO queues are used for regulating the processing of tasks in a system to ensure “fair” treatment

Client-server systems: Clients line up to wait for service

Operating systems: Queues of tasks, e.g. print queue

Simulation and modeling: Performance analysis of systems under various loads, e.g. air traffic control, urban transport etc.

A FIFO queue is defined as an ADT that comprises two basic operations: put a new item (i.e.

insert), and get the item that was least recently inserted (i.e. remove it).

Figure (Q1): A FIFO queue example This list shows the result of the sequence of operations in the left column (top to bottom), where a letter denotes put and an asterisk denotes get. Each line displays the operation, the letter returned for get operations, and the content of the queue in order from least recently inserted to most recently inserted, left to right.

 F F I F I R F I R S F I R S * F I R S T I R S T * I R S T I R S T I N R S T I N * R S T I N * S T I N * T I N F I N F I I N F I * I N F I R N F I R S N F I R S * N F I R S * F I R S * I R S T R S T * R S T O S T O U S T O U T S T O U T * S T O U T * T O U T * O U T * U T * T

3.2.2 Array Implementation of a FIFO Queue

The array representation of a FIFO queue requires that we reserve enough space for the maximum number of items expected throughout the computation. We maintain two indices into the array: one to the beginning of the queue and one to the end of the queue. We consider the contents of the queue to be the elements between the indices. To get an element, we remove it from the beginning (head) of the queue and increment the heads index; to put an element; we add it to the end (tail) of the queue and increment the tail index. A sequence of put and get operations causes the queue to appear to move through the array, as illustrated in the figure below. When it hits the end of the array, we arrange for it to wrap around to the beginning. The details of this compuation are given in the program given below the shown figure.

Figure (Q2): A FIFO queue example, array implementation This sequence shows the data manipulation underlying the abstract representation in the previous figure when we implement the queue by storing the items in an array, keeping indices to the beginning and end of the queue, and wrapping the indices back to the beginning of the array when they reach the end of the array. In this example, the tail index wraps back to the beginning when the second T is inserted, and the head index wraps when the second S is removed

 F F I F I R F I R S F I R S * F I R S T I R S T * I R S T I R S T I N R S T I N * R S T I N * S T I N * T I N F I N F I I N F I * I N F I R N F I R S N F I R S * N F I R S * F I R S * I R S T T R S * R T S O T O S U T O U S T T O U T S * S T O U T * T O U T * O U T * U T * T

The QUEUE ADT An array implementation of the QUEUE ADT is given herein. The interface part of the QUEUE ADT is given below:

template <class Item> class QUEUE

{

private:

enum Size {maxN=80}; Item q[maxN+1]; int N, head, tail; public:

QUEUE(); int empty() const; void put(Item); Item get();

};

The implementation part of the QUEUE ADT is shown below:

#include "queue.h" template <class Item> QUEUE<Item>::QUEUE() { N = maxN+1; head = N; tail = 0; }

template <class Item>

int QUEUE<Item>::empty() const

{ return head % N == tail; }

template <class Item> void QUEUE<Item>::put(Item item)

{ q[tail++] = item; tail = tail % N; }

template <class Item> Item QUEUE<Item>::get()

The following example shows a client program that uses the QUEUE ADT. Compare the output with that of the similar program that uses the STACK ADT, which is given in the previous section.

#include <iostream.h> #include "queue.cpp" int main()

 { QUEUE q1; q1.put(11); q1.put(22); cout << "1: " << q1.get() << endl; //11 cout << "2: " << q1.get() << endl; //22 q1.put(33); q1.put(44); q1.put(55); q1.put(66); cout << "3: " << q1.get() << endl; //33 cout << "4: " << q1.get() << endl; //44 cout << "5: " << q1.get() << endl; //55 cout << "6: " << q1.get() << endl; //66 } return 0;

Output:

1: 11

2: 22

3: 33

4: 44

5: 55

6: 66

Exercises

1. Give the contents of q[0],…,q[4] after the execution of the operations illustrated in Figure (Q1), using the QUEUE ADT program. Assume that maxN is 10.as in Figure

(Q1).

2. A letter means put and an asterisk means get in the sequence

E A

S

*

Y

*

Q

U

E

*

*

*

S

T *

*

*

I O

*

N

*

*

*

.

Give the sequence of values returned by the get operations when this sequence of operations is performed on an initially empty FIFO queue.

CHAPTER(4)

POINTERS

4.1 What Is a Pointer?

A PC’s random access memory (RAM) is comprised of many thousands of sequential storage locations. Each storage location is identified by a unique address. The memory addresses in a given computer range from 0 to a maximum value that depends on the amount of memory installed.

When you declare a variable in C++, the compiler sets aside memory to store that variable. The storage location has a unique address. The compiler associates that address with the variable name. When your program uses the variable name, it automatically accesses the proper memory location. The location is being used, but it is hidden from you you need not be concerned with it.

The following figure shows schematically a variable named rate that has been declared and initialized to 100. The compiler has set aside storage at address 1003 for the variable, and has associated the name rate with the address 1003.

1000 1001

1002

1003

1004

100

rate

4.2 Creating a Pointer

You should note that the address of the variable rate (or any other variable) is a number and can be treated like any other number in C++. If you know a variable's address, you can create a second variable in which to store the address of the first. The first step is to declare a variable to hold the address of rate. Give it the name prate, for example.

float *prate;

At first, prate is uninitialized. Storage has been allocated for prate but its value is undetermined (suppose it is at address 1001), as shown in the Figure below.

1000 1001

1002

1003

1004

?

100

prate

rate

memory storage has been allocated for the variable prate

The next step is to store the address of the variable rate in the variable prate:

prate = &rate;

Because prate now contains the address of rate, it indicates the location where rate is stored in memory. In C++ parlance, prate points to rate, or is a pointer to rate. This is shown below:

 1000 1001 1002 1003 1004 1003 100

prate

rate

The variable prate contains the address of the variable rate and is therefore a pointer to rate

To summarize, a pointer is a variable that contains the address of another variable. Now you can get down to the details of using pointers in your C++ programs.

Hint:

numeric value, like all variables, must be declared before it can be used.

You should note that the address of the variable rate is itself a number. A pointer is a

To print the content of rate, we can use the following statement:

cout<<rate;

cout<< *prate;

Pointers enable us:

or

to effectively represent complex data structures,

to change values as arguments to functions,

to work with memory which has been dynamically allocated, and

to more concisely and efficiently deal with arrays.

A pointer provides an indirect means of accessing the value of a particular data item. Let’s see how pointers actually work with a simple example,

int count = 10, *ptr_int;

declares an integer count initialized with a value of 10, and also an integer pointer called ptr_int. Note that the prefix * defines the variable to be of type pointer. To set up an indirect reference between ptr_int and count, the & prefix is used, i.e.,

ptr_int = &count

This assigns the memory address of count to ptr_int, not the actual value of count stored at that address.

To reference the value of count using ptr_int, the * is used in an assignment, e.g.,

x = *ptr_int;

Since ptr_int is set to the memory address of count, this operation has the effect of assigning the contents of the memory address pointed to by ptr_int to the variable x, so that after the operation variable x has a value of 10. Note

POINTERS CONTAIN MEMORY ADDRESSES, NOT VALUES!

The operator & gives the address of an object.

The operator * is the indirection or dereferencing operator (used to access the object the pointer points to)

4.3 Using Pointers

To see more examples of pointers, suppose that x and y are integers and ip is a pointer to int. The sequence below shows how to declare a pointer and how to use & and *:

int x = 1, y = 2, z[10]; int *ip;

ip = &x; y = *ip; *ip = 0; ip = &z[0];

//ip is a pointer to int

//ip now points to x //y is now 1 // x is now 0 //ip now points to z[0]

If ip points to the integer x, then *ip can occur in any context where x could, so

*ip = *ip + 10;

increments *ip by 10 (i.e. x is icreased by 10). The statement

y = *ip + 1;

takes whatever the pointer ip points to, adds 1 and assigns the result to y. The statement

*ip += 1;

increments what ip points to, as do

and

++*ip;

(*ip)++;

The parentheses are necessary in this last example; without them, the statement would increment ip instead of what it points to.

Since pointers are variables, they can be used without dereferencing. For example, if iq is another pointer to int,

iq = ip;

copies the contents of ip into iq, thus making iq point to whatever ip pointed to.

A complete program is given below:

#include <iostream.h>

int main()

{

int count

= 10, x, * ptr_int;

// this assigns the memory address of count to ptr_int ptr_int = &count;

//assigns the value stored at the address specified by ptr_int to x x = *ptr_int;

cout<<"count = “<<count<<” return 0;

}

x = "<<x;

This however, does not illustrate a good use for pointers. The following program illustrates another way to use pointers, this time with characters,

#include <iostream.h>

int main()

 { char c = 'Q'; char *ptr_char = &c; cout<

Exercises

Determine the output of the pointer programs 1, 2 and 3 shown below:

(1) // illustrating pointers #include <iostream.h>

 int main() { int count = 10, x, *int_pointer; // this assigns the memory address of count to int_pointer int_pointer = &count; //assigns the value stored at the address specified by int_pointer to x x = *int_pointer; cout<<"count = “<

(2) // Further examples of pointers

#include <iostream.h>

 int main() { char c = 'Q'; char *ptr_char = &c; cout<

(3)

// Another program with pointers #include <iostream.h>

int main()

{

int i1, i2, *p1, *p2;

i1 = 5; p1 = &i1; i2 = *p1 / 2 + 10; p2 = p1;

cout<<"i1=”<<i1<<” i2=”<<i2<<” *p1=”<<*p1<<” *p2="<<*p2; return 0;

}

Exercises

1. Declare a pointer to an integer called address.

2. Assign the address of a float variable balance to the float pointer temp.

3. Assign the character value 'W' to the variable pointed to by the char pointer letter.

4. What is the output of the following program segment?

int

count = 10, *temp, sum = 0;

temp = &count; *temp = 20; temp = &sum; *temp = count; cout<<"count = “<<count<<” *temp = “<<*temp<<” sum = "<<sum;

5. Declare a pointer to the text string "Hello" called message.

4.4 Pointers and Structures

Consider the following,

struct date {

int month, day, year;

};

date todays_date, *date_pointer;

date_pointer = &todays_date;

(*date_pointer).day = 21; (*date_pointer).year = 1958; (*date_pointer).month = 1;

++(*date_pointer).month; if((*date_pointer).month == 8 )

Pointers to structures are so often used in C++ that a special operator exists. The structure pointer operator, the ->, permits expressions that would otherwise be written as,

(*x).y

to be more clearly expressed as

x->y

making the if statement from above program

if( date_pointer->month == 08 )

Therefore, there are three ways to access a structure member:

Using the structure name

Using a pointer to the structure with the indirection operator (*)

Using a pointer to the structure with the indirect membership operator (->)

If p_str is a pointer to the structure str, the following expressions are all equivalent:

Example:

str.memb

(*p_str).memb

p_str->memb

// Program to illustrate structure pointers #include <iostream.h>

int main()

{

struct date { int month, day, year;

}; date today, *date_ptr;

date_ptr = &today; date_ptr->month = 6; date_ptr->day = 4; date_ptr->year = 1987;

cout<<"Todays date is “<<date_ptr->day<<”/”<<date_ptr->month <<”/”<< date_ptr->year % 100; return 0;

}

Consider the following example:

struct coord{ int x; int y;

};

struct rectangle{ coord topleft; coord bottomrt;

} mybox, *rp=&mybox;

then the following five expressions are equivalent:

mybox.topleft.x

rp->topleft.x

(mybox.topleft).x

(rp->topleft).x

(*rp).topleft.x

So far, all that has been done could've been done without the use of pointers. Shortly, the real value of pointers will become apparent.

4.5

Pointers and Objects

4.5.1 Declaration and Access

Pointers can point to objects as well as to simple data types and arrays, for example Distance *dpointer; Just as we do with structures, we access object members using the (*dpointer).member form, or dpointer->member form.

Example

// accessing member functions by pointer #include <iostream.h> ////////////////////////////////////////////////////////////////

class Distance

//English Distance class

{

private:

int feet; float inches; public:

void getdist()

//get length from user

{

cout << "\nEnter feet: ";

cout << "Enter inches: ";

cin >> feet; cin >> inches;

}

void showdist()

//display distance

{ cout << feet << "\'-" << inches << '\"'; }

}; //////////////////////////////////////////////////////////////// int main()

{

Distance dist; dist.getdist(); dist.showdist();

//define a named Distance object //access object members

//

with dot operator

Distance* distptr;

distptr = new Distance; //points to new Distance object

distptr->getdist(); distptr->showdist(); cout << endl; return 0;

//pointer to Distance

//access object members

//

with -> operator

}

4.5.2 An array of Pointers to Objects

A common programming construction is an array of pointers to objects. This arrangement allows easy access to a group of objects, and is more flexible than placing the objects themselves in an array. This is illustrated in the following example:

// array of pointers to objects #include <iostream.h>

////////////////////////////////////////////////////////////////

class person

//class of persons

{

protected:

char name[40];

//person's name

public:

void setName()

{

cout << "Enter name: ";

cin >> name;

//set the name

}

void printName()

{

cout << "\n

};

}

//get the name

Name is: " << name;

////////////////////////////////////////////////////////////////

int main()

{

person* persPtr[100];

int n = 0; char choice;

do { persPtr[n] = new person; persPtr[n]->setName(); n++;

//array of pointers to persons //number of persons in array

//put persons in array //make new object //set person's name //count new person

cout << "Enter another (y/n)? "; //enter another

cin >> choice;

//person?

}

while( choice=='y' );

for(int j=0; j<n; j++)

{

//quit on 'n'

//print names of //all persons

cout << "\nPerson number " << j+1; persPtr[j]->printName();

}

cout << endl; return 0;

}

//end main()

4.6 Pointers and Function Arguments

The C++ language passes arguments to functions by value. This means that a copy of the argument is passed to the function. There is no direct way for the called function to alter a variable in the calling function. For instance, a sorting routine might exchange two out-of-order elements with a function called swap(). It is not enough to write

swap (a, b);

where the swap function is defined as

void swap (int x, int y)

 { int temp; temp = x; x = y; y = temp; }

//WRONG

because of call by value, swap can't affect the arguments a and b in the routine that called it. The function above only swaps copies of a and b.

One way to obtain the desired effect is for the calling program to use call by reference (see chapter 1). Another way is to pass pointers to the values to be changed:

swap (&a, &b);

Since the operator & produces the address of a variable, &a is a pointer to a. In swap itself, the parameters are declared to be pointers, and the operands are accessed indirectly through them.

void swap (int *px, int *py)

 { int temp; temp = *px; *px = *py; *py = temp; }

//interchange *px and *py

The above action may be shown pictorially as follows:

In caller:
a:
b:
In swap:
px
py

Pointer arguments enable a function to access and change objects in the functions that called it. It should be clear by now why we use the & operator with the scanf() function. Without the & operator scanf() fails to pass the input into the actual parameters.

Example:

//This programs inputs a time in seconds and converts it to hours, //minutes and seconds.

#include <iostream.h>

void time_hms(int t, int *h, int *m, int *s);

 int main() { int time; int hours, minutes, seconds; //original data value //computed values cout<<"Enter the time in seconds: "; cin>>time; time_hms(time, &hours, &minutes, &seconds); cout <<"The time “<

void time_hms(int t, int *h, int *m, int *s)

 { int temp = t/60; //total minutes *s = t % 60; *m = temp % 60; *h = temp / 60; //number of seconds //number of minutes //number of hours }

4.7 Pointers and Arrays

In C++ there is a strong relationship between pointers and arrays, strong enough that pointers and arrays should be discussed simultaneously. Any operation that can be achieved by array subscripting can be done with pointers. The pointer version will in general be faster.

The declaration

int a[10];

defines an array a of size 10, that is a block of 10 consecutive objects named a[0], a[1], …, a[9].

a:

a[0] a[1]
a[9]

The notation a[i] refers to the i-th element of the array. If pa is a pointer to an integer, declared as

int *pa;

then the assignment

pa = &a[0];

sets pa to point to element zero of a; that is, pa contains the address of a[0].

pa:

a:
a[0] a[1]
a[9]

Now the assignment

x = *pa;

will copy the contents of a[0] into x.

If pa points to a particular element of the array, then by definition pa+1 points to the next element, pa+i points i elements after pa, pa-i points i elements before. Thus, if pa points to

a[0],

*(pa+1)

refers to the content of a[1], pa+i is the address of a[i], and *(pa+i) is the content of a[i].

pa:
pa+1: pa+2:
a:
a[0] a[1]
a[9]