You are on page 1of 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) 1.1 1.2 1.3 FUNDAMENTALS .................................................................................................................................. 1 DATA TYPES .......................................................................................................................................................... 1 FUNCTIONS................................................................................................................................................................ 2 FORMATTING OUTPUT ............................................................................................................................................... 3 ARRAYS AND STRINGS ....................................................................................................................... 4

CHAPTER(2)

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) 6.1 6.2 6.3 6.4 6.5 6.6 6.7 LINKED LISTS ...................................................................................................................................... 43

INTRODUCTION........................................................................................................................................................ 43 CREATING A LINKED LIST ....................................................................................................................................... 43 DELETING A NODE FROM A LINKED LIST .................................................................................................................. 44 ADDING NODES TO A LINKED LIST (INSERTING A NODE) .......................................................................................... 44 TRAVERSING A LINKED LIST .................................................................................................................................... 45 WORKING WITH DYNAMICALLY CREATED NODES .................................................................................................. 46 A LINKED LIST EXAMPLE ......................................................................................................................................... 48

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.
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

Sample program illustrating each data type #include <iostream.h> int main() { int sum, N=3; float money, average; char letter; double pi; sum = 10; // assign money = 2.21; // assign letter = 'A'; // assign pi = 2.01E26; // assign average = ((float) sum) / N; cout<<"value of cout<<"value of cout<<"value of cout<<"value of cout<<"Average return 0; } 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 userdefined 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. integer value float value character value a double value

sum = "<<sum<<endl; money = "<<money<<endl; letter = "<<letter<<endl; pi = "<<pi<<endl; = "<<average<<endl;

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 objects behavior. In C++ there are two types of functions:
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

(i) (ii)

functions which compute and return a value; 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

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

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() { int d, m; //initialize array elements double sales[DISTRICTS][MONTHS] = { { 1432.07, 234.50, 654.01 }, { 322.00, 13838.32, 17589.88 }, { 9328.34, 934.00, 4492.30 }, { 12838.29, 2332.63, 32.93 } }; cout << "\n\n"; cout << " Month\n"; cout << " 1 2 3"; for(d=0; d<DISTRICTS; d++) { 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; } //array dimensions

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

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 = 8; birthdays[1].year = 1992;

Or we may initialize the array birthdays as follows:


date birthdays[6] = { {8, 14, 1989}, {3, 8, 1992}, {10, 12, 1994}, {9, 29, 1996}, {6, 20, 1999}, {6, 10, 2002}};

Example:
// 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; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

";

Program input/output example


Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Name: Name: Name: Name: first name: Salim last name: Ahmed phone in 123-4567 format: 555-1212 first name: Samia last name: Ibrahim phone in 123-4567 format: 555-3434 first name: Nasir last name: Abdalla phone in 123-4567 format: 555-1212 first name: Hanan last name: Adam phone in 123-4567 format: 555-1234 Salim Ahmed Phone: 555-1212 Samia Ibrahim Phone: 555-3434 Nasir Abdalla Phone: 555-1212 Hanan Adam 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:


Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

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. Lets 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;
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

if(m<0) break; else 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]; //array of distances int n=0; //count the entries char ans; //user response ('y' or 'n')
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

cout << endl; do { //get distances from user cout << "Enter distance number " << n+1; dist[n++].getdist(); //store distance in array cout << "Enter another (y/n)?: "; cin >> ans; } while( ans != 'n' ); //quit if user types 'n' for(int j=0; j<n; j++) //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.

\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:


Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

< 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); will print 0.

strcpy()
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; }

The advantage is its simplicity: It is easy to understand Easy to implement Does not require the array to be in order The disadvantage is its inefficiency: 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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

10

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]); } }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

