Vous êtes sur la page 1sur 107

CHAPTER 2

LISTS, STACKS, QUEUES AND RECURSION

Abstract Data Type (ADT) – Sequences as value definitions - Data types in C


- Pointers in C -Data structures and C - Arrays in C - Array as ADT - One
dimensional array -Implementing one dimensional array - Array as parameters -
Two dimensional array -Structures in C - Implementing structures - Unions in C -
Implementation of unions -Structure parameters - Allocation of storage and scope
of variables.

The List ADT – The Stack ADT – The Queue ADT - Recursion

2.0 INTRODUCTION: BASIC TERMINOLOGY;


ELEMENTARY DATA ORGANIZATION

Data means simply a value or sets of values. A data item refers to a


single unit of value. Data items that are divided into sub-items are called
group items; those that are not called elementary items. For example, an
employee’s name may be divided into three sub-items namely, first name,
middle name and last name - but the Employee security or Identification
number would normally be treated as a single item.
Collections of data are frequently organized into a hierarchy of fields,
records and files. A field is a single elementary unit of information. A record
is the collection of field values of a given entity and a file is the collection of
records.
Records may be classified into two types namely, Fixed Length Record
and Variable Length Record.
In fixed-length records, all the records contain the same data items
with the same amount of space assigned to each data item.
In variable-length records, file records may contain different lengths.
For example, student records usually have variable lengths, since different
students take different numbers of courses. Usually, variable-length records
have a minimum and a maximum length.
The above organization of data into fields, records and files may not be
complex enough to maintain and efficiently process certain collections of data.
For this reason, data are also organized into more complex types of structures.

2.1 Data Type


A data type consists of two parts, namely
1. Set of data
2. The operations that can be performed on the data.

For example, an integer type consists of values – whole numbers


defined in some range and the operations are add, sub, multiply, divide and
any other operations appropriate for the application.
2.1.1 Classification of Data type
There are two types namely,
1. Atomic or Primitive data type
2. Composite or Structured data type

Atomic (Primitive) Data type

Data may be defined as a set and the elements of the set are
called the values of the type. It is a set of atomic data having identical
properties. It is defined by a set of values and a set of operations that act on
the values. There are four basic (atomic or primitive) data types. These are
int, float, char and double.

For example, we can define atomic data types are shown below:
Integer
Values : - ∞, ….., -2, -1, 0,1 ,2, ……….., ∞
Operations : *,+,-,/,%,++,--,…….
Real
Values : -∞,……….0.0,………… ∞
Operations : *,+,-,/,…….
Character
Values : \0,……..,’A’,’B’,……..’a’,’b’,…….~
Operations : +, - ,……….

Composite data type

It can be broken down out sub fields that have meaning. For example,
consider a telephone number. There are actually three different parts to a
telephone number. First one is country code, then area code and third is the
actual telephone number.

The simple data types built from primitives are arrays, pointers ,strings
and records with which we can build new data types called structured or
composite types.

The structured data types can be classified into two types.


1. Linear data structure
Example: Linked lists, Stacks, and Queues
2. Non-linear data structure
Example: Trees and Graphs

2.2 ABSTRACT DATA TYPE (ADT)

It is an abstract collection of data elements and accessing functions that


are meaningful on the data type, where we are not concerning about how the
accessing functions will be implemented is referred as ADT.
For example, the code to read the keyboard is an ADT. It has a data
structure, character and a set of operations that can be used to read that data
structure. The rules allow us to not only read the structure but to convert it
into different data structures such as integers and strings.

With an ADT, users are needed to be concerned only with


a. What a data type can do
b. Statement of operation that can be performed on elements of the
ADT

There are four levels of processing for a problem.

1. Abstract Level
Here, we recognize the relationships among the data elements and
general accessing procedures and functions. But we do not decide any thing
about how the data will actually be stored or how the operations will actually
implemented.

2. Data Structure Level


Here, we can analyze the behavior of the operations and make
appropriate choice according to the problem.

3. Implementation Level
Here, one can decide the ways to represent the data elements in
computer memory and to implement the operations in a programming
language.

4. Application Level
Here, final details of the particular application are decided.

The first level is called conceptual because at these levels we are more
concerned with problem solving than with programming.
The middle two levels can be called algorithmic because they concern
precise methods for representing data and operating with it.
The last two levels are specifically concerned with programming.

The accessing functions act as an interface between application and


implementation level. To specify communication between these two levels ,
one can define the certain conditions on the newly defined data type.

1. Precondition : The input specifications and the allowable assumptions


to the implementation level are called preconditions.
2. Post condition : The output specifications from the implementation
level to the application level are called post conditions.
2.3 Data Types in C:

The primary data types in C language are:

int used to represent integer type data


float used to represent a number with a decimal form
char used to represent a character type data
double used to represent double precision floating point data
type
The following table shows the size and range of various data types.
Data type Size(Bytes) Range
char or signed char 1 -128 to 127
unsigned char 1 0 to 255
int or short int or 2 -32768 to 32767
signed short int or
unsigned short int 2 0 to 65,535
or unsigned int
float 4 3.4e-38 to 3.4e+38
long int or 4 -2,147,483,648 to 2,147,483,647
signed long int
unsigned long int 4 0 to 4,294,967,295
double 8 7e-308 to 1.7e+308
long double 10 3.4e-4932 to 1.1e+4932

2.3.1 USER DEFINED DATA TYPES

It allows the user to define new data types. These data types can be
used to declare variable names later in the program. The user defined data
type has two types, namely
1. typedef data_type 2. enumerated data_type

typedef data__type
The typedef can be used to define new data types and also used to
define structures. Its syntax is :
typedef data_type identifier;
where,
data_type ----> refers to int, float or char
identifier ----> refers to the new names given to the data_type.

Example :
typedef int marks;
typedef float salary;
typedef char string;

Here, marks, salary and string represents int, float and char
respectively. These can be later used to declare variables names.

Example :
marks phy,chem,compu;
salary total;
string name[20];

Here, phy, chem and compu are declared as integer variables, total is
declared as floating point variable and name is declared as character array
variable.

2.3.2 Enumerated data_ type :


It is a set of named integer constants represented by identifiers that
specifies all legal values of same data type.
Its syntax is:
enum name {enumeration list};
where,
enum ---> is a keyword used to represent enumerated datatype
name ---> is an user defined data type name which can be declare
the enumeration list.
Example :
enum month {JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG , SEP, OCT,
NOV, DEC};

It creates a new data type called month in which the enumeration list
are automatically set to 0 to 11. That is, JAN as 0 , FEB as 1, MAR as 2 and so
on.
Suppose, if you want to assign the enumeration list from 1 to 12, the
declaration should be,
enum month {JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP,
OCT, NOV, DEC};
One can also assign different initializes for the enumeration list.

For example,
enum month {JAN=1, FEB, MAR=10, APR, MAY, JUN, JUL=25, AUG,
SEP, OCT, NOV=50, DEC};
Then the values of the enumeration constant will be

JAN = 1 FEB = 2 MAR = 10 APR = 11 MAY = 12 JUN = 13


JUL = 25 AUG = 26 SEP = 27 OCT = 28 NOV = 50 DEC = 51
2.4 ARRAYS IN C

Arrays are used to store a large amount of data. The array may be
defined as a group of related data items that share a common name and type.
The variables which are used to represent the individual data in the memory
are called by subscripted variables.
For example, assumes the group v = 2.0, 1.2, 3.6, ..... If we want to refer
to the second number of the group, we write in C as v[1] where 1 is called the
subscript. The subscript is enclosed in parenthesis.

There are two types of array . These are :

1. One dimensional array : Which is used for the complete list of item.
2. Two dimensional array : Which is used for all the entries in a given table.

The elements in a list (or) table can be either numerical quantity (or)
string quantity.

NOTE : No two arrays can have the same name (or) An array and an
ordinary variable can be assigned the same name.

2.4.1 SUBSCRIPTED VARIABLES :


Individual elements within an array is called subscripted variable. It is
referred by means of an array name followed by the value of the subscript
enclosed in parenthesis. In C, the array subscript must be an integer with
range from 0 to n-1.

Rules for using subscripted variables :

1. Subscript must be an integer and cannot be negative.


2. The subscript must be in square braces after the array name.
3. If there are more than one subscript each should be given in separate
square braces continuously without any commas.

Example :
x[3] ........... an element of the list x contain three characters.
t[8][4]......... an element of the table t contains a total of 32 characters.
In C, the array is declared as follows :

datatype var_name [size];


where,
datatype ----> may be int or real or long or double or char
size ----> maximum number of elements in an array.

Example :
int a[10];
This means that, 10 variables are declared to be integers and they are
referred to as a[0],a[1],a[2],.....a[9]. In C, the array elements are started

2.4.2 ARRAY INITIALIZATION

The syntax of initializing an array during declaration is:


storage_type datatype array_name[size]={list_of_values};
where,
storage_type --> is optional
datatype --> Specifies the type of element(int/float/char)
size --> optional, which indicates the maximum number of
elements in the array.
list_of_values --> values used for initializing the array.

Example:
int marks[5]={50,60,70,80,90};
Here, the values 50,60,70,80 and 90 are assigned to the variable
marks[0],marks[1],marks[2],marks[3] and marks[4] respectively.
char xyz[10]={'a','b','c'};

Here, the number of values in the array is less than the size of the
array. So, the values 'a', 'b' and 'c' are assigned to the variable xyz[0], xyz[1]
and xyz[2] and the remaining variables xyz[4] to xyz[9] will be set to zero
automatically.
float a[]={1.1,2.24,3.33,4.44}

Here, the array size is omitted. Hence, the C compiler automatically


allocates the memory space for all the elements. Therefore, the size of the
array is 4.

Note :
The character array can be initialized by a string constant. Here, the
first element of the array being set to the first character in the string, the
second element to the second character and so an. The array also receives the
terminating string constant '\0'. For example,
char name[]="RUSHMI"; which is equivalent to
char name[]={'R','U','S','H','M','I','\0'};

An array is read or written by loop statements as while, do ... while,


and for loop.

Example :
for (i=0;i<10;i++)
scanf("%d",&a[i]);

The above segment can also be written as using while loop as follows:
int a[10],i=0;
while (i<10)
{
scanf("%d",&a[i]);
i++;
}

Arrays can also be represented as more than one subscript value. This
is called as multi dimensional array. The arrays of more than one dimension
is also called as arrays of arrays. It is mainly used in matrix operations. Its
syntax is :
variable_name [size][size];

Here the first size denotes the size of row and the second size denotes
the size of column.

Example :
int a[5][6];
int b[10][10];

In the above example an array name a has 5 rows and 6 columns and
an array b has 10 rows and 10 columns.

2.5 POINTERS IN C

We know that, the variables are stored in memory. The number of


bytes occupied by each variable varies with respect to the type of the variable.
An integer type variable will occupy two bytes, character occupies one byte
and float occupies four bytes of memory. Each memory location has a unique
address. C provides the features of data manipulation with the addresses of
the variables using pointers.

2.5.1 Pointer - Definition:


A pointer is defined as a variable that contains the address of other
variable.

Advantages of pointer:
1. Execution time is reduced.
2. Reduce the length and complexity of a program.
3. Storage space is reduced.
4. Efficient in handling multidimensional arrays (data tables).
5. It normally returns multiple data items from a function via function
arguments.
6. It is used to pass information back and forth between a function and its
reference point.

2.5.2 Declaring a Pointer Variable :

To declare and refer a pointer type variable, two special unary


operators such as & and * symbols are used.
The ampersand (&) symbol is used to represent an address in which
the value will be stored in the variable name.

Example : &a --- a is a varilable, and &a is the address in the value of a
is stored.

The asterisk (*) called as indirection operator which is used to


declare pointer variable name and it is followed by any pointer variable
name. Its syntax is :
datatype *ptr_var;
where,
datatype---> may be int,long,float or double.
* ---> tells that the variable ptr_var is a pointer variable.
ptr_var ---> is the name of the pointer variable of type datatype.
char *a; ---> here a is a pointer variable pointing to a character
type variable.
int *a,b; ---> a is a pointer variable which can hold the address of an
integer.

Example :

A C program contains the following statements.

float a = 0.01, b =0.03;


float c,*xa,*xb;
xa = &a;
*xa= 2*a;
xb = &b;
c = 3*(*xb-*xa);

Assume that each floating point number occupies 4 bytes of memory.


And the value assigned to A begins at address 1130, the value assigned to B
begins at address 1134 and the value assigned to C begins at 1138. Hence,
i) The value assigned to &a is 1130
ii) The value assigned to &b is 1134
iii) The value assigned to &c is 1138
iv) The value assigned to xa is 1130 (Since xa = &a)
v) The value represented by *xa is 0.02
vi) The value represented by &(*xa) is 1130 (Since &(*xa)=xa)
vii) The value assigned to xb is 1134 (Since xb = &b)
viii) The value represented by *xb is 0.03
ix) The value assigned to c is 0.03

2.5.3 Passing values to a Function :

Arguments to a function are usually passed in two ways. They are:


1. Sending the values of the arguments (Call by Value) and
2. Sending the addresses of the arguments (Call by Reference)

Call by Value :
The process of passing the actual value of variables using in the calling
function is copied into the corresponding formal arguments used in the called
function.

Advantages

1. Expressions can be passed as arguments.


2. Unwanted changes to the variables in the calling function can be avoided.

Example :
Write a program to interchange two values

#include <stdio.h>
main()
{
int x=5;
int y=10;
interchange(x,y);
printf("\n X = %d",x);
printf("\n Y = %d",y);
}
interchange(int a,int b)
{
int k; k=a; a=b; b=k;
printf("\n A = %d",a);
printf("\n B = %d",b);
}

Example :

#include <stdio.h>
void add(int);
void main()
{
int a = 5;
printf("\n The value of A before calling the function is %d",a);
add(a);
printf("\n The value of A after calling the function is %d",a);
}
void add(int x)
{
x = x + 5;
printf("\n The value of A in the called function is %d",x);
}

The above program displays the following output :

The value of A before calling the function is 5


The value of A in the called function is 10
The value of A after calling the function is 5

CALL BY REFERENCE

Here, the addresses of actual arguments in the calling function are


copied into formal arguments of the called function.

Example :
Write a program to interchange two values using pointers.
#include <stdio.h>
main()
{
int x=5;
int y=10;
interchange(&x,&y);
printf("\n X = %d",x);
printf("\n Y = %d",y);
}
interchange(int *a,int *b)
{
int k;
k=*a;
*a=*b;
*b=k;
}

2.6 SCOPE AND LIFETIME OF VARIABLES IN FUNCTIONS

In C, all the variables have datatypes and storage classes. Datatype may
be int, float or char, that represents the type of variable and storage classes
determine the lifetime of the storage, associated with the functions. The
various storage classes in C are:
1.Automatic storage class
2. External storage classes
3. Static storage classes
4. Register storage classes

Automatic storage classes

These variables are declared inside a function. These type of


variables are also called as local variables. They are created when the
function is called and destroyed when the function is existed. Its value
cannot be accessed by any other function. The general form of automatic
variables is :
auto datatype v1,v2,....
where,
auto ----> is the keyword used only if the variables are declared
explicitly as an automatic variable.
datatype --> may be int, float or char
v1,v2,... ---> variable lists

Example:
#include <stdio.h>
main()
{
auto int n; is equivalent to
.....
}

Example :

#include <stdio.h>
main()
{
int n=10;
fun();
printf("\n n = %d in function main",n);
}
fun1()
{
int n=5;
n=n*5; /* compound statement with another n */
{
int n;
n=1;
printf("\nn = %d in inner most block ",n);
}
printf("\n n= %d (parameter of function fun) ",n);
}

The output of the above program is


n = 1 in inner most block
n = 25 (parameter of function fun)
n = 10 in function main.

External storage classes :


These are declared out of the main function. They are also
known as global variables. These variables can be accessed by any function in
the program. The general form of external variables is :
extern datatype v1,v2,....
where,
extern ----> is the keyword used only to define the storage
class as external variables.
datatype ---> may be int, float or char
v1,v2,... ---> variable lists