11

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 { int i, N = atoi(argv[1]), argv[0] = program name sw = atoi(argv[2]); argv[1] = N argv[2] = sw ( if sw==0 input elements int *a = new int[N]; else choose random numbers.) 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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

12

#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<<x[i]<<" "; cout<<endl<<endl; bubble(x, 0,99); for (i=0; i<100; i++) cout<<x[i]<<" "; return 0; } 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: l 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].
Greater than or equal to v v

Less than or equal to v

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

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

13

#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<<x[i]<<" "; cout<<endl<<endl; quicksort(x, 0,99); for (i=0; i<100; i++) cout<<x[i]<<" "; return 0; } 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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

14

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-FirstOut 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.

Spring-loaded pile: 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. 15

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

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.
L A * S T I * N * F I R * S T * * O U * T * * * * * * * A L L L L L L L L L L L L L L L L L L L L L L L L L L L A S S S S S S S S S S S S S S S S S S S S S S S T T T T T T T T T T T T T T T T T T T T T

I N F F F F F F F F F F F F F F F I I I I I I I I I I I I I

I N

R S S T S O O U O O T O

R T S U T O I F T S L

Exercises: 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 so that the sequence of values returned by the pop operations is (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.

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 598+46**7+*
(
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

16

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 * ( ( ( 9 + 8 ) * ( 4 * 6 ) ) + 7 ) )

5 * * * * * * * * * * * * * * * * * *

9 8 + 4 6 * * 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 9 8 + 4 6 * * 7 + * 5 5 5 5 5 5 5 5 5 5 2075 9 9 17 17 17 17 408 408 415

8 4 4 24 7 6

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

17

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) 59*8746+*213*+*+* . 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(); };

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

18

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<int> s1; s1.push(11); s1.push(22); cout << "1: " cout << "2: " s1.push(33); s1.push(44); s1.push(55); s1.push(66); cout << "3: " cout << "4: " cout << "5: " cout << "6: " return 0; } Output: 1: 2: 3: 4: 5: 6: 22 11 66 55 44 33

<< s1.pop() << endl; << s1.pop() << endl;

//22 //11

<< << << <<

s1.pop() s1.pop() s1.pop() s1.pop()

<< << << <<

endl; endl; endl; endl;

//66 //55 //44 //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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

19

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 I R S * T * I N * * * F I * R S * * * T * O U T * * * * * F F F F I I R R R S T I I I N N N F I R R S S S S T O U T I I I R R S S S T I N N N F F F I R S S T T T T O U T R R S S T T T I N F F I I I R S T O O U O U T U T T

S T I I N N

F I R S T I N F I R

I R R S S

S T O U T

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

20

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 I R S * T * I N * * * F I * R S * * * T * O U T * * * * * F F I F I R F I R S I R S I R S R S R S R S S

F I R S T I N F I R

T T T T T T

I I I I I I I

N N N N N N N N N

F F F F F F

I I I I I I

S T O U T

T T T T T T

R R R R R R

O O O O O

U U U U U

T T T T T

S S S S S S S S S

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; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

21

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() { head = head % N; return q[head++]; } 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<int> q1; q1.put(11); q1.put(22); cout << "1: cout << "2: q1.put(33); q1.put(44); q1.put(55); q1.put(66); cout << "3: cout << "4: cout << "5: cout << "6: return 0; } Output: 1: 2: 3: 4: 5: 6: 11 22 33 44 55 66

" << q1.get() << endl; " << q1.get() << endl;

//11 //22

" " " "

<< << << <<

q1.get() q1.get() q1.get() q1.get()

<< << << <<

endl; endl; endl; endl;

//33 //44 //55 //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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

22

CHAPTER(4) POINTERS 4.1 What Is a Pointer?


A PCs 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 100 1004

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 100

1004

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;

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

23

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 1003 1002 1003 100 1004

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: You should note that the address of the variable rate is itself a number. A pointer is a numeric value, like all variables, must be declared before it can be used. To print the content of rate, we can use the following statement: cout<<rate; or cout<< *prate;

Pointers enable us: 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. Lets 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
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

24

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 = *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 ++*ip; and (*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

//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

= 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,
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

25

#include <iostream.h> int main() { char c = 'Q'; char *ptr_char = &c; cout<<c<< <<*ptr_char<<endl;

c = 'Z'; cout<<c<< <<*ptr_char<<endl; *ptr_char = 'Y'; //assigns Y to the memory address specified by ptr_char cout<<c<< <<*ptr_char; return 0; }

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 = <<count<< return 0; } x = "<<x;

(2)
// Further examples of pointers #include <iostream.h> int main() { char c = 'Q'; char *ptr_char cout<<c<<

= &c;

<<*ptr_char<<endl;

c = '/'; cout<<c<< <<*ptr_char<<endl; *ptr_char = '('; //assigns ( to the memory address specified by ptr_char cout<<c<< return 0; } <<*ptr_char<<endl;

(3) 26

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

// Another program with pointers #include <iostream.h> int main() { int i1, i2, *p1, *p2; i1 p1 i2 p2 = = = = 5; &i1; *p1 / 2 + 10; 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
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

27

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: str.memb (*p_str).memb p_str->memb Example:
// 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.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

28

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: "; cin >> feet; cout << "Enter inches: "; cin >> inches; } void showdist() //display distance { cout << feet << "\'-" << inches << '\"'; } }; //////////////////////////////////////////////////////////////// int main() { Distance dist; //define a named Distance object dist.getdist(); //access object members dist.showdist(); // with dot operator Distance* distptr; distptr = new Distance; distptr->getdist(); distptr->showdist(); cout << endl; return 0; } //pointer to Distance //points to new Distance object //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
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

29

public: void setName() //set the name { cout << "Enter name: "; cin >> name; } void printName() //get the name { cout << "\n Name is: " << name; } };
////////////////////////////////////////////////////////////////