Example :
#include <stdio.h>
int x;
main()
{
x=5;
run1();
run2();
run3();
printf("%d \t",x);
}
run1()
{
x=x+10;
printf("%d\t",x);
}
run2()
{
x=10;
printf("%d\t",x);
}
run3()
{
x+=100;
printf("%d\t",x);
}

The program displays the following output;


15 10 110 110
In the above program, the keyword extern is not used, since the
variable is declared above all functions. If you want to declare a variable
inside a function as global, we have to use the keyword extern before the
definition of the variable.

static storage classes :


These variables are defined within a function. It has two types internal
static and external static variables.
Internal static variable is same as automatic variables except that the
contents of the variables is retained throughout the program.
The External static variable is declared outside of all functions and is
available to all the functions in that program. The general form of external
variables is :
static datatype v1,v2,....
where,
extern ----> is the keyword used to define the storage class as static
variables.
datatype ---> may be int, float or char
v1,v2,... ---> variable lists

Example :
#include <stdio.h>
main()
{
int i;
for (i=0;i<5;i++)
state();
}
state()/*static variable */ state() /* automatic variable*/
{ {
static int x=5; int x=5;
x=x+1; x=x+1;
printf("\nx = %d",x); printf("\nx = %d",x);
} }

It displays the following output.

x=6 x=7 x=8

In the above program static variable is initialized only once. During the
first call to state, x is incremented to 1. Because x is static, this value persists
and therefore, next call adds another 1 to x giving it a value of 7. The value of
x becomes 8 when the third call is made.
Suppose if x is declared as auto, then the output will be:
x=6 x=6 x=6
This is because, each time state() is called, the auto variable x is set to 6.

Register storage classes :


It is similar to the automatic storage classes. Here, variables defined
inside a function are local to the block in which it is defined. It can be
accessed by the function in which it is defined and its value cannot be
accessed by any other function.
Normally, the register variables are stored in the CPU registers. CPU
registers are faster than memory access and hence in a program, a variable is
used to read or repeat again and again, it is better to declare its storage class
as register. The general form of register variables is :
register datatype v1,v2,....
where,
register ---> is the keyword used to define the storage class of
variables as a register type.
datatype ---> may be int, float or char
v1,v2,... ---> variable lists

Example :
Write a program to display a number and its square from 0 to 5 using
register variables.
#include <stdio.h>
main()
{
register int i,x,y,z;
x=0;
y=0;
while (x<=10)
{
z=f(x,y);
printf("%d\t%d\n",x,z);
++x;
++y;
}
}
f(x,y)
register int x,y;
{
register int temp;
temp = x*y;
return(temp);
}

Note:
The CPU registers in the microcomputers are usually 16 or 32 bit
registers and therefore it is impossible to declare float or double as register
type. When you use float or double as register type, then C compiler will
automatically ignore the register data type and it keeps them in the memory
and compiler will be treated as an automatic variables.

2.7 MEMORY ALLOCATION

It is used to allocate memory space to all the variables in the program.


The memory space can be allocated in two ways. They are:

1. Static Memory Allocation


2. Dynamic Memory Allocation.

Static Memory Allocation


Here, memory space that is allocated for all variables used in the
program remains unchanged before run time, as long as the program stops
execution.

Example :
char class[25];

It reserves 25 memory locations for the array named class as type char.
In static memory allocation, the address of a variable is assigned to a pointer
variable which is declared as the same data type. The compiler allocates the
required memory space for a variable declared. For

Example,
int a,b,*c;
*c = &b;

It initializes the pointer variable c with the address of b.

Dynamic Memory Allocation


It is used to allocate additional memory space or to release the
unwanted memory space at run time. Using this technique, the
programmer can allocate memory whenever one decides and releases it after
using the memory.
In C, the following functions are used to implement the Dynamic
Memory Allocation technique.
1. The malloc() function
2. The calloc() function
3. The realloc() function
4. The free() function

malloc() statement:
It is used to assign sufficient blocks of memory for a given variable in
bytes. The syntax is :
var = malloc(parameter);
where,
var ---> must be a pointer variable and
parameter ---> must be an integer value.

Example :
ptr = malloc(10);

In this example 10 bytes of memory are allocated to the pointer


variable ptr.
calloc() statement:
It is used to allocate a sufficient multiple blocks of memory in bytes.
The syntax is :

var = calloc(a1,a2); where,


a1 -----> total number of blocks of memory required.
a2 -----> total number of bytes in each block of memory location.

Example :
ptr = calloc(3,2)

In this 3 blocks of memory, 2 bytes of memory are allocated in each and


the starting address is stored in ptr.

realloc( ) statement :
It is used to increase or decrease the size of memory previously
allocated in each by using malloc( ) or calloc( ) function. Its syntax is :
var = realloc(old_pointer_variable, new_size)
where,
old_pointer_variable ---> is the variable used in malloc( ) or
calloc( ) function.
new_size ---> size of the new memory area needed.

Example:
ptr = malloc(10)
x = realloc(ptr,20)

The first statement allocates memory space of size 10 bytes and returns
the starting address of the memory through the pointer ptr. The second
statement reallocates (increases) the already allocated space to 20 bytes.

free() statement :

It is used to free (release or deallocate) the block of unused or


already used memory. Its syntax is:
free(pointer_variable);
where,
pointer_variable ---> is a pointer to the memory block which has
been already created by malloc() or calloc() function.

Example:

ptr = malloc(10)
free(ptr);

The first statement allocates memory space of 10 bytes and returns the
starting address of the allocated memory. The second statement frees the
allocated memory.

2.8 STRUCTURES and UNIONS

Definition :
C provides a compound data type called a structure. It is defined as a
collection of one or more variables of different data types grouped together
under a single name.
For example, if we consider the student details, we may refer student
roll number(integer), his/her name(character), his/her marks(integer),
his/her total mark(integer) and an average(float). These data are of different
types organized together under a single name called as a structure.
The general form of the structure is :

struct <tag_name>
{
data type declarations;
};
where
struct ---> is a keyword.
tag_name ---> It is used to identify the structure and it is optional.

Here, the datatype declarations are called as members.


One can declare structure variable using the tag name anywhere in the
program.

For example :
A book has name, author's name, contents, index and year of
publication. It is defined as follows :
struct book
{
char book_name[20];
char auth_name[20];
char contents[10];
char index[5];
int year;
};
From the above declarations , the name of the structure is book. The
members of the structure are book_name, auth_name, contents, index and
year.
One can also declare structure variable using the tag name anywhere in
the program. Its syntax is :

struct <tag_name>
{
data type declarations;
}structure_name;
where,
struct ---> is a keyword.
tag_name ---> It is used to identify the structure and it is optional.
structure_name ---> Name of the structure.

Here, the datatype declarations are called as members.

For example, the following statement

struct book b1,b2,b3;

declares b1,b2 and b3 as variables of type struct book. Therefore, the


complete declaration might be:

struct book
{
char book_name[20];
char auth_name[20];
char contents[10];
char index[5];
int year;
};
struct book b1,b2,b3; OR
One can also written as :
struct book struct
{ {
char book_name[20]; char book_name[20];
char auth_name[20]; char auth_name[20];
char contents[10]; OR char contents[10];
char index[5]; char index[5];
int year; int year;
}b1,b2,b3; }b1,b2,b3;
In the second example, the tag_name book is omitted and it does not
include later use in declaration. The above structure declares b1, b2 and b3 as
structure variables representing three b's.

2.8.1 ACCESSING A STRUCTURE :

For accessing the member of a structure, the syntax is:

structure_name.member_name

Example :
To assign a value to the structure variable,
book.book_name="digital electronics"

This assigns the name "digital electronics" to the structure member


book_name. Similarly, to display the book_name on the screen, type
puts(book.book_name);
displays the name contained in the book_name, member structure variable
book.

2.8.2 STRUCTURE INITIALIZATION :

The syntax is:


main ( )
{
static struct structure_name
{
data_type_declarations;
......
} struct_name = {initialization_values}
}

Example:
main( )
{
static struct student
{
int height;
float weight;
} student = {170,90}
}
In the above example, 170 assigns to student.height and 90 assigns to
student.weight. Note that, the assigning values must be in same order as that
in which the members are specified. That is, there must be one-to-one
correspondence between the members and their initialization values.
NESTING OF STRUCTURES :
It is nothing but a structure containing one or more structures.
The syntax is:
struct struct_name
{
data_type_declarations;
struct another_structure_name
{
data_type_declarations;
}
}
Example:
struct student
{
int rollno;
char name[20];
struct stud_addr
{
int no;
char street[15];
char place[15];
char city[10];
long pincode;
}address;
char deptname[15];
char year[5];
}stud1,stud2;

In the above declaration struct student is the main structure. It gets


additional information about the student address from the struct stud_addr.

The member of a nested structure is accessed as


main_strcture_variable.sub_structure_variable.sub_structure_member

Example :
stud1.address.no

refers to the member of the sub structure, address is the structure


variable of the inner structure and stud1 is the structure variable of the main
structure.

2.8.3 ARRAY OF STRUCTURES


It is defined as a group of different data types stored in consecutive
memory locations with a common variable name.
Its syntax is:
struct structure_name
{
data_type_declarations;
}array_name[size];
Example :
struct student
{
int rollno;
char name[20];
char dept[10];
char year[5];
}stud[5];
The above declaration declares an array of structures, which allocates
memory for five structures of type struct student.

2.8.4 STRUCTURES AND POINTERS


A structure containing a member that is a pointer to the same structure
type is called a self referential structure. One can declare a pointer variable
for the structure by placing an asterisk (*) infront of the structure pointer
variable. Its syntax is:
struct <tag_name>
{
data type declarations;
}*structure_name;

Here, structure_name is a pointer, pointing to the structure specified in


<tag_name>. When pointers are pointing to a structure, then the structure
elements can be referenced by using the symbol -> (Hyphen followed by
greater than symbol). This symbol -> is called as structure pointer symbol.

DIFFERENCE BETWEEN ARRAYS and STRUCTURES


ARRAYS STRUCTURES
It is a single name that represents a It is a single name that represents
collection of data items of same data collection of data items of different
type data types
Individual entries in an array are Individual entries in a structure are
called elements. called members.
It reserves enough memory space for It reserves enough memory space for
its elements. its members.
There is no keyword to represent The keyword struct tells us that we
arrays, but the square braces[ ] are dealing with structures.
preceding the variable name tells us
that we are dealing with arrays.
Initialization of elements done only Initialization of members can be
during structure declarations. definition.
The elements of an array are stored in The members of a structure are not
sequence of memory locations. stored in sequence of memory
locations.
The array elements are accessed by The members of a structure are
its name followed by square brackets accessed by the dot operator.
[] within which the subscript value
is placed.
Its syntax is: Its syntax is:
datatype var_name[size]; struct structure_name
{
datatype_declarations;
} structure_variable;

2.8.5 UNIONS :
It is another compound data type that may hold objects of different
types and sizes. Union provides different kinds of data in a single area of
storage. Its syntax is:
union union_name
{
datatype_declarations;
}union_variables;

Example :
union stud
{
char name[20];
int rollno;
float mark1,mark2,mark3;
};

In the above example, union stud has 5 members. The first member is a
character name having 20 characters. The second member is a integer type
rollno having 2 bytes. All the other members mark1, mark2 and mark3 are of
type float which requires 4 bytes each.

In the union all the 5 members are allocated in a common place of


memory. All the members share the same memory locations.

Rules :

1. Any number of union members can be present in the union. But union
type variables will take the largest memory occupied by its members.
2. At any time only one value can be stored.
3. The data can be retrieved with the type of the largest storage.
4. Unions can also appear in an array and structures.
5. Pointers to union is also possible and the members of the pointer to
union are referred using the symbol ->

DIFFERENCE BETWEEN STRUCTURES and UNIONS


STRUCTURES UNIONS
It occupies its own memory space It uses the same memory space.
The keyword struct tells us that we The keyword union tells us that we
are dealing with structures. are dealing with structures.
All the members of a structure can be Only the first member of an union
initialized can be initialized.
The members of a structure are not The members of a union are stored
stored in sequence of memory in sequence of memory locations.
locations.
Here, each member is stored in a Here, all members are stored in the
separate memory location. Therefore, same memory locations. Hence, less
more memory space is required. memory space is required.
Its syntax is: Its syntax is:
struct structure_name union union_name
{ {
datatype_declarations; datatype_declarations;
}structure_variable; }union_variable;

2.9 Data structure Definition:


It is an aggregation of atomic and composite data types into a set with
defined relationships. In this definition, the structure means a set of rules that
hold the data together.
The data structures can be nested. We can have a data structure that
consists of other data structures.

2.9.1 OPERATION OF DATA STRUCTURES

There are different operations are performed in data structures. The


following four operations play a major role.
1. Traversing: Accessing each record exactly once so that certain items in
the record may be processed.
2. Searching: Finding the location of the record with a given key value, or
finding the locations of all records which satisfy one or more
conditions.
3. Inserting: Adding a new record to the data structure.
4. Deleting: Removing a record from the data structure.

The following two operations, which are used in special situations:

1. Sorting: Arranging the records in some logical order (e.g.,


alphabetically according to NAME key, or in numerical order
according to some NUMBER key, such as register number or account
number)
2. Merging: Combining the records in two different sorted files into a
single sorted file.
2. 10 Arrays
Array is a structured data type. It may be defined as a finite ordered set
of elements all of which are of the same data type. The simplest type of data
structure is a linear (or one-dimensional) array.
By a linear array, we mean a list of a finite number n of similar data
elements referenced by a set of n consecutive numbers, usually 1,2,3, . . . , n. If
we choose the name A for the array, then the elements of A are denoted by
subscript notation A1, A2, A 3,..., An
Or by the parenthesis notation A(1), A(2), A(3), . ., A(N)
Or by the bracket notation A[1] A[2], A[3], . . . , A[N]
Regardless of the notation, the number K in A[K] is called a subscript
and A[K] is called a subscripted variable.
The number n of elements are called length or size of the array. It is
usually defined by
Length = UB - LB +1
where, UB = Upper Bound (Largest Index)
LB = Lower Bound (Smallest Index)
Example:
Let A be an array having 5 elements such that A(1) = 23 A(2) = 45
A(3) = 37 A(4) = 56 A(5) = 97
We denote the array as follows:
A : 23,45,37,56,97
The array A is pictured as follows:
A
23 45 37 56 97

OR

A
23
45
37
56
97
Example
An automobile company uses an array Auto to record the number of
automobiles sold each year from 1945 to 2004. Find the number of
automobiles sold?
Solution
Length = UB - LB + 1
= 2004 - 1945 + 1
= 60
2.10.1 Representation of Linear Array in memory
Let A be an array in the memory of the computer. The memory of the
computer is simply a sequence of addressed locations, which can be defined
as follows:
LOC(A[k]) = Address of the element A[k] of the array A
The elements of A are stored in successive memory cells. We know
that, the computer does not keep track of the address of every element of A,
but needs to keep track only of the address of the first element of A, denoted
by Base(A) is called the base address of A. Using this base address, the
computer calculates the address of any element of A by the following
formula:
LOC(A[K]) = Base (A) + w(K - Lower Bound)
Where, w = Number of words per memory cell of array A.
Example:
Consider the arrays A(5:50), B(-5:10) and C(18)
a. Find the number of elements in each array
b. Suppose Base (A)=300, w = 4 words per memory cell for A. Find
the address of A[15], A[35] and A[55].
Solution:
a. The number of elements is equal to the length.
Length = UB - LB + 1
Therefore, Length (A) = 50 - 5 + 1 = 46
Length (B) = 10 - (-5) + 1 = 16
Length(C) = 18 - 1 + 1 = 18
Note: Length (C) = UB, Since LB = 1
b. Use the formula LOC (A[K]) = Base (A) + w(K - LB)
Hence, LOC(A[15]) = 300 + 4 (15 - 5) = 340
LOC(A[35])= 300 + 4 (35 - 5) = 420
A[55] is not an element of A, since 55 exceeds UB = 50.
2.10.2 Representation of Two-Dimensional Array
A two-dimensional array m x n array A is a collection of mxn elements
such that each element is specified by a pair of integers (such as J,K) called
subscripts, with the property that,
1 <= J <= m and 1 <= K <= n
The element of A with subscripts J and K will be denoted by AJ,K or
A[J, K] .
The two-dimensional arrays are called matrices in mathematics and
tables in business applications. Hence two-dimensional arrays are sometimes
called matrix arrays.
The length of the array = Pair of lengths J x K.
Li = Upper Bound - Lower Bound - 1