int main() { person* persPtr[100]; int n = 0; char choice;

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

do { //put persons in array persPtr[n] = new person; //make new object persPtr[n]->setName(); //set person's name n++; //count new person cout << "Enter another (y/n)? "; //enter another cin >> choice; //person? } while( choice=='y' ); //quit on 'n' for(int j=0; j<n; j++) //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; } 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.
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

//WRONG

30

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 <<time<< seconds\n"; cout <<"converts to <<hours<< hours <<minutes << minutes and <<seconds<< seconds\n"; return 0; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

31

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]

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

32

These remarks are true regardless of the type or size of the variables in the array a. The meaning of "adding 1 to a pointer," and by extension, all pointer arithmetic, is that pa+1 points to the next object, and pa+i points to the ith object beyond pa. BY definition, the value of a variable or expression of type array is the address of element zero of the array. Thus after the assignment pa = &a[0]; pa and a have identical values. Since the name of an array is a synonym for the location of the initial element, then the assignment pa=&a[0] can also be written as pa = a; A reference to a[i] can be written as *(a+i). In evaluating a[i], C++ converts it to *(a+i) immediately; the two forms are equivalent. Applying the operator & to both parts of this equivalence, it follows that &a[i] = a+i are also identical: a+i is the address of the ith element beyond a. If pa is a pointer, expressions may use it with a subscript; pa[i] is identical to *(pa+i). An array-and-index expression is equivalent to one written as a pointer and offset. There is one difference between array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and a++ are illegal. When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is a variable containing an address. We can use this fact to write strlen, which computes the length of a string. //strlen returns the length of a string s int strlen (char *s) { int n; for (n=0; *s!='\0'; s++) n++; return n; } Since s is a pointer, incrementing it is perfectly legal; s++ has no effect on the character string in the function that called strlen(), but merely increments strlen's private copy of the pointer. A formal parameter in a function definition, char s[]; and char *s; are equivalent.

4.8 Pointer Arithmetic


Only two arithmetic operations can be used on pointers: addition and subtraction. To understand what occurs in pointer arithmetic, let p be a pointer to int with a current value of 2000, and assume that integers are 2 bytes long. After the expression p++;
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

33

the content of p is 2002, not 2001! Each time p is incremented, it points to the next integer. The same is true of decrements. For example, p--; will cause p to have the value 1998, assuming that it previously was 2000. Each time a pointer is incremented it points to the memory location of the next element of its base type. Pointers increase or decrease by the length of the data type they point to. Consider the following declaration (according to Borland C++ specifications): int long float char double then ip++ lp++ fp++ cp++ dp++ will will will will will increase increase increase increase increase ip lp fp cp dp by by by by by 2, 4, 4, 1, 8. *ip; *lp; *fp; *cp; *dp;

You are not limited to increment and decrement, however. You may also add or subtract integers to or from pointers. The statement p = p + 9; makes p point to the ninth element of p type beyond the one it is currently pointing to. Besides addition and subtraction of a pointer and an integer, the only other operation allowed is to subtract a pointer from another pointer. This makes sense when handling arrays. You cannot multiply or divide pointers. You cannot add pointers. You cannot add or subtract type float or double to pointers. Example: //strlen returns the length of a string s int strlen (char *s) { char *p=s; while (*p != '\0') p++; return p - s; } It is possible to compare pointers in a relational expression, as the following example shows: Assume that p and q are pointers, the following statement is perfectly valid. if (p<q) cout<<"p points to lower memory than q\n"; //pointer is incremented //pointers are subtracted

Exercises
1. In the context of the declaration float table[10]; float *pt, *qt; What is the effect of each of the following statements?
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

34

(a)

pt = table; *pt = 0; *(pt+2) = 3.14; pt = table; qt = table + 10; cout<<qt-pt;

(b)

pt = table + 2; qt = pt; *qt = 2.718; pt = table; qt = table + 10; for (; pt<qt; pt++) *pt = 1.23;

(d) (c)

2. Consider the following declaration: int x, y, z, j, t[7] = {7, 2, 9, 6, 4, 0, 3}; int *p=t; What is the value of x, y and z in the following statements? (i) x = *(t+3) + *(t+5); x = (ii) y = *t - 3; y = (iii) z = *p/2 z = (iv) What is the output of the following for-loop? for (j=2; j<5; j++) cout<<*(t+j)<< ;

4.9 Dynamic Memory Allocation (new, delete)


It is desirable to dynamically allocate space for variables at runtime. It is wasteful when dealing with array type structures to allocate so much space when declared, e.g., client clients[100]; This practice may lead to memory contention or programs crashing. A far better way is to allocate space to clients when needed. The C++ programming language allows users to dynamically allocate and de-allocate memory when required. The operators that accomplish this are new, which obtains memory from the operating system, and delete, which returns memory back to the system.

4.9.1 The new Operator


This operator is used to allocate storage to a variable whilst the program is running. It returns a pointer to the starting point of the reserved memory block. date *date_pointer; date_pointer = new date;

4.9.2 The delete Operator


When the variables are no longer required, the space which was allocated to them by new should be returned to the operating system. This is done by, delete date_pointer ; Example
#include <iostream.h> #include <string.h>
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

35

int main() { char *s="Dynamic memory allocation"; int len = strlen(s); //get length of s char *ptr; ptr=new char[len+1]; //set aside memory: string+'\0'

strcpy(ptr,s); //copy s to new memory area ptr cout << "ptr = "<<ptr; delete [] ptr; //the brackets indicate that an array is deleted return 0; }

4.9.3 Dynamic Arrays


We can declare arrays at run-time by making use of the new operator. This approach leads to better memory management, as illustrated by the following example (The STACK ADT, given in chapter 3). The constructor method is modified to accept a parameter, maxN that is used to set the array size at runtime. //Interface part template <class Item> class STACK { private: enum Size {maxN=80};//a tricky way of defining a constant. Item *s; int N; public: STACK(int maxN); int empty() const; void push(Item item); Item pop(); }; //Implementation part #include "stack.h" template <class Item> STACK<Item>::STACK(int maxN) { s = new Item[maxN]; 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]; } Exercise: Modify the QUEUE ADT given in chapter 3 to use a dynamic array.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

36

CHAPTER(5) FILE INPUT/OUTPUT 5.1 Disk File I/O with Streams


A stream is a general name given to a flow of data.
1 1 1 111100001010100010 101001010001010001 111100001010100010 101000101010001010 10001111 0 0 111100001010100010 101001010001010001 111100001010100010 101000101010001010 10001111

1 1 1 0 0

Input stream

Output stream

In C++ a stream is represented by an object of a particular class. So far we have used the cin and cout stream objects. Different streams are used to represent different kinds of data flow. Most programs need to save data to disk files and read it back in. Working with disk files requires another set of classes: ifstream for input, fstream for both input and output, and ofstream for output. These classes are declared in the fstream.h file.

5.2 Formatted File I/O


In formatted I/O, numbers are stored on disk as a series of characters. Thus 6.02, rather than being stored as a 4-byte type float or an 8-byte type double, is stored as the characters 6, ., 0, and 2. This can be inefficient for numbers with many digits, but its appropriate in many situations and easy to implement. Characters and strings are stored more or less normally.

5.2.1 Writing data


#include <fstream.h> #include <iostream.h> int main() { char ch='X'; int j=77; double d=6.02; char str1[]="data"; char str2[]="strucures"; ofstream outfile("fdata.txt"); outfile << ch<<' '<<j<<' '<<d<<' '<<str1<<' '<<str2; cout << "file written"; return 0; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

37

In the above program we declare outfile object and initialize it to the file fdata.txt. This initialization opens the file on the disk. If the file does not exist, it is created. If it does exist, it is truncated and the new data replaces the old. Note: If the file is in a floppy disk, in a folder called myFolder for example, use the file name as: a:\\myFolder\\fdata.txt

5.2.2 Reading Data


We can read the file generated by the above program by using an ifstream object, initialized to the name of the file. The file is automatically opened when the object is created. We can then read from it using the (>>) operator. #include <fstream.h> #include <iostream.h> int main() { char ch; int j; double d; char str1[10]; char str2[10]; ifstream infile("fdata.txt"); infile >> ch >> j >> d >> str1 >> str2; cout << ch<<' '<<j<<' '<<d<<' '<<str1<<' '<<str2; return 0; } Detecting End-Of-File (EOF) When there is no more data in a file to be read, the operating system sends a special signal to the program. This signal is called end-of-file mark (eof). The following example shows the use of eof() as a condition to control the while loop. To read the entire line of text the getline() function is called. #include <fstream.h> #include <iostream.h> int main() { const int MAX=80; char buffer[MAX]; ifstream infile("fdata.txt"); while (!infile.eof()) { infile.getline(buffer, MAX); cout << buffer<<endl; } return 0; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

38

5.2.3 Character I/O


The put() and get() functions can be used to output and input single characters. Here is a program that outputs a string one character at a time. #include <fstream.h> #include <iostream.h> #include <string.h>
int main() { char str[]="Time is a great teacher, but unfortunately\nit kills all its pupils."; ofstream outfile("test.txt"); for (int j=0;j<strlen(str);j++) outfile.put(str[j]); cout <<"File written"; return 0; }

To read the file created by the above program we can use the get() function as shown in the next program. #include <fstream.h> #include <iostream.h> #include <string.h> int main() { char ch; ifstream infile("test.txt"); while (!infile.eof()) { infile.get(ch); cout << ch; } cout <<endl; return 0; } Another example on character I/O is given below: #include <fstream.h> #include <iostream.h> int main() { char in_name[25], out_name[25]; char c;
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

39

cout<<"File to be copied:\n"; cin>>in_name; cout<<"Output filename:\n"; cin>>out_name; //open input file stream ifstream infile(in_name); //open output file stream ofstream outfile(out_name); //Read a character from infile and write it in //outfile, exit loop when eof is reached while( !infile.eof()) { infile.get(c); outfile.put(c); } cout<<"File has been copied.\n"; return 0; }

5.3 Binary I/O


You can store a few numbers to disk using formatted I/O, but if you are storing a large amount of numerical data, it is more efficient to use binary I/O. In binary I/O numbers are stored as they are in the computers RAM, rather than as strings of characters. In binary I/O int is always stored in 2 bytes, whereas the text version might be 12345, requ iring 5 bytes. Similarly a float is always stored in 4 bytes, while its formatted version might be 6.02314e13, requiring 10 bytes. The following example shows how to create a binary file for output and how to open it for input. //binary input/output with integers #include <iostream.h> #include <fstream.h> const int MAX=100; int buff[MAX]; int main() { //fill buffer with data (0, 1,...,99) for (int j=0;j<MAX;j++) buff[j]=j; //create output stream ofstream os("edata.dat",ios::binary); //Write to the output stream os.write((char*)buff,MAX*sizeof(int)); os.close();
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

40

//erase buffer for (j=0;j<MAX;j++) buff[j]=0; //create input stream ifstream is("edata.dat",ios::binary); //read from the input stream is.read((char*) buff, MAX*sizeof(int)); //Display data obtained from file. for (j=0;j<MAX;j++) cout << buff[j]<<' '; return 0; } In the statement os.write((char*)buff, MAX*sizeof(int)); the expression(char*)buff casts the starting address of the buff array into a character pointer. The expression MAX*sizeof(int) is the required number of bytes. Example The following examples stores data in an array called list of type entry. It then saves the whole of the array in a file called list.out. #include <iostream.h> #include <fstream.h> int main() { struct entry { char fname[20]; char lname[20]; char phone[10]; }; entry list[4]; int i; ofstream file("list.out",ios::binary); // 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 999-9999 format: cin>>list[i].phone; } file.write((char*)list,4*sizeof(entry)); file.close(); cout<<"data saved"; return 0; } 41

";

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

Example The following examples reads data stored in the binary file list.out, which was created by the above program, into an array called list. It then displays the array content on the screen. #include <iostream.h> #include <fstream.h> int main() { struct entry { char fname[20]; char lname[20]; char phone[10]; }; entry list[4]; int i; ifstream inf("list.out",ios::binary); inf.read((char*)list,4*sizeof(entry)); //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; } Practice Exercise: 1. Write the include line that is needed to work with disk file I/O? 2. Write the C++ statement that opens the file text.out for input? 3. Write a while loop that reads a character from that file and display it on the computer screen. Make sure that the while loop ends when there are no more data. 4. Write a statement to close the above file. 5. Write a statement to create file data.bin for binary output of 100 double numbers, stored in an array called doub.

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

42

CHAPTER(6) LINKED LISTS 6.1 Introduction


A linked list is a useful method of data storage that can easily be implemented in C++. There are several kinds of linked lists, including single-linked lists, double-linked lists, and binary trees. Each type is suited for certain types of data storage. The one thing that these lists have in common is that the links between data items are defined by information that is contained in the items themselves, in the form of pointers. This chapter explains the most fundamental kind of linked list: the single-linked list (which is referred to as simply a linked list).

6.2 Creating a Linked List


A linked list is comprised of a series of nodes, each node containing a data element, and a pointer to the next node, e.g., head
data pointer data pointer data Null pointer

A pointer that stores the address of the first node is usually called the head of the list. A structure, which contains a data element and a pointer to the next node, is created by, struct list { int list }; This defines a new data structure called list (actually the definition of a node), which contains two members. The first is an integer called value. The second is called next, which is a pointer to another list structure (or node). Suppose that we declare two structures to be of the same type as list, e.g., list n1, n2; The next pointer of structure n1 may be set to point to the n2 structure by n1.next = &n2; // assign address of first element in n2 to the pointer next of the n1 structure which creates a link between the two structures.
// Program to illustrate linked lists #include <iostream.h> int main() { struct list { int value; list *next; }; list n1, n2, n3, *head=&n1; int i; n1.value = 100; n2.value = 200; n3.value = 300; n1.next = &n2; n2.next = &n3; n3.next = 0; //NULL, as shown in the figure below i = n1.next->value; cout<<i<< <<n2.next->value; return 0; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

value; *next;

43

The following diagram illustrates the linked list created by the above program. n3 n1 n2 head
value=100 next value=200 next value=300

In using linked list structures, it is common to assign the value of 0 to the last pointer in the list, to indicate that there are no more nodes in the list, e.g., n3.next = 0;

6.3 Deleting a node from a linked list


To delete a node from the list we simply assign its link field (next field in this example) to the link field of the previous node (or to the head pointer if it is the first node). If the node was dynamically created, we use the delete statement to deallocate the memory reserved for it. The following statement deletes n2: n1.next = n2.next; // deletes n2 The following diagram illustrates deleting n2 from the above linked list. Notice that n2 still occupies memory, but it is not part of the linked list. We cannot delete n2 since it was not created dynamically. The gray arrows show previous links. head n1
value=100 next

n2
value=200 next

n3
value=300

6.4 Adding nodes to a linked list (Inserting a node)


Nodes may be added (inserted) to a linked list at the beginning, at the end, or somewhere in the middle of the list. To add a node, we only need the address of the new node and the node on the left. This left node could be the linked list head (in the case of insertion at the beginning), the last node (address=null), or any node in the middle. Let us add node n1_2 between n1 and n2: n1_2.next = n1.next; n1.next = &n1_2; n1_2
value next

// adds node n1_2

head

n1
value=100 next

n2
value=200 next

n3
value=300

To add a node at the beginning of the list (node n0, say), we use the head of the list: n0.next = head; // adds node n0 head = &n0; n0
value=100 next

head

n1
value=100 next

n2
value=200 next

n3
value=300

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

44

We can also add a node (let us say node n4) to the end of a list, as shown in the following statements: n4.next = 0; //adds node n4 (it is possible to replace 0 with n3.next) n3.next = &n4; n4
value=100

head

n1
value=100 next

n2
value=200 next

n3
value=300 next

6.5 Traversing a linked list


Traversing a linked list means visiting each node of the list. To achieve this we utilize a pointer, ptr, which is initialized to the address of the first node (also stored in head). Each time we visit a node, we access the data element(s) and then set the pointer ptr to point to the following node. This is achieved by ptr = ptr->next; where next is the link field. The following program illustrates this technique. // Program to illustrate traversing a list #include <iostream.h> int main() { struct list { int value; list *next; }; list n1, n2, n3, n4, *head=&n1; list *ptr = head; n1.value = 100; n1.next = &n2; n2.value = 200; n2.next = &n3; n3.value = 300; n3.next = &n4; n4.value = 400; n4.next = 0; while( ptr != 0 ) { cout<<ptr->value<<endl; ptr = ptr->next; } return 0; }

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

45

This program uses a pointer called ptr to cycle through the linked list. Note that we may simply write the while loop condition to read as: while(ptr) , since any non-zero value is true and 0 is false. Practice Exercise: 1. Define a structure called node, which contains an integer element called data, and a pointer to a structure of type node called next_node. 2. Declare three structures called node1, node2, node3, of type node. 3. Write C++ statements which will link the three nodes together, with node1 at the head of the list, node2 second, and node3 at the tail of the list. Assign the value NULL to node3.next to signify the end of the list. 4. Using a pointer list, of type node, which has been initialized to the address of node1, write C++ statements which will cycle through the list and print out the value of each nodes data field. 5. Assuming the state of the list is that as in 3., write C++ statements which will insert a new node node1a between node1 and node2, using the pointer list (which is currently pointing to node1). Assume that a pointer new_node points to node node1a.

6.6 Working with Dynamically Created Nodes


Nodes in a linked list are usually created dynamically using the new operator, then added to the list. Whenever a statement containing the new operator is executed, storage is allocated from a pool of available memory called the heap. When a node is deleted, its allocated memory is returned to the heap using the delete operator. If we neglect this, our program eats up available memory resources, and the system may eventually be out of memory! Let us create our previous linked list using dynamic memory allocation. The list structure is
struct list { int value; list *next; };

First we create the list head and initialize it with 0, indicating that the list is empty. head list *head=0; //this is represented as: Now, let us create a node and add it to the list. We need a pointer to store the address of the current node of the list; call it listptr. list * listptr; listptr = new list;
head value=? next=? listptr

//create a new node and store its address in listptr.

This is illustrated in the following diagram:

To add the newly created node to the list we use the information stored in the head pointer as shown in the following statements. Notice that the new node has no identifier (variable name), but we know its address. Making use of the structure pointer operator (arrow operator; ->) we can access its members. head listptr ->next = head; head = listptr;
listptr value=?

WARNING: It's important to switch the pointers in the correct order. If you reassign Prog. Datapointer Structures(702233)/Fall2003/Dr. the & head first, you will lose the Sayed list! A. Hussein

46

To create another node, again we use the new operator, and store the returned address in a pointer newnode, say. newnode = new list;
head value=? listptr value=?

newnode

The following statements add this node to the linked list and set listptr to point to the end node. newnode->next = listptr->next; listptr->next = newnode; listptr = newnode;
head value=? listptr next value=?

//set listptr to point to the end node

To continue adding nodes we keep repeating the above sequence of statements. Let us assume that the value field is to be read from the standard input. We need to reset listptr and then traverse the list. The value field of the node we access is read from the keyboard. This is illustrated in the following statements: listptr = head; //reset listptr while(listptr) { cout<< Enter value please: ; cin >>listptr->value; listptr = listptr->next; }

Practice exercise: 1. Write a function called delete_node, which accepts a pointer to a list, and a pointer to the node to be deleted from the list, e.g. void delete_node(node *head, node *delnode ); 2. Write a function called insert_node, which accepts a pointer to a list, a pointer to a new node to be inserted, and a pointer to the node after which the insertion takes place, eg void insert_node(node *head, node *newnode, node *prevnode);

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

47

6.7 A Linked list example


// linked list example #include <string.h> #include <iostream.h> // definition of a node struct node { char data[20]; node *next; }; void freenodes(node * ); int insert(node * ); void del_node(node *, node * ); void list(node * ); void menu(node *, node * ); int main() { node *headptr, *nodeptr; headptr = new node; nodeptr = headptr; headptr->next = NULL; menu( headptr, nodeptr ); freenodes( headptr ); return 0; } // free memory allocated for node void freenodes(node *headptr ) { node *temp; while( headptr ) { temp = headptr->next; delete headptr; headptr = temp; } } // insert a new node after nodeptr, return 1 = success int insert(node *nodeptr ) { char buffer[20]; node *newptr; newptr = new node; // allocate a new node if( newptr == NULL ) { return 0; } else { // fill in its data and add to the list newptr->next = nodeptr->next; nodeptr->next = newptr; cout<<"\nEnter data --->"; cin>>buffer; strcpy( newptr->data, buffer ); } return 1; }
Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

48

// delete a node from list void del_node( node *headptr, node *nodeptr ) { node *deletepointer, *previouspointer; char buffer[20]; deletepointer = headptr->next; previouspointer = headptr; // find the entry cout<<"\nEnter name to be deleted --->"; cin>>buffer; while( deletepointer ) { if( strcmp( buffer, deletepointer->data ) == 0 ) { // delete node pointed to by delete pointer previouspointer->next = deletepointer->next; break; } else { // goto next node in list deletepointer = deletepointer->next; previouspointer = previouspointer->next; } } // did we find it? if( deletepointer == NULL ) cout<<"\n\007Error, "<<buffer<<" not found or list empty\n"; else { delete deletepointer; // adjust nodeptr to the last node in list nodeptr = headptr; while( nodeptr->next != NULL ) nodeptr = nodeptr->next; } }

// print out the list void list( node *headptr ) { node *listpointer; listpointer = headptr->next; if( listpointer == NULL ) cout<<"\nThe list is empty.\n"; else { while( listpointer ) { cout<<"Name : "<<listpointer->data<<endl; listpointer = listpointer->next; } } }

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

49

// main menu system void menu( node *headp, node *nodep ) { int menuchoice = 1; while( menuchoice != 4 ) { cout<<"1 insert a node\n"; cout<<"2 delete a node\n"; cout<<"3 list nodes\n"; cout<<"4 quit\n"; cout<<"Enter choice -->"; cin>>menuchoice; switch( menuchoice ) { case 1 : if( insert( nodep ) == 0 ) cout<<"\n\007Insert failed.\n"; nodep=nodep->next; break; case 2 : del_node( headp, nodep ); break; case 3 : list( headp ); break; case 4 : break; default : cout<<"\n\007Invalid option\n"; break; } } }

Prog. & Data Structures(702233)/Fall2003/Dr. Sayed A. Hussein

50

You might also like