Representation of Two Dimensional Arrays in Memory


Let A be a two-dimensional m x n array. The array A will be
represented in memory by a block of mxn sequential memory locations. The
data may store the array A either
1. Column – major order (Column by Column)
2. Row – major order (Row by Row).
A A
A(1,1) A(1,1)
A(2,1) A(1,2)
A(1,2) A(1,3)
A(2,2) A(2,1)
A(1,3) A(2,2)
A(2,3) A(2,3)
Column Major Order Row-major order

For two-dimensional array, the computer also keeps track of Base (A) -
the address of the first element A[1,1] of A and computes the address
LOC(A[J, K]) of A[J, K] using the formula:

Column major Order


LOC(A[J, K]) = Base (A) + w[M(K - 1) + (J - 1)]

Row major Order

LOC (A[J, K] = Base (A) + w[N(J - 1) + (K - 1)]


Where,
w = Number of words per memory location of the array A
Using the above formula, one can find the address LOC(A[J, K]) in time
independent of J and K.

2.10.3 Multi- Dimensional Array

For a multi-dimensional array,


Length Li = Upper Bound - Lower Bound - 1
For a given subscript Ki, the effective index Ei of Li is the number of
indices preceding Ki in the index set and Ei can be calculated from
Ei = Ki - Lower Bound
Then, the address LOC(C[K1,K2,K3,........,KN] of an arbitrary element
of C can be obtained from the following formula:

Column major Order


Base(C) + w[(((...(ENLN-1 + EN-1)LN-2) + .....+ E3)L2 + E2)L1+E1]

Row Major Order


Base(C) + w[(......(E1L2 + E2)L3 + E3)L4 + ........+ EN-1)LN+EN]
Where
w = Number of words per memory location.

Example:
Consider the 25 x 4 matrix array A. Suppose Base (A) = 200 and there
are w = 4 words per memory cell. Find the address of A[12,3] using row-
major order.

Solution
LOC(A[12, 3]) = 200 + 4[4(12 - 1) + (3 - 1)]
= 200 + 4[46] = 384

Example:
Suppose a three dimensional array A is declared as A(2:8, -4:1, 6:10).
Find the length of A. Also find A[5, -1, 8]. Assume Base(A)=200 and w = 4
words per memory cell.

Solution
L1 = 8 - 2 + 1 = 7
L2 = 1 - (-4) + 1 = 6
L3 = 10 - 6 + 1 = 5
The total elements of A = L1.L2.L3 = 7.6.5 = 210 elements.
Suppose, the programming language stores A in memory in row-major
order.
The effective indices of the subscripts are:
E1 = 5 - 2 = 3
E2 = -1 - (-4) = 3
E3 = 8 - 6 = 2
For row-major order, we have:
E1L2 = 3.6 = 18
E1L2 + E2 = 18 + 3 = 21
(E1L2 + E2)L3 = 21.5 = 105
(E1L2 + E3)L3 + E3 = 105 + 2 = 107
Therefore, LOC(A[5, -1, 8]) = 200 + 4(107)
= 200 + 428 = 628.

Example:
Suppose multi-dimensional arrays A and B are declared using A(-2:2,
2:22) and B(1:8, -5:5, -10:5)

a. Find the length of each dimension and the number of elements in A


and B.
b. Consider the element B[3,3,3] in B. Find the effective indices E1,E2,E3
and the address of the element, assuming Base(B) = 300 and there are
w = 4 words per memory location.

Solution:
(a).
The Length = Upper bound - Lower bound + 1
Hence the length Li of the dimensions of A are:
L1 = 2 - (-2) + 1 = 5
L2 = 22 - 2 + 1 = 21
Therefore, A has 5.21 = 105 elements.
Similarly, the length Li of the dimensions of B are:
L1 = 8 - 1 + 1 = 8
L2 = 5 - (-5) + 1 = 11
L3 = 5 - (-10) + 1 = 16
Therefore, B has 8.11.16 = 1408 elements.
(b).
The effective indes Ei is obtained from Ei = Ki - LB
where, Ki = Given Index and LB = Lower Bound
E1 = 3 - 1 = 2
E2 = 3 - (-5) = 8
E3 = 3 - (-10) = 13

The address depends on whether the programming language stores B


in row-major order or column major order. Assuming B is stored in column
major order, we use the following equation:
Base(C) + w[(((...(ENLN-1 + EN-1)LN-2) + .....+ E3)L2 + E2)L1+E1]
E3L2 = 13.11 = 143
E2L2 + E2 = 143 + 8 = 151
(E3L2 + E2)L1 = 151.8 = 1208
(E3L2 + E2)L1 + E1 = 1208 + 2 = 1210
Therefore, LOC(B[3,3,3]) = 300 + 4(1210)
= 300 + 4840
= 5140

2.10.4 Triangular Array


It may be either upper-triangular (all elements below the diagonal are
zero) or lower-triangular (all elements above the diagonal elements are zero.
An array is called strictly triangular (upper or lower) if the elements of
the diagonal are also zero.

x x x x x  x 0 0 0 0
  
0 x x x x  x x 0 0 0
0 0 x x x  x x x 0 0
  
0 0 0 x x  x x x x 0
0 0 0 0 x  x x x x x 

a. Upper Triangular Array b. Lower Triangular Array

n n( n + 1)
The total number of non-zero elements is not more than, ∑i = 2
i =1

2.10.5 Sparse Array


An array is called sparse if it has a relatively high density of zero
elements. For example, the array has 10 non-zero elements out of 50, is a
sparse array.

0 0 0 0 0 1 0 0 0 0
 
0 0 2 6 1 0 0 0 0 0
0 0 0 0 2 1 0 0 0 0
 
0 0 1 0 0 0 0 1 0 0
0 0 0 0 5 0 0 2 0 0 

2.11 LIST

The term “list” refers to a linear collection of data items. Example:


Shopping list; it contains a first element, a second element… and a last
element.
We know that, data processing function involves storing and
processing data organized into lists. One way to store such data is by means
of arrays.
There are some advantages and disadvantages using arrays. These are:

2.11.1 Advantages or list

1. It is easy to create a list and print the elements


2. Finding the kth element consumes less time
3. The data are stored in contiguous memory locations.

2.11.2 Disadvantages

1. It is expensive to insert and delete elements in an array.


2. Also, since an array usually occupies a block of memory space, one
cannot simply double or triple the size of an array when additional space
is required. (For this reason, arrays are called dense lists and are said to
be static data structure.
3. Performance is very slow
4. When the program starts up, it must determine the maximum number of
items.
An another way of storing a list in memory is called a link or pointer,
which contains the address of the next element in the list. Thus successive
elements in the list need not occupy adjacent space in memory. This will make
it easier to insert and delete elements in the list. This type of data structure is
called a linked list.

2.11.3 Linked List (One-way List) or Single Linked List

It is a linear collection of data elements called nodes, where the linear


order is given by means of pointers.
The node is divided into two parts: the first part contains the
information of the element, and the second part, called the link field or
nextpointer field, contains the address of the next node in the list.
The node of the linked list is represented as follows:

The Next address field of the last node contains a special value, known
as null value. This is not a valid address. This is only tells us that we have
reached the end of the list.
Figure shows a schematic diagram of a linked list with 3 nodes.
Figure : Linked List wity 5 nodes

Each node is pictured with two parts.

The left part represents the information part of the node, which may
contain an entire record of data items(e.g., NAME, ADDRESS, . . .).

The right part represents the nextpointer field of the node, and there is
an arrow drawn from it to the next node in the list.

The nodes in the linked list are called self-referential structures. In a


self-referential structure each instance of the structure contains a pointer to
another instance of the same structural type.

2.11.4 Operations on Linked List

There are five basic types of operations associated with linked list.
1. To determine if the list is empty or not. It returns true if the list
contains no elements.
2. Create a list.
3. Add new elements anywhere in the list
4. To check if the particular element is present in the list or not.
5. To delete a particular element from the list placed any where in the list.
6. Find the size of the list
7. To print all the elements of the list.

Before describing the algorithms w will introduce some notations to be


used in algorithms.

If p is a pointer to a node, then


node(p) = Node pointed to by p
info(p) = Data part of that node
next(p) = Address part of that node
info(next(p)) = Data part of the next node which
follows node (p) in the list if next(p) is not null.

We can initialize the list by making the external pointer null.

list = null
Also, we can check whether the list is empty by checking whether the
external pointer is null.

if list = null
then
return (true)
else
return(false)

This routine will return true if the list is empty, otherwise it will return
false.

2.11.5 Traversing a Linked list

To traverse or to print the elements of a linked list, we need to use a


temporary pointer p, known as a traversal pointer.

1. p = list
2. Repeat steps 3 and 4 while list <> null
3. print info(p)
4. p=next(p) [Updates pointer]
[End of Step 2 loop]
5. Return

If we do not use the traversal pointer and instead change the value of
the external pointer list, we will not be able to access that node anymore.

The following procedure finds the number of elements in the linked


list.

1. Set N : = 0 . [Initializes counter.]


2. Set p := LIST. [Initializes pointer.]
3. Repeat Steps 4 and 5 whik P <> NULL.
4. Set N : = N + 1. [Increases NUM by 1.]
5. Set p : = next(p). [Updates pointer.]
[End of Step 3 loop.]
6. Return.
2.11.6 INSERTION INTO A LINKED LIST
Let List be a linked list. Now let us consider the linked list containing
two elements as shown in figure:

To add a new node containing data value x in the beginning of the list
we need to follow the step.

a. To get a new node which is not in use getnode(p)


The operation getnode(p) obtains an empty node and sets
the contents of a variable named p to the address of that node.
b. To set the data field of the new node to x. info(p) = x
c. To set the next field of the new node to point to list.
next(p) = list
d. To set pointer list point to the new node list = p.

To understand the above steps, pictorially, we can represent as follows:

The above algorithm works even if the list is initially empty. Using
these steps iteratively, we can create a linked list.

2.11.7 INSERTION INTO A ORDERED LINKED LIST

To insert a node into the list, which consists of three steps.


1. Allocate memory for the new node and insert data
2. Point the new node to its successor
3. Point the new node’s predecessor to it.
Let us consider the steps to insert data element 10 in the following
ordered list:
(2,7,12,15)

a. Get a new node which is currently unused


b. To set the data field of this node to 10 or x in general
c. To set the next field of the new node to the node containing 12.
d. To set the next field of the node containing 7 to the new node.

Figure shows these illustrations.

Figure (a)

In order to carry out step c and d, we must find the correct place for
inserting x.

p = list
while x >= info(p)
p = next(p)

The above steps are used to find the correct place for insertion, p will
point the node that should follow the new node. This enables us to carry step
(c). But, there is no way to access the node which should precede the new
node to carry out step (d).

To overcome this problem, we must check the data field of the next
node, rather than the node itself.

p=list
while x>= info(next(p))
p=next(p)

Therefore, we can insert the node by using the following algorithm:

getnode(new)
info(new)=x
p=list
while x>=info(next(p))
p=next(p)
next(new)=next(p)
next(p)=new

Working through these steps, we can insert data 10 correctly.

Suppose we want to insert 1 in this list. Working thorugh the


steps, we notice that we skip the first node in the comparison.
Also, if x < info(list) (data field of the first node) we must treat it as a
special case as we will need to change the external pointer. So, we can modify
our algorithm to include the following steps.

p = list
if x < info(p)
then
next(new) = list /* To insert new node as first
node */
list = new
else
while ....

The insertion of 1 using above steps in the list of above figure(a) is


shown in figure (b).

Figure: b

Now suppose, we have to insert 20 to the above figure a, which is


greater than all the values in the list. Now, the term info(next(p)) will give us
an error, as we try to move p pass null. To avoid this, we can use the
following loop control.

while (next(p) <> null) and (x >= info(next(p)))

Please note that, the compiler stops checking the second condition if
the first one fails.
Another special condition is to insert when the list is empty. In this
case we must check if the list is empty, we can make the list pointer point to
the new node
if list = null
list = new
Hence putting all the above conditions together our algorithm to insert
anywhere in the ordered list becomes as follows:

getnode(new)
info(new) = x
next (new) = null
if list = null
then
list = new
else
{
p = list
if x < info(p)
then
next(new) = list
list = new
else
{
while (next(p)<>null) and (x >= info (next(p)))
p = next(p)
next(new) = next(p)
next(p) = new
}
}

2.11.8 Deleting from a linked list

Let LIST be a linked list with a node N between nodes A and B.


Suppose Node N is to be deleted from the linked list.

For example, if the node to be deleted is the first one in the list, we are
simply required to make the external pointer list equal to the next field of the
first node in the list.
Consider the following figure:
The dashed arrows in the figure shows that list now point to the next field
of the first node in the list.

The first node (data field containing 2) is still connected to the node
containing 7 but there is no way to access that node as it is not pointed to by
any pointer. Therefore, this is a logical deletion.

Also, the node that was first on the list is currently useless because it is
no longer on the list. Therefore, there should be a mechanism for making this
node available for reuse. To do this, we need to access the node. This means,
before we change pointer list to point to next node, we must assign a new
pointer to this node which we can use t access it later. Also we need to store
the value of the data item of this node. Therefore, to delete the first node, the
algorithm is as follows:

p = list
x = info(p)
list = next (list)

Let us assume that, there exists an operation FREENODE that takes a


temporary pointer to an unrequired node and puts that node back into the
pool of available nodes that can be reused. Using this operation, we can free
the node pointed to by pointer p in our algorithm and include the following
statement
freenode(p)
Once this operation is performed, it becomes illegal to reference
node(p).
To understand this algorithm to remove the first node, pictorially as
shown in figure.
Now let us remove a node which may be placed anywhere in the list.
Let the node to be deleted has data value x. For simplicity, we assume that x
appears only once in the list.
The deletion operation can be broken down into three simple sub
operations.

a. Find the node containing the value x


b. Modify the pointers to delete the node
c. Free the node

To find the node containing value x we have to traverse the list till we
find the node or till we reach the end of the list (the node with value x is not
present in the list).

p = list
while (p <> null) and (info (p) <> x)
p = next(p)

When we reach the node containing x, that is info (p) =x, p is pointing to
the node, we wish to delete.
But, to delete a node, we must know the address of the previous node.
Deleting the node means, changing the next pointer of the previous node to
point to the node following the node, which is to be deleted.
To solve this problem, we have to check the contents of the next node.
Another way is to keep two pointers PREV and CURRENT to traverse the
list. So that, when current points to the node to deleted, prev points to the
preceding node.

We can do the following steps:

prev = null
current = list
while (current <> null) and (info (current) <> x)
{
prev = current
current = next(current)
}

At this stage, current points to the node which is to be deleted and


prev points to the preceding node. To delete the current node, we simply say

next(prev) = next(current)
freenode(current)

Please note that, if the node to be deleted is the last one in the list,
which mean next of current points to null, the above two statement will work.
As in that case next(prev) will point to null, as we desire.

Suppose we want to delete the first node, we will not enter into the
search loop and therefore prev will remain null. We can add the following
steps to our algorithm.

if prev = null
then
list = next(list)
freenode (urrent)

If x doesn’t exit in the list when we dropout of the loop, current will be
null. We can check that and print a message.
Finally, we can put together these steps to make a complete algorithm
to delete a node which is as follows:

current = list
prev=null
while (current <> null) and (info(current)<>x) do
{
prev=current
current=next(current)
}
if current not null
then
if prev=null
then
list=next(list)
else
next(prev)=next(current)
freenode(current)
else
print(“Node with a given value doesn’t exist in the list”)

In order to understand this algorithm, let us work through some examples:

Let us consider a linked list as shown in figure (a).

Figure (a)
Now, let us try to delete 2.
Since, it is the first node, prev will be null. The pointer adjustment to
delete 2 is shown in figure b.

Figure (b)

Similarly, we can delete node 7.

Now, let us try to delete 10. Since this node is not present in the list, it
will print a message Node with the given value doesn’t exist in the list” and
a pointer positions will be shown in figure (c).

Figure (c)

Next, we delete node 12. It is in the middle of the list. Neither current
nor prev is null. So, pointer adjustment is as shown in figure (d).

Figure (d)

Lastly, we can delete node 15 which is the last node of the list. The next
(prev) will point to next (current) which is null as shown in figure (e).
Figure (e)

2.12 Garbage Collection

Suppose some memory space becomes reusable because a node is


deleted from a list or an entire list is deleted from a program. Clearly, we
want the space to be available for future use.
The operating system of a computer may periodically collect all the
deleted space onto the free-storage list. Any technique which does this
collection is called garbage collection. It is usually takes place in two steps:

1. The computer runs through all lists, tagging those cells which are
currently in use.
2. Then the computer runs through the memory, collecting all untagged
space onto the free-storage list.

The garbage collection may takes place when there is only some
minimum amount of space or no space at all left in the free storage list or
when the CPU is idle and has time to do the collection. Please note that, the
garbage collection is invisible to the programmer.

2.12.1 OVERFLOW AND UNDERFLOW

Sometimes new data are to be entered into a data structure but there is
no available space, then such situation is called Overflow. Similarly, when
one wants to delete data from a data structure, that is empty, then it is called
Underflow.

AVAIL List

The maintenance of linked lists in memory assumes the possibility of


inserting new nodes into the lists and hence requires some mechanisms which
provides unused memory space for the new nodes.
We know that, the getnode operation uses our delete-first-node
algorithm and removes the first node from this list and makes it available for
use. The freenode operation uses our insert-first-algorithm and adds the
unused nodes to the front of the list, making it available for reuse by the next
getnode. This list of available nodes is called AVAIL List and it is pointed by
an external pointer called AVAIL, as shown in figure a.

Figure (a)

We can write the algorithm for getnode(p) as follows:

if (avail=null)
then
print “Overflow”
else
{
p=avail
avail=next(avail)
}

The available list will only be empty when all nodes are currently in
use and it is not possible to allocate any more. Figure(b) shows how
getnode(p) works pictorially.

Figure (b)

Similarly, we can implement freenode(p) as


next(p)=avail
avail=p

Figure (c) shows how freenode(p) works pictorially in two steps.


2.13 Linked List Algorithms

2.13.1 Traversing Algorithm

Let LIST be a linked list in memory. This algorithm traverses applying


an operation PROCESS to each element of LIST. The variable P points to the
node currently being processed.
1. [Initialize pointer P]
set P:= START
2. Repeat steps 3 and 4 while P<> Null
3. Apply PROCESS to INFO[P]
4. Set P=Link[P] [P now points to the next node]
[End of Step 2 loop]
5. Exit

2.13.2 Printing Information about Each node of a linked list

1. Set P=START
2. Repeat steps 3 and 4 while P<>Null
3. Write:INFO[P]
4. Set P:=LINK[P] [Update pointer]
[End of Step 2 loop]
5. Return

2.13.3 Find the number of elements in a linked list

1. [Initialize counter]
Set Num=0
2. [Initialize pointer]
Set P=START
3. Repeat steps 3 and 4 while P<> Null
4. Set Num=Num+1 [Increment Num by 1]
5. Set P=LINK[P] [Update pointer]
[End of Step 3 loop]
6. Return

2.13.4 Searching

Let LIST is a linked list in memory. This algorithm finds the location
LOC of the node where ITEM first appears in LIST or sets LOC=NULL.

1. Set P=START
2. Repeat steps 3 while P<>NULL
3. If ITEM=INFO[P], then
Set LOC=P and Exit
Else:
Set P=LINK[P] [Pointer now points to the next node]
[End of IF structure]
[End of Step 2 loop]
4. [Search is unsuccessful?]
Set LOC=NULL
5. Exit

2.13.5 Inserting item as the first node in the list

1. [Overflow?]
If Avail=Null, then:
Write: Overflow and Exit
2. [Remove first node from Avail list]
Set New=Avail and Avail=Link[Avail]
3. [Copies new data into new node]
Set INFO[New]=ITEM
4. [New node now points to original first node]
Set LINK[New]=START
5. [Changes START so it points to the new node]
Set START=NEW
6. Exit

2.13.6 Inserting after a given node

This algorithm inserts ITEM so that ITEM follows the node with
location LOC or inserts ITEM as the first node when LOC=NULL.
1. [Overflow?]
If Avail= NULL, then:
Write: Overflow and Exit
2. [Remove First node from Avail list]
Set New=Avail and Avail=LINK[Avail]
3. [Copies new data into new node]
Set INFO[New]=ITEM
4. [Inset as first node]
If LOC=Null, then:
Set LINK[New]=START and START=New
Else:
[Insert after node with location LOC]
Set LINK[New]=LINK[Loc] and LINK[LOC]=NEW
[End of IF structure]
5. Exit

2.13.7 Deleting the node following a given node

This algorithm deletes the node N with location LOC. LOCP is the
location of the node which precedes N or, when N is the first node, LOCP =
NULL.

1. If LOCP = NULL, then:


Set START:= LINK[START]. [Deletes first node.]
Else:
Set LINK[LOCP] : = LINK[LOC]. [Deletes node N.]
[End of If structure.]
2. [Return deleted node to the AVAIL list.]
Set LINK [LOC}: = AVAIL and AVAIL = LOC.
3. Exit.

2.13.8 Implementation of Linked List

There are two ways of implementing linked lists in C namely arrays


and dynamic memory allocation.

2.13.8.1 Implementation of Linked List using Arrays

Assume, we allocate 100 nodes to the program and place them all on the
available list as all nodes are initially unused. We link these nodes initially in
their natural order so that each node point to the next node in the array.
Node [0] is the first node and node [99] is the last one.
Let the link list be defined as follows:

# define maxnodes 100


struct nodetype
{
int info, next;

};
struct nodetype node[maxnodes];

Here the index of array represents a pointer to a node


which ranges between 0 to maxnodes -1. The two fields of the
node can be accessed as node[p].info and node [p].next
respectively. Null is represented by –l.
Using this declaration we can organize our avail list as;

initialize()
{
int k;
avail = 0;
for(k=0;k<maxnodes - 1 ; k++)
node[k].next=k+l;
node[maxnodes-l].next = - 1 ;
}
Now all the 100 nodes are on the avail list. Here we have
assumed avail to be global variable. Whenever an individual list for
a particular model requires a node, a getnode function may be
called. Let us now write getnode, which removes a node from the
available list and returns a pointer to it. If there are no more nodes
available, avail contains value null that is -1 and in that case, it
prints an error message.

getnode ()
{
int p;
if (avail == -1)
{
printf ("no more nodes available \n");
exit(l);
}
p = avail;
avail = node [avail] .next ;
return (p);
}

Similarly, we can write another function free node, which


accepts a pointer to a node and adds that node at the front of the
avail list.

freenode(int p)
{
node[p] .next =avail;
avail = p;
return;
}
Using the avail list, we can create four lists, one for each model. We
initialize the external pointer of all the four lists to null.
We can write a routine in C, which accepts address p of a
node and item x which is to be inserted as parameter. p is the
starting address of the list in which item x is to be inserted.

inserted (p,x)
int p, x;
{
int r,q;
/* put x into a new node */
q = getnode();
node[q] .info = x;
node[q] .next= -1;
/* insert the new node into the list */
if (p == -1)
{
p = q;
return;
}
/ * insert into empty list */
if (x < node[p] .info)
{
node[q].next = p;.
p = q;
return;
}
/* insert before first node*/
r = p;
while (node[r] .next <> -1) && (x >= node[r] .info)
{
r = node[r] .next;
}
/ * find insertion place */
node[q] .next = node[r] .next;
node[r] .next = q;
return;
/* connect the pointers */
}

The routine delete (p, x), called by the statement delete (list, val) deletes
the node containing x.)

delete (p, x)
int p, x;
{
int q, current, trail;
current = p;
trail = -1;
while (current != -1) && (node [current] .info !=x)
{
trail = current;
current = node [current] .next;
}
/* search the node with value x */
if (current == -1)
{
printf ("void deletion \n");
return;
}
/* the node with value x is not present */
if (trail == -1)
{
q = p;
p = node[p] .next;
freenode (q) ;
return;
}
/* delete the first node */
q = current;
node [trail] .next = node [current] .next;
freenode (q) ;
return;
}

Advantages of Array implementation:


1. Less time consuming.
2. We have to keep an extra node at the front of the list. This
node does not represent an item in the list, but may contain
some global information about an entire list. Example:
Number of nodes in the list, such a node is called a header
node.
In the above program, a fixed number of nodes represented by an
array is established at the start of execution. Therefore the main
disadvantages are:

1. We have to predict the number of nodes when a program is


written which is not always possible.
2. We have to allocate the declared number of nodes
throughout the execution of the program whereas the
program may not be actually using those many numbers of
nodes.

To overcome the above drawback, we have to use dynamic memory


allocation. That is, storage must be allocated to a program only when it is
required, and it must be released, when it is no longer in use. Now, we will
implement the list using dynamic memory allocation.

2.13.8.2 Implementation of Link List Using Dynamic Allocation


We can implement a link list using the concept of pointers in C. A node
of a
link list can be defined as follows.

struct node
{
int info;
struct node *next;
};
typedef struct node *nodeptr;

Here, a node consists of an information field and a pointer to the next


node in the list rather than an integer as in the case of array implementation.
Here, there is no need to check for overflow as it will be
detected by the malloc function. We can, therefore, write the
function getnode as follows.

nodeptr getnode()
{
nodeptr p;
p = (nodeptr) malloc(size of(struct node));
return (p);
}

If we call this function as


p = getnode();

It should place the address of an available node into p.


Similarly we can write the freenode function as
freenode (p)
nodeptr p;
{
free (p);
}

If we call this function as freenode (p);

it should return the node whose address is at p to the available storage.


We can now write a C routine insert(p, x) using the dynamic
implementation of a linked list.

insert (p,x)
nodeptr p;
int x;
{
nodeptr r, q;
q = getnode();
q → info = x;
q → next = null;
/* insert the new node into the list */
if (p == null)
{
p = q;
return;
}
if (x < p →info)
{
q → next = p;
p = q;
return;
}
r = p;
/* find the insertion place */
while (r → next != null) && (x >= r → info)
r = r → next;
q → next = r → next;
r → next = q;
return;
/* connect the pointers */
}

Now we present the delete (p, x) routine using dynamic


implementation of a linked list.
delete (p, x)
nodeptr p;
int *x;
{
nodeptr q, current, back;
current = p;
trail = null;
while (current != null) && (current → info != x)
{
trail = current;
current = current → next;
}
/* end of searching the node with values */
if (current == null)
{
printf ("void deletion \n");
return;
}
/* the node with value x is not present */
if (trail == null)
{
q = p;
p = p →next;
*x = p → info;
freenode (q) ;
return;
}
/* delete the first, node */
q = current;
trail → next = current → next;
*x = q → info;
freenode (q);
return;
}

The major advantage of dynamic implementation are:

1. A set of nodes is not required to be reserved in advance. Even if a


program uses different types of lists (say, one list of integers and one
list of characters).
2. No storage is allocated for variable until needed. Any storage not used
for one type of node may be used for another.
3. No overflow occurs as long as sufficient storage is available unlike the
array implementation where two arrays of fixed size would be
allocated immediately and if one group of lists overflow its array, the
program cannot continue.
4. No address calculation is required to reference *p as it is given directly
by the contents of p unlike reference to node[p] whose address must be
computed by adding the contents of p to the base address of the array.

Let us now write a complete C program to add, delete, search, display


and count the number of nodes using linked list. .

#include <stdio.h>
#include <conio.h>
#include <alloc.h>
struct node
{
int data;
struct node *link;
};
void append (struct node **);
void delete (struct node **);
void display (struct node *);
int count (struct node *);
void search (struct node *);
main ( )
{
int ch;
struct node *list=NULL;
while (1)
{
clrscr();
printf(“\n1. ADD A NODE\n”);
printf(“2. DELETE A NODE\n”);
printf(“3. SEARCH \n”);
printf(“4. VIEW THE LIST \n”);
printf(“5. COUNT THE LIST \n”);
printf(“6. QUIT\n”);
printf(“\nENTER YOUR CHOICE:”);
scanf (“%d”, &ch) ;
switch (ch)
{
case 1: append (&list);
break;
case 2: delete (&list);
break;
case 3: search (&list);
break;
case 4: display (&list);
break;
case 5: printf(“ Number of nodes in the list = %d”,i);
getch;
break;
case 6: exit();
default: printf(“Invalid choice ….. Try again……\n”);
}
}
}

void append (struct node **q)


{
struct node *temp;
int x:
printf(“nter the values you want to add & terminate by –l \n”);
while (x!= -l)
{
scanf (“%d” , &x) ;
if (x != -1)
{
if (*q==NULL)
{
*q=(struct node*) malloc (size of (struct node));
(*q) →data=x;
(*q) →link=NULL;
}
else
{
temp=*q;
while (temp→link !=null)
temp=temp→link;
temp→link=(struct node *) malloc (size of (struct
node));
temp=temp→link;
temp→data=x;
temp→link=NULL;
}
}
}
}

void display (struct node *q)


{
while (q!=NULL)
{
printf (“%d\n”, q→data);
q=q→link;
}
printf(“ Press any key to continue”);
getch();
}

int count (struct node *q)


{
int i = 0;
while (q! =NULL)
{
i++;
q=q→link;
}
return i;
}

void delete (struct node **q)


{
struct node *temp, *temp1, *temp2;
int x;
printf (“enter the value of node you want to delete:”) ;
scanf (“%d”, &x);
if (q==NULL)
{
printf (“no node to delete\n”);
getch ( ) ;
}
else
{
temp=*q;
if (x==(*q)→data)
*q=(*q) →link;
else
{
while ((temp!=NULL)&&((temp→link) →data!=x) )
temp=temp→link;
if (temp==NULL)
{
printf (“node not found\n”);
getch ( );
}
else
{
temp1=temp→link;
temp2=temp1→link;
free (temp1) ;
temp→link=temp2;

}
}
}
}

void search(struct node *q)


{
int x;
if (q==NULL)
{
printf (“no node to search\n”);
getch ( );
}
else
{
printf(“enter the value of node you want to search \n”);
scanf (“%d”, &x);
while ((q!=NULL) && (q→data!=x))
q=q→link;
if (q==NULL)
{
printf (“node not found\n”);
getch();
}
else
{
printf (“node found\n”);
getch ( );
}
}
}

2.14 HEADER LINKED LISTS

A header-linked list is a linked list, which always contains a


special node, called the header node, at the beginning of the fist. There are
two types of header lists:

1. A grounded header list is a header list where the last node contains the
null pointer.
Figure (a). Grounded Header List

2. A circular header list is a header list where the last node points back to
the header node.

Figure (b). Circular Header List

Observe that the list pointer START always points to the header node.
Accordingly, LINK[START] = NULL indicates that a grounded header list is
empty, and LINK[START] = START indicates that a circular header list is
empty.

2.14.1 Properties of Circular Header List

1. The null pointer is not used, and hence all pointers contain valid
addresses.
2. Every (ordinary) node has a predecessor, so the first node may not
require a special case.

2.14.2 Algorithm (Traversing a Circular Header List)

Let LIST be a circular header list in memory. This algorithm traverses


LIST, applying an operation PROCESS to each node of LIST.

1. Set PTR:= LINK[START]. [Initializes the pointer PTR.]


2. Repeat Steps 3 and 4 while PTR <> START:
3. Apply PROCESS to INFO[PTR].
4. Set PTR: = LINK[PTR]. [PTR now points to the next node.]
[End of Step 2 loop.]
5. Exit.
2.14.3 Algorithm find the Location of ITEM in a Circular linked list

Suppose LIST is a linked list in memory, and suppose a specific ITEM


of information is given. This algorithm finds the location LOC of the first
node in LIST, which contains ITEM when LIST is an ordinary linked list. The
following is such an algorithm when LIST is a circular header list.

1. Set PTR:= LINK[START].


2. Repeat while INFO[PTR] <> ITEM and PTR <> START:
Set PTR:= LINK[PTR]. [PTR now points to the next node.]
[End of loop.]
3. If INFO[PTR] = ITEM, then:
Set LOC: = PTR.
Else:
Set LOC: = NULL.
[End of If structure.]
4. Exit.

2.14.4 Circular Linked List

A linked list whose last node points back to the first node instead of
containing the null pointer, called a circular list.

2.14.5 Circular Linked List with Header and Trailer Nodes


A linked list which contains both a special header node at the
beginning of the list and a special trailer node at the end of the list.
Figure: Linked List with header and trailer nodes

2.15 Double Linked List or Two-way List

A double linked list or a two-way list, which can be traversed in two


directions:
1. In the usual forward direction from the beginning of the list to the
end.
2. In the backward direction from the end of the list to the beginning.

The double linked list or a two-way list, is a linear collection of data


elements, called nodes, where each node N is divided into three parts:

1. An information field INFO which contains the data of node


2. A pointer field NEXT which contains the location of the next node in
the list
3. A pointer field BACK which contains the location of the previous node
in the list

The list also requires two list pointer variables:

1. FIRST, which points to the first node in the list


2. LAST, which points to the last node in the list.

Figure shows the schematic diagram of such a list.


Figure: Two-way list or Double linked list

Observe that the null pointer appears in the NEXT field of the last node
in the list and also in the BACK field of the first node in the list.

Advantages:

1. It is more efficient.

Disadvantages of Two-way list over one way list

1. The location of the preceding node is needed. The two-way list


contains this information, whereas with a one-way list we must
traverse the list.
2. A two-way list is not much more useful than a one-way list except in
special circumstances.

2.15.1 Two-Way Header Lists

The advantages of a two-way list and a circular header list may be


combined into a two-way circular header list. This is shown in figure. The
list is circular because the two end nodes point back to the header node.
Observe that such a two-way list requires only one list pointer variable
START, which points to the header node. This is because the two pointers in
the header node point to the two ends of the list.

Figure: Two-way circular header list.

2.16 Application of Linked Lists


Linked lists are used in many applications. One application of linked
lists is to maintain directory of names. This type of problem occurs in
compiler construction to store the list of identifiers appearing in a program for
maintaining symbol table.
Another application is polynomial manipulation. For maintaining the
polynomials in memory, header linked lists are frequently used. The
hardware of most computers allows integers of only a maximum specific
length. But using linear lists we can perform arithmetic operations on long
integers.
Linked lists are also used to implement sparse matrices. These can also
be used to implement a line editor, where we can keep a linked list of line
nodes, each containing a line number, a line of text and a pointer to the next
line node. It would be an appropriate application to implement with
dynamically created nodes since you can not predict the number of lines
needed.

2.17 Polynomials Manipulation

Here, we can represent any number of different polynomials as long as


their combined size doesn’t exceed our block of memory. Here, we will
represent each term by a node. A node will be of fixed size having three
fields; one representing the coefficient, second representing exponent and a
third is a pointer to the next term.

A node will look like this:

Coefficient Exponent Link

For example, consider the polynomial in one variable:

P(x)=2x8 – 3 x4 – 2x2 + 4x + 5

The polynomial would look shown below:


Observe that the list pointer variable POLY points to the header node,
whose exponent field is assigned a negative number, in this case –1.

2.18 PROBLEMS
1. Let LIST be a linked list in memory. Write a procedure which

a. Finds the number NUM of times a given ITEM occurs in LIST


b. Finds the number NUM of nonzero elements in LIST
c. Adds a given value K to each element in LIST

Solution:
a.
1. [Initialize Counter]
Set NUM=0
2. [Initialize pointer P]
Set P:= START
3. Repeat steps 4 and 5 while P<> Null
4. Apply PROCESS to INFO[P]
5. If INFO[P]=ITEM, then: set NUM=NUM+1
[End of Step 3 loop]
6. Exit

b.
a. [Initialize Counter]
Set NUM=0
b. [Initialize pointer P]
Set P:= START
3. Repeat steps 4 and 5 while P<> Null
4. Apply PROCESS to INFO[P]
5. If INFO[P] <> ITEM, then: set NUM=NUM+1
[End of Step 3 loop]
6. Exit

c.
1. [Initialize Counter]
Set NUM=0
2. [Initialize pointer P]
Set P:= START
3. Repeat steps 4 and 5 while P<> Null
4. Apply PROCESS to INFO[P]
5. Set INFO[P] =INFO[P]+1
[End of Step 3 loop]
6. Exit

2. Suppose LIST is in memory. Write an algorithm which deletes the last


node from list.

Solution
The last node can be deleted only when one also knows the location of
the next-to-last Accordingly, traverse the list using a pointer variable P, and
keep track of the preceding node using pointer variable SAVE. P points to the
last node when LINK[P] = NULL, and in such a case, SAVE points to the next
to last node. The case that LIST has only one node is treated separately, since
SAVE can be defined only when the list has 2 or more elements. The
algorithm follows.

1. [List empty?]
If START = NULL, then Write: UNDERFLOW, and Exit.
2. [List contains only one element?]
If LINK[START] = NULL, then:
a. Set START:= NULL. [Removes only node from list]
b. Set LINK[START]:= AVAIL and AVAIL:= START.
[Returns node to AVAIL list.]
c. Exit
[End of If structure.]
3. Set P:= LINK[START] and SAVE:= START. [Initializes pointers]
4. Repeat while LINK[P] <> NULL. [Traverses list, seeking last node.]
Set SAVE:= P and P:= LINK[P]. [Updates SAVE and P].
[End of loop.]
5. Set LINK[SAVE]:= LINK[P]. [Removes last node.]
6. Set LINK[P]:= AVAIL and AVAIL:= P. [Returns node to AVAIL List]
7. Exit.

3. Suppose NAME1 is a list in memory. Write an algorithm which copies


NAME1 into a list NAME2.

First set NAME2:= NULL to form an empty list. Then traverse NAME1
using a pointer variable P, and while visiting each node of NAME1, copy its
contents INFO[P] into a new node, which is then inserted at the end of
NAME2. Use LOC to keep track of the last node of NAME2 during the
traversal. Inserting the first node into NAME2 must be treated separately,
since LOC is not defined until NAME2 has at least one node. The algorithm
follows:

This algorithm makes a copy of a list NAME1 using NAME2 as the list
pointer variable of the new list.

1. Set NAME2 : = NULL. [Forms empty list.]


2. [NAME1 empty?] If NAME1 = NULL, then: Exit.
3. [Insert first node of NAME1 into NAME2.]
a. If AVAIL = NULL, then: Write: OVERFLOW, and Exit.
b. Set NEW: = AVAIL and AVAIL: = LINK[AVAIL].
[Removes first node from AVAIL list.]
c. Set INFO[NEW]:= INFO[NAME1]. [Copies data into new node.]
d. [Insert new node as first node in NAME2.]
Set LINK[NEW]:= NAME2 and NAME2:= NEW.
4. [Initializes pointers P and LOC.]
Set P:= LINK[NAME1] and LOC:= NAME2.
5. Repeat Steps 6 and 7 while PTR <> NULL:
6.
a. If AVAIL = NULL, then: Write: OVERFLOW, and Exit.
b. Set NEW:= AVAIL and AVAIL:= LINK[AVAIL].
c. Set INFO[NEW]:= INFO[PTR]. [Copies data into new node.]
d. [Insert new node into NAME2 after the node with location LOC]
Set LINK[NEW]:= LINK[LOC], and LINK[LOC]:= NEW.

7. Set P:= LINK[P] and LOC:= LINK[LOC]. [Updates P and LOC.]


[End of Step 5 loop.]
8. Exit.

4. Suppose LIST is a header (circular) list in memory. Write an algorithm


which deletes the last node from LIST.

The algorithm for deleting the last node from the header list.

1. [List empty?] If LINK[START] = NULL, then: Write: UNDERFLOW.


2. Set P:= LINK[START] and SAVE:= START. [Initializes pointers.]
3. Repeat while LINK[P] <> START: [Traverses list seeking last node.]
Set SAVE = P and P:= LINK[P]. [Updates SAVE and P]
[End of loop.]
4. Set LINK[SAVE]:= LINK[P]. [Removes last node.]
5. Set LINK[P]:= AVAIL and AVAIL:= P. [Returns node to AVAIL list]
6. Exit.

6. Imagine we have the two linked lists shown below. What would happen
if we apply the following statement to these two lists?
list1=list2
Solution:

list1 no longer points to the head of the first list, rather it points to the
head of the second list in the figure shown below.

7. Imagine we have the linked list shown in figure.

Show what would happen if we apply the following statements to


this list?

1. temp=pList
2. loop (temp->link not null)
i. temp=temp->link
3. temp->link=pList

Solution:
This will create a circularly linked list.

8. Imagine we have a liked list as shown below:


Show what happens if we use the following statement in a search of the
linked list:
header = header → link
What is the problem with using this kind of statement?

Solution:
Header is a unique pointer that is used to keep track of the head or the
first node of the list. Executing statements like header = header → link
modifies header which no longer points to the first node of the list, an
effect that may be devastating for the whole program.

2.19 STACK

A stack is a linear structure in which items may be added or removed


only at one end called top of the stack. This means that, last item to be added
to a stack is the first item to be removed. Therefore, the stacks are also called
Last-In First-Out (LIFO) or First-In-Last-Out (FILO) lists. Other names used
for stacks are: piles and push-down lists.

The various operations performed over stacks are:

1. Create the stack, leaving it empty


2. Determine whether the stack is empty or not
3. Determine whether the stack is full or not.
4. Find the size of the stack.
5. Push a new entry onto the top of the stack, provided the stack is not
full.
6. Retrieve the top entry in the stack, provided the stack is not empty.
7. Pop the entry off the top of the stack, provided the stack is not empty.
8. Clear the stack to make it empty.
9. Traverse the stack, performing a given operation with each entry.

Examples:
A stack of books, stack of folded towels, stack of discs, stack of coins,
computer stack

2.19.1 Stack Operations:


The basic operations with stack are:

a. Push
It is used to insert an element into the stack.

b. Pop
It is used to remove an element from the stack.

c. Stack Top
It copies the item at the top of the stack. That is, it returns the
data in the top element to the user but does not delete it. It can also
result in underflow if the stack is empty.

Figure: PUSH stack operation


Figure: POP stack operation

Example:
Suppose the following 6 elements are pushed, in order, onto an empty
stack:
AA, BB, CC, DD, EE, FF

Figure shows three ways of picturing such a stack. For notational


convenience, we will frequently designate the stack by writing:

STACK: AA, BB, CC, DD, EE, FF


Figure: Diagram of stacks

Please note that, EEE cannot be deleted before FFF is deleted, DDD
cannot be deleted before EEE and FFF are deleted, and so all.

2.19.2 ARRAY REPRESENTATION OF STACKS

Stacks may be represented in the computer in various ways, usually by


means of a one-way list or a linear array. A pointer variable TOP, which
contains the location of the top element of the stack; and a variable
MAXSTACK which gives the maximum number of elements that can be held
by the stack. The condition TOP = 0 or TOP = NULL will indicate that the
stack is empty.
Figure shows an array representation of a stack.

Since TOP = 6, the stack has six elements, AA, BB, CC, DD, EE and FF.
and since MAXSTACK = 12, there is room for 6 more items in the stack.

The operation of adding (pushing) an item onto a stack and the


operation of removing (popping) an item from a stack may be implemented,
respectively, by the following procedures, called PUSH and POP.
In executing the procedure PUSH, one must first test whether there is
room in the stack for the new item; if not, then we have the condition known
as overflow.
Similarly, in executing the procedure POP, one must first test whether
there is an element in the stack to be deleted; if not, then we have the
condition known as underflow.

2.19.3 Algorithm : PUSH Operation


This procedure pushes an ITEM onto a stack.
1. [Stack already filled?]
If TOP = MAXSTACK, then: Print: OVERFLOW, and Return.
2. Set TOP: = TOP + 1. [Increases TOP by 1.]
3. Set STACK[TOP] : = ITEM. [Inserts ITEM in new TOP position.]
4. Return.
2.19.4 Algorithm : POP Operation

This procedure deletes the top element of STACK and assigns it to the
variable ITEM.

1. [Stack has an item to be removed?]


If TOP = 0, then: Print: UNDERFLOW, and Return.
2. Set ITEM: = STACK[TOP]. [Assigns TOP element to ITEM.]
3. Set TOP:= TOP - 1. [Decreases TOP by 1.]
4. Return.

2.19.5 Applications of Stack

1. Reversing a data
2. Parsing
3. Postponement
It is used to convert infix to postfix notation and also to evaluate a
postfix notation.
4. Backtracking
It is used to make decisions between two or more paths. Backtracking
is found in applications such as computer gaming, decision analysis and
expert systems.

2.19.5.1 Reversing a data


This means a given set of data to be reordered so that the first and last
elements are exchanged, with all of the positions between the first and last
being relatively exchanged also. For example, {1 2 3 4} becomes {4 3 2 1}.
Example:
Reversing a list, converting decimal to binary number

2.19.5.2 Parsing
Parsing is a logic that breaks data into independent pieces for further
processing. For example, to translate a source program into machine
language, a compiler must parse the program into individual parts such as
keywords, names and tokens.
The common problem you have faced is unmatched parenthesis in an
algebraic expression. When parenthesis are unmatched, two types of error can
occur:
1. The opening parenthesis can be missing
Example: ((A+B)/C
2. The closing parenthesis can be missing
Example: (A+B)/C)

2.19.5.3 Postponement
It is used to convert infix to postfix notation and also to evaluate a postfix
notation.
An arithmetic expression can be represented in three different formats:
infix, prefix and postfix.

Infix Notation

In an infix expression, the operator is placed between two operands.


Example:
a+b

Disadvantage:
1. We need to use parenthesis to control the evaluation of the
operators.

Prefix Notation
In a prefix expression, the operator is placed before the two operands.
Example:
+ab

Postfix Notation
In a postfix expression, the operator is placed after the two operands.
Example:
ab+

2.19.5.3.1 Rules for converting infix to postfix expression

1. Evaluate parenthesis
2. Evaluate power
3. Evaluate multiplication and division; the operations are done from left
to right.
4. Evaluate addition and subtraction; the operations are done from left to
right.
5. Change all infix notations in each parenthesis to postfix notation
starting from the innermost expressions.
6. Remove all parenthesis.

Example:
Infix Notation Postfix Notation
ABC*+
A+B*C
A B + C * D + E F * +G -
(A+B)*C+D+E*F-G
AB*C+
A*B+C
ABC*DE/-+
A+B*C-D/E
AB*CD+-E+
A*B-(C+D)+E
AB+C-
A+B – C
AB+CD-*
(A+B)*(C-D)
ABC+*D*
A*(B+C)*D
BC*D-EF/GH+/+
B*C-D+E/F/(G+H)
ABC*DEF↑/G*-H*H
A+(B*C-(D/E↑F)*G)*H
AB-DE/*
A-B*(D/E)
ABD↑+EF-/G+
(A+B↑D)/(E-F)+G
ABD+*E/FGHK/+*-
A*(B+D)/E-F*(G+H/K)
AB+D*EF-↑
((A+B)*D) ↑(E-F)

Postfix notation to Infix notation

Example:

Postfix Notation Infix Notation


ABC+* A * B+C
12 7 3 - / 2 1 5 + * + 12/(7-3)+2*(1+5)

2.19.5.4 Backtracking
It is used to make decisions between two or more paths. It is found in
application such as computer gaming, decision analysis and expert systems.
Examples of backtracking are: Goal seeking and Eight-queens problem.

Evaluation of a Postfix Expression

Suppose P is an arithmetic expression written in postfix notation. The


following algorithm, which uses a STACK to hold operands, evaluates P.

Algorithm
This algorithm finds the VALUE of an arithmetic expression P
written in postfix notation.

1. Add a right parenthesis “)” at the end of P. [This acts as a sentinel.]


2. Scan P from left to right and repeat Steps 3 and 4 for each element of P
until the sentinel “)” is encountered.
3. If an operand is encountered, put it on STACK.
4. If an operator ⊗ is encountered, then:
i. Remove the two top elements of STACK, where A is the
top element and B is the next-to-top element.
ii. Evaluate B ⊗ A.
iii. Place the result of (b) back on STACK.
[End of If structure.]
[End of Step 2 loop.]
5. Set VALUE equal to the top element on STACK.
6. Exit.

Please note that, when Step 5 is executed, there should be only one
number on STACK.

2.19.6 Algorithm for Infix Expressions into Postfix Expressions

Let Q be an arithmetic expression written in infix notation. Besides


operands and operators, Q may also contain left and right parentheses.
The operators in Q consist only of exponentiations (↑), multiplications
(*), divisions (/), additions (+) and subtractions (-).
We also assume that operators on the same level, including
exponentiations, are performed from left to right.
The following algorithm converts the infix expression Q into its
equivalent postfix expression P. The algorithm uses a stack to temporarily
hold operators and left parentheses. The postfix expression P will be
constructed from left to right using the operands from Q and the operators,
which are removed from STACK. We begin by pushing a left parenthesis onto
STACK and adding a right parenthesis at the end of Q. The algorithm is
completed when STACK is empty.

Algorithm

Suppose Q is an arithmetic expression written in infix notation. This


algorithm finds the equivalent postfix expression P.
1. Push “(“ onto STACK, and add “)” to the end of Q.
2. Scan Q from left to right and repeat Steps 3 to 6 for each element of Q
until the STACK is empty:
3. If an operand is encountered, add it to P.
4. If a left parenthesis is encountered, push it onto STACK.
5. If an operator ⊗ is encountered, then:
i. Repeatedly pop from STACK and add to P each operator
(on the top of STACK) which has the same precedence as
or higher precedence than ⊗.
ii. Add ⊗ to STACK.
[End of If structure.]
6. If a right parenthesis is encountered, then:
i. Repeatedly pop from STACK and add to P each operator
(on the top of STACK) until a left parenthesis is
encountered.
ii. Remove the left parenthesis. [Do not add the left
parenthesis to P.]
[End of If structure.]
[End of Step 2 loop.]
7. Exit

2.19.7 Check for Balanced Parenthesis


An another application of stacks, we will try to determine whether
parenthesis and brackets are balanced properly in algebraic expressions.
To indicate the boundaries of a sub-expression, we used parenthesis,
brackets and braces.
To determine, whether the pair is matching or not we must check that a
right counter part ‘)’ or ‘]’ or ‘}’ exist for each left parenthesis ‘(‘ or ‘[‘ or ‘{‘ in
the proper order. To do this, we must use a stack.
Whenever we encounter a left parenthesis, we push it onto the stack.
Whenever we encounter a right parenthesis, we pop the top symbol off
the stack and check to see whether its type matches the type of right
parenthesis, bracket or brace encountered.
The expression has properly balanced parenthesis, if the stack is empty
by the time we get to the end of an expression and all pairs of matched
parenthesis were of the same type. Otherwise, the parenthesis are not
balanced properly.
For example, the stack will vary as shown below for the algebraic
expression.
We may note that all pairs of parenthesis are matching and the stack is
empty at the end of expression string. Therefore, the expression string has
balanced parenthesis.
Figure: An example of checking balanced parenthesis

An algorithm for checking balanced parenthesis may be written as


follows:

1. [Initialize]
match=True;
stack=empty;
2. Read symbol from input string
3. While not end of input string and matching
{
If symbol =’(‘ or ‘{‘ or ‘[‘
Push(symbol,stack);
Else
If symbol =’)’ or ‘}’ or ‘]’ or
if stack is empty
then
match=false
Write (“More right parenthesis than left parenthesis”)
Else
C=pop(stack)
Match c and the input symbol;
If not matched
{
match=false;
write(“Mismatched parenthesis”);
}
read the next input symbol;
}
if stack is empty then
write (“Parenthesis are balanced properly”)
else
write (“More left parenthesis than write parenthesis”)
2.19.8 Eight Queens Problem (using stack and backtracking logic)

A classic chess problem requires that you place eight queens on the
chessboard in such a way that no queen can capture another queen. A queen
can attack another queen if it is in the same row, same column or on a
diagonal. There are actually several solutions to this problem. The computer
solution to this problem requires that we place a queen on the board and then
analyze all of the attack positions to see if there is a queen that could capture
the new queen. If there is, then we try another queen.
Now, let us first demonstrate four queen’s on a four-by-four
chessboard. The queens capture rule and one solution are shown in figure.

Figure: Four Queens Solution

We can solve this problem using a stack and backtracking logic.


Because only one queen can be placed in any row, we begin by placing the
first queen in row 1 and column 1. This location is then pushed into a stack,
giving the position as shown below.

After placing a queen in the first row, we look for a position in the
second row. Position 2, 1 is not possible because the queen in the first row is
guarding this solution on the vertical. Likewise, position 2, 2 is guarded on
the diagonal. We therefore place a queen in the third column in row 2 and
push this location into the stack. This is shown below.
We now begin to locate a position in row 3. It is noted that, none of the
positions in row 3 are possible. The first column is guarded by the queen in
row one, and the other three positions are guarded by the queen in row two.
At this point we must backtrack the second row by popping the stack and
continue looking for a position for the second row queen. Because column
four is not guarded, we place a queen there and push its location into the
stack. This is shown below:

Looking again at row three, we see that the first column is still guarded
by the queen in row one, but that we can place a queen in the second column.
We do so and push this location into the stack. This is shown below.

When we try to place a queen in row four, however we find that all
positions are guarded. Column one is guarded by the queen in row one and
the queen in row three.
Column two is guarded by the queen in row two and the queen in row
three.
Column three is guarded by the queen in row three and the column
four is guarded by both the queen in row one and the queen in row two.
We therefore backtrack to the queen in row three and try to find
another place for her. Because the queen in row two is guarding both column
three and column four, there is no position for a queen in row three. Once
again backtrack by popping the stack and find that the queen in row two has
nowhere else to go, so we backtrack to the queen in row one and move her to
column two. This is shown below.

Analyzing row two, we see that the only possible solution for a queen
is column four because the queen in row one is guarding the first three
positions. We therefore place the queen in this location and push the location
into the stack. This is shown below.

Column one in the third row is unguarded so we place a queen ther.


This is shown below.
Moving to row four, we find that the first two positions are guarded,
the first by the queen in row three and the second by all three queens. The
third column is unguarded, however, so we can place the fourth queen in this
column for a solution to this problem.
Generalizing the solution, we see that we place a queen in a position in
a row and then examine all positions I the next row to see if a queen can be
placed there. If we can’t place a queen and try to position her in the next
column. If there is no room in the next column, then we fall back again. Given
that there is a solution, this trial and error method works well.
Please note that, there is no solution for boards for boards less than 4 x
4 positions. All boards from 4 x 4 to 8 x 8 have at least one solution.

2.19.9 PROBLEMS

1. Consider the following stack of characters, where STACK is


allocated N = 8 memory cells:
STACK : A,C,D,F,K, - , - , -
Describe the stack as the following operations take place:
a. POP(STACK, ITEM)
b. POP(STACK, ITEM)
c. PUSH(STACK, L)
d. PUSH(STACK, P)
e. POP(STACK, ITEM)
f. PUSH(STACK, R)
g. PUSH(STACK, S)
h. POP(STACK, ITEM)

Solution:
The POP procedure always deletes the top element from the stack, and
the PUSH procedure always adds the new element to the top of the stack.
Accordingly:

a. STACK: A,C,D,F,_,_,_,
b. STACK: A, C, D, -, -, -, -, -
c. STACK: A, C, D, L, -, -, -, -
d. STACK: A, C, D, L, P, -, -, -
e. STACK: A, C, D, L, -, -, -, -
f. STACK: A, C, D, L, R, -, -, -
g. STACK: A, C, D, L, R, S, -, -
h. STACK: A, C, D, L, R, -, -, -

2. Consider the data in above problem. (a) When will overflow occur?
(b) When will C be deleted before D?

Solution:
a. Since STACK has been allocated N = 8 memory cells, overflow
will occur when STACK contains 8 elements and there is a
PUSH operation to add another element to STACK.
b. Since STACK is implemented as a stack, C will never be deleted
before D.
3. Consider the following stack, where STACK is allocated N = 6
memory cells:
STACK: AA, DD, EE, FF, GG, -
Describe the stack as the following operations take place:
a. PUSH(STACK,KK)
b. POP(STACK, ITEM)
c. PUSH(STACK,LL)
d. PUSH(STACK,SS)
e. POP(STACK, ITEM)
f. PUSH(STACK, TT).

Solutions:
a. KK is added to the top of STACK, yielding
STACK: AA,DD,EE,FF,GG,KK
b. The top element is removed from STACK, yielding
STACK: AA, DD, EE, FF, GG,-
c. LL is added to the top of STACK, yielding
STACK: AA, DD, EE, FF, GG, LL
d. Overflow occurs, since STACK is full and another element SS is to be
added to STACK.
e. The top element is removed from STACK, yielding
STACK: AA, DD, EE, FF, GG,-
f. TT is added to the top of STACK, yielding
STACK: AA, DD, EE, FF, GG, TT

4. Suppose STACK is allocated N = 6 memory cells and initially


STACK is empty, or, in other words, TOP = 0. Find the output of the
following module:
1. Set AA:= 2 and BB :=5.
2. Call PUSH(STACK, AA).
Call PUSH(STACK, 4).
Call PUSH(STACK, BB + 2).
Call PUSH(STACK, 9).
Call PUSH(STACK, AA + BB).
3. Repeat while TOP <> 0
Call POP(STACK, ITEM).
Write: ITEM.
[End of loop.]
4. Return.

Solution:

Step 1. Sets AA = 2 and BB = 5.


Step 2. Pushes AA = 2, 4, BB + 2 = 7, 9 and AA + BB = 7 onto STACK, yielding
STACK: 2,4,7,9,7, -
Step 3. Pops and prints the elements of STACK until STACK is empty. Since
the top element is always popped, the output consists of the following
sequence: 7,9,7,4,2, -

Observe that, this is the reverse of the order in which the elements
were added to stack.

5. Consider the following arithmetic expression P, written in postfix


notation:
P: 12, 7, 3, -, /, 2, 1, 5, +, *, +
a. Translate P, into its equivalent infix expression.
b. Evaluate the infix expression.

Solution:
a. Scanning from left to right, translate each operator from postfix to infix
notation. (We use brackets [ ] to denote a partial translation.)
P = 12, [7 - 3], /, 2, 1, 5, +, *, +
= [12/(7 - 3)], 2, 1, 5, +, *, +
= [12/(7 - 3)], 2, [1 + 5], *, +
= [12/(7 - 3)], [2 * (1 + 5)], +
= 12/(7 - 3) + 2* (1 + 5)
b. Using the infix expression, we obtain:
P = 12/(7 - 3) + 2 * (1 + 5)
= 12/4 + 2 * 6
= 3 + 12
= 15

5. Imagine we have two empty stacks of integers s1 and s2. Draw a


picture of each stack after the following operations:
Pushstack (s1,3);
Pushstack (s1,5);
Pushstack (s1,7);
Pushstack (s1,9);
Pushstack (s1,11);
Pushstack (s1,13);
While (!emptystack (s1))
{
popstack(s1,x);
Pushstack (s2,x);
}

Solution:

6. Using the manual transformation, write the following infix


expressions in their postfix and prefix forms:
a. D – B + C b. A * B + C * D
c. (A + B) * C – D * F + C d. (A – 2 * (B + C) – D * E) * F

SOLUTION:

INFIX PREFIX POSTFIX


D–B+C +-DBC DB–C+
A*B+C*D +*AB*CD AB*CD*+
(A + B) * C – D * F + C +-**ABC*DFC AB+C*DF*-C+
(A – 2 * (B + C) – D * E) * F *--A*2+BC*DEF A2BC+*-DE*-F*

7. If the value of A,B, C and D are 2, 3, 4 and 5 respectively, manually


calculate the value of the following prefix expressions:
a. A B * C – D + b. A B C + * D –

Solution:

a. 7 b. 9
8. Change the following infix expressions to postfix expressions using
the algorithmic method (a stack).
a. D – B + C b. A * B + C * D
c. (A + B) * C – D * F + C d. (A – 2 * (B + C) – D * E) * F
Solution:
a.

Original Stack Output


b.
D -B+C
Original
-B+C Stack D Output
c.
A * BB ++CC*D - D
d. *Original
B+C +C*D - StackDAB Output
(A + B) *BC+–CD*C*DF + C*+ DAB -
A + BOriginal
) * C+–CD* *DF + C *+ (StackDABB– C + Output
2.20 (A – 2 *+(B
B )+*C) C –CD**D E)
F +* CF+ ( AAB*
QUEU A – 2 * (B +
B)*C C) – D *
*D E) * F
F + C+ + ( AAB*C
E - 2 * (B )+*C)C – D * E)F +*
D +* CF ( + A B *BC
A
2 * (B + *C)C––D D**E)F+ * FC+ * ( - AAB *BC+ D
A * (B + C)C––DD**E) F+* FC+ *( - AAB *2BC+ D *
queue (B + C) –- D * E)
D*F+C * * F ( - * A 2 C+ D
A B *B C* +
is a B + C) – D * FE)+*CF -( - * ( A 2B + C * D
linear + C) – D**FE)+*CF (- - * ( A 2B B+ C * D
list of C) – D * FE)+*CF (- -* * ( + A 2B B+ C * D
element ) – D * E)+* C F (- -* * ( + A 2B B+ C C*DF
s in - D * E) * C F (+- * A 2B B+ C
C +* D F * -
which D * E) * F (+- A 2B B+ C
C +* D
* -F * - C
items * E) * F ( - A 2B B+ C
C +* D
* -FD* - C +
may be E) * F ( - * A2BC+*-D
added )*F (-* A2BC+*-DE
only at *F A2BC+*-DE*-
one end F * A2BC+*-DE*-
called * A2BC+*-DE*-F
rear and A2BC+*-DE*-F*
items
may be deleted only at the other end called front. Thus a queue is also called
First-In-First-Out (FIFO) lists, since the first element in a queue will be first
element out of the queue.
Queues abound in everyday life. The automobiles waiting to pass
through an intersection for queue, in which the first car in line is the first car
through; the people waiting in line at a bank or milk booth forms a queue,
where the first person in line is the first person to be waited on; and so on.
An important example of a queue in computer science occurs in a
timesharing system, in which programs with same priority form a queue
while waiting to be executed.
The operations performed over queue are:

1. Create the queue, leaving it empty


2. Determine whether the queue is empty or not
3. Determine whether the queue is full or not.
4. Find the size of the queue.
5. Append a new entry onto the rear of the stack, provided the queue is
not full.
6. Retrieve the front entry in the queue, provided the queue is not empty.
7. Serve (and Remove) the entry from the front of the queue, provided the
queue is not empty.
8. Clear the queue to make it empty.
9. Traverse the queue, performing a given operation with each entry.

2.20.1 Representation of Queue

Queues may be represented in the computer in various ways, usually


by means of one-way lists or arrays. The queues will be maintained by a
linear array QUEUE and two pointer variables: FRONT, containing the
location of the front element of the queue; and REAR, containing the location
of the rear element of the queue. The condition FRONT=NULL will indicate
that the queue is empty.
Figure shows the representation of queue stored in memory using an
array and Linked list.

Using Linked List

Figure: Representation of a queue using linked list

Figure shows a schematic diagram of a queue with 4 elements, where


AA is the front element and DD rear element. Observe that the front and rear
elements of the queue are also, respectively, the first and last elements of the
list.
Suppose an element is deleted from the queue. Then it must be AA.
This yields the queue in figure (b), where BB is now the front element. Next,
suppose EE is added to the queue and then FF is added to the queue. Then
they must be added at the rear of the queue, as shown in figure (c).
Note that FF is the rear element. Now suppose another element is
deleted from the queue; then it must be BB, to yield the queue in figure (d).
And so on. Observe that in such a data structure, EE will be deleted before FF
because it has been placed in the queue before FF. However, EE will have to
wait until CC and DD are deleted.

Representation of queues using Arrays

Figure: Array Representation of Queue

Figure also indicates the way elements will be deleted from the queue
and the way the new elements will be added to the queue. Observe that
whenever an element is deleted from the queue, the value of FRONT is
increased by 1; this can be implemented by the assignment
FRONT: = FRONT + 1

Similarly, whenever an element is added to the queue, the value of


REAR is increased by 1; this can be , implemented by the assignment

REAR:=REAR+ 1

This, means that after N insertions, the rear element of the queue will
occupy QUEUE[N] or, in other words, the queue will occupy the last part of
the array. This occurs even though the queue may not contain many elements.

Suppose we want to insert an element ITEM into a queue at the time


the queue does occupy the part of the array, i.e., when REAR = N. One way to
do this is to simply move the entire queue to beginning of the array, changing
FRONT and REAR accordingly, and then inserting ITEM as above. The
procedure we adopt is to assume that the array QUEUE is circular, that is,
that QUEUE[l] comes after QUEUE[N] in the array. With this assumption, we
insert ITEM into the queue by assigning ITEM to QUEUE[l]. Specifically,
instead increasing REAR to N + 1, we reset REAR = 1 and then assign
QUEUE[REAR]:= ITEM

Similarly, if FRONT = N and an element of QUEUE is deleted, we reset


FRONT = 1 instead of increasing FRONT to N+1.
Suppose that our queue contains only one element, i.e., suppose that
FRONT = REAR <> NULL

and suppose that the element is deleted. Then we assign

FRONT: = NULL and REAR: = NULL

to indicate that the queue is empty.

2.20.2 Algorithm for Queue Insert

This algorithm will insert a data ITEM into a queue.

QINSERT(QUEUE, N, FRONT, REAR, ITEM)

1. [Queue already filled?]


If FRONT = 1 and REAR = N, or if FRONT = REAR + 1,
then:
Write: OVERFLOW, and Return.
2. [Find new value of REAR.]
If FRONT:= NULL,
then:
[Queue initially empty.]
Set FRONT:= 1 and REAR:= 1.
Else if REAR = N,
then:
Set REAR:= 1
Else:
Set REAR:= REAR + 1.
[End of If structure.]
3. Set QUEUE[REAR] : = ITEM.
[This inserts new element]
4. Return.
2.20.3 Algorithm for Queue Delete

This algorithm will delete the first element from a queue.

QDELETE(QUEUE, N, FRONT, REAR, ITEM)

1. [Queue already empty?]


If FRONT: = NULL,
then:
Write: UNDERFLOW, and Return.
2. Set ITEM:= QUEUE[FRONT].
3. [Find new value of FRONT.]
If FRONT = REAR,
then:
[Queue has only one element to start.!
Set FRONT: = NULL and REAR: = NULL.
Else if FRONT = N, then:
Set FRONT: = 1.
Else:
Set FRONT: = FRONT + 1.
[End of If structure.]
4. Return.

2.20.4 DEQUES
A deque is a linear list in which elements can be added or removed at
either end but not in the middle.
There are various ways of representing a deque in a computer. The
deque is maintained by a circular array DEQUE with pointers LEFT and
RIGHT, which point to the two ends of the deque. We assume that the
elements extend from the left end to the right end in the array. The term
"circular" comes from the fact that we assume that DEQUE[1] comes after
DEQUE[N] in the array.
There are two variations of a deque-namely, an input -restricted deque
and an output-restricted deque-which are intermediate between a deque and
a queue.
An input-restricted deque is a deque which allows insertions at only
one end of the list but allows deletions at both ends of the list.
An output-restricted deque is a deque which allows deletions at only
one end of the list but allows insertions at both ends of the list.

2.20.5 PRIORITY QUEUES

A priority queue is a collection of elements such that each element has


been assigned a priority. The following rules are used to delete and process
the elements from a priority queue.
1. An element of higher priority is processed before any element of lower
priority.
2. Two elements with the same priority are processed according to the
order in which they were added to the queue.

A prototype of a priority queue is a timesharing system: programs of high


priority are processed first, and programs with the same priority form a
standard queue.
There are various ways of maintaining a priority queue in memory.
These are:
1. One -way list
2. Multiple queues.

2.20.5.1 One-Way List Representation of a Priority Queue

Here,
1. Each node in the list will contain three items of information:
a. An information field INFO
b. A priority number PRNUM
c. A link number LINK.
2. A node X precedes a node Y in the list
a. When X has higher priority than Y or
b. When both have the same priority but X was added to the list
before Y.
This means that, the order in the one-way list
corresponds to the order of the priority queue.

Example:
Consider the following figure:

Figure shows a diagram of a priority queue with 7 elements. The


diagram does not tell us whether BB was added to the list before or after DD.
On the other hand, the diagram does tell us that BB was inserted before
CC, because BB and CC have the same priority number and BB appears
before CC in the list.
The main property of the one-way list representation of a priority
queue is as follows:
The element in the queue that should be processed first always appears
at the beginning of the one-way list.

The outline of the algorithm follows.

Algorithm :
This algorithm deletes and processes the first element in a priority
queue which appears in memory as a one-way list.
1. Set ITEM:= INFO[START].
[This saves the data in the first node.]
2. Delete first node from the list.
3. Process ITEM.
4. Exit.

Adding an element to the priority queue is much more


complicated than deleting an element from the queue, because we need
to find the correct place to insert the element. An outline of the
algorithm follows.

Algorithm:
This algorithm adds an ITEM with priority number N to a priority
queue which is maintained in memory as a one-way list.
1. Traverse the one-way list until finding a node X whose
priority number exceeds N. Insert ITEM in front of node X.
2. If no such node is found, insert ITEM as the last element of the
list.

The main difficulty in the algorithm is that ITEM is inserted before


node X. This means that, while traversing the list, one must also keep track of
the address of the node preceding the node being accessed.

Example:

Consider the priority queue as shown in figure.

Suppose an item XXX with priority number 2 is to be inserted into the


queue. We traverse the list, comparing priority numbers. Observe that DDD is
the first element in the list whose priority number exceeds that of XXX. Hence
XXX is inserted in the list in front of DDD, as shown in figure. Observe that
XXX comes after BBB and CCC, which have the same priority as XXX.
Suppose now that an element is to be deleted from the queue. It will be AAA,
the first element in the list. Assuming no other insertions, the next element to
be deleted will be BBB, then CCC, then XXX, and so on.

2.20.5.2 Array Representation of a Priority Queue

Another way to maintain a priority queue in memory is to use a


separate queue for each level of priority (or for each priority number). Each
such queue will appear in its own circular array and must have its own pair of
pointers, FRONT and REAR.
If each queue is allocated the same amount of space, a two-dimensional
array QUEUE can be used instead of the linear arrays.
Figure shows this representation for the priority queue in the above
figure. Observe that FRONT[K] and REAR[K] contain, respectively, the front
and rear elements of row K of QUEUE, the row that maintains the queue of
elements with priority number K.

FRONT REAR 1 2 3 4 5 6
1 2 2 1 AAA
2 1 3 2 BBB CCC XXX
3 0 0 3
4 5 1 4 FFF DDD EEE
5 4 4 5 GGG

The following are outlines of algorithms for deleting and inserting


elements in a priority queue that is maintained in memory by a two-
dimensional array QUEUE, as above.

Algorithm:

This algorithm deletes and processes the first element in a


priority queue maintained by a two-dimensional array QUEUE.
1. [Find the first non empty queue.]
Find the smallest K such that FRONT[K] <>NULL.
2. Delete and process the front element in row K of QUEUE.
3. Exit.

Algorithm :
This algorithm adds an ITEM with priority number M to a priority
queue maintained by a two-dimensional array QUEUE.
1. Insert ITEM as the rear element in row M of QUEUE.
2. Exit.

2.20.6 Applications of Queues

1. Simulation of a real world situation so that it is possible to understand


what happens in a real world in a particular situation without actually
observing its occurrence.
2. It is also useful in a time-sharing computer system, where many users
share the system simultaneously.
3. It is also used for finding a path using breadth-first search of graphs.
4. Queues are also used in business such as processing customer requests,
jobs and orders.

2.20.7 PROBLEMS

1. Consider the following queue of characters, where QUEUE is a


circular array which is allocated six memory cells:
FRONT = 2, REAR=4 QUEUE: -, A, C, D, -, -
Describe the queue as the following operations take place:
a. F is added to the queue.
b. Two letters are deleted.
c. K, L and M are added to the queue.
d. Two letters are deleted.
e. R is added to the queue.
f. Two letters are deleted
g. S is added to the queue
h. Two letters are deleted
i. One letter is deleted
j. One letter is deleted.

Solution:
a. F is added to the rear of the queue, yielding FRONT = 2, REAR = 5
Queue : -, A, C, D, F, -
Note that REAR is increased by 1.
b. The two letters, A and C, are deleted, leaving FRONT = 4, REAR = 5
QUEUE: -, -, -, D, F,
Note that FRONT is increased by 2.
c. K, L and M are added to the rear of the queue. Since K is placed
in the last memory cell of QUEUE, L and M are placed in the first
two memory cells. This yields FRONT = 4, REAR = 2
QUEUE: L, M, _ , D, F, K
Note that REAR is increased by 3 but the arithmetic is modulo 6:
REAR = 5 + 3 = 8 = 2 (mod 6)
d. The two front letters, D and F are deleted, leaving
FRONT = 6, REAR = 2 QUEUE: L, M, -, -, -, K
e. R is added to the rear of the queue, yielding
FRONT = 6, REAR = 3, QUEUE: L, M, R, -, -, K
f. The two front letters, K and L, are deleted, leaving FRONT=2,
REAR=3,
QUEUE: -, M, R, -, -,
Note that FRONT is increased by 2 but the arithmetic is
modulo 6:
FRONT=6+2=8=2(mod 6)
g. S is added to the rear of the queue, yielding
FRONT = 2, REAR = 4 QUEUE: -, M, R, S, -, -
h. The two front letters, M and R, are deleted, leaving FRONT = 4,
REAR = 4
QUEUE: -, -, -, S, -,
i. The front letter S is deleted. Since FRONT = REAR, this means
that the queue is empty; hence assign NULL to FRONT and
REAR. Thus
FRONT = 0, REAR = 0 QUEUE: -, -, -, -, -, -
j. Since FRONT= NULL, no deletion can take place. That is,
underflow has occurred.

2. Suppose each data structure is stored in a circular array with N memory


cells.
a. Find the number NUM of elements in a queue in terms of FRONT
and REAR.
b. Find the number NUM of elements in a deque in terms of LEFT and
RIGHT.
c. When will the array be filled?
Solution:

(a) If FRONT <=REAR, then NUM = REAR - FRONT + 1.


For example, consider the following queue with N = 12:
FRONT = 3, REAR=9 QUEUE: -,-,*,*,*,*,*,*,*,-,-,-
Then NUM = 9 - 3 + 1 = 7, as pictured.
If REAR < FRONT, then FRONT - REAR - 1 is the number
of empty cells, so NUM = N - (FRONT - REAR - 1)
= N + REAR - FRONT + 1

For example, consider the following queue with N = 12:


FRONT = 9, REAR = 4, QUEUE: *,*,*,*,-,-,-,-,*,*,*,*
Then NUM = 12 + 4 - 9 + 1 = 8, as pictured.
Using arithmetic modulo N, we need only one formula,
as follows:
NUM = REAR - FRONT + 1 (mod N)
(b) The same result holds for deques except that FRONT is replaced by
RIGHT. That is, NUM = RIGHT - LEFT + 1 (mod N)
(c). With a queue, the array is full when
(i) FRONT = 1 and REAR=N
OR
(ii) FRONT = REAR + 1
Similarly, with a deque, the array is full when
(i) LEFT = 1 and RIGHT = N
OR
(ii) LEFT = RIGHT + 1

Each of these conditions implies NUM = N.

3. Consider the following deque of characters where DEQUE is a


circular array which is allocated six memory cells:
LEFT = 2, RIGHT = 4 DEQUE: -, A, C, D, -, -
Describe the deque while the following operations take place.
a. F is added to the right of the deque.
b. Two letters on the right are deleted.
c. K, L and M are added to the left of the deque.
d. One letter on the left is deleted.
e. R is added to the left of the deque.
f. S is added to the right of the deque.
g. T is added to the right of the deque.

Solution:

a. F is added on the right, yielding LEFT = 2, RIGHT = 5


DEQUE: -, A, C, D, F, -
Note that, RIGHT is increased by 1.
b. The two right letters, F and D, are deleted, yielding LEFT = 2,
RIGHT = 3 DEQUE: -, A, C, -, -, -
Note that RIGHT is decreased by 2.
c. K, L and M are added on the left. Since K is placed in the first memory
cell, L is placed in the last memory cell and M is placed in the next-to-
last memory cell. This yields LEFT = 5, RIGHT=3
DEQUE: K, A, C, -, M, L
Note that LEFT is decreased by 3 but the arithmetic is modulo 6:
LEFT = 2 - 3 = -1 = 5 (mod 6)
d. The left letter, M, is deleted, leaving LEFT = 6, RIGHT = 3
DEQUE: K, A, C, -, -, L
Note that LEFT is increased by 1.
e. R is added on the left, yielding LEFT = 5, RIGHT = 3
DEQUE: K, A, C, -, R, L
Note that LEFT is decreased by 1.
f. S is added on the right, yielding LEFT = 5, RIGHT=4
DEQUE: K,A,C,S,R,L
g. Since LEFT = RIGHT + 1, the array is full, and hence T cannot be added
to the deque. That is, overflow has occurred.

4. Consider a deque maintained by a circular array with N memory cells.


a. Suppose an element is added to the deque. How is LEFT or RIGHT
changed?
b. Suppose an element is deleted. How is LEFfTor RIGHT changed?

Solution:
a. If the element is added on the left, then LEFT is decreased by 1 (mod
N). On the other hand, if the element is added on the right, then
RIGHT is increased by 1 (mod N).
b. If the element is deleted from the left, then LEFT is increased by 1 (mod
N). However if the element is deleted from the right, then RIGHT is
decreased by 1 (mod N). In the case that LEFT = RIGHT before the
deletion (that is, when the deque has only one element), then LEFT and
RIGHT are both assigned NULL to indicate that the deque is empty.

4. Consider a queue as shown below.


Queue
FRONT REAR 1 2 3 4 5 6
1 2 2 1 AAA
2 1 3 2 BBB CCC XXX
3 0 0 3
4 5 1 4 FFF DDD EEE
5 4 4 5 GGG

The above queue is maintained by a two-dimensional array QUEUE.


a. Describe the structure after (PP,3), (RR,4), (SS,1), (MM,1), (II,4) and
(NN, 2) are added to the queue.
b. Describe the structure if, after the preceding insertions, four
elements are deleted.
Solution:
a. Insert each elements in its priority row. That is PP as the rear
element in row 3, and RR as the rear element in row 4, and SS as the
rear element in row 1 and add MM as the rear element in row 1, II as
the rear element in row 4 and NN as the rear element in row 2. This
yields the structure as shown below.

1 2 3 4 5 6
1 AAA SS MM
2 BBB CCC XXX NN
3 PP
4 FFF RR II DDD EEE
5 GGG

b. First delete the elements with the highest priority in row 1. Since
row 1 contains three elements AAA, SS and MM, then the front
element in row 2, BBB is also deleted. The resulting structure is as
shown below.

1 2 3 4 5 6
1
2 BBB CCC XXX NN
3 PP
4 FFF RR II DDD EEE
5 GGG

Please note that, in both cases the FRONT and REAR elements
are changed accordingly.

5. What would be the contents of queue Q after the following code is


executed and the following data are entered?
Q = createqueue
Loop (not end of file)
Read number
If (number not 0)
Enqueue(Q,number)
Else
Queuerear(Q,x)
Enqueue(Q,x)

The data are: 5, 7, 12, 4, 0, 4, 6, 8, 67, 34, 23, 5, 0, 44, 33, 22, 6, 0

Solution:
5, 7, 12, 4, 4, 4, 6, 8, 67, 34, 23, 5, 5, 44, 33, 22, 6, 6

6. What would be the contents of queue Q after the following code is


executed and the following data are entered?
Q=createqueue
S=createstack
Loop (not end of file)
Read number
If (number not 0)
Pushstack(S,number)
Else
Popstack(S,x)
Popstack(S,x)
Loop (not empty S)
Popstack(S,x)
Enqueue(Q,x)

The data are: 5, 7, 12, 4, 0, 4, 6, 8, 67, 34, 23, 5, 0, 44, 33, 22, 6, 0.

Solution
7, 5, 34, 67, 8, 6, 4, 33, 44

2.21 Recursion

A recursion is an important programming tool which can simplify the


design and coding of an operation. A subprogram invokes itself or invokes a series
of other subprograms that eventually invokes the first subprogram again is called
recursion.
Every recursive call must either solve a part of the problem or reduce the
size of the problem.
A general rule for designing a recursive algorithm is as follows:
1. First determine the base case
2. Then determine the general case
3. Combine the base case and general case into an algorithm.

For example, in mathematics, the factorial function of a non-negative integer


is usually defined by the following formula
n! = n x (n – 1) x (n – 2) x …….x 2 x 1
For more precise definition, we can also write

n! = {
1
n x (n - 1)!
if n = 0
if n > 0
Now, let us consider the case of 4! , since n>0, we use the second clause of
the definition
4! = 4 x 3!
= 4 x (3 x 2!)
= 4 x (3 x (2 x 1!))
= 4 x (3 x (2 x (1 x 0!)))
= 4 x (3 x (2 x (1 x 1)))
= 4 x (3 x (2 x 1))
= 4 x (3 x 2)
= 4 x (6)
= 24.
Such definition is called iterative because it calls for the explicit relation of
some process until a certain condition is met.
The C language routine for calculating factorial of n is as follows:
factorial(n)
int n;
{
int x;
if (n==0)
return 1;
else
x=n-1;
return(n*factorial(x));
}
Note that, the second return statement factorial calls itself. This is essential
for recursive routine. Therefore, a procedure call to itself or a procedure call to a
second procedure, which eventually causes the first procedure to be called, is
known as recursive procedure.
The general algorithm for recursive procedure is as follows:
1. Save the parameters, local variables and return address
2. If the base criterian has been reached, then perform the final computation
and go to step 3, otherwise perform the partial computation and go to step 1
with reduced parameter values (initiate a recursive call)
3. Restore the most recently saved parameters, local variables and return
address. Go to this return address.

Note:
One should not use recursion if the answer to any of the following
questions is no.
1. Is the algorithm or data structure naturally suited to recursion?
2. Is the recursion solution shorter and more understandable?
3. Does the recursive solution run in acceptable time and space limits?

2.21.1 Some more recursive algorithms

1. Multiplication of Natural numbers


2. Fibonacci Sequence
3. Greatest Common Divisor(GCD)
4. Towers of Hanoi problem
5. Ackermann function
2.21.1.1 Multiplication of Natural numbers
Here, the product a*b, where a and b are positive numbers, may be written
as a added to itself b times. This definition is iterative. We can write this definition
in a recursive way as:
{
a * b = aa * (b - 1) + a if b = 1
if b > 1
To evaluate 5 *3, we first evaluate 5 * 2 and add 5. To evaluate 5*2, we first
evaluate 5 * 1 and add 5. 5*1 is equal to 5 due to first part of the definition.
Therefore, 5 * 3 = 5 * 2 + 5
= 5 * 1 + 5 +5
=5+5+5
= 15
We can convert this recursive definition of multiplication of natural
numbers to C routine is as follows:
multiplication (a,b)
int a,b;
{
int c;
if (b==1)
return(a);
c=b-1;
return(multiplication (a,c)+a);
}

2.21.1.2 Fibonacci Sequence


The fibonacci sequence is the sequence of integers
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …….
Each element is the sum of the previous two numbers.
We may define the fibonacci sequence by letting fib(0)=0 and fib(1)=1, as
follows:
{ n
fib( n ) = fib(n - 2) + fib(n - 1)
if n = 0, 1
if n ≥ 2
This definition can be written in C as follows:
fib(n)
int n;
{
int a,b;
if (n<=1)
return n;
a=fib(n – 1);
b=fib(n – 2);
return(a+b);
}

2.21.1.3 Finding Greatest Common Divisor (GCD)

The greatest common divisor of two integers is defined as follows:


GCD (n, m) if n > m
GCD(m , n ) = m if n = 0
GCD (n, mod(m, n)) Otherwise
Here, mod(m,n) is the remainder on dividing m by n. The base case is when
the second argument is zero. In this case, the greatest common divisor is equal to
the first argument.
If the second argument is greater than the first, the order of argument is
interchanged. Finally, the GCD is defined in terms of itself. Note that, the size of
the argument is getting smaller as mod(m,n) will eventually reduce to zero in a
finite number of steps.
A C routine for computing GCD can be written as follows:
gcd(m,n)
int m,n;
{
int x;
if (n>m)
return (gcd(n,m));
if n=0
return(m);
x=m mod n;
return(gcd(n,x));
}

2.21.1.4 Towers of Hanoi Problem

Another complex recursive procedure is that towers of Hanoi problem. The


problem is as follows:
There are n disks of different sizes and there are three needles A, B and C.
All the n disks are placed on a needle (A) in such a way that a larger disk is always
placed below a smaller disk, which is shown below.

The other two needles are initially empty.


The aim is to move the n disks to the second needle (C) using he third needle
(B) as a temporary storage. The rules for the movement of disks as follows:
1. Only the top disk may be moved at a time
2. A disk may be moved from any needle to any other needle
3. A larger disk may never be placed upon a smaller disk

Now, let us see how to solve this problem.


CASE 1:
If there is only one disk, we can directly move from A to C.
CASE 2:
If there are two disks, we move the top disk to B and move the second disk
to C and finally move the disk from B to C.
In general, we can move n –1 disks from A to B and then move the nth disk
from A to C, finally we can move n – 1 disks from B to C.
Now, let us consider n= 4 disks.
We first move three disks from needle A to B using C as temporary. This is
shown below:

Then we move the fourth disk from A to C. This is shown below.

Finally we move the three disks from needle B to C using A as


temporary. This is shown below.

Therefore, a recursive solution can be formulated to solve the problem of


Hanoi to move n disks from A to C using C as temporary as follows:

1. If n=1, move the single disk from A to C and return


2. If n>1,
a. move the top n – 1 disks from A to B using C as temporary
b. Move the remaining disk from A to C
c. Move the n – 1 disks from B to C, using A as temporary

Now, let us convert this algorithm to C program. For that let us decide about
the input and output to the program. As an input to the program, we must give n,
the number of disks. Apart from number of disks, we must also specify the needles
which are to act as the initial needle from which we are moving disks, the final
needle to which we are moving disks and temporary needle.
We must also decide about the names of the disks. Let the numbering itself
represents the names of the disks. The smallest disk, which is on the top of the
needle is numbered 1, the next disk is 2 and so on. Such that the largest disk is
represented by number n.
The output from the program could be a list statements as:

Move disk nn from needle x to needle y.


The solution of the problem then would be to perform action according to
the output of the statement in exactly the same order that they appear in the
output.
The C program for the problem of towers of Hanoi can be written as follows:
#include <stdio.h>
void hanoi (int n, char initial, char final, char temp)
{
if (n==1)
{
printf(“Move disk 1 from needle %c to %c\n”,initial,final);
return;
}
honai(n – 1, initial, temp, final);
printf(“Move disk %d from %c to %c\n”, n, initial, final);
hanoid(n – 1, temp, final, initial);
}

void main()
{
int n;
printf(“Enter number of disks to be moved\n”);
scanf(“%d”,&n);
hanoi (n,’A’,’B’,’C’);
}

The output produced by the above problem when n=3 as follows:


Move disk 1 from needle A to needle C
Move disk 2 from needle A to needle B
Move disk 1 from needle C to needle B
Move disk 3 from needle A to needle C
Move disk 1 from needle B to needle A
Move disk 2 from needle B to needle C
Move disk 1 from needle A to needle C

2.21.1.5 Ackermann Function


The Ackermann function is a function with two arguments each of which
can be assigned any non-negative integers: 0, 1, 2,. …. This function is defined as
follows:

a. If m=0, then A(m, n) = n + 1.


b. If m ≠ 0 but n = 0, then A(m, n) = A(m – 1, 1)
c. If m ≠ 0 and n ≠ 0, then A(m , n) = A(m – 1, A(m, n – 1))

2.21.1.6 PROBLEMS:

1. Let a and b denote positive integers. Suppose a function Q is recursively


defined as follows:
{
Q(a , b) = 0
Q(a - b, b) + 1
if a < b
if b ≤ a
a. Find the value of Q(2, 3) and Q(14, 3)
b. What does this function do? Q(5861, 7).

Solution:
a. Q(2,3) = 0, since 2 < 3.
Q(14, 3) = Q(11, 3) + 1
= [Q(8, 3) + 1] + 1 = Q(8, 3) + 2
= [Q(5, 3) + 1] + 2 = Q(5, 3) + 3
= [Q(2, 3) + 1] + 3 = Q(2, 3) + 4
=0+4=4

b. Each time b is subtracted from a, the values of Q is increased by 1. Hence


Q(a,b) finds the quotient when a is divided by b. Thus, Q(5871, 7) = 837

2. Use the definition of Ackermann function, find A(1,3)

Solution:

a. If m=0, then A(m, n) = n + 1.


b. If m ≠ 0 but n = 0, then A(m, n) = A(m – 1, 1)
c. If m ≠ 0 and n ≠ 0, then A(m , n) = A(m – 1, A(m, n – 1))

Here, m = 1 and n = 3

A(1, 3) = A(0, A(1, 2))


A(1, 2) = A(0, A(1, 1))
A(1, 1) = A(0, A(1, 0))
A(1, 0) = A(0, 1)
A(0, 1) = 1 + 1 = 2
A(1, 0) = 2
A(1, 1) = A(0, 2) = 2 + 1 = 3
A(1, 2) = A(0, A(1, 1))
= A(0, 3) = 3 + 1 = 4
A(1, 3) = A(0, A(1, 2))
= A(0, 4) = 4 + 1 = 5

3. Consider the following algorithm.


Algorithm fun1 (int x)
If (x < 5)
Return(3 *x)
Else
Return(2*fun1(x – 5)+7)
End fun1.

What would be returned if fun1 is called as


a. fun1(4)?
b. fun1(10)?
c. Fun1(12)?

Solution:
a.
3*4 = 12

b.
(2*fun1(5) + 7)
= (2 * (2 *fun1(0) +7) + 7)
= (2 * (2*(3 * 0) +7) + 7)
= 21
c.
(2* fun1(7)+7)
= (2* (2* fun1(2)+7) + 7)
= (2* (2* (3*2) + 7) + 7
= 45

4. Consider the following algorithm.


Algorithm fun2 ( int x, y)
If (x > y)
Return –1
Else if (x==y)
Return 1
else
Return(x*fun2(x+1, y))
End fun2.
What would be returned if fun2 is called as
a. fun2(10, 4)?
b. fun2(4, 3)?
c. fun2(4, 7)?
d. fun2(0, 0)?

Solution:
a.
-1
b.
-1
c.
(4 * fun2(5, 7))
= (4 * (5 * fun2(6, 7)))
= 4 * (5 * (6* fun2(7, 7)))
=4*5*6*1
= 120
d.
1

Review Questions

1. How is a 2 dimensional array represented in row major order?


2. What is meant by the terms ‘row-major order and column-major
order’?
3. The array DATA [10,15] is stored in memory in row-major order. If
base address is 200 and element size is 1. Calculate the address of
element DATA[07,12].
4. What is an empty linked list? How do you check, whether a given
linked list is empty?
5. Write the applications of doubly linked list.
6. What are the basic operations on a data structure?
7. What is called multilinked structure? Explain.
8. Write an algorithm to insert an item at the beginning of the linked
list.
9. Define data structure.
10. What are the advantages of linked lists over arrays?
11. Give the differences between a queue and a circular queue.
12. What are the advantages of linked list over linear list.
13. Compare a list and an array.
14. Discuss in detail the algorithmic notations with example.
15. Formulate an algorithm that searches for an item in a linked list that
is unsorted.
16. Explain any one control structure in PSEUDO linked list.
17. Write an algorithm to insert, delete and search an item in a doubly
linked list.
18. Give the implementation of circular linked list.
19. Give the implementation of stack using linked list.
20. Describe the process of evaluation of postfix expression using stack.
Illustrate with an example.
21. Give the recursive algorithms for preorder, postorder and inorder
traversals.
22. Perform the three traversals on the expression (a-b+c*e/f) and
explain.
23. What are the basic operations performed on a list?
24. What are associative lists?
25. Explain with an example how arrays can be used to represent a
linked list? What are its limitations?
26. What is a header node in linked list? What are its uses? Give an
algorithm to add an element to a circularly linked (singly) list with
a header node.
27. What are the primitive data structures used in a computer?
28. What do you mean by garbage collection?
29. What are the uses of priority queues?
30. What is an array?
31. Convert the following expression to postfix expression:
A+B*C/D+E
32. What is the purpose of stack in implementing a recursive
procedure?
33. Explain how arrays are useful in storing sparse matrices. Give an
algorithm to transpose a sparse matrix stored in three tuple form.
34. Explain how a linked list can be used to represent sparse
polynomials?
35. What is external fragmentation?
36. Write procedures to add and delete an element from the stack.
37. Explain the various methods of implementation of lists.
38. Write algorithms to insert and delete a node into and from circular
list.
39. Write procedures for implementing queue operations using linked
list.
40. Write and explain the procedure for evaluating a postfix expression
using stack with an example.
41. Differentiate between list and array.
42. What is a linear list?
43. Write an algorithm to delete and insert an element in the doubly
linked linear list.
44. Write the advantages of circular list with example.
45. What do you mean by multi-dimensional arrays?
46. Give the structure of a circular linked list.
47. List any two applications of stack.
48. Write the pseudo code to delete an element from a queue.
49. Write procedures for implementing stack operations peep and
change.
50. Write an algorithm to insert an element in a circular queue.
51. Write a recursive procedure for finding the greatest common
divisor (GCD) of any two numbers.
52. What is priority queue.
53. Discuss the operations that can be performed on a queue.
54. Describe the implementation of a priority queues with an
illustrative example.
55. What is a stack?
56. Explain the use of stacks in a recursive procedure to solve the
“Tower of Hanoi” problem.
57. Give an algorithm that can be used to evaluate an expression given
in the postfix form.
58. Explain non-recursive method for implementing tower of Hanoi.
59. Convert the expression a+3(b-2)/(5c+76/2) into postfix notation.
60. Define recursion.
61. Define ‘prefix’ and ‘infix’ notation.
62. What is backtracking?
63. Explain how the following infix expression is evaluated with the
help of stack: 5*(6+2)-12/4.
64. Explain the procedure to delete a node with a given item of
information in a singly linked list.
65. What is sparse matrix?
66. Consider the following stack of characters, where STACK is
allocated N=8 memory cells. STACK : A,C,D,F,K,-,-,-. ‘-‘ DENOTES
ON EMPTY MEMORY CELL. Describe the stack as the following
operations takes place:
i. POP(STACK,ITEM)
ii. POP(STACK,ITEM)
iii. POP(STACK,ITEM)
iv. PUSH(STACK,L)
v. PUSH(STACK,P)
vi. PUSH(STACK,R)
vii. POP(STACK,ITEM)
viii. POP(STACK,ITEM)
ix. PUSH(STACK,S)
x. POP(STACK,ITEM)
67. Discuss different data types?
68. Discuss: Evaluation of arithmetic expression using stack.
69. With example, show how a linked list can be used to represent
sparse polynomials.
70. Why are parenthesis needed to specify the order of operations in
infix expressions but not in postfix expressions?
71. Why are queues that are housed in arrays are represented in
circular rather than linear fashion?
72. Convert the following infix expression to postfix expression.
((A+B)-((C+D)*E)/F)*G
73. What is the value of the following postfix expression?
546+*493/+*
74. Write short notes on:
a. Linked list implementation of a stack
b. Circular implementation of queue.

Vous aimerez peut-être aussi