Kamthane-Data Structures Using C-2012
Kamthane-Data Structures Using C-2012
USING C
Ashok N. Kamthane
Associate Professor
Department of Electronics and Telecommunication
Associate Dean (Academics)
SGGS Institute of Engineering and Technology
Nanded, Maharashtra
No part of this eBook may be used or reproduced in any manner whatsoever without the publisher’s
prior written consent.
This eBook may or may not include all assets that were part of the print version. The publisher
reserves the right to remove any material in this eBook at any time.
ISBN 9788131765067
eISBN 9788131776148
Head Office: A-8(A), Sector 62, Knowledge Boulevard, 7th Floor, NOIDA 201 309, India
Registered Office: 11 Local Shopping Centre, Panchsheel Park, New Delhi 110 017, India
Acknowledgements
I express my sincere gratitude towards my beloved father, Late Shri Namdev Kamthane; my grandfather,
Late Shri Jagganath Kamthane; and my mother, Late Shrimati Sumanbai Namdev Kamthane. It is because
of their blessings that I could write this book in addition to several other technical books.
I am grateful to the members of the Board of Governors of our institute for encouraging and inspiring
me to write this book. I thank the chairman, Mr Baba N. Kalyani, the directors, Dr S. R. Kajale and
Shri Kamlesh Pande for their support.
I thank all the students, faculty, and non-teaching staff of this college who either directly or indirectly
helped me to complete this book. I also take this opportunity to thank the editorial team at Pearson
Education for their support.
Thanks are also due to my wife Surekha who supported me patiently during the preparation of this
book. Last, but not the least, my sons, Amol and Amit; daughter, Sangita; and daughter-in-law, Swaroopa,
were also of great help and supported me at all times. I thank them all.
Ashok N. Kamthane
Module II Dynamic storage management: garbage collection and compaction; infix to postfix conver-
sion; postfix expression evaluation. Trees: tree terminology, binary tree, binary search tree,
general tree, B+ tree, AVL tree, complete binary tree representation, tree traversals, operation
on binary trees, expression manipulation.
Module III Graphs: graph terminology, representation of graphs, path matrix, breadth first search
(BFS), depth first search (DFS), topological sorting, Warshall’s algorithm (shortest path
algorithm); sorting and searching techniques: bubble sort, selection sort, insertion sort,
quick sort, merge sort, heap sort, radix sort; linear and binary search methods; hashing
techniques and hash functions.
Students will attend three hours of lecture classes per week. This course carries three credits.
Introduction to Data
Structures
CHAP TER O U T LIN E
1.1 Introduction 1.9 Hardware and Software
1.2 Data and Information 1.10 Concept of Data Types
1.3 Overview of Data Structures 1.11 Data Types in C
1.4 Types of Data Structures 1.12 Abstract Data Types
1.5 Primitive and Non-primitive Data 1.13 Pointers
Structures and Operations
1.14 Structures in C
1.6 Binary and Decimal Integers
1.15 Unions
1.7 Logical Information
1.16 Algorithms
1.8 Storage of Information
1.1 INTRODUCTION
Humans use different languages to communicate data and information with each other. Besides, thoughts
and feelings can also be communicated to others using language. The newspaper, television and other
media use different languages for communication of data/information. In all occupations, language plays
a vital role. In fact, with the help of languages humans interact with each other.
Data is represented in the form of text or numbers or in the form of figures, tables, graphs, pictures,
etc. The data can be stored in physical devices such as note books and in memory as string, number,
characters, etc. A computer language is incomplete without data.
Information is derived from data. The following information can be drawn if data is provided:
Method to
Data Information
Process Data
1. Everyday newspapers are delivered to our houses by the service man. It may be The Times of India or
the Indian Express, and some other local language newspapers such as Lokmat and Janjagruti. The cost
of each newspaper is different. How many papers have been received can be recorded in a notebook.
This task is needed for calculating the total bill at the end of the month. The data here are the names
of the newspapers, the dates on which they have been received and the cost of each. This data is needed
to calculate the monthly or fortnightly bill. The total bill for the English newspapers as well as for
the other papers can be calculated and analysed. One can draw different kinds of inferences such as
whether the family is spending money on English newspapers or not and if so, how much. Does the
family have educated members? Is the amount incurred on newspapers more than that on food? Data
provided can be analysed for extracting different types of information.
2. Information about any human can be expressed with his/her name, age, gender, phone number, city,
etc., and can be stored in the form of a table, which is as shown in Table 1.1.
3. Consider Fig. 1.2. A and B are two stations and distance between them is 100 km by train. The distance
between them is certainly more if travelled by road and shorter by plane. Here, the distance between the
two stations can be indicated by connecting two cities by train, road, or air. The distances, both relative
and absolute, between the cities are nothing but the data. This information helps a person to decide
the mode of travel from station A to B. One can decide to go by train or bus depending upon the need,
urgency and convenience. It should be clear now that data and information are not the same.
100 km
A B
4. From the score of an individual cricket player we can draw information. Suppose, data consisting of
the runs scored by a player in one-day cricket matches are provided. From the given data, the following
information can be retrieved:
1. Total runs scored
2. Highest score
3. Lowest score
4. Average
5. Best performance
6. Strike rate
7. Number of sixes
8. Number of fours
9. Number of forward shots
10. Runs due to wide balls.
Information helps planners/analysts to make decisions. In this chapter, we are going to discuss applica-
tion of data and information related to the computer.
A computer is an electronic programmable device that manipulates information presented in the form
of data. Computer science is mainly related to the study of data structures and their applications. The
student of computer science should be aware of how data is manipulated in a system and the meth-
ods for utilization of data. It is, therefore, very essential to study the concepts of data organization and
manipulation.
Data “Data is nothing but a collection of numbers, alphabets and symbols combined to represent information.”
Data stands for value or group of values. Table 1.2 describes the common data types used in the computer.
Entity An entity possesses some attributes and some values can be allocated to it. For example, a student
is an entity of a college. The probable properties and the corresponding values are indicated in Table 1.3:
Every student in a college is an entity with different attributes. Sets of attributes are known as domain.
Domains refer to possible values of a particular property. For example, the gender property can be assigned
with values M or F.
The study of computer science deals with storage, retrieval, handling and organization of information.
Information is generated in every sphere of life. For example, information about business transactions, or a
student’s performance in an examination, can be stored in the computer memory. The same information can be
retrieved, modified, and restored. The total marks of a student can be obtained by adding the marks scored in
all subjects. Similarly, the average percentage of marks can be computed and student performance can be moni-
tored and evaluated using different factors such as total marks, average marks, highest score, and lowest score.
Information is symbolic representation. It has some useful meaning that helps us in making good judgments.
Data structures are a method of representing of logical relationships between individual data elements
related to the solution of a given problem. Data structures are the most convenient way to handle data
of different types including abstract data type for a known problem. For example, the characteristics of
a house can be represented by the house name, house number, location, number of floors, number of
rooms on each floor, kind of fencing to the house—either with brick walls or wire, electrification—either
underground or open, whether a balcony has been provided or not, etc. One can use a variety of data
types to represent these data elements while solving a problem with the help of computer language such
as char, int, boolean type, etc. Figure 1.3 shows the various fields of house. For example, with the help
of computer the characteristics of a house can be represented: the number of floors of a house can be given
with either int or char type. The house number, the number of rooms in it can be declared with the same
type, provided that the data is within the limits of the range of data type. In C language, the char data type
House
works perfectly as integer data in the range from −128 to 127 and as unsigned char from 0 to 255. Some-
times, selection of data type is important. Just as, for example, if we are going to fence the house should
we use brick or wire. Here, the brick wall may be expensive. Likewise, if we are using signed int instead
of char to store a positive number (number of floors) we are wasting one byte of the system. Hence, here
char is appropriate, which occupies only one byte.
Electrification should be declared with char data type. Like the above example, in the practical appli-
cation of data structures, to create data structures some components are involved directly or indirectly
to build the system. In other words, data structures are a structured set of variables associated with one
another in different ways, co-operatively defining components of the system.
The components of data can be organized and records can be maintained. Further, the record forma-
tion leads to the development of abstract data type and database systems.
In data structures, we also have to decide on the storage, retrieval and operation that should be carried
out between logically related items. For example, the data must be stored in memory in computer-
understandable format, i.e. 0 and 1 and the data stored must be retrieved in human-understandable
format, i.e. ASCII. In order to transform data various operations have to be performed.
A data structure is a structured set of variables associated with one another in different ways, co-opera-
tively defining components in the system and capable of being operated upon in the program. As stated
earlier, the following operations are done on data structures:
1. Data organisation or clubbing
2. Accessing technique
3. Manipulating selections for information
Data structures are the basis of programming tools and the choice of data structures should provide the
following:
1. The data structures should satisfactorily represent the relationship between data elements.
2. The data structures should be easy so that the programmer can easily process the data.
Data structures have been classified in several ways. Different authors classify it differently. Figure 1.4
(a) shows different types of data structures. Besides these data structures some other data structures such
Data Structures
Linear Non-linear
as lattice, petri nets, neural nets, semantic nets, search graphs, etc. can also be used. The reader can see
Figs. 1.4 (a) and (b) for all data structures.
1 Rear
Front
2
4 Top
∗
∗ 3
∗ Header Queue
2
n
1
Arrays
Stack 30 ∗ 40 ∗ 50 ∗
N1 N2 Linked List N 3 Nn
Tree Graph
Table Sets
Linear In linear data structures, values are arranged in linear fashion. Arrays, linked lists, stacks and
queues are examples of linear data structures in which values are stored in a sequence.
Non-Linear This type is opposite to linear. The data values in this structure are not arranged in order.
Tree, graph, table and sets are examples of non-linear data structures.
Homogenous In this type of data structures, values of the same types of data are stored, as in an array.
Non-homogenous In this type of data structures, data values of different types are grouped, as in
structures and classes.
Dynamic In dynamic data structures such as references and pointers, size and memory locations can be
changed during program execution.
Static Static keyword in C is used to initialize the variable to 0 (NULL). The value of a static variable
remains in the memory throughout the program. Value of static variable persists. In C++ member functions
are also declared as static and such functions are called as static functions and can be invoked directly.
The most commonly used operation linked with data structures is selection, which is used by programmers
to access the data within data structures. The selection relationship depends upon yes/no. This operation
updates or alters data. The other three operations associated with selections are:
1. Sorting
2. Searching
3. Merging
Searching operations are used to seek a particular element in the data structures. Sorting is used to
arrange all the elements of data structures in the given order: either ascending or descending. Chapter
11 discusses sorting and searching in detail. Merging is an operation that joins two sorted lists.
An iteration relationship is nothing but a repetitive execution of statements. For example, if we want
to perform any calculation several times then iteration, in which a block of statements is repetitively
executed, is useful.
One more operation used in combination with data structures is update operation. This operation
changes data of data structures. An assignment operation is a good example of update operation.
For example,
int X=2;
Here, 2 is assigned to x.
X=4;
Data is expressed in terms of 0 and 1 known as bits. Different number systems are used to represent
numbers. For example, decimal, octal and hexadecimal number systems, can be used. The decimal
system uses ten different symbols from 0 to 9, octal uses 0 to 7, hexadecimal from 0 to 9 and fur-
ther from A to F. We can convert from one number system to another. To convert a decimal number
to binary, the double dabble method is used. In this method, the decimal number is divided by 2
and remainders are listed in reverse direction. The binary number obtained from this method is the
equivalent of the decimal. For example, the binary 111 is nothing but decimal 7. In the binary number
system, every bit has its own weight. The least significant bit has 20 weight. Weight position is calcu-
lated to the power of 2. The right-most bit of a binary number has a value of 1, which is represented
as 20. The next bit position has value 21 and so on and the values for further bits can be calculated.
In other words, weights are assigned to each bit in binary. The weights from the least significant bits
are 1,2,4,8----.
For example, the equivalent binary representation of 5 is 101. This is explained as follows:
1 × 22 + 0 × 21 + 1 × 20
4 + 0 + 1 = 5
The power of two (weights) and binary bits are multiplied and the sum of all multiplications is taken
to give the equivalent decimal number. There are two commonly used methods for representing negative
binary numbers; they are as described in the following sections.
1.6.4 Integers
We know what integers are and are aware of the arithmetic operations performed with them such
as addition, subtraction, multiplication, and division. Integer-type data plays an important role in
calculation. A counting of the number of objects can be shown by integers. The amount in a bank
account, the number of students in a class and number of keys on a keyboard-all this information can
be expressed in integers.
The traditional method of writing negative numbers is to put a sign symbol before the number. This
method is called as sign and magnitude method and is commonly used in several computers to represent
signed numbers. Normally, the sign is shown at the first or left-most bit of the binary number representa-
tion. In addition, the magnitude part appears followed by the sign.
The left-most bit is only for indicating the sign, but it does not have any weight as in an unsigned
number system. In other words, a bit string beginning with zero shows a positive number whereas a
bit string beginning with one indicates a negative number, if numbers are to be represented in a signed
format.
In Table 1.5, 0 and 1 are used to represent positive and negative numbers. An integer shown with
its sign is known as a signed integer. There are two types of signed integers: a) positive signed integer
b) negative signed integer. We studied that these signs are represented in memory by 0 and 1 as shown
in Table 1.5. The sign bit is always written as the leftmost bit. While storing signed numbers, the system
reserves the leftmost bit for representation of the sign.
Positive signed integers are shown in the form called the signed magnitude form. In this style, in the
leftmost bit, the sign is shown by 0 and magnitude is shown in the matching binary form.
For example, +7 is shown by 0111.
Negative signed numbers are shown in one of the following forms:
In the signed magnitude form method, 1 shows the sign of the number and magnitude is shown by the
corresponding binary form. For example:
Table 1.6 shows the various ways of representing binary and negative numbers.
Table 1.6 Binary, Negative Numbers in Signed Format and 1’s Complement of
Negative Numbers
Decimal Binary Decimal Signed Numbers 1’s Complement
+0 0000 −0 1000 1111
+1 0001 −1 1001 1110
+2 0010 −2 1010 1101
+3 0011 −3 1011 1100
+4 0100 −4 1100 1011
+5 0101 −5 1101 1010
+6 0110 −6 1110 1001
+7 0111 −7 1111 1000
Positive and negative numbers are shown in Table 1.6. Signed numbers and signed −1’s complements
of negative numbers are also shown in the table. The sign is shown by 1 and the magnitude is shown in
the 1’s complement form. −7 is shown as 1,000.
In the signed −2’s complement form, the sign of the number is shown by 1 and the magnitude is shown
in the 2’s complement form. The 2’s complement of a number is the addition of 1’s complement of the
particular number and 1. Table 1.7 shows 2’s complement of the numbers. 2’s complement of −7 is shown
as 1,001.
are several notations or methods to represent real numbers. Each method is unique and has different
characteristics.
The real number has two parts: mantissa and base. It is represented by a number called mantissa. The
base portion is always raised to the number and it is called the exponent. The base is always fixed.
The mantissa and exponent vary. Different values of mantissa and exponent give different real numbers.
For example,
The base is 10; the number 221.98 would be 22198 × 10−2. The mantissa is 22198, exponent is −2. The
values can also be written as .22198 × 103, 221.98.
The benefit of floating-point notation is that it can be used to show numbers with small or large abso-
lute values. The biggest number that can be represented is 223−1 × 1027. In addition, the smallest positive
number may be 10−128, which is very small. The limit of precision on a computer is the number of signifi-
cant binary digits in the mantissa.
Example 1.1 Write a program to declare float and double type values and display them.
# include <stdio.h>
# include <conio.h>
void main()
{
float f=31343412.123;
double d=46481232.77;
clrscr();
printf("f=%lg",f);
printf("\nd=%lg",d);
}
OUTPUT
f = 3.13434e+07
d = 4.64812e+07
Explanation:
In this program the variables f of float and d of double type are declared and are initialized. The
values are displayed using base and mantissa format.
Information contains all types of data, viz., numbers, characters, symbols, etc. Hence, it is not possible
to represent information by only numeric data. Information such as names and book titles are required
to be represented with characters. Thus, all non-numeric information is represented by a sequence of
characters called strings.
For example, in some systems 00100110 is used to show the ‘$’ symbol and different types of such
bit patterns represent ‘A’, ‘C’, ‘D’ and so on. In different countries, the computer may have a few addi-
tional symbols. The character set can be created using fonts. The programmer can create different fonts
according to the language and culture of the country.
We know that 8 bits are used to represent a character and 256 different characters can be shown using
these unique bit combinations. Suppose, the string 1101001 is used to represent character ‘A’ and 0100001
is used to represent ‘B’, then the string “AB” can be shown by the bit string 11010010100001. We know
that a string is sequence of a characters and a character string is shown by concatenation as the bit strings,
which represent separate characters of the string. Examples of strings are illustrated in Chapter 2.
Primitive data structures also include logical data, i.e. true or false. Only two logical constants, true and
false are present. In C /C++programming 0 is assumed as false and any non-zero value as true. In C++,
a new data type bool is introduced to handle logical conditions.
Expressions containing logical variables are used with relational operators such as <, >, ==, = etc.
The result of such expression is only true (1) or false (0). The result is returned by the expression.
The storage representation of logical values depends upon the compiler or interpreter used by the
language to convert the source program to a machine code. Normally, one bit is enough to store logical
information. In C/C++ it is possible to store information in bits. The following programs explain true and
false values returned by expressions and storing data using bits.
Example 1.2 Write a program to display logical values returned by logical expressions.
# include <stdio.h>
# include <conio.h>
void main()
{
clrscr();
printf("False : %d\n", 3>5);
printf("True : %d",5>1);
}
OUTPUT
False : 0
True : 1
Explanation:
The two expressions are put in the printf () statement. The first condition 3 > 5 is false;
hence, the return value is 0. The second expression 5 > 1 returns 1, i.e. the expression is true.
Consider the following program, which stores return values in bits using structure.
Example 1.3 Write a program to identify the entered IP address. Display its class, mask
and last address.
# include <stdio.h>
# include <conio.h>
void main()
{
int ip0,ip1,ip2,ip3;
clrscr();
OUTPUT:
Explanation:
In this program the IP address is stored in the ip0, ip1, ip2 and ip3 variables. For each class
a separate if condition is used and the relevant values are displayed on the screen.
Example 1.4 Write a program to store logical values in bit using structure.
# include <stdio.h>
# include <conio.h>
void main()
{
struct bits
{
int state :1;
};
struct bits a;
a.state=13>5;
clrscr();
if(a.state==0) printf("False");
else printf("True : %d",5>1);
}
OUTPUT
True: 1
Explanation:
In the structure bits, the number of bits given is to be used by the member of structure to store
values. The value returned by the expression 13 > 5 are stored in variable state that stores the
value in one bit. The if statement checks the value and the appropriate message is displayed.
The digital computers have two types of memory: operational memory and storage memory. The
operational memory refers to CPU registers. The CPU registers are temporarily used to store values.
The CPU contains registers called accumulators that contain the variables while arithmetic operations
are performed. For example, when numbers are added the result is stored in AX register of the CPU. The
following program explains this.
# include <stdio.h>
# include <conio.h>
void main()
{
int x=10,y=2;
clrscr();
_AX=x*y;
printf("\n Multiplication of x and y is %d",_AX);
_AX=x+y;
printf("\n Addition of x and y is %d",_AX);
}
OUTPUT
Multiplication of x and y is 20
Addition of x and y is 12
Explanation:
In this program, integer variable, x and y are declared and initialised with 10 and 2. The statement
x * y calculates the product and x + y calculates the addition of the variables. The obtained
result is stored in register AX. We print the value using pseudo variable _AX.
In addition, CPU registers are also used to store ‘temporarily’ program instructions and program control
information, which is helpful in program execution. Therefore, registers are only used to hold data
temporarily.
The storage type memory is used for permanent storage of data that can be retrieved at any time.
Before performing an arithmetic operation, the value of a variable is stored in the memory unit and then
transferred to the register. If the obtained result is to be stored in another variable, it is necessary to again
transfer values from register to storage memory.
When a program execution is started, instructions and data are stored in storage units. The whole
storage unit of computer is called main memory. Information is an input to the storage unit.
The memory of the computer is nothing but a group of bits. The values 0 and 1 are called bits. The unit
formed by bits is called value, i.e. a group of bits represents a value. A group of 8 bits is called byte. Many
bytes when grouped together are called word. Every byte in a memory has a unique address. The address
is useful to identify the byte and its contents. In C it is possible to access the byte address. The address is
always numeric (unsigned integer) and a not-negative number. The address is also called memory location.
The computer system has a built-in mechanism that handles and manipulates different types of bit patterns
according to the object or variable that holds the value.
For example, the computer contains an instruction to multiply two binary numbers and place the
obtained value in another memory location. The mechanism to perform such an operation should have
the following steps:
The computer hardware is bit oriented whereas software is byte oriented, i.e. the computer hardware
is capable of interpreting the bit patterns at a specified memory location. This is what we discussed con-
cerning an operation between two integers. In case two real values are given, the computer hardware will
apply another built-in mechanism to handle this data type. As discussed earlier, to handle native data type,
necessary routines are in-built into the hardware. The instruction identifies the data type by value or its
address. The values are assigned explicitly and addresses are allocated implicitly by the system.
Consider the following C/C++ statements.
int j=4, k=5,h;
float p=9.1, q=5.5, r;
In the above statements, variables are declared and memories in bytes are allocated to them. j, k, p, h, r
and q are called variables or identifiers. The variable name is useful to store values. A variable is nothing but
a name given to memory location. The values stored in j and k will be interpreted as integers whereas the
values of p and q will be interpreted as float. The C/C++ compiler translates the source program to machine
language (object code).
Consider the following statements:
h=j+k;
r=p+q;
The first statement performs addition of j and k integer variables and stores the result in h. In the
second statement addition of p and q are stored in variable r.
The operator ‘+’ is common. Operators such as +,*,-,/ are overloaded in such a way that they can be
used with any data type to perform an operation. All these operators are generic, i.e. they can be used with
all standard data types.
In high-level language, declaration of variable plays an important role. The declaration contains data
type name followed by variable list. The variables are allocated memory according to the data type.
For example, integer variable requires two bytes; float variable requires four bytes, etc.
Refer Table 1.8 for C data types. The first column contains the name of the data type. These names are
reserved words known as keywords. The second column has the number of bytes required by a variable of
data type. The number of bytes may vary in different systems. The third column contains a range of data
types, i.e. lowest and highest limits of numbers that can be stored in variables of respective data type.
A data type is a term that refers to the type of data values that may be used for processing and computing.
The information stored in memory is in the form of bits 0 and 1. We can view the memory in human-
understandable format with the help of data type. The type of computer hardware decides the data type
and the range it supports. Range means the largest and smallest possible values that can be represented.
Our goal is to study how a user can utilize the data type to represent information. We need not think
about what the computer does internally.
Suppose that data types are independent of computer hardware, then unlimited data types can be
built. A data type is nothing but an abstract concept designed using logical operators. After designing
such data type and its supporting operations we can use the data type. The instructions regarding the
operation related to data type can be given by hardware and software. The necessary instructions are
built into the computer hardware as hardware implementation. When a program gives such instructions
it is known as software implementation. The instructions given in the computer program interpret the
bit patterns.
Various data types are used in the C language. Variables are used for assigning integers, long integers, or
characters, etc. We can differentiate between data types such as integer, real, logical, complex variables, etc.
C contains four standard data types. They are int, float, char and double. Almost in all computers, all the
above four data types are local to computer hardware. We have already discussed how integers, floats and
characters are stored in memory and their manipulation. A double variable is a double precision floating-
point number.
In addition, there are three qualifiers applicable to int. They are short, long and unsigned. When a vari-
able is declared unsigned, it can store only positive values. By default when a variable is declared as int, it
is short int. Its range is −32,768 to 32,767. To store values above this range, a long qualifier is used and
its range is −2147483648 to 2147483647. An unsigned integer is always an absolute value and follows
arithmetic laws of module 2n, where, n is the number of bits in an integer. The ranges of data types and
number of bytes needed to store them are shown in Table 1.8.
In programming, a situation occurs when built-in data types are not enough to handle the complex data
structures. It is the programmer’s responsibility to create this special kind of data type. The programmer
needs to define everything related to the data type such as how the data values are stored, the possible
operations that can be carried out with the custom data type and that it must behave like a built-in type
and not create any confusion while coding the program. Such custom data types are called Abstract data
type. In C struct and in C++ struct/class keywords are used to create abstract data type.
For example, if the programmer wants to define date data type which is not available in C/C++, s/he
can create it using struct or class. Only a declaration is not enough; it is also necessary to check whether
the date is valid or invalid. This can be achieved by checking using different conditions. The following
program explains the creation of date data type:
# include <stdio.h>
# include <conio.h>
struct date
{
int dd;
int mm;
int yy;
};
void main()
{
struct date d; /* date is abstract data type */
clrscr();
printf("Enter date(dd mm yy) :");
scanf("%d %d %d",&d.dd,&d.mm, &d.yy);
printf("Date%d-%d-%d",d.dd,d.mm,d.yy);
}
OUTPUT
Enter date(dd/mm/yy): 25 10 2011
Date 25-10-2011
Explanation:
In this program, using struct keyword the date data type is declared. It contains three-integer
variables dd, mm and yy to store date, month and year. Through scanf statement date, month
and year is entered and it is displayed using printf statement.
1.13 POINTERS
A pointer is a link or reference to data structures. The most important characteristic of pointer is that it allows
identical method of referencing any data structures, no matter of what complexity and type. The pointer can
point to any data type character, float, int, etc., and the method of accessing elements are the same. A pointer
also allows quick insertion and deletion of an item in a list. The linked list is discussed later in this book.
There are two techniques of accessing data structures.
2. The indirection operator (*) is also called the dereferencing operator. When a pointer is dereferenced,
the pointer retrieves the value stored at that address.
3. The indirection operator (*) is used in two distinct ways with pointers, declaration and deference.
4. When the pointer is dereferenced, the indirection operator indicates that the value at that location
stored in the pointer is to be accessed rather than address.
5. The ‘&’ operator is an address operator and it gives the address of the variable. The ‘&’ immediately
preceding the variable returns the address of the variable.
# include <stdio.h>
# include <conio.h>
void main()
{
int x=2;
int *p;
clrscr();
p=&x;
printf("\n Address of x is = %u", &x);
printf("\n Value of x is = %d",*p);
}
OUTPUT
Address of x is = 65524
Value of x is = 2
Explanation:
In this program, an integer variable x is declared and assigned with value 2. Also, integer pointer
*p is declared and initialized with address of x. The first printf statement displays the address
of x. The second statement displays the value of x using pointer p.
Arithmetic operations on pointer variables are also possible. Increase, decrease, prefix and postfix
operations can be performed with the help of pointers.
The following operations are not possible with address:
1. Addition of two addresses (pointers).
2. Multiplication of two addresses or multiplication with a constant.
3. Division of address with a constant.
1.14 STRUCTURES IN C
In this topic, we will discuss C data structures called structures. A structures is a collection of one or
more variables of different data types, grouped together under a single name and new custom data type
formed. The individual variables are called member variables. By using structures, we can make a group
of variables, arrays, pointers, etc. The new data type formed by the structures is independent of computer
hardware and we already studied that when data type is independent of computer hardware, unlimited
data types can be formed. Now, consider the following declaration of structure and program to understand
the concept.
struct student
{
char name[20];
int age;
float wt;
};
In this example, the structure student is declared by combining three standard data types. The structure
name is called as tag name. Here, three data types can be accessed separately and hence they are not mixed.
Consider the following example,
Example 1.8 Write a program to demonstrate how structures are used to store and display data.
# include <stdio.h>
# include <conio.h>
struct student
{
char *name;
int age;
float weight;
};
void main()
{
struct student s1;
s1.name="Nilesh";
s1.age=25;
s1.weight=55;
clrscr();
printf("\n Name = %s",s1.name);
printf("\n Age = %d",s1.age);
printf("\n Weight = %g",s1.weight);
}
OUTPUT
Name = Nilesh
Age = 25
Weight = 55
Explanation:
In this program, structure is defined and a custom data type is formed. Here, the program
contains information regarding data type; hence, it is called software implementation.
Example 1.9 Write a program to demonstrate that the size of structure variable is equal to the sum
of sizes of member variables.
# include <stdio.h>
# include <conio.h>
void main()
{
struct data
{
char ch;
int in;
float fl;
};
struct data d;
clrscr();
printf("\nSize of character ch = %d",sizeof(d.ch));
printf("\nSize of integer in = %d",sizeof(d.in));
printf("\nSize of float fl = %d",sizeof(d.fl));
printf("\nSize of object d = %d",sizeof(d));
}
OUTPUT
Size of character ch = 1
Size of integer in = 2
Size of float fl = 4
Size of object d = 7
Explanation:
In this program structure data is declared with three standard data types: char, int and
float. d is a variable of data type. Using sizeof () operator size of every member, variable
d is calculated and displayed. The size of variable d is the sum of the sizes of all members.
The output gives a clear idea.
1.15 UNIONS
Union is a user defined data type very similar to structure. It contains members like structure but no
separate memory is allocated to each member. The size of the largest element is detected and this much
amount is allocated. Members of a union share common memory locations. The union data structure is
useful to invoke BIOS and DOS services. An example follows:
# include <stdio.h>
# include <conio.h>
# include <dos.h>
void main()
{
union REGS in,out;
int86(18,&in,&out);
clrscr();
printf("\n Total memory = %d KB", out.x.ax);
}
OUTPUT
Total memory = 640 KB
Explanation:
In this program union is in use. int86() is a function. The value 18 is a service number. The
function calculates the total size of memory and it is displayed by printf () statement. While
discussing types of data structures, we discussed static data structures. Consider the following
example of a static variable.
Example 1.11 Write program for displaying the size of the data type in union and the size of the
object of the union.
# include <stdio.h>
# include <conio.h>
void main()
{
union set
{
char x;
int y;
float z;
long double a;
};
union set s1;
clrscr();
printf("\nSize of character x = %d",sizeof(s1.x));
printf("\nSize of integer y = %d",sizeof(s1.y));
printf("\nSize of float z = %d",sizeof(s1.z));
printf("\nSize of long double a = %d",sizeof(s1.a));
OUTPUT:
Size of character x = 1
Size of integer y = 2
Size of float z = 4
Size of long double a = 10
Size of object s1 = 10
Explanation:
In this program, union of the set is declared and s1 is the object of the union set. The union set
contains integer, character, float and long double data types. The program shows the size of all
the data types but it shows the size of the object 10 because the long double has the size 10.
# include <stdio.h>
# include <conio.h>
void main()
{
static int x;
clrscr();
x++;
printf(" x= %d",x);
}
OUTPUT
x=1
Explanation:
When variables are declared and not initialized, they contain garbage values. Hence, they cannot
be used without initializing. When such variables are declared as static, they are initialized to zero
and can directly be involved in an operation.
1.16 ALGORITHMS
Algorithms are used to solve applications that are complex. An algorithm is said to be accurate and
truthful only when it provides the exact wanted output, obviously, after implementation of algorithm
into program.
SUMMARY
1. A computer is an electronic device that manipulates information presented in the form of
data.
2. The study of any characteristic in computer science means the study of storage, retrieval,
manipulation and organization of information.
3. Information is symbolic representation. It has some useful meaning for us and allows us
to make good judgements. Information is always expressed by data. Data is nothing but a
collection of numbers, alphabets, symbols, etc., combined to represent information.
4. The integers, real, logical data, character data, pointer and reference are primitive data
structures. Data structures that are normally directly operated upon by machine-level
instructions are known as primitive data structures.
5. Operations that create data structures are known as creation.
6. Destroy operations destroy data structures.
7. The most commonly used operation linked with data structures is selection, which is used
by programmers to access the data within data structures. Selection relationship depends
upon yes/no.
8. The iteration relationship depends upon repetition.
9. A number that has only the integer part is called an integer number.
10. A number that has the integer part and the fractional part is called a real number.
11. Primitive data structures also include logical data, i.e. true or false. Only two logical
constants—true and false—are present.
12. All non-numeric information is represented by a sequence of characters called strings.
13. The information stored in memory is in the form of bits 0 and 1. The information in the
memory can be viewed and understood by humans with the help of data types. The type
of computer hardware decides the data type and the range it supports. Range means the
largest and smallest possible values that can be represented.
14. A pointer is a link or reference to data structures.
15. A pointer is a memory variable that stores a memory address of another variable. It can
have any name that is valid for the other variable and it is declared in the same way as any
other variable. It is always denoted by ‘*’.
16. A structure is a collection of one or more variables of different data types grouped
together under a single name and new custom data type is formed.
17. An algorithm is a well-organized, pre-arranged, and defined computational module that receives
some value or set of values as input and provides a single or a set of values as output.
18. Analyzing an algorithm refers to calculating or estimating the resources needed for the
algorithm. Resources means computer memory, processing time, logic gates, etc.
19. The space or memory requirement of a program is the memory space required to
execute the program. The execution time is dependent upon the machine and the way of
implementation.
EXERCISES
A. Answer the following questions:
1. What is a computer?
2. What is information? Explain with a few examples.
3. What is data? Explain with a few examples.
4. Mention different types of data structures.
5. Distinguish between structure and class.
6. What are the functions of pointer and reference?
7. Explain logical information. List the operators related to it.
8. Explain the different operations related to data structures.
9. Explain the concept of data type.
10. Explain integers and real numbers.
11. Explain algorithm and its types. (a) Searching (b) sorting.
12. Explain algorithm with the factors time and space.
13. What are the different data types available in C language? Explain with examples.
4. In this method a positive integer is converted to negative by changing each bit to its opposite value.
(a) one’s complement (c) both (a) and (b)
(b) two’s complement (d) none of the above
5. The data type defined by the user is known as ———.
(a) abstract data type (c) classic data type
(b) built-in data type (d) none of the above
1. {
# include <stdio.h> union xyz
# include <conio.h> {
void main() float c;
{ int i;
static int a; double f;
clrscr(); };
printf(" %d",a++); union xyz w;
static int a; clrscr();
clrscr(); printf("\n%d",sizeof(w.c));
printf(" %d",a++); printf("\n%d",sizeof(w.i));
printf(" %d",a); printf("\%d",sizeof(w.f));
printf(" %d",a++); printf("\n%d",sizeof(w));
printf(" %d",a); }
}
4.
2. # include <stdio.h>
# include <stdio.h> # include <conio.h>
# include <conio.h> void main()
void main() {
{ int a=2;
struct abc float b=220.20;
{ clrscr();
char c; printf("\n %f %d",a,a);
int i; printf("\n %f %d",b,b);
float f; }
};
struct abc a;
clrscr(); 5.
printf("\n%d",sizeof(a.c)); # include <stdio.h>
printf("\n%d",sizeof(a.i)); # include <conio.h>
printf("\%d",sizeof(a.f)); void main()
printf("\n%d",sizeof(a)); {
} int a=2,b=10,c=100;
a=b;
3. b=c;
# include <stdio.h> clrscr();
# include <conio.h> printf("\n%d %d %d",a,b,c);
void main() }
2.1 INTRODUCTION
Array means collection. An array is used to store elements of the same type. It is a very popular and useful
data structure and stores data elements in contiguous locations. More than one element is arranged in
sequence so it is also called a composite data structure. Array is a linear and homogenous data structure.
Homogenous means that the same types of elements are stored in it. It can be combined with a non-
homogenous structure and a complex data structure can be created. We know that an array of structure
objects can also be useful. Array of any standard or custom data type can be declared. The array of charac-
ter (strings) type works somewhat differently from the array of int, float, etc.
In the above statement, all elements are initialized. It is also possible to initialize individual elements
as follows:
num[0]=1;
num[1]=2;
num[2]=4;
num[3]=2;
num[4]=5;
The initialization can be done at compile time or dynamically at run time. The above is an example
of compile-time initialization. In the statement (b) declaration and initialization is done at once. In such
declaration, the number of elements (5) need not be mentioned in the []. The compiler automatically
counts the values initialized and assumes the number of elements initialized as the array size.
In the above array, the element, num [0], i.e. 1 is the lowest bound and num [4], i.e. 5 is the upper
element. In C, there is no bound checking. Hence, the programmer has to
check it while accessing or storing elements. Once the array is declared, its
lowest bound cannot be changed but the upper bound can be expanded. 1 num[0]
The array name itself is the constant pointer and, therefore, we cannot 2
modify it. Storing elements in successive memory locations can expand
the upper bound. 4
The array name itself is a pointer. The array num is pointer to the
2
first element, i.e. num contains the address of the memory location
where element 1 is stored as shown in Fig. 2.2. The address stored in 5 num[4]
the array name is called the base address. To access individual elements,
the following syntax is used: Figure 2.2 Arrays of Integers
num[0] refers to the 1
num[1] refers to the 2
An array is a collection of elements of the same data type, stored in unique and contiguous memory
locations.
Type Types refers to data type. It decides which type of element is stored in the array. It also instructs the
compiler to reserve memory according to data type.
Base The address of the first element (0th) element is a base address. The array name itself stores the
address of the first element.
Index The array name is used to refer to the array element. For example, in num [x], num is the array
name and x is the index. The value of x begins from 0 onwards depending on the size of the array. The
index value is always an integer value.
Range The index of an array, i.e. the value of x varies from the lower bound to the upper bound
while writing or reading elements from an array. For example, for num[100] the range of the index is
0 to 99.
Word This indicates the space required for an element. In each memory location, a computer can store
a data piece. The space occupied varies from machine to machine. If the size of the element is more than
word (one byte) then it occupies two successive memory locations. The various data types such as integer,
float, long, etc. needs more than one byte in memory.
Example 2.1 Write a program to display array elements with their addresses.
# include <stdio.h>
# include <conio.h>
void main()
{
int Num[5]={1,2,3,2,5};
clrscr();
OUTPUT
Num[0] = 1 Address: 65516
Num[1] = 2 Address: 65518
Num[2] = 3 Address: 65520
Num[3] = 2 Address: 65522
Num[4] = 5 Address: 65524
Explanation:
In the output of the program, elements and their addresses are displayed. Recall that integer
requires two bytes in memory. The memory locations displayed have a difference of two. From
the above program, it is clear that array elements are stored in successive memory locations.
Fig. 2.3 shows the memory location and the values stored.
1 2 3 2 5
2. Once the array is declared, its lowest bound cannot be changed, but the upper bound can be expanded
with the C++ compiler. The array name itself is a constant pointer and we cannot modify it. Therefore,
the lowest bound of an array cannot be expanded.
Example 2.2 Write a program to demonstrate that the upper bound of an array can be expanded.
# include <stdio.h>
# include <conio.h>
void main()
{
int Num[5]={1,2,3,2,5};
Num[5]=6;
clrscr();
printf("Num[5]=%d",Num[5]);
}
OUTPUT
Num[5]=6
Explanation:
In this program array, Num [5] is declared with the array size 5 and it is initialized with five
elements. In the next statement, the 6th element is also initialized and displayed. Hence, we can
say that the upper bound of an array can be expanded. If you try this program with a C compiler,
a run time error message will be displayed.
3. We know that the array name itself is a pointer. Though it is a pointer, it does not need the ‘*’ operator.
The brackets [] automatically denote that the variable is a pointer.
4. All the elements of an array share the same name, and they are distinguished from one another with
the help of the element number.
5. The amount of memory required for an array depends upon the data type and the number of
elements.
Total bytes = size of (data type) * size of array
6. Operations such as insertion and deletion of element done with a list cannot be done with an array.
Once an array is created, we cannot remove or insert memory locations. An element can be deleted or
replaced but the memory location remains as it is.
7. When an array is declared and not initialized, it contains garbage values. If we declare an array as static,
all elements are initialized to zero. However, the values of static type data persist and remain in the
memory as long as the program executes. To overcome this problem, we initialize the first element of an
array with zero or any other number. All the remaining elements are automatically initialized to zero,
provided that the initialization is done in the declaration statement of array. The following program
illustrates this:
# include <stdio.h>
# include <conio.h>
void main()
{
int Num[5]={0},j;
clrscr();
printf("The elements in array after initialization ");
for(j=0;j<5;j++)
printf("\nNum[%d]=%d",j,Num[j]);
}
OUTPUT
The elements in an array after initialization
Num[0]=0
Num[1]=0
Num[2]=0
Num[3]=0
Num[4]=0
Explanation:
In this program an array, Num [5] is declared and the first element is initialized with zero. The
compiler automatically initializes all the elements with zero. Using the for loop the contents of an
array are displayed and we can see that all are zeros.
We have learnt how to declare, initialize and access the array elements. A one-dimensional array does not
have any corresponding elements; it has only one row of elements. So far, the example we have discussed
is of a one-dimensional array.
For example,
int Num[5]; /* one dimensional array */
In the following program we learn how to store and display values in the array during program
execution.
Traversing The operation of displaying or listing all elements of an array is called traversing. The
following program explains traversing with a one-dimensional array:
Example 2.4 Write a program to read and display the elements of an array.
# include <stdio.h>
# include <conio.h>
void main()
{
int num[5],j;
clrscr();
printf("\n Enter five elements: ");
for(j=0;j<5;j++)
scanf("%d",&num[j]);
printf("\n Elements Address ");
for(j=0;j<5;j++)
printf("\n%3d %10u ",num[j],&num[j]);
}
OUTPUT
Enter five elements: 4 6 4 2 1
Elements Address
4 65516
6 65518
4 65520
2 65522
1 65524
Explanation:
In this program, an array num[] is declared. The first for loop, with the help of scanf()
statement, reads the elements and places in the array. The element position is indicated by
the loop variable j. The same procedure is applied for displaying elements. The printf()
statement displays the elements and addresses on the screen. The %3d and the %10u is used
to display the space.
From Figure 2.4 we can see that elements in a one-dimensional array are stored one after another in
sequence in memory. In the array we can insert, delete, or add any element but we cannot insert or delete
the memory location. We can change only the values.
4 6 4 2 1
Base Address
Deletion
Insertion
Searching
Array
Merging
Sorting
2.4.1 Deletion
This operation involves deleting specified elements from the array. Now, consider the following programs:
Example 2.5 Write a program to delete specified element from an array and rearrange the elements.
# include <stdio.h>
# include <conio.h>
void main()
{
int Num[20]={0},j,k,n,p,t;
clrscr();
printf("\n Enter Number of elements: ");
scanf("%d",&n);
printf("\n Enter elements: ");
for(j=0;j<n;j++)
scanf("%d",&Num[j]);
printf("\n Elements are: ");
for(j=0;j<n;j++)
printf("\n Num[j]=%d",Num[j]);
printf("\n Enter element number to delete: ");
scanf("%d",&p);
p–-;
for(j=0;j<n;j++)
{
if(j>=p)
Num[j]=Num[j+1];
}
for(j=0;j<n;j++)
if(Num[j]!=0)
printf("\nNum[j] = %d",Num[j]);
}
OUTPUT
Enter Number of elements: 5
Enter elements: 9 4 6 3 2
Elements are:
Num[j]=9
Num[j]=4
Num[j]=6
Num[j]=3
Num[j]=2
Enter element number to delete: 5
Num[j] = 9
Num[j] = 4
Num[j] = 6
Num[j] = 3
Explanation:
In this program, an array Num [20] is declared. The program asks for number of elements to
be entered. The user has to enter the following input:
1. Number of integer elements to be entered.
2. Element number to be erased from an array.
In the first for loop the scanf statement reads the numbers from the keyboard and places it in the
array. In the second for loop the array elements are displayed. After this for loop the number to be
deleted is entered through the keyboard. In the third for loop the entered element is compared with
all array elements. When match is found, the element is deleted and successive elements are pushed in
their previous memory locations. The fourth for loop and printf statement display the elements
of the array. You can see in the output that the third memory location is the same; only its contents are
changed. See Fig. 2.6, where element ‘l’ is deleted and after deletion elements are shown in Fig. 2.6(c).
9 9 9
4 4 4
6 6 3
3 3
2.4.2 Insertion
This operation is used to insert an element at specified positions in an array. Consider the following program.
Example 2.6 Write a program to insert an element at a specified position in the array.
# include <stdio.h>
# include <conio.h>
void main()
{
int num[20]={0},j,k,n,p,t,s;
clrscr();
printf("\n Enter number of elements: ");
scanf("%d",&n);
printf("\n Enter elements: ");
for(j=0;j<n;j++)
scanf("%d",&num[j]);
printf("\n Elements are: ");
for(j=0;j<n;j++)
printf("\n num[j]: %d",num[j]);
for(j=n;j!=p;j––)
num[j]=num[j-1];
num[j]=s;
for(j=0;j<=n;j++)
printf("\n num[j] = %d",num[j]);
}
OUTPUT
Enter number of elements: 5
Enter elements: 1 2 3 4 5
Elements are:
num[j]: 1
num[j]: 2
num[j]: 3
num[j]: 4
num[j]: 5
Enter element and position to insert at: 2 5
num[j] = 1
num[j] = 2
num[j] = 3
num[j] = 4
num[j] = 2
num[j] = 5
Explanation:
This program is somewhat like the previous program. Here, an element is inserted. The
array elements are shifted to the next location, and at a specified position a space is created
and the new element is inserted (Figures 2.7 (b) and 2.7 (c)). Here also, you can see that
though we insert a new element, the memory location of the second element is the same
(65482). Once again, it is proved that in an array operation, only the contents of memory can
change but the actual address remains as it is. The address of the first element, i.e. num [0]
(65480), is called the base address. This address can also be stored in another pointer and array
elements can be accessed. The next program is illustrative in this regard.
1 1 1
2 9
3 2 2
4 3 3
4 4
Example 2.7 Write a program to display a one-dimensional array using an integer pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int *p,num[5]={4,5,6,7,8},j;
clrscr();
p=num;
printf("Elements Address");
for(j=0;j<5;j++)
printf("\n %3d %7u", *(p+j),(p+j));
}
OUTPUT
Elements Address
4 65516
5 65518
6 65520
7 65522
8 65524
Explanation:
In this program, an integer array num [] is declared and initialized. In the same statement
pointer *p and integer variable j are declared. The base address is assigned to pointer p.
While assigning the base address it is enough to write the name of the array. It is optional to
write subscript number, i.e. num [0][0]. The for loop executes five times and the value
of j varies from 0 to 4. The first time 0 is added to the base address and there is no change
in the address. Hence, the 1st element is displayed. In the second iteration, one is added to
the base address. It takes the next successive address and the 2nd element is displayed. The
same procedure is continued and array elements are displayed. Fig. 2.8 shows what exactly
takes place.
4 5 6 7 8
0 1 2 3 4
In the above figure, the first line of boxes contains values, the second contains memory addresses and
the third contains loop variable values as used in the program. When the value of loop variable is added to
the base address we get the successive memory addresses and the values stored in them can be displayed.
For example,
65516 + 0 = 65516, i.e. 4
65516 + 1 = 65518, i.e. 5
¦ ¦
and so on values can be retrieved.
2.4.3 Searching
The process of seeking specified elements in an array is called searching.
# include <stdio.h>
# include <conio.h>
void main()
{
int j=0,n;
int x[10];
clrscr();
printf("Enter ten elements of array:\n");
for(j=0;j<10;j++)
scanf("%d",&x[j]);
printf("\n Enter element to search:");
scanf("%d",&n);
for(j=0;j<10;j++)
{
if(x[j]==n)
break;
}
if(x[j]==n)
OUTPUT
Enter ten elements of array:
1 2 3 4 5 6 7 8 9 12
Enter element to search: 3
Element found
Explanation:
In the above program, an integer array is declared and initialized with elements. The element
which is to be searched is entered through the keyboard. Using while loop, all the elements
are read. The if statement checks every element with the array elements. When the if
condition is satisfied the, message “element found” is displayed. Thus, the specified element
can be searched. When the element is not found the message displayed will be “Element is
not found”.
2.4.4 Merging
The merging of two arrays into a single one is an important operation. The easiest way of merging two
arrays is first, to copy all elements of one array into a third one and then copy all elements of the second
array also into the third one. We can also merge elements in an alternate order as shown in the follow-
ing program. Figure 2.9 indicates the merging of two arrays. The following points should be taken into
account.
1. Elements of one array can be appended to the end of the second array.
2. Elements of two arrays can be merged in an alternate order.
3. The size of the resulting array must be more than the size of two arrays.
Example 2.9 Write a program to merge two arrays into a third one. Display the contents of all the
three arrays.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,h=0,k=0;
int x[4]={1,2,3,4};
int y[4]={5,6,7,8};
int z[8];
clrscr();
for(j=0;j<4;j++)
printf(" %d ",y[j]);
j=0;
while(j<8)
{
if(j%2==0)
z[j]=x[k++];
else
z[j]=y[h++];
j++;
}
OUTPUT
Array X: 1 2 3 4
Array Y: 5 6 7 8
Array Z: 1 5 2 6 3 7 4 8
Explanation:
In the above program, three integer arrays are declared and initialized. The first two for loops
are used to view the elements of array x and z by a transverse process. The while loop is used
to execute until the condition is true. The ‘if’ statement checks the condition and accordingly
if and else blocks are executed. These statements also fetch elements from both arrays and
place them into array z. Fig. 2.9 illustrates the merging of two arrays.
1 1
2 5
3 2
4 6
3
5 7
6 4
7 8
8
2.4.5 Sorting
Arranging elements in a specific order, either ascending or descending, is known as sorting. Sorting is a
very important operation and compulsorily used in database application programs. Let us study the fol-
lowing program, which sorts an array of integers and stores them in another array.
Example 2.10 Write a program to enter integer elements and sort them in ascending order.
# include <stdio.h>
# include <conio.h>
void main()
{
int num[5],j,k,s=0;
clrscr();
printf("\n Enter five Elements: ");
for(j=0;j<5;j++)
{
scanf("%d",&num[j]);
s=s+num[j];
}
for(k=0;k<s;k++)
{
for(j=0;j<5;j++)
{
if(num[j]==k)
printf(" %d ",num[j]);
}
}
}
OUTPUT
Enter five Elements: 5 8 9 7 2
2 5 7 8 9
Explanation:
In the above program, an integer array is declared and five numbers are entered. The sum of
all the numbers is taken. Using nested loops every number of the array is compared from one
to s (s = sum of all numbers). The if statement checks every array element with the
value of s and displays the numbers in ascending order. Sorting can be done in various ways.
The above sorting method is the simplest one.
Two-dimensional arrays can be thought of as rectangular display of elements with rows and columns.
Consider the following example:
int x[3][3];
The arrangement of array elements shown in Fig. 2.10 is only for the sake of understanding. Actually,
the elements are stored in continuous memory locations.
A two-dimensional array is a collection of two one-dimensional arrays. The meaning of the first argument is in
x [3]. [3] means the number of rows, i.e. number of one-dimensional arrays and the second argument indicates
the number of elements. x [0][0] means the first element of the first row and column. Row number remains
the same but column number changes. The number of rows and columns is called as the range of the array. A
two-dimensional array clearly shows the difference between logical assumption and physical representation of
data. The computer memory is linear and whatever may be the type of array—one, two or multi dimensional—
it is stored in continuous memory locations. Fig. 2.11 explains storage of two-dimensional arrays.
# include <stdio.h>
# include <conio.h>
void main()
{
int i,j;
int a[3][3]={1,2,3,4,5,6,7,8,9};
clrscr();
printf("\n Array elements and address ");
printf("\n\t Col-0 Col-1 Col-2");
printf("\n\t ====== ====== ======");
printf("\nRow0");
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
printf(" %d [%u]",a[i][j],&a[i][j]);
printf("\nRow%d",i+1);
}
printf("\r ");
}
OUTPUT
Array elements and address
Col-0 Col-1 Col-2
====== ====== ======
Row0 1 [65508] 2 [65510] 3 [65512]
Row1 4 [65514] 5 [65516] 6 [65518]
Row2 7 [65520] 8 [65522] 9 [65524]
Explanation:
From this program’s output, you can see that the memory addresses displayed are in sequence
and it is true that elements of two-dimensional arrays are stored in successive memory locations.
The one-dimensional array can be accessed using a single loop. However, for two-dimensional
arrays, two loops are required for the row and column. The inner loop helps to access the row-
wise elements and the outer loop changes the column number; just as a one-dimensional array
base address can be stored in a pointer. Consider the following program.
Example 2.12 Write a program to assign the base address of a two-dimensional array to a pointer
and display the elements.
# include <stdio.h>
# include <conio.h>
void main()
{
int *p,num[2][2]={4,5,6,7},j;
clrscr();
p=&num[0][0];
printf("Elements Address");
for(j=0;j<4;j++)
printf("\n%3d %9u",*(p+j),(p+j));
}
OUTPUT
Elements Address
4 65490
5 65492
6 65494
7 65496
Explanation:
This program is the same as the last one. But the point to note here is that to store the base
address of a two-dimensional array, it is not enough to mention the array; in addition, subscript
number and address operation should also be mentioned. Only then will the compiler accept
the statement; other-wise, the compiler flags an error message. For a one-dimensional array, an
array name is sufficient, but onwards we have to mention the element number with the address
operator.
Example 2.13 Write a program to illustrate an insert operation with a two-dimensional array.
# include <stdio.h>
# include <conio.h>
void main()
{
int num[5][5]={0,0},j,*p,at,e,n;
clrscr();
printf("\n Enter how many elements (<=25): ");
scanf("%d",&n);
p=&num[0][0];
for(j=0;j<n;j++)
scanf("%d",p++);
p=&num[0][0]; printf("\n");
for(j=0;j<n;j++,p++)
printf(" %d ",*p);
for(j=n;j>=at;j––)
{
*p=*(p–1);
p––;
}
*p=e;
p=&num[0][0];
for(j=0;j<=n;j++,p++)
printf(" %d ",*p);
}
OUTPUT
Enter how many elements (<=25): 5
1 2 3 4 5
1 2 3 4 5
Enter a number & position to insert: 8 3
1 2 8 3 4 5
Explanation:
In all types of array, one, two and three-dimensional elements are stored in the successive
memory locations. A pointer can dereference to get successive memory location of memory. In
this way, elements of the array can be accessed, changed or replaced. For all this, we require only
the base address of the array that is to be assigned to a pointer.
Consider the statement p=&num [0][0]; used to store base address (address of 0th element of the array)
of an array. Later in this program the array is not accessed by its name and corresponding row column
numbers. The total capacity of the array of storing elements is 25 as per the declaration. The user is asked
to enter the number of elements he wants to enter. The entered value is stored in the variable n through
the scanf() statement. Thus, by using loops, values are repetitively entered and displayed.
The user is again asked to enter a number and position in the array where the element is to be inserted.
These values are stored in the variables (scanf( “%d %d ”,&e,&at);), e & at.
for(j=n;j>=at;j––)
{
*p=*(p−1);
p––;
}
Using the for loop the elements are shifted to next position up to the value of the variable “at”.
When the loop ends, the entered element is assigned to * (*p=e;). The ex- element is already shifted to
the next position. Thus, finally the list of latest elements is displayed. Figure 2.12 (a) and (b) shows the
insertion of element.
8 is to be inserted after 3
1 2 3 4 5 25
Shifting
a. Before Insertion
1 2 8 3 4 5
b. After Insertion
# include <stdio.h>
# include <conio.h>
void main()
{
int num[5][5]={0,0},j,*p,at,e,n;
clrscr();
for(j=0;j<n;j++)
scanf("%d",p++);
p=&num[0][0]; printf("\n");
for(j=0;j<n;j++,p++)
printf(" %d",*p);
for(j=at;j<n;j++)
{
*p=*(p+1);
p++;
}
*p=0;
p=&num[0][0];
while(*p!=0)
{
printf(" %d ",*p);
p++;
}
}
OUTPUT
Enter how many elements (<=25): 7
1 2 3 4 5 6 7
1 2 3 4 5 6 7
Enter Element position to delete: 5
1 2 3 4 6 7
Explanation:
The method for obtaining base address and pointer arithmetic involved in the program is same
as the last program. The element to be deleted is replaced with the next element. Thus, the
entire elements are shifted to the previous location. Figs. 2.13 (a) and (b) describe the deletion
of element.
Element to be
deleted
1 2 3 4 5 6 7
a. Before Deletion
1 2 3 4 5 6 7
b. After Deletion
A three-dimensional array can be thought of as array of arrays. Consider the following program:
int mat[3][3][3] =
{1,2,3,
4,5,6,
7,8,9,
1,4,7,
2,5,8,
3,6,9,
1,4,4,
2,4,7,
8,8,5};
# include <stdio.h>
# include <conio.h>
void main()
{
int a,b,c;
int mat[3][3][3] ={1,2,3,
4,5,6,
7,8,9,
1,4,7,
2,5,8,
3,6,9,
1,4,4,
2,4,7,
8,8,5};
clrscr();
for(a=0;a<3;a++)
{
printf("\n");
for(b=0;b<3;b++)
{
for(c=0;c<3;c++)
printf("%3d [%u] ",mat[a][b][c],&mat[a][b][c]);
printf("\n");
}
}
}
OUTPUT
1 [65470] 2 [65472] 3 [65474]
4 [65476] 5 [65478] 6 [65480]
7 [65482] 8 [65484] 9 [65486]
Explanation:
In this example, a three-dimensional array is initialized. The three loops are used to access the
elements. The logic of accessing the elements is same as in the case of two-dimensional array.
From the output obtained, we can say that elements of multi-dimensional array are stored in
continuous memory locations.
2.7 STRINGS
The collection of characters, digits and symbols enclosed within quotation marks are called as strings.
Strings are always declared as character array. Like other type of arrays, the elements of character array are
also stored in continuous memory location. Each character occupies one byte in memory. One different
thing about strings is that, the compiler automatically adds NULL (‘\0’) character at the end of the string.
This character is used to detect the end of the string. The declaration of string can be done as follows. The
Fig. 2.14 shows how the name is stored in the array.
char name[6]= "KAMAL";
K A M A L ‘\0’
While declaring character array the declaration of NULL character should be taken into account.
Therefore, the above array is declared of 6 sizes out of which five characters are stored first, then NULL
character is stored at the end.
Example 2.16 Write a program to declare, initialize character array and display the string.
# include <stdio.h>
# include <conio.h>
void main()
{
char name[6]="kamal";
clrscr();
printf("%s",name);
}
OUTPUT
kamal
Explanation:
In this program a character array is declared and initialized with string “kamal”. Here, the array
limit is declared for 6 characters and total six characters are stored including NULL character.
Using printf() statement, string is displayed. Instead of 6 if limit is declared of 5, there is no
place to insert NULL character and therefore the string will not be displayed properly.
# include <stdio.h>
# include <conio.h>
void main()
{
char name[10],iname[10],dname[10];
int i=0,y;
clrscr();
printf("\nEnter the name:-");
scanf("%s",name);
while(name[i]!='\0')
{
iname[i]=name[i]+3;
i++;
}
iname[i++]='\0';
printf("\nThe encrypted name is:- %s",iname);
i=0;
while(iname[i]!='\0')
{
dname[i]=iname[i]-3;
i++;
}
dname[i++]='\0';
printf("\nThe decrypted name is:- %s",dname);
}
OUTPUT:
Enter the name:-amit
The encrypted name is:- dplw
The decrypted name is:- amit
Explanation:
In this program, name is entered through the keyboard. In the encryption process, each letter is
shifted by 3 and during decryption each letter is shifted downward by 3.
Example 2.18 Write a program to display the string and determine end of the string by NULL character.
# include <stdio.h>
# include <conio.h>
void main()
{
char arr[]={'A','B','C','D','E','\0'};
int j=0;
clrscr();
printf("Characters Address");
while(arr[j]!='\0')
{
printf("\n %3c %9u",arr[j],&arr[j]);
j++;
}
OUTPUT
Characters Address
A 65492
B 65493
C 65494
D 65495
E 65496
Explanation:
In this program a character array is declared and initialized. In the initialization, a NULL character
is also initialized. The condition given in the while loop tests for NULL character. If it finds,
the while loop is terminated, otherwise a character is displayed. We can also find that the
characters are stored in successive memory locations.
We have already studied structure in the first chapter. We also know that array is a homogeneous data
structure and structure is a non-homogenous data structure. By declaring an array of structure objects,
we can make combination of data structure of different types. However, we cannot create an array of non-
homogenous type as such an array will also be homogenous type. Because all the elements are of the same
type, i.e. structure type. The following program explains array of structure.
Example 2.19 Write a program to read display data using array of structure.
# include <stdio.h>
# include <conio.h>
void main()
{
struct item
{
int codeno;
int price;
};
int j;
struct item a[2];
clrscr();
for(j=0;j<2;j++)
scanf("%d %d",&a[j].codeno,&a[j].price);
for(j=0;j<2;j++)
printf("\n %d %d",a[j].codeno,a[j].price);
}
OUTPUT
Enter Codeno, Price
002 541
003 125
Codeno Price
2 541
3 125
Explanation:
In this program, structure item is defined with two numeric fields, i.e. codeno and price.
An array of structure item type a[2] is declared. Using for loop data read and displayed.
A sparse matrix is a two-dimensional array in which most of the elements are null. Figs. 2.15 (a) and 2.15 (b)
indicate sparse and dense matrices. The matrix, which is not a sparse matrix, is called dense matrix. One
cannot exactly determine the boundary between sparse and dense matrix. The sparse matrices are very
useful in various applications. In sparse matrices, memory is wasted because the NULL values are stored
and hence to overcome wastage of memory a mechanism must be followed. One of the mechanism is
linked list, which will be discussed in detail in Chapter 6.
The classification of sparse matrices is shown in Fig. 2.16.
3 0 78 0 12 3 0 0 0 1
1 6 7 8 11 2 0 0 0 0
0 5 0 1 0 0 0 0 1 0
1 0 1 0 5 1 0 1 0 0
a. Dense Matrix b. Sparse Matrix
Diagonal
Upper Lower
Tridiagonal
Right Right
0 0
0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
Figure 2.17 Upper Right Triangular Figure 2.18 Upper Left Triangular
Example 2.20 Write a program to initialize and display an upper right triangular matrix.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,urt[5][5]={ 0,4,3,2,1,
0,0,3,2,1,
0,0,0,2,1,
0,0,0,0,1,
0,0,0,0,0,
};
clrscr();
printf("\n UPPER RIGHT TRIANGULAR\n");
for(j=0;j<5;j++)
{
for(k=0;k<5;k++)
printf(" %d ",urt[j][k]);
printf("\n");
}
}
OUTPUT
UPPER RIGHT TRIANGULAR
0 4 3 2 1
0 0 3 2 1
0 0 0 2 1
0 0 0 0 1
0 0 0 0 0
Explanation:
In this program an array is initialized and the NULL elements form an upper right triangular
matrix and it is displayed. Fig. 2.18 shows the upper left triangular matrix.
Example 2.21 Write a program to initialize and display an upper left triangular matrix.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,ult[5][5]={ 1,2,3,4,0,
1,2,3,0,0,
1,2,0,0,0,
1,0,0,0,0,
0,0,0,0,0,
};
clrscr();
printf("\n UPPER LEFT TRIANGULAR\n");
for(j=0;j<5;j++)
{
for(k=0;k<5;k++)
printf(" %d ",ult[j][k]);
printf("\n");
}
}
OUTPUT
UPPER LEFT TRIANGULAR
1 2 3 4 0
1 2 3 0 0
1 2 0 0 0
1 0 0 0 0
0 0 0 0 0
Explanation:
The two-dimensional matrix ult[5][5] is declared and initialized. The elements of the matrix
are displayed.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,llt[5][5]={ 0,0,0,0,0,
1,0,0,0,0,
1,2,0,0,0,
1,2,3,0,0,
1,2,3,4,0,
};
clrscr();
printf("\n LOWER LEFT TRIANGULAR\n");
for(j=0;j<5;j++)
{
for(k=0;k<5;k++)
printf(" %d ",llt[j][k]);
printf("\n");
}
}
OUTPUT 0 0 0 0 0
LOWER LEFT TRIANGULAR
0 0 0 0
0 0 0 0 0
1 0 0 0 0 0 0 0
1 2 0 0 0 0 0
1 2 3 0 0
0
1 2 3 4 0
Figure 2.19 Lower-left Triangular
Explanation:
In this program an array llt [][] is initialized. The array is initialized in such a way that
maximum elements are NULL and hence, it is sparse matrix. The NULL elements form a triangular
at lower left side. Hence, this matrix is called lower left triangular matrix. The Fig. 2.19 shows the
lower left triangular matrix.
Example 2.23 Write a program to initialize and display a lower right triangular matrix.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,lrt[5][5]={ 0,0,0,0,0,
0,0,0,0,1,
0,0,0,2,1,
0,0,3,2,1,
0,4,3,2,1,
};
clrscr();
printf("\n LOWER RIGHT TRIANGULAR\n");
for(j=0;j<5;j++)
{
for(k=0;k<5;k++)
printf(" %d ",lrt[j][k]);
printf("\n");
}
}
OUTPUT
0 0 0 0 0
LOWER RIGHT TRIANGULAR
0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 2 1
0 0
0 0 3 2 1
0 4 3 2 1
Figure 2.20 Lower Right Triangular
Explanation:
This program is the same as the last one and displays lower right triangular matrix. Fig. 2.20 is an
example of lower right triangular matrix.
Example 2.24 Write a program to find whether the given matrix is lower right triangular matrix or not.
# include <stdio.h>
# include <conio.h>
int matrix[3][3];
void main()
{
clrscr();
input();
display();
lowe_r();
}
input()
{
int n,j,k;
printf("\n Enter elements for matrix ");
for(j=0;j<3;j++)
{
for(k=0;k<3;k++)
scanf("%d",&matrix[j][k]);
}
return 0;}display()
{
int n,j,k;
printf("\n Elements of matrix\n");
for(j=0;j<3;j++)
{
for(k=0;k<3;k++)
printf("%4d",matrix[j][k]);
printf("\n"); }
return 0;}
lowe_r()
{
}
if(b=='Y')
puts("\n The given matrix is lower right triangular matrix");
else
puts("\n Not a sparse matrix");
return 0;
}
OUTPUT
Enter elements for matrix 0 0 0 0 0 0 0 1 5
Elements of matrix
0 0 0
0 0 0
0 1 5
Elements of matrix
0 0 0
0 0
0
Explanation:
In this program matrix [3][3] is declared. Through input() function, elements are entered.
The display() function displays the elements of the matrices. Through use of lowe_r()
function, elements of rows 1st, 2nd and 3rd are checked. The numbers of elements are 3,2 and 1,
respectively. If these elements are NULL, the matrix is lower right triangular matrix.
In some of the earlier programs, arrays, i.e. matrix, are initialized and displayed. This can be imple-
mented easily. However, in the above program, elements are entered during program execution. Hence,
few additional statements are required.
In case of diagonal matrices, only diagonal elements are non-zero and other values are NULL as shown
in the Fig. 2.21.
1 0 0 0 0 0 0 0 0 5
0 2 0 0 0 0 0 0 4 0
0 0 3 0 0 0 0 3 0 0
0 0 0 4 0 0 2 0 0 0
0 0 0 0 5 1 0 0 0 0
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,dgl[5][5]={ 1,0,0,0,0,
0,2,0,0,0,
0,0,3,0,0,
0,0,0,4,0,
0,0,0,0,5,
};
clrscr();
printf("\n DIAGONAL MATRIX\n");
for(j=0;j<5;j++)
{
for(k=0;k<5;k++)
printf(" %d ",dgl[j][k]);
printf("\n");
}
}
OUTPUT
DIAGONAL MATRIX
1 0 0 0 0
0 2 0 0 0
0 0 3 0 0
0 0 0 4 0
0 0 0 0 5
Explanation:
In this program an array is initialized in which diagonal elements are non-zero.
Figure 2.22 shows diagonal matrices. Readers can write programs to show elements as seen in these tables.
1 1 0 0 0 0 0 0 2 5
1 2 8 0 0 0 0 4 4 1
0 4 3 3 0 0 7 3 9 0
0 0 5 4 5 8 2 7 0 0
0 0 0 8 5 1 4 0 0 0
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,x[3][3];
clrscr();
printf("\n Enter nine elements: ");
for(j=0;j<=2;j++)
for(k=0;k<=2;k++)
scanf("%d",&x[j][k]);
Explanation:
In this program two for loops are used. The elements are entered through the keyboard. The
outer loop indicates row and inner loop indicates column. After finishing the execution of inner
loop, outer loop is executed once. Thus, when one complete row is filled with elements, the
outer loop is executed and it indicates next row, which is to be filled. Again, the inner loop is
used to fill the elements. Thus, elements are stored row-by-row.
In column major array elements are stored column-by-column as shown in Fig. 2.24.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,k,x[3][3];
clrscr();
printf("\n Enter nine elements: ");
for(j=0;j<=2;j++)
for(k=0;k<=2;k++)
scanf("%d",&x[k][j]);
printf("\n Column major array\n");
for(j=0;j<=2;j++)
{
for(k=0;k<=2;k++)
printf(" %d ",x[j][k]);
printf("\n");
}
}
OUTPUT
Enter nine elements: 1 2 3 4 5 6 7 8 9
Explanation:
In column major method the elements entered are stored column by column. The outer loop indicates
column number and inner loop indicates row. When one complete column is filled, next column is
considered for filling.
Array name by itself is an address or pointer. It points to the address of the first element (0th element of
an array). The elements of the array together with their addresses can be displayed by using array name
itself. Array elements are always stored in contiguous memory locations.
Programs in this regard are explained below.
Example 2.28 Write a program to display elements of an array. Start element counting
from 1 instead of 0.
# include <stdio.h>
# include <conio.h>
void main()
{
int x[]={2,4,6,8,10},k=1;
clrscr();
while(k<=5)
{
printf("%3d",k[x-1]);
k++;
}
}
OUTPUT:
2 4 6 8 10
Explanation:
Array element counting always starts from ‘0’. The element number is added in the base
address and each element of an array is accessed. If one is subtracted from base address of an
array, it points to the prior address of 0th element. By adding one to its reduced base address it
is possible to start element counting from ‘1’.
Example 2.29 Write a program to display array element with their addresses using array name as a pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int x[5]={2,4,6,8,10},k=0;
clrscr();
printf("\nElement No. Element Address");
while(k<5)
{
printf("\nx[%d] = \t%8d %9u",k,*(x+k),x+k);
k++;
}
}
OUTPUT:
Element No. Element Address
x[0] 2 4056
x[1] 4 4058
x[2] 6 4060
x[3] 8 4062
x[4] 10 4064
Explanation:
In this program variable ‘k’. acts as element number and its value varies from 0 to 4.
When it is added with array name ‘x’, i.e. with address of the first element, it points
to the consecutive memory location. Thus, element no., element, and their addresses are
displayed.
Example 2.30 Write a program to display array elements with their addresses using array
name as a pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int num[4]={10,25,35,45},i;
clrscr();
printf("Element Address\n");
for(i=0;i<4;i++)
{
printf("num[%d]=%d",i,*(num+i));
printf("%8u\n",num+i);
}
}
OUTPUT:
Element Address
num[0] = 10 4062
num[1] = 25 4064
num[2] = 35 4066
num[3] = 45 4068
Explanation:
Here, the array name ‘num’. itself acts as a pointer to the array num []. The pointer ‘num’
provides address of first element and ‘*num’ gives value stored at that address. When ‘i’ is
added with ‘num’, the equations *(num+i) and num+i show ‘i’ the element and its location,
respectively.
Example 2.31 Write a program to access elements of array through different ways using pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int arr[5]={10,20,30,40,50},p=0;
clrscr();
for(p=0;p<5;p++)
{
printf("Value of arr[%d]=",p);
printf("%d ¦ ",arr[p]);
printf("%d ¦ ",*(arr+p));
printf("%d ¦ ",*(p+arr));
printf("%d ¦",p[arr]);
printf(" address of arr[%d] =%u\n",p,&arr[p] );
}
}
OUTPUT:
Value of arr[0]=10 ¦ 10 ¦ 10 ¦ 10 ¦ address of arr[0]=4056
Value of arr[1]=20 ¦ 20 ¦ 20 ¦ 20 ¦ address of arr[1]=4058
Value of arr[2]=30 ¦ 30 ¦ 30 ¦ 30 ¦ address of arr[2]=4060
Value of arr[3]=40 ¦ 40 ¦ 40 ¦ 40 ¦ address of arr[3]=4062
Value of arr[4]=50 ¦ 50 ¦ 50 ¦ 50 ¦ address of arr[4]=4064
Explanation:
In this program elements are displayed using different syntax, a. arr[p] b. *(arr+p))
c) *(p+arr)) d) p[arr]). The results of all of them would be the same.
a. arr[p]: This statement displays various array elements. Here, ‘arr’ refers to the address and
‘p’ refers to the element number.
b. *(arr+p): The arr+p is addition of constant with base address of the array. It shows address
of pth element. The *(arr+p) points to the pth element of the array.
c. *(p+arr): This statement is same as (b).
d. p[arr]: This statement is same as (a). Here, ‘p’ refers to the element number and ‘arr’ refers
to the base address. By varying ‘p’ and ‘arr’ the various elements of the array are displayed.
Example 2.32 Write a program to find sum of all the elements of an array. Use array name
itself as a pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int sum=0,i=0,a[]={1,2,3,4,5};
clrscr();
printf("Elements Values Address\n\n");
while(i<5)
{
printf("a[%d]\t%5d\t%8u\n",i,*(a+i),(a+i)); sum=sum+*(a+i++);
}
OUTPUT:
Elements Values Address
a[0] 1 4056
a[1] 2 4058
a[2] 3 4060
a[3] 4 4062
a[4] 5 4064
Explanation:
In this program array name ‘a’ acts as pointer and variable ‘i’ is used for referring element
numbers. Using the for loop and expressions *(a+i) and (a+i) various elements and their
addresses are displayed respectively. In the ‘sum’ variable sum of all elements is obtained.
A matrix can represent two-dimensional elements of an array. Here, the first argument is row number
and second is column number. To display the elements of two-dimensional array using pointer it is
essential to have ‘&’ operator as pre-fix with array name followed by element numbers, otherwise
compiler shows an error.
Example 2.33 Write a program to display array elements and their address using pointers.
# include <stdio.h>
# include <conio.h>
void main()
{
int i,j=1,*p;
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
clrscr();
printf("\tElements of An Array with their addresses\n\n");
p=&a[0][0];
for(i=0;i<9;i++,j++)
{
printf("%5d [%5u ]",*(p),p);
p++;
if(j==3)
{
printf("\n");
j=0;
}
}
}
OUTPUT:
Elements of an Array with their addresses.
1 [4052] 2 [4054] 3 [4056]
4 [4058] 5 [4060] 6 [4062]
7 [4064] 8 [4066] 9 [4068]
Explanation:
In this program two-dimensional array is declared and initialized. The base address of array
is assigned to integer pointer ‘p’. While assigning base address of two-dimensional array,
‘&’ operator is to be pre-fixed with array name followed by element numbers are necessary
otherwise compiler shows an error. The statement p = &a[0][0] is used in this context.
The pointer ‘p’ is printed and incremented in for loop till it prints entire array elements.
The if statement splits a line when three elements in each row are printed.
Example 2.34 Write a program to display array elements and their address. Use array name itself
as a pointer.
# include <stdio.h>
# include <conio.h>
void main()
int i;
int a[][3]={{1,2,3},{4,5,6},{7,8,9}};
clrscr();
printf("\tElements of An Array with their addresses. \n\n");
for(i=0;i<9;i++)
{
printf("%8u",&a[0][0]+i);
printf(" [%d]",*(&a[0][0]+i));
if(i==2 || i==5)
printf("\n");
}
}
OUTPUT:
Elements of an Array with their addresses.
1 [4052] 2 [4054] 3 [4056]
4 [4058] 5 [4060] 6 [4062]
7 [4064] 8 [4066] 9 [4068]
Explanation:
The logic of the program is the same as the previous one. The only difference is that the array
name itself is used as pointer. The if statement inserts a line after displaying every three
elements.
So far, we have studied array of different standard data types such as array of int, float, characters, etc. In
the same way the ‘C ’ language also supports array of pointers. It is nothing but a collection of addresses.
Here, we store address of variables for which we have to declare array as pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int *arrp[3];
int arrl[3]={5,10,15},k;
for(k=0;k<3;k++)
arrp[k]=arrl+k;
clrscr();
printf("\n\tAddress Element\n");
for(k=0;k<3;k++)
{
printf("\t%u",arrp[k]);
printf("\t%7d \n",*(arrp[k]));
}
}
OUTPUT:
Address Element
4060 5
4062 10
4064 15
Explanation:
In this program *arrp[3] is declared as array of pointer. Using first for loop addresses of
various elements of array ‘arr1[]’ are assigned to ‘*arrp[]’. The second for loop
picks up addresses from ‘*arrp[]’ and displays the value present at those locations. Here,
each element of ‘*arrp[]’ points to respective element of array ‘arr[]’. Table 2.1 shows
elements of array and their addresses using array of pointers.
# include <stdio.h>
# include <conio.h>
void main()
{
int a[5]={0,1,2,3,4};
int *p[5],i;
for(i=0;i<5;i++)
p[i]=a+i;
clrscr();
for(i=0;i<5;i++)
{
printf("\n\t%d at location",*(*p+i));
printf("\t%u at location ",*(p+i));
printf("%u",p+i);
}
Explanation:
In this program the first for loop assigns addresses of elements of integer array to pointer
array. The first printf() statement prints element, the second displays address of element
and the third displays address of the address, i.e. address of the pointer. Thus, it is clear from
the above example that integer requires two bytes and pointer requires four bytes.
Example 2.37 Write a program to read string from keyboard and display it using character pointer.
#include <stdio.h>
#include <conio.h>
void main()
{
char name[15],*ch;
printf("Enter Your Name:");
gets(name);
ch=name;
/* store base address of string name */
while(*ch!='\0')
{
printf("%c",*ch);
ch++;
}
}
OUTPUT:
Enter Your Name: KUMAR
KUMAR
Explanation:
Here, the address of 0th element is assigned to character pointer ‘ch’. In other words, base
address of string is assigned to ‘ch’. The pointer ‘*ch’ points to the value stored at that
memory location and it is printed through printf() statement. After every increment of
‘ch’ the pointer goes to the next character of the string. When it encounters NULL character
the while loop terminates the program.
Example 2.38 Write a program to find length of a given string including and excluding
spaces using pointers.
# include <stdio.h>
# include <conio.h>
void main()
{
char str[20],*s;
int p=0,q=0;
clrscr();
printf("Enter String:");
gets(str);
s=str;
while(*s!='\0')
{
printf("%c",*s);
p++;
s++;
if(*s==32) /* ASCII equivalnet of ' ' (space)is 32*/
q++;
}
printf("\nLength of String including spaces: %d",p);
printf("\nLength of String excluding spaces: %d",p-q);
}
OUTPUT:
Enter String: POINTERS ARE EASY
POINTERS ARE EASY
Length of String including spaces: 17
Length of String excluding spaces: 15
Explanation:
Here, the address of 0th element is assigned to character pointer ‘ch’. In other words, base
address of string is assigned to ‘ch’. The pointer ‘*ch’ points to the value stored at that
memory location and it is printed through printf() statement. After every increment of
‘ch’ the pointer goes to the next character of the string. When it encounters NULL character
the while loop terminates the program.
Example 2.39 Write a program to find length of a given string including and excluding
spaces using pointers.
# include <stdio.h>
# include <conio.h>
void main()
{
char str[20],*s;
int p=0,q=0;
clrscr();
printf("Enter String:");
gets(str);
s=str;
while(*s!='\0')
{
printf("%c",*s);
p++;
s++;
if(*s==32) /* ASCII equivalent of ' ' (space)is 32*/
q++;
}
printf("\nLength of String including spaces: %d",p);
printf("\nLength of String excluding spaces: %d",p-q);
}
OUTPUT:
Enter String: POINTERS ARE EASY
Explanation:
This program is the same as the previous one. Here, the counter variables ‘p’ and ‘q’ are
incremented to count number of characters and spaces found in the string. The ASCII value of
space is 32. Thus, at the end of the program both the variables are printed.
Example 2.40 Write a program to interchange elements of character array using pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
char *names[]={
"kapil",
"manoj",
"amit",
"amol",
"pavan",
"mahesh"
};
char *tmp;
clrscr();
printf("Original: %s %s",names[3],names[4]);
tmp=names[3];
names[3]=names[4];
names[4]=tmp;
printf("\nNew: %s %s",names[3],names[4]);
}
OUTPUT:
Original: amol pavan
New: pavan amol
Explanation:
In this program character array *names [] is declared and initialized. Another character pointer
*tmp is declared. The destination name that is to be replaced is assigned to variable ‘*tmp’.
The destination name is replaced with source name and the source name is replaced with *tmp
variable. Thus, using simple assignment statements two names are interchanged.
SUMMARY
1. Array is a very popular and useful data structure used to store data elements in successive
manner. Array is a linear and homogenous data structure. Homogenous means same types
of elements are stored in it. It may be non-homogenous if structure is associated with it.
2. One-dimensional arrays having only one row of elements and elements are stored in suc-
cessive memory locations.
3. The various operations carried out with arrays are insertion, deletion, searching, sorting, etc.
4. Two-dimensional arrays can be thought of as rectangular display of elements with rows
and columns.
5. A three-dimensional arrays can be thought of as array of arrays.
6. The collection of characters, digits, and symbols enclosed within quotation marks are
called strings. Strings are always declared as character array. Like other type of arrays,
elements of character array are also stored in continuous memory location. Each
character array occupies one byte in memory. One different thing about strings is that,
the compiler automatically adds NULL (‘\0’) character at the end of string.
EXERCISES
A. Answer the following questions:
1. Write a program to declare and initialize a one-dimensional float type array. Display its
elements with memory addresses.
2. Write a program to read ten integers in an array and find the number of even and odd integers
in it.
3. Write a program to input a number containing five digits. Obtain cube of each digit and dis-
play the cubes of all digits on the screen.
4. Read the marks in physics, chemistry and mathematics obtained by ten students. Display the
top three students with their total marks.
5. Write a program to enter a four-digit number using array and arrange it in the reverse order.
6. Write a program to demonstrate use of two-dimensional array.
7. Write a program to access elements of an array using another pointer.
8. Write a program to declare a character array. Initialize it with the string and display the contents.
9. Write a program to demonstrate the use of multi-dimensional array.
10. Write a program to create array of structure objects. Read and display the data.
1. 3.
#include <stdio.h> # include <stdio.h>
#include <conio.h> # include <conio.h>
void main() void main()
{ {
char nam[10],*h; int i;
printf("Enter Your Name:"); char name[10];
scanf("%s",&nam); clrscr();
h=nam; scanf("%s",name);
while(*h!='\0') for(i=0;i<10;i++)
{ {
printf("%c",*h); if(name[i]=='\0')
h++; exit(1);
} printf("%c",
} name[i]+3);
}
2.
}
# include <stdio.h>
# include <conio.h>
void main()
4.
# include <stdio.h>
{
# include <conio.h>
int x[5]={12,42,
26,18,80},a=0;
void main()
clrscr();
{
while(a<5)
char name[6]="DATA";
{
clrscr();
printf("\n%d",x[a]);
printf("%c",name);
a++;
printf("%s",name);
}
} }
Recursion
3.1 INTRODUCTION
A function is a block of statement that can be used to perform a specific task. Figure 3.1 describes the various
parts of the function. A function has the following parts:
a. Function prototype declaration
b. Definition of a function (function declarator)
c. Actual and formal arguments
d. Return statement
e. Invoking function.
void main ()
{
float sum (float, int); Function Prototype
float x,y=2.4;
int z=5; Actual Arguments
}
x=sum( y, z ); Function Call
} Formal Arguments
Return Statement
In example (A) the return type is void, i.e. the function will not return any value. The void functions are
always without return statement. The argument void indicates the function will not require any argument.
By default, every function returns an integer value. To return a non-integer value, the data type should
be mentioned in function prototype and definition. While writing definition of function, the return type
must be preceded by the function name and it is optional if return type is default (int).
In example (B) the prototype of function sum() is declared. Its return type is float and arguments are
float and integer type, respectively. It is shown in the Fig. 3.1.
In example (C) with argument type, argument names are also declared. It is optional and not compulsory
to use the same variable name in the program.
void main()
{
x=sum( y, z );
}
Actual Arguments
As shown in Fig. 3.2, variable y and z are actual arguments and j and k are formal arguments. The
values of y and z are stored in j and k, respectively. The values of actual arguments are assigned to formal
arguments. The function uses formal arguments for computing.
Recursion a little bit tough, but if we keep track of the program, in which sequence it is executing, it is easy
to understand. Recursion is one of the most dominant tools used in the programming technique. There
are various situations when we need to execute a block of statements for a number of times depending
on condition at that time recursion is useful. Recursion is used to solve a problem, which has iterations
in reverse order. Data structures also support recursion, for example, tree supports recursion. Various
programs are solved with the use of recursion. The major application of recursion is game programming
where series of steps can be solved with recursion.
When a function calls itself recursively it is known as recursion. Recursion is of two types:
1. Direct recursion
2. Indirect recursion.
The recursion in which the function calls itself is called direct recursion. In this type, only one function is
involved. In indirect recursion two functions call each other. Fig. 3.3 describes direct and indirect recursion.
int num ()
{
−−−
int sum ();
}
int num ()
{ int sum ()
{
−−−
−−−
int num (); num();
} }
Recursion is one of the applications of stack. Stacks are explained in the next chapter. There are several
problems in which without recursion their solution is lengthy.
The programming languages like C, C++ allow us to define user defined functions. Functions in the
programming languages are very useful, because by using function a separate block of statements can be
defined. This block can be invoked a number of times anywhere in the program. A function which can call
itself or another function and which eventually calls another function is known as recursive function.
Two essential conditions should be satisfied by a recursive function. First, every time a function should
call itself directly or indirectly. Second, the function should have a condition to stop the recursion. Other-
wise, an infinite loop is generated and the loop will not lose control.
Some people think that recursion is a very needless luxury in the programming language. Using itera-
tion one can solve the problems. However, in programming in some situation there is no substitute for
recursion.
There are some problems associated with recursive function which are not present in the non-recursive
function. The recursive function can be invoked by itself or any other function. To make sure execution it
is very essential for the function to save the return address in order to return to proper location. Also, the
function has to save the formal and local variables.
Example 3.1 Write a program to calculate factorial of given number using recursion using
C statements.
# include <stdio.h>
# include <conio.h>
int fact(int);
void main()
{
int num,f;
clrscr();
printf("\n Enter a number:");
scanf("%d",&num);
f=fact(num);
printf("\n Factorial of %d is %d",num,f);
}
int fact(int f)
{
if(f==1) return f;
else return f*fact(f-1);
}
OUTPUT
Enter a number: 4
Factorial of 4 is 24
Explanation:
In this program fact()is a recursive function. The entered number is passed to function
fact(). When function fact()is executed, it is repeatedly invoked by itself. Every time a
function is invoked, the value of f is reduced by one and multiplication is carried. The recursive
function produces the numbers 4,3,2 and 1. The multiplication value of these numbers is taken
and returned to main()function.
1. In recursion, it is essential for a function to call itself, otherwise recursion will not take place, as shown
in Fig. 3.3.
2. Only user defined function can be involved in the recursion. Library function cannot be involved in
recursion because their source code cannot be viewed.
# include <stdio.h>
# include <conio.h>
# include <process.h>
main();
void main()
{
switch(str[x])
{
case 'H':
clrscr();
default:
printf("%c",str[x]);
break;
case '\0':
exit(0);
break;
}
x++;
main();
}
OUTPUT
Have a Good Day
Explanation:
In this program main()function is invoked recursively. A prototype of function main()is given
before function definition. However, in normal practice it is not necessary to give prototype.
A recursive function must know its return type and number and type of the arguments passed.
In this type, only one function is involved which calls itself until the given condition is true. Consider the
following program:
Example 3.3 Write a program call function main () and perform sum of first five numbers.
# include <stdio.h>
# include <process.h>
void main(int);
int x,s;
void main(x)
{
s=s+x;
printf("\n x=%d s=%d",x,s);
if(x==5) exit(0);
main(++x);
}
OUTPUT
x=1 s=1
x=2 s=3
x=3 s=6
x=4 s=10
x=5 s=15
Explanation:
In this program variable x and s are declared outside main(). Initially their values are zero.
The prototype of function main() is declared. This is essential because while calling, the
compiler checks the function with its prototype. It is advance information regarding the function.
The variable x is passed to the function main(). The variable x is added to variable s until
the value of x reaches to five. When x reaches to five, the program terminates. The steps of
recursive function main(++x) are shown in Table 3.1.
# include <stdio.h>
# include <conio.h>
# include <process.h>
void main()
{
int trinum(int);
int t,x;
clrscr();
printf("\n Enter a Number: ");
scanf("%d",&x);
t=trinum(x);
printf("\n Triangular Number of %d is %d",x,t);
}
int trinum(int x)
{
int f=0;
if(x==0) return f;
else f=f+x+trinum(x-1);
return f;
}
OUTPUT
Enter a Number: 4
Triangular Number of 4 is 10
Explanation:
In this program a function trinum() is defined which calls itself. An integer is entered through
the keyboard and it is stored in the variable x. The function trinum() calls itself and the
argument passed to this called function is one less than the value of x. The return values are
added to variable f. When value of x becomes zero, the recursion ends and the total is returned
to variable t in function main(). The value of t is triangular number of the entered number,
which is nothing but the sum of all the integers between 1 to the entered number. The recursive
function is called repetitively by itself without completing the execution of previous call. When
program ends and the control is about to return to caller function the number of times the
return statement executed is equal to the number of times the function is called recursively.
The compiler keeps track of how many times a function is executed. To see, these points
execute the program in single step by pressing F7 key.
When a function returns three actions are done. The return address is placed in the safe location. The
data stored in local variables of function are freed. The previously saved address is retrieved. The return
value of function is returned and put in the safe location and calling program receives it. Normally, the
location is a hardware register, which is placed in CPU for the same purpose.
As shown in Fig. 3.5 the main function call invokes the function B. The function B invokes the
function C and again C invokes D. The figure shows the control is present in the function D. In every
function, a location is reserved for storage of return address. The return address location of function D
contains the address of statement in C immediately after the function invocation statement.
Call on B
Call on C Call on D
Whenever a recursive function invokes itself, new data variables are created and memory is allocated
for data. The data area contains all local variables and return address. In the recursion function, the data
area is not connected only with the function but closely associated with the particular function call.
In every call new data area is allocated and the particular function’s data area contains information about
recent call. Every time control returns, the data area is de-allocated or freed and the former data area turn
into current.
The recursion in C and C++ language is more expensive as compared to non-recursive function. It takes
not only more space but it is also time consuming. In some system program such as compiler or operating
system, if program contains recursive function it will be executed many a time. In such a case, an alternate
non-recursive function may be defined.
Example 3.5 Write a program to define to a recursive function to calculate factorial of a number.
# include <stdio.h>
# include <conio.h>
void main()
{
int fact(int);
int f,n;
clrscr();
int fact(int j)
{
int f=1;
if(j==1)
return 1;
else
f=j*fact(j-1);
return f;
}
OUTPUT
Enter a Number: 5
Factorial of Number 5: 120
Explanation:
In this program function fact() is defined and the number entered by the user is passed to
the function fact(). In the function fact() the if statement checks the value of variable j
(formal argument of n). If the value is 1 the return statement returns 1 otherwise the fact()
function is called. At this point, the recursion actually begins. When the function fact() is
recursively invoked the value of j is decreased by one and then passed.
In this type of recursion two or more functions are involved in the recursion. The indirect recursion
does not make any overhead as direct recursion. When control exits from one function and enters into
another function, the local variables of former function are destroyed. Hence, memory is not engaged.
The following program explains the indirect recursion.
# include <stdio.h>
# include <conio.h>
# include <process.h>
int s;
void show(void);
void main()
{
if(s==5) exit(0);
show();
}
void show()
{
printf("%d",s);
s++;
main();
}
OUTPUT
0 1 2 3 4
Explanation:
In this program two user defined functions are defined main() and show(). The s is global
variable. The main() function invokes show() function and the show() function invokes
main() function. The value of s is increased and displayed. When value of s reaches to 5 the
program is terminated.
# include <stdio.h>
# include <conio.h>
# include <process.h>
int s;
void show(void);
void main()
{
if(s==0)
clrscr();
if(s==10)
exit(0);
show();
}
void show()
{
int j;
for(j=0;j<=s;j++)
printf("%d",j);
printf("\n");
s++;
main();
}
OUTPUT
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8 9
Explanation:
This program is same as the last one. Here, depending on the value of variable s, the iteration of
for loop is performed.
We have studied both recursion and iteration. They can be applied to a program depending upon the
situation. Table 3.2 explains the differences between recursion and iteration.
Initialization The variables involved in the iteration process are initialized. These variables are used to
decide when to end the loop.
Loop continuation condition The loop variable is used to decide whether to continue or discontinue the
loop. When the condition is true, control goes to computation blocked or else it exits.
Entry
Initialization
False
Condition Exit
?
True
Update Computation
Preliminary part The use of this block is to store the local variables, formal arguments and return
address. The end-part will restore these data. Only recently saved arguments, local variables and return
address are restored. The variables last saved are restored first.
Body If the test condition is satisfied, it performs the complete processing and control passes to end-
block. If not, partial processing is performed and a recursive call is made. The body also contains call
to itself and one or more calls can be made. Every time a recursive call is made, the preliminary part of
the function saves all the data. The body also contains two processing boxes i.e. partial processing and
complete processing. In some programs, the result can be calculated after complete processing. For this,
the recursive call may not be required. For example, we want to calculate factorial of one. The factorial
of one is one. For this, it is needless to call function recursively. It can be solved by transferring control to
complete processing box.
In other case, if five is given for factorial calculation, the factorial of five can be calculated in step 8.
Hence, the function will be called recursively. Every time one step is solved, i.e. 5*4*3 and so on. Hence,
it is called partial processing.
Depth of Recursion The recursion function calls itself infinite times. If we want to calculate factorial
of five then we can easily estimate the number of times the function would be called. In this case, we can
determine the depth of the recursive function. In complex program, it is difficult to determine the number
of calls of recursive function.
Entry
Preliminary part
Condition
Check Partial
? Processing Body
Function
Complete call to
Processing itself
Restore previously
stored local variables,
formal parameters and
return address.
End-part
Exit
The Tower of Hanoi has historical roots in the ceremony of the ancient tower of Brahma. There are n disks
of decreasing sizes mounted on one needle as shown in the Fig. 3.8(a). Two more needles are also required
to stack the entire disk in the decreasing order. The use of third needle is for impermanent storage. While
mounding the disk, following rules should be followed.
1. At a time only one disk may be moved.
2. The disk may be moved from any needle to another needle.
3. No larger disk may be placed on the smaller one.
Our aim is to move the disks from A to C using the needle B as an intermediate by obeying the
above three conditions. Only top-most disks can be moved to another needle. The following figures and
explanation clear the process of Tower of Hanoi stepwise.
In Fig. 3.8 (a), the three needles are displayed in the initial state. The needle X contains three disks and
there are no disks on needle Y and Z.
In Fig. 3.8 (b), the top-most disk is moved from needle X to Z. The arrow indicates the movement of
disk from one needle to another needle.
In Fig. 3.8 (c), the disk from the X needle moves to the Y needle.
In Fig. 3.8 (d), the disk from Z needle moves to Y needle. Needle Y has two disks.
In Fig. 3.8 (e), the disk from the X needle moves to the Z needle. Now there is no disk in the X needle.
In Fig. 3.8 (f ), the disk from the Y needle moves to the X needle.
In Fig. 3.8 (g ), the disk from the Y needle moves to the Z needle. The Y needle contains no disk.
In Fig. 3.8 (h ), the disk from the X needle moves to the Z needle. Thus, the Z needle contains all the
three disks of the X needle shown in Fig. 3.8 (a). Thus, the problem is solved.
X Y Z X Z
Figure 3.8 (a) Towers of Hanoi Figure 3.8 (b) Towers of Hanoi
X Y Z X Y Z
Figure 3.8 (c) Towers of Hanoi Figure 3.8 (d) Towers of Hanoi
X Y Z X Y Z
Figure 3.8 (e) Towers of Hanoi Figure 3.8 (f) Towers of Hanoi
X Y Z X Y Z
Figure 3.8 (g) Towers of Hanoi Figure 3.8 (h) Towers of Hanoi
# include <conio.h>
# include <stdio.h>
void hanoi(int,char,char,char);
main()
{
int num;
clrscr();
printf("\n Enter a Number:");
scanf("%d",&num);
clrscr();
hanoi(num,'A','C','B');
}
void hanoi( int num, char f_peg,char t_peg, char a_peg)
{
if(num==1)
{
printf("\nMove disk 1 from Needle %c to Needle %c",
f_peg,t_peg);
return;
}
hanoi(num-1,f_peg,a_peg,t_peg);
printf("\nMove disk %d from Needle %c to Needle %c",num,
f_peg,t_peg);
hanoi(num-1,a_peg,t_peg,f_peg);
}
Output
Enter a number:3
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
Explanation:
In the above program numbers of disks are entered. The function Hanoi() is invoked from main().
The A, B and C are needles. If value of num is one, the disk is transferred from A to C and program
ends. If the value of num is greater than one, then the Hanoi() function invokes itself recursively,
every time value of num is decreased by one. The output of the program is shown as above.
Advantages of recursion,
1. Sometimes, in programming a problem can be solved without recursion, but at some situations in
programming it is must to use recursion. For example, a program to display the list of all files of the
system cannot be solved without recursion.
2. The recursion is very flexible in data structure like stacks, queues, linked list and quick sort.
3. Using recursion, the length of the program can be reduced.
Disadvantages of recursion,
1. It requires extra storage space. The recursive calls and automatic variables are stored on the stack.
For every recursive call separate memory is allocated to automatic variables with the same name.
2. If the programmer forgot to specify the exit condition in the recursive function, the program
will execute out of memory. In such a situation user has to press ctrl + break to pause or stop the
function.
3. The recursion function is not efficient in execution speed and time.
4. If possible, try to solve problem with iteration instead of recursion.
In this chapter we have studied the recursion and we can say that recursion is a method in which set of
process is given in terms of itself. Recursive function is invoked by itself and this function call is given
in the same function body. If the function call statement is the last statement in the function body, this
recursion is called tail recursion. In tail recursion, we can use return value as a parameter for the function.
All the functions are maintained using stack, however, in this type, it will not push new function call to
stack but it will replace the value of previous call with new call. This approach provides decreased stack
implementation time and gives faster execution. The program 3.7 is an example of tail recursion.
We have studied both advantages and disadvantages of recursion. We have also studied the iteration which
is an alternative to recursion. The major overhead of recursion is the memory it occupies and execution
time. A non-recursive function will require minimum memory and less time for execution as compared
with recursive function. The recursive function uses stack to push and pop the local variables. In non-
recursive function the above push and pop operations with stack can be skipped. However, at some
situation in programming use of recursion is must. If the part of program is to be invoked frequently,
in such a case it is better to develop non-recursive function.
SUMMARY
1. A function is a block of statement that can be used to define to perform a special task.
2. A function prototype declaration consists of function’s return type, name, and arguments list.
It tells the compiler, a) name of the function, b) type of value returned, c) the type and number
of arguments.
3. When a function calls itself is called direct recursion.
4. When a function calls another function it is called indirect recursion.
5. The user defined function main() can be invoked recursively. The library functions cannot be
invoked recursively.
6. Constructor is a special member function used to initialize the data members. The con-struc-
tor can also be invoked recursively.
7. Whenever a recursive function invokes itself, every time new data variables are created and
data area and memory is allocated. The data area contains all local variables and return address.
In the recursion of function, the data area is not connected only with the function but closely
associated with the particular function call.
EXERCISES
A. Answer the following questions:
1. Explain recursion process in detail.
2. Differentiate between direct and indirect recursions.
3. Differentiate between recursion and iteration.
4. Mention the advantages and disadvantages of recursions.
5. List the three rules of tower of Hanoi problem.
6. What is depth of recursion?
7. Explain tail recursion.
B. Attempt following program with recursion:
1. Write a program to compute the nth number of fibonacci series.
2. Write a program to reverse a number.
3. Write a program to display list of prime numbers up to 15.
4. Write a program to display a message “Hello” for 10 times.
5. Write a program to call function main() recursively.
6. Write a program to demonstrate tail recursion.
7. Consider the following list
58, 69, 59, 12, 34, 78, 25, 98, 20
a. Find elements 34, 25 and 20 using recursion
void main() }
{
clrscr();
show();
}
Stacks
4.1 INTRODUCTION
In this chapter, we will study one of the most important simple data structures ‘stack’. Stack is an important
tool in programming languages. Stack is one of the most essential linear data structures. Implementation
of most of the system programs is based on stack data structure. We can insert or delete an element from a
list, which takes place from one end. The insertion of element onto the stack is called as “push” and deletion
operation is called “pop”, i.e. when an item is added to the stack the operation is called “push” and when it
is removed the operation is called “pop”. Due to the push operation from one end, elements are added to
the stack, the stack is also known as pushdown list. The most and least reachable elements in the stack are
respectively known as the “top” and “bottom” of the stack. A stack is an arranged collection of elements into
which new elements can be inserted or from which existing new elements can be deleted at one end. Stack is
a set of elements in a last-in-first-out technique. As per Fig. 4.1 the last item pushed onto the stack is always
the first to be removed from the stack. The end of the stack from where the insertion or deletion operation
is carried out is called top. In Fig. 4.1(a) a stack of numbers is shown.
As shown in Fig. 4.1(a) the stack is based on the rule last-in-first-out. The element appended lastly is
deleted first. If we want to delete element 3 it is necessary to delete the top elements 5 and 4 first.
Top of
5 the stack
Figure 4.1 (a) Stack Figure 4.1 (b) A Pot with Plates
In Fig. 4.1 (b), you can see a pot containing plates kept one above the other. Plates can be inserted or
removed from the top. The top plate can be removed first in case pop operation is carried out, otherwise
plates are to be added on the top. In other words, the removal operation has to be carried out from the
top. Thus, placing or removing takes place from top, i.e. from the same end.
4 4 4
3 3 3 3 3
2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 1
push pop
As shown in the first half portion of Fig. 4.2, first integer 1 is inserted and then 2,3,4 and 5 are pushed
on to the stack. In the second half portion of the same figure, the elements are deleted ( pop) one by one
from the top. The elements are deleted in the order from 5 to 1. The insertion and deletion operation is
carried out at one end (top). Hence, the recently inserted element is deleted first. If we want to delete a
particular element of the stack, it is necessary to delete all the elements present above that element. It is
not possible to delete element 2 without deleting elements 5,4 and 3. The stack expands or shrinks with
the passage of time. In the above example, the stack initially expands until element 5 is inserted and then
shrinks after removal of elements. There is no higher limit on the number of
elements to be inserted in the stack. The total capacity of stack depends on
memory of the computer.
In practical life, we come across many examples based on the principle
of stack.
Fig. 4.3 illustrates the stack of books that we keep in the order. Whenever
we want to remove a book the removal operation is made from the top or
new books can be added at the top. Figure 4.3 Stack of Books
Another example of stack that can be cited is a railway system for shunting the trains. Figure 4.4 shows
a picture of a railway system. The incoming trains arrive in a sequence in the stack. The last entered train
in the stack leaves first and it will be placed at first on the outgoing track.
STACK
Stack: Stack is a memory portion, which is used for storing the elements. The elements are stored based
on the principle of last-in-first-out. In the stack the elements are kept one above the other and its size is
based on the memory.
Top: The top of the pointer points to the top element in the stack. The top of the stack indicates its
door from where elements are entered or deleted. The stack top is used to verify stack’s current position,
i.e. underflow, overflow, etc. The top has value 0 when the stack is empty. Some programmers assign
−1 to the top as initial value. This is because when the element is added, the top is incremented and it
would become zero. The stack is generally implemented with the help of an array. In an array, counting
of elements begins from 0 onwards. Hence, on the similar grounds stack top also begins from 0 and it
is convenient to assign −1 to top as initial value.
Stack Underflow: When there is no element in the stack or stack holds elements less than its capacity, the
status of stack is known as stack underflow. In this situation, the top is present at the bottom of the stack.
When an element is pushed, it will be the first element of the stack and top will be moved one step up.
Stack Overflow: When the stack contains equal number of elements as per its capacity and no more
elements can be added, the status of stack is known as stack overflow. In such a position, the top rests at
the highest position.
Storage: A function containing local variables and constants are stored in stack. Only global variables
are stored in a stack frame.
Stack Frames: This data structure holds all formal arguments; return address and local variables on the
stack at the time when function is invoked.
2 2 Top
3 3
4 4 Bottom
Stack is very helpful in every ordered and chronological processing of functions. The most useful appli-
cation of stack is in recursion (explained in previous chapter). It saves memory space. The mechanism of
stack last-in-first-out (LIFO) is commonly useful in applications such as manufacturing and accounting
calculations. It is a well-known accounting concept.
dynamic implementation is achieved using pointers. Using pointer implementation at run time there is no
restriction on the number of elements. The stack may be expandable. The memory is efficiently utilized
with pointers. Memory is allocated only after element is pushed to the stack. Both the above implementa-
tions are illustrated with suitable examples in the next section.
The following fundamental operations on stack can be carried out through static implementation called
array implementation. Operation on stack is also possible with the pointers, which is called dynamic
implementation. We will perform the following operations on the stack:
1. Creating a stack
2. Checking stack—either empty or full
3. Initializing a stack
4. Insert (push) an element in the stack
5. Delete (pop) an element from the stack
6. Access the top element
7. Display elements of stack
8. Status: to identify present position of stack.
Some terms relevant to the stack operations are narrated below.
Push The procedure of inserting a new element to the top of the stack is known as push operation. After
every push operation, the top is incremented by one. When the array is full (if static implementation is
applied) the status of stack is full and this condition is called as stack overflow. In such a case, no further
element can be inserted. After every push operation, the new element rests at the top. Fig. 4.6 (a) shows
the push operation.
8 TOP
5 TOP
5
2 TOP 2
2
Pop The procedure of removing element from the top of the stack is called as pop operation. After
every pop operation, the top is decremented by one. If there is no element in the stack, it is called as
empty stack or stack underflow. In such a case, pop operation cannot be applied. Fig. 4.6 (b) illustrates
the pop operation.
8 TOP
5 5 TOP
2 2 2 TOP
Stack Initially pop(8) pop(2)
EMPTY
STACK
The stack can be supposed as a one-dimensional array. The various operations listed above can be car-
ried out with one-dimensional array. The following programs explain the operations listed above.
As mentioned earlier, one-dimensional array can be used as stack. We can add elements in the one-
dimensional array. Using if statement we can check whether the stack is empty or full. Consider the
following programs.
Example 4.1 Write a program to explain working of stack using array implementation.
# include <stdio.h>
# include <conio.h>
# include <process.h>
void main()
{
int j,stack[5]={0};
int p=0;
clrscr();
printf("Enter Elements, put zero to exit: \n");
while(1)
{
scanf("%d",&stack[p]);
if(stack[p]==0)
{
printf("\n By choice terminated: ");
exit(0);
}
p++;
if(p>4)
{
printf("Stack is full \n");
break;
}
else
printf("Stack is empty\n");
}
OUTPUT
Enter Elements, put zero to exit:
4
Stack is empty
2
Stack is empty
6
Stack is empty
8
Stack is empty
3
Stack is full
Elements of stack are: 4 2 6 8 3
Explanation:
In this program, an array stack [5] is declared. Using while loop, elements are entered
through the keyboard and placed in an array. The value of variable p is initially zero. The
variable p increases in every iteration. The variable p acts as a top of stack. The if statement
checks the value of p. If the value of p is greater than four it means that, the stack is full and no
more elements can be added. When value of p is less than four it means more elements can
be inserted in the stack. The for loop and printf statements display the array elements. In
stack random insertion or deletion of element is not possible (Though in array it is possible,
but here keep in mind that we are treating an array as a stack. Hence, we have to follow the
restrictions that exist in stack implementation). If we want to delete any particular element,
we have to delete every element present before that element. Example 4.2 explains deletion
of elements.
Example 4.2 Write a program to store elements in the stack. Delete the specified element.
# include <stdio.h>
# include <conio.h>
void main()
{
int j,stack[5];
int p=0;
clrscr();
printf("Enter Elements
put zero to exit: \n");
while(1)
{
scanf("%d",&stack[p]);
if(stack[p]==0)
break;
p++;
if(p>4)
{
printf("Stack is full \n"); break;
}
}
for(j=0;j<5;j++)
printf(" %d ",stack[j]);
printf("\n Enter element position to delete: ");
scanf("%d",&p);
p−−;
for(j=4;j>=p;j−−)
stack[j]=NULL;
printf("Stack elements are:");
for(j=0;j<5;j++)
printf(" %d ",stack[j]);
getch();
}
OUTPUT
Enter Elements put zero to exit:
8
9
4
5
3
Stack is full
Stack elements are: 8 9 4 5 3
Enter element position to delete: 3
Stack elements are: 8 9 0 0 0
Explanation:
In this program, as usual, elements are entered and stored in the stack. The element number
that is to be deleted from the stack will be entered from the keyboard. Recall that in a stack,
before deleting any element, it is compulsory to delete all elements before that element. In this
program, the elements before a target element are replaced with value NULL (0). The elements
after target element are kept as it is. Recall that when an element is inserted in the stack the
operation is called “push.” When an element is deleted from the stack the operation is “pop”.
Example 4.3 explains the push and pop operations.
Example 4.3 Write a simple program to demonstrate push () and pop () operations.
# include <stdio.h>
# include <conio.h>
void main()
{
void push(int);
void pop(void);
void show(void);
clrscr();
printf("\n\n push operation: ");
push(5);
show();
push(8);
show();
push(9);
show();
printf("\n\n pop operation: ");
show();
pop();
show();
pop();
show();
}
void push(int j)
{
top++;
stack[top]=j;
}
void pop()
{
stack[top]=0;
top−−;
}
void show()
{
int j;
printf("\n stack elements are: ");
for(j=0;j<10;j++)
stack[j]!=0 ? printf(" %d ",stack[j]): printf("");
}
OUTPUT
push operation:
stack elements are: 5
stack elements are: 5 8
stack elements are: 5 8 9
pop operation:
stack elements are: 5 8 9
stack elements are: 5 8
Stack elements are: 5
Explanation:
In this program stack[ ] and top variables are declared as static. The variable top is
initialized with −1. It is not necessary to declare top as static. The push() operation inserts
an element into the stack. The next element pushed is displayed after the first element and so
on. The pop() operation removes the element from the stack. The last element inserted is
firstly deleted. In the output the element 9 is deleted first as it is entered at last. Then, element
8 is removed. At last, the stack holds only one element having number 5.
Example 4.4 Write a program to perform pop and push operation on the stack.
# include <stdio.h>
# include <conio.h>
# include <process.h>
int ch,p,z=6,top=0,stack[7],c;
void main()
{
void make();
void push();
void pop();
void show();
clrscr();
while(ch<5)
{
printf("\n1] Stack creation");
printf("\n2] Insert element");
printf("\n3] Delete Element");
switch(ch)
{
case 1: make();
continue;
case 2: push();
continue;
case 3: pop();
continue;
case 4: show();
continue;
default: exit(0);
}
}
}
void make()
{
printf("\n Enter number of elements in the stack(<7): ");
scanf("%d",&c);
for(top=0;top<c;++top)
{
printf("Enter Element[%d]:- ",top);
scanf("%d",&stack[top]);
}
}
void push()
{
if(top>=z)
{
printf("\n Stack is full");
getch();
}
else
{
printf("\n Enter number to be pushed:-");
scanf("%d",&stack[top]);
top++;
}
}
void pop()
{
if(top<=0)
{
printf("\nThe stack is empty ");
getch();
}
else
top−−;
}
void show()
{
for(p=top-1;p>=0;−−p)
printf(" %d ",stack[p]);
}
OUTPUT
1] Stack creation
2] Insert element
3] Delete Element
4] Display Element
5] Exit
1
1] Stack creation
2] Insert element
3] Delete Element
4] Display Element
5] Exit
Explanation:
In this program, few variables and array stack[] are declared globally so that every program
can access them. Functions make(), push(), void pop() and void show() are defined.
When the user selects first option, the user is prompted to enter the number of elements.
Immediately followed by this, the user has to enter choice by entering the integer number. These
integers are stored in the array stack[]. The user can see the stack elements by selecting
fourth choice on the screen. When delete operation is performed, the top element of the stack is
deleted. The user can confirm this operation by selecting display element operation. When insert
operation is selected, the entered element is inserted at the top of the stack. The global variable
top keeps track of the total number of elements present in the stack.
Example 4.5 Write a program to create a class stack and define member function push () and pop ().
# include <stdio.h>
# include <conio.h>
int stacks[10];
int top;
void main()
{
void show(void);
void push(int j);
void pop();
int n,j;
top=-1;
stacks[10]=NULL;
clrscr();
printf("\nInsert four element in stack");
printf("\npush operation: ");
push(2);
push(5);
push(8);
push(3);
show();
printf("\n\nDeleting two elements from the stack");
printf("\npop operation: ");
pop();
pop();
show();
}
void push(int j)
{
top++;
stacks[top]=j;
void pop()
{
stacks[top]=0;
top−−;
}
void show()
{
int j;
for(j=0;j<=top;j++)
printf(" %d",stacks[j]);
}
OUTPUT
Insert four element in stack
push operation: 2 5 8 3
Deleting two elements from the stack
pop operation: 2 5
Explanation:
In this program an array stack is declared. Both two array variables, stacks[10] and
top, are static and they are initialized. Three-functions push(), pop() and show() are
defined. Invoking push() function elements are pushed. The pop() operation removes the
elements. The show() function displays the elements of stack.
We are already aware that array name itself is a pointer. Pointer implementation of a stack carried out is simi-
lar to array implementation. Stack is particularly straightforward version of linked lists. The implementation
of array-like elements can be done using pointer. Like array, elements can be stored in successive memory
location using pointer. First, we learn a simple program to create stack using pointer instead of array.
Example 4.6 Write a program to create an array-like list of integers using pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int *p,j,e;
clrscr();
printf("\n Enter five Integers: ");
for(e=4;e>=0;e−−)
scanf("%d",(p+e));
for(e=0;e<5;e++)
printf("%d ",*(p+e));
OUTPUT
Enter five Integers: 4 5 8 7 5
5 7 8 5 4
Explanation:
In this program a pointer *p is declared. The first for loop is used to read numbers in association
with scanf() statement. The value of variable e is added to the address of pointer p, so
that successive locations are accessed and the number is placed in the location. For displaying
elements, same logic is applied.
Example 4.7 Write a program to implement stack operation push and pop with pointer.
# include <stdio.h>
# include <conio.h>
# include <stdlib.h>
struct stack
{
int num;
struct stack *next;
} *T=0;
void push();
int pop();
void show();
void main()
{
char chr;
int opt,no;
do
{
clrscr();
printf("\n 1: push ");
printf("\n 2: pop ");
printf("\n 3: show ");
printf("\n >3: exit");
printf("\n Enter your option: ");
scanf("%d",&opt);
switch(opt)
{
case 1: push(); break;
case 2: no = pop();
printf("\n The delete number was %d ", no);
break;
case 3: show(); break;
default: printf("\n Good Bye ");
exit(0);
}
} while(chr=='Y' || chr=='y');
}
void push()
{
it *node;
node=(it*) malloc(sizeof(it));
printf("\n Enter the number ");
scanf("%d",&node->num);
node->next=T;
T=node;
}
int pop()
{
it *temp;
temp=T;
if(T==NULL)
{
puts("\n Stack underflow ");
getch();
exit(0);
}
else
{
T=T->next;
free(temp);
}
return(temp->num);
}
void show()
{
it *tmp;
tmp=T;
while(tmp->next !=NULL)
{
printf(" %d ", tmp->num);
tmp=tmp->next;
}
printf(" %d ", tmp->num);
}
OUTPUT
1: push
2: pop
3: show
>3: exit
Enter your option: 3
7 2
Continue(y/n)
Explanation:
In this program the structure stack is defined with variables num and a pointer variable *next of
stack (itself) type. Later *T a pointer is declared. The prototype declaration of pop (), push ()
and show () is done. The user is asked to entered option and the entered option number is
passed to switch () case structure to execute the anticipated option. The user has to press ‘y’
to continue the program, else if other alphabet is entered, the program will terminate.
In the push() function, it is a custom data type created with typedef statement. The variable
*node is variable of type structure stack. The malloc() function is used to allocate memory to *node
pointer equal to the size of structure variable. The element entered immediately by the user is stored in
allocated memory. The number is stored in the num variable and the T is assigned to node->next.
In pop() function, another variable tmp is declared. If the T is NULL, i.e. the stack is still underflow
and we can continue the push operation. Otherwise, the free function de-allocates the memory of last
element pointed by next. When the memory is de-allocated the element also is destroyed. The show()
function is used to print the elements. This is achieved using while loop.
An arithmetic expression contains operators and operands. Variables and constants are operands. The
operators are of various kinds such as arithmetic binary, unary for example, + (addition), − (subtraction),
*(multiplication), / (division) and % (modular division). The following Table 4.1 describes types of
operators and the Fig. 4.7 shows the numbers of operators.
Arithmetic Operators
Relational Operators
Logical Operators
Assignment Operators
Conditional Operators
Bitwise Operators
Special Operators
The precedence of operator also plays an important role in expression solving. The precedence of
operators is given in Table 4.2.
We have already studied the different stack operations. Now, we will study how stack can be useful in
problem solving. Consider a mathematical expression.
5 - ((A * (( B + K)/ (U-4))+K) / 8 (8-4.3))
The common mistake, which can be made frequently by the programmer, is unbalance of parenthesis.
For correct representation of mathematical expression, the following precautions must be taken:
1. There must be equal number of left and right parenthesis.
2. Each left parenthesis must be balanced by right parenthesis.
The expressions ((x+y) and ) x+y (-u are wrong.
Thus, by using stack such a problem can be solved. Assume the left parenthesis as opening scope
and right parenthesis as end of scope. Some time the expression contains nested parenthesis, hence
various scopes are opened but not closed. Count the number of opening parenthesis from beginning
of expression to end. If the number of opening parenthesis is equal to closing parenthesis it means no
scope is left open. The number of opening and closing parenthesis is found same. If the parenthesis
count in the expression is negative, it means a right parenthesis is found, but no left parenthesis of its
pair is defined.
( (x + y )
1 2 1
In the above example, when opening parenthesis is encountered, counter is incremented and when
closing parenthesis is encountered, counter is decremented.
) x + y ( -z
-1 0
In the above example, when the first closing parenthesis is encountered, the counter is decremented.
5 - ( ( A * ( ( B + K ) / ( U-4 ) ) + K ) / 8 ( 8-4.3 ) )
0 1 2 3 4 3 4 3 2 1 2 1 0
In the above example the opening and closing parenthesis are balanced.
Example 4.8 Write a program to count opening and closing parenthesis and test the expression
having balanced parenthesis or not.
# include <stdio.h>
# include <conio.h>
int stack[10];
int top=9;
void main()
{
int c=0,p=0;
char exp[]="(p+(q-(m+n))*j-((x+y))) / (j-(k-(-k(l-n))))";
clrscr();
printf("\n%s\n",exp);
while(exp[c]!='\0')
{
switch(exp[c])
{
case '{':
case '(':
case '[':
p++;
printf("%d",p);
break;
case '}':
case ')':
case ']':
p−−;
printf("%d",p);
break;
default:
printf("%d",p);
}
c++;
}
}
OUTPUT
(p +(q-(m +n))*j-((x + y))) / (j-(k-(-k(l - n))))
111 222333321111 23333 21000011122233344443210
Explanation:
In this program whatever we discussed in last paragraph are applied practically. When an
opening parenthesis is found counter p is incremented and when closing parenthesis is found
counter is decremented. The counter value is displayed in the output. In the above program if
expressions“)p + (p + q)” are entered the output will be -1-1-10000-1. So far, we used only
one type of parenthesis, i.e. (). In expression other type of parenthesis like {}, [] can be used. In
such case, it is necessary to keep record of not only number of opening and closing braces but
also their type. The stack can be used in such application.
When an opening parenthesis is found it is pushed into the stack. When a closing parenthesis is found
the pop operation is carried and it is tested, the deleted parenthesis corresponds to the opening parenthesis.
If the opening and closing parentheses match, the program will continue otherwise the entered string is
invalid. When the end of string is found, the stack must be empty. If the stack contains elements, it means
there is opening parenthesis that had not been closed.
Consider the following expression.
{p+(q-[m+n])*j-[(x+y)]} / (j-(k-(k-[l-n])))
( ( (
{ { { { {
( (
[ (
(
{ (
As soon as the opening parenthesis of types (, {or [is encountered it is pushed on to the stack. We
can see this in Fig. 4.8 (a), (b) and (c). When a closing parenthesis is found the top element of the
stack is popped. Only when the encountered closing bracket and the top element of the stack matching,
i.e. {}, () and []. We can observe this in Fig. 4.8 (d) and (e). In Fig. 4.8 (g) the stack is empty because there
expression up to {p+(q-[m+n])*j-[(x+y)]} is read. The number of opening and closing brackets are same.
Hence, the expression is correct. When an element remains in the stack, it means the expression is wrong
and parentheses are not properly closed. Again, the expression is read and same push and pop procedure
is applied. The remaining expression (j- (k-(k-[l-n]))) is read.
# include <stdio.h>
# include <conio.h>
char stack[15];
int t=14;
void main()
{
void pop(void);
void show(void);
void push(char);
char exp[20];
int j=0,b=0;
clrscr();
while(exp[j]!='\0')
{
switch(exp[j])
{
case '{':
case '(':
case '[':
push(exp[j]);
show();
b++;
break;
case '}':
if(stack[t+1]=='{')
{
pop();
b−−;
}
else
printf("\n Error in expression");
break;
case ')':
if(stack[t+1]=='(')
{
pop();
b−−;
}
else
printf("\n No matching opening and closing brackets");
break;
case ']':
if(stack[t+1]=='[')
{
pop();
b−−;
}
else
printf("\n No matching opening and closing brackets");
break;
}
j++;
}
if(b==0)
printf("\n Expression is correct");
else
printf("\n %d closing brackets are not closed",b);
show();
}
void push(char c)
{
stack[t]=c;
t−−;
}
void pop()
{
t++;
stack[t]=NULL;
}
void show()
{
int k;
printf("\n");
for(k=0;k<=14;k++)
printf("%c",stack[k]);
}
OUTPUT
1)
{
(({
Expression is correct
2)
{
({
Error in expression
2 closing brackets are not closed
({
Explanation:
An entered expression by the user is stored in the array exp []. Using while loop the entire
string (expression) is scanned. The switch () case structure within while loop is used to
check different conditions. If any opening bracket comes across, it is pushed on to the stack
by invoking push () function, i.e. the meet opening bracket is stored in the array stack using
push () function.
We know that in any valid expression, the first opened parenthesis is closed lastly and the lastly opened
parenthesis is closed first, i.e. the parenthesis arrangement is like stack mechanism first-in-last-out. The
encountered opening parenthesis are pushed onto the stack. When closing parenthesis of same type is
found the item is popped from the stack. Thus, matching pairs of parenthesis are checked. If no match is
found, the error will be displayed. In case an opening parenthesis does not have closing parenthesis, the
message displayed will be “Error in expression”.
SUMMARY
1. Stack is an important tool in programming languages. Most of the programming languages
support stack operations. The main operations are push and pop. The insertion of element
onto the stack is called “push”. and deletion operation is called “pop”. Due to the push
operation that adds elements in the stack, the stack is also known as pushdown list. The most
and least reachable elements in the stack are known as the “top” and “bottom” of the stack.
Stack is a set of elements in a last-in-first-out technique.
2. The insertion and deletion operations are carried out at one end. Hence, the recent ele-
ment inserted is deleted first. If we want to delete the particular element of the stack, it is
necessary to delete all the elements appearing before that element.
3. A top pointer maintains track of the top elements.
4. When stack has no element, it is called empty stack.
5. Whenever an element is inserted (push operation) in the stack the value of pointer top is
increased by one. In the same manner, the value of pointer is decremented when an ele-
ment is deleted from the stack (pop operation).
6. If a pop operation is performed on an empty stack it is called as underflow stack. An opera-
tion to insert an element more than the capacity of stack is known as overflow stack.
7. Static implementation can be implemented using arrays. However, it is a very simple method
but it has few limitations.
8. Pointers can also be used for implementation of stack. The linked list is an example of this
type of implementation. The limitations noticed in static implementation can be removed
by using dynamic implementation.
9. Recall that array name itself is a pointer. Pointer implementation of a stack is carried out
similar to array implementation.
10. An arithmetic expression contains operators and operands. Variable and constant are
operands. The operators are of various kinds such as arithmetic binary, unary, for example, +
(Addition), − (subtraction), *(multiplication), / (division) and % (modular division).
EXERCISES
A. Answer the following question:
1. Write a program to demonstrate push () and pop () operation using class and member function.
2. Write a program to enter an integer. Reverse the digits of the integer. Use stack mechanism.
3. Write a program to implement stack operation push and pop with pointer.
4. Write a program to perform following operations:
a. push
b. pop
c. stack underflow
d. stack overflow
e. display all elements.
5. Write a program to implement stack with two-dimensional array. Perform push () and pop ()
operation.
b. Delete the items having quantity zero and update the stack.
Queues
5.1 INTRODUCTION
A queue is one of the simplest data structures and can be implemented with different methods on a
computer. Queue of tasks to be executed in a computer is analogous to the queue that we see in our daily
life at various places. The theory of a queue is common to all. Queue of people in a bank, students queue
in school, a travellers’ queue for tickets at railway station are few examples of queue. It is necessary to wait
in a queue for obtaining the service. In the same fashion, in a computer there may be a queue of tasks wait-
ing for execution, few others for printing, and some are for inputting the data and instructions through
the keyboard. Unless the task turns and comes on front, it will not be executed. This data structure is very
useful in solving various real life problems. One can simulate the real life situations with this data struc-
ture. This chapter describes the queue and its types.
A queue is a non-primitive, linear data structure, and a sub-class of list data structure. It is an ordered,
homogenous collection of elements in which elements are appended at one end called rear end and
elements are deleted at other end called front end. The meaning of front is face side and rear means
back side. The first entry in a queue to which the service is offered is to the element that is on front. After
servicing, it is removed from the queue. The information is manipulated in the same sequence as it was
collected. Queue follows the rule first-in-first-out (FIFO). Fig. 5.1 illustrates a queue.
Deletion Insertion
Front Rear
Figure 5.1 shows that insertion of elements is done at the rear end and deletion at the front end.
A P Q R S
B Front Q R S Rear
C Q R S T
Fig. 5.2 illustrates the queue containing elements. The queue (Fig. 5.2 (a)) contains four elements P, Q,
R and S. The element P is at the front end and element S is at the rear end. In Fig. 5.2 (b), an element P has
been deleted from the queue. Now, the element Q is the first element and it is on the front. In Fig. 5.2 (c),
element T is inserted from the rear end. Thus, the elements are inserted from the rear end of queue. The
element T is inserted after S. As far as removal operation is concerned, the S is to be removed before T.
In other words, the service is provided to the element, which is at front and this element is to be
removed first. We call this entry front of the queue. The entry of element that is recently added is done
from rear of the queue or it can be called as tail end of the queue.
Two operations are frequently carried on queue. They are insertion and deletion of elements. These two
operations depend upon the status of the queue. The insertion operation is possible, only when the queue
contains elements less than the capacity it can hold. The deletion operation is only possible when the queue
contains at least one element. The queue containing no elements is called underflow as shown in Fig. 5.3.
0 1 2 3 4 5
In Fig. 5.4 (a) the queue is empty, hence value of rear = –1 and front = –1. The above queue is called as
empty queue. Initially, the status of queue is always empty.
5
0 1 2 3 4 5
In Fig. 5.4 (b), the queue holds one element. The value of rear = 0 and front = 0. Both front and rear
end are on same element because the queue contains only one element. The next element appended will
be stored followed by 5. Then, the front will remain at 5 but rear will be shifted on to the new element.
The value of front is changed only once when the first element is inserted in the queue. Later, for every
insertion of element, the front remains same but the value of rear changes.
5 3
0 1 2 3 4 5
In Fig. 5.4 (c), another element is inserted from the rear side and it is stored behind 5. Whenever we
insert an element in the queue the value of rear is incremented, i.e. the rear end is shifted towards the right.
At this step the value of front = 0 and rear = 1.
5 3 2
0 1 2 3 4 5
3 2
0 1 2 3 4 5
Figure 5.4 (e) Deletion of the Element and Shifting to the Front
In Fig. 5.4 (e), one element is deleted from the queue. Now, the front is shifted towards bottom.
Thus, the front will be on element 3 now. The value of the front will be increased when an element is
removed. The value of front = 1 and rear = 2. The programs on above discussion are explained in queue
implementation.
5 4 8 7 3 8
Front Rear
The above operation can be shown with few programs as described below. Consider the following
program,
# include <stdio.h>
# include <conio.h>
# include <process.h>
void main()
{
int queue[8];
int rear=0;
clrscr();
while(1)
{
printf("Enter element: ");
scanf("%d",&queue[rear]);
rear++;
if(rear==7)
{
printf("\n Queue is full ");
break;
}
}
printf("\n Elements of Queue are: ");
rear=0;
while(1)
{
OUTPUT
Enter element: 1
Enter element: 2
Enter element: 3
Enter element: 4
Enter element: 5
Enter element: 6
Enter element: 7
Queue is full
Elements of Queue are: 1 2 3 4 5 6 7
Explanation:
This a simple example of queue. All the elements are entered and stored in the queue. Of course,
the queue is implemented by declaring an array. The while loop and scanf() statement read
elements through the keyboard. The variable rear is incremented to get successive position in
the queue. In the same way, the second while loop displays the elements.
# include <stdio.h>
# include <conio.h>
# include <process.h>
# define S 5
void main()
{
int queue[S];
int r=0,n;
clrscr();
while(1)
{
if(r>=S)
{
printf("\n Queue overflow");
break;
}
else
printf("Enter a number: ");
scanf("%d",&n);
queue[r]=n;
r++;
}
OUTPUT
Enter a number: 3
Enter a number: 4
Enter a number: 5
Enter a number: 6
Enter a number: 7
Queue overflow
Queue elements are: 3 4 5 6 7
Explanation:
This a simple example of queue that contains maximum 5 elements. All the elements are entered
and stored in the queue. The queue is implemented by declaring array[s]. The while loop
and scanf() statement read elements through the keyboard. The rear is incremented to get
successive position in the queue. The for loop displays the elements.
Example 5.3 Write a program to perform insertion operation and show value of front and rear.
# include<stdio.h>
# include <conio.h>
void main()
{
int queue[7];
int r=-1,f=-1,j;
clrscr();
while(r<6)
{
r++;
printf("rear=%d front=%d Enter element:",r,f);
scanf("%d",&queue[r]);
if(r==0) f=0;
}
printf("\n Queue elements are: ");
for(j=0;j<7;j++)
printf(" %d ",queue[j]);
OUTPUT
rear=-1 front=-1 Enter element:8
rear=0 front=0 Enter element:9
rear=1 front=0 Enter element:4
rear=2 front=0 Enter element:5
Explanation:
In this program our aim is to concentrate only at element insertion operation and the
values of front and rear ends displayed after insertion of every element. Initially, rear and
front are initialized to –1. Conceptually, it is not necessary to initialize these variables
to –1. The user can also initialize them to zero. The values of rear start from –1 to 6. Here, –1 is
the value before insertion operation. When one element is added the value of rear is increased
to 1 and later the value reaches up to 6. On the other hand the value of front changes only once
from –1 to 0 and then its value remains same.
Example 5.4 Write a program to perform deletion operation on queue and show value of front and rear.
# include<stdio.h>
# include <conio.h>
void main()
{
int queue[7]={11,12,13,14,15,16,17};
int i,r=6,f=0,n;
clrscr();
OUTPUT
The Elements of queue are as follows:-
11 12 13 14 15 16 17
Initial values rear=6 front=0
How many elements u want to delete: 3
rear=6 front=1
rear=6 front=2
rear=6 front=3
Queue elements are: 0 0 0 14 15 16 17
Explanation:
This program is the second part of the first program. We have started exactly from where we
ended the last program. The output of the last program is starting of this program. The array is
initialized with entered values and rear and front are initialized to six and zero, respectively.
The user is asked to enter number of elements to be deleted. The loop is executed for n times and zero
to n elements of the array are nullified. At every deletion operation the front is shifted to inside. It is
shifted from 0 to 3 when three elements are deleted. The output of the program is easy to understand.
In the previous sections we have studied implementation of queues using arrays. There are some disad-
vantages in simple queue implementation using arrays. In the following example, it is considered that the
elements after deletion are not shifted to beginning of an array. Consider the following example:
Queue [5] is a simple queue declared. The queue is initially empty. In the following figures insertion and
deletion operations will be performed.
0 1 2 3 4
The above figure is an empty queue. Initially the queue is always empty. Here, rear = –1 and front = –1.
The user can also assign 0 instead of –1. However, the –1 is suitable because we are implementing this
problem with arrays and an array element counting begins always with zero. Thus, it is suitable for problem
logic.
5
0 1 2 3 4
In the above figure, one element is inserted. So, the new values of front and rear are increased and
reaches to 0 (zero). This is the only stage where front and rear have same values.
5 7 9
0 1 2 3 4
In the above figure, two elements are appended in the queue. At this moment, the value of rear = 2 and
front = 0.
7 9
0 1 2 3 4
In the above figure one element is deleted and the value of rear = 2 and f = 1. The value of front is
increased due deletion of one element.
7 9 8 1
0 1 2 3 4
In the above figure, two more elements are appended to queue. The value of rear = 4 and front = 1.
If more elements are deleted, the value of front will be increased and as a result, the beginning portion
of queue will be empty. In such a situation if we try to insert new elements to queue, no elements can
be inserted in the queue. This is because, new elements are always inserted from the rear and if the last
location of queue (rear) holds an element, even though the space is available at the beginning of queue, no
elements will be inserted. The queue will be treated as overflow.
To overcome this problem, we have to update the queue. Let us take an example, suppose we have allo-
cated n number of bytes to pointer *p using either malloc()(c) or new (c++) function. After successful
allocation of memory (though the program is not utilizing the memory), memory will be part of the
program and cannot be used by other program or cannot be allocated to other program. To utilize this
memory, memory must be released using free or delete functions. Thus, whatever the portion of
memory is not in use, can be used. In our example of queue the beginning portion of queue which is empty
due to delete operations, can be filled by shifting following element to beginning of the array. Thus, the
space will be available at the rear end and new elements can be inserted at the end. Thus, entire queue can
be used to store the element. This approach also has a problem when the queue contains more elements.
The solution to this problem is circular queue, which is discussed separately in this chapter.
Here, a program is provided with menu driven with different choices such as insertion, deletion,
display, etc.
Example 5.5 Write a program to perform different operations with queue such as insert, delete
and display of elements.
# include <stdio.h>
# include <conio.h>
int queue[8];
int r;
void insert(void);
void del();
void show(void);
void main()
{
int c;
clrscr();
printf("\n 1] Insert Element ");
void insert()
{
char ans='Y';
if(r>=7)
{
printf("\n Queue is full \n\n\n");
main();
}
while(ans=='Y'|| ans=='y')
{
printf("\n Enter element:- ");
scanf("%d",&queue[r]);
if(r==7)
{
clrscr();
printf("\n Queue is Full");
getch();
break;
}
else
r++;
printf("\nContinue [Y/N]:-");
ans=getche();
}
}
void show()
{
int j;
for(j=0;j<r;j++)
printf(" %d ",queue[j]);
getch();
}
void del()
{
int j;
if(r==0)
{
clrscr();
printf("\n Queue is empty \n\n\n");
main();
}
for(j=0;j<r;j++)
queue[j]= queue[j+1];
queue[r]=0;
r––;
}
OUTPUT
1] Insert element
2] Delete element
3] Display element/s
4] Exit
Enter your choice:- 1
Enter element:- 23
Continue [Y/N]:-y
Enter element:- 45
Continue [Y/N]:-
Explanation:
In this program, the different tasks such as insert, delete and display of elements are performed
using individual functions. A menu appears on the screen and user is required to enter choice.
When user enters 1, the insert() function is executed. The integers entered by the user are
stored in the queue one after another.
When the choice is 2, the element at the front is deleted. In queue, the elements are inserted from the
rear side and deleted from the front side. The display option displays the elements presently stored in
the queue. When del() function is executed, the element present at front is deleted. Here, the elements
are shifted to front side. This is accomplished by the statement queue [j ] = queue [ j+1]; the next element
is shifted to the previous location and the last element is replaced with zero.
In the above program, necessary care is taken to prevent overflow and underflow. When queue is full
with the elements, a message will be displayed “Queue is Full” and further no elements can be added.
When queue is empty user cannot delete any element. The function show() is used to display elements,
this is achieved by using for loop and printf() statement.
1 2 3 4 5 6 7
2 3 4 5 6 7
2 3 4 5 6 7 8
As shown in Fig. 5.6, element 1 is at the front and seven is at the rear. When delete operation is per-
formed one is deleted first and the element two is shifted at front as shown in the Fig. 5.7. When an ele-
ment is inserted, it is inserted after seven as shown in the Fig. 5.8. When eight is inserted, it is inserted
after seven. Consider another program that explains the operation insertion and deletion.
Example 5.6 Write a program to perform different operations with queue such as insert, delete and
display of elements.
# include <stdio.h>
# include <process.h>
# include <conio.h>
create();
void insert();
void del();
void show();
void main()
{
int choice;
clrscr();
puts("1] Create");
puts("2] Insert");
puts("3] Delete");
puts("4] Show");
puts("5] End");
puts("\nEnter Your choice: ");
scanf("%d",&choice);
switch(choice)
{
case 1: create(); break;
case 2: insert(); break;
case 3: del(); break;
case 4: show(); break;
case 5: puts("Bye"); exit(0);
}
main();
}
create()
{
puts("Insert number of elements <11 in the queue: ");
scanf("%d",&cur);
if( fr==0)
{
fr++;
scanf("%d",&num);
if(num==0) return 0;
queue[fr]=num;
re=fr;
}
do
{
++re;
if(re>cur) break;
scanf("%d",&num);
if(num==0) break;
queue[re]=num;
}while(re<cur);
return 0;
}
void insert()
{
if(re<cur)
{
puts("Queue is full");
exit(0);
}
void del()
{
if(fr==re)
{
puts("The queue is empty"); exit(0); }
fr++;
num=queue[fr];
if(fr==n)
{
fr=1;
re=1;
}
}
void show()
{
for(m=fr;m<=re;++m)
printf(" %d ",queue[m]);
getch();
}
OUTPUT
1] Create
2] Insert
3] Delete
4] Show
5] End
Enter Your choice:
4
2 3 0
Explanation:
We are already familiar with the operation create, insert and delete in the previous program. In
this program, separate functions are defined for different tasks such as create, insert, and delete
operation. Now, we will discuss one by one.
In the create function, user has to enter number of elements to be entered in the queue. User also needs
to enter elements. If zero is inserted the control exits from the function. The zero will not be considered as
an element, it is entered to exit from the function. If elements entered are less than the capacity entered,
the remaining portion remains vacant.
The insert()function is used to insert elements if the space is available. If the queue is already
filled, this function will not work. The del()function is used to delete element. To display the element
show()function is used. You might have noticed that the function main()is invoked recursively by
itself. This is to enable the user to enter choice of operation.
Dynamic implementation is carried out using pointers. By applying increment operation on pointer suc-
cessive locations of memory can be accessed and an element can be stored in that location. Thus, the series
of elements can be contiguously stored up to any number of elements. The programmer has to keep the
starting address of the pointer in which first element is stored. Thus, in the same way the numbers can be
viewed or altered. Here is the simple example.
# include <stdio.h>
# include <conio.h>
# include <process.h>
void main()
{
int *q;
int rear=0;
clrscr();
while(1)
{
printf("Enter element: ");
scanf("%d",q);
q++;
rear++;
if(rear==7)
{
printf("\n Queue is full ");
break;
}
}
q=q-rear;
printf("\n Elements of Queue are: ");
rear=0;
while(rear<7)
{
printf("\n Element: %d rear=%d", *(q+rear),rear);
rear++;
}
}
OUTPUT
Enter element: 4
Enter element: 2
Enter element: 3
Enter element: 4
Enter element: 7
Enter element: 8
Enter element: 9
Queue is full
Elements of Queue are:
Element: 4 rear=0
Element: 2 rear=1
Element: 3 rear=2
Element: 4 rear=3
Element: 7 rear=4
Element: 8 rear=5
Element: 9 rear=6
Explanation:
In this program, pointer *q and integer rear are declared. The variable rear is initialized with
zero. Using scanf(), statement elements are entered. In the while loop pointer q and rear
are increased. If the rear reaches to seven, the break statement terminates the loop. After
the execution of while loop, the pointer q points to last memory location of the queue. The
starting address of the queue is again set by the statement q = q–rear; Finally, using second
while loop and continuous increment operation with rear, successive memory locations are
accessed and elements are displayed.
The insertion and deletion operations can also be performed with the queue. The following program explains
both the insertion and deletion operation with the queue. Queue implementation is done with an array.
Example 5.8 Write a program to perform insert and delete operation with queue.
# include <stdio.h>
# include <conio.h>
void create();
insert();
erase();
void show();
int k,j=10,f=0, queue[11],r=0, e_ment, crn,s;
void main()
{
int option;
clrscr();
do
{
puts("\n\n QUEUE OPERATIONS\n");
puts("1: Create");
puts("2: Insertion");
puts("3: Erase");
puts("4: Show");
puts("5: Exit");
puts("Enter your option: ");
scanf("%d",&option);
switch(option)
{
case 1: create(); continue;
case 2: s=insert(); continue;
case 3: erase(); continue;
case 4: show(); continue;
case 5:
printf("\n s=%d",s);
return 0;
}
getche();
clrscr();
}while(option!=5);
}
void create()
{
puts("Insert number of elements<11 in the queue: ");
scanf("%d",&crn);
puts("Enter elements:");
if(f==0)
{
f++;
scanf("%d",&e_ment);
queue[f]=e_ment;
r=f;
}
do
{
++r;
if(r>crn) break;
scanf("%d",&e_ment);
queue[r]=e_ment;
} while(r<crn);
}
insert()
{
r++;
if(r>j)
{
puts("Queue is full "); return(1); }
puts("Enter number to be inserted: ");
scanf("%d",&e_ment);
queue[r]=e_ment;
return 0;
}
erase()
{
if(f==r)
{
puts("The queue is empty "); return(0); }
f++;
e_ment=queue[f];
if(f==j)
{
f=1;
r=1;
}
return 0;
}
void show()
{
for(k=f;k<=r;++k)
printf(" %d ",queue[k]);
}
OUTPUT
1 2 2 4 5
QUEUE OPERATIONS
1: Create
2: Insertion
3: Erase
4: Show
5: Exit
Enter your option:
4
1 2 2 4 5
Explanation:
In this program, required variables and function prototypes are declared before main(). The
user can perform different operations and they are displayed. Program will ask the user to enter
a choice. The option entered by user is passed to switch() case statement where appropriate
case statement is executed. Actually, the case statement body invokes function and operation
gets started.
The create function is used to insert numbers into the queue. In this function user has to enter number
of elements to be inserted and followed by this user has to enter numbers. The if block is executed if
the value of f (front) is zero. The element entered is stored in the queue. Using while loop repeatedly
elements are entered and stored in the queue. The variable r is incremented in every loop. The insert()
function is used to insert an element at the given position in the queue. The variable j has value 10 and
it shows maximum number which the queue can hold. If the value of r (rear) is greater than j its simple
meaning is that queue is full and the return statement suspends the function execution. If the queue is
empty and in the next available location, the entered element is stored.
In the erase() function, first value of r (rear) and f (front) are compared and if they are same its meaning
is that queue is empty. Because value of front and rear are same means the queue has no element. If f and j
(maximum number) are same, both these variables are set to one.
The show() function is used to display the element as output. The loop is executed from value of front vari-
able to rear. In the erase() function, the elements are not actually erased, just position of front is increased.
The same value of front is used to display the elements. Before the fth position, the elements exist physically. We
can also erase them physically, but then we have to move the elements to the beginning of the array.
In the last few topics, we have studied simple queue and already seen the disadvantages. When rear pointer
reaches to the end of the queue (array), no more elements can be added in the queue, even if beginning
memory locations of array are empty. To overcome the above disadvantage, different types of queues can
be applied. Different types of queues are:
5 8 7
0 1 2 3 4 5
Front = 3 rear = 5
In the above discussion, it is supposed that front is not shifted to the beginning of the queue. As shown
in the Fig. 5.10 a circular queue is like a wheel or a perfect ring. Suppose, c [4] is an array declared for
implementation of circular queue. However, logically elements appear in circular fashion but physically in
computer memory, they are stored in successive memory locations.
C [0] C [1]
C [3] C [2]
In the circular queue, the first element is stored immediately after last element. If last location of the
queue is occupied, the new element is inserted at the first memory location of queue implementing an
array (queue). For example, C[n], C is queue of n elements. After inserting, storing an element in the
last memory location [(n–1th) element], the next element will be stored at the first location, if space is
available. It is like a chain of where starting and ending points are joined and a circle is formed. When an
element is stored in the last location wrapping takes place and element inserting routine of the program
pointed to beginning of the queue.
Recall that a pointer (stack and queue pointer) plays an important role to know the position of the ele-
ments in the stack and queue. Here, as soon as last element is filled, the pointer is wrapped to the begin-
ning of the queue by shifting the pointer value to one.
A circular queue overcomes the limitation of the simple queue by utilizing the entire space of the
queue. Like a simple queue, the circular queue also have front and rear ends. The rear and front ends
help us to know the status of the queue after the insertion and deletion operations. In the circular
queue implementation, the element shifting operation of queue that we apply in the simple queue is
not required. The pointer itself takes care to move at vacant location of the queue and element is placed
at that location. In order to simulate the circular queue a programmer should follow the following
points:
1. The front end of the queue always points to the first element of the queue.
2. If the values of front and rear of queue are equal, the queue is empty. The values of front and rear
pointers are only increased/decreased after insertion/deletion operation.
3. Like simple queue, rear pointer is incremented by one when a new element is inserted and front
pointer is incremented by one when an element is deleted.
Insertion
Front
C [0] C [1]
5 7
C [3] C [2]
REAR
C [0] 7 C [1]
5
C [3] 3 9 C [2]
REAR
From the Fig. 5.11 (b), the value of rear is three and the capacity to store maximum element of queue
is four. Therefore,
REAR = (1+REAR) % MAXSIZE
REAR = (1+3)%4
REAR = 4%4 = 0.
The operator % is modular divisor operator and returns remainder. Thus, in the above equation the
remainder obtained is zero. The value of front as well as rear is zero, it means stack is full. Any attempt to
insert new element will display the “queue full”. message.
Consider, the following Fig. 5.11 (c),
C[0] C[1]
C[0] C[1]
Example 5.9 Write a program to perform insert, delete operations with circular queue and display
elements.
# include <stdio.h>
# include <conio.h>
# include <process.h>
main()
{
int c;
clrscr();
printf("\n 1] Insert Element ");
printf("\n 2] Delete Element ");
printf("\n 3] Display element");
printf("\n 4] Exit");
printf("\n Enter your choice: ");
scanf("%d",&c);
switch(c)
{
case 1: insert(); break;
case 2: del(); show();break;
case 3: show(); break;
default: exit(0);
}
main();
}
void insert()
{
int n;
char ch,ans='Y';
if(r>=7)
{
while(queue[x]==0)
{
printf("\n Enter a Number: ");
scanf("%d",&n);
queue[x]=n;
x++;
puts("\n Continue Y/N: ");
ch=getch();
if(ch!='Y') break;
}
printf("\n Queue is full \n\n\n");
main();
}
void show()
{
int j;
for(j=0;j<8;j++)
printf(" %d ",queue[j]);
getch();
}
void del()
{
int j;
if(r==0)
{
clrscr();
printf("\n Queue is empty \n\n\n");
main();
}
if(f==8) f=0;
queue[f]=0;
f++;
}
OUTPUT
1] Insert Element
2] Delete Element
3] Display element
4] Exit
Enter your choice: 2
0 2 0 0 0 0 0 0
Explanation:
In this program, various operations such as insert, delete and display are performed. When an
insert operation is performed and elements are inserted, elements appear one after another in
sequence. If we remove element, the beginning location remains empty. If the last location holds
an element and if insertion request is made by the user, the new element inserted is stored at
the starting location. If we go on continuously performing remove operation, the elements at
the beginning are not removed first (see Fig. 5.12).
6 2
5
3
In the above program, assume the elements inserted are 1,2,3,4,5 and 6 and consider their position in
the array is from 0th to 5th. The status of the queue is full. If we keep on removing elements, the elements
would be removed one by one. On deletion of element of the 0th location, this location remains empty.
Now, if an element is inserted, suppose seven, it will be stored in the 0th location as in Fig. 5.13.
Due to circular queue mechanism, if last location is occupied and the beginning locations are empty,
the element is inserted at the empty location. If we keep on removing elements, the elements will be
removed in the order from 2,3,4,5,6 and 7. However, the seven is stored at 0th location of the array but
it was the last element inserted.
6 2
5
3
Deletion Insertion
1 2 3 4 5
Insertion Deletion
FRONT REAR
In deque, it is necessary to fix the type of operation to be performed on front and rear ends. The deque
can be classified into two types:
Input Restricted Deque In the input restricted deque, insertion and deletion operation are performed
at rear end whereas only deletion operation is performed at front end. The Fig. 5.15 represents the input
restricted deque.
The following operations are possible in the input restricted deque:
1. Insertion of an element at the rear end.
2. Deletion of element at both front and rear ends.
Front Rear
Insertion
Deletion
Deletion
Thus, input restricted deque allows insertion at one and deletion at both ends.
# include <stdio.h>
# include <conio.h>
int ird[5];
void main()
{
int r=0,c=1,op,f=-1;
int insert(int);
void deleter(int,int);
void deletef(int,int);
void show(void);
while(1)
{
clrscr();
printf("\n (1) Insert ");
printf("\n (2) Delete from rear");
printf("\n (3) Delete from front");
printf("\n (4) Display ");
printf("\n (0) Exit");
printf("\n Enter Your choice:- ");
scanf("%d",&op);
switch(op)
{
case 1: while(c && r<5)
{
c=insert(r);
r++;
}
if(c==0)
{
c=1;
r—;
}
else
if(f<0)
printf("\n Deque is full ");
getch();
break;
case 2: deleter(—r,f);break;
case 3: deletef(r,++f);
break; case 4: show (); break;
default: exit(0);
}
}
}
void show()
{
int j;
printf("\n Elements of deque are: ");
for(j=0;j<5;j++)
printf(" %d ",ird[j]);
getche();
}
int insert(int h)
{
int k;
printf("\n Enter an element (enter 0 to exit): ");
scanf("%d",&k);
ird[h]=k;
return(k);
}
{
if(m<=n)
printf("Deque is empty");
else
{
printf("\n %d The element deleted: %d",m,ird[m]);
ird[m]=0;
}
getch();
printf("Deque is empty");
else
{
printf("\n %d The element deleted:- %d",j,ird[j]);
ird[f]=NULL;
}
getch();
}
OUTPUT:
(1) Insert
(2) Delete from rear
(3) Delete from front
(4) Display
(0) Exit
Enter Your choice:- 1
Enter an element (enter 0 to exit): 2
Enter an element (enter 0 to exit): 3
Enter an element (enter 0 to exit): 5
Enter an element (enter 0 to exit): 8
Enter an element (enter 0 to exit): 6
Deque is full
(1) Insert
(2) Delete from rear
(3) Delete from front
(4) Display
(0) Exit
Enter Your choice:- 4
Elements of deque are: 2 3 5 8 6
Explanation:
The output of the program is not fully displayed here. User can see it by executing it. Recall that
in input restricted queue, insertion can be done only from rear end and it is carried out by the
function insert(). The while loop is executed for continuous execution of the insert()
function. User can enter element and when zero is entered, the loop is terminated. Thus, insertion
of elements is carried out. The variable r(rear) is incremented and its value should not exceed
the maximum capacity of the array, i.e. 5 and the same is done in the while loop condition.
We have a choice to remove the elements. We can remove elements from both the ends. If we go for
second choice, elements from rear side are removed. The variable r (rear) is decremented and the r th ele-
ment of the deque is erased. This option has a side effect. The empty locations generated by the deletion
operation can be filled using insertion operation. However, if we go for option 3 the case is different. In
this case, the elements are deleted from front and vacant space is created at the beginning of the deque.
If we want to insert the element in that place, the insert option will fail here. This is because it is pro-
grammed in circular fashion. This is the drawback of input restricted deque.
Out Restricted Deque In the output restricted deque insertion of an element can be done at both front
and rear end and deletion operation can be done only at front end. The output restricted deque is shown
in Fig. 5.16.
Front Rear
Insertion
Insertion
Deletion
# include <stdio.h>
# include <conio.h>
# include <process.h>
int ird[5];
void main()
{
int r=0,c=1,op,f=-1;
int insertr(int);
int insertf(int);
void deletef(int);
void show(void);
clrscr();
while(1)
{
switch(op)
{
case 1:
while(c && r<5)
{
c=insertr(r);
r++;
}
if(c==0)
{
c=1;
r⎯;
}
else if(f<0)
printf("\n Deque is full ");
getch();
break;
case 2: f=insertf(f); break;
case 3: deletef(++f); break;
case 4: show(); break;
default: exit(0);
}
}
}
void show()
{
int j;
printf("\n Elements of deque are: ");
for(j=0;j<5;j++)
printf(" %d ",ird[j]);
getche();
}
int insertr(int h)
{
int k;
printf("\n Enter a Element(enter 0 to exit): ");
scanf("%d",&k);
ird[h]=k;
return(k);
}
int insertf(int m)
{
int h;
if(ird[m]==0 && m>=0)
{
printf("\n Enter a element: ");
scanf("%d",&h);
ird[m]=h;
}
else
printf("\n no space available ");
return ⎯m;
}
void deletef(int f) { ird[f]=NULL; }
OUTPUT
1 Insert from rear:
2 insert from front
3 Delete from front
4 Display
0 Exit
Enter Your choice: 1
Enter a Element (enter 0 to exit): 1
Enter a Element (enter 0 to exit): 2
Enter a Element (enter 0 to exit): 3
Enter a Element (enter 0 to exit): 0
1 Insert from rear:
2 insert from front
3 Delete from front
4 Display
0 Exit
Enter Your choice: 4
Elements of deque are: 1 2 3 0 0
Explanation:
In the output restricted queue, insertion can be done at both the ends and deletion can be done
only at front end. The deletion procedure is same as the last program. In addition, insertion
from rear end follows the same instruction as described in the previous program. The newly
introduced function is insertf (), which performs insertion of elements from front of the
deque. First, the space availability is checked. If space is available and request is made, the
user has to insert the element and the same is stored in the empty location. The variable f is
decremented. This is because while performing deletion operation the front is shifted forward
and to reverse it, the value of f is decremented.
Ascending Priority Queues In this queue elements can be inserted randomly. The smallest element of
the queue is deleted first.
Example 5.12 Write a program to create ascending priority queue. Insert and remove the element.
# include <stdio.h>
# include <conio.h>
int prq[5]={0};
int insert();
void remove1();
void show();
void main()
{
int j,c=1;
while(c!=0)
{
clrscr();
printf("\n 1. Insert");
printf("\n 2. Remove");
printf("\n 3. Display");
printf("\n 0. Exit");
printf("\n Enter Your choice: ");
scanf("%d",&c);
switch(c)
{
case 1: insert(); break;
case 2: remove1(); break;
case 3: show(); break;
default: exit(0);
}
}
}
int insert()
{
int n,j;
printf("\n Enter the position: ");
scanf("%d",&n);
n⎯;
if(n>4 || prq[n]!=0)
{
printf("\n Location is not available");
return 0;
}
printf("\n Enter Element: ");
scanf("%d",&j);
prq[n]=j;
return 0;
}
void remove1()
{
int j,k,min=0,s=0;
for(j=0;j<=4;j++)
s=s+prq[j];
for(j=s;j>0;j⎯)
{
for(k=0;k<5;k++)
{
if(j==prq[k])
min=prq[k];
}
}
for(j=0;j<5;j++)
{
if(prq[j]==min)
{
prq[j]=0;
break;
}
}
}
void show()
{
int h;
for(h=0;h<5;h++)
printf(" %d ",prq[h]);
getch();
}
OUTPUT
1. Insert
2. Remove
3. Display
0. Exit
Enter Your choice: 1
Enter the position: 1
Enter Element: 3
Explanation:
In this program an array prq[] is defined. The array prq[] is used to implement priority
queue. The insert() function is used to insert an element in the queue. The position and
element number are to be provided by the user. According to user entered values, the process
is executed. User can randomly insert position numbers from 1 to 5. If the user repeats the
same location number in which the element is already stored, the insertion will be terminated.
The replacement of element is not allowed here. If the location is empty then only insertion of
element is possible.
The show() function is used to display elements only. The remove1() function is used to remove
the element. Before the removal of an element, it must be sorted first (in ascending or descending order).
We know that in ascending order queue, the smallest element is deleted first. Hence, instead of arranging
all the elements of the queue in ascending order, here the smallest element of the queue is searched and it
is removed. In the priority queue, we can insert an element anywhere in the queue. Hence, it may not be
compulsory to arrange the numbers in ascending order. If we arrange the elements in ascending order and
then begin the removal operation, it will also change the exact position of the element. The user can also
implement this program by sorting the queue elements, storing elements temporarily and then the small-
est element can be erased. The program for descending priority queue can be done in the same fashion.
Descending Priority Queue In this queue also, elements can be inserted randomly but the largest element
is deleted first.
Example 5.13 Write a program to create descending priority queue. Insert and remove the element.
# include <stdio.h>
# include <conio.h>
int prq[5]={0};
int insert(void);
void remove1(void);
void show(void);
void main()
{
int j,c=1;
clrscr();
while(c!=0)
{
printf("\n 1. Insert");
printf("\n 2. Remove");
printf("\n 3. Display");
printf("\n 0. Exit");
printf("\n Enter Your choice: ");
scanf("%d",&c);
switch(c)
{
case 1: insert(); break;
case 2: remove1(); break;
case 3: show(); break;
}
}
}
int insert()
{
int n,j;
printf("\n Enter the position: ");
scanf("%d",&n);
n––;
if(n>4 || prq[n]!=0)
{
printf("\n Location is not avaliable");
return 0;
}
void remove1()
{
int j,k,max=0,s=0;
for(j=0;j<=4;j++)
s=s+prq[j];
for(j=0;j<s;j++)
{
for(k=0;k<5;k++)
{
if(j==prq[k]) max=prq[k];
}
}
for(j=0;j<5;j++)
if(prq[j]==max)
{
prq[j]=0;
break;
}
}
}
void show()
{
int h;
for(h=0;h<5;h++)
printf(" %d ",prq[h]);
}
OUTPUT
1. Insert
2. Remove
3. Display
0. Exit
Enter Your choice: 3
8 3 1 10 9
1. Insert
2. Remove
3. Display
0. Exit
Enter Your choice: 2
1. Insert
2. Remove
3. Display
0. Exit
Enter Your choice: 3
8 3 1 0 9
Explanation:
This program is same as previous one. Here, only the largest element is deleted first. It is an
example of descending order queue.
In both the above types, if elements with equal priority are present, the FIFO technique is applied.
In case the queue elements are sorted, the deletion of an element will be quick and easy because elements
will appear either in ascending or descending order. However, the insertion operation will be easier said
than done because empty locations will have to be searched and only then elements can be placed at that
location.
In case the queue elements are not in order, i.e. not sorted, insertion operation will be quick and easy.
However, the deletion operation will take place after the priority value set.
Therefore, we can say that in both the above types insertion operation can be carried out easily. How-
ever, the deletion operation, which is, based on certain priority, i.e. the smallest or largest is first searched
out and later it is removed from the queue. The locations of that element do not matter. In stack and
queues deletion operation is performed at the ends. Conceptually, there is no provision for deleting an
element, which is neither first nor last element of the list.
In the above program, we have not arranged the elements in an ascending or descending order. Just
smallest or largest element is searched and erased.
In deletion operation the element may not be physically removed from the queue and it can be kept
inaccessible in the program. Alternatively, special character called empty indicator such as #, $ can be
placed at that place. Both the insertion and deletion operation perform scanning of the queue elements.
While performing deletion operation the element must be deleted physically. It is possible to make its
access denied. However, for other computation operation, the element logically deleted but physically
existing will be taken into account and will change the result. So, for sure result, the element must be
removed.
The elements of the queue can be number, character or any complex object which is a composition
of one or more basic data types.
In priority queue every element has been assigned with a priority value called priority. The elements can
be inserted or deleted randomly anywhere in the queue. Consider the following points of queue:
1. An element of upper priority is processed prior to an element of lower priority.
2. If two elements have the same priority, they are processed depending on the order they are inserted in
the queue, i.e. FIFO.
Front Rear
1 2 3 5 7 8 9 Elements
5 1 8 2 9 4 3 Priority Value
As shown in Fig. 5.17, the element 7 is deleted first. However, the element 1 is nearer to the front as
compared to 7, but 7’s priority value is nine, which is higher, and hence it is deleted first. Though the
queue is based on FIFO technique, the priority queue is not based on FIFO operation firmly. A program
on priority queue is provided below for detailed understanding.
Example 5.14 Write a program to demonstrate priority queue. Enter number and priority value.
Perform insert and deletion operation.
# include <stdio.h>
# include <conio.h>
int pq[2][5]={0};
void main()
{
int c=1;
int insert(void);
void show(void);
void remove1(void);
clrscr();
do
{
printf("\n1 Insert");
printf("\n2 Remove ");
printf("\n3 Display");
printf("\n0 Exit");
printf("\n Enter Your Choice: ");
scanf("%d",&c);
switch(c)
{
case 1: insert(); break;
case 2: remove1(); break;
case 3: show(); break;
case 0: c=0;
}
}
while(c!=0);
}
int insert()
{
int p,e,r;
printf("\n Position: ");
scanf("%d",&p);
if(p>5 || p<0)
{
printf("\n Invalid Argument ");
return 0;
}
p⎯;
printf("\n Element: ");
scanf("%d",&e);
if(pq[0][p]==0)
pq[0][p]=e;
printf("\n Priority: ");
scanf("%d",&r);
pq[1][p]=r;
return 0;
void show()
{
int j,k;
for(j=0;j<2;j++)
{
if(j==0)
printf("\n Elements: ");
else
printf("\n Priority: ");
for(k=0;k<5;k++)
printf(" %d ",pq[j][k]);
printf("\n");
}
}
void remove1()
{
int j,maxp=0;
for(j=0;j<5;j++)
{
if(pq[1][j]>maxp)
maxp=pq[1][j];
}
for(j=0;j<5;j++)
{
if(pq[1][j]==maxp)
{
pq[0][j]=pq[1][j]=0;
break;
}
}
}
OUTPUT
Elements: 1 2 8 9 4
Priority: 1 3 9 8 3
1 Insert
2 Remove
3 Display
0 Exit
Enter Your Choice: 2
1 Insert
2 Remove
3 Display
0 Exit
Enter Your Choice: 3
Elements: 1 2 0 9 4
Priority: 1 3 0 8 3
Explanation:
This program is the same as the last few programs. Here, in this program two-dimensional array
is defined. Using insert, function elements position number, element and its priority value
are entered. Replacement of element is not allowed. When remove function is executed, the
element with higher priority is detected and removed. The show() function is used to display
the elements and priority value.
There are various applications of computer science, which are performed using data structure queue.
This data structure is usually used in simulation, various features of operating system, multiprogramming
platform systems and different type of scheduling algorithm are implemented using queues. Round robin
technique is implemented using queues. Printer server routines, various applications software are also
based on queue data structure.
There are total four tasks and the total time to complete all the tasks would be (10+19+8+5) 42 units.
Suppose, the time slice is of 7 units. The RR scheduling for the above case would be as given the following
paragraph.
In the first pass each task takes seven units of time-task P4 is executed in the first pass whereas task
P1, P2, P3 requires more than 7 units of time. Hence, in the second round task P1 and P3 are executed.
At last, P2 is executed.
5.9.2 Simulation
It is an extremely powerful tool used for experimentation purpose. Without performing real experiments
simulation permits to take the results and if necessary modification can be done as per expected results.
One of the standard applications of queue can be implemented with simulation. Simulation is a method
of managing a theoretical illustration of a real life problem with the purpose of understanding the effect
of modification, concern factors and implementing some approaches to get reliable solution. The main
goal of simulation is to help the user or to guess for obtaining the output of the program after implement-
ing some approaches. It permits the user to make several trials for getting the actual results and planned
situations without disturbing the real situation.
There are few disadvantages in the simulation. Lengthy simulation creates execution over-head on
computer. For solving a real life problem, various assumptions are made to find out the key solution. If the
assumption is straightforward, the outcome will be less reliable. In contrast, if the assumption is having
more particulars, the outcome will be reliable and better. If the primary assumptions are wrong, even
though, the program source code is given in detail, the guess obtained will be incorrect. In this situation,
the program developed becomes futile.
A system may be isolated or continuous. The continuous system has some arguments, which can receive
any real value in some gaps. Simulation of this system runs on these continuous arguments. In the discrete
system, the method is different, it takes only selected set of values. The moving of a fan at home is a discrete
system and its speed can be controlled. The motion of earth or sun is an example of continuous system
and its motion is out of our control. The input and output parameters also defines system. The system can
be either deterministic or stochastic. The input and output parameters also have a relationship. The various
systems are descried in Fig. 5.18.
Deterministic System In this case if input values and initial state are available the result can be
determined.
Stochastic System This system necessitates efforts for simulation. There is no standard clarification for
this system.
System
To simulate a system a model is formed. It is a body of detailed particulars and applied to represent
the system in various forms. There are various models as event-driven, simulation and time-simulation
model and selection of model depends upon particulars of information available. The first step in solving
the problem is selecting structure of model because the model indicates the system and it is easier than
the problem. The model should be restricted and should not provide information out of the system.
To facilitate the formation of model for a state, the characteristics, entities, activities and operations
of the system must be determined. Activity refers to change of system state. The entities indicate the
mechanism of the system and serves as an object in the simulation. The objects have characteristics. The
characteristics of objects denote the state of the system and association between various entities at that
particular movement. The program must execute given steps and activities (change of state) must occur
in sequence.
SUMMARY
1. A queue is a non-primitive, linear data structure and a sub-class of list data structure. It is
an ordered, homogenous collection of elements in which elements are appended at one
end called rear end and elements are deleted at other end called front end.
2. The static implementation means the array implementation. In this implementation,
we should be assured about the number of elements we want to have in the queue.
3. Two operations can be carried out on queue:
a. insertion of element
b. deletion of element.
4. Dynamic implementation is carried out using pointers. By applying increment, operation
on pointer successive locations of memory can be accessed and an element can be stored
in that location.
5. There are three types of queue, i.e. circular queue, deque and priority queue.
6. In the circular queue, the first element is stored immediately after last element. If last
location of the queue is occupied then the new element is inserted at the first memory
location of queue implementing array (queue).
7. In the double-ended queue, insertion and deletion can be performed from both the ends
and therefore, it is called double-ended queues and in short called deque. It is a homog-
enous list of elements.
8. In the input restricted deque, insertion and deletion operation are performed at rear end
whereas only deletion operation is performed at front end.
9. In the output restricted deque, insertion of an element can be done at both front and rear
end and deletion operation can be done at only front end.
10. Priority queue is a data structure in which prioritized insertion and deletion operations on
elements can be performed according to their priority value.
11. Ascending priority queue: In this, queue elements can be inserted randomly. The smallest
element of the queue is deleted first.
12. Descending priority queue: In this queue also, elements can be inserted randomly and the
largest element is deleted first.
13. Simulation is a method of organizing a theoretical representation of a real life problem with
the purpose of understanding the effect of alteration, concern factors and implementing
some approaches to get reliable solution.
EXERCISES
A. Answer the following questions:
1. Write a program to insert and display the eight alphabetic characters in a queue.
2. Write a program to declare a priority queue using two-dimensional array, store elements and
priority. Display the elements according to priority from higher to lower.
3. Write a program to implement circular queue with the pointers.
4. Write a program to insert and display the elements of deque, with the array and pointers.
5. Write a program to insert five elements in the ascending priority queue. Sort the elements in
ascending order. Perform the deletion operation and shift the empty location at the end of array.
6. Write a program to create a priority queue. Store element and priority value. Assume square
root of entered number as its priority value. Insert and delete the elements.
7. Write a program to implement a circular queue containing eight elements and perform the
insertion and deletion operation.
int r=0,f=0,j; 4.
clrscr(); # include<stdio.h>
while(r<6) # include <conio.h>
{ void main()
printf("rear=%d front=%d Enter {
element:",r,f); int q[6];
scanf("%d",&q[r]); int i,r=6,f=0,n;
r++; clrscr();
} printf("Enter the seven elements
printf("\n Queue elements are: of a queue:- ");
"); for(i=0;i<7;i++)
for(j=0;j<6;j++) scanf("%d",&q[i]);
printf(" %d ",q[j]);
printf("\nThe queue element are
}
as:-\n");
for(i=0;i<7;i++)
3. printf("%2d ",q[i]);
# include<stdio.h> printf("\nValues of at start
# include<conio.h> rear=%d front=%d",r,f);
printf("\n\nHow many elements you
void main() want to delete: ");
{ scanf("%d",&n);
int q[7]; while(f<n)
int r=0,f=0,j; {
char ch; q[f]=NULL;
clrscr(); f++;
while(r<6) printf("\nrear=%d front=%d",
{ r,f);
printf("rear=%d front=%d Enter }
element:",r,f); printf("\n Queue elements are:
scanf("%d",&q[r]); ");
r++; for(n=0;n<7;n++)
} printf(" %d ",q[n]);
printf("\nQueue elements }
are: ");
for(j=0;j<6;j++)
printf(" %d ",q[j]); 5.
printf("\nWould you like # include <stdio.h>
to delete one element from # include <conio.h>
queue?[Y/N]:-"); # include <process.h>
scanf("%s",&ch); int qe[8];
if(ch=='y'||ch=='Y') int r=0,j;
{ char ans='Y';
q[f]=0; void insert();
f++; void del();
printf("\nAfter deletion of one void show();
element main()
rear=%d front=%d\n ",r,f); {
printf("\nQueue elements are:- int c;
"); while(1)
for(j=f;j<6;j++) {
printf(" %d ",q[j]); clrscr();
} printf("\n 1] Insert
} Element ");
Linked List
CHAP TER O U T LIN E
6.1 Introduction 6.13 Insertion of Node at a Given Position
6.2 Linked List 6.14 Representation of Stacks Using Linked
Lists
6.3 Illustration of Linked List for Storing
a String 6.15 Representation of Queues Using Linked
Lists
6.4 Important Terms
6.16 Reversing the Singly Linked List
6.5 Memory Allocation and De-allocation
6.17 Concatenation of Two Lists
6.6 Operations on Linked Lists
6.18 Splitting of a Linked List
6.7 Singly Linked List
6.19 Circular Linked List
6.8 Linked List with Header
6.20 Method for Detecting End
6.9 Linked List Without Header
6.21 Doubly Linked List
6.10 Insertion in the Linked List
6.22 Circular Doubly Linked List
6.11 Insertion of Node at Start
6.23 Applications of Linked List
6.12 Insertion of Node at End
6.1 INTRODUCTION
A list is a series of linearly arranged finite elements (numbers) of same type. The data elements are called
nodes. The list can be of two types, i.e. basic data type or custom data type. The elements are positioned
one after the other and their position numbers appear in sequence. The first element of the list is known
as head and the last element is known as tail.
Head Tail
Elements
1 5 8 4 3 2
0 1 2 3 4 5 Element
Position
As shown in Fig. 6.1, the element 1 is at head position (1st position) and element 2 is at tail posi-
tion (6th). The element 5 is the predecessor of element 8 and 4 is the successor. Every element can act as
a predecessor excluding the first element because it does not have a predecessor in the list. The list has
following properties:
a. The list can be enlarged or reduced from both the ends.
b. The tail (ending) position of the list depends on how long the list is extended by the user.
c. Various operations such as traverse, insertion and deletion can be performed on the list.
d. The list can be implemented by applying static (array) or dynamic (pointer) implementation.
A linked list is a dynamic data structure. It is an ideal technique to store data when the user is not aware
of the number of elements to be stored. The dynamic implementation of list using pointers is also known
as linked list. Each element (data and link) of the linked list is called as node. Each node points to the next
node. In the linked list, a node can be inserted or deleted at any position. Each node of linked list has two
components. The first component contains the information or any data field and second part the address
of the next node. In other words, the second part holds address of the next element. This pointer points to
the next data item. The pointer variable member of the last record of the list is generally assigned a NULL
value to indicate the end of the list. Fig. 6.2 indicates the linked list.
The basic data type in the linked list can be int, float and user defined data types can be created by
struct cla. The link structure as well as a pointer of structure type to the next object in the link is observed
in it.
A simple example describing the operation of linked list is illustrated in the following section.
In this example, a linked list containing a string of characters to be stored in the memory, is described. The
linked list has two field data, i.e. actual data stored and links, i.e. pointing to the next node of the linked
list. These two fields are shown with two arrays such as data[j] and link[j] where ‘j’ is the position of an
element. A variable begin is initiated which contains the beginning location of the list. The field ‘No.’ is
taken for numbering the nodes of the linked list. It is not the part of the linked list. It is given simply for
understanding. In the link[j] ‘0’ (zero) indicate the NULL pointer which is end of the string. In this type
of lists the nodes or elements are not stored in the successive memory locations. Many links have been
shown between data and link.
The nodes of the linked list are not ordered sequentially. The characters are stored dynamically with
their link fields. The link field shows the link to the next node of the linked list. The information obtained
from the above linked list is ‘computer’. It is shown in Fig. 6.3 and the explanation is as follows:
a. Begin =5, data[5] is ‘c’ and link[5]= ‘3’.
b. Link[3]=6 and data[3]= ‘o’.
c. Link[6]=1 and data[6]= ‘m’.
d. Link[1]=2 and data[1]= ‘p’.
No DATA LINK
1 p 2
2 u 8
3 o 6
Begin 5 c 3
6 m 1
7 e 9
8 t 7
9 r 0
We have already discussed in previous sections that a linked list is a non-sequential collection of elements
called nodes. These nodes are nothing but objects. These nodes are declared using structure or classes.
Every node has two fields and they are:
1. Data field: In this field, the data or values are stored and processed.
2. Link field: This field holds address of the next data element of the list. This address is used to access
the successive elements of the list.
In the linked list the ordering of elements is not done by their physical location in the memory but by
logical links, which are stored in the link field.
Node The components or objects which form the list are called nodes.
Null Pointer The link field of the last record is assigned a NULL value instead of any address. It means
NULL pointer not pointing to any element.
External Pointer It is a pointer to the starting node. The external pointer contains the base, i.e. address
of first node. Once a base address is available, its next successive nodes can be accessed.
Empty List When there is no node in the list, it is called as empty list. If the external pointer were
assigned a value NULL, the list would be empty.
1. The static list is implemented using arrays. The linked list uses pointer in which memory can be allo-
cated or de-allocated during program execution. The linked list is more efficient in memory manage-
ment. The linked list can be expanded or shrunk as per requirement whereas in static list once an array
is defined its size remains the same throughout the program.
2. Once an array is declared, its size remains the same. The memory allocated for array may be extra or
insufficient. The user cannot determine the exact amount of memory required. Due to this, the pro-
gram execution may not be safe. The pointer has intrinsic flexibility to enlarge or reduce the capacity
of storage. We are acquainted with pointer arithmetic operations increment/decrement, which enables
pointer to access any memory location. The use of pointer to point the next node in the linked list data
structure indicates that elements which are logically contiguous due to linear organization need not be
physically contiguous in the memory. This type of memory allocation is called linked allocation.
3. The elements are stored in order logically as well as physically. Array elements are stored in successive
memory locations. This is not the case in the linked list. Insertion and deletion with static list are time con-
suming and shifting of elements is done to rearrange the numbers in order. In the linked list the insertion
and deletion operations are done very easily. The element can be deleted by de-allocating the memory of
that particular element. The insertion can be done by allocating space at the specified position.
4. A list has been created to hold an ordered set of elements without knowing the number of nodes. The
simple method to create link is to enlarge each node and it should contain reference of the next node.
This is called singly linked linear list or one-way chain as shown in Fig. 6.4. The arrow (first) indicates
the address of the first node.
First
5. By using linked list more complex data structures such as circular linked list, doubly linked list, stack
and queues can be formed.
6. A pointer plays an important role in the dynamic implementation of linked list. The interpretation of
pointer is an address, which is in-built, and the programmer need not take care of how the addresses
are assigned in the memory. Mostly, all computer systems use addresses to know the instruction to be
executed. Pointers of any data type always are of same length and occupy equal space in the memory,
i.e. 2 bytes only. The pointer is always of unsigned integer type. Consider the following program:
Example 6.1 Write a program to demonstrate the size of pointers of different data types.
# include <stdio.h>
# include <conio.h>
void main()
{
int *p;
float *f;
clrscr();
OUTPUT
Integer Pointer Size=2
Float Pointer Size=2
Structure pointer Size=2
Explanation:
In this program, pointers of int,float and struct type are declared. The sizeof ()
operator returns the size of pointers. The size of any pointer is always two bytes. The address is
always of unsigned integer having range 0 to 65535.
7. In the above program we have demonstrated that pointers occupy only two bytes irrespective of the
type of data. This property of pointers makes it possible for the pointer to operate in carrying out the
referencing of structure in a homogeneous manner with easy allocation methods. The prototype of
structure does not matter. In the sequential allocation, the address of any node in the list can be calcu-
lated if the storage allocation must be in same pattern.
# include <stdio.h>
# include <conio.h>
struct list
{
int n;
int *p;
};
void main()
{
struct list item0,item1, item2;
item2.n=3;
item2.p=NULL;
item1.n=5;
item1.p=&item2.n;
item0.n=7;
item0.p=&item1.n;
clrscr();
printf("\n Linked list elements are: ");
printf(" %d ",item0.n);
printf(" %d ",*item0.p);
printf(" %d ",*item1.p);
printf(" %d ",*item2.p);
}
OUTPUT
Linked list elements are: 7 5 3 0
Explanation:
In this program the structure list is declared with two elements int n and *p. The pointer
*p recursively points to the same structure. The struct item0, item1 and item2 are three
variables of type list. Consider the initialization.
item2.n=3;
item2.p=NULL;
The item2 is the third (last) node of the list. The pointer p is initialized with null because next to this
no node is present and thus no need to point any address.
item1.n=5;
item1.p=&item2.n;
In this node, n is assigned with five. The pointer points to the data field of the next node i.e.
item2.n (7).
item0.n=7;
item0.p=&item1.n;
This is the first node. The n is assigned 7 and pointer points to data field of item 1, i.e. 5. Figure 6.5
simulates the operation more clearly.
7 5 3
& item1.n & item2.n NULL
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct link
{
char str[30];
struct link *next;
};
printf("%s ",begin->str);
do
{
begin=begin->next;
printf("%s ",begin->str);
}
while(begin->next!='\0');
}
OUTPUT
Data
Structure
with
C
Output: Data Structure with C
Explanation:
In this program, structure link is declared. The str [] char array is used to store string. The
pointer *begin and objects node1, node2, node3 and node4 are declared. In the address
field of object address of other object is stored. For example, in address field of node1 address
of node2 is stored. In address field of node2, address of node3 is stored and so on. Similarly,
using gets () in str [] array string (entered by user) is stored. Using while loop successive
addresses are accessed and complete string is displayed.
Singly Linked List In this type of linked list two successive nodes of the linked list are linked with each
other in sequential linear manner. An example of a singly linked list can be cited as train in which two
successive compartments are connected as shown in Fig. 6.6.
Doubly Linked List In this type of linked list each node holds two-pointer field. In the singly linked
list, address of only next element is pointed. Unlike that, in doubly linked list addresses of next as
well as preceding elements are linked with current node. An example of doubly linked list is shown
in Fig. 6.7.
START
Previous Next
A Circular Linked List Recall that in circular list the first and last elements are adjacent. This type of list
has neither end node nor starting node. A linear single linked list can be circled by storing the address of
first node in the link field of last node as shown in Fig. 6.8.
A Circular Doubly Linked List In this type of linked list each node contains three fields: two link fields
and one data field. The link field holds address of previous and next elements. The address of end node is
linked to first node and vice-verse. Figure 6.9 shows the circular double link list.
START
malloc(): This library function is used to allocate memory space in bytes to the variable. The func-
tion reserves bytes of requested size and returns the base address to pointer variable. The prototype of this
function is declared in the header file alloc.h and stdlib.h. One of the header files must be included in the
header. The syntax of the malloc() function is as follows:
Syntax: pnt=(data type*)malloc(size);
In the above statement, 20 bytes are allocated to integer pointer pnt. In addition, there are other various
memory allocation functions and its complete description is out of the scope of this book.
free(): This function is used to release the memory allocated by the malloc()function.
Syntax: free(pointer variable)
Example: free(pnt)
The above statement releases the memory allocated to pointer pnt. Consider the following program
based on this concept:
Example 6.4 Write a program to demonstrate the use of malloc() and free() functions.
# include <stdio.h>
# include <conio.h>
# include <alloc.h>
void main()
{
char *str;
str=(char *) malloc(6);
clrscr();
printf("Enter string:-");
scanf("%s",str);
printf("\n str=%s",str);
free(str);
}
OUTPUT
Enter string:-India
str=India
Explanation:
In this program a character pointer variable str is declared. The malloc () function allocates
seven bytes to character pointer *str. The string "India" is assigned to pointer str and it is
displayed. The free () function releases the memory from pointer str.
Creation The linked list creation operation involves allocation of structure size memory to pointer of the
same structure. The structure must have a member which points recursively to the same structure. In this
operation, constituent node is created and it is linked to the link field of preceding node.
Traversing It is the procedure of passing through (visiting) all the nodes of the linked list from starting
to end. When any given record is to be searched in the linked list, traversing is applied. When the given
element is found, the operation can be terminated or continued for next search. The traversing is a
common procedure and it is necessary because operations like insertion, deletion, listing cannot be carried
out without traversing linked list.
Display The operation in which data field of every node is accessed and displayed on the screen. In the
display operation from beginning to end, link field of every node is accessed which contains address of
next node. The data of that field is displayed. When NULL is detected the operation ends. Each node
points to the next node and this recursion fashion enables the pointer to reach the successive elements.
The above three operations are common and every program involves these operations. Hence, separate
program is not given.
Searching The searching is a process, in which a given element is compared with all the linked list
elements. The if statement is placed and it checks entire list elements with the given element. When an
element is found it is displayed.
Besides the above operations additional operations such as concatenation and merging of list can be done.
Recall that linked list is a dynamic data structure with ability to expand and shrink as per the program
requirement. The singly linked list is easy and straightforward data structure as compared to other struc-
tures. By changing the link position other type of linked list such as circular, doubly linked list can be
formed. For creating linked list the structure is defined as follows,
struct node
{
int number;
struct node *p;
};
The above structure is used to implement the linked list. In the number, variable entered numbers are
stored. The second member is pointer to the same structure. The pointer *p points to the same structure.
Here, though the declaration of struct node has not been completed, the pointer declaration of the
same structure type is permitted by the compiler. However, the variable declaration is not allowed. This is
because, the pointers are dynamic in nature whereas variables are formed by early binding. The declara-
tion of objects inside the struct leads to preparation of very complex data structure. This concept is called
object composition and its detailed discussion is out of the scope of this book.
We are familiar with the array and we know the importance of base address. Once a base address is
obtained, successive elements can also be accessed. In the linked list, list can be created with or without
header node. The head holds the starting address.
The following steps are used to create linked list with header:
1. Three pointers, i.e. header, first and rear are declared. The header pointer is initialized with NULL. For
example, header=NULL where, header is a pointer to structure. If it remains NULL, it implies that the
list has no element. Such list is known as NULL list or empty list, which is shown in Fig. 6.10 (a).
NULL header
2. In the second step, memory is allocated for the first node of the linked list. For example, the address of first
node is 1888. An integer, say 3, is stored in the variable num and value of header is assigned to pointer next
(Fig. 6.10 (b)).
1888 3 NULL
3. The address of pointer first is assigned to pointers header and rear. The rear pointer is used to identify
the end of the list and to detect the NULL pointer.
4. Again, create a memory location suppose 1890 for the successive node (Fig. 6.10 (c)).
node node->next
1890 10 NULL
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
}*header,*first,*rear;
void main()
{
void create(void);
clrscr();
create();
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
void create(void)
{
struct num *node;
printf("\n Enter number(0 exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
OUTPUT
Enter number(0 exit): 1 3 4 8 7 9 0
Linked list elements are: 1 3 4 8 7 9
Explanation:
This program is an example of linked list with header. Three structure pointers *header,
*first, and *rear are declared after structure declaration. Initially, these pointers are NULL
because they are declared as global. The create function is used to create linked list nodes. Inside
the function create () another structure pointer *node is defined and its scope is local
to the same function. The procedure for creating first and later successive nodes is different.
The if statement checks the value of header. The value of header is NULL. The malloc ()
function allocates memory to pointer first and the entered number is stored in variable num of
the node. In the same if block, both the pointers header and rear are assigned the value of
first. Once, the first node is created, next time the execution of if block is not required. The
while loop iterated continuously and successive nodes are created by allocating memory to
pointer node. Consider the following statements:
1. node->next=NULL;
The above statement, assigns NULL to the pointer next of current node. The user can initiate
this step if no further elements are desired in the linked list.
2. rear->next=node;
The rear pointer keeps track of last node; the address of current node (node) is assigned to
link field of previous node.
3. rear=node;
Before creating next node, the address of last created node is assigned to pointer rear, which is used
for statement (2). In function main (), using while loop the elements of linked list are displayed.
The pointer header is very useful in the formation of linked list. The address of first node (1888)
is stored in pointer header. The value of header remains unchanged until it turns out to be NULL.
The starting location of the list only can be determined by pointer header only.
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
}
In the previous topic, we discussed how linked list is created using header. The implementation of linked
list without header is similar to the linked list with header. The only difference in manipulation is that in
the header list pointer header contains address of first node. In the list without header list, pointer first
itself is starting of the linked list. Consider the following program:
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
}*first,*rear;
void main()
{
void create(void);
clrscr();
create();
printf("Double linked list elements: ");
while(first!=NULL)
{
printf(" %d",first->num);
first=first->next;
}
}
void create(void)
{
struct num *node;
printf("\n Enter number(0 to exit): ");
if(first==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
OUTPUT
Enter number(0 to exit): 1 5 9 8 7 4 2 0
Double linked list elements: 1 5 9 8 7 4 2
Explanation:
In this program only two pointers, first and rear, are declared. They are global pointers;
hence, their contents are NULL. The function create () is invoked and the rest of the
execution of the program is the same as the previous program.
Example 6.7 Write a program to create a single linked list and display the elements.
# include <stdio.h>
# include <conio.h>
struct node
{
int number;
struct node *p;
} *h,*f,*t;
void main()
{
char c;
clrscr();
f=NULL;
do
{
h=(struct node *)malloc(sizeof(struct node));
printf("Enter a number: ");
scanf("%d",&h->number);
if(f!=NULL)
{
t->p=h;
t=h;
}
else f=t=h;
fflush(stdin);
printf("Continue(y/n): ");
scanf("%c",&c);
} while(c=='y');
t->p=NULL;
t=f;
printf("\nThe list elements are: ");
while(t!=NULL)
{
printf(" %d ",t->number);
t=t->p;
}
}
OUTPUT
Enter a number: 5
Continue(y/n): y
Enter a number: 3
Continue(y/n): y
Enter a number: 8
Continue(y/n): y
Enter a number: 9
Continue(y/n): n
The list elements are: 5 3 8 9
Explanation:
In this program, the structure node is declared. The struct node contains integer variable
number and pointer to the same structure *p. The structure pointer variables *h, *f and *t
are also declared. In function main (), variable of character type c is defined. The pointer f is
initialized to NULL.
In the do–while, loop using malloc() function memory equal to size of structure node (4 byte) is
allocated to pointer h. The entered number is stored in the number variable. The if statement checks
the pointer f. If it is NULL, the h is assigned to other two pointers, i.e. f and t. Here, f is first node. h is
header and t is used for temporary storage. The user has to enter ‘y’ to enter next elements. First time,
the value of f is NULL and hence, else block is executed. The purpose of assignment of h to f and t is
as follows:
1. In if() block t is used to assign address of newly created next node to the current, i.e. h.
2. Only for the first time else block is executed and in which address of h (when first node is created) is
assigned to f. f contains address of first node.
When the user finishes entering number, t->p=NULL; this statement assigns NULL to pointer p and
link ends here. The address of the first node (f ) is assigned to t. Using while, loop, the pointer p is accessed
and elements are accessed. When NULL pointer is encountered, the while loop terminates.
In Fig. 6.11, the number contains the actual data of the node. The structure node pointer is used to
point to the next node in the linked list hence it is called as the linked field. The link field refers to itself
in recursive form.
struct node
{
};
Example 6.8 Write a program to create a list. Display the list up to specified element.
# include <stdio.h>
# include <conio.h>
struct node
{
int number;
struct node *p;
} *h,*f,*t;
void main()
{
char c,n;
clrscr();
f=NULL;
do
{
h=(struct node *)malloc(sizeof(struct node));
printf("Enter a number: ");
scanf("%d",&h->number);
if(f!=NULL)
{
t->p=h;
t=h;
}
else f=t=h;
fflush(stdin);
printf("Continue(y/n): ");
scanf("%c",&c);
} while(c=='y');
t->p=NULL;
t=f;
printf("\n Enter a element to search: ");
scanf("%d",&n);
printf("\nThe list elements are: ");
while(t!=NULL)
{
printf(" %d ",t->number);
if(t->number==n)
break;
t=t->p;
}
}
OUTPUT
Enter a number: 1
Continue(y/n): y
Enter a number: 3
Continue(y/n): y
Enter a number: 5
Continue(y/n): y
Enter a number: 4
Continue(y/n): y
Enter a number: 9
Continue(y/n): n
Enter a element to search: 4
The list elements are: 1 3 5 4
Explanation:
This program is based on searching operation. In this program, the specific element is compared
with every element of the linked list. When the given element is found in the list, the while
loop terminates.
Insertion of an element in the linked list leads to several operations. The following steps are involved in
inserting an element:
a. Creation of node: Before insertion, the node is created. Using malloc() function memory space for
node is booked.
b. Assignment of data: Once the node is created, data values are assigned to members.
c. Adjusting pointers: The insertion operation changes the sequence. Hence, according to the sequence,
the address of next element is assigned to inserted node. The address of current node (inserted) is
assigned to previous node.
The insertion of node can be done in different positions in the list by the following ways:
a. Insertion of the node at the starting: The created node is inserted before the first element. After inser-
tion, the newly inserted element will be the first element of the linked list. In this insertion only the
contents of successive node’s pointer are changed.
b. Insertion at the end of the list: A new element is appended at the end of the list. This operation is easy
as compared to other two (a) and (c) operations. In this insertion only the contents of previous node’s
pointer is changed.
c. Insertion at the given position in the list: In this operation, the user has to enter the position number.
The given position is counted and the element is inserted. In this insertion contents of both previous
and next node’s pointer are altered.
Inserting an element at the beginning involves updating links between link fields of two pointers. After
insertion of new node, the previously existing nodes are moved to the front. Figures 6.12 (a) and (b) and
Example 6.9 illustrates the insertion at the starting.
Header
5 & 8 & 3
Node 1 Node 2 Node 3
9 &
New node
Header
After insertion, the new node will be first node and its link field points to the second element, which
was ex-first element.
The program given below explains the insertion of an element in the list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
void main()
{
void atbeg(void);
void create(void);
void show(void);
clrscr();
printf("\n Operation creation:");
create();
show();
atbeg();
printf("\n The elements after insertion: ");
show();
atbeg();
show();
atbeg();
show();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void atbeg()
{
struct num *node,*t;
node=(struct num*) malloc(sizeof(struct num));
printf("\n Insert an element at starting: ");
scanf("%d",&node->num);
t=first;
header=node;
header->next=t;
first=header;
}
void show()
{
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
}
}
OUTPUT
Operation creation:
Enter numbers(0 to exit): 1 2 3 4 0
Linked list elements are: 1 2 3 4
Insert an element at starting: 5
The elements after insertion:
Linked list elements are: 5 1 2 3 4
Insert an element at starting: 9
Linked list elements are: 9 5 1 2 3 4
Insert an element at starting: 3
Linked list elements are: 3 9 5 1 2 3 4
Explanation:
Here, an element is inserted at the beginning. The function void atbeg() performs this task. In
this function two pointers *node (local to this function) and *t are declared. The malloc()
function allocates memory to pointer node. The entered element is placed in the newly created
node.
t=first This statement assigns contents of first to pointer t. This is essential because the newly created
node will become first. Hence, before changing pointer first, it is assigned to pointer t.
Header=node We know that header always points to the first node. The newly created node, that is to
be inserted at starting is assigned to pointer header.
header->next=t The pointer t holds address of first element (before insertion). After insertion, it will
be second. The address of second node is assigned to first. The pointer header holds address of first node
and pointer t holds address of second element. Thus, these two successive elements are linked by this
statement.
first=header Before execution of this statement the first points to second node. If we remove this
statement, only one element can be inserted. If we try to insert second element, it will be replaced by the
first element. After assignment of address of header to first, the insertion can be carried out successfully.
A new element is inserted or appended at the end of existing linked list. The address of newly created node
is linked to previous node that is NULL. The new node link field is assigned a NULL value. Figure 6.13
shows the insertion operation at the end.
Header
5 & 8 & 3
New Node
Header
Example 6.10 Write a program to insert an element at the end of the linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
}*header,*first,*rear;
void main()
{
void atend(void);
void create(void);
void show(void);
clrscr();
printf("\n Operation creation:");
create();
show();
atend();
show();
atend();
show();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void atend()
{
struct num *node,*t;
t=rear;
rear=(struct num*) malloc(sizeof(struct num));
printf("\n Insert an element at end: ");
scanf("%d",&rear->num);
t->next=rear;
rear->next=NULL;
}
void show()
{
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
}
header=first;
}
OUTPUT
Operation creation:
Enter numbers(0 to exit): 1 2 3 0
Linked list elements are: 1 2 3
Explanation:
In this program the function atend() is used to insert element at the end of the list.
rear=(struct num*) malloc(sizeof(struct num)) After assigning address of rear pointer to t, new
memory location is allocated to pointer rear. The new element entered is stored in the data field of
pointer rear.
t->next=rear Now, the pointer t holds the last but one node of the linked list. The address of new
inserted end node is assigned to link field of node through pointer t.
rear->next=NULL The rear pointer holds address of last node and its link field is assigned a NULL
value.
Insertion of node can be done at a specific position in the linked list. Figure 6.14 and the program explain
insertion of a node at the specific position in the linked list. If we want to insert a node at third position,
then observe Figs. 6.14 (a) and 6.14 (b).
Header
5 & 8 & 9
3 &
Node is inserted at third position and after insertion, the linked list would be as shown below.
Header
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
int count;
void main()
{
void atpo(int);
void create(void);
void show(void);
int p;
clrscr();
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void atpo(int k)
{
struct num *nw,*su,*pr;
int c=1;
while(header!=NULL)
{
c++;
header=header->next;
if(k==1) pr=first;
if(c==k) pr=header;
if(c==(k+1)) su=header;
}
pr->next=nw;
nw->next=su;
header=first;
}
void show()
{
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
count++;
header=header->next;
}
header=first;
}
OUTPUT
Operation creation:
Enter numbers(0 to exit): 1 2 3 4 5 0
Explanation:
In this program the operation creation and display of linked nodes is similar as explained in the
last programs. Here, we are going to discuss insertion at a specified position carried out by a
function atpo().
The position number specified by the user after reducing value by one is passed to function atpo().
The variable k, formal argument of p, gets this value. Another variable c is declared in the body of atop().
The variable c is incremented and successive nodes of the linked list are accessed by the statement
header=header->next;
Consider the statements
a. if (k==1) pr-first;
b. if (c==k) pr=header;
c. if (c==(k+1)) su=header;
The insertion of node can be done in between two nodes and hence the preceding and succeeding
nodes must be stored in separate pointer variables. When the user specifies two, the entered value is
reduced by one and becomes one and stored in variable k.
When value of k is one, i.e. the address of first node is stored in the pointer pr. When k is one the
statement (b) has no reason to consider. The address of succeeding node is obtained in the statement
(c). The address obtained is stored in the pointer variable su. Thus, by using following statements proper
links are connected:
a. pr->next=nw;
b. nw->next=su;
c. header=first;
Before execution of the above statements, the new node is created using nw pointer. The nw is a node
to be inserted.
The address of data field of node nw is linked to the previous node (pr) by the statement
pr->next=nw.
The address of succeeding node is stored in the linked field of new node nw by the statement
nw->next=su.
Example 6.12 Write a program to count the total nodes of the linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
void main()
{
void create(void);
int count=0;
clrscr();
create();
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
count++;
}
printf("\n Total Nodes are: %d ",count);
}
void create(void)
{
struct num *node;
printf("\n Enter number: ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
OUTPUT
Enter number: 1 2 3 4 5 6 7 8 0
Explanation:
This program is same as the last few programs. The count variable is placed in the while loop
and incremented. The count variable displays the number of nodes.
6.13.2 Deletion
An element that is to be deleted from the linked list is to be searched first. For this, the traversing opera-
tion must be carried out thoroughly on the list. Deleting a node from the linked list means
1. Deleting the first node
2. Deleting the last node
3. Deleting a specified node.
If first node is to be deleted, then the second node is assigned to header.
If the last node is to be deleted, NULL value is assigned to link field of the last but one node.
If the node which is to be deleted is in between the linked list then access the predecessor and successor
node of the specified node and link them. Figure 6.15 shows the deletion of first node.
1. Deleting the first node
Header Last Node
Element to be Deleted
(a) Before Deletion
2 & 7 & 3
Example 6.13 Write a program to remove elements from the beginning of the linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{ int num;
struct num *next;
} *header,*first,*rear,*k;
void main()
{
void erase(void);
void create(void);
void show(void);
clrscr();
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void erase()
{
struct num *s;
s=header;
free(s);
header=first->next;
first=first->next;
}
void show()
{
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
}
OUTPUT
Operation creation:
Enter numbers(0 to exit): 1 2 3 4 5 0
Linked list elements are: 1 2 3 4 5
After deletion:
Linked list elements are: 2 3 4 5
Linked list elements are: 3 4 5
Linked list elements are: 4 5
Explanation:
In this program erase() function is used to remove the beginning node of the linked list. The
creation of linked list is similar as discussed in the last few program. In the erase() function
another object *s is declared. The pointer header is assigned to pointer *s. The pointer *s
occupies same amount of memory like header. The free() function is used to release the
memory. The memory allocated with pointer *s is released. The address of second node
is assigned to header and first node. Thus, whenever the function erase() is invoked, the
beginning element is removed and the updated list is displayed by the function show().
Element to be Deleted
(a) Before Deletion
9 & 2 & 7
The representation of stacks using linked lists is discussed in this section. 34 & Top
The stack grows vertically as shown in the Fig. 6.17. The stack moves
from the bottom to the top element.
5 &
The following steps are used to represent stacks using linked lists:
1. The list is empty initially. So the top pointer is NULL (stack is empty).
7 &
2. The insertion of elements into the stack can be performed in the
following ways:
(a) Allocate memory using dynamic memory allocation for new node. 4
top = struct(node*)malloc(sizeof(node));
(b) Insert element ‘x’ at the top. Where ‘x’ may be any value. Figure 6.17 Stack Elements
top->data=x
X Top
Top
3. Similarly, other elements can be inserted into the stack through the following steps.
(a) node *p;
(b) Dynamic memory allocation for new node p
top = struct(node*)malloc(sizeof(node));
(c) Insert the new element ‘y’ into the newly allocated node p.
p->data=y;
Y P
X Top
X Top
p->next=top.
(e) After insertion, the top position changes. Pointer points to the top i.e. newly inserted node ‘Y’
Y Top
Example 6.14 A program to implement stack operations using linked list is given below.
# include<stdlib.h>
struct node
{
int data;
struct node *link;
};
struct node *top=NULL,*temp;
void main()
{
int ch,data;
clrscr();
while(1){
printf("\n1.Push\n2.Pop\n3.Display\n4.Exit\n");
printf("\nEnter Your choice:");
scanf("%d",&ch);
switch(ch)
{
case 1:
temp=(struct node *)malloc(sizeof(struct node));
printf("Enter a node data :");
scanf("%d",&data);
temp->data=data;
temp->link=top;
top=temp;
break;
case 2:
if(top!=NULL)
{
printf("The poped element is %d",top->data);
top=top->link;
}
else
{
printf("\nStack Underflow");
}
break;
case 3:
temp=top;
if(temp==NULL)
{
printf("\nStack is empty\n");
}
while(temp!=NULL)
{
printf("->%d->",temp->data);
temp=temp->link;
}
break;
case 4:
exit(0);
}
}
}
OUTPUT:
1.Push
2.Pop
3.Display
4.Exit
1.Push
2.Pop
3.Display
4.Exit
1.Push
2.Pop
3.Display
4.Exit
Explanation:
In this program, using switch, various operations are performed. Stack of elements operation is
done with case 1. Case 2 deals with pop, case 3 is for display and case 4 is for exit.
Example 6.15 Write a program to remove last elements of the linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
int c;
struct num
{
int num;
struct num *next;
} *header,*first,*rear,*k;
void main()
{
void create(void);
void show(void);
clrscr();
printf("\n Operation creation:");
create();
show();
printf("\n After deletion: ");
show();
show();
show();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
c++;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
c++;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void show()
{
OUTPUT
Operation creation:
Enter numbers(0 to exit): 1 5 9 7 5 3 2 0
Linked list elements are: 1 5 9 7 5 3
After deletion:
Linked list elements are: 1 5 9 7 5
Linked list elements are: 1 5 9 7
Linked list elements are: 1 5 9
Explanation:
This program demonstrates the deletion from the end of the list. Here, the show() function
performs the deletion function and after the function is executed, the rear pointer of the list is freed
and an address of previous node is assigned to it. The show() function also displays the elements.
Last Node
So far, we have studied how to remove the first and last node of the list. Now, we want to remove the
node randomly from the list. This case is different from the last two cases. In this deletion operation,
when a specific node is removed, the previous and next nodes are linked. The memory of the given node
is released. The removal of node is shown in Fig. 6.18.
Example 6.16 Write a program to remove a specific element from the list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
# include <process.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear,*k;
int count;
void main()
{
void remove1(int);
void create(void);
void show(void);
int n;
clrscr();
printf("\n Operation creation:");
create();
show();
printf("\n Enter position of number for removal < %d ",count);
scanf("%d",&n);
remove1(n);
getch();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
count =1;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
count++;
node->next=NULL;
rear->next=node;
rear=node;
}
}
void remove1(int j)
{
int p,h;
void show(void);
j––;
p=j-1;
if(j==0)
{
header=header->next;
show();
}
for(h=0;h<count;h++)
{
if(h==p)
{
header->next=header->next->next;
header=first;
show();
}
header=header->next;
}
}
void show()
{
printf("\n Linked list elements are: ");
while(header!=NULL)
{
printf(" %d ",header->num);
header=header->next;
}
header=first;
}
OUTPUT
Operation creation:
Enter numbers(0 to exit): 4 8 6 5 9 7 4 0
Linked list elements are: 4 8 6 5 9 7 4
Enter position of number for removal < 7 4
Linked list elements are: 4 8 6 9 7 4
Explanation:
In this program the element specified by the user is removed. After entering the number, the list
of number is displayed. User has to specify only element number. Using for loop the specified
location is searched. The address of next element is assigned to previous element by skipping the
element to be deleted. Thus, the link between previous and next element is joined.
There are only a few differences between the functioning of a queue and a linked list. Elements can be
deleted or inserted from anywhere in a linked list. However, in a queue the element in the front is deleted.
The addition of elements is performed at the rear.
Front Rear
2. The insertion of an element into the queue depends upon two cases.
Case 1: If the queue is empty.
(a) Allocate memory for new node.
R= F= struct(node*)malloc(sizeof(node));
FR
(b) Insert the element ‘A’ into the empty queue. As the queue is empty after insertion, rear (R) and
front (F) will point to the inserted A.
R->data=A;
FR
Case 2: If the queue is not empty, i.e. the insertion of a new element is to be done from rear.
(a) Allocate memory for new node.
R=struct(node*)malloc(sizeof(node));
F R
A &
Memory allocated to
insert new element
(b) R=R->next
F R
A &
(c) Insert the new element ‘B’ from the rear end.
R->data=B;
R->next=NULL;
F R
A & B
Conceptually, the singly linked list can be traversed in only forward direction. The structure of singly
linked list contains only one pointer, pointing to the next node. The same list can be displayed in reverse
fashion using the following program.
# include <stdio.h>
# include <conio.h>
struct node
{
int value;
struct node *next;
};
void create(void);
void reverse(void);
void show(void);
struct node *rear;
int nodes;
struct node *head;
void main()
{
clrscr();
create();
reverse();
show();
}
void create()
{
struct node *item;
printf("Enter numbers(0 to exit): ");
if(head==NULL)
{
head=(struct node*)malloc(sizeof(struct node));
scanf("%d",&head->value);
head->next=NULL;
rear=head;
}
while(1)
{
item=(struct node *)malloc(sizeof(struct node));
scanf("%d",&item->value);
if(item->value==0) break;
item->next=NULL;
rear->next=item;
rear=item;
}
}
void reverse()
{
struct node *prv, *cur, *next;
if(head==NULL)
{
printf("List is empty");
return;
}
cur=head;
prv=NULL;
while(cur !=NULL)
{
next=cur->next;
cur->next=prv;
prv=cur;
cur=next;
}
head=prv;
}
void show()
{
printf("\n Reverse list is: ");
while(head!=NULL)
{
nodes++;
printf(" %d ",head->value);
head=head->next;
}
nodes=0;
}
OUTPUT
Enter numbers(0 to exit): 1 2 3 4 5 6 7 0
Reverse list is: 7 6 5 4 3 2 1
Explanation:
In this program the pointer *first is global and hence has an initial value NULL. The logic of
the program is very simple. The three pointers *prv, *cur, *next; are used for swapping
of the addresses. The end of the list is obtained and the address of previous is assigned to first.
Thus, the link list is reversed. The pointer head now points to the last node and every node
contains an address of the previous node instead of next node.
Concatenation of two lists means adding a list at the end of another list as shown in Fig. 6.20. Consider
the following program.
List (1)
1 & 5 & 9 &
Example 6.18 Write a program to concatenate two linked lists and display the elements.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct node
{
int value;
struct node *next;
};
struct node *rear,*start,*f;
struct node *create(void);
void concat(void);
void print(void);
int count=1;
struct node *one,*two;
void main()
{
clrscr();
one=create();
two=create();
concat();
print();
}
struct node *create()
{
struct node *item,*temp;
temp=(struct node *)malloc(sizeof(struct node));
printf("\nEnter numbers of list (%d) (0) to exit: ",count);
scanf("%d",&temp->value);
temp->next=NULL;
rear=temp;
while(1)
{
item=(struct node*)malloc(sizeof(struct node));
scanf("%d",&item->value);
if(item->value==0)
{
break;
}
item->next=NULL;
rear->next=item;
rear=item;
if(count==1)
f=rear;
}
count++;
return(temp);
}
void concat()
{
f->next=two;
start=one;
}
void print()
{
printf("Merged list: ");
while(start!=NULL)
{
printf(" %d ",start->value);
start=start->next;
}
}
OUTPUT
Enter numbers of list (1) (0) to exit: 1 5 9 0
Enter numbers of list (2) (0) to exit: 7 3 4 0
Merged list: 1 5 9 7 3 4
Explanation:
The creation() function is used to create linked lists. In this program, two lists are created.
The starting address of the list(2) is assigned to the rear of the first list. Here, the same
function is used to create both the lists. Hence, the contents of the local variables used in
create() function alters. In order to obtain the address of last node of the first list, if
condition is inserted and the address of the rear node is stored in the pointer *f. All this
process is carried out by the function concat(). In the function concat(), the starting
address of the list(2) is assigned to rear of the list(1). The pointer start is assigned the
address of the list(1). Using the same starting address the function print() prints the
element.
Once a singly linked list is formed, it can split many sub-lists. The technique involved in splitting is very
simple. Put the NULL value in the node where you want to finish the first sub-list in the main list. Later,
the next node will act as first node of the second sub-list. Thus, two or more sub-lists can be created from
a single list (see Fig. 6.21).
NULL
Partition
New Head
Example 6.19 Write a program to split a single linked list in two separate lists and display the
elements.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct node
{
int value;
struct node *next;
};
struct node *rear,*start,*f;
struct node *create(void);
struct node * split(int);
void print(struct node *);
int count=0;
struct node *one,*two;
void main()
{
clrscr();
one=create();
two=split(count/2);
printf("The two separated list are:-");
print(one);
print(two);
}
struct node *create()
{
struct node *item,*temp;
temp=(struct node *)malloc(sizeof(struct node));
while(s!=NULL)
{
printf(" %d ",s->value);
s=s->next;
}
}
OUTPUT
Enter numbers of list(0 to exit): 1 2 3 4 5 6 7 8 0
The two separated list are:
list (1): 1 2 3 4
list (2): 5 6 7 8
Explanation:
In the create() function the counter variable c is incremented and keep an eye on the number
of nodes created. In the split(), function c is divided by two, i.e. to divide the list elements. In
case, odd number of nodes are created then, the first sub-list gets more nodes. In the split()
function, exactly half of the nodes are traversed and the node in the middle is assigned a value
NULL. Thus, the first sub-list ends here. The next node is considered as a head node for the
second sub-list. The address of head node of the second sub-list is assigned to pointer two. Thus,
using pointers one and two with function print() two sub-lists are displayed.
In circular linked list the last node points to the header node. The linear link list can be converted to circu-
lar linked list by linking the last node to the first node. The last node of the linear linked list holds NULL
pointer, which indicates the end of linked list but performance of linked list can be advanced with minor
adjustment in the linear linked list. Instead of placing the NULL pointer, the address of the first node can
be given to the last node, such a list is called circular linked list (as shown in Fig. 6.22).
The circular linked list is more helpful as compared to singly linked list. In the circular linked list, all
the nodes of the list are accessible from the given node. Once a node is accessed, by traversing all the nodes
can be accessed in succession.
First Node
In this type of list, the deletion operation is very easy. To delete an element from the singly linked list, it is
essential to obtain the address of the first node of the list. For example, we want to delete the element, say 5,
which exists in the middle of the list. To remove the element five, we need to find predecessor of five. Obviously,
a particular element can be searched using searching process in which all elements are visited and compared.
In the circular linked list, no such process is needed. The address of predecessor can be found from the given
element itself. In addition, the operation splitting and concatenation of the list (discussed later) are easier.
Though the circular linked list has advantages over linear linked list, it also has some limitations. This
list does not have first and last node. While traversing the linked list, due to lack of NULL pointer, there
may be a possibility to get into an infinite loop. Thus, in the circular linked list it is necessary to identify
the end of the list. This can be done by setting up the first and last node by convention. We detect the
first node by creating the list head, which holds the address of the first node. The list head is also called as
external pointer. We can also keep a counter, which is incremented when nodes are created and end of the
node can be detected. Figure 6.23 gives an example of circular linked list with header.
Header Last Node
Element to be Deleted
There are two methods for finding or detecting the end of the circular linked list. They are:
1. Detecting end with counter
2. Detecting end with pointer.
First Node
Example 6.20 Write a program to create a circular linked list and display the elements.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
int count=0;
void main()
{
void create(void);
int j,k=0;
clrscr();
create();
printf("\n Linked list elements are: \n");
printf("The circular linked list traversing\n");
for(j=0;j<5;j++)
{
printf("\n In (%d) round: ",j+1);
while(k<count)
{
printf(" %d ",header->num);
header=header->next;
k++;
}
k=0;
}
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
count++;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
count++;
node->next=header;
rear->next=node;
rear=node;
}
}
OUTPUT
Enter numbers(0 to exit): 1 5 9 8 7 0
Linked list elements are:
The circular linked list traversing
In ( 1 ) round: 1 5 9 8 7
In ( 2 ) round: 1 5 9 8 7
In ( 3 ) round: 1 5 9 8 7
In ( 4 ) round: 1 5 9 8 7
In ( 5 ) round: 1 5 9 8 7
Explanation:
Consider the following statement node->next=header;. The address of the first node
(header node which points to the first node) is assigned to last. If you see the same statement
in the creation of singly linked list, the statement is node->next =NULL;. Instead of NULL,
header is assigned.
With creation operation, the counter variable is incremented. The counter variable is used to count the
total number of nodes in the linked list. The for loop iterates from zero to 5 and the linked list elements
are displayed five times. The while loop takes loops and it contains the condition k <counter. The variable
k is incremented and when it is one less than counter it means end of the circular linked lists is reached.
Again, in second iteration of for loop, k is initialized to zero. The value of k is nothing but the position
of the node in the list. The value 0 means first node, 1 means second node and so on. Thus, first and last
node of the list can be obtained.
If we remove the counter and the condition statements, which terminate the loop, we get into the infinite
loop. After the last node, immediately first node is accessed. After removing the statement printf(“ \nRound
(%d):” , j+1); (after for loop) the output of the program would be
OUTPUT
Enter number: 1 2 3 0
Linked list elements are:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
From the output, you can observe that sequence is preserved after last element (3). First, element 1
appears and displays the remaining elements. This process would be continued.
same pointer is used here to detect the end of the circular linked list. Recall that in the circular linked lists
the last node points to the first node, i.e. the pointer field of the rear pointer points to the data field of the
first node as per the Fig. 6.25.
While scanning the circular linked list in order to get the end of the list, we have to check contents of
all the pointer fields of the nodes. A link field of a node in a circular linked list pointing to the data field
of the first node is the last node of the linked list. To implement this idea into a practical program we have
to follow the following theory.
1. Obtain the address of the data field of the first node.
2. While traversing, compare the obtained address with the link field.
3. If the address obtained and the address pointing link field is same, i.e. that node is the last node.
Consider the following program,
Example 6.21 Write a program to detect the last node of the list using external pointer.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
void main()
{
void create(void);
int j;
clrscr();
create();
printf("\n Elements of circular linked list are:\n");
for(j=0;j<5;j++)
{
printf("\nRound(%d): ",j+1);
do
{
printf(" %d ",header->num);
header=header->next;
if(rear->next==(struct num *)&(header->num))
break;
}while(1);
}
printf("\nAddress of rear link field = %u",rear->next);
printf("\nAddress of first data field = %u",(struct num *)&(header->num));
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=header;
rear->next=node;
rear=node;
}
}
OUTPUT
Enter numbers(0 to exit): 5 4 8 7 0
Elements of circular linked list are:
Round (1): 5 4 8 7
Round (2): 5 4 8 7
Round (3): 5 4 8 7
Round (4): 5 4 8 7
Round (5): 5 4 8 7
Address of rear link field = 1970
Address of first data field = 1970
Explanation:
This program is similar to the previous program.
As soon as the last node is detected, the above statement terminates the while loop. The if statement
compares the address of data field of first node with pointer field of each node. When it is found that both
are same, the end of linked list is spotted. Here, the address of the data field is obtained by the typecasting
statement (struct num *) &(header->num).
Here, typecasting is essential because an address of integer cannot be assigned to pointer of structure
type. The syntax (struct num*) converts the address of integer header->num to pointer rear->next. In the
output, addresses are also displayed.
4 &
New Node
(a) A Circular Linked List Before Insertion
4 &
New Node
When a new node is inserted at the end, the address of new node is given to the last node. The address
of first node is given to the new node. Figure 6.27 shows the insertion operation at end.
Example 6.22 Write a program to demonstrate insertion of element at the beginning and at end of
the circular linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
void main()
{
void create(void);
void iatend(void);
void iatbeg(void);
void show(void);
clrscr();
create(); /* creation of linked list*/
show(); /* display of linked list*/
iatbeg(); /* insertion at beginning*/
show(); /* display of elements*/
iatend(); /* insertion at the end*/
show();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=header;
rear->next=node;
rear=node;
}
}
void iatend()
{
struct num *node;
node=(struct num*) malloc(sizeof(struct num));
printf("\nEnter a number to be inserted at end: ");
scanf("%d",&node->num);
node->next=header;
rear->next=node;
rear=node;
}
void iatbeg()
{
struct num *node;
node=(struct num*) malloc(sizeof(struct num));
printf("\nEnter a number to be inserted at beginning: ");
scanf("%d",&node->num);
node->next=header;
header=node;
rear->next=header;
}
void show()
{
printf("Linked list elements are: ");
do
{ printf(" %d ",header->num);
header=header->next;
if(rear->next==(struct num *)&(header->num))
break;
} while(1);
}
OUPUT
Enter numbers(0 to exit): 1 2 3 0
Linked list elements are: 1 2 3
Enter a number to be inserted at beginning: 4
Linked list elements are: 4 1 2 3
Enter a number to be inserted at end: 5
Linked list elements are: 4 1 2 3 5
Explanation:
The reader is already aware of the creation and display of linked list. The function iatbeg()
inserts the element at the beginning of the list and the function iatend() inserts the element
at the end.
Example 6.23 Write a program to insert an element at a given position in the circular linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
void main()
{
void create(void);
void show(void);
void atgiven(int);
int n;
clrscr();
create();
printf("\n Enter position number: ");
scanf("%d",&n);
n––;
atgiven(n);
show();
}
void create(void)
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=header;
rear->next=node;
rear=node;
}
}
void show()
{
printf("Linked list elements are: ");
do
{ printf(" %d ",header->num);
header=header->next;
if(rear->next==(struct num *)&(header->num))
break;
} while(1);
}
void atgiven(intj)
{
int c=0,k=j-2;
struct num *node,*prv;
printf("\nLinked list upto %d element are: ",j);
do
{ printf(" %d ",header->num);
header=header->next;
if(k==c) prv=header;
c++;
} while(c<j);
node=(struct num*) malloc(sizeof(struct num));
printf("\n Enter a element: ");
scanf("%d",&node->num);
node->next=header;
prv->next=node;
header=first;
}
OUTPUT
Enter numbers(0 to exit): 1 2 3 5 6 7 0
Enter position number: 3
Linked list up to 3 element are: 1 2 3
Enter a element: 4
Linked list elements are: 1 2 3 4 5 6 7
Explanation:
In this program, the function atgiven() is used to insert the element at the specified position.
The user specifies the position number where number is to be inserted. The position number is
passed to the function atgiven(). In this function, using while loop the linked list is traversed
and the previous and next node of the given element are stored in the pointers prv and header.
node->next=header;
prv->next=node;
header=first;
The new node created is given the address of header. At this point header is not pointing to the
first node. Due to traversing in the while loop, it is pointing to the nth element, see the statement
(header=header->next;). The new node is assigned the address of next node. The prv is assigned the
address of new node. Again, the header is assigned the address of first node.
Some authors describe the insertion before and after. I think there is no need to classify it differently.
The above program inserts the element after the specified position. In order to insert an element before the
given position, only decrease the value of variable n before passing to function atgiven(). By changing
the value of n, the user can accomplish the operation insertion before, after and exactly at given position.
1. Deletion at beginning
2. Deletion at the end
3. Deletion at the specified position.
The pointer adjustment is same in insertion and deletion. However, in insertion, an element is inserted
and memory is allocated. In deletion, a node from the list is removed and memory is released. Consider
the following program. Refer Figures 6.28 and 6.29.
Example 6.24 Write a program to delete an element from end and beginning of the linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
static int c;
main()
{
void create(void);
void datend(void);
void datbeg(void);
void show(void);
clrscr();
create();
show();
printf("\nAfter deletion of beginning element");
datbeg();
show();
printf("\nAfter deletion of end element");
datend();
show();
}
void create()
{
struct num *node;
printf("\n Enter numbers(0 to exit): ");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
c++;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=header;
rear->next=node;
rear=node;
c++;
}
}
void datend()
{
int k=0;
while(k++<(c-2))
header=header->next;
free(rear);
rear=header;
rear->next=first;
header=first;
c––;
}
void datbeg()
{
struct num *node;
node=first;
first=header=header->next;
free(node);
rear->next=header;
c––;
}
void show()
{
printf("\nLinked list elements are: ");
do
{
printf(" %d ",header->num);
header=header->next;
if(rear->next==header) break;
} while(1);
header=first;
}
OUTPUT
Enter numbers(0 to exit): 1 2 3 4 5 6 7 0
Linked list elements are: 1 2 3 4 5 6 7
After deletion of beginning element
Linked list elements are: 2 3 4 5 6 7
After deletion of end element
Linked list elements are: 2 3 4 5 6
Explanation:
In this program to erase the element from beginning and end, help of counter variable is taken. The
variable c is incremented in the function create() when a new node is created. Similarly, when
an element is removed the counter c is decremented. The counter variable is needful only in the
function datend(), because to erase the last element, the last but one element is obtained and
it is connected to first node.
Example 6.25 Write a program to delete the specified element from the linked list.
# include<stdio.h>
# include<conio.h>
# include<malloc.h>
struct num
{
int num;
struct num *next;
} *header,*first,*rear;
static int c;
main()
{
void create(void);
void atgiven(int);
void show(void);
int n;
clrscr();
create();
show();
printf("\n Enter position number:");
scanf("%d",&n);
atgiven(n);
show();
getch();
}
void create()
{
struct num *node;
printf("\n Enter numbers(0 to exit):");
if(header==NULL)
{
first=(struct num*)malloc(sizeof(struct num));
scanf("%d",&first->num);
first->next=header;
header=first;
rear=first;
c++;
}
while(1)
{
node=(struct num*) malloc(sizeof(struct num));
scanf("%d",&node->num);
if(node->num==0) break;
node->next=header;
rear->next=node;
rear=node;
c++;
}
}
void atgiven(int g)
{
struct num *prv,*temp;
int k=0,p;
p=g-2;
while(k++<g)
{
header=header->next;
if(k==p) prv=header;
if(k==(p+1))temp=header;
}
c––;
prv->next=header;
header=first;
free(temp);
}
void show()
{
printf("\nLinked list elements are:");
do
{
printf("%d ",header->num);
header=header->next;
if(rear->next==header) break;
} while(1);
header=first;
}
OUTPUT
Enter numbers(0 to exit: 5 8 7 4 5 3 2 0
Linked list elements are: 5 8 7 4 5 3 2
Enter position number: 3
Linked list elements are: 5 8 4 5 3 2
Explanation:
In this program the function atgiven() deletes the specified element from the circular linked
list. The user enters the position number and it is passed to the function atgiven(). In this
function using while loop entire list is scanned and predecessor and successor of the given
element are stored in the pointer *prv and header. The node, which is to be removed, is
stored in the pointer temp. Using free() function the memory allocated is released. Then, a
link between pointer prv and header is established by the following statements.
The singly linked list and circular linked list contain only one pointer field. Every node holds an address
of next node. Thus, the singly linked list can traverse only in one direction, i.e. forward. This limitation
can be overcome by doubly linked list. Each node of the doubly linked list has two pointer fields and
holds the address of predecessor and successor elements. These pointers enable bi-directional traversing,
i.e. traversing the list in backward and forward direction. In several applications, it is very essential to
traverse the list in backward direction. The pointer pointing to the predecessor node is called left link and
pointer pointing to successor is called right link. A list having such type of node is called doubly linked
list. The pointer field of the first and last node holds NULL value, i.e. the beginning and end of the list
can be identified by NULL value. The structure of the node is as shown in Fig. 6.30.
Previous Next
Link Data Link
Example 6.26 Write a program to create the doubly linked list. Display the elements in original and
reverse sequence.
# include<stdio.h>
# include<conio.h>
# include<malloc.h>
struct dlist
{
struct dlist *prev;
int number;
struct dlist *next;
};
struct dlist *end,*start;
struct dlist *node=NULL;
void create(void);
void showl(void);
void showr(void);
void main()
{
clrscr();
create();
showl();
showr();
getch();
}
void create()
{
node=(struct dlist*)malloc(sizeof(struct dlist));
printf("\n Enter Numbers (0 to stop):");
while(1)
{
scanf("%d",&node->number);
if(node->number==0) break;
else
{
node->prev =end;
end=node;
node=(struct dlist*) malloc(sizeof(struct dlist));
}
}
node=end;
start=NULL;
while(node!=NULL)
{
node->next=start;
start=node;
node=node->prev;
}
}
void showl()
{
printf("\n Original List:");
while(start!=NULL)
{
printf("%d",start->number);
start=start->next;
}
}
void showr()
{
printf("\n Reverse List: ");
while(end!=NULL)
{
printf("%d",end->number);
end=end->prev;
}
}
OUTPUT
Enter Numbers(0 to stop): 1 2 4 7 5 0
Original List: 1 2 4 7 5
Reverse List: 5 7 4 2 1
Explanation:
The create() function is used to create the doubly linked list. The method of creation of node
is same as in the previously discussed methods of the linked list. Here, an additional one pointer
field is present and we will discuss that in detail in next section.
We know the use of next pointer and how we have used it previously to access the next node. Every time
a new node is inserted, the address of new node is assigned to the next pointer of previous node. This is
required for details of manipulating the next pointer. The pointer *prev (pointing to data field of previous
node) is not different case. The pointer next is right hand and *prev is the left hand of the node. Every time a
new node is created, the address of data field of previous node is assigned to the previous pointer. The rest of
the operations are same for accessing the elements. Go through the functions showl()and showr().
Head
5 New node
We know that the head node of the doubly linked list contains NULL value. When a new node is to be
inserted at the beginning, the address of the head node is assigned to the new node. The previous pointer
of the node is assigned a NULL value. The arrow ↔ indicates that the node has both previous and next
pointer.
When a node is inserted at the end, the next pointer of the new node is assigned a NULL value and the previ-
ous pointer of the node is assigned the address of last node. Figure 6.33 describes the insertion at the end.
In the deletion operation as shown in the Fig. 6.34 when a node is deleted, the memory allocated to
that node is released and the previous and next nodes of that node are linked.
Head
When a node is to be deleted from the beginning of the node, the head pointer points to the second
node. Because after deletion of first node, the second node becomes the first. The symbol X indicates the
link will be destroyed. This is shown in Fig. 6.35.
Head
Example 6.27 Write a program to perform insertion and deletion operation on the doubly
linked list.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct dlist
{
struct dlist *prev;
int number;
struct dlist *next;
};
struct dlist *end,*start;
struct dlist *node=NULL;
void create(void);
void show(void);
void insert(void);
void re_move(void);
void main()
{
clrscr();
create();
insert();
show();
re_move();
show();
getch();
}
void create()
{
node=(struct dlist*)malloc(sizeof(struct dlist));
printf("\n Enter Numbers(0 to stop):");
while(1)
{
scanf("%d",&node->number);
if(node->number==0) break;
else
{
node->prev=end;
end=node;
node=(struct dlist*) malloc(sizeof(struct dlist));
}
}
node=end;
start=NULL;
while(node!=NULL)
{
node->next=start;
start=node;
node=node->prev;
}
}
void show()
{
struct dlist *x;
printf("\n Origional List:");
x=start;
while(start!=NULL)
{
printf("%d",start->number);
start=start->next;
}
start=x;
}
void insert()
{
struct dlist *q,*r,*p;
int number,k=1;
p=start;
printf("(Insertion)Enter position of node:");
scanf("%d",&number);
if(number==1)
{
q=(struct dlist*)malloc(sizeof(struct dlist));
printf("\n (Insertion) Enter the Number:");
scanf("%d",&q->number);
q->prev=NULL;
q->next=start;
start=q;
}
while(start!=NULL)
{
if(++k==number)
{
q=(struct dlist*)malloc(sizeof(struct dlist));
printf("\n Enter the Number:");
scanf("%d",&q->number);
r=start->next;
r->prev=q;
q->next=r;
q->prev=start;
start->next=q;
break;
}
else
start=start->next;
}
if(number==1)start=q;
else start=p;
}
void re_move()
{
struct dlist *q,*r,*p,*n;
int number,c=0;
printf("\n (Deletion) Enter position of node:");
scanf("%d",&number);
r=start;
if(number==0)
{
p=start;
free(p);
start=start->next;
start->prev=NULL;
}
else
while(r!=NULL)
{
c++;
if(c==(number-1)) p=r;
if(c==number)
{
q=r;
free(q);
}
if(c==(number+1)) n=r;
r=r->next;
}
p->next=n;
}
OUTPUT
Enter Numbers(0 to stop): 1 2 3 4 5 6 7 0
(Insertion)Enter position of node:3
Enter the Number:8
Original List: 1 2 8 3 4 5 6 7
(Deletion) Enter position of node:6
Original List: 1 2 8 3 4 6 7
Explanation:
In this program both insertion and deletion operations are performed. The method of adjusting
pointer is same as explained in the programs of singly and circular linked list operations. The
user can insert or delete the element of any position including beginning and end.
1. The doubly linked list is bi-directional, i.e. it can be traversed in both backward and forward direction.
2. The operations such as insertion, deletion and searching can be done from both ends.
3. Predecessor and successor of any element can be searched quickly.
4. It is very helpful to implement arithmetic operations on large integer numbers.
A circular doubly linked list has both successor and predecessor pointers. Using the circular fashioned
doubly linked list the insertion and deletion operation, which are little complicated in the previous types
of linked list are easily performed. Consider Fig. 6.36:
Head
Head
The × indicates that the previous links are destroyed. The pointer links from ex-first node are removed
and linked to new inserted node at the beginning. The element 8 was previous first node and 3 is the new
node inserted and becomes first node now after inserting it at the beginning.
Head
The × indicates, that the previous links are destroyed. The pointer links from ex-last node are removed
and linked to new inserted node at the end. The address of last node is given to first node to form circular
list. The node 9 was previously the last node but after insertion of the node at the end, newly inserted node
is the last node (Fig. 6.38). Figure 6.39 shows the deletion of the node at beginning.
Head
The × indicates that the previous links are destroyed. After deletion of the first node, the second
node becomes first node. The pointer head also points to the newly appeared first node. Thus, when a
node from the beginning is removed, the node followed by it will become the head node (first node).
Accordingly, pointer adjustment is performed in the real application.
Head
The × as usual is the symbol destroying previous links. When the last node is removed, one node will be
the last node and its links are established with the first node. The removal operation is shown in Fig. 6.40.
The most useful linear data structure is linked list. This section introduces you to a few applications
of linked lists which are useful in computer science.
The coefficient field contains the value of coefficient of the term. Similarly, the exponent field contains
the value of exponent. As usual, the link field points to the term (next node).
The structure for the above node would be as follows:
struct poly
{
double coeff;
int exp;
struct poly *next;
};
Consider a polynomial
P = P 8 + 5P 4 – 7P 2 + 6P
In this equation 1,5,7 and 6 are coefficients and exponents are 8,4,2 and 1. The number of nodes
required would be the same as the number of terms in the polynomial. There are four terms in this poly-
nomial hence it is represented with four nodes.
1 5 −7 6
8 4 2 1
Traverse the list P and Q. Compare the corresponding terms of list P and Q. In case one node has
larger exponent value than the other then insert the larger exponent node in the third list and forward the
pointer to next node of the list whose current term is inserted in the third list. The pointer of the list whose
exponent is smaller will not be forwarded. The pointer of the lists forwarded only when the current nodes
from the lists are inserted into the third list. Table 6.1 shows these operations.
If exponents are equal, add the coefficients and insert their addition in the third list. In this step, expo-
nents from both the expressions are same, move the pointer to next node in both the list P and Q. Repeat
the same process until one list is scanned completely.
expo(P) = expo(Q).
2 9
R= 2 9 & 1 8
R= 2 9 & 1 8 & 7 5
In this step a node from list Q is inserted and therefore, the pointer in the list Q will be forwarded and
point to the term (–3,4) and from list P we have the current node (5,4). Compare exponent of these two
terms. The condition observed here is 4==4. Here, exponents of both the terms are equal. Therefore, addi-
tion of coefficients is taken and result is inserted in the list R. The addition is 2 (5–3). The list R becomes
as the Fig. 6.44 (d).
Move forward the pointers to next nodes in both the lists, since, the previous terms were having same
exponents. The next comparison is 2<3. Here, the exponent of current node of list Q is greater than of P.
The node from Q will be inserted to list R. The list R will be shown as Fig. 6.44 (e).
The list Q is completely scanned and reached to end. The remaining node from the list P will be
inserted to list R. The list R is as Fig. 6.44 (f ).
2 3 & −7 2 & 6 1
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct term
{
float coef;
int expn;
struct term *next;
};
struct term *ployadd(struct term *, struct term *);
struct term *poly_in(st ruct term *);
struct term *insert(struct term *,float,int);
struct term *show(struct term *);
void main()
{
struct term *t1,*t2,*t3;
clrscr();
t1=t2=t3=0;
puts("Polynomial(A):");
t1=poly_in(t1);
puts("Polynomial(B):");
t2=poly_in(t2);
t3=ployadd(t1,t2);
puts("Polynomial(A):");
show(t1);
puts("Polynomial(B):");
show(t2);
puts("Added Polynomial(C):");
show(t3);
}
struct term *poly_in(struct term *begin)
{
int j,ex,n;
float cof;
printf("\n Enter number of terms:");
scanf("%d",&n);
for(j=1;j<=n;j++)
{
printf("Enter coefficient for term %d:",j);
scanf("%f",&cof);
printf("Enter exponent for term %d:",j);
scanf("%d",&ex);
begin=insert(begin,cof,ex);
begin=insert(begin,cof,ex);
}
return begin;
}
struct term *insert(struct term *begin,float co, int e_x)
{
struct term *pt,*temp;
temp=(struct term*)malloc(sizeof(struct term));
temp->coef=co;
temp->expn=e_x;
if(begin==0 || e_x>begin->expn)
{
temp->next=begin;
begin=temp;
}
else
{
pt=begin;
while(pt->next!=0 && pt->next->expn>e_x)
pt=pt->next;
temp->next=pt->next;
pt->next=temp;
if(pt->next==0) temp->next=0;
}
return begin;
}
struct term *ployadd(struct term *g1,struct term *g2)
{
struct term *t3=0,*p3, *temp;
if(g1==0 && g2==0) return t3;
while(g1!=0 && g2!=0)
{
temp=(struct term*)malloc(sizeof(struct term));
if(t3==0)
{
t3=temp;
p3=t3;
}
else
{
p3->next=temp;
p3=p3->next;
}
if(g1->expn>g2->expn)
{
temp->coef=g1->coef;
temp->expn=g1->expn;
g1=g1->next;
}
else if(g2->expn>g1->expn)
{
temp->coef=g2->coef;
temp->expn=g2->expn;
g2=g2->next;
}
else if(g1->expn==g2->expn)
{
temp->coef=g1->coef+g2->coef;
temp->expn=g1->expn;
g1=g1->next;
g2=g2->next;
}
}
while(g1!=0)
{
temp=(struct term*)malloc(sizeof(struct term));
temp->coef=g1->coef;
temp->expn=g1->expn;
if(t3==0)
{
t3=temp;
p3=t3;
}
else
{
p3->next=temp;
p3=p3->next;
}
g1=g1->next;
}
while(g2!=0)
{
temp=(struct term*)malloc(sizeof(struct term));
temp->coef=g2->coef;
temp->expn=g2->expn;
if(t3==0)
{
t3=temp;
p3=t3;
}
else
{
p3->next=temp;
p3=p3->next;
}
g2=g2->next;
}
p3->next=0;
return t3;
}
struct term *show(struct term *pt)
{
if(pt==0)
{
printf("Empty\n");
return 0;
}
while(pt!=0)
{
printf("%.1fx^%d)+",pt->coef,pt->expn);
pt=pt->next;
}
printf("\b\b\bn");
return 0;
}
OUTPUT
***
Polynomial(A):
Enter number of terms: 1
Enter coefficient for term 1: 2
Enter exponent for term 1:3
Polynomial(B):
Enter number of terms: 1
Enter coefficient for term 1: 2
Enter exponent for term 1:3
Polynomial(A):
2.0x^3)+
Polynomial(B):
2.0x^3)+
Added Polynomial(C):
4.0x^3)+
Explanation:
In this program at the beginning three pointers t1,t2, and t3 are declared and initialized to
NULL (0). The value zero means NULL. The pointers t1 and t2 denotes address of polynomial
t1 and t2 respectively. The pointer t3 points to the added polynomial. The pointer t3 always
points to the term added. In case the polynomials are empty, the resulting polynomial will be
empty. That is why we are returning NULL value through the pointer t3. The NULL value of
t3 indicates that the polynomial is empty.
Both polynomials are traversed, i.e. each and every node is visited. The new node is inserted to the third
polynomial and followed by it coefficient and exponent are assigned.
We have written a ladder of if-else conditions.
1. if t1->expo > t2->expo: In this case, the value of current node of t1 is assigned to current node of t3.
The polynomial linked list (A) is traversed and pointer t1 will point to the next node.
2. If t2->expo>t1->expo: In this case, the value of current node of polynomial (B) is assigned to current
node of t3. The node from polynomial is added hence, the linked list (B) is traversed and pointer t2
will point to next node.
3. If (t1->expo==t2->expo): In this case, where terms of both polynomials are same, the coefficient of
both terms are added and the resulting value is assigned to node of third list. Here, both the lists are
traversed and pointer t1 and t2 will point to next node of polynomial (A) and (B).
The same addition of two polynomials can be done from the following example.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct poly
{
float coef;
int expo;
struct poly *nterm;
};
int j,num;
struct poly begin;
void create_poly(struct poly *node)
{
char h;
begin.nterm=NULL;
node=&begin;
j=0;
printf("\n Input n for end:");
h=getchar();
while(h!='n')
{
node->nterm=(struct poly*) malloc(sizeof(struct poly));
node=node->nterm;
printf("\n Enter coefficient value %d:", j+1);
scanf("%g",&node->coef);
printf("\n Enter exponent value %d:",j+1);
scanf("%d",&node->expo);
node->nterm=NULL;
fflush(stdin);
printf("\n Input n for end:");
h=getchar();
j++;
}
printf("\n Total Nodes = %d",j);
}
void show(struct poly *node)
{
node=&begin;
node=node->nterm;
while(node)
{
printf("\n + %g", node->coef);
printf("X^%d", node->expo);
node=node->nterm;
}
}
void main()
{
struct poly *node=(struct poly*)malloc(sizeof(struct poly));
clrscr();
create_poly(node);
show(node);
getch();
}
OUTPUT
Input n for end:
Enter coefficient value 1: 4
Enter exponent value 1: 5
Input n for end:
Enter coefficient value 2: 9
Enter exponent value 2: 8
Input n for end: n
Total Nodes = 2
+ 4X^5 + 9X^8
Explanation:
The logic of this example is the same as the logic used in Example 6.28.
1. The digits of the given numbers are individually traversed from left to right.
2. In parallel, i.e. corresponding digits and a carry from prior digits sums are added.
Example 6.30 Write a program to create a linked list of integers and search for a given element.
# include <stdio.h>
# include <conio.h>
# include <malloc.h>
struct node
{
int data;
struct node *next;
};
int j;
struct node begin;
void search(struct node*);
void create_list(struct node*);
void show(struct node*);
void create_list(struct node *node)
{
int n=1;
begin.next=NULL;
node=&begin;
j=0;
printf("\n Input integers (0 to stop):");
while(n)
{
scanf("%d", &n);
if(n==0) break;
else node->next=(struct node*) malloc(sizeof(struct node));
node->data=n;
node=node->next;
node->next=NULL;
fflush(stdin);
}
}
void search(struct node *node)
{
int node_num=0;
int s_node;
int flag=0;
node=&begin;
printf("\n Enter number to be searched:");
scanf("%d",&s_node);
if(node==NULL)
printf("\n List is empty");
while(node)
{
if(s_node==node->data)
{
printf("\n search is successful");
printf("\n Position of %d from beginning of the list:
%d",s_node,node_num+1);
node=node->next;
flag=1;7
}
else
node=node->next;
node_num++;
}
if(!flag)
{
printf("\n Search is unsuccessful");
printf("\n %d does not found in the list", s_node);
}
}
void show(struct node *node)
{
node=&begin;
while(node->next)
{
printf(" %d",node->data);
node=node->next;
}
}
void main()
{
struct node *node=(struct node*) malloc
(sizeof(struct node));
clrscr();
create_list(node);
printf("\n List: ");
show(node);
search(node);
}
OUTPUT
Input integers(0 to stop):
1
2
3
4
5
9
7
5
0
List: 1 2 3 4 5 9 7 5
Enter number to be searched: 3
search is successful
Position of 3 from beginning of the list:3
Explanation:
In this program a list is created with create()function. The number, which is to be searched,
is entered. In the search()function the entered number is compared with the linked list
elements. When equal number is found the number along with its position in the linked list is
displayed. The show()function is used to display all the linked list elements. Thus, through
traversing and comparing elements search process is done.
Example 6.31 Write a program to create linked list and sort the list in ascending order.
# include <stdio.h>
# include <conio.h>
struct node
{
int value;
struct node *next;
};
void create(void);
void sort(void);
void show(void);
struct node *rear;
int nodes;
struct node *head;
void main()
{
clrscr();
create();
sort();
show();
}
void create()
{
struct node *item;
printf("Enter numbers(0 to exit):");
if(head==NULL)
{
head=(struct node*)malloc(sizeof(struct node));
scanf("%d",&head->value);
head->next=NULL;
rear=head;
}
while(1)
{
item=(struct node*)malloc(sizeof(struct node));
scanf("%d",&item->value);
if(item->value==0) break;
item->next=NULL;
rear->next=item;
rear=item;
}
}
void sort()
{
int temp;
OUTPUT
Enter numbers(0 to exit): 12 1 2 34 5 65 4 0
Sorted list is: 1 2 4 5 12 34 65
Explanation:
In this program the create()function is used for inputting the elements into the linked list.
The sort()is used to sort the linked list elements in ascending order. The struct num is used
to store the linked list. The show()displays the sorted elements of linked list.
SUMMARY
1. Series of linearly arranged numbers is a list. The list can be of basic data type or custom
data type. The first element of the list is called HEAD and the last element TAIL.
2. Static implementation of list can be implemented using arrays. Examples have been illus-
trated on this point.
3. Pointers are used for implementation of linked list. The linked list is a major application of
the dynamic implementation.
4. In the list of elements, for any location n, (n–1) is predecessor and (n+1) is successor. In other
words, for any location n in the list, the left element is predecessor and the right element is
successor.
5. The merging is a procedure in which two or more lists can be combined and third list is
created.
6. A linked list is a dynamic data structure. It is an ideal technique to store data when user
does not know in advance how many elements are to be stored. The dynamic implementa-
tion of list using pointers is also known as linked list.
7. Singly linked list: In this type of linked list two successive nodes of the linked list are linked
with each other in sequential linear manner.
8. Doubly linked list : In this type of linked list the data structure holds two-pointer fields.
9. Circular list : In this list the first and last elements are adjacent. This type of list has neither
end nor starting node.
10. A circular doubly linked list : In this type of linked list the structure field contains three fields.
Two link fields and one data field.
11. Creation: The linked list creation operation involves allocation of structure size memory to
pointer of the same structure.
12. Traversing: It is the procedure of passing through (visiting) all the nodes of the linked list
from starting to end.
13. Display: The operation in which data field of every node is accessed and are displayed on
the screen.
14. Splitting of a linked list: Once a singly linked list is created, it can be divided into many sub-
lists. The technique is very simple. Put the NULL value in the node where you want to
finish the first sub-list in the main list. Later, the next node will act as first node for the
second sub-list.
15. Doubly linked list: Each node of the doubly linked list has two pointer fields and holds
the address of predecessor and successor elements. These pointers enable bi-directional
traversing, i.e. traversing the list in backward and forward direction.
16. Circular doubly linked list: A circular doubly linked list has both successor and predecessor
pointers. In this type insertion and deletion operations are easily performed as compared
to other type of linked lists.
17. Polynomial manipulation: The linked list is used for implementation of polynomial. The operations
such as addition, multiplication etc., are performed. To get better proficiency in processing
each polynomial is stored in decreasing order.
18. Linked dictionary: While compiling a program the linked dictionary has an important role.
In compiler, the organization and maintenance of dictionary holding names and their
corresponding values are maintained in the linked dictionary. This linked directory is
called symbol table. While designing compiler, two factors, i.e. time and memory space are
considered.
EXERCISES
A. Answer the following questions:
1. Write a program to remove first and last node of the linked list.
2. Write a program to duplicate a linked list from other linked list. Remove all odd numbers from
the new list and display both of the linked lists.
3. Write a program to create the linked list and remove all the duplicate elements of the list. The
list should have unique elements.
4. Write a program to find successor of the given element in a linked list.
5. Write a program to create and display the elements of a circular linked list.
6. Write a program to terminate the program when malloc () returns to NULL.
7. Write a program to find the number of even and odd elements in a linked list.
8. Write a program to find the average value of elements of a linked list.
9. Write a menu driven program to perform the following operations on linked list:
1. Create
2. Insert
3. Delete
4. Display
5. Exit.
10. Write a program to multiply two polynomials.
11. Write a program to create and display circular linked list containing five elements.
12. Write a program to find the minimum and maximum value from the linked list.
Storage Management
CHAP TER O U T LIN E
7.1 Introduction 7.6 Storage Release
7.2 Allocation Techniques 7.7 Buddy System
7.3 Memory Representation 7.8 Binary Buddy System
7.4 Boundary Tag System 7.9 Compaction
7.5 Storage Allocations 7.10 Garbage Collection
7.1 INTRODUCTION
The fundamental purpose of any program is to manipulate data and its storage in the computer memory.
Storage management consists of techniques that are used to manage the heap. Two memory management
techniques are used for this purpose. They are:
1. Static storage management
2. Dynamic storage management.
Static Storage Management It is necessary to load the program into the memory before execution of
a program. Before execution of a program, it is essential that the system should have enough memory.
When program execution starts, it takes memory through operating system. Once the memory is allocated
to the program, the memory allocated cannot be increased or decreased during program execution. The
same memory cannot be used by other programs. All these are taken care of by the operating system. For
example, an array is best example of static implementation in which the memory allocated is fixed in size.
Dynamic Storage Management The dynamic storage management allows the user to manage the
memory during program execution. According to the request made by the program, memory can be
allocated. In this technique, wastage of memory can be avoided. This is efficient for multiprogramming and
single user environment, in which more than one program is executed simultaneously. The exact amount of
memory needed for the programs can be estimated only during program execution. The operating system
performs dynamic storage management. The linked list is an example of dynamic storage management.
In linked list, we have noticed during program execution that when new node is created memory is
allocated. Thus, depending upon the need of the user, any number of nodes can be created. Following are
the techniques of dynamic memory management.
De-allocation Techniques The different ways of de-allocation techniques are mentioned below:
1. Ordered de-allocation
2. Random de-allocation.
The free blocks of memory storage are nothing but a collection of non-contiguous memory blocks. They
can be accessed in sequence by pointer from one block to another. The size of block depends on the
method applied as given below.
There are two functions getnode() and freenode(). Using these functions, the user program
communicates with operating system. The getnode() is used to obtain memory from the heap for stor-
age of data. The argument passed denotes amount of memory required. When the user program makes
a call to this function, a pointer to block in heap is returned after successful allocation otherwise returns
NULL. The NULL value indicates that there is not enough memory available. The returnnode() is
opposite of the getnode() function in which when a node is no longer needed its allocated memory is
released and returned to pool as shown in Fig. 7.2.
Though the above method is straightforward, it consumes more memory. For example, the size of
each block is 1 KB (1024 bytes) and the program requests the memory of 1.2 KB, in such a situation
the memory allocated is 2 KB, hence, the extra memory allocated is of no use. If the size of the block is
decreased, it reduces the program performance.
In static storage management, memory blocks of fixed sizes are allocated for storing the programs
whereas in many applications there is a need to have memory of different sizes. In getnode() and
returnnode() function, we discussed that memory blocks of different sizes are present and blocks
are connected with each other. However, in reality when there is no program in the memory, the whole
memory is considered as a block. In this situation, the blocks of required sizes are automatically made
through use of system by calling the functions many times. The blocks returned in all executions are
together shaped as a block as per the Fig. 7.4 (a to d). Suppose, the pointer returns M1, M2, M3 and M4.
In the above Fig. 7.4 (a), no program is loaded and hence total block of memory is available.
M1 M2 M3 M4 Unused memory
As per the above Fig. 7.4 (b), four programs M1 to M4 are loaded and memory is allocated to them in
sequence of their order. The Fig. 7.4 (b) also indicates the unused memory, which is available for use.
The execution of programs M3 is completed and when it quits from the memory the space is returned
to main storage. Here, blocks of variable sizes are generated.
Another program (M5) requests for memory and the memory needed is allocated to it. Thus, the
dynamic memory management performs the following tasks:
1. It searches requested blocks of memory and allocates memory.
2. Supervises the released blocks of memory.
3. Combines the tiny blocks of memory to bigger blocks.
In order to provide the above services two memory management systems are used. They are (i) bound-
ary tag system and (ii) buddy system.
The boundary tag system is a resourceful memory system for dynamic memory management system. It
works on a node structure as per the Fig. 7.5. In the node structure for a specified block, first and last words
are set aside for block information. The LL and RL are pointers to predecessor and successor blocks from
LL TAG S RL
FREE SPACE
TAG UPLINK
the current block. The argument S contains amount of free memory available. The TAG is flag field and
stores values 0 (free) or 1 (engaged). There are two flag fields on the two boundaries of the block. Hence,
the system is called boundary tag system. The filed UPLINK points to beginning of the same block. The
uses of all the above fields are explained at appropriate situations. These systems sustain a circular linked
list of free memory blocks. Using circular doubly linked implementation a block can be easily released.
The header node is also used in the boundary tag system and holds the value zero in field S and TAG.
As discussed in the list structure, the request for memory block to system can be made in the following ways:
1. First-fit storage allocation
2. Best-fit storage allocation
3. Worst-fit storage allocation
4. Next-fit storage allocation.
The de-allocation of the memory is a very essential process and utmost care must be taken by the programmer
to shun the wastage of memory. In static implementation of data structure the wastage of memory is more
than that in the dynamic implementation. This topic is devoted for discussion of de-allocation of allocated
memory, which is returned to free store. It is one type of re-cycling. The system sources allocated must be
returned to the system if the program is not in use. This way the performance of the system is maintained.
We have observed in the boundary tag system that it inserts the block to the list of free blocks and merge
recently inserted blocks with its neighbouring blocks.
The buddy system is the alternative storage management system. This system limits the sizes of the block
to some fixed set of sizes. Till now we have learnt that the block sizes are fixed or totally arbitrarily. The
linked list keeps the size of blocks of buddy system to a restricted size. When a request is made for size S
then the size X, which is the minimum of the fixed sizes but equivalent to or larger than S, is searched and
block of size X is allocated.
In case if block of size S is not available, then a larger block is split into two sub-buddies (block). The
blocks splitted are also of equal and fixed sizes. This procedure is repetitively done until a block of size X
is formed. The restricted block sizes in buddy system are as F0, F1 to FM. According to the recurrence, the
requirement of block sizes can be expressed by the following equation:
Fn = Fn-1 + Fn-g, g ≤ n ≤ M (7.1)
M is maximum size.
For given values of g and M
F0=V0,F1=V1,..., Fg-1=Vg-1
If values of g = 1 and F0 = 8 the block sizes are 8,16,32,64,128. These values can be obtained from
Eq. 7.1. We can also observe that the block sizes are successive powers of two. The buddy system based on
the above condition is called binary buddy system.
One more buddy system, which restricts the fixed sizes to the Fibonacci series sequence, is known
as Fibonacci buddy system. The initial value is g = 2 and F0 = 8, F1 = 13. The sizes of the blocks are
8,13,21,34,55,89…. The values of F0, F1 and F2 are depending according to the application.
When a request is made to get memory space to the system, the system searches for a block, which is
closely equal to but not less than the requested memory block. When the above-described block is found
it is immediately allocated, else next larger block in the list will be divided until the block of nearest size
is found. On the contrary, when a block of memory is returned to the system (de-allocated), the returned
block is pooled with another free block into larger free blocks.
In order to illustrate the above allocation and de-allocation methods, consider the fibonacci buddy
system in which the restricted block sizes are 8,13,21,34,55,89 and 144. The 144 is the biggest possible
block from the memory available. Figure 7.6 illustrates the allocation and de-allocation.
From Fig. 7.6 the following points are observed. In the beginning, the system keeps a free block of size
144. When a demand for memory comes for size 25, the system divides and creates two buddies of sizes
89 and 55, respectively. The second buddy, i.e. 55 is divided into two buddies as 21 and 34. The block of
size 34 is closely nearest further and hence it is used for allocation as per Fig. 7.6.
For a succeeding request for a block of size say 40, the system searches for a matching block and sets the
block of size 89 as obtainable but it is too huge for the made request. That is why the partition is done as
55 and 34, the block size 55 is allocated, and the request is fulfilled. Figure 7.7 illustrates this point.
144
89 34 21
144
89 55
21 34
144
89 34 21
55 34
144
55 89
55 34
34
21
144
89 34 21
144
89 55
34 21
55
34
Consider the situation if block of size 55 is de-allocated and returned to the system as shown in Fig. 7.8. The
two buddies namely 55 and 34 are free buddies. These two free buddies come together and block of size 89 is
formed. We know that the block 89 is a free buddy and the combination comes to the end of the procedure.
While deallocation of a block of size 34 as shown in Fig 7.9, the first blocks of size 34 and 21 are com-
bined as they are free buddies of size 55.
So far we have seen theoretical representation. The use of structure of a node to implement above
operations can be defined as follows:
There are two fields and they are right and left link. These two link fields store the address of the
predecessor and successor of the block. The size is stored in the size field. In the field, the absolute size is
not stored and only index value according to the recurrence relation is stored. Suppose, Fibonacci system
is applied then the equation would be,
144
89 55
34 21
After combining these two buddies the ensuing block CODE field is one less as compared to its left
buddy as follows:
CODEP=CODEL-1
By the following method, various partitions from biggest block to the smallest block can be created.
Depending upon the value of code field we can determine whether the block is left block or right block.
When the value of CODE=0 then it is right block and if the value is greater than 0 then it is a left block
(buddy). The code value denotes the number of splits of some larger block so far made by the buddy.
Consider two cases of allocation,
1. Best-fit strategy
2. Fibonacci buddy system.
According to Fibonacci buddy system, Fn = Fn−1 + Fn−2
Also,
2 ≤ n ≤ M = 6 When F0 = 8 and F1 = 13
HEADER
0 5 1 3 1 0 2 0
1 89 123 144
At the starting the block of block of size 34 is allocated as shown in the Fig. 7.10 (a).
Assume that the successive request is made for a block size 50. The system will search for the required
closer block from header. When the block of size 89 is found which is larger than the requested one,
partition is carried out. After partition, we get two buddies of 55 and 34 size. The block of size 55 is allo-
cated as per the Fig. 7.10 (b).
1 4 2 0 3 0 1 3 1 0 2 0
1 55 89 123 144
HEADER
0 5 1 1 3 1 0 2 0
1 89 123 144
When a block of size 34 is returned as shown in the Fig. 7.10 (c) the code of free block is one and hence
it is a left buddy of any larger buddy. Its right corresponding block is free; they can be combined as shown
in Fig. 7.10 (c). The size of newly formed block will be 55 and it is a free buddy. We can notice that it is a
left buddy. One more combination can be done if we find its right corresponding block. Figures 7.10 (d)
and 7.10 (e) show this block. This is the largest block and no further combinations are possible. Thus,
de-allocation is complete.
0 5 1 0 4 0
1 89 144
0 6 0
1 144
After studying the buddy system now, it is time to turn to binary buddy system (BBS). The BBS is based
on the recurrence relation given as follows:
Fi= Fi-1 + Fi-1 (7.3)
1 ≤ I ≤ MAX
In the binary system, the block sizes are power of base 2. In this system the block of memory is accu-
rately partitioned into two sections of fifty-percent each. This system also has one advantage that the base
address of other buddy system can be easily computed, if the size along with base address of at least one
buddy is available for the block of size 2k. In this way when a block of memory is released, its amalgama-
tion with its other matching part is straightforward.
7.9 COMPACTION
The part of free memory is mixed together with the dynamically used partitions throughout the memory
and this is one of the critical problem in dynamic storage allocation. For example, consider the system
whose memory picture is as per the Fig. 7.11. When all requirements for storage are of fixed size, then it
is sufficient to link all unused available blocks together into an existing space list and the request received
can be fulfilled. On the other hand, when storage requests received are of different sizes, it might be too
expensive. For example, assume that a request for a block of size 256 KB is accepted even if total free
0 KB
M0 Available
100 KB Memory
M1
200 KB
M2 From 360 KB
340 KB
160 KB 160 KB
500 KB
M3
700 KB
700 KB
100 KB 100 KB
800 KB
M4
900 KB
900 KB
100 KB 100 KB
1000 KB
space available is 360 KB. The space available is more than the storage amount needed. However, the
needed storage space cannot be allocated. This is because of external fragmentation. It is usually detected
in dynamic memory management systems with different size blocks.
We have already learnt in previous sections that combining of contiguous free portions when free
blocks are returned is a technique frequently followed to decrease fragmentation and thus the amount
of unused wasted memory is prohibited. Nevertheless, the above techniques are inclined to put off the
impact moderately than to avert the problem. Comprehensive study provides the fact that after some
time of operation, the system is likely to attain a status of constancy and maintain a rule which is called
fifty per cent rule. This rule points out that the number of used and free blocks are same, i.e. 50% each.
In addition, the study divulges that more or less one-third of memory is not used and wasted because
of fragmentation. This is the case even if contiguous free areas are re-combined every time a block is
returned. Furthermore, this type of implementation slows down execution speed of program as the system
has a task of releasing and coalescing block at the same time during program execution.
When memory is fragmented, it can be re-allocated a few or entire portion into one last part of memory
and consequently coalesce the small holes into one big free part. This practice of recovering memory
storage is known de-fragmentation or compaction. Compaction mechanism is shifting of used blocks
from one place in memory to other location. To accomplish this all the active processes are required to be
suspended and in fact copied from one part of memory into a different part.
The process of memory compaction is possibly carried out whenever required. In some memory man-
agement methods, compact process is performed when a free portion of memory is available. In this way,
gathering maximum of the free memory into one large portion takes place. A substitute way is to compact
only when the request received cannot be fulfilled and system fails to allocate the memory. If the com-
bined sizes of free portions of memory go above the requested amount of memory available, or else, free
memory cannot satisfy the pending need in any case then compaction may not be useful.
There are two approaches:
1. Incremental compaction
2. Selective compaction.
Figure 7.12 shows used and unused (free) memory for mapping with the above two techniques.
0 KB
M0
Free
100 KB Memory
M1
200 KB
M2
340 KB Used
160 KB Memory
500 KB
M3
700 KB
100 KB
800 KB
M4
900 KB
100 KB
1000 KB
Incremental Compaction In this type every free block is moved into one end of the memory to compose a big
hole. This procedure is very straightforward to apply but costly. The Fig. 7.13 shows incremental compaction.
0 KB
M0
100 KB
M1
200 KB
M2
340 KB
M3
Used
540 KB Memory
M4
640 KB
Unused
360 KB
Memory
1000 KB
Selective Compaction Selective compaction seeks for least number of free blocks, movement of which
produces a larger free hole. The obtained hole may be at any place in the memory and need not be at the
end of the memory. Figure 7.14 shows a memory picture of selective compaction.
0 KB
M0
100 KB
M1
200 KB
M2
340 KB
360 KB
700 KB
M3
900 KB
M4
1000 KB
Through selective compaction, only by moving block M3 of size 200 KB from location 500 KB to the
location 700 KB; continuing this way we can create the largest possible free holes of size 360 KB at the
location 340 KB.
Therefore, time for shifting data is minimum. However, with the incremental method we have to move
first the memory portion of the block M3 from 500 KB location to 340 KB location, then the memory
content of the block M4 at 540 KB location. Therefore, here, total movement is 200 KB + 100 KB =
300 KB instead of 200 KB in selective method. Selective compaction approach is seldom applied because of
the overhead in evaluating the option while selecting the most advantageous moving method. Time for select-
ing such block may be more than the time required to shift the block one-by-one at the end of the memory.
Consequently, a more general approach to compaction is to reallocate all free blocks to one end of memory.
Linked Allocation We know that a linked list is nothing but a sequence of nodes linked with the pointer.
Every node has two parts, i.e. data field and link field. The data is stored in the data filed and link field
stores address of next field.
Fixed Sized Partition Technique In this type memory space is partitioned into fixed sections and each
block holds given memory. The size of the block is predetermined and cannot be modified at run time.
If the program size is less than the block of memory, the memory more than the requirement remains
unused. If the memory of the block is less than the program size, the program will not be executed.
Bit Tables/Paging Technique When all the nodes of the data structure are of identical size, this method
is applicable. In this technique, every program is split into fixed size pages. Later, each page is sub-divided
into fixed blocks of nodes known page frames. For example, memory size is 20 KB and every page frames
needs 200 KB. The memory space divided as following table.
In the above table one shows free space and initially for page frame 0 to 9 the page frame status is zero.
For example, five programs are to be executed and memory needed is as follows:
Program –1 60 KB
Program –2 80 KB
Program –3 80 KB
Program –4 40 KB
Program –5 40 KB
Program No. Page No. Part of Page No. Bit Status (a) Bit Status (b)
1 0 0 0 1
1 1 1 0 1
2 0 2 0 0
2 1 3 0 0
2 2 4 0 0
3 0 5 0 1
3 1 6 0 1
3 2 7 0 1
4 0 8 0 0
5 0 9 0 0
In the above table, the bit status 1 specifies that all the nodes of the memory be already allocated
to 1 to 5 programs. Assume, program 3 and 1 complete their execution and the memory engaged due
to these programs is released. The status bit 2 points the altered bit status. The paging technique has
some drawbacks and undergoes trouble for searching the best possible page frame size. In case the size of
the program is larger and memory space allocated is more then memory is wasted. Similarly, if the page
frame is small, the program has to undergo several partitions and it will be very tedious to match the frame
with the required size. In paging technique, the entire program must be memory resident. In this situation,
if program size is larger than available memory, the program will not be executed.
Demand Paging Method To overcome the limitations of the paging technique, demand paging method
is followed. In this method, a large program is divided into small sizes of program segments and first page is
then loaded in the memory. If few functions or statements of first pages, which are currently executed in
the memory having relation with statements which are in next page, then the required page is loaded from
disk to memory. In this procedure, the operating system can take part and transfer the required page to
the disk and vice-versa. This is known as thrashing.
Garbage collection means the de-allocation of unused memory. Deciding when to release the allocated
memory is very simple. The memory de-allocation is done when the programmer requests by executing a
specific function. Languages like Java have in-built garbage collection system. The function free() in C
and delete in C++ are used to release the memory. The garbage collection system checks the entire memory,
marks the unused nodes and releases the memory associated with them.
The garbage collection consists of two steps:
Marking In this process, all the accessible nodes are marked and one field is kept for marking. The field
is marked with true or false.
Collection In this process, memory allocated of all the nodes, which are not marked, are released.
The garbage collection is a complex procedure and has the following disadvantages:
1. The garbage collection technique is applied when there is not much space.
2. The garbage collection may not be helpful if the pointers associated with the list are not properly
pointed.
However, the memory de-allocation is not as easy as it appears. The memory allocated during compile
time can be freed easily. The memory allocated dynamically at run time can be de-allocated but some care
must be taken. The dynamic structure such as linked list contains nodes, which are dynamically created
and contains reference of next nodes.
SUMMARY
1. In this chapter, the reader is exposed to the allocation and release of memory. Different
techniques of allocation of memory are discussed. Memory is one of the precious
resources and it is essential to manage it efficiently. Two methods are used for management
of memory.
2. Static storage management When program execution starts, it takes memory through
operating system. Once a memory is allocated to the program, the memory allocated cannot
be increased or decreased during program execution.
3. Dynamic storage management The dynamic storage management allows the user to
manage the memory during program execution. According to the request made by the pro-
gram, memory can be allocated. Under dynamic memory management various techniques
have been discussed in this chapter. The best-fit, first-fit and worst-fit memory management
techniques have been briefed.
EXERCISES
A. Answer the following questions:
Applications of Stacks
8.2 Infix, Prefix and Postfix Notations 8.7 Conversion of Number System
8.1 INTRODUCTION
Stacks have various real-life applications. Stacks are used to maintain the sequence of processing. In solving
arithmetic expressions, stacks are used. Stacks are used to convert bases of numbers. In a large program,
various functions are invoked by the main() function; all these functions are stacked in the memory.
Most of the calculators work on stack mechanism.
Arithmetic expressions can be defined in three kinds of notation: infix, prefix and postfix. The prefixes in,
pre and post indicate the relative location of the operator with two operands.
Infix Notation Expressions are generally expressed in infix notation with binary operator between the
operands. The binary operator means the operator requires two operands such as +, *, / and %. In this
type, the operator precedes the operands. Following are examples of infix notation:
1. x+y
2. x+y*z
3. (x+y)*z
4. (x+y)*(p+q)
Every single letter (A-Z) and an unsigned integer is a legal infix expression. If X and Y are two valid
expressions then (X+Y ), then (X–Y ) and (X/Y ) are legal infix expressions.
Prefix Notation The prefix notation is also called polish notation. The polish mathematician Lukasiewicx
invented it. In this type also the operator precedes the operands. Following are the examples of prefix notation:
1. +xy
2. +x*yz
3. *+xyz
4. *+xy+pq
Postfix Notation The postfix notation is also called as reverse polish notation. The operator trails the
operand. Following are the examples of postfix notation:
1. xy+
2. xyz*+
3. xy+z*
4. xy+pq*+
Consider the following example:
X+Y*Z
In the above expression, we know that the multiplication has higher precedence than addition. The
expression X +(Y *Z ) is same as the expression X +Y *Z. By applying rules of priority, we can convert the
above expression to postfix notation.
X+(Y*Z)
X+Y XY+
In the above example, if parenthesis is not inserted the multiplication operation gets first priority. Due
to parenthesis the addition operation gets first priority.
(XY+)*Z
X+Y XY+
X+Y–Z XY+Z–
(X+Y)*(P–Q) XY+PQ–*
L^M*N–O+P/Q/(R+S) LM^N*O–PQ/RS+/+
((L+M)*N–(O–P)^(Q+R) LM+N*OP––QR+^
L-M/(N*O^P) LMNOP^*/–
For converting an expression from infix to prefix the rules are same as like conversion from infix to
postfix. In the prefix conversion the operator is placed before the operand. Table 8.3 converts the infix
expression to the prefix.
Table 8.3 Infix to Prefix
Infix Prefix
X+Y +XY
X+Y–Z –+XYZ
(X+Y)*(P–Q) *+XY–PQ
L^M*N–O+P/Q/(R+S) +–*^LMNO//PQ+RS
((L+M)*N–(O–P)^(Q+R) ^–*+LMN–OP+QR
L–M/(N*O^P) –L/M*N^OP
The postfix form doesn’t require parenthesis. Consider the following expressions:
X+(Y*Z) and (X+Y)*Z
Table 8.4 Postfix
Infix Postfix
X+(Y*Z) XYZ*+
(X+Y)*Z XY+Z*
You can see from Table 8.4 that postfix expressions do not have parenthesis. In the postfix expressions,
sequence or precedence of operators decides sequence of operation. While converting infix expression to
postfix, it is difficult to know the operation associated with operand.
To write a valid expression whole parenthesization should be applied. However, the parenthesis must
not be large. The expression (X +Y )*Z is valid expression and need not be written like ((X +Y )*Z ). Here,
the outer parenthesis can be omitted and has no purpose. Consider the following expression. The above
expression can be written as (P+(Q*R))+(S *T ). To evaluate the expression it must be scanned from left to
right. Fig. 8.1 illustrates the expression.
If parentheses are present in the expression, then the precedence will be changed. For example, in
expression (P +Q )*R, first P +Q is solved and then multiplication takes place. Actually, it is possible to
declare an expression that makes the sequence of evaluation of expression independent of the priority of
the operators. This can be done by putting sub-expressions in the parentheses. The operators and oper-
ands are enclosed in the parentheses. Thus, we have several parenthetical levels as shown in the Fig. 8.2.
Such expressions are called fully parenthesized expressions. Consider the following example,
P + Q * R + S * T
(P + ((Q * R) + (S + T )))
1 2
1 3 2 3
3 4
Figure 8.1 Expression and Operation Precedence Rank Figure 8.2 Fully Parenthesized Expressions
The numbers indicate parenthetical level of operators. When such an expression is solved, the
sub-expression having the operator with top parenthetical level is solved first. When two parentheses have
equal parenthetical levels, the left-sided parentheses are evaluated first. Followed by parenthesis 2 and 1.
In case the expression is partially parenthesized, a repetitive scanning from left to right is essential to
evaluate the expressions. This is because the operators present in conjunction with the operands within
the expression. The scanning is shunned in case the infix expression is converted to postfix or prefix
expression.
The postfix expressions are very simple. On the other hand, programming of evaluation of expression is
complicated. This is because the numbers and characters are mixed in one string. The programmer needs
to separate digits and symbols and perform arithmetic operation.
Each operator in the postfix expression is associated with the previous two operands. When we read an
operand from a given expression it is pushed onto the stack. When an operator symbol is read, the oper-
ands associated with it are the top elements of the stack. We can pop these two elements from the stack
and perform the operation. The obtained result from the last operation is again pushed onto the stack.
Table 8.5 describes the algorithm for conversion of the infix expression to postfix.
# include <stdio.h>
# include <conio.h>
# include <ctype.h>
# include <math.h>
float stack[10];
int top=-1;
void push(char);
float pop();
float exp_eval(char[],float[]);
void main()
{
int j=0;
char s_fix[20];
float number[20],res;
clrscr();
printf("\n Enter a postfix expression: ");
gets(s_fix);
while(s_fix[j]!='\0')
{
if(isalpha(s_fix[j]))
{
fflush(stdin);
printf("\n Enter number for %c: ",s_fix[j]);
scanf("%f",&number[j]);
}
j++;
}
res=exp_eval(s_fix, number);
printf("The res of %s =%f", s_fix,res);
getch();
}
while(s_fix[j]!='\0')
{
ch=s_fix[j];
if (isalpha(s_fix[j]))
{
push(data[j]);
}
else
{
opB=pop();
opA=pop();
switch(ch)
{
case '+': push(opA+opB); break;
case '-': push(opA-opB); break;
case '*': push(opA*opB); break;
case '/': push(opA/opB); break;
void push(char c)
{
top=++top;
stack[top]=c;
}
float pop()
{
float n;
n=stack[top];
top=––top;
return(n);
}
OUTPUT
Enter a postfix expression: jk*
Enter number for j: 4
Enter number for k: 5
The res of jk* =20.000000
Explanation:
In this program before main () function, the float type array stack [10] and integer variable
top are defined. They are initialized with –1. The prototype of functions push (), pop () and
exp_val () are declared. User has to enter expression and the gets () function activates the
input stream and the entered expression is stored in the s_fix[] character array.
The expression is checked and for every alphabet user has to enter a number. Immediately after this, the
function exp_eval() is invoked with two arguments, i.e. s_fix and number. Both the arguments are
array name and holds base address. This function returns result and is stored in variable res.
In the exp_eval() function the variable ch holds the arithmetic operation. The switch()
case structure determines the operation to be carried out. The pop() function is invoked and the two
popped values are stored in variables popB and popA. These variables are passed to push() function in
switch() case structure.
In any expression, there are two types of components clubbed together. They are operand and operators.
The operator indicates the operation to be carried out and the operand indicates the values. The operands
are the variables on which the operator operates. Operators have their priority of execution. When one
or more operations are clubbed together in a single expression the priority decides which operation is to
be performed first. Suppose, an expression contain two operations with high and low priority. We can
force the low priority operation to evaluate first by putting that particular operation in parenthesis. When
nested parenthesis are present in the expression the inner-most parenthesis is solved first. In any expression
the parenthesis are solved first. The operations are generally solved from left to right. Table 8.6 shows the
operator precedence.
Exponent High
Multiplication (*), Division (/) Next
Addition (+), Subtraction (–) Low
The operators are -,/,^,+,* and x,y,p,q,u and v are variables. The parenthesis are used to provide higher
priority to the operation (p^q) and (u*v).
Consider the example
Expression: X +Y *Z
X X
+ X +
Y XY +
* XY +*
Z XYZ +*
XYZ* +
XYZ*+
The expression is scanned and when operators are found, they are pushed on the stack. The operators
are pushed according to priority of execution of the stack. When operator + is found it is pushed onto the
stack. The precedence of * is higher than +. The symbol * is pushed onto the stack. At last, when string
reaches to end the stack is popped and its content is appended to the postfix string. These operations are
illustrated in Table 8.7.
Expression: (P +Q )*C
In the above example, opening parenthesis is found. Until the left parenthesis is not found the entire
stack is popped. Here, the parentheses provide first priority to addition. Table 8.8 explains the conversion.
Table 8.9 describes the algorithm for infix to postfix.
( (
P P (
+ P (+
Q PQ (+
) PQ+
* PQ+ *
R PQ+R *
PQ+R*
# include <stdio.h>
# include <conio.h>
# include <string.h>
# include <process.h>
char stack[20];
int T=-1;
void main()
{
char in_fix[25];
clrscr();
puts(" Enter an infix expressions: ");
gets(in_fix);
puts("\n Postfix Expression: ");
topost(in_fix);
}
void push(char s)
{
if(T>=19)
{
puts("Stack overflow");
exit(0);
}
else
{
T=T+1;
stack[T]=s;
}
}
char pop()
{
char num;
if(T==-1)
{
puts("Stack is Empty");
getch();
return(0);
}
else
{
num=stack[T];
T––;
}
return(num);
}
int len;
static int index=0,pos=0;
char s,t;
char postfix[40];
len=strlen(in_fix);
push('#');
while(index<len)
{
s=in_fix[index];
switch(s)
{
case '(': push(s); break;
case ')': t=pop();
while(t!='(')
{
postfix[pos]=t;
pos++;
t=pop();
}
break;
case '+':
case '-':
case '*':
case '/':
case '^':
while(prefix(stack[T])>prefix(s))
{
t=pop();
postfix[pos]=t;
pos++;
}
push(s); break;
default: postfix[pos++]=s; break;
}
index++;
}
while(T>0)
{
t=pop();
postfix[pos++]=t;
}
postfix[pos++]=’\0’;
puts(postfix);
return;
}
OUTPUT
Enter an infix expressions: m+s+p
Postfix Expression: msp++
Explanation:
In this program an infix expression is converted to postfix form. The prototype of function
topost (), push () and pop () are declared. The array stack [20] is declared. In addition,
the integer variable T is declared and initialized with –1. In function main (), a character array
in_fix[] is declared. The infix expression is entered by the user. The function topost () is
invoked and in_fix is sent as an argument.
In the function topost(), length of in_fix array is counted. Using push() function ‘# ’ is pushed.
The integer variable index is initialized to zero. The while loop executes until the value of index is less than
len. The variable s contains the value of in_fix[index] character. If the value of s is ‘(’, it is pushed and if
it contains.‘ )’. and pop() function is executed. The variable s is passed to switch() case statement.
Accordingly, case statements are executed.
Few applications of stack are narrated together with examples.
We know the stack is based on last-in-first-out rule. It can be achieved simply by pushing each character
of the string onto the stack. The same can be popped in reverse fashion. Thus, reverse string can be done.
Consider the following program.
# include <stdio.h>
# include <conio.h>
char text[40];
void main()
{
char ch;
void push(char,int);
char pop(int);
int j=39,k;
clrscr();
puts("\n Enter a string(* to end): ");
{
ch=getche();
push(ch,j);
j––;
}
k=j;
j=0;
while(k!=40)
{
ch=pop(k);
printf("%c",ch);
k++;
}
}
void push(char c, int j)
{
if(c!=’*’)
text[j]=c;
}
char pop(int j)
{
char c;
c=text[j];
text[j]=text[j+1];
return c;
}
OUTPUT
Enter a string(* to end):
HELLO*
Reverse string is: OLLEH
Explanation: PUSH
In this program the array text[40] is STACK
declared as global. So, other two functions
push() and pop() have access to it.
The character entered is pushed on O L L E H
the stack. When user enters '*' the
character reading loop is interrupted
and immediately pop operation is carried
POP
out. During pop operation the pop()
function returns one character its per
execution. The string is displayed as
reverse because the pop operation is
reverse of push. Figure 8.3 represents
the process. Figure 8.3 Reverse String
All the programs compiled using high-level language compilers uses stack frame for function invocation.
Operational memory uses stack frames to store the variables declared in the program. During the func-
tion, execution number of data is pushed onto the stack. When the function execution terminates, the
stack content is popped. When a function is invoked, local variables, formal arguments and return address
are pushed onto the stack. Thus, every function executes in a particular or a separate circumstance and
hence recursion of function is also possible. Consider the following program code:
fun(int p, int q)
{
int h;
if(condition) return(value);
h=10;
return joy(int h);
}
joy(int l)
{
int m,u;
m=1, u=2;
return fun(m,u);
}
The above is an example of indirect function in which two functions fun () and joy () invokes
each other. For every invocation of function new stack frame is created and as mentioned earlier new copy
local variables, return address and formal arguments are pushed. Conceptually, the stack is a piece of main
memory, which is used by the program to temporarily store data. When a function is invoked, variables are
pushed onto the stack and when function execution ends, the stack is popped. When a function invokes
another function (other than itself ) the parameters of caller function are pushed onto the stack with
address subsequently called instruction. This is because after execution of called function, the compiler
can keep track of previous path and it can determine where to return after execution.
Suppose, one wants to calculate binary number of a given decimal number, the given number is repeatedly
divided by 2 until 0 is obtained. The binary number can be displayed in reverse order using stack rule
last-in-first-out (LIFO). Consider the following program:
Example 8.4 Write aprogram to convert a given decimal number to binary. Explain the role
of stack mechanism.
# include <stdio.h>
# include <conio.h>
int num[7];
void main()
{
int n,k,T=7;
void show();
void push(int,int);
clrscr();
printf("\n Enter a number: ");
scanf("%d",&n);
printf("\n The binary number is: ");
while(n)
{
k=n%2;
push(––T,k);
n=n/2;
}
show();
}
OUTPUT
Enter a number: 9
The binary number is: 0 0 0 1 0 0 1
Explanation:
In this program the binary equivalent is obtained by repeatedly dividing by two. Here, the first
binary digit obtained is pushed on the stack. This process is continued. The show () function
displays the binary digits stored in the stack, i.e. an array.
8.8 RECURSION
The recursion is the fundamental concept of the mathematical logic. Recursion is one of the important
facilities provided in many languages. C also supports recursion. There are many problems, which can
be solved recursively. The loop which performs repeated actions on a set of instructions can be classified
as either iterative or recursive. The recursive loop is different from the iterative loop. In the recursion,
the procedure can call itself directly or indirectly. In the directly called recursion the procedure calls itself
repeatedly. On the other hand, if the procedure calls another procedure then it is an indirect recursion.
In recursion, some statements, which are specified in the function, are executed repeatedly. Every time a
new value is passed to the recursive function till the condition is satisfied. A simple programming example
is given below.
Example 8.5 Write a program and find the greatest common divisor of the given two numbers.
# include <stdio.h>
# include <conio.h>
int stack[40],top=-1;
void main()
{
void gcd(int,int );
int n1,n2;
clrscr();
printf("\nEnter number:- ");
scanf("%d",&n1);
printf("\nEnter number:- ");
scanf("%d",&n2);
gcd(n1,n2);
printf("\nThe gcd of %d & %d is:- %d",n1,n2,stack[top]);
}
OUTPUT:
Enter number:- 5
Enter number:- 25
Top value is:- 20
Top value is:- 15
Top value is:- 10
Top value is:- 5
The gcd of 5 & 25 is:- 5
Explanation:
In this program the stack[] and the top are the global variables. By using the n1 and n2
variables the two numbers are input by the user. The gcd() function is the recursive function
which is recursively called till the greatest common divisor of the two given numbers is obtained.
The program stores the difference of the two numbers, which are passed by the function into
stack []. The value present at top is the greatest common divisor of the two numbers.
Example 8.6 Write a program to convert decimal to binary by using the concept of recursion.
# include <stdio.h>
# include <conio.h>
int stack[40],top=-1;
void main()
{
void binary(int);
int no;
clrscr();
printf("\nEnter number:- ");
scanf("%d",&no);
binary(no);
while(top>=0)
{
printf(" %d ",stack[top]);
top––;
}
}
void binary(int b)
{
if(b>0)
{ top++;
stack[top]=b%2;
binary(b/2);
}
}
OUTPUT:
Enter number:- 255
The binary of the given number is:- 1 1 1 1 1 1 1 1
Explanation:
This program includes the function binary () which is called recursively for calculating the
binary of a given decimal number. The number is prompted by the user in variable no. On
modulus operation, the reminders are stored in the stack. At first, the least significant bit is
obtained and it is stored on the top of the stack. Subsequently, the other reminders are obtained
and pushed into the stack and the pop operation is carried out for displaying them.
The group of fields such as: a) memory for local variables, b) declaration of function, c) return addresses,
and d) pointer to location are pushed, when a function is invoked and popped, when a function execution
is completed. This particular record of function is known as an activation record.
The details of the fields are described in the following topics. In the block structure programming
languages, the programmer can define variable with same name but with different scopes. Two variables
with the same name cannot be declared in a single block. The scope can be categorized in two types:
a) Local b) Global. The local scope of a variable is limited to a specific block in which a variable is declared.
The global scope covers entire program. The variables declared in a global scope can be accessed by any
sub-program. If, in a program, a variable with same name is declared in local and global variable then the
local variable gets first priority. Consider the following program:
Example 8.7 Write a program to declare same variable within different blocks and display their
values and addresses.
# include <stdio.h>
# include <conio.h>
void main()
{
int j=10;
clrscr();
printf("\n Value of j is %2d & it’s address is = %u",j,&j);
{
int j=5;
printf("\n Value of j is %2d & it’s address is = %u",j,&j);
}
printf("\n Value of j is %2d & it’s address is = %u",j,&j);
}
OUTPUT:
Value of j is 10 & it’s address is = 65496
Value of j is 5 & it’s address is = 65494
Value of j is 10 & it’s address is = 65496
Explanation:
In this program you can observe that a variable int j is declared twice, first, immediately in
main () function and second, declaration is done in a separate block inside the main ()
function.The values and their addresses displayed of both variables are different.The first and last
printf () statement displays the same value and address because they are in main () block.
The second statement is in the sub block.
The above program is based on scope rule of the variable. The scope rules are of two kinds:
1. Static scope
2. Dynamic scope.
Static Scope The static scope declares the variable in circumstances of syntactic program structure.
In such a program structure, one can easily conclude the declaration of variable by only reading the
program. This is why the rule is known as static. The static rule can be defined as follows:
a. The scope of a variable is limited to the current block in which it is declared, other blocks have no
access to the variable.
b. If a variable is not declared in a particular block, in such a case, the variable declaration of previous
block is considered. If declaration, not found, it searches the nested blocks until the declaration is
found. This rule is called “most closely nested rule”.
For example, consider the following program code from the previous program:
{
int j;
printf("\n Address of j =%u",&j);
}
If we remove the statement int j, the output of the program would be:
Address of j =65524
Address of j =65524
Address of j =65524
This is because, when declaration is not found in the above block, the nearest closely declaration is
considered and this is why the address of variable j declared in main() is printed.
Dynamic Scope The dynamic scope is different from static scope. In the dynamic scope, address of
variable is determined at the time of program execution. The same variable name can be defined several
times in the same program. The variable is activated in the memory when a sub-block is under execution.
However, the variables have same name but their addresses are different. Hence, no ambiguous situation
occurs for compiler.
The declaration of variables is considered from the nearly occurring and currently active definition of
the variable during execution of the program.
Consider the following program:
# include <stdio.h>
# include <conio.h>
void main()
{
int j;
void A(void);
void B(void);
clrscr();
A();
printf("\n Address of j in main() %u",&j);
B();
}
void A()
{
int j;
printf("\n Address of j in A() is %u",&j);
}
void B()
{
int j;
printf("\n Address of j in B() is %u",&j);
}
OUTPUT
Address of j in A() is 65518
Address of j in main() 65524
Address of j in B() is 65518
Explanation:
In this program, the variable int j is declared in functions main() A()and B(). Only declaration
does not allocates memory, when a program execution starts, only at that time memory is allocated
to the variable. If execution of main() is started, the memory is not allocated to the variables of
other functions. Memory is allocated to variables only when a function is invoked. From the output,
we can observe that though variable names are same, their memory locations are different.
So far, we have studied the scope rules of variable. Now, we will learn its implementation through stack.
While following scope rules with stack, the system programmer needs to take care of memory allocation
of variables declared in different blocks. There are two types of storage allocation:
1. Static storage allocation
2. Dynamic storage allocation.
Static Storage Allocation The static storage allocation is simple to define. It is a compile time binding.
The space required for variables including all sub-programs are reserved. All sub-programs are compiled.
The total memory required can be calculated by adding the total amount of memory required by each
individual program. The amount of memory calculated cannot further increase or decrease during the
program execution. Thus, it is very safe for program execution. Thus, the program is only executed when
the system is able to provide required resources to the program. Otherwise, compile time error or warning
message will be displayed.
Dynamic Storage Allocation In dynamic storage allocation, memory is allocated during program
execution as per the requirement of the program. When a sub-function is invoked, memory is allocated and
de-allocated when control returns. The space requirement is not constant. At different calls of a function,
the amount of memory required may be different. For example, if a function is invoked recursively, one
cannot determine how deep the recursion will proceed and it is not possible to calculate the accurate
amount of memory required by the function. The program related to dynamic storage allocation is
required to take necessary precautions to shun accidental failure of system due to insufficient memory.
Thus, it is as well necessary to check whether the memory allocation is successfully carried or not. If
memory allocation fails, the sub-routine should transfer the control to next statement. The explanation of
such routines is out of the scope of the book.
Now, we are going to discuss scope rules implementation with dynamic memory allocation. The handy
implementation tool is stack. Such implementation is known as run-time stack. When a sub-function is
invoked, memory is allocated and as soon as execution ends the memory is de-allocated. That specific
block of memory is called an activation record. Such record consists of following structure:
• Storage space for all variables including sub functions
• Definition of functions and pointers
• Return address
• A pointer to activation record.
Memory Space
Fig. 8.4 shows activation record. for Local Varaibles
Activation records are stored in the stack. The stack is controlled during
program execution. When a new function is invoked, control passes to Declaration of
it. Its activation record is pushed onto the stack. The return address is Functions and
Beginning Address
stored in return address field. When function execution completes, the
control returns to the caller function. It obtains the return address from
Return Address
the return address field of the activation record. Immediately after that
the activation record is popped. Figure 8.4 shows the activation records. Pointer to Location
Consider the following program code and Fig. 8.5.
Figure 8.4 Activation Record
void main ()
{
P()
}
main () function
P()
Function P ()
{
Q();
R(); Q
}
R
The function main() invokes function P(). The function P() invokes function Q() and R().
When a new function is invoked, the activation record is pushed and when function execution completes
the record is popped. Thus, the stack is updated when new function is invoked and a function completes
its execution. Thus, by maintaining the stack the next function, which is to be executed, is decided.
Table 8.9 shows the stack operation.
Consider the following program code. There are five activation records. The total numbers of activa-
tion records are equal to number of functions defined. The filed pointer to the location is defined only in
dynamic scope. At the time of program execution, activation records of functions are pushed and popped
to and from the stack.
Example 8.9 Write a program to demonstrate the above theory with an example.
# include <stdio.h>
# include <conio.h>
void Q(void);
void R(void);
void main()
{
void P(void);
clrscr();
printf("\n main()");
P();
printf("\n Again in main() ");
}
void P()
{
printf("\n In P() ");
Q();
printf("Execution of Q complete");
R();
printf("Execution of R complete");
}
void Q() { printf("\n In Q() "); }
void R() { printf("\n In R() "); }
OUTPUT
main()
In P()
In Q() Execution of Q complete
In R() Execution of R complete
Again in main()
Explanation:
In this program P(), Q() and R() are three functions defined. The main() function invokes
P(). The function P() invokes Q() and R() one by one. Messages are displayed when a
function is invoked which help the user to understand the flow of the program. From the following
Fig. 8.6 we can observe that the program execution sequence is main ()-P()-Q()-P()-
R()-main(). Here, function P() is not invoked twice. After complete execution of Q(),
control returns in function P().
main ()
P ()
R ()
Q ()
SUMMARY
1. Arithmetic expressions can be defined in three kinds of notation: infix, prefix and postfix.
The prefixes ‘pre’, ‘post’ and ‘in’ indicates the relative location of the operator with two
operands.
2. Each operator in the postfix expression is associated with the previous two operands.
When we read an operand from a given expression it is pushed onto the stack. When an
operator symbol is read, the operands associated with it are the top elements of the stack.
We can pop these two elements from the stack and perform the operation.
3. In any expression, two types of components can be clubbed together. They are operand
and operators. The operator indicates the operation to be carried out and the operand
indicates the values. The operands are the variables on which the operator operates. Oper-
ators have their priority of execution. When one or more operations are clubbed together
in a single expression the priority decides which operation is performed first.
4. The group of field such as a) memory for local variables, b) declaration of function, c) return
addresses and d) pointer to location are pushed when a function is invoked and popped
when a function execution completed. This particular record of function is known as an
activation record.
5. The static scope declares the variable in circumstances of syntactic program structure.
6. The dynamic scope is different from static scope. In the dynamic scope, address of a variable
is determined at the time of program execution.
7. The static storage allocation is simple to define. It is a compile time binding.
8. In dynamic storage allocation, memory is allocated during program execution as per the
requirement of the program. When a sub-function is invoked, memory is allocated and
de-allocated when control returns.
EXERCISES
A. Answer the following question:
1.
Explain the terms infix expression, postfix expression, and polish notation.
2.
Distinguish between prefix and postfix expressions.
3.
What are the applications of stacks?
4.
Explain scope rules.
5.
Explain activation record.
6.
Distinguish between static and dynamic implementation of stack.
7.
Explain the stack technique last-in-first-out with two real life examples.
8.
Translate following infix expressions to its equivalent prefix expressions:
a. (x+y–z)/(h+k)–z
b. (j+k)*(c/d)
9. Explain solution for following expressions:
a. From infix to postfix j–k /(g^h)+(n+m)
b. From infix to prefix j–k /(g^h)+(n+m)
c. From infix to postfix x*(c+d)+(j/k)*n+m*p
d. From infix to postfix A^B^C
e. From infix to postfix A&&B|| ! (A>B)
10. Evaluate the expression 123+*78 3/–. Show the contents of stack after every step in tabular
form
Trees
9.1 INTRODUCTION
In the previous chapters, we have studied various data structures such as arrays, stacks, queues and linked list.
All these are linear data structures. In these data structures the elements are arranged in a linear manner, i.e.
one after another. Tree is an equally useful data structure of non-linear type. In tree, elements are arranged in
non-linear fashion. A tree structure means data is organized in branches. Figure 9.1 is a sample tree.
A tree is a non-linear data structure and its elements are arranged in sorted order.
Tree has several practical applications. It is immensely useful in manipulating data and to protect
hierarchical relationship among data. The fundamental operations such as insertion, deletion etc. is easy
and efficient in tree data structure than in linear data structures. Fig. 9.2 represents family hierarchy,
which keeps relations among them. The hierarchy gives relations between associates of family members.
In this tree, node ‘A’ can be assumed as a parent of ‘B’ and ‘C’, ‘D’ and ‘E’ are the children of ‘B’. ‘F’ is the
child of ‘C’. This tree represents the relation between the family members.
B C
D E F
For example, suppose the left side member is male and right side member is female. Then, various rela-
tions such as sister, brother, grandfather, grandmother can also be implied.
The algebraic expression can be represented with a tree. Consider the following example of an algebraic
expression.
Z=(J-K)/((L*M)+N)
The operators of the above equation have different priority. The priority of the operators can be repre-
sented by the tree structure. The operators of high priority are at low level and operator and associated oper-
ands are represented in tree structure. Figure 9.3 illustrates the representation of an algebraic expression.
Some of the basic concepts relevant to trees are described in this section. These are node, parents, roots,
child, link, leaf, level, height, degree of node, sibling, terminal nodes, path length, and forest.
− +
J K * N
L M
Root It is the mother node of a tree structure. This is the most important node of any tree. This node
does not have parent. It is the first node in the hierarchical arrangement.
Node It is the main component of the tree. The node of a tree stores the data and its role is same as the
linked list. Nodes are connected by means of links with other nodes. This is shown in Fig. 9.4.
Data
B C
Child When a predecessor of a node is parent then all successor nodes are called child nodes. In Fig. 9.5,
B and C are child nodes of A. The node at left side is called left child node and node at right side is called
right child node.
Link The link is nothing but pointer to node in a tree structure. In other words, link connects the two
nodes. The line drawn from one node to other node is called a link. Fig. 9.5 shows left and right child.
Here, two links are shown from node A. More than two links from a node may be drawn in a tree. In a
few textbooks the term edge is used instead of link. Functions of both of them are same.
Leaf This node is located at the end of the tree. It does not have any child hence it is called as leaf node.
Here, ‘H’, ‘I’, ‘F’, and ‘J’ are leaf nodes in Fig. 9.6.
Level Level is a rank of tree hierarchy. The whole tree structure is levelled. The level of root node is always
at 0. The immediate children of root are at level 1 and their immediate children are at level 2 and so on.
If children nodes are at level n+1 then parent node would be at level n. Fig. 9.6 shows the levels of tree.
Root A
Level 0
B C
Level 1
D E F G
Level 2
H I J
Level 3
Height The highest number of nodes that is possible in a way starting from the first node (root) to a leaf
node is called the height of tree. In Fig. 9.6, the height of tree is 4. This value can be obtained by referring
three different paths from the source node to leaf node. The paths A-B-D-H, A-B-E-I, and A-C-G-J have
the same height. The height can be obtained from the number of levels, which exists in the tree. The
formula for finding the height of the tree h = imax +1, where h is the height and imax is maximum level of
the tree. In the above Fig. 9.6 the maximum level of the tree is 3(imax=3). By substituting the value into the
formula the h will be 4. The term depth can be used in place of height.
Degree of a Node The maximum number of children that can exist for a node, is called as the degree of
the node. In Fig. 9.6 the node A, B and C have maximum two Children. So, the degree of A, B and C is
same and it is equal to 2.
Sibling The child nodes of same parent are called sibling. They are also called brother nodes. A, B and C
nodes in the Fig. 9.6 have two child nodes. B and C are the siblings of the node A, whereas D and E are
the siblings of the node B.
Terminal Node A node with degree zero is called terminal node or leaf. Fig. 9.6 shows 4 terminal nodes
and they are H, I, F and J.
Path Length It is the number of successive edges from source node to destination node. In Fig. 9.6, the
path length from the root node A to H is three because there are three edges.
Forest It is a group of disjoint trees. If we remove a root node from a tree then it becomes the forest.
If we remove root node A then the two disjoint sub-trees will be observed. They are left sub-tree B and
right sub-tree C.
Labelled Trees In the labelled tree the nodes are labelled with alphabetic or numerical values. The
labels are the values allotted to the nodes in the tree. Fig. 9.7 shows the diagram of a labelled tree. Finally
obtained tree is called as labelled tree.
3 7
1 5 2 9
11 10 14
17
Fig. 9.7 shows the labelled tree having 11 nodes; root node is 4 and leaf nodes 11,10,17, and 2. The
parent and children relationship between them is shown in Table 9.1.
There is one more relationship, which is called left node left child and right node right child. This is
useful for traversing the binary tree.
Table 9.2 describes the relationship between the parents, left and right nodes, which is drawn from the
Fig. 9.7. The ‘—’ indicate that root node (parent) does not have child node. The 11,10 and 17 labelled
nodes are the leaf nodes of the tree.
4 3,7
3 1,5
1 11
5 10
7 2,9
9 14
14 17
4 3 7
3 1 5
1 – 11
5 – 10
7 2 9
9 – 14
14 – 17
A program is provided based on the above relationships. It finds the number of nodes in a tree and leaf
nodes.
Example 9.1 Write a program to find number of nodes and leaf nodes in the given tree.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
int node=1,lev;
struct tree *bt=NULL;
struct tree *insert(struct tree*bt,long no);
void lev_count(struct tree*bt);
void node_count(struct tree*bt);
void main()
{
long no;
clrscr();
puts("Enter the nodes of tree in preorder: and 0 to quit");
scanf("%ld",&no);
while(no!=0)
{
bt= insert(bt,no);
scanf("%d",&no);
}
node_count(bt);
printf("The tree contains %d nodes\n",node);
lev_count(bt);
printf("\nThe tree contains %d leaf nodes\n", lev);
}
struct tree*insert(struct tree*bt,long no)
{
if(bt==NULL)
{
bt=(struct tree*) malloc(sizeof(struct tree));
bt->left=bt->right=NULL;
bt->data=no;
}
else
{
if(no<bt->data)
bt->left=insert(bt->left,no);
else
if(no<bt->data)
bt->right=insert(bt->right,no);
else
if(no==bt->data)
{
puts("Duplicates nodes: Program exited");
exit(0);
}
}
return(bt);
}
void node_count(struct tree*bt)
{
if(bt!=NULL)
{
if(bt->left!=NULL)
{
node++;
node_count(bt->left);
}
if(bt->right!=NULL)
{
node++;
node_count(bt->right);
}
}
}
void lev_count(struct tree*bt)
{
if(bt!=NULL)
{
if((bt->left==NULL)&&(bt->right==NULL))
lev++;
else
lev_count(bt->left);
lev_count(bt->right);
}
}
OUTPUT
Enter the nodes of tree in preorder: and 0 to quit
5 3 11 0
The tree contains 3 nodes
The tree contains 2 leaf nodes
Explanation:
This program contains three functions insert(), lev_count(), and node_count().
The insert() is used to insert the element into the binary tree. The lev_count()
function is used to count the total leaf nodes of the binary tree. The global variable lev
stores the total number of the leaf nodes. The function node_count() is initialized to
count number of the nodes which are present in the binary tree. The global variable node is
used to store the total number of nodes present in the tree.
A general tree is similar to the family tree. The origin of the family tree is called the root node. The
various generations are represented in hierarchical structure with nodes. A tree is a finite set of one or
more nodes. A root node T may have n number of children named T1,T2 … Tn. The total number of
children may vary.
T1 T2 T3
In Figure 9.8, T is the root node and T1, T2, T3 are the children of the root node T. T11, T12, and T13
are the children of the node T1. Similarly T31 is the child of the node T3. So it is clear from the figure that
the number of children may vary from node to node.
A binary tree is a finite set of data elements. A tree is binary if each node of it has a maximum of two
branches. The data element is either empty or holds a single element called root along with two disjoint
trees called left sub-tree and right sub-tree, i.e. in a binary tree the maximum degree of any node is
two. The binary tree may be empty. However, the tree cannot be empty. The node of tree can have any
number of children whereas the node of binary tree can have maximum two children. Fig. 9.9 shows
a sample binary tree.
In Fig. 9.9, A is the root and B and G are its child nodes. The nodes B and G are non-empty nodes,
hence they are called left successor and right successor of the root A. The node root without successor is
called the terminal node of that root. In Fig. 9.9 the node E, F, J, and K are the terminal nodes.
The right tree is G and left tree is B. Next B has left tree C and right tree D. The right tree further has
left tree H and right tree I. This will be continued up to last level.
A tree is called complete binary tree if each of its nodes has two children, except the last nodes. In other
words, every non-terminal node of it must have both children except the last leaf nodes. So, at any
A A
B G B C
C D H I
D E F G
E F J K H I J K L M N O
Figure 9.9 (a) A Sample Binary Tree Figure 9.9 (b) Complete Binary Tree
level the maximum number of nodes is equal to 2. At level 0, there must be only one node and that
is the root node. A at level 1 the maximum nodes must be 2. At level 3 the maximum nodes must be
equal to 8. Figures 9.9 (a) and 9.9 (b) show a complete binary tree.
The advantages of this complete binary tree is that one can very easily locate the position of the parent
and also left and right child nodes of a complete binary tree. Left child node and right child nodes are
located at 2N and 2N+1. V=2n+1−1, where V is the total number of nodes, and n is the depth of the tree.
Similarly, the parent node of any node would be at floor (N/2).
Parent of D would be floor (5/2) = 2, i.e. B is the parent and its left and right child are 2*2 = 4 and
2*2+1=5. 4 and 5 are the C and D child nodes in Fig. 9.9.
When every non-leaf node in binary tree is filled with left and right sub-trees, the tree is called strictly
binary tree. It is shown in Fig. 9.10.
I K
J
L
M N
In the strictly binary tree shown in Fig. 9.10, L and I are non-terminal nodes with non-empty left and
right sub trees.
When every node of a tree has either 0 or 2 children then such a tree is
called an extended binary tree or a 2-tree. The nodes with two children
are called internal nodes. The nodes without children are known as exter-
nal nodes. At some places in order to identify internal nodes in Figures
9.11 to 9.14 circles are used. To identify external nodes squares are used.
The nodes in binary tree that have only one child can be extended with
one more child. This extended binary tree can be used for implement-
ing the algebraic equation because in the algebraic equation the left and
right child nodes are operands and the parent of the child represents the Figure 9.11 Binary Tree
operator.
1 2 3
4 5 6
The root node of the tree always starts at index zero. Then, successive memory locations are used for
storing left and right child nodes. Consider the following Fig. 9.15,
TREE[0] X
TREE[1] Y
TREE[2] Z
In Fig. 9.15, X is the root and Y and Z are the children. X is the father of child Y and Z. Consider
Fig. 9.16 with one more level.
X
1 2
Y Z
3 4 5 6
S T U V
It is very easy in this representation to identify the father, left and right child of an arbitrary node.
For any node n, 0 ≤ n ≤ (MAXSIZE –1), the following can be used to identify the father and child
nodes.
Father (n) The location of the father node can be identified in a given tree by using the ((n–1)/2)
where n is the index of child node, provided that n! =0 (not equal to zero). In case if n=0, the said
node is root node. It has no father node. Consider the node 3 in Fig. 9.16 i.e. S. The father of node S
is Y and the index of Y is 1. By substituting these values in the equation we identify the index of the
father node.
Floor ( (3-1)/2) ) i.e. (2/2)=1
1. Lchild(Xn) =lchild(0)
=2 * 0+1
=1
The node with index 1 is Y.
2. lchild (Z) = lchild(2)
= 2*2+1 = 5
The node with index 5 is U.
rchild (n) The right child of node (n) can be recognized by (2n+2)
Siblings In case the left child at index n is known then its right sibling is at (n+1). Likewise, if the
right child at index n is known then is left sibling is at (n–1). The array representation is perfect for
complete binary tree. However, this is not appropriate for other tree types and if we use array for their
representation, it results in inefficient use of memory. Consider the binary trees shown in Figs. 9.17 (a)
and 9.17 (b).
The above is skewed binary tree. Here, every left sub-tree again represents left sub-tree. Such type of binary
tree is called left skewed binary tree. Similarly, right skewed binary tree is also present [See Figs. 9.17 (a)
and 9.17 (b)]. Fig. 9.18 shows the array representation of left and right skewed trees.
X
X
0
Y Z
1
S V
3
Figure 9.17 (a) Left Skewed Binary Tree Figure 9.17 (b) Right Skewed Binary Tree
0 X
1 Y Left Skewed
2 Z Binary Tree
Right Skewed
3 S
Binary Tree
4 T
5 U
6 V
struct node
{ M
int data;
struct node *rchild;
struct node *lchild;
N O
};
P Q R S
N O
P Q R S
Binary tree has one root node and few non-terminal and terminal nodes. The terminal nodes are called
leafs. The non-terminal nodes have their left wand right child nodes. However, the terminal nodes are
without child. While implementing this, fields of lchild and rchid are kept NULL. The non-terminal
nodes are known as internal nodes and terminal nodes are known as external nodes.
In addition to above mentioned operations, following operations can also be performed on binary tree:
1. Tree traversal
2. Insertion of nodes
3. Deletion of node
4. Searching for a given node
5. Copying the binary tree.
Three parameters are needed for formation of binary tree. They are node, left and right sub-trees. Travers-
ing is one of the most important operations done on binary tree and frequently this operation is carried
on data structures. Traversal means passing through every node of the tree one by one. Every node is tra-
versed only once. Assume, root is indicated by O, left sub-tree as L and right sub-tree as R. The following
traversal combinations are possible:
Out of six methods only three are standard and are discussed in this chapter. In traversing always right
sub-tree is traversed after left sub-tree. Hence, the OLR is preorder, LOR is inorder and LRO is postorder.
Figure 9.22 shows a model tree (binary tree)
N O
P Q R S
The inorder representation of the above tree is P-N-Q-M-R-O-S-V. Traversing is a common operation
on binary tree. The binary tree can be used to represent an arithmetic expression. Here, divide and conquer
technique is used to convert an expression into a binary tree. The procedure to implement it is as follows.
The expression for which the following tree has been drawn is (X*Y)+Z. Fig. 9.23 represents the expression.
Using the following three methods, the traversing operation can be performed. They are:
1. Preorder traversal +
2. Inorder traversal
3. Postorder traversal.
All the above three types of traversing methods are * Z
explained below.
P N Q M R O S V
B E
C D F G
Example 9.2 Write a program for inserting the elements into the tree and traverse the tree by the
inorder.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
struct tree *btree=NULL;
struct tree *insert(struct tree*btree,long digit);
void inorder(struct tree*btree);
void main()
{
long digit;
clrscr();
puts("Enter integers: and 0 to quit");
scanf("%ld",&digit);
while(digit!=0)
{
btree= insert(btree,digit);
scanf("%d",&digit);
}
puts("Inorder traversing of btree:\n");
inorder(btree);
}
struct tree*insert(struct tree*btree,long digit)
{
if(btree==NULL)
{
btree=(struct tree*) malloc(sizeof(struct tree));
btree->left=btree->right=NULL;
btree->data=digit;
}
else
{
if(digit<btree->data)
btree->left=insert(btree->left,digit);
else
{
if(digit>btree->data)
btree->right=insert(btree->right,digit);
else
if(digit==btree->data)
{
puts("Duplicates nodes: Program exited");
exit(0);
}
}
return(btree);
}
OUTPUT
Enter integers: and 0 to quit
6 1 2 3 7 8 9 0
Inorder traversing of btree:
1 2 3 6 7 8 9
Explanation:
This program is used to evaluate the inorder of the given tree. The given binary tree is stored
in *btree. The elements are inserted by using the insert(). The inorder traversing, i.e. left,
root and right is done by inorder(). Fig. 9.26 illustrates the binary tree.
1 7
2 8
3 9
N O
P Q R S
The preorder traversing for Fig. 9.27 is M, N, P, Q, O, R, S, and V. This can also be shown in Fig. 9.28.
In this traversing the root comes first and the left sub-tree and right sub-tree at last.
M N P Q O R S V
In the preorder the left sub-tree appears as N, P, and Q and right sub-tree appears as O, R, S, and V.
Example 9.3 Write a program for inserting the elements in the tree and display them in preorder.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
struct tree *btree=NULL;
struct tree *insert(struct tree*btree,long digit);
void preorder(struct tree*btree);
void main()
{
long digit;
clrscr();
puts("Enter integers: and 0 to quit");
scanf("%ld",&digit);
while(digit!=0)
{
btree= insert(btree,digit);
scanf("%d",&digit);
}
OUTPUT 2 7
Enter integers: and 0 to quit
5 2 1 7 0
Preorder traversing btree: 1
5 2 1 7
Figure 9.29 Binary Tree
Explanation:
The program stores the binary tree in *btree structure variable of the structure tree. By
invoking the insert() the elements are inserted. The preorder traversing is done by using
preorder(). Fig. 9.29 shows the *btree, which is inserted in this program.
N O
P Q R S
P Q N R V S O M
Example 9.4 Program for inserting the nodes in tree and traversing these nodes by using the
postorder method.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
struct tree *btree=NULL;
struct tree *insert(struct tree*btree,long digit);
OUTPUT
Enter integers: and 0 to quit
5 3 4 1 9 6 7 0
Postorder traversing btree:
1 4 3 7 6 9 5
Explanation:
In this program, *btree is used to store the binary tree. The *btree is the structure variable
of the structure tree. In this program the insert() is invoked to insert the elements in
the *btree. postorder() is used to traverse the binary tree in the post order manner.
Fig. 9.32 shows the btree which is inserted in the program.
3 9
1 4 6
Example 9.5 Write a menu-driven program for various traversals such as (1) inorder (2) preorder
and (3) postorder.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
struct tree *btree=NULL;
struct tree *insert(struct tree*btree,long digit);
void inorder(struct tree*btree);
void preorder(struct tree*btree);
void postorder(struct tree*btree);
void main()
{
long digit;
int ch;
clrscr();
puts("Enter integers: and 0 to quit");
scanf("%ld",&digit);
while(digit!=0)
{
btree= insert(btree,digit);
scanf("%d",&digit);
}
while(1)
{
clrscr();
printf(" 1] For Inorder traversal of btree\n");
printf(" 2] For Preorder traversal of btree\n");
printf(" 3] For Postorder traversal of btree\n");
printf(" 4] For Exit\n");
printf("Enter the choice:-");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("The inorder traversing of tree\n");
inorder(btree);
getch();break;
case 2: printf("The preorder traversing of tree\n");
preorder(btree);
getch();break;
case 3: printf("The postorder traversing of tree\n");
postorder(btree);
getch();break;
default: exit(0);
}
}
}
struct tree*insert(struct tree*btree,long digit)
{
if(btree==NULL)
{
btree=(struct tree*) malloc(sizeof(struct tree));
btree->left=btree->right=NULL;
btree->data=digit;
}
else
{
if(digit<btree->data)
btree->left=insert(btree->left,digit);
else
if(digit>btree->data)
btree->right=insert(btree->right,digit);
else
if(digit==btree->data)
{
puts("Duplicates nodes: Program exited");
exit(0);
}
}
return(btree);
}
void inorder(struct tree*btree)
{
if(btree!=NULL)
{
inorder(btree->left);
printf("%4ld",btree->data);
inorder(btree->right);
}
}
void preorder(struct tree*btree)
{
if(btree!=NULL)
{
printf("%4ld",btree->data);
preorder(btree->left);
preorder(btree->right);
}
}
void postorder(struct tree*btree)
{
if(btree!=NULL)
{
postorder(btree->left);
postorder(btree->right);
printf("%4ld",btree->data);
}
}
OUTPUT
Enter integers: and 0 to quit
5 3 1 4 11 8 12 0
1] For Inorder traversal of btree
2] For Preorder traversal of btree
3] For Postorder traversal of btree
4] For Exit
Enter the choice:-1
The inorder traversing of tree
1 3 4 5 8 11 12
1] For Inorder traversal of btree
2] For Preorder traversal of btree
3] For Postorder traversal of btree
4] For Exit
Enter the choice:-3
The postorder traversing of tree
1 4 3 8 12 11 5
Explanation:
This program uses the switch() and case for the menu-driven purpose, the structure tree
is used to store the whole tree and the left and right pointer of the tree is used to maintain
the left and right sub-tree. The function inorder() is used for displaying the tree in inorder
and similarly, the postorder() and preorder() used for the traversing purpose. The tree,
which is used in this program, is shown in Fig. 9.33.
3 11
1 4 8 12
Example 9.6 Find the inorder, preorder and postorder traversals for the tree shown in Fig 9.34.
B E
C D F G
Preorder: ABCDEFG
Postorder: CDBFGEA
Example 9.7 Find the inorder, preorder, and postorder traversals for the tree shown in Fig 9.35.
Q T
R S U
Inorder: RQSPTVU
Postorder: RSQVUTP
Preorder: PQRSTUV
Example 9.8 Construct a binary tree from the given inorder and postorder traversal.
Inorder: CBDAFEG
Postorder: CDBFGEA
1. It is clear that A is the root of the tree since the root is traversed last in the postorder traversal.
2. The inorder traversal indicates all the nodes that are on the left side of A belonging to the left sub-
tree and those on the right side of A belonging to the right sub-tree.
Step 1:
Figure 9.36 indicates that CBD is the left sub-tree of the root node A and FEG is the right sub-
tree of the root node A. From the left sub-tree CBD, one can obtain the root from the postorder
traversal, i.e. B is the root node. Thus, from the inorder traversal we can say that C is the left child
of B and D is the right child of B. Similarly, for the right sub-tree of A, i.e. FEG, one can obtain
E as the root node from the given postorder traversal. From the inorder traversal, one can obtain
F as the left node of node E and G as the right node of node E. Thus, the final binary tree is as
follows (Fig. 9.37).
A
B E
CBD FEG C D F G
Example 9.9 Construct a binary tree from the given inorder and postorder traversal.
Inorder: 24316587
Postorder: 43268751
The steps are as follows (Figs. 9.38–9.41):
Step 1:
1
243 6587
Step 2: Step 3:
1 1
2 5 2 5
43 6 87 43 6 87
Step 4:
2 5
3 6 7
4 8
Example 9.10 Construct a binary tree from the given inorder and preorder traversals.
Inorder: RTSUQPWYXZV
Preorder: PQRSTUVWXYZ
1. We obtain the root nodes as we traverse from left to right. P is the root of the tree from the given
preorder traversal.
2. One can obtain all the nodes that are on the left side of P belonging to the left sub-tree and those
on the right side of P belonging to the right sub-tree from the inorder traversal.
The steps are depicted in Figures 9.42–9.46.
Step 1:
RTSUQ WYXZV
Step 2:
Q V
RTSU WXYZ
Step 3:
Q V
R W
TSU
YXZ
P P
Q V
Q V
R W
R W
X
X
S
S
Y Z Y Z
T U T U
The arithmetic expression can be converted from infix to postfix. Consider the following example:
(A+B)*(D/E)
In the conversion of the expression into binary tree the operator divides the expression into two parts,
and on that basis the binary tree is formed. Always the operators are at the root nodes and the operands
are at the leaf nodes.
In above expression at first the ‘*’ operator divides the expression into the two parts, i.e. (A+B) and
(D/E). The ‘*’ is at the root of the tree.
(A+B) (D/E)
+ I
A B D E
# include <stdio.h>
# include <conio.h>
# include <string.h>
char stack[50];
int top=-1;
void post(char inexp[]);
void push(char);
char pop();
int preced(char c);
void main()
{
char inexp[25];
clrscr();
printf("\nEnter the inexp expression:- ");
scanf("%s",inexp);
post(inexp);
getch();
}
void push(char sy)
{
if(top>=49)
{
printf("\nstack overflow");
getch();
}
else
{
top=top+1;
stack[top]=sy;
}
}
char pop()
{
char item;
if(top==-1)
{
printf("\nStack is empty");
getch();
return 0;
}
else
{
item=stack[top];
top––;
}
return(item);
}
int preced(char ch)
{
if(ch==47)
return(5);
if(ch==42)
return(4);
if(ch==43)
return(3);
return(2);
}
void post(char inexp[])
{
int len;
static int index=0,pt=0;
char oper,temp;
char postf[40];
len=strlen(inexp);
push('#');
while(index<len)
{
oper=inexp[index];
switch(oper)
{
case '(': push(oper);
break;
case ')': temp=pop();
while(temp!='(')
{
postf[pt]=temp;
pt++;
temp=pop();
}
break;
case '+':
case '-':
case '*':
case '/':
case '^':
while(preced(stack[top])>=preced(oper))
{
temp=pop();
postf[pt]=temp;
pt++;
}
push(oper);
break;
default: postf[pt++]=oper;
break;
}
index++;
}
while(top>0)
{
temp=pop();
postf[pt++]=temp;
}
postf[pt++]='\0';
printf("\nThe expression into postfix:-");
printf("%s",postf);
}
OUTPUT
Enter the inexp expression:- (A+B)*(C/D)
The expression into postfix:-AB+CD/*
A binary search tree is also called as binary sorted tree. Binary search tree is either empty or each node N
of tree satisfies the following property:
1. The key value in the left child is not more than the value of root.
2. The key value in the right child is more than or identical to the value of root.
3. All the sub-trees, i.e. left and right sub-trees follow the two rules mentioned above.
4 8
3 5 1 9
4 6
Example 9.12 Write a program to search an element from the binary tree.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
struct tree *right;
};
int sn;
struct tree *bt=NULL;
struct tree *insert(struct tree*bt,long no);
void search(struct tree *bt, long sn);
void main()
{
long no;
clrscr();
puts("Enter the nodes of tree in preorder: and 0 to quit");
scanf("%ld",&no);
while(no!=0)
{
bt= insert(bt,no);
scanf("%d",&no);
}
printf("\nEnter the number to search:-");
scanf("%d",&sn);
search(bt,sn);
}
}
void search(struct tree*bt, long fn)
{
if(bt==NULL)
puts("The number does not exit");
else
if(fn==bt->data)
printf("The number %d is present in tree",fn);
else
if(fn<bt->data)
search(bt->left,fn);
else
search(bt->right, fn);
}
OUTPUT
Enter the nodes of tree in preorder: and 0 to quit
3 5 11 17 34 0
Enter the number to search:-17
The number 17 is present in tree
Enter the nodes of tree in preorder: and 0 to quit
3 5 11 17 34 0
Enter the number to search:-4
The number does not exit
Explanation:
This program contains struct tree which is used to store the binary tree. The insert()
function inserts the nodes into the binary tree. The search() function searches the number
from the tree.The fn variable is used to store the number which the user will not find.The search
function first compares the fn with the root node. If the value of fn is less than the root node then
the function searches the number in the left sub-tree, else it finds the number in the right sub-tree.
Example 9.13 Write a program to insert an element into the binary search tree.
# include <stdio.h>
# include <conio.h>
struct tree
{
long data;
struct tree *left;
main()
{
long no;
clrscr();
puts("Enter the nodes of tree in preorder: and 0 to quit");
scanf("%ld",&no);
while(no!=0)
{
bt= insert(bt,no);
scanf("%d",&no);
}
printf("Enter the number to insert:- ");
scanf("%d",&in);
bt=insert(bt,in);
printf("The inorder of tree after insertion of an element\n");
inorder(bt);
}
struct tree*insert(struct tree*bt,long no)
{
if(bt==NULL)
{
bt=(struct tree*) malloc(sizeof(struct tree));
bt->left=bt->right=NULL;
bt->data=no;
}
else
{
if(no<bt->data)
bt->left=insert(bt->left,no);
else
if(no>bt->data)
bt->right=insert(bt->right,no);
else
if(no==bt->data)
{
puts("Duplicates nodes: Program exited");
exit(0);
}
}
return(bt);
}
void inorder(struct tree *bt)
{
if(bt!=NULL)
{
inorder(bt->left);
printf("%d ",bt->data);
inorder(bt->right);
}
}
OUTPUT
Enter the nodes of tree in preorder: and 0 to quit
7 5 9 0
Enter the number to insert:- 2
The inorder of tree after insertion of an element
2 5 7 9
Explanation:
This program invokes a function insert() which inserts the node into the structure pointer
object *bt. Firstly, the nodes which are to be inserted are entered and then program calls
insert() for insertion of new element. The new inserted element firstly checks with the
root of the tree. If the number is lesser than the root node it is recursively checked with the
nodes, which are present on the left sub-tree, otherwise right sub-tree. Appropriate position
of parent node is found and the element is inserted. After insertion, elements are arranged in
inorder using the inorder() and the same numbers are displayed on the screen.
Example 9.14 Program to traverse the binary search tree by using the inorder, preorder and
postorder methods.
# include <stdio.h>
# include <conio.h>
struct rec
{
int data;
struct rec *left;
struct rec *right;
};
struct rec *t1;
struct rec *insert(struct rec * t1, int data);
void inorder(struct rec *t1);
void preorder(struct rec *t1);
void postorder(struct rec *t1)
void main()
{
int digit;
clrscr();
}
void postorder(struct rec *t1)
{
if(t1!=NULL)
{
postorder(t1->left);
postorder(t1->right);
printf("%d ",t1->data);
}
}
OUTPUT
Enter the t1 in pre order and 0 to quit
3 2 4 0
The preorder of the t1 is
3 2 4
The inorder of the t1 is
2 3 4
The post order of the t1 is
2 4 3
Explanation:
The program first gets the binary search tree into the structure object t1. And the insert()
is used to insert the element into the tree. The three functions inorder(), preorder()
and postorder() are used to traverse the tree in appropriate manner.
While studying the linked representation of a binary tree, it is observed that the number of nodes that
have null values are more than the non-null pointers. The number of left and right leaf nodes has number
of null pointer fields in such a representation. These null pointer fields are used to keep some other infor-
mation for operations of binary tree. The null pointer fields are to be used for storing the address fields of
higher nodes in tree, which is called thread. Threaded binary tree is the one in which we find these types
of pointers from null pointer fields to higher nodes in a binary tree. Consider the following tree:
In Fig. 9.50, in the binary tree there are 7 null pointers. These are shown with the dotted lines. There
are total 12 node pointers out of which 5 are actual node pointers, i.e. non-null pointer (solid lines). For
any binary tree having n nodes there will be (n+1) null pointers and 2n total pointers. All the null pointers
can be replaced with appropriate pointer value known as thread. The binary tree can be threaded accord-
ing to appropriate traversal method. The null pointer can be replaced as follows:
Threaded binary tree can be traversed by any one of the three traversals, i.e. preorder, postorder and
inorder. Further, in inorder threading there may be one-way inorder threading or two–way inorder thread-
ing. In one way inorder threading the right child of the node would point to the next node in the sequence
of the inorder traversal. Such a threading tree is called right in threaded binary tree. Also, the left child of
the node would point to the previous node in the sequence of inorder traversal. This type of tree is called
as left in threaded binary tree. In case both the children of the nodes point to other nodes then such a tree
is called as fully threaded binary tree.
J Non-Null
Pointer
K
L
M N O
Null
Pointer
Figure 9.50 describes the working of right in threaded binary tree in which one can see that the right
child of the node points to the node in the sequence of the inorder traversal method.
J
K L
M N O
The inorder traversal shown in the Fig. 9.51 will be as M-K-N-J-O-L. Two dangling pointers are shown
to point a header node as shown below:
• rchild of M is made to point to K
• rchild of N is made to point to J
• rchild of O is made to point to L.
Similarly, the working of the left in binary threaded tree is illustrated in Fig. 9.52. In this case the left
child of node points to the previous node in the sequence of inorder traversal.
As shown in Fig. 9.52, thread of N points to K. Here, K is the predecessor of N in inorder traversal.
Hence, the pointer points to K. In this type of tree the pointers pointing to other nodes are as follows:
• lchild of N is made to point to K
• lchild of O is made to point to J.
K L
M N Q
Figure 9.53 illustrates the operation of fully threaded binary tree. Right and left children are used for
pointing to the nodes in inorder traversal method.
• rchild of M is made to point to K
• lchild of N is made to point to K
• rchild of N is made to point to J
• lchild of O is made to point to J
• rchild of O is made to point to L.
K L
M N O
Header node
K L
M N O
The working of the fully threaded binary tree is illustrated in Fig. 9.54. In this case the left child
of node points to the previous node in the sequence of inorder traversal and right child of the node
points to the successor node in the inorder traversal of the node. In the previous two methods left
and right pointers of the first and last node in the inorder list are NULL. But in this method the left
pointer of the first node points to the header node and the right pointer of the last node points to the
header node. The header node’s right pointer points to itself, and the left pointer points to the root
node of the tree. The use of the header is to store the starting address of the tree. In the fully threaded
binary thread each and every pointer points to the other nodes. In this tree we do not find any NULL
pointers.
In the Fig. 9.54 the first node in the inorder is M and its left pointer points to the left pointer of the
header node. Similarly, the last node in the inorder is L and its right pointer points to the left pointer of
the header.
In memory representation of threaded binary tree, it is very important to consider the difference
between thread and normal pointer. The threaded binary tree node is represented in Fig. 9.55.
Each node of any binary tree stores the three fields. The left field stores the left thread value and the
right field stores the right thread value. The middle field contains the actual value of the node, i.e. data.
Binary search tree is, in general called multi-way search tree. The integer m is called the order of the
tree. Each node should have maximum m children. If k< m, where m is number of children, then node
has accurately k-1 keys which divides all the keys into k number of sets. In case some sets are empty, the
children are also empty.
The B-tree is also known as the balanced sort tree. The B-tree is used in external sorting. The B-tree is
not a binary tree. While implementing B-tree following conditions are followed:
1. The height of the tree must be minimum.
2. There should be no empty sub-trees after the leaves of the tree.
3. The leaves of the tree should be at the same level.
4. All nodes excepting the leaves should have at least few children.
1 5 6 2 8 11 13 18 20 7 9
1 5 6
The value 1 is put in a new node. This node can also hold next two values.
1 2 6 8 11
When value 2 (4th value ) is put, the node is split at 5 into leaf nodes. Here, 5 is parent. The element 8
is added in leaf node. The search for its accurate position is done in the node having value 6. The element
8 also is present in the same node.
1 2 6
The element 13 is to be inserted. However, the right leaf node, in which 1 to 3 values have appropriate
plane, is occupied. Hence, the node splits at median 8 and this moves it up to the parent.
1 2 11 13
6
By following the above procedure the remaining nodes can be included. The final figure would be as
follows:
5 8 13
1 2 6 7 9 11 18 20
or record from a terminal node. While deleting the record the new node size is more than minimum, i.e.
the deletion is complete. If the node size is less than minimum, an underflow happens.
Rearrangement is done if either of adjacent siblings has more than the minimum elements (records).
For rearrangement, the contents of the node (only those nodes having less than minimum records) along
with sorting out records from parent node are gathered. The central record is written back to the parent
and left and right halves are written back to two siblings.
Concatenation is applied if the node with less than minimum number of records has no adjacent sib-
ling. The node is combined with its neighbouring sibling and element is moved from its parent.
B C D E
F G H I J K L M N O P Q R S T
B C D Q
F G H I J I M N O P R S T
3. To delete P, the node has less than minimum numbers of keys. The sibling is carried. R moves up and
Q moves down.
4. Deleting H, again node has less than minimum keys than required. The parent is left with only one
key. Here, sibling cannot be applied. Hence, A, C, D and R form a new node.
ACDR
FGB JL MN OQ ST
C A D F
2. Insert B. The node is full. It is splitted. Here, A is median in BCADF. A is parent. The splitting is at
root node. We should make one node.
BC DF
3. Again, insert P, Q, R, S.
RBCS DPQF
A Q
RBCS D P F E
A Q
RBCS D P FEXY
6. After addition of T.
TAQ
RB CS DP F E XY
7. J, K are inserted.
TAQ
RB CS DPJK FEXY
TAQX
RB CS DPJK FE LY
9. Z can be placed in DPJK and it will be promoted to TAQX. But this is also full and hence, the root
will split and new root will be created.
T A Q X
R B C S D P J K F E L Y
9.16 B+ TREE
A B+ tree can be obtained with slight modification of indexing in B tree. A B+ tree stores key values repeat-
edly. Fig. 9.56 shows B+ tree indexing.
10
22 13
7 8 9 22 6 10 15 13 12
F F F F F F F F F F F F F
By observing Fig. 9.56, we will come to know that the key values 10, 22 and 13 are stored repeatedly in
the last level (terminal nodes). In these leaf nodes a link is maintained so that the traversing can be done
from the leaf node at the extreme left to the leaf node at the extreme right. B tree and B+ tree are same
except the above differences.
Every leaf node of B+ tree has two parts:
1. Index part It is the interior node stored repeatedly.
2. Sequence set It is a set of leaf nodes.
The interior nodes or keys can be accessed directly or sequentially. B+ tree is useful data structure in
indexed sequential file organization.
The AVL tree is a binary search tree. It is also known as height balanced tree. It is used to minimize the
search time by keeping every node of the tree completely balanced in terms of height. The balance factor
plays an important role in the insertion and deletion of elements in an AVL tree. The balance factor
decides whether all the nodes of a tree are completely balanced. The AVL tree was defined as a height bal-
anced tree by two scientists Georgii Adelson-Velskii and E. M. Landis.
1 1 0
0 0 0
2 −2
−1
1
0
0
(b) Non-AVL Trees
A non-AVL tree can be converted to an AVL tree by performing different types of rotation on a given
tree. Single or double rotation may be performed.
Single rotation can be left rotation (anti-clock-wise rotation) or right rotation (clock-wise rota-
tion). Left and right rotations for AVL and non-AVL trees are shown in Fig. 9.59 and Fig. 9.60,
respectively.
–2
A
Rotate Left
Rotate Left 0 B
−1 B
0 A 0 C
0 C
0
C
0 B
1 Rotate Right
B
0 A 0 C
0
A
0 C 0 C
2 C 20 C
B
0 B 0 A
Example 9.15 Draw an AVL tree and show the balance factor.
Step 2: Insert 15
5 –1
15 0
Step 3: Insert 9
–2 5 –2 9 0
5
0 0
9
15
0 −1
0 0
5 15 5 15
0 0 0 0 0
8
2 2 8 17
Step 6: Insert 23
−1 0
9 9
Rotate Left
0 −2 0
5 15 5 0 17
−1 0 0
0 0 0 0
2 8 2 8
−1 9 −1
9
0 −1 0 0
17 5 17
5
0 1 0 0 1 1
0 0
2 8 23
2 8
0 0 0
18
Step 9: Insert 20
9 -2
-2
9
5 0 17 -1
0 17 -1
5
Rotate Left
0 0 2
0 1 2 1
0
8 23
8 23
0 1 0 1
18 20
0 0
20 18
-1
9
0 0
5 17
0 0 1 0
2 8 20
0 0 0
SUMMARY
1. Tree is a useful data structure of non-linear type. Elements are arranged in a non-linear
fashion in a tree. A tree structure means that data is organized in branches.
2. Tree has several practical applications. It is immensely useful in manipulating data and to
protect hierarchical relationships among data.
3. Root is the mother node of a tree structure. This is the most important node of any tree.
Node is the main component of the tree. The node of a tree stores the data and its role
is the same as the linked list. Nodes are connected by means of links with other nodes.
4. When the predecessor of a node is a parent, then all successor nodes are called child
nodes. The link is a pointer to a node in a tree structure. In other words, link connects the
two nodes. The line drawn from one node to other node is called a link.
5. The highest number of nodes that is possible starting from the first node (root) to a leaf
node is called the height of a tree.
6. A general tree is similar to the family tree. The origin of the family tree is called the root
node. The various generations are represented in hierarchical structure with nodes. A tree
is a finite set of one or more nodes.
7. A binary tree is a finite set of data elements. A tree is binary if each of its nodes has a maxi-
mum of two branches. The data element is either empty or holds a single element called
root along with two disjoint trees called left sub-tree and right sub-tree, i.e. in a binary tree
the maximum degree of any node is two.
8. A tree is called a complete binary tree if each of its nodes has two children, except the
last nodes. In other words, every non-terminal must have both children except the last leaf
nodes.
9. Three parameters are needed for the formation of binary tree.They are node, left and right
sub-trees.Traversing is one of the most important operations done on binary tree and fre-
quently this operation is carried on data structures. Traversal means passing through every
node of the tree one by one. Every node is traversed only once.
10. A binary search tree is also known as binary sorted tree.
11. The B-tree is also known as the balanced sort tree. The B-tree is used in external sorting.
The B-tree is not a binary tree.
12. The AVL tree is a binary search tree. It is also known as height balanced tree. It is used
to minimize the search time by keeping every node of the tree completely balanced in
terms of height. The balance factor plays an important role in the insertion and deletion
of elements in an AVL tree.
EXERCISES
A. Answer the following questions:
1. Traversing means
(a) visiting all the nodes of the list (c) randomly accessing the elements
(b) shifting all the nodes at forward (d) none of the above
2. Every tree structure must contain
(a) root node (c) both left and right branches with root
(b) either left right branch with root node (d) all of the above
3. When every no-leaf node in binary tree has filled left and right sub-trees, the tree is called
(a) strictly binary tree (c) binary search tree
(b) complete binary tree (d) none of the above
4. The last node of this binary tree contains two branches. Such a tree is called
(a) strictly binary tree (c) extended binary tree
(b) complete binary tree (d) none of the above
5. The following sequence of the traversing is called
Go to the root
Traverse the left sub-tree
Traverse the right sub-trees
(a) inorder (c) preorder
(b) postorder (d) pre-postorder
6. The following sequence of the traversing is called
Traverse the left sub tree in inorder Visit root node.
Traverse the right sub tree in inorder.
(a) inorder (c) preorder
(b) postorder (d) pre-postorder
7. The following sequence of the traversing is called
Traversing the left sub-tree in postorder style.
Traverse the right sub tree in postorder.
Visit the root node (N).
(a) inorder (c) preorder
(b) postorder (d) pre-postorder
B C
D E F G
1. Write a program to create tree structure, perform insert and delete operations.
2. Write a program to traverse the binary search tree with different traversal methods such as
inorder, preorder and postorder.
3. Construct binary tree for the following data:
Inorder: F E A C D G H B I
Postorder: E F C D H I B G A
4. Construct the binary tree for the following:
1. Inorder: 3 5 6 8 12 15 18 19
Preorder: 12 5 3 6 8 18 15 19
Postorder: 3 8 6 5 15 19 18 12
2. Preorder: A E F D J H I G B C
Inorder: F E A H J I D B G C
3. Inorder: 1 3 4 5 6 7 9 12
Postorder: 3 5 4 1 7 12 9 6
5. Determine the preorder, inorder and postorder of the following trees:
8 V
K
10
O
18
L
15 R
(1) (2)
B
89
C
45 96
H
25 67
D
F 31 77
(3) (4)
6. Write a C program to insert nodes into a binary tree and traverse them in preorder.
7. Write a program for finding the height of the tree.
8. Write a program to display all the nodes of the binary search tree and then insert data and
display it along with the previous data of the tree.
9. Write a C program to search an element from the binary search tree.
10. Write a program for implementing binary tree traversal.
else scanf("%ld",&no);
{ while(no!=0)
if(ele<btree->no) {
btree->ls=creat(btree->ls,ele); bt= insert(bt,no);
else scanf("%d",&no);
if(ele>btree->no) }
btree->rs=creat(btree->rs,ele); puts("traversing btree by
else fun_t:\n");
if(ele==btree->no) fun_t(bt);
{ }
puts("Duplicates nodes: Program struct tree*insert(struct
exited"); tree*bt,long no)
exit(0); {
} if(bt==NULL)
} {
return(btree); bt=(struct tree*)
} malloc(sizeof(struct tree));
bt->left=bt->right=NULL;
void fun1(struct tree*btree) bt->data=no;
{ }
if(btree!=NULL) else
{ {if(no<bt->data)
printf("%4ld",btree->no); bt->left=insert(bt->left,no);
fun1(btree->ls); else
fun1(btree->rs); if(no>bt->data)
} bt->right=insert(bt->right,no);
} else
if(no==bt->data)
3. {
# include <stdio.h>
puts("Duplicates nodes: Program
# include <conio.h>
exited");
struct tree
exit(0);
{
}
long data;
}
struct tree *left;
return(bt);
struct tree *right;
}
};
void fun_t(struct tree*bt)
struct tree *bt=NULL;
{
struct tree *insert(struct
if(bt!=NULL)
tree*bt,long no);
{
void fun_t(struct tree*bt);
fun_t(bt->left);
void main()
fun_t(bt->right);
{
printf("%4ld",bt->data);
long no;
}
clrscr();
}
puts("Enter integers: and 0 to
quit");
Graphs
CHAP TER O U T LIN E
10.1 Introduction 10.4 Graph Representation
10.2 Graphs 10.5 Traversal in Graph
10.3 Terminologies of Graph 10.6 Spanning Trees
10.1 INTRODUCTION
Graphs are frequently used in every walk of life. Every day we come across various kinds of graphs
appearing in newspapers or television. The countries in a globe are seen in a map. A map depends on
the geographic location of the places or cities. As such, a map is a well-known example of a graph. In the
map, various connections are shown between the cities. The cities are connected via roads, rail or aerial
network. How to reach a place is indicated by means of a graph. Using the various types of the links the
maps can be shown.
Fig. 10.1 illustrates a graph that contains the cities of India connected by means of road. Assume that
the graph is the interconnection of cities by roads. As per the graph, Mumbai, Hyderabad and Kolkata are
directly connected to all the other cities by road. Delhi is directly connected to Mumbai, Hyderabad, and
Kolkata. Delhi is connected to Chennai via Hyderabad.
Generally, we provide the address of our office/residence to a stranger who is not aware of our address
and location of city. At this juncture we use the graph for the easiest identification of our residential
location. Figure 10.2 shows the location of place by graph. By using the graph any stranger can easily
find location. For example, a pilgrim wishes to reach the Gurudwara in Nanded. The path is shown
in the figure for reaching the Gurudwara. The devotee has to reach the destination via Shivaji statue,
Mahatma Gandhi statue and then Gurudwara as per the graph. Once a map is provided, any stranger
can reach any destination by using appropriate conveyance.
Delhi
Mumbai K
Kolkata
Hyderabad
Chennai
Bengaluru
Gurudwara
In Fig. 10.2, four places are connected by road links. In the graph the road links are called as edges
and the places are called as vertices. The graph is a collection of the vertices and the edges, hence a map is
treated as graph. The following section describes the graph and relevant theories.
Like tree, graphs are nonlinear data structures. Tree nodes are arranged hierarchically from top to
bottom like a parent is placed at the top and child as successor at the lower level. Tree has some specific
structure whereas graph does not have a specific structure. It varies from application to application.
10.2 GRAPHS
O P
A graph is set of nodes and arcs. The nodes are also termed as
vertices and arcs are termed as edges. The set of nodes as per the
Fig. 10.3 is denoted as {O, P, R, S, Q}. The set of arcs in the fol- R S
lowing graph are {(O ,P), (O, R), (O, Q), (R, S), (Q, S)}. Graph
can be represented as,
G = (V, E) and V(G) = (O, Q, P, R, S) or group of vertices. Q
Similarly, E(G) = ((O, P), (O, R), (O, Q), (R, S), (Q, S)) or
group of edges. Figure 10.3 Graph
A graph is linked if there is pathway between any two nodes of the graph, such a graph is called connected
graph or else, it is non-connected graph. Figures 10.4 and 10.5 are connected and non-connected graphs, respec-
tively. In both the figures four nodes are depicted. In the latter all the nodes are not connected by links whereas
in the former case all the nodes are joined by paths or links.
Q
P Q P
S S
R R
Weighted Graph A graph is supposed to be weighted if its every edge is assigned some value which is
greater than or equal to zero, i.e. non-negative value. The value is equal to the length between two vertices.
Weighted graph is also called as network. Weighted graph is shown in A
Fig. 10.8.
4 5
Adjacent Nodes When there is an edge from one node to another
C B
then these nodes are called adjacent nodes.
5 4
Incidence In an undirected graph the edges v0 , v1 is incident on
D
nodes. In a direct graph the edge v0, v1 is incident from node v0. It is
incident to node v1. Figure 10.8 A Weighted Graph
Path A path from edges u0 to node un is a sequence of nodes u0, u1,u2, u3 .un−1, un. Here, u0 is adjacent to
u1, u1 is adjacent to u2 and un−1 is adjacent to un.
Length of Path Length of path is nothing but total number of edges included in the path from source
node to destination node.
Closed Path When first and last nodes of the path are same, A B
such path is known as closed path. In Fig. 10.9 closed path at
node A is shown.
C D
Simple Path In this path all the nodes are different with an
exception that the first and last nodes of the path can be similar. Figure 10.9 Closed Path for Node A
Cycle Cycle is a simple path. The first and last nodes are same.
A
In other words, a closed simple path is a cycle. In a digraph a path
is known as cycle if it has one or more nodes. The starting node is 4 5
connected to the last node. In an undirected graph a path is called
C B
cycle if it has at least three nodes. The starting node is connected
to last node. In the following figure path ACDBA is a closed path.
5 4
Example of a cycle is shown in Fig. 10.10.
D
Cycle Graph A graph having cycle is called cycle graph. In this case Figure 10.10 Example of a Cycle
the first and last nodes are the same. A closed simple path is a cycle.
This is same as closed path shown in Fig. 10.10.
Acyclic Graph A graph without cycle is called acyclic graph. Examples of acyclic graphs are shown in
Fig. 10.11.
Dag A directed acyclic graph is called dag after its acronym (reduction). Figure 10.12 is a graph showing
the dag.
Degree In an undirected graph, the total number of edges linked to a node is called degree of that node.
In a digraph there are two degrees for every node called indegree and outdegree. In the above Fig. 10.12,
E has two edges hence degree is 2. Similarly, D has degree three and so on.
A A
A B
C B B C C
E D
D
Indegree The indegree of a node is the total number of edges coming to that node. In Fig. 10.12, C
is receiving two edges hence, the indegree is two.
Outdegree The outdegree of a node is the total number of edges going outside from that node. In the
above Fig. 10.12 the outdegree of D is one.
Source A node, which has only outgoing edges and no incoming edges, is called a source. The indegree
of source is zero. In Fig. 10.12 the node E is the source since it does not have any incoming edges. It has
only the outgoing edges.
Sink A node having only incoming edges and no outgoing edges is called sink node. Node C in
Fig. 10.12 is a sink node because it has only incoming edges but no outgoing edges.
Pendant Node When indegree of node is one and outdegree is zero then such a node is called pendant
node.
Reachable If a path exists between two nodes it will be called reachable from one node to other node.
Isolated Node When degree of node is zero, i.e. node is not connected
A B
with any other node then it is called isolated node. In Fig. 10.13 B node
is the isolated node.
Biconnected Graph The biconnected graph is the graph which does not contain any articulation point.
P Q
U
There is no edge between nodes P and Q.
2. If there is an edge between any two nodes. Figure 10.15 Model Graph
P Q
P
Q
R
S
T
U
As per Table 10.1, there is an edge in between the nodes P and Q, P and R, and there is no edge between
nodes P and S. The symbol indicates existence of edge and indicates absence of edge between two
nodes.
From the above table one can predict the path to reach a particular node. For example, initial node is P
and the destination node is U. We have to find the path to reach node U from P.
There is no edge between P and U. Then, find out the edge for nearest node in forward direction.
By observing, we know there are two edges from P to Q and P to R. We can select either Q or R.
Suppose, we have selected node Q, again find out next nearest successive node to Q by observing
column Q. The next successive forward node will be S. Then, refer column S and it provides two edges
Q and U. The node U is our solution. Thus, by using the above table, paths between any two nodes can
be determined. The path should be P-Q-S-U. The graph can be represented by sequential representation
and linked list representation.
Adjacency Matrix The matrix can be used to represent the graph. The information of adjacent
nodes will be stored in the matrix. Presence of edges from a particular node can be determined easily.
The matrix can be represented by two-dimensional array. In a two-dimensional array [][], the first
sub-script indicates row and second, column. For example, there are five nodes in the graph then
the 0th row indicates node1 and so on. Likewise, column represents node1, node2, and so on. For
example, consider a two-dimensional array.
Nodes[j[k] P Q
1 indicates presence of edge between two nodes j and k.
0 indicates absence of an edge between two nodes j and k.
Thus, the matrix will contain only 0 and 1 values. S R
The matrix for the graph given in Fig. 10.16 would be:
Figure 10.16 An Example of Graph
P Q R S
P 0 1 0 1
Matrix X = Q 0 0 1 1
R 0 0 0 0
S 0 0 1 0
A B
In the above matrix, Mat[0][1]=1, which represents an edge
between node P and Q. Entry of one in the matrix indicates that
there is an edge and 0 for no edge. Thus, adjacency is maintained
in the matrix X. One can also represent the undirected graph with
adjacency matrix. Figure 10.17 is an undirected graph. D C
The adjacency matrix for the above graph would be as follows:
Figure 10.17 Undirected Graph
A B C D
A 0 1 0 1
Matrix X = B 1 0 1 0
C 0 1 0 1
D 1 0 1 0
A B C D
A 0 0 0 0
X= B 0 0 0 0
C 0 0 0 0
D 0 0 0 0
# include <stdio.h>
# include <conio.h>
void main()
{
int mej=l,k,beg,num, des;
int adj[20][20]={(0,0)};
char g_type;
clrscr();
printf("\nEnter number of nodes:");
scanf("%d",&num);
printf("Bnter type of graph,(d)irected or (u)ndirected:");
g_type=getch();
fflush(stdin);
if(g_type=='u') me=num*(num-1)/2;
else me=num*(num-1);
while(j<=me)
{
printf("\n Enter edges %d ( 0 to exit ): ",j);
scanf("%d %d",&beg,&des);
for(j=1;j<=num;j++)
{
for(k=1;k<=num;k++)
printf(" %d ",adj[j][k]);
printf("\n");
}
return 0;
}
OUTPUT
Enter number of nodes: 5
Enter type of graph,(d)irected or (u)ndirected: u
Enter edges 1 ( 0 to exit ): 1 1
Enter edges 2 ( 0 to exit ): 2 1
Enter edges 3 ( 0 to exit ): 2 2
Enter edges 4 ( 0 to exit ): 0 1
Invalid edges !
Enter edges 4 ( 0 to exit ): 1 2
Enter edges 5 ( 0 to exit ): 0 0
Explanation:
In this program maximum edges are calculated by using formula. If the graph is directed type, the
formula is me=num*(num-l); where, num is the total number of nodes. When the graph is
undirected then the formula is me=num*(num-l)/2.
After computing maximum edges, the user is prompted to enter edges. When 0,0 are entered
the while loop is terminated. When the entered edges are greater than num and less than or
equal to zero, message “Invalid edges” is displayed. Otherwise, adj[ beg ][ des ]=1.
DATA
Points to the
First Node
Neighboring to
Neader Node
Points to the
Next Node
P Q S
Q R S
R S
S R
Traversing in a graph is nothing but visiting each and every node of a graph. The following points are to
be noted in a graph:
1. The graph has no first node or root. Therefore, the graph can be started from any node.
2. In graph, only those nodes are traversed which are accessible from the current node. For complete
traversing of graph, the path can be determined by traversing nodes step by step.
3. In the graph, the particular node can be visited repeatedly. Hence, it is necessary to keep the track of
the status of every node whether traversed or not.
4. In graph to reach a particular node, more paths are available.
Two techniques are used for traversing nodes in a graph. They are depth first and breadth first. These
techniques have been elaborated in detail in the following sections.
A spanning tree is an undirected tree, containing only those nodes that are necessary to join all the nodes
in the graph. The nodes of spanning trees have only one path between them. In spanning trees, the
number of edges are less by one than the number of nodes (see Fig. 10.23).
P Q P Q
R S R S
I J K L M I J K
N L
Figure 10.24 Depth First Spanning Tree Figure 10.25 Breadth First Spanning
Weighted Graph When the edges of the graph contain positive values as weight it is known as weighted
graph or network.
Prim’s Algorithm In this algorithm, the traversing can start from any node and addition can be done in
spanning tree according to the weight of the edge. For example, we start with node n1, and then the next
step is to check the entire connecting path to that node and find out which path has minimum weight. Thus,
the nodes with minimum weight are added in Prim’s algorithms. Here, while program implementation it is
essential to know the weight of the edge. For example, we have two edges e1 and e2 with weights 3 and 5.
The edge e1 has minimum weight than e2 and hence, e1 will be added to spanning tree.
Example 10.2 Write a program to create minimum spanning tree using Prim’s algorithm.
# include <stdio.h>
# include <conio.h>
# include <process.h>
struct prim
{
int pred_sor;
int stat;
int distance;
};
struct node { int x; int w;};
int adjc[10][10],num;
int main()
{
int y=1,wt,counter;
struct node trees[10];
void create(void );
void show(void );
int m_tree(struct node*, int*);
clrscr();
create();
printf("Adjacency of Matrix : ");
show();
counter=m_tree(trees,&wt);
printf("Weight of spanning trees is : %d\n", wt);
printf("Edges to be included in spanning trees :");
while(y<=counter)
{
printf("%d->",trees[y].x);
printf("%d\num",trees[y].w);
y++;
}
return 0;
}
void create()
{
int y=1,m_edge,start,end,wt;
printf("Enter number of vertices :");
scanf("%d",&num);
m_edge=num*(num-1)/2;
while (y<=m_edge)
{
printf("Enter node %d(0 to Exit) : ",y);
scanf("%d %d",&start,&end);
if((start==0)&&(end==0)) break;
printf("Enter weight of this node : ");
scanf("%d",&wt);
if( start > num || end > num || start<=0 ||end<=0
{
printf("Wrong node!\n");
––y;
}
else
{
adjc[start][end]=wt;
adjc[end][start]=wt;
}
y++;
}
if(y<num-1)
{
printf("Spanning tree not possible\n");
exit(0);
}
}
void show()
{
int y=1,p;
while (y<=num)
{
for(p=1;p<=num;p++)
printf("%3d",adjc[y][p]);
printf("\n");
y++;
}
}
int m_tree(struct node trees[10],int *wht)
{
struct prim p_ion[10];
int y=1,mm,counter,cur;
int perm(struct prim *);
int uu,vv;
wht=0;
while(y<=num)
{
p_ion[y].pred_sor=0;
p_ion[y].distance = 9999;
p_ion[y].stat = 0;
y++;
}
p_ion[1].pred_sor=0;
p_ion[1].distance=0;
p_ion[1].stat = 1;
cur=1;
counter=0;
while( perm(p_ion) != 1)
{
for(y=1;y<=num;y++)
{
if(adjc[cur][y] > 0 && p_ion[y].stat == 0)
{
if(adjc[cur][y] < p_ion[y].distance)
{
p_ion[y].pred_sor = cur;
p_ion[y].distance = adjc[cur][y];
}
}
}
mm=9999;
for(y=1;y<=num;y++)
{
if(p_ion[y].stat == 0 && p_ion[y].distance < mm)
{
mm = p_ion[y].distance;
cur=y;
}
}
p_ion[cur].stat=1;
uu=p_ion[cur].pred_sor;
vv=cur,counter++;
trees[counter].x=uu;
trees[counter].w=vv;
*wht=*wht+adjc[uu][vv];
}
return(counter);
}
int perm(struct prim p_ion[10] )
{
int y=1;
while(y<=num) if( p_ion[y++].stat == 0 )
return 0;
return 1;
}
OUTPUT
Enter number of vertices : 5
Enter node 1( 0 to Exit) : 1 1
Enter weight of this node : 5
Enter node 2( 0 to Exit) : 1 1
Enter weight of this node : 3
Enter node 3( 0 to Exit) : 0 0
Spanning tree not possible
Explanation:
In this program the Structure prim is defined as follows:
struct prim
{
int pred_sor;
int stat;
int distance;
};
struct node {int x; int w;};
The function create() is used to compute maximum edges. The m_tree() function is used
to calculate the edges. The show() function is used to display the nodes.
First, all the nodes neighbouring Q are traversed, then neighbouring nodes of S and finally T
are taken into account. The adjacent node of Q is R and T is U. Similarly, the adjacent node of T
is U and S does not have any adjacent node. Hence, in this step the traversal now should be in the
following way:
P Q S T R U
Now, the new nodes obtained are R and U after traversing. The new adjacent node of R is U and U
node does not have any adjacent node. Node U has been visited in the previous case hence it must be
ignored in this step.
The search is always carried in forward direction. After reaching to U, we reach the end of the path and
further movement in forward direction is not possible. Hence, the controls go to the previous node and
again traverse through the available paths for non-traversed nodes.
In reverse direction, we get the node R and it has unvisited node. Hence, Q is taken and it gives T.
The node T gives U, but it is already visited. Therefore, control in reverse direction checks all the nodes.
It takes P and it gives node S. The sequence of traversal will be
P Q R U T S
Example 10.3 Write a program to demonstrate breadth first search and depth first search.
# include <stdio.h>
# include <conio.h>
# include <process.h>
# define num 21
int aj[num][num],tra_sed[num],count;
int main()
{
int j,x,select;
int show(void),create(void),dfs(int);
int dfs_rsn(int),bfs(int),adj_edg(int);
void nodes(void);
clrscr();
create();
for(;;)
{
printf("1. Adjacency of matrix\n");
printf("2. Depth First Search with stack\n");
printf("3. Depth First Search with recursion\n");
printf("4. Breadth First Search\n");
printf("5. Adjacent vertices\n");
printf("6. Elements\n");
printf("7. Quit\n");
printf("Enter option:");
scanf("%d",&select);
switch(select)
{
case 1:
printf("Adjacency of Matrix\n");
show();
break;
case 2:
printf("Enter Beginning node for Depth
First Search:");
scanf("%d",&x);
for(j=1;j<=count;j++)
tra_sed[j]=0;
dfs(x);break;
case 3:
printf("Enter Beginning node for Depth
First Search : ");
scanf("%d",&x);
for(j=1;j<=count;j++)
tra_sed[j]=0;
dfs_rsn(x);break;
case 4:
printf("Enter Beginning node for Breadth First Search:");
scanf("%d", &x);
for(j=1;j<=count;j++)
tra_sed[j]=0;
bfs(x);
break;
case 5:
printf("Enter node to search adjacent vertices :");
scanf("%d", &x);
printf("Adjacent Vertices :");
adj_edg(x);
break;
case 6:
nodes();
break;
case 7:
exit(0);
default:
printf("Invalid selection\n");
break;
}
}
}
create()
{
int j,m_edges,start,target;
printf("Enter number of nodes:");
scanf("%d",&count);
m_edges=count*(count-1);
for(j=1;j<=m_edges;j++)
{
printf("Enter edge %d( 0 0 to quit) :",j);
scanf("%d %d",&start,&target);
if((start==0) && (target==0))
break;
if(start > count || target > count || start<=0 || target<=0)
{
printf("Invalid edges try again !\n");
––j;
}
else aj[start][target]=1;
}
return 0;
}
show()
{
int j;
for(j=1;j<=count;j++)
{
for(j=1;j<=count;j++)
printf("%5d",aj[j][j]);
printf("\n");
} return 0;
}
dfs_rsn(int x)
{
int j;
tra_sed[x]=1;
printf(" %d ",x);
for(j=1;j<=count;j++)
printf("%d ",x);
tra_sed[x]=1;
re++;
fr++;
queue[re]=x;
while(fr<=re)
{
x=queue[fr];
fr++;
for(j=1;j<=count;j++)
{
/* Check for adjacent unvisited nodes */
if(aj[x][j]==1 && tra_sed[j]==0)
{
printf("%d ",j);
tra_sed[j]=1;
re++;
queue[re]=j;
}
}
}
return 0;
}
adj_edg(int x)
{
int j;
for(j=1;j<=count;j++)
if(aj[x][j]==1)
printf("%d ",j);
printf("\n");
return 0;
}
void nodes()
{
int j;
for(j=1;j<=count;j++)
tra_sed[j]=0;
for(j=1;j<=count;j++)
{
if(tra_sed[j]==0)
dfs_rsn(j);
}
printf("\n");
}
OUTPUT
Enter number of nodes : 5
Enter edge 1( 0 0 to quit ) : 1 1
Enter edge 2( 0 0 to quit ) : 2 2
Enter edge 3( 0 0 to quit ) : 1 5
Enter edge 4( 0 0 to quit ) : 0 0
1. Adjacency of matrix
2. Depth First Search with stack
3. Depth First Search with recursion
4. Breadth First Search
5. Adjacent vertices
6. Elements
7. Quit
Enter option : 1
Adjacency of Matrix
1 1 0 0 0
Explanation:
In this program following user defined functions are defined to perform various tasks. Create()
function when executed prompts user to enter number of edges and corresponding edge values.
When user enters edges, maximum edges are calculated. For complete details of the procedure
read the topic depth first search. The function adj_edg() calculates the edges. The function
bfs() performs breadth first search. For details, refer the topic breadth first search illustrated
in the same topic.
Example 10.4 Calculate the indegree and outdegree of the vertices of Figure 10.28.
V1 V6
V2 V7 V5
V3 V4
Figure 10.28
Table 10.2 Calculation of Indegree and Outdegree
Vertex Indegree Vertices Outdegree Vertices No. of Indegree Vertices No. of Outdegree Vertices
V1 Null V2, V7, V6 0 3
V2 V1, V7 V3 2 1
V3 V2, V7 V4 2 1
V4 V7, V3 V5 2 1
V5 V7, V4, V6 Null 3 0
V6 V1 V7, V5 1 2
V7 V1, V6 V2, V3, V4, V5 2 4
Example 10.5 Illustrate the topological sorting of the graph shown in Fig. 10.29.
V5
V2 V3
V4
V5
V2 V3
Step 3: Among the vertices V2, V3, V4, and V5, the vertex V2 has an indegree of zero. Hence, remove
all the edges which are associated with the vertex V2.. Redraw the graph and place the vertex in the
ordering set (Fig. 10.31).
Ordering set: V1,V2
V4
V5
V3
Step 4: Among the remaining vertices V3, V4, and V5, the vertex V4 has an indegree of zero. Remove
all the edges that are associated with the vertex V4, redraw the graph and place the vertex in the
ordering set (Fig. 10.32).
V3
Step 5: Among the remaining vertices V3 and V5, the vertex V3 has an indegree of zero. Remove all
the edges that are associated with the vertex V3, redraw the graph and place the vertex in the ordering
set (Fig. 10.33).
Ordering set: V1, V2, V4, V3
V5
Step 6: Only the vertex V5 is remaining the end as the indegree of vertex V5 is zero. Add the vertex V5 to
the previous ordering set. After adding the vertex V5 to the ordering set, the graph becomes empty.
Ordering set: V1, V2, V4, V3, V5
A B
C D
A B C D
A 0 1 1 0
D 0 0 0 0
Figure 10.34
In the above matrix, p[A][B] = 1 as a path exists from vertex A to B (Fig. 10.34). Similarly W[A][D] = 0
as a path does not exist from A to D. Thus, from the above matrix we can analyze that
P[i][j] = 1 if a path exists from vertex Vi to Vj
P[i][j] = 0 if a path does not exist path from Vi to Vj
Example 10.6 Determine the path matrix from the adjacency matrix (Fig. 10.35)
A B
Step 2: After multiplying the adjacency matrix with itself, we obtain a path matrix of length 2.
A B C
A ⎛0 1 1⎞ ⎛ 0 1 1⎞
B ⎜1 0 1⎟ * ⎜ 1 0 1⎟
⎜ ⎟ ⎜ ⎟
C ⎜⎝ 1 1 0⎟⎠ ⎜⎝ 1 1 0⎟⎠
⎛ 0 1 + 1 0 + 0 1 0 1 + 0⎞ ⎛ 2 1 1⎞
A 2 = ⎜ 0 0 + 1 1 + 0 1 1 0 + 0⎟ = ⎜ 1 2 0⎟
⎜ ⎟ ⎜ ⎟
⎜⎝ 0 1 + 0 1 + 0 0 1 1 + 0 ⎟⎠ ⎜⎝ 0 1 2⎟⎠
Thus, we obtain the path matrix of length 2 after multiplying the adjacency matrix with itself.
Step 3: Multiply the adjacency matrix with the obtained path matrix of length 2 from the above
matrix A2 to obtain a path matrix of length 3.
⎛ 2 1 1⎞ ⎛ 0 1 1⎞
A 2
A = A = ⎜ 1 2 0⎟ * ⎜ 1 0 1⎟
3
⎜ ⎟ ⎜ ⎟
⎜⎝ 0 1 2⎟⎠ ⎜⎝ 1 1 0⎟⎠
⎛ 0 1 + 1 2 + 0 + 1 2 + 1 + 0⎞ ⎛ 2 3 3⎞
A = ⎜ 0 + 2 + 0 1 + 0 + 0 1 + 2 + 0⎟ = ⎜ 2 1 3⎟
3
⎜ ⎟ ⎜ ⎟
⎜⎝ 0 + 1 + 2 0 + 0 + 2 0 + 1 + 0⎟⎠ ⎜⎝ 3 2 1⎟⎠
From the above path matrix of length 3, we can say as per the definition of a path matrix p[i][j]=1
if there exist a path from vertex Vi to Vj.
Step 4: To obtain the path matrix, as per the definition of a path matrix we can say that there exist
a path from Vi to Vj if p[i][j]=1. The obtained matrix A3 contains all non-zero numbers. Replace all
non-zero entries from matrix A3 to obtain the path matrix.
⎛1 1 1⎞
P = ⎜1 1 1⎟
⎜ ⎟
⎜⎝1 1 1⎟⎠
Example 10.7 Find the transitive relation of Fig. 10.36 and redraw the graph.
A B
Figure 10.36
The adjacency matrix of the above graph is as follows:
A B C
A ⎛0 1 1⎞
B ⎜1 0 1⎟
⎜ ⎟
C ⎜⎝ 1 1 0⎟⎠
Another way to represent the adjacency matrix is by using the method of sets. Let G be the graph,
which is the set of vertices and edges (those vertices and edges are selected in which there exist a path
from Vi to Vj, i.e. from one to another vertex.)
G = {(A, B), (A, C), (B, C), (B, A), (C, A), (C, B)}
So by the property of transitive relation the new set of vertices and edges are as follows:
1) ARB and BRA then ARA
2) BRC and CRB then BRB
3) CRA and ARC then CRC
After adding the new transitive relation, the set of vertices and edges is as follows in set G.
G ={(A, B), (A, C), (B, C),(B, A),(C, A),(C, B), (A, A),(B, B), (C, C)}
After performing the transitive relation on the above graph, the newly changed graph is shown in
Fig. 10.37.
A B
Figure 10.37
Example 10.8 Find the path matrix from the given graph (Fig. 10.38).
1 2
Figure 10.38
Step 1: Represent the above graph in the form of form of an adjacency matrix.
1 2 3
1 1 1 1
W0 = 0 2 0 0 1
3 1 1 0
To find W1, consider the first row and the first column. Let Pi be the columns and Q j be rows.
P1 : (1,1) P2 : (3,1)
Q1 : (1,1) Q2: (1,2) Q3:(1,3)
As 3R1 and 1R3 then 3R3 (by the transitive relation property) add to the next matrix W1.
Step 2: Thus, W1 is just w0 with one new position (3,3).
1 2 3
1 1 1 1
W1 = 2 0 0 1
3 1 1 1
To find W2, consider the second row and second column. Let Pi be the columns and Q j be rows.
P1: (1,2) P2 : (3,2)
Q1: (2, 1)
By the property of transitive relation, 2R1 and 1R2 then 2R2, add to the next matrix W2.
Step 3: Thus, W2 is just W1 with a new 1 position (2,2)
1 2 3
1 1 1 1
W2 = 2 0 1 1
3 1 1 1
To find W2, consider the second row and second column. Let Pi be the columns and Q j be the
rows.
P1: (1,3) P2: (2,3) P3: (3,3)
Q1: (3,1) Q2: (3,2) Q3: (3,3)
By the property of transitive relation, 2R3 and 3R1 then 2R1, add to the next matrix W3.
Step 4: Thus W3 is just as W2 with a new 1 position (2,1). Thus, the final path matrix is as
follows.
1 2 3
1 ⎛1 1 1⎞
W3 = 2 ⎜1 1 1⎟
⎜ ⎟
3 ⎜⎝1 1 1⎟⎠
SUMMARY
1. In this chapter graph theory is described. Also, its implementation is illustrated with
numerous examples.
2. A graph is a set of nodes and arcs. The nodes are also termed as vertices and arcs are
termed as edges.
3. A graph is linked if there is a pathway between any two nodes of the graph, such a graph
is called connected graph or else it is non-connected graph.
4. The graph can be implemented by linked list or array.
5. Explanation on adjacency matrix is provided in the form of matrix. The information of
adjacent nodes can be stored in the matrix. Presence of edges from a particular node can
be determined easily. The matrix can be represented by two-dimensional array. In two-
dimensional, array [][] the first subscript.
6. Traversal in a tree is also explained in this chapter. The node can be visited repeatedly.
Hence, it is necessary to keep the track of node as to whether it has been visited or not.
7. Spanning tree is discussed in detail. Two methods have been elaborated together with
examples. Breadth first and depth first spanning trees have been illustrated in detail.
EXERCISES
A. Answer the following questions:
1. Write a program to demonstrate breadth first search and depth first search.
2. Write a program to demonstrate spanning tree.
B C
A D
F E
Sorting
CHAP TER O U T LIN E
11.1 Introduction 11.7 Tree Sort
11.2 Sorting 11.8 Merge Sort
11.3 Insertion Sort 11.9 Heap Sort
11.4 Selection Sort 11.10 Radix Sort
11.5 Bubble Sort 11.11 Partition Exchange Sort
11.6 Quick Sort
11.1 INTRODUCTION
In our daily life, we keep the information in particular order. This helps in accessing any part of it easily.
Likewise, in database programs, large data is maintained in the form of records. It would be very difficult
if data or records were unsorted. It is very difficult and time consuming to find a particular record if data
or records are randomly stored. The process of sorting is essential for database applications.
Sorting is a process in which records are arranged in ascending or descending order. A group of fields is
called record. For example, consider the following structure:
struct employee
{
char *name;
int age;
float height;
} s;
We are familiar with the above declarations and definitions. The object s contains three fields as
described in the structure. The database is a collection of such fields. Each record contains the above
fields, and it may vary depending upon the structure defined. The sorting can be done on any one or a
combination of one or more fields. Suppose we sort the records based on name, then the field name will
be the key field. A record is quickly searched in a sorted database. Thus, sorting is a very useful concept
of data structure.
11.2 SORTING
As already defined in the previous section sorting is a process in which records are arranged in ascending
or descending order. In real life we come across several examples of sorted information. For example, in
telephone directory the names of the subscribers and their phone numbers are written in alphabetial order.
The records of the list of these telephone holders are to be sorted by their names. By using this directory,
we can access the telephone number and address of the subscriber very easily.
Bankers or businesspersons sort the currency denomination notes received from customers in the
appropriate form. Currency denominations of Rs 1000, 500, 100, 50, 20, 10, 5, and 1 are separated first
and then separate bundles are prepared.
While we play the cards, initially the cards appear random but we keep them in ascending or descending
order. In most of the offices the officials keep the files in a specific order for tracing them easily in future.
Even at our homes, many times we keep the utensils in particular order such as increasing or decreasing
height. This helps us in accessing a particular item without difficulty. The sort method has great impact
on data structures in our dally life.
For example, consider the five numbers 5, 9, 7, 4, 1.
The above numbers can be sorted in ascending or descending order.
The representation of these numbers in
Ascending order (0 to n): 1 4 5 7 9
Descending order (n to 0): 9 7 5 4 1
Similarly, alphabets can also be sorted.
Consider the alphabets B, A, D, C, E. These can be sorted in ascending or descending order.
Ascending order (A to Z): A B C D E
Descending order (Z to A): E D C B A.
In insertion sort the element is inserted at appropriate place. For example, consider an array of n elements.
In this type, swapping of elements is done without taking any temporary variable. The greater numbers
are shifted towards the end of the array and smaller are shifted to beginning. Here, a real life example of
playing cards can be cited. We keep the cards in increasing order. The card having least value is placed
at the extreme left and the largest one at the other side. In between them the other cards are managed in
ascending order.
Consider an example of playing cards. The playing cards having values 4, 6, 2, 1, 8 can be sorted
according to the method described above, as
Figure 11.1 illustrates the insertion sort.
Initial List 4 6 2 1 8
Pass 1 4 6 2 1 8
Pass 2 4 6 2 1 8
Pass 3 2 4 6 1 8
Pass 4 1 2 4 6 8
Example 11.1 Write a program to sort array elements using insertion sort.
# include <stdio.h>
# include <conio.h>
void main()
{
int nums[20],p=0,q=1,r,num;
clrscr();
printf("Enter number of elements:");
scanf("%d",&num);
printf("\n Enter %d elements:",num);
while(p<num)
scanf("%d", &nums[p++]);
while(q<num)
{
r=nums[q];
for(p=q-1;p>=0 && r<nums[p];p––)
nums[p+1]=nums[p];
nums[p+1]=r;
printf("Element %d inserted in appropriate place",r);
p=0;
while(p<num)
printf("%d ", nums[p++]);
printf("\n");
q++;
}
printf("Sorted elements are : ");
for(p = 0; p < num; p++)
printf("%d ", nums[p]);
}
OUTPUT
Enter number of elements: 5
Enter 5 elements: 4 6 2 1 8
Element 6 inserted in appropriate place 4 6 2 1 8
Element 2 inserted in appropriate place 2 4 6 1 8
Element 1 inserted in appropriate place 1 2 4 6 8
Element 8 inserted in appropriate place 1 2 4 6 8
Sorted elements are: 1 2 4 6 8
Explanation:
In this program, the nested while loops are used. The variable q is initialized with 1 and p
with 0. The variable r inside the while loop is used to take elements from array and the same
element is compared with remaining elements. The ‘for’ loop inside the while loop executes
if p>=zero and r (recently assigned value of array element) less than other elements, the
insertion is carried out.
Example 11.2 Write a program to sort given list of elements by using insertion sort. Use pointer.
# include <stdio.h>
# include <conio.h>
void main()
{
int ls[5],limit,*pt,p=0,q=0,tem;
pt=&ls[0];
clrscr();
printf("How many numbers would you like to sort?-");
scanf("%d",&limit);
printf("Enter %d elements:-",limit);
while(q<limit)
scanf("%d",&ls[q++]);
for(q=1;q<limit;q++)
{
tem=*(pt+q);
for(p=q-1;p>=0 && tem<*(pt+p);p––)
*(pt+p+1)=*(pt+p);
*(pt+p+1)=tem;
}
OUTPUT
How many numbers would you like to sort?-4
Enter 4 elements:-12 3 1 2
Sorted elements are:- 1 2 3 12
Explanation:
In this program, the limit or the array is made available with variable limit. The variable *pt is
used for storing the starting address of the array element. With address location list is sorted
by using the insertion sort method.
Time Complexity of Insertion Sort in Worst Case Consider an example to sort the 5 elements 9, 8, 7, 6
and 5. The comparisons are shown in the different passes in Fig. 11.2.
Number of
Comparisons
P=1 9 8 7 6 5 1
P=2 8 9 7 6 5 2
P=3 7 8 9 6 5 3
P=4 6 7 8 9 5 4
Total
Sorted 5 6 7 8 9 10
Comp
List
The number of comparisons in worst case can be calculated by referring to Fig. 11.2. The comparisons
in the different passes are made as follows:
1. In pass =1 only one comparison is made between the elements 9 and 8.
2. Pass =2 contains the two comparisons the element 7 is compared with 9 and 8.
3. Three comparisons are made in the pass=3. The element 6 is compared with 7, 8 and 9.
4. Four comparisons are made in the pass=4. The element 5 is compared with 6, 7, 8 and 9.
The total comparisons in the sorting method in worst case = 1+2+3+4=10.
Thus, theoretically f(n) = n(n–1)/2 =5(5–1)/2=10.
Practical values of comparisons are equal to the theoretical value.
The selection sort is nearly the same as exchange sort. Assume, we have a list containing n elements. By
applying selection sort, the first element is compared with all remaining (n–1) elements. The smallest ele-
ment is placed at the first location. Again, the second element is compared with remaining (n–2) elements.
If the item found is lesser than the compared elements in the remaining (n–2) list then the swap operation
is done. In this type, entire array is checked for the smallest element and then swapped.
Figure 11.3 illustrates the process of sorting elements by selection sort. In each pass, one element is
sorted and kept at the left. Initially, the elements are temporarily sorted and after next pass, they are per-
manently sorted and kept at the left. Permanently sorted elements are covered with squares and temporar-
ily with circles. Element inside the circle “O” is chosen for comparing with the other elements marked in
a circle and sorted temporarily. Sorted elements inside the square “” are shown.
No. 1 2 3 4 5
Pass
1 2 6 4 8 5
2 2 6 4 8 5
3 2 4 6 8 5
4 2 4 5 8 6
5 2 4 5 6 8
SList 2 4 5 6 8
Consider the elements 2, 6, 4, 8, and 5 for sorting under selection sort method.
1. In pass 1, select element 2 and check it with the remaining elements 6, 4, 8, and 5. There is no smaller
value than 2, hence the element 2 is placed at the first position.
2. Now, select element 6 for comparing with remaining (n–2) elements, i.e. 4, 8, and 5. The smaller
element is 4 than 6. Hence, we swap 4 and 6 and 4 is placed after 2.
3. Again, we select the next element 6 and compare it with the other two elements 8 and 5 and swapping
is done between 6 and 5.
4. In the next pass element 8 is compared with the remaining one element 6. In this case, 8 is swapped with 6.
5. In the last pass the 8 is placed at last because it is the highest number in the list.
Time Complexity The performance of sorting algorithm depends upon the number of iterations and
time to compare them. The first element is compared with the remaining n–1 elements in pass 1. Then,
n–2 elements are taken in pass 2. This process is repeated until the last element is encountered. The
mathematical expression for these iterations will be equal to (n–1)+(n–2)+ ... +(n– (n–1)). Thus, the
expression become n*(n–1)/2.
Thus, the number of comparisons is proportional to (n2). The time complexity of selection sort is
O(n2).
Comparison with Other Methods 1. This method requires more number of comparisons than quick sort,
tree sort. 2. It is easier to implement than other methods such as quick sort, tree sort. 3. The performance
of the selection sort is quicker than bubble sort.
Example 11.3 Write a program to sort elements of an array using selection sort.
# include <stdio.h>
# include <conio.h>
void main()
{
int nums[15], p=0,q,r,num,t,small;
clrscr();
printf("How many elements to sort ? : ");
scanf("%d",&num);
while(p<num)
scanf("%d", &nums[p++]);
for(p=0; p<num-1;p++)
{
small = p;
for(r=p+1; r<num;r++)
if(nums[small] > nums[r])
small = r ;
if( p != small )
{
t = nums[p];
nums[p] = nums[small];
nums[small] = t ;
}
printf("\nAfter %d pass elements are: ",p+1);
q=0;
while(q<num)
printf(" %d ", nums[q++]);
}
printf("\nSorted elements are: ");
p=0;
while(p < num)
printf("%d ", nums[p++]);
}
OUTPUT
How many elements to sort? : 5
Enter 5 elements: 8 6 4 2 5
After 1 pass elements are: 2 6 4 8 5
After 2 pass elements are : 2 4 6 8 5
After 3 pass elements are : 2 4 5 8 6
After 4 pass elements are : 2 4 5 6 8
Sorted elements are: 2 4 5 6 8
Explanation:
This program of selection sort uses nested loops. Using nested loops array elements are
compared with one another. The value of loop variable p is assigned to variable small.
The inner loop executes from (p+1) to num, in which the number present at array position
nums[small] if greater than nums[r], the location indicated by r is assigned to small. The
next if() block carries swapping of elements. Thus, all array elements are compared and
necessary swapping is done.
Bubble sort is a commonly used sorting algorithm. In this type, two successive elements are compared and
swapping is done if the first element is greater than the second one. The elements are sorted in ascending
order. It is easy to understand but it is time consuming.
Bubble sort is an example of exchange sort. In this method repetitively comparison is performed between
the two successive elements and if essential swapping of elements is done. Thus, step-by-step entire array
elements are checked. It is different from the selection sort. Instead of searching the minimum element
and then applying swapping, two records are swapped instantly upon noticing that they are not in order.
The Fig. 11.4 illustrates the exchange sort.
Initial List 9 5 1 4 3
Pass 1 5 1 4 3 9
Pass 2 1 4 3 5 9
Pass 3 1 3 4 5 9
Let us consider the elements 9, 5, 1, 4, and 3 for sorting under bubble sort.
1. In pass 1, first element 9 is compared with its next element 5. The next element is smaller than 9.
Hence, it is swapped. Now, the list is 5, 9, 1, 4, 3. Again 9 is compared with the next element 1. The
next element is smaller than 9. Hence, swapping is done. The same procedure is done with 4 and 3
and at last we get the list as 5, 1, 4, 3, 9.
2. In pass 2, the elements of pass 1 are compared. The first element 5 is compared with its next element
1, 5 and 1 are swapped because 1 is smaller than 5. Now, the list becomes 1, 5, 4, 3, 9. Next, 5 is com-
pared with element 4. Again, 5 and 4 are swapped. This process is repeated until all successive elements
are compared and if the succeeding number is smaller than the present number then the numbers are
swapped. The final list at the end of this pass is 1, 4, 3, 5, 9.
3. In pass 3, the first element 1 is compared with the next element 4. The element 4 is having the higher
value than the first element 1, hence they remain at their original positions. Next, 4 is compared with
the subsequent element 3 and swapped due to smaller value of 3 than 4.
4. At last, the sorted list obtained is 1, 3, 4, 5, 9.
Example 11.4 Write a program to sort a given array by using the bubble sort.
# include <stdio.h>
# include <conio.h>
void main()
{
int a[5],i,j,temp;
clrscr();
printf("Enter the elements of an array : ");
for(i=0;i<5;i++)
scanf("%d",&a[i]);
for(i=0;i<5;i++)
{
for(j=i;j<4;j++)
{
if(a[i]>a[j+1])
{
temp=a[i];
a[i]=a[j+1];
a[j+1]=temp;
}
}
}
printf("Sorted elements in ascending order : ");
for(i=0;i<5;i++)
printf(" %d ",a[i]);
}
OUTPUT
Enter the elements of an array: 9 5 1 4 3
Sorted elements in ascending order: 1 3 4 5 9
Explanation:
In this program an array of five elements is declared. The variable j and i are loop variables. The
variable i is used in the outer loop and j is used in the inner loop. The ‘if ’ statement checks the
number, it compares every current element denoted by ith location with (j+1)th element. If the
number ith is greater, it is assigned to temp. The smaller number is assigned to ith location and temp
(holds larger number) assigned to (j+1)th location. Thus, by exchange elements are sorted.
Time Complexity The performance of bubble sort in worst case is n (n–1)/2. This is because in the
first pass n–1 comparisons or exchanges are made; in the second pass n–2 comparisons are made. This is
carried out until the last exchange is made. The mathematical representation for these exchanges will be
equal to (n–1)+(n–2)+ ... +(n– (n–1)). Thus, the expression becomes n*(n–1)/2.
Thus, the number of comparisons is proportional to (n2). The time complexity of bubble sort is 0 (n2).
It is also known as partition exchange sort. It was invented by CAR Hoare. It is based on partition.
Hence, the method falls under divide and conquer technique. The main list of elements is divided into
two sub-lists. For example, a list of X elements are to be sorted. The quick sort marks an element in a
list called as pivot or key. Consider the first element J as a pivot. Shift all the elements whose value is
less than J towards the left and elements whose value is greater than J to the right of J. Now, the key
element divides the main list into two parts. It is not necessary that selected key element must be in the
middle. Any element from the list can act as key element. However, for best performance preference
is given to middle elements. Time consumption of the quick sort depends on the location of the key
in the list.
Consider the following example in which five elements 8, 9, 7, 6, 4 are to be sorted using quick sort.
Figure 11.5 illustrates it.
Pass 1
Iteration 1 8 9 7 6 4
Iteration 2 4 9 7 6 8
Iteration 3 4 8 7 6 9
Iteration 4 4 6 7 8 9
Consider pass 1 under which the following iterations are made. Similar operations are done in pass 2,
pass 3, etc.
In iteration 1 the first element 8 is marked as pivot one. It is compared with the last element whose
value is 4. Here, 4 is smaller than 8 hence the numbers are swapped. Iteration 2 shows the swapping
operation.
In iteration 2, 8 and 9 are compared and swapping is done after iteration 2.
In iteration 3, 8 and 6 are compared and necessary swapping is done. After this, it is impossible to
swap. Hence, the position of 8 is fixed. Because of fixing the position of 8 the main list is divided into
two parts. Towards the left of 8 elements smaller than 8 are placed and towards the right greater than 8
are placed.
Towards the right of 8 only one element is present hence there is no need of further swapping. This may
be considered under pass 2.
However, towards the left of 8 there are three elements and these elements are to be swapped as per the
procedure described above. This may be considered under pass 3.
Example 11.5 Write a program to sort the array elements using quick sort.
# include <stdio.h>
# include <conio.h>
void main()
{
int nums[15],num,j=0;
int q_sort(int *,int,int);
int show(int *,int,int);
clrscr();
printf("Enter the number of elements : ");
scanf("%d",&num);
printf("\n Enter %d elements : ",num);
while(j<num)
scanf("%d",&nums[j++]);
q_sort(nums,0,num-1);
printf("Sorted list is :\n");
show(nums,0,num-1);
}
q_sort(int items[],int lower,int upper)
{
int key,temp,l,r,key_placed=0;
int show(int *,int,int);
l=lower;
r=upper;
key=lower;
if(lower>=upper) return 0;
while(key_placed==0)
{
while(items[key]<=items[r] && key!=r)
r––;
if(key==r) key_placed=1;
if(items[key] > items[r])
{
temp=items[key];
items[key]=items[r];
items[r]=temp;
key=r;
}
while(items[key]>=items[l] && l!=key) l++;
if(key==l) key_placed=1;
if(items[key] < items[l])
{
temp=items[key];
items[key]=items[l];
items[l]=temp;
key=l;
}
}
printf(" * Key Placed is %d -> ",items[key]);
show(items,lower,upper);
printf("\n");
q_sort(items,lower,key-1);
q_sort(items,key+1,upper);
return 0;
}
show(int items[],int lower,int upper)
{
while(lower<=upper)
printf("%d ",items[lower++]);
return 0;
}
OUTPUT
Enter the number of elements: 5
Enter 5 elements: 8 9 7 6 4
* Key Placed is 8 -> 4 6 7 8 9
* Key Placed is 4 -> 4 6 7
* Key Placed is 6 -> 6 7
Sorted list is : 4 6 7 8 9
Explanation:
In this program the functions q_sort() and show() are defined. In q_sort() elements
are passed by value and address. The base address of an array nums is passed to function
q_sort(). The variable num holds total number of elements. The q_sort() function is
invoked recursively. In the first execution of q_sort(), value 0 is taken as lower key and (n-1)
is taken as upper key. If value of lower is greater than upper, the q_sort() function terminates.
The value of upper variable is assigned to r. The value of r is decremented continuously until it
reaches to key value. When the value of variables key and r are same the key is placed to one
and the while loop is terminated.The ‘if()’ statement carries swapping if element positioned at
value key is greater than element positioned at value r. The same procedure is carried out when
key is placed at one and swapping is carried out.
Time Complexity The efficiency of quick sort depends upon the selection of pivot. The pivot can
bifurcate the list into compartments. Sometimes, the compartments are having the same sizes or dissimilar
sizes. Assume a case in which pivot is in the middle. Thus, the total elements towards left of it and right
are equal. We can express the size of list with the power of 2. The mathematical representation for the size
is n=2s. The value of s can be calculated as log2 n.
After the first pass is completed there will be two compartments having equal number of elements, i.e.
n/2 elements are on the left side as well as right side. In the subsequent passes, four portions are made and
each compartment contains n/4 elements. In this way, we will be proceeding further until the list is sorted.
The number of comparisons in different passes will be as follows:
Pass 1 will have n comparisons. Pass 2 will have 2*(n/2) comparisons. In the subsequent passes will
have 4*(n/4), 8*(n/8) comparisons and so on. The total comparisons involved in this case would be
0 (n)+0 (n)+0 (n)+ ... +s. The value of expression will be 0 (n log n). Thus, time complexity of quick sort
is 0 (n log n).
In binary tree, we know that the elements are inserted according to the value greater or less in between
node and the root in traversing. If the value is less than traversing node, insertion is done at left side. If the
value is greater than traversing node, it is inserted at right side. The elements of such tree can be sorted.
This sorting involves creation of binary tree and then in order traversing is done. Few examples are illus-
trated based on the tree such as inorder tree and heap sort.
Consider the list containing elements 1, 3, 4, 2, 9 for sorting. Figure 11.6 illustrates the inorder sort.
1. At first, element 1 is inserted in the tree as root node and element 3 is inserted at right to the root
because the inserted node is larger than the root. Figure 11.6 (a) explains it.
2. The element 4 is inserted at the right of the node 3 because it is larger than it. Insertion of element 4
is shown in Fig. 11.6 (b).
3. As per Fig. 11.6 (c) element 2 is inserted at the left of the node 3 due to lesser value.
4. At last, the element 9 is inserted at right of the node 4. This is shown in Fig. 11.6 (d).
5. The final binary tree is shown in Fig. 11.6 (e). Using the inorder traversing the sorted list is obtained.
1 1 1
3 3 3
4 2 4
1 1
3 3
2 4 2 4
9 9
(d) (e)
Example 11.6 Write a C program to insert nodes into a binary tree and to traverse in inorder.
# include <stdio.h>
# include <malloc.h>
# include <conio.h>
# include <process.h>
struct nodes
{
int data;
struct nodes *ls;
struct nodes *rs;
}*tree;
void main()
{
int number;
int insert(int);
int inorder(struct nodes *);
int search(int ele,struct nodes **p,struct nodes **l);
tree=0;
clrscr();
printf("Enter the numbers and 0 to exit :-");
while(1)
{
scanf("%d",&number);
if(number==0)
break;
insert(number);
}
printf("Inorder of the tree is as:-");
inorder(tree);
}
insert(int ele)
{
struct nodes *t,*father,*m_loc;
search(ele,&father,&m_loc);
if(m_loc!=0)
{
printf("Item already present");
return 0;
}
t=(struct nodes *)malloc(sizeof(struct nodes));
t->data=ele;
t->ls=0;
t->rs=0;
if(father==0)
tree=t;
else
if(ele<father->data)
father->ls=t;
else
father->rs=t;
return 0;
}
OUTPUT
Enter the numbers and 0 to exit :-12 3 53 5 0
Inorder of the tree is as:-3 5 12 53
Explanation:
In this program, insert() and inorder()are user defined functions. The insert()
function is invoked to insert the value in the binary tree. When entered number is zero, the
insertion operation is terminated. The inorder() is used for traversing the binary tree. This
function is used for getting the sorted list.
Merging is a process in which two lists are merged to form a new list. The new list is termed as the sorted list.
Before merging, individual lists are sorted and then merging is done. The procedure is very straightforward.
Consider two arrays containing integer elements. The elements of the first array are added successively.
The result of the addition of all the elements of the first array is added with the successive elements of the
second array. Thus, one obtains the summation of all the elements of two arrays. Initially, sum is assumed
to 0. Successively, addition of elements is carried out with the sum. It is then compared with the elements
of the two arrays. In case there is no match, the assumed value is incremented till it matches the array
element value (ascending order). This process is repeated till the assumed value reaches the obtained sum.
A program on merge sort is discussed below.
Example 11.7 Write a program to create two arrays containing integer elements. Sort and store
the elements of both the arrays in the third list.
# include <stdio.h>
# include <conio.h>
# include <math.h>
void main()
{
int m,n,p,sum=0;
int a[5],b[5],c[10];
clrscr();
printf("\n Enter elements for first list : ");
for(m=0;m<5;m++)
{
scanf("%d",&a[m]);
if(a[m]==0)
m--;
sum=sum+abs(a[m]);
}
printf("\n Enter elements for second list : ");
for(n=0;n<5;n++)
{
scanf("%d",&b[n]);
if(a[n]==0) n--;
sum=sum+abs(b[n]);
}
p=n=m=0;
m=m-sum;
while(m<sum)
{
for(n=0;n<5;n++)
{
if(m==a[n] || m==b[n])
c[p++]=m;
if(m==a[n] && m==b[n])
c[p++]=m;
}
m++;
}
Output:
Enter elements for first list: 1 4 2 6 7
Enter elements for second list: 9 5 3 0 8
Merged sorted list :
0 1 2 3 4 5 6 7 8 9
Explanation:
In the above program, three arrays a[], b[] and c[] are declared. Using for loop, elements
in a[] and b[] are declared. The sum of all the ten elements entered in both the arrays is taken
as the variable sum. The while and nested for loop checks the corresponding elements of
both the lists:
a) If one of the corresponding elements is the same, that element is stored in the c[]
array.
b) If both the corresponding elements are same, they are stored in successive locations in
the c[].
The value of m is initially zero. The sum obtained is again subtracted from m. This is because
negative numbers have to be considered while sorting. For example, the value of sum = 20. In
real execution, the sum may be different depending on the integers entered.
Value of m would be m = –20 (as m = m – 20 when m = 0)
Thus, in the while loop, the value of m varies from –20 to 20. All the entered elements
are covered in this range. The value of m changes from –20 to 20, i.e. –20, –19 up to +20 in
ascending order. Thus, the same order is applied while saving element in c[].
Alternately, the process of merge sort can be performed as follows: In the following program, the elements
of two arrays are compared position-wise. The element which appears to be smaller is placed in the third
array. If all the elements of any one of the arrays are compared completely, then elements from the array con-
taining large elements are placed in the third array. The following program is based on the above concept.
# include <stdio.h>
# include <conio.h>
void main()
{
int i=0,j=0,k=0,n1,n2;
int a[]={6,9,17,23};
int b[]={7,16,45,48};
static int c[30];
clrscr();
else
{
c[k]=b[j];
j++;
}
k++;
}
while(i<4)
{
c[k]=a[i];
i++;
k++;
}
while(j<4)
{
c[k]=a[j];
j++;
k++;
}
getche();
}
Output:
Elements after merge sort are as follows: 6 7 9 16 17 23 17 23
Alternately, the merge sort of array elements can be done using the following method.
Consider a list of 9 elements. They are as follows:
21, 12, 34, 3, 7, 11, 9, 23, 4
Pass 1: In merge sort, the list is first divided into pairs containing two elements. These can be shown
as follows. If numbers are even, then exact pairs are possible. In this case, nine numbers are assumed for
selection. Therefore, 5 pairs are possible. They are as follows.
21 12 34 3 7 11 9 23 4
After dividing the list into pairs, sort the elements of each pair.
12 21 3 34 7 11 9 23 4
Pass 2: Merge the pairs after sorting in the first pass. The elements appear as follows.
12 21 3 34 7 11 9 23 4
Sort the elements of the above merged pairs. The elements will be as follows after sorting.
3 12 21 34 7 9 11 23 4
Pass 3: Again merge two groups as follows.
3 12 21 34 7 9 11 23 4
Sort the elements of the merge groups.
3 7 9 11 12 21 23 34 4
Pass 4: Merge element 4 with the remaining elements.
3 7 9 11 12 21 23 34 4
All the elements get sorted in the last pass.
3 4 7 9 11 12 21 23 34
In the following program, we divide the elements of an array recursively in smaller groups and perform
sorting operations.
Example 11.9 Write a program to implement merge sort based on the above concept.
# include <stdio.h>
# include <conio.h>
int a[5],b[5];
void merge(int,int,int);
void merge_sort(int,int);
void main()
{
int i,n;
clrscr();
printf(“\nEnter the number of elements of the array:”);
scanf(“ %d”,&n);
{
scanf(“ %d”,&a[i]);
}
merge_sort(0,n-1);
printf(“\nElements after Merge-sort are as follows:”);
for(i=0;i<n;i++)
{
printf(“ %d”,a[i]);
}
getche();
}
void merge_sort(int low,int high) /*low = initial position && high = Final
position */
{
int mid;
if(low<high)
{
mid=(low+high)/2;
merge_sort(low,mid);
merge_sort(mid+1,high);
merge(low,mid,high);
}
}
void merge(int low,int mid,int high)
{
int h=low,i=low,j=mid+1,k;
while((h<=mid)&&(j<=high))
{
if(a[h]<=a[j])
{
b[i]=a[h];
h++;
}
else
{
b[i]=a[j];
j++;
}
i++;
}
if(h<=mid)
{
for(k=h;k<=mid;k++)
{
b[i]=a[k];
i++;
}
}
else
{
for(k=j;k<=high;k++)
{
b[i]=a[k];
i++;
}
for(k=low;k<=high;k++)
{
a[k]=b[k];
}
}
Output:
Enter the number of elements of the array: 5
Enter the elements of the array: 1 7 3 0 5
Elements after merge sort are as follows: 0 1 3 5 7
In heap sort, we use the binary tree in which the nodes are arranged in specific prearranged order. The
rule prescribes that each node should have bigger value than its child node. The following steps are to be
followed in heap sort:
1. Arrange the nodes in the binary tree form.
2. Node should be arranged as per the specific rules.
3. If the inserted node is bigger than its parent node then replace the node.
4. If the inserted node is lesser than its parent node then do not change the position.
5. Do not allow entering nodes on right side until the child nodes of the left are fulfilled.
6. Do not allow entering nodes on left side until the child nodes of the right are fulfilled.
7. The procedure from step 3 to step 6 is repeated for each entry of the node.
Consider the numbers 56, 23, 10, 99, 6, 19, 45, 45, 23 for sorting using heap. Sorting process is
illustrated in Fig. 11.7.
1. At first, we pickup the first element 56 from the list. Make it as the root node.
2. Next, take the second element 23 from the list. Insert this to the left of the root node 56. Refer the
Fig. 11.7 (2).
3. Then, take the third element 10 from the list for insertion. Insert it to the right of the root node.
4. Take the fourth element 99. Insert it to the left side node 23. The inserted element is greater than the parent
element, hence swap the 99 with 23. But the parent node 56 is smaller than the child node 99 hence 99
and 56 are swapped. After swapping 99 becomes the root node. This is shown in Fig. 11.7 (4) and (5).
5. Consider the next element 6 to insert in the tree. Insert it at left side. There exists left node, hence
insert it to the right of the 56. Refer Fig. 11.7 (6).
6. Element 19 is inserted to the right side of 99 because the left side gets full. Insert the element 19 to the
right node 10. However, the parent element is lesser than the child, hence swaps 19 with 10.
7. Now, element 45 is to be inserted at the right side of 19. However, the parent element is having the
value lesser than the child element hence swap the 45 with 19. Refer the Fig. 11.7 (9) and (10).
8. Now, the right side is fully filled hence add the next element 45 to the left. The element 45 is inserted
at the last node of left, i.e. 23. However, the parent element is having the value lesser than the child
element hence swap 45 with 23. Refer the Fig. 11.7 (11) and (12).
9. Insert the last element 23 to the left side node, i.e. 45. The left of the 45 is already filled hence insert
the element 23 at the right of the 45. Refer the Fig. 11.7 (13).
10. At last, we get a sorted heap tree, as shown in Fig. 11.7 (13).
Time Complexity We know that the depth of the complete binary tree is (log2n). In worst case, the
number of comparisons in each step would be equal to the depth of the tree. The number of comparisons
for the above observation would be O (n log2n). Thus, the number of comparisons in this method would
be O (n log2 n).
56 56 56
23 23 10
(1) (2) (3)
56 99
23 10 56 10
99 (4) 23 (5)
99 99
56 10
56 10
23 6
23 6 19
(6) (7)
99 99
56 19 56 19
23 6 10 23 6 10 45
(8) (9)
99
56 45
23 6 10 19
(10)
99
56 45
45 6 10 19
23
(11)
99 99
56 45 56 45
45 6 10 19 45 6 10 19
23 23 23
(12) (13)
The radix sort is a technique, which is based on the position of digits. The number is represented with
different positions. The number has units, tens, hundreds positions onwards. Based on its position the
sorting is done. For example, consider the number 456, which is selected for sorting. In this number 6 is at
units position and 5 is at tens and 4 is at the hundreds position. 3 passes would be needed for the sorting
of this number with this procedure. In the first pass, we place all numbers at the unit place. In the second
pass all numbers are placed in the list with consents to the tens position digit value. Also, in the third pass
the numbers are placed with consent to the value of the hundreds position digit.
For example:
23,46,12,34,45,49,58,38,10,21.
In the first pass, the numbers are placed as per the value of the unit position digit. First, take number
23, which is present at the first in the list. Insert 23 in the packet labelled 3. Second number 46 is
inserted in packet 6. 12 is inserted in the packet 2. The number 34 is inserted in the packet 4. 45 in the
packet 5, 46 in the packet 6, 58 in the packet 8, 38 is also in the packet 8, 10 is in the packet 0 and 21
is in the packet 1. The numbers are inserted in the packets as per the sequence of that number in the
original list.
In first pass sort the elements as per the value of the unit place digit. The operation is shown in the
above Table 11.1. After the first pass we get the following list as:
10,21,12,23,34,45,46,58,38,49.
Perform the second pass operation on the list, which is sorted list after the first pass. As per the values
of the digit of the tens position, the place of the elements is fixed. At first, take the first element 10 of the
list, which is obtained after the first pass. Insert 10 into the packet 1; insert 21 in the 2(second) packet.
This procedure is repeated until the end of the list. In the second pass, the numbers are as:
Pass 49
12 23 38 46
2nd 10 21 34 45 58
Packets for unit place 0 1 2 3 4 5 6 7 8 9
Table 11.2 shows the second pass for sorting the numbers. The sorted list after the second pass is as:
10,12,21,23,34,38,45,46,49,58.
By appropriately placing numbers, the sorted list of the items or elements is obtained. In this example
of radix sort, two passes are required for complete sorting. The number of passes depends upon the length
of the greatest number in the list. If the list contains the numbers greater than 99, then the number of
passes must be greater than 3 or equal to 3.
Time Complexity Number of comparisons require for radix sort algorithm in the worst case f(n)=O(n2)
and in best case f(n)= O(n log2n).
Example 11.10 Write a program to sort the given single digit numbers by using the radix sort.
# include <stdio.h>
# include <conio.h>
void main()
{
static int bk[10][5],k1,k2,k3,k4,k5,k6,k7,k8,k9,k0,p;
int i,j,no_p,list[5],slist[5];
clrscr();
printf("Enter the one digit elements:-");
for(i=0;i<5;i++)
scanf("%d",&list[i]);
for(i=0;i<5;i++)
{
no_p=list[i]%10;
switch(no_p)
{
case 0: bk[0][k0++]=list[i]; break;
case 1: bk[1][k1++]=list[i]; break;
case 2: bk[2][k2++]=list[i]; break;
case 3: bk[3][k3++]=list[i]; break;
case 4: bk[4][k4++]=list[i]; break;
case 5: bk[5][k5++]=list[i]; break;
case 6: bk[6][k6++]=list[i]; break;
case 7: bk[7][k7++]=list[i]; break;
case 8: bk[8][k8++]=list[i]; break;
case 9: bk[9][k9++]=list[i]; break;
}
}
printf("The elements in table are:-");
printf("\n— — — — — — — — — — — — — — — — — — —");
printf("\n\t\t\tElements");
for(j=0;j<10;j++)
{
printf("\nElements in bucket %d:-",j);
for(i=0;i<5;i++)
if(bk[j][i]>0)
{
printf("%3d",bk[j][i]);
slist[p++]=bk[j][i];
}
}
printf("\n— — — — — — — — — — — — — — — — — — —");
OUTPUT
Enter the one digit elements:- 1 6 8 3 6
The elements in table are:
————————————————————————————
Elements
Elements in bucket 0:-
Elements in bucket 1:- 1
Elements in bucket 2:-
Elements in bucket 3:- 3
Elements in bucket 4:-
Elements in bucket 5:-
Elements in bucket 6:- 6 6
Elements in bucket 7:-
Elements in bucket 8:- 8
Elements in bucket 9:-
————————————————————————————
The sorted numbers:- 1 3 6 6 8
Explanation:
In this program the list[] is initialized to store the input numbers from the keyboard. The
static int bk[][] is used to store the numbers in the buckets 0 to 9. The k0 to k9 are the
variables, which are used to limit the buckets. The no_p variable stores the elements in buckets
by the switch() control statement. At last, the sorted list is stored into the slist[].
Example 11.11 Write a program to sort the given two digit numbers by using the radix
sort. Use function.
# include <stdio.h>
# include <conio.h>
int l[5],flag=0;
void main()
{
int i,j;
void place();
clrscr();
printf("Enter five elements with two digits:-“);
for(i=0;i<5;i++)
scanf("%d",&l[i]);
printf("\nThe first sorted table is");
place();
printf("\nFirst sorted list :-");
for(i=0;i<5;i++)
printf("%3d",l[i]);
flag=2;
getch();
printf("\n\nThe final sorted table is");
place();
printf("\nFinal sorted list :-");
for(i=0;i<5;i++)
printf("%3d",l[i]);
getch();
}
void place()
{
int k1,k2,k3,k4,k5,k6,k7,k8,k9,k0;
int a,p,q,r;
int n,b[10][5]={0};
k0=k1=k2=k3=k4=k5=k6=k7=k8=k9=a=0;
while(a<5)
{
if(flag==2)
n=l[a]/10;
else
n=l[a]%10;
switch(n)
{
case 0: b[0][k0++]=l[a]; break;
case 1: b[1][k1++]=l[a]; break;
case 2: b[2][k2++]=l[a]; break;
case 3: b[3][k3++]=l[a]; break;
case 4: b[4][k4++]=l[a]; break;
case 5: b[5][k5++]=l[a]; break;
case 6: b[6][k6++]=l[a]; break;
case 7: b[7][k7++]=l[a]; break;
case 8: b[8][k8++]=l[a]; break;
case 9: b[9][k9++]=l[a]; break;
}a++;
}
r=0;
for(p=0;p<10;p++)
{
if(b[p][0]!=0)
printf("\nBucket %d contain:-",p);
for(q=0;q<5;q++)
if(b[p][q]>0)
{
printf("%3d",b[p][q]);
l[r++]=b[p][q];
}
}
}
OUTPUT
Enter five elements with two digits:-99 12 11 48 10
The first sorted table is
Bucket 0 contain:- 10
Bucket 1 contain:- 11
Bucket 2 contain:- 12
Bucket 8 contain:- 48
Bucket 9 contain:- 99
First sorted list :- 10 11 12 48 99
The final sorted table is
Bucket 1 contain:- 10 11 12
Bucket 4 contain:- 48
Bucket 9 contain:- 99
Final sorted list :- 10 11 12 48 99
Explanation:
In this program function place() is used to sort the list. The variable n is used to store either
the reminder or quotient. The modulus operator is used for finding the unit place and the divide
operation for tens place. Then, these elements are stored in buckets by the switch() control
statement. After the first call of the place() the first sorted list is stored into the l[ ] and
after second call the final sorted list is placed into l[ ].
The partition exchange sort is the sorting technique in which list of elements is divided into the two sub-
parts. Using the pivot from the list makes the sublist of the list. The left part is called left sublist and the
right part is called right sublist. On the sublist quick or tree sort is performed and the two-sorted parts are
prepared and combined. For example, consider the following list:
24, 30, 27, 32, 11, 21, 19.
Assume the pivot key selected is 24 and the smaller numbers than pivot are shifted on its left side and
remaining greater elements to the right of it. The element 24 divides the list in two parts. The Fig. 11.8
shows the partition exchange sort.
Selecting 24 as
24 30 27 32 11 21 19
Pivot
Pivot Divides
11 21 19 24 30 27 32
List into 2 Parts
After Sorting 2
Lists the Final 11 19 21 24 27 30 32
Sorted List is
After combining the two parts including the pivot key the sorted list becomes:
11, 19, 21, 24, 27, 30, 32.
Following program is presented on the same concept.
Example 11.12 Write a program to sort the elements by using the partition exchange sort.
# include <stdio.h>
# include <conio.h>
{
while(items[key]<=items[r] && key!=r)
r––;
if( key==r )
key_placed=1;
if( items[key] > items[r] )
{
temp=items[key];
items[key]=items[r];
items[r]=temp;
key=r;
}
while( items[key]>=items[l] && l!=key )
l++;
if(key==l)
key_placed=1;
if( items[key] < items[l] )
{
temp=items[key];
items[key]=items[l];
items[l]=temp;
key=l;
}
}
q_sort(items,lower,key-1);
q_sort(items,key+1,upper);
return 0;
}
OUTPUT
Enter the 7 elements:- 24 30 27 32 11 21 19
Enter pivot element:- 24
11 19 21 24 27 30 32
Explanation:
In this program seven elements are input for sorting into the arr[ ]. After selecting the
pivot key the elements whose values are lower than pivot key are placed into the ltree[ ]
otherwise [rtreel ]. Then the q_sort() is resurvey called for sorting the left as well
as right side elements of that pivot key. In the function q_sort() the values of the lower
and upper bound are checked. If the value of the lower bound is greater than or equal to the
upper bound then the function is terminated else the remaining elements are to be sorted.
After performing these operations the sorted list is obtained for the ltree[ ] as well as
rtree[ ].
SUMMARY
1. Sorting is a process in which records are arranged in ascending or descending order.
2. This chapter presents information on various sorting methods; each of them is illustrated
with suitable example.
3. Insertion sort: In insertion sort the element is inserted at appropriate place. Here, a real
life example of playing cards can be quoted. The cards are kept in increasing order. The
card having least value is placed at the extreme left and the largest one at the other side.
In between them the other cards are inserted in ascending order.
4. Selection sort: The selection sort is almost same as exchange sort. By applying selection
sort, the first element is compared with remaining (n–1) elements. The smallest element
is placed at the first location. Again, the second element is compared with remaining (n–2)
elements. If the item found is lesser than the compared elements in the remaining n–2 list
then the swap operation is done. In this type, entire array is checked for the smallest ele-
ment and then swapped.
5. Bubble sort: Bubble sort is frequently used sorting algorithm. In this method, two succes-
sive elements are compared and exchanging is done if the first element is greater than the
second one. The elements are sorted in ascending order.
6. Quick sort: It is also recognized as partition exchange sort. This method also falls under
divide and conquer technique. The main list of elements is divided into two sub-lists. Any
one element from the list is marked as pivot key in the quick sort. Now, the pivot key
divides the main list into two parts. It is not necessary that selected key element must be
at middle.Any element from the list can act as key element. However, for best performance
preference is given to middle elements. Time consumption of the quick sort depends on
the location of the key in the list.
7. Tree sort: Inorder and heap sort methods are elaborated under tree sorting. Inorder tree
sorting method involves arranging the elements as per rules. In this method, firstly the left
element of tree is chosen, then node and right node.The rule for heap sort prescribes that
each parent node should have bigger value.
8. Heap sort: In heap sort, binary tree is used in which the nodes are arranged in specific
prearranged order. The rule prescribes that each node should have bigger value than its
child node. The heap sort process can be followed from the figure and example given in
this chapter.
9. Radix sort: The radix sort is based on the position of digits. Based on the positions of
digits sorting is done.
10. Partition exchange sort: The partition exchange sort divides the elements into the two
subparts. A pivot is selected among the elements. The elements whose values are smaller
than the pivot are shifted towards left of it. The right part has elements whose values are
greater than the pivot.
EXERCISES
A. Answer the following questions:
1. Apply selection sort on given ten numbers and sort them in ascending order.
2. Sort ten names of the students using merge sort.
3. Apply the quick sort to the given ten names and sort them in alphabetic order.
4. Write a program for sorting the given list by descending orders, using selection sort.
5. Apply tree-sorting method (inorder) on given ten numbers and arrange them in ascending
order.
6. Write a program by using heap sort for sorting given numbers.
7. Apply partition sort on twelve numbers.
8. Write a program for sorting two digit numbers using radix sort.
9. Write a function for bubble sort.
10. Use selection sort method for sorting five numbers using pointers.
11. Write a program to sort 15 elements using bubble sort. Assume any suitable elements.
12. Write a program to sort the elements 2,32,45,67,89,4,3,8,10 in descending order using inser-
tion sort.
13. Write a program to sort a given list of elements using tree sort.
14. Write a program to sort one digit number by using the radix sort method. Use pointer.
15. Write a program to sort the following numbers in ascending order using selection sort.
12,34,5,78,4,56,10,23,1,45,65.
1. int ns[5],p=0,q=1,temp;
# include <stdio.h> clrscr();
# include <conio.h> printf("\n Enter 5 elements:");
while(p<5)
void main() scanf("%d", &ns[p++]);
{ for(q=1;q<5;q++)
static int b[10][7],k1,k2,k3,k4,k5, {
k6,k7,k8,k9,k0,p; temp=ns[q];
int i,j,no,l[7],sl[7]; for(p=q-1;p>=0 && temp<ns[p];p—)
clrscr(); ns[p+1]=ns[p];
printf("Enter elements <10:-"); ns[p+1]=temp;
for(i=0;i<7;i++) }
scanf("%d",&l[i]); printf("Sorted elements are:");
i=0; for(p = 0; p < 5; p++)
while(i<7) printf("%d ", ns[p]);
{ }
no=l[i]%10; 3.
switch(no) # include <stdio.h>
{ # include <conio.h>
case 0: b[0][k0++]=l[i]; break; void main()
case 1: b[1][k1++]=l[i]; break; {
case 2: b[2][k2++]=l[i]; break; int p=0,q,r,num,t,small;
case 3: b[3][k3++]=l[i]; break; int arr[5]={12,23,1,4,2};
case 4: b[4][k4++]=l[i]; break; clrscr();
case 5: b[5][k5++]=l[i]; break; for(p=0; p<4;p++)
case 6: b[6][k6++]=l[i]; break; {
case 7: b[7][k7++]=l[i]; break; small = p;
case 8: b[8][k8++]=l[i]; break; for(r=p+1; r<5;r++)
case 9: b[9][k9++]=l[i]; break; if(arr[small] > arr[r])
}i++; small = r ;
} if( p != small )
printf("The table is:-"); {
for(j=0;j<10;j++) t = arr [p];
{ arr[p] = arr[small];
printf("\nBucket %d contain:-",j); arr[small] = t ;
for(i=0;i<5;i++) }
if(b[j][i]>0) }
{ printf("\nSorted elements are:");
printf("%3d",b[j][i]); p=0;
sl[p++]=b[j][i]; for(p=0;p<5;p++)
} printf("%d", arr[p]);
} }
printf("\nSorted list:-");
for(i=0;i<7;i++) 4.
printf("%2d",sl[i]); # include <stdio.h>
# include <conio.h>
}
void main()
2. {
# include <stdio.h> int arr1[5]={12,3,122,321,1},i,j,t;
# include <conio.h> clrscr();
void main() for(i=0;i<5;i++)
{ for(j=i;j<4;j++)
if(arr1[i]>arr1[j+1]) }
{ for(n=0;n<5;n++)
t=arr1[i]; {
arr1[i]=arr1[j+1]; sum=sum+l2[n];
arr1[j+1]=t; }
} p=n=m=0;
for(i=0;i<5;i++) m=m-sum;
printf(" %d ",arr1[i]); while(m<sum)
} {
for(n=0;n<5;n++)
5. {
# include <stdio.h>
if(m==l1[n] || m==l2[n])
# include <conio.h>
sl[p++]=m;
# include <math.h>
if(m==l1[n] && m==l2[n])
void main()
sl[p++]=m;
{
}
int m,n,p,sum=0,sl[10]={0};
m++;
int l1[5]={4,2,1,43,3},l2[5]={9,7,
}
6,8,5};
puts("Merged sorted list : ");
clrscr();
for(n=0;n<10;++n)
for(n=0;n<5;n++)
printf(" %d ",sl[n]);
{
}
sum=sum+l1[n];
Searching
12.1 INTRODUCTION
In our day-to-day life there are various applications, in which the process of searching is to be carried.
Searching a name of a person from the given list, searching a specific card from the set of cards, etc., are
few examples of searching.
A typical example where searching technique is applied is personal telephone diary. This diary is used
to store phone/mobile numbers of friends, relatives, etc. For searching the phone number of a person one
can directly search the number by using indexing in that diary. Quick search methods are to be adopted
to search the data from the heap or large records. Two processes such as searching and sorting are essential
for database applications.
In short, searching is a process in which a given record is searched from the set of records. Sorting is a
process in which records are arranged in ascending or descending order. A group of field is called record.
For example, consider the following structure:
struct employee
{
char *name;
int age;
float height;
}s;
We are familiar with the above declarations and definitions. The object s contains three fields as
described in the structure. The database is a collection of such fields. Each record contains the above fields
or may vary depending upon the structure defined. The sorting can be done on any one or combination of
one or more fields. Suppose, we sort the records based on name. Then, the field name will be the key field.
A record is quickly searched in a sorted database. Thus, sorting and searching are very useful concepts of
data structure. This chapter aims to provide the information on searching and its techniques.
12.2 SEARCHING
Searching is a technique of finding an element from the given data list or set of the elements like an array,
list, or trees. It is a technique to find out an element in a sorted or unsorted list. For example, consider an
array of 10 elements. These data elements are stored in successive memory locations. We need to search
an element from the array. In the searching operation, assume a particular element n is to be searched.
The element n is compared with all the elements in a list starting from the first element of an array till
the last element. In other words, the process of searching is continued till the element is found or list is
completely exhausted. When the exact match is found then the search process is terminated. In case, no
such element exists in the array, the process of searching should be abandoned.
In case the given element is existing in the set of elements or array then the search process is said to be
successful as per Fig. 12.1. It means that the given element belongs to the array. The search is said to be
unsuccessful if the given element does not exist in the array as per Fig. 12.2. It means that the given ele-
ment does not belong to the array or collection of the items.
The complexity of any searching method is determined from the number of comparisons performed
among the collected elements in order to find the element. The time required for the operation is depen-
dent on the complexity of the operation or algorithm. In other words, the performance of searching
algorithm can be measured by counting the comparisons in order to find the given element from the list.
There are three cases in which an element can be found.
In the best case, the element is found during the first comparison itself. In the worst case, the element
is found only in the last comparison. Whereas in the average case, number of comparisons should be more
than comparisons in the best case and less than the worst case.
The searching can be classified into following types:
1. Linear search (sequential)
2. Binary search.
The linear search is a conventional method of searching data. The linear search is a method of searching
a target element in a list sequence. The expected element is to be searched in the entire data structure in
a sequential method from starting to last element. Though, it is simple and straightforward, it has serious
limitations. It consumes more time and reduces the retrieval rate of the system. The linear or sequential
name implies that the items are stored in systematic manner. The linear search can be applied on sorted
or unsorted linear data structure.
Figures 12.1 and 12.2 shows the searching by using the linear search method.
Example 12.1 Write a program to search an element from the entered numbers. Also, indicate its
position in case the element is found or unavailability.
# include<stdio.h>
# include<conio.h>
void main()
{
int sn,arr[10],a;
clrscr();
printf("Enter ten elements:-");
for(a=0;a<10;a++)
scanf("%d",&arr[a]);
printf("Enter the element to be searched:-");
scanf("%d",&sn);
a=0;
while(a<10)
{
if(arr[a]==sn)
{
printf("The number %d is found %d location",sn,a+1);
break;
}
a++;
}
if(arr[a]!=sn)
printf("The element %d is not found",sn);
}
OUTPUT
Enter ten elements:-12 34 64 23 67 123 346 45 30 19
Enter the element to be searched:-64
The number 64 is found 3 location
Explanation:
In this program the user enters ten elements by involving an array arr[]. The number, which
is to be searched, is initialized with variable sn. By using the for loop ten elements are entered
into the arr[]. By using the while loop the number is searched throughout the array and
appropriate message is displayed.
Example 12.2 Write a program to search the element from the entered numbers using the pointer.
#include<stdio.h>
#include<conio.h>
void main()
{
int sno,i;
int ar[10],*list;
clrscr();
printf("Enter ten elements:-");
for(i=1;i<=9;i++)
scanf("%d",&ar[i]);
printf("Enter the element to be searched:-");
scanf("%d", &sno);
list=&ar[0];
for(i=0;i<=9;i++)
{
if(*list==sno)
{
printf("The number %d is found %d location",sno,i+1);
break;
}
list++;
}
if(*list!=sno)
printf("The element %d is not found",sno);
getch();
}
OUTPUT
Enter ten elements:-12 44 23 122 45 67 78 56 65 90
Enter the element to be searched:-67
The number 67 is found 6 location
Explanation:
In this program *list is a pointer variable which is used to store the address of the first
element of the array. Ten elements are entered into the arr[] by using the for loop. First
element address is stored into the *list variable. By increasing the address, all elements of the
array are accessed. If the match is found then message is displayed on screen.
Example 12.3 Write a program to create a linked list and search a specific element from the
linked list.
# include<stdio.h>
# include<conio.h>
# include<malloc.h>
struct link
{
int num;
struct link *next;
};
int j;
struct link begin;
void search(struct link*);
void create_list(struct link*);
void show(struct link *);
void create_list(struct link*link)
{
int n=1,c=1;
begin.next=NULL;
ink=&begin;
j=0;
printf("\n Input integers (0 to stop) :\n");
while (n)
{
printf("Enter the %d number:-",c);
scanf ("%d", &n);
if (n==0) break;
else link->next=(struct link*) malloc(sizeof(struct link));
link->num=n;
link=link->next;
link->next=NULL;
fflush(stdin);
c++;
}
}
void search(struct link *link)
{
int node_num=0;
int s_node;
int flag=0;
link=&begin;
printf("\n Enter number to be searched : ");
scanf("%d",&s_node);
if(link==NULL)
printf("\n List is empty ");
while(link)
{
if(s_node==link->num)
{
printf("\n search is successful");
printf("\n Position of %d from beginning of the list : %d",s_node,node_
num+1);
link=link->next;
flag=1;
}
else
link=link->next;
node_num++;
}
if(!flag)
{
printf("\n Search is unsuccessful");
printf("\n %d does not found in the list", s_node);
}
}
void show(struct link * link)
{
link=&begin;
while(link->next)
{
printf(" %d",link->num);
link=link->next;
}
}
void main()
{
struct link *link=(struct link*) malloc(sizeof(struct link));
clrscr();
create_list(link);
printf("\n List :");
show(link);
search(link);
}
OUTPUT
Input integers(0 to stop):
Enter the 1 number:-12
Enter the 2 number:-23
Enter the 3 number:-34
Explanation:
In this program, the struct link is used to store linked list. List is created with create()
function. The number, which is to be searched, is entered. In the search() function the
entered number is compared with the linked list elements. When equal number is found the
number along with its position in the linked list is displayed. The show() function is used to
display all the linked list elements. Thus, through traversing and comparing elements search
process is done.
The binary search approach is different from the linear search. Here, search of an element is not passed
in sequence as in the case of linear search. Instead, two partitions of lists are made and then the given ele-
ment is searched. Hence, it creates two parts of the lists known as binary search. Figure 12.3 illustrates the
operation of the binary search.
Binary search is quicker than the linear search. It is an efficient searching technique and works with
sorted lists. However, it cannot be applied on unsorted data structure. Before applying binary search, the
linear data structure must be sorted in either ascending or descending order. The binary search is unsuc-
cessful if the elements are unordered. The binary search is based on the approach divide-and-conquer.
In binary search, the element is compared with the middle element. If the expected element falls before
the middle element, the left portion is searched otherwise right portion is searched.
The binary searching method is illustrated in Fig. 12.3.
Fig. 12.3 represents the operation of binary search. The total elements in the list are 8, and they are
shown in the ascending order. In case the key element is less than 5 then searching is done in lower half
otherwise in the upper half. The process of searching can be followed from Fig. 12.4.
Consider the array of seven elements. The array can be represented in Fig. 12.4 (a). The elements of an
array are 12, 14, 16, 19, 23, 27, and 31. Assume that we want to search the element 27 from the list of
elements. The steps are as follows:
Arr[0] Arr[1] Arr[2] Arr[3] Arr[4] Arr[5] Arr[6]
12 14 16 19 23 27 31
12 14 16 19 23 27 31
Arr
[5]
12 14 16 19 23 27 31
Arr
[4]
12 14 16 19 23 27 31
# include<stdio.h>
# include<conio.h>
void main()
{
int items[20],s=0,e,n,j,num,mid;
clrscr();
OUTPUT
Enter number of elements: 7
Enter element 1: 7
Enter element 2: 13
Enter element 3: 22
Enter element 4: 31
Enter element 5: 45
Enter element 6: 64
Enter element 7: 77
Enter element to be searched: 7
7 found at position 1
Explanation:
In this program an array items[20] elements is declared. The user is prompted to enter
number of elements to be in the list. The numbers are entered through the keyboard. The
variable s keeps track of the starting of list and the variable e keeps track of the end. The middle
is calculated with the formula mid =(s+e)/2;If the sum of (s+e) is odd number then the
result of division operation would be a float value. However, all the operands (s,e) of equation
are integers, the float value is converted to lowest integer.
The above explanation is illustrated with the following figure, which is self-explanatory.
S M E
7 13 22 31 45 64 77
S M E
7 13 22 31 45 64 77
S M E
7 13 22 31 45 64 77
Example 12.5 Write a program to demonstrate binary search. Use character array and store 10 names.
# include<stdio.h>
# include<string.h>
# include<conio.h>
void main()
{
int beg=1,mid,end=10,i,flag=0,val;
char name[15][10],fn[15];
clrscr();
printf("Enter 10 names :-\n");
for(i=1;i<11;i++)
scanf("%s",&name[i]);
printf("\n\tEnter the name to search:-");
scanf("%s",&fn);
mid=(beg+end)/2;
printf("\n\tAt starting beg is %d, mid is %d & end is %d",beg,mid,end);
while((strcmp(fn,name[mid]))!=0 && beg<=end)
{
val=strcmp(fn,name[mid]);
if(val>0)
{
beg=mid+1;
mid=(beg+end)/2;
printf("\n\tmid is %d & beg is %d",mid,beg);
}
else
{
end=mid-1;
mid=(beg+end)/2;
printf("\n\tmid is %d & end is %d",mid,end);
}
}
if(strcmp(fn,name[mid])==0){ flag=1;}
if(flag==1)
printf("\n\tThe name %s is traced successfully",fn);
else
printf("\n\tThe number %s is not traced",fn);
getche();
}
OUTPUT
Enter 10 names :-
ashish bhaskar deepak dinesh gajanan ganesh gopal govind raj ramash
Enter the name to search:-deepak
At starting beg is 1, mid is 5 & end is 10
mid is 2 & end is 4
mid is 3 & beg is 3
The name deepak is traced successfully
Enter 10 names :-
ashish bhaskar deepak dinesh gajanan ganesh goval govind raj ramesh
Enter the name to &arch;-amit
At starting beg is 1, mid is 5 & end is 10
mid is 2 & end is 4
mid is 1 & end is 1
mid is 0 & end is 0
The number amit is not traced
Explanation:
This program prompts the user to enter the names. They are stored in the name [15][10],
the fn [15] is used to store the name which we want to search. The program uses the binary
search to search the entered name. In the search method the user must enter the list of name in
ascending order. In this method first the mid is calculated by using beg and end.
Hashing technique is one of the complex searching techniques. In this section, we consider a class of
search techniques whose search time is dependent on the number of entries available in the table. In this
method, we fix the position of the key (element) into the table or the file, which is determined by the hash
function. The function in which we use this key is known as hashing function.
For example, we want to search a number from the ten numbers of a file. Then, we must find the
number throughout this range from the 1st number to 10th number. When we use the key for fixing its
position in a table then the number can be searched very easily.
In order to follow the operation of hashing function, consider an array that comprises a list containing
some finite number of elements. Each element of it is a key. On performing operations on each key some
value is obtained. The key is assigned to the index, which is having the appropriate value. The table is
prepared with the keys called a hashing table.
Let us assume the following array that will hold the hash table:
12,22,32,17,19,28, 15,29,16,18,13,11.
Let us use the division method for the following hash table. We divide the key by ‘3’ and the key is
placed in the index based on the remainders obtained. The keys having equal remainders are placed in the
same index. Any number divided by 3 always returnes 0,1, or 2. Hash table is prepared with three indices
such as 0,1,2. All above keys are stored in these three indices as shown in Table 12.1.
Take the first key ‘12’, divide it by ‘3’. The reminder is ‘0’, hence insert it into the ‘0’ index.
Then, take the next key ‘22’ divide it by the ‘3’, the reminder is ‘1’. Hence, insert the key in to the ‘1’
index.
In the same way divide the key ‘32’ by the 3, its reminder is 2. Place it into the ‘2’ index in the table.
For the remaining elements, the same procedure is to be continued and by knowing reminders the
numbers can be placed appropriately in the respective index table.
Table 12.1 explains hashing table.
Index Key
0 12, 15, 18
1 22, 19, 28, 16, 13
2 32, 17, 29, 11
In short, the hashing function takes the key, and maps it to some index to the array. In our example,
division function has been selected. The programmer can take any other function. This function maps
several different keys to the same index. In the above table, key 12,15 and 18 are assigned to the index 0.
While keys, 22, 19, 28, 16 and 13 are placed in index 1. Keys 32,17,29 and 11 are in the index 2.
After preparation of the index table, it becomes easier to search the element from the list. One can use
any searching method to search the element from the hashing table.
Consider the following program for preparation of an index using divide method.
Example 12.6 Write a program to prepare the hashing table. Enter the elements through the
keyboard and map them in index. Use the division function.
# include<stdio.h>
# include<conio.h>
void main()
{
int n[12],i0[12],i1[12],i2[12];
int rem,a=0,b=0,c=0,i,j;
clrscr();
printf("\nEnter the elements of array:-");
for(i=0;i<12;i++)
scanf("%d",&n[i]);
for(i=0;i<12;i++)
{
rem=n[i]%3;
if(rem==0)
{
a++;
10[a]=n[i];
}
else
{
if(rem==1)
{
b++;
i1[b]=n[i];
}
else
{
c++;
12[c]=n[i];
}
}
}
printf("\n\nThe Hashing Table is as follows\n");
printf("––––––––––––––––\n");
printf("Index 0 :-");
for(j=1;j<=a;j++)
printf("%5d",i0[j]);
printf("\nIndex 1 :-");
for(j=1;j<=b;j++)
printf("%5d",i1[j]);
printf("\nIndex 2 :-");
for(j=1;j<=c;j++)
printf("%5d",i2[j]);
printf("\n––––––––––––––––");
}
OUTPUT
Enter the elements of array:-12 22 32 17 19 28 15 29 16 18 13 11
The Hashing Table is as follows
––––––––––––––––------------
Index 0 :- 12 15 18
Index 1 :- 22 19 28 16 13
Index 2 :- 32 17 29 11
––––––––––––––––-------------
Explanation:
In this program the main array 'n[12]' is declared having 12 elements. In addition to the
above i()[],il[] and i2[] arrays are initialized for storing the index. These arrays also
accommodate 12 elements. In the above program, we calculate the hashing table by using the
division method. The keys are divided by 3. Using the mod operation and the reminders are
computed, they are placed in the appropriate index.
Some of the hashing methods that are commonly used are stated below.
The commonly used hashing method is the division method. In this hashing function, the integer key is
divided by some number. The remainder value is taken as the hashing value. This method can be expressed
as follows:
H(k)=k(mod x)+1.
In above equation the H(k) is the hashing function. In addition, the k is the key, which is to be used in
hashing. The k(mod x) shows the result of division k by x.
In the above equation, the ‘k’ is the key and the ‘x’ is the size of list. We can take any value for the x.
For example, key is 25 and x=10
H(25)=25(mod 10)+1 =5+1=6.
Consider an example to prepare a hashing table and search the prompted number from the hashing
table. Each number (key) is divided (mod operation) by 3. No addition of 1 is done after division. It is
up to the user how to deal with the problem. The user can perform simply division operation and note
down the remainders and place them in appropriate index. As such, there will be 3 indices. The following
program illustrates this concept.
Example 12.7 Write a program for division method hashing, and find the given number from the
hashing table.
# include<stdio.h>
# include<conio.h>
void main()
{
int n[5],i0[5],i1[5],i2[5];
int num,rem,a=0,b=0,c=0,i,j;
clrscr();
printf("\nEnter the elements of array:-");
for(i=0;i<5;i++)
scanf("%d",&n[i]);
for(i=0;i<5;i++)
{
rem=n[i]%3;
if(rem==0)
{
a++;
i0[a]=n[i];
}
else
{
if(rem==1)
{
b++;
i1[b]=n[i];
}
else
{
c++;
i2[c]=n[i];
}
}
}
printf("\n\nThe Hashing Table is as follows\n");
printf("––––––––––––––––----------------------\n");
printf("Index 0 :-");
for(j=1;j<=a;j++)
printf("%5d",i0[j]);
printf("\nIndex 1 :-");
for(j=1;j<=b;j++)
printf("%5d",i1[j]);
printf("\nIndex 2 :-");
for(j=1;j<=c;j++)
printf("%5d",i2[j]);
printf("\n––––––––––––––––----------------------");
printf("\nEnter the key to search:-");
scanf("%d",&num);
rem=num%3;
if(rem==0)
{
for(j=1;j<=a;j++)
if(num==i0[j])
printf("\nThe position of the number is %d in the Index 0",j);
}
if(rem==1)
{
for(j=1;j<=b;j++)
if(num==i1[j])
printf("\nThe position of the number is %d in Index 1",j);
}
if(rem==2)
{
for(j=1;j<=c;j++)
if(num==i2[j])
printf("\nThe position of the number is %d in the Index 2",j);
}
}
OUTPUT
Enter the elements of array:-4 11 5 17 23 12 10 13 6 21
The Hashing Table is as follows
––––––––––––––––----------------------
Index 0:- 12 6 21
Index 1:- 4 10 13
Index 2:- 11 5 17 23
––––––––––––––––----------------------
Enter the key to search:-23
The position of the number is 4 in the Index 2
Explanation:
In this program the main array 'n[]' is declared. In it, five elements are declared. In addition to
the above i0[],il[] and i2[] arrays are also initialized for storing the index. In the program, we
can calculate the hashing table by using the division method. The keys are divided by 3. Using the
mod operation, the remainders are calculated and are placed in the appropriate index.The number,
which is to be searched, is prompted through keyboard using variable num. The number is divided
by 3 and after knowing the reminder; the particular index is searched for searching the element.
Example 12.8 Write a program to prepare hashing table by division method. Use functions and
search number from the hashing table.
# include<stdio.h>
# include<conio.h>
void main()
{
void show(int *a,int *nos);
int search(int gno,int *arr, int lim);
int n[5],i0[5],i1[5],i2[5];
int num,rem,a=0,b=0,c=0,i,j;
clrscr();
printf("\nEnter the elements
of array:-");
for(i=0;i<5;i++)
scanf("%d",&n[i]);
for(i=0;i<5;i++)
{
rem=n[i]%3;
switch(rem)
{
case 0: a++; i0[a]=n[i]; break;
case 1: b++; i1[b]=n[i]; break;
case 2: c++; i2[c]=n[i]; break;
}
}
printf("\n\nThe Hashing Table is as follows\n");
printf("––––––––––––––––\n");
printf("Index 0 :-");
show( &i0[1],&a);
printf("\nIndex 1 :-");
show(&i1[1],&b);
printf("\nIndex 2 :-");
show(&i2[1],&c);
printf("\n––––––––––––––––");
printf("\nEnter the key to search:-");
scanf("%d",&num);
rem=num%3;
switch(rem)
{
case 0:
printf("\nThe position of the number is %d in the Index
0",search(num,&i0[1],a));
break;
case 1:
printf("\nThe position of the number is %d in Index
1",search(num,&i1[1],b));
break;
case 2:
printf("\nThe position of the number is %d in the Index
2",search(num,&i2[1],c));
break;
}
}
void show(int *a,int *nos)
{
int j;
for(j=1;j<=*nos;j++)
{
printf("%5d",*a);
a++;
}
}
int search(int gno,int *arr,int lim)
{
int j;
for(j=1;j<=lim;j++)
if(gno==*arr)
return j;
else
arr++;
}
OUTPUT
Enter the elements of array:-12 34 65 23 10
The Hashing Table is as follows
––––––––––––––––-------------
Index 0 :- 12
Index 1 :- 34 10
Index 2 :- 65 23
––––––––––––––––-------------
Enter the key to search:-10
The position of the number is 2 in Index 1
Explanation:
In this program the division hashing method is used for storing the numbers. The i()[],il[],
and i2[] are three arrays, which are used for storing results. The variable a,b,c are used for
storing the boundaries of the buckets. The function show() is used to display the elements of
a bucket. Function search() is used for searching the given number into the three buckets. In
function, show() the two arguments are passed and they are the address of bucket array and
address of boundaries. Function search() contains three arguments address of bucket array,
value of boundaries, and the given number to search.
In mid-square method the key is multiplied with itself and middle digit is chosen as the address of that
element in the hashing table. It is defined as follows:
H(k)=(k*k)
For example, if the key is k =12
H(12)=(12*12)=144.
In this example, the key is 12, its square is 144 and its middle digit is 4. Hence, 12 is placed in the hash table
in the index 4 as index 4 is the address of 12. Similarly, for other keys the squares are calculated and mid
digits are evaluated. Based on the mid digits the keys are placed in the appropriate index. Here, the mid digit
obtained can be from 0 to 9. Hence, we may need up to 10 indices starting from index 0 through index 9.
For example, if the key is k =10
H(10)=(10*10)=100. Its middle digit is 0. Hence, number 10 would be placed in index 0. Similarly,
the same procedure is adopted for all other numbers and they are placed in different indices based on the
mid digit value. Table 12.2 describes the above concepts.
Consider the following number for constructing the hashing table:
12,14,18,20,36,31,27,35,23,59.
Table 12.2 Table of Key, Square and Mid
Example 12.9 Write a program to display the hash table, which is to be prepared by using the mid-
square method.
# include<stdio.h>
# include<conio.h>
void main()
{
Int msarr[6]={18,21,29,27,12,11},ms1[6],ms2[6],ms3[6],ms4[6];
int a,i,m1,m2,m3,m4,smid;
int squ_mid(int);
void disp(int farr[],int count);
clrscr();
m1=m2=m3=m4=0;
printf("The numbers are as follows :-\n");
for(i=1;i<=7;i++)
printf("\t%d",msarr[i]);
for (a=1;a<7;a++)
{
smid=squ_mid(msarr[a]);
switch (smid)
{
case 1:
{m1++; ms1[m1]=msarr[a]; break;}
case 2:
{m2++; ms2[m2]=msarr[a]; break;}
case 3:
{m3++; ms3[m3]=msarr[a]; break;}
case 4:
{m4++; ms4[m4]=msarr[a]; break;}
}
}
printf("\n\nThe Hash Table with Mid-Square method is as follows");
printf("\n––––––––––––––\n");
printf("Index 1:-");
disp(ms1,m1);
printf("\nIndex 2:-");
disp(ms2,m2);
printf("\nIndex 3:-");
disp(ms3,m3);
printf("\nIndex 4:-");
disp(ms4,m4);
printf("\n––––––––––––––\n");
}
int squ_mid(int ele)
{
int squ=0,mid,n;
squ=ele*ele;
for(n=0;n<2;n++)
{
mid=squ%10;
squ=squ/10;
}
return mid ;
}
void disp(int farr[],int count)
{
int a1;
for(a1=1;a1<=count;a1++)
printf("%5d",farr[a1]);
}
OUTPUT
The numbers are as follows:-
21 29 27 12 11 64 0
The Hash Table with Mid-Square method is as follows
––––––––––––––––-------------
Index 1:-
Index 2:- 27 11
Index 3:-
Index 4:- 21 29 12
––––––––––––––––-------------
Explanation:
In this program the main array 'msarr[]' having six elements are declared. In addition to the
above 'ml[]', 'm2[]', 'm3[]' and 'm4[]' are initialized. These arrays are initialized
for storing the index. Two functions are declared, they are squ_mid(); and dis(). These
two functions are initialized for finding the middle digit of the square and display the keys. By
using the mod operator, the middle digit of the square number is calculated. After finding the
middle digit they are displayed in the hash table.
In the folding method, the key is divided into number of parts. The parts, of course, are dependent on the
length of the key. All the parts are added together. If the carry results from this operation it is to be ignored
and the number what remains behind after ignoring the carry is the address of key. By using this method,
we fold the key hence this method is called as folding method.
For example,
1. If key k= 879987
Then, by using this method, we divide it into two parts like 879 and 987. They are added and then
carry ignored and the number after ignoring the carry becomes the address of the key.
879 +987 = 1866. Its most significant digit is 1. Hence, after ignoring 1, the number that is left is 866
which is the address of the key.
2. If k = 678897678
In this, key is divided into the 3 parts. They are 678, 897 and 678.
By adding, 678+897+678= 2253.
After ignoring last carry 2, the number that is left is 253. Therefore, 253 is the address of the key. Thus,
the folding method is useful in converting multiword key into a single word. By applying hashing func-
tion, the key can be identified. The reader can develop a program on the topic.
One more hashing technique used is length dependent method. In this method, the length of the key is
measured and the number is placed in the index table appropriately. Here, a simple program is made to
search an element from the array. The program is self-explanatory. Length of an array and length of each
element can be anything but some finite value can be taken.
Example 12.10 Write a program for searching an element by using the length dependent hash
function. Display the hash table and search the element from the table.
#include<stdio.h>
#include<conio.h>
void main()
{
int arr[5],in1[5],in2[5],in3[5],in4[5],in5[5];
int a,i,len,i1,i2,i3,i4,i5,snum;
int length(int);
void disp(int farr[],int count);
void search(int sarr[],int n,int co);
clrscr();
i1=i2=i3=i4=i5=0;
printf("\nEnter the number of the elements:-");
for(i=1;i<=5;i++)
scanf("%d",&arr[i]);
for (a=1;a<6;a++)
{
len=length(arr[a]);
printf("\nThe length of %d is:-%d",arr[a],len);
switch(len)
{
case 1:
{i1++;in1[i1]=arr[a]; break;}
case 2:
{i2++; in2[i2]=arr[a]; break;}
case 3:
{i3++; in3[i3]=arr[a]; break;}
case 4:
{i4++; in4[i4]=arr[a]; break;}
case 5:
{i5++; in5[i5]=arr[a]; break;}
}
}
printf("\nThe Hash Table is as follows");
printf("\n––––––––––––––\n");
printf("Index 1:-");
disp(in1,i1);
printf("\nIndex 2:-");
disp(in2,i2);
printf("\nIndex 3:-");
disp(in3,i3);
printf("\nIndex 4:-");
disp(in4,i4);
printf("\nIndex 5:-");
disp(in5,i5);
printf("\n––––––––––––––\n");
printf("Enter the number to search;-");
scanf("%d",&snum);
len=length(snum);
switch(len)
{
case 1:
{search(in1,snum,i1);printf("in Index 1");break;}
case 2:
{search(in2,snum,i2);printf("in Index 2");break;}
case 3:
{search(in3,snum,i3);printf("in Index 3");break;}
case 4:
{search(in4,snum,i4);printf("in Index 4");break;}
case 5:
{search(in5,snum,i5);printf("in Index 5");break;}
}
}
int length(int ele)
{
int c=0,num;
while(ele>0)
{
if(ele>=10)
{
ele=ele/10;
c++;
}
else
{
c++;
break;
}
}
return c ;
}
void disp(int farr[],int count)
{
int a1;
for(a1=1;a1<=count;a1++)
printf("%5d",farr[a1]);
}
void search(int sarr[],int n,int co)
{
int a2;
for(a2=1;a2<=co;a2++)
{
if(n==sarr[a2])
{
printf("The position of the number is %d ",a2);
break;
}
}
}
OUTPUT
Enter the number of the elements:-1 12 123 12345 1234
The length of 1 is:-1
The length of 12 is:-2
The length of 123 is:-3
The length of 12345 is:-5
The length of 1234 is:-4
The Hash Table is as follows
––––––––––––––
Index 1:- 1
Index 2:- 12
Index 3:- 123
Index 4:- 1234
Index 5:-12345
––––––––––––––
Enter the number to search;-12345
The position of the number is 1 in Index 5
Explanation:
As usual, the arrays are declared by using arr[], in1[], in2[], in3[], in4[] in5[]
each one having length of 5. By using length(), function length of each key is calculated and the
keys are placed in the appropriate index.The number which is to be searched is inputted through
the keyboard. Here, snum variable is declared. Its length is calculated by using the length();
and by knowing the length it is searched in the hash table.
The name itself indicates that the function is having the multiplication operation in it. The multiplicative
hashing function can be written as
H(key)=(b(c*key mod 1))+1.
In this hashing method, the key is multiplied with the number ‘c’. The c is used in this function as
a constant. After performing the mod of this product with 1 again multiplication operation with the
number b is done and 1 is added. The number obtained is the index number for the placing the key.
Mainly, there are two types of searching methods used with hashing function. One is external searching
and other is internal search. When the records are bulky then the records can be stored in the file and the
file is stored on the disk, tape, etc. The record is outside of the computer memory. On that file the search-
ing operation is to be performed. This searching operation is called as the external searching. Two tech-
niques are used for the external search, one is hashing method and other is tree search method. In another
case, when the records are shorter and in less numbers the same can be stored in the computer memory
like array, list, etc. In general, the linear searching and binary search are used in the internal searching.
Digit analysis method is the hashing function in which the analysis is done on the whole digits. In this
method some digits of some positions of the number are taken out. Then, by making the reverse of the
selected digits a value is obtained. The key is transferred to that location. Assume a key 348526917 and just
select the digits at the 2, 5 and 7 position. They are as 1,2 and 8. By combining these digits we get a number
128 and after reversing the number have 821. Now, the key is transferred at the address location 821.
SUMMARY
1. Searching is a technique of finding accurate location of an element in the given item list or
set of the elements of an array, list, or trees.
2. If the given element is present in the collected elements or array then the search process
is said to be successful. The search is said to be unsuccessful if the given element does not
exist in the array.
3. The search method in which an element to be searched, is checked in the entire data
structure in a sequential way from starting to end is called linear search.
4. Binary search is quicker than the linear search. However, it cannot be applied on unsorted
data structure.This is the precondition. Before applying binary search, the linear data structure
must be sorted in either ascending or descending order by using one of the sorted methods.
5. Hash is one of the complex searching techniques. In this method, the position of the key
(element) is fixed into the table or the file, which is determined by the hash function. Divi-
sion method is explained with a suitable example in this method. Other methods have also
been discussed in detail.
EXERCISES
A. Answer the following questions:
1. What is searching? How is the searching process essential for data base applications?
2. Explain linear and binary search.
3. Differentiate between linear and binary search.
4. What are the different searching techniques known to you? Explain one of them in detail with
a suitable example.
5. Distinguish between the external and internal searching.
1. Write a program to find the given number in an array of 50 elements. Search how many times
a given element exists in the list.
2. Write a program to demonstrate successful and unsuccessful search. Display appropriate mes-
sages.
3. Write a program to demonstrate binary search. Use character array and store 10 names. Find
the given name.
4. Write a program to search with linear search the given name in string array of 10 names.
1. while(b<=5)
#include<stdio.h> {
#include<conio.h> if(a[b]==n)
{
void main() printf("The number %d is found %d
{ location",n,b);
int n,b=1,a[5]; break;
clrscr(); }
printf("Enter five elements:-"); b++;
while(b<=5) }
{scanf("%d",&a[b]);b++;} if(a[b]!=n)
printf("Enter the element to be printf("The element %d is not
searched:-"); found",n);
scanf("%d",&n); getch();
b=1; }
2. st=mid+1;
# include<stdio.h> else
# include<conio.h> en=mid-1;
mid=(st+en)/2;
void main() }
{ if(num==its[mid])
int no,j; printf("\n %d found at position %d ",
int r[5],*pt; num,++mid);
clrscr(); if(st>en)
printf("Enter five elements:-"); printf(" %d not found ", num);
for(j=1;j<=5;j++) getch();
scanf("%d",&r[j]); }
printf("Enter the element to be 4.
searched:-"); # include<stdio.h>
scanf("%d",&no); # include<string.h>
pt=&r[1]; # include<conio.h>
j=1;
while(j<=5) void main()
{ {
if(*pt==no) int beg=1,mid,end=5,i,flag=0,val;
{ char name[5],fn;
printf("The number %d is found %d clrscr();
location",no,j); printf("Enter name:-\n");
break; for(i=0;i<5;i++)
} scanf("%c",&name[i]);
pt++; printf("\n\tEnter the name to
j++; search:-");
} scanf("%s",&fn);
if(*pt!=no) mid=(beg+end)/2;
printf("The element %d is not printf("\n\tAt starting beg
found",no); is %d, mid is %d & end is
getch(); %d",beg,mid,end);
} while((strcmp(fn,name[mid]))!=0 &&
3. beg<=end)
# include<stdio.h> {
# include<conio.h> val=strcmp(fn,name[mid]);
if(val>0)
void main() {
{ beg=mid+1;
int its[5], st=0,en=4,j,num,mid; mid=(beg+end)/2;
clrscr(); printf("\n\tmid is %d & beg is
for(j=0;j<5;j++) %d",mid,beg);
{ }
printf("Enter element %d:",1+j); else
scanf("%d",&its[j]); {
} end=mid-1;
printf("\n Enter element to be mid=(beg+end)/2;
searched :"); printf("\n\tmid is %d & end is
scanf("%d",&num); %d",mid,end);
mid=(st+en)/2; }
while(num!=its[mid] && st<=en) }
{ if(strcmp(fn,name[mid])==0)
if(num>its[mid]) flag=1;
Question 1 is compulsory. Answer any five questions from the rest. The figures in the right-hand margin indicate
marks.
1. Answer the following questions: (2 10)
(a) Define and differentiate between linear and non-linear data structures.
(b) What do you mean by the efficiency of an algorithm? Discuss time and space complexity?
(c) What is meant by recursion? Which data structure is used to implement recursion?
(d) Find out the postfix expression for the following expression:
A* (B + C) − (G + H)/L/P
(e) Represent the polynomial 2x2 + 5y3 − 7 in a linked list.
(f ) Explain the term garbage collection.
(g) What is the difference between a binary tree and a complete binary tree?
(h) Find the preorder and post order traversal of the following binary tree.
C G
E
Y D
4. (a) Sketch the binary search tree from 50, 72, 99, 100, 62, 12, 9, 2, 25, 57. What will be the new
binary search tree after the insertion of four new numbers, 205, 13, 31 and 301? (5)
(b) Write an algorithm for max heap and create a max heap for 23, 74, 57, 65, 73, 36 and 99. (5)
5. (a) Construct a tree from inorder and postorder traversal where
Inorder: a + b – c * d – e/f + g – h
Postorder: abc – +de – fg + h–/* (5)
(b) Write an algorithm to search a node in a binary search tree. (5)
6. (a) Describe the different methods to represent a graph in computer memory. Represent the following
graph in adjacency matrix form. (5)
V1 V2
V3
V4 V5
V6
(b) Describe DFS algorithm. Find the shortest path from A to H from the given graph. (5)
7. (a) Describe radix sort. Sort the following data using radix sort:
42, 23, 74, 11, 65, 74, 94, 36, 99, 87, 70, 81 (5)
(b) Define quick sort algorithm. Arrange the following elements in ascending order. (5)
5, 14, 2, 9, 21, 34, 17, 19, 1, 44
8. (a) What is hashing? What are the different methods used for calculating hash function. Explain with
an example? (5)
(b) Discuss the Linear Probing Method. Discuss the disadvantages of Linear Probing Method with an
example. (5)
Solutions
1. (a) In linear data structures, data are stored in sequential order and memory is allocated in contiguous
manner, e.g. array, stack, queue. In non-linear data structures, data are stored in random manner
and memory allocation is also carried out in random manner, e.g. trees, graphs.
(b) The efficiency of an algorithm can be determined by finding the complexity of that algorithm.
Complexity refers to time complexity and space complexity. We observe the total time required to
complete an algorithm in time complexity and find the total space required to execute the algo-
rithm in space complexity.
(c) A function is called recursive function if it calls itself. The mechanism is called function recursion.
The stack data structure is used to implement recursion.
(d) A*(BC+) – (GH+)/L+P
0 −1 −1 → 2 2 0 → 5 0 3 → −7 0 0 N
(f ) Garbage collection refers to the de-allocation of unused memory. Deciding when to release the
allocated memory is very simple. The memory de-allocation is done when the programmer requests
by executing a specific function. Languages like Java have in-built garbage collection system. The
function free() in C and delete in C++ are used to release the memory. The garbage collec-
tion system checks the entire memory, marks the unused nodes and releases the memory associated
with them.
(g) In a complete binary tree, each node has exactly 2 or 0 degrees. However, in a binary tree each
node has 0, 1, or 2 degrees.
(h) Preorder: B, C, E, G, Y, D
Postorder: E, C, Y, D, G, B
B
C G
E
Y D
(i) A binary tree is called an extended or 2-tree if each node N has either 0 or 2 degrees. At level n, the
number of nodes = 2n.
(j) We use stack data structure for DFS and queue data structure for BFS.
2. (a) Queue is a linear data structure, which follows the principle of FIFO (First In First Out). In other
words, we can say that if the FIFO principle is implemented with array, then it is termed as queue.
Stack is a linear data structure, which follows the principle of LIFO (Last In First Out). In other
words we can say that if the LIFO principle is implemented with array, then it termed as stack.
* – ^ /
2 4 2 6
5 (5*2) ((5*2) – 4) (((5*2) – 4)^2)
12 12 12 12
S1 S2 S3 S4
+
((((5*2)–4)^2)/6)
12 12 + ((((5*2)–4)^2)/6)
S5 S6
Step 5: If the scanned operator is a closing parenthesis, then pop the operators from the stack
up to the opening parenthesis and omit them. The poped operators will be stored in the
postfix.
Step 6: Repeat Step 3, Step 4 and Step 5 till all the elements are scanned from the array.
Step 7: Print the postfix as the result.
Example: Convert A + B – (C*D – E + F^G) + (H + I*J) into postfix by using stack.
Array Stack Postfix
( (
A ( A
+ (+ A
B (+ AB
– (– AB +
( (– ( AB +
C (– ( AB + C
* (– (* AB + C
D (– (* AB + CD
– (– (– AB + CD*
E (– (– AB + CD*E
+ (– (+ AB + CD*E –
F (– (+ AB + CD*E – F
^ (– (+^ AB + CD*E – F
G (– (+^ AB + CD*E – FG
) (– AB + CD*E – FG^+
+ (+ AB + CD*E – FG^+ –
( (+( AB + CD*E – FG^+ –
H (+( AB + CD*E – FG^+ – H
+ (+(+ AB + CD*E – FG^+ – H
I (+(+ AB + CD*E – FG^+ – HI
* (+(+* AB + CD*E – FG^+ – HI
J (+(+* AB + CD*E – FG^+ – HIJ
) (+ AB + CD*E – FG^+ – HIJ*+
) AB + CD*E – FG^+ – HI* ++
(b) The algorithm to find the first occurrence of a given integer in a single linked list is given below.
Struct link
{
Int info;
Struct link *next;
};
Search (start, node, no) [start is the structure type of variable]
[node is the structure type of pointer variable]
[no is the information to search]
Step 1: node=next[start]
Step 2: set count=1
set opt=0
Step 3: repeat while(node!=null)
if(count=no)then
write: infor[node]
write: “is found at”
write: count
write: “position”
opt=1
return
else
node=next[node]
first=next[first]
count=count+1
[end of if]
[end of loop]
Step 4: if(opt!=1)
write: ”the number is not found”
[end of if]
Step 5: return
(
4. (a) A binary search tree is a binary tree that is either empty or in which each node possesses a key that
satisfies the following properties:
• The elements in the left subtree are smaller than the key in the root
• The elements in the right subtree are greater than or equal to the root
• The left and right subtrees are also the Binary Search Tree.
The initial binary search tree is given below:
50
12 72
9 25 62 99
2 57 100
50
12 72
9 25 62 99
2 13 31 57 100
205
301
ALGORITHM Build_Heap(A)
1. {
2. heapsize[A] ←length[A]
3. for i←length[A]/2 to l by -1
4. do Max_Heap(A,i)
5. }
ALGORITHM Heap_sort(A)
1. {
2. Build_Heap(A)
3. for i←lenghth[A] to 2 by -1
4. do swap(A[1],A[i])
5. heapsize[A] ← heapsize[A]-1
6. Max_Heap(A,1)
7. }
Example: Create a Max-heap by considering the numbers 23, 74, 57, 65, 73, 36 and 99.
Step 1:
23
Step 2:
23 74
Interchange
23 23
Step 3:
74
23 57
Step 4:
74 74
23 57 Interchange 65 57
65 23
Step 5:
74 74
65 57 Interchange 73 57
23 73 23 65
Step 6:
74
73 57
23 65 36
Step 7:
74 74
73 57 Interchange 73 99
23 65 36 99 23 65 36 57
Interchange
99
73 74
23 65 36 57
(D, B, H, E) (F, C, I, G)
Step 2: B will be chosen as the parent from the preorder and from the inorder D will be on the left
of B, and H and E will be on the right.
A (F, C, I, G)
D (H, E)
Step 3: E will be chosen as the parent from the preorder and H is present in the inorder to the
left.
A (F, C, I, G)
D E
Step 4: From the postorder, B will be chosen as the parent and from the inorder we observe that
to the right of B (H, E, A) and to the left (D) will be used.
A
B C
(H, E)
D E G
Step 5: From the postorder, we will choose E as the parent and from the inorder to the left of E
(H) will be used.
A
B C
D E F G
H I
Algorithm bst_search(x,num)
Step 1: if(x=null or num = key[x]) then
return x
Step 2: if(num<key[x]) then
return bst_search(left[x], num)
else
return bst_search(right[x],num)
Step 3: exit
6. (a) The major components of the graph are nodes and edges. Graphs can be represented in array rep-
resentation and linked representation.
Overall there are four major approaches to represent graphs:
• Adjacency Matrix
• Adjacency Lists
• Adjacency Multilists
• Incedence Matrix
Adjacency matrix: Adjacency matrix stores the information of adjacent or nearby nodes. This
matrix keeps the information whether the node is adjacent to any other node or not.
Example:
V1 V2
V3
V4 V5
V6
V1 V2 V3 V4 V5 V6
V1 0 0 0 0 0 0
V2 1 0 0 0 0 0
V3 0 0 0 0 0 0
V4 1 1 0 0 0 0
V5 0 0 1 1 0 1
V6 0 0 0 0 1 0
V1 V2
V3
V4 V5
V6
V1 V2 V3 V4 V5 V6
V1 0 1 0 1 0 0
V2 1 0 0 1 0 0
V3 0 0 0 0 1 0
V4 1 1 0 0 1 0
V5 0 0 1 1 0 1
V6 0 0 0 0 1 0
Adjacency list: Here, for each vertex we keep a list of all adjacent vertices. We maintain two lists
in the adjacency list representation of graphs. The first list will keep track of all the nodes in the
graph. The second list will contain a list of adjacent nodes for every node. Each list has a header
node, which will be the corresponding node in the first list. The header nodes are sequential pro-
viding easy, random access to the adjacency list for any particular vertex.
V1 V2
V3
V4 V5
V6
V1 V4 N
V2 V1 V3 V5 N
V3
NULL
V4 V2 V5 N
V5 V3 V6 N
V6
NULL
Incidence matrix: This type of representation clearly signifies the vertices to which the edges are
connected. The vertex from where the edge starts is represented by 1 and the vertex at which the
edge ends is represented by –1.
E1
V1
V2
E2 E4
E3
V6 V5
E6
E5 E8
V3 V4
E7
V1 V2 V3 V4 V5 V6
E1 1 –1 0 0 0 0
E2 –1 0 0 0 0 1
E3 0 –1 0 1 0 0
E4 0 1 0 0 –1 0
E5 0 0 1 0 0 –1
E6 0 0 1 0 –1 0
E7 0 0 1 –1 0 0
E8 0 0 0 –1 1 0
(b) The DFS uses stack for the traversal of graph. The procedure is as follows:
1. Push the starting node into the stack.
2. Pop an element from the stack. If it has not been traversed, traverse it. If it has already been
traversed, then just ignore it. After traversing, make the value of the visited array true for this
node.
3. Now push all the unvisited adjacent nodes of the popped elements on stack. Push the element
even if it is already in the stack.
4. Repeat Step 3 and Step 4 until the stack is empty.
1 2 3
4 5 6
7 8 9
Traversal =1, 2, 3, 6, 5
Step 7: Pop 8 and traverse it and push 9
Traverse node = 8
Visited [8] = T
Top = 2, stack = 5, 4, 9
Traversal =1, 2, 3, 6, 5, 8
Step 8: Pop 9 and traverse it and nothing is to be pushed into the stack
Traverse node = 9
Visited [9] = T
Top =1, stack = 5, 4
Traversal =1, 2, 3, 6, 5, 8, 9
Step 9: Pop 4 and traverse it and push 7 into the stack
Traverse node = 4
Visited [4] = T
Top =1 stack = 5, 7
Traversal =1, 2, 3, 6, 5, 8, 9, 4
Step 10: Pop 7 and traverse it and nothing is to be pushed into the stack
Traverse node = 7
Visited [7] = T
Top = 0 stack = 5
Traversal =1, 2, 3, 6, 5, 8, 9, 4, 7
Step 11: Pop 5 and traverse it but since visited [5] = T, so just ignore it
Top = 0, stack = empty
7. (a) The radix sort is based upon the positional value of the actual digits of the number being stored.
This method was earlier performed on a mechanical card sorter. For example, the number 245 in
decimal notation is written with a 2 in the hundredth position, 4 in the ten’s position and 5 in the
unit’s position. These three digits will be sorted in a maximum of 3 passes. In the first pass, the
unit’s digit will be sorted, in the second pass the ten’s digit will be sorted and in the third and final
pass the hundred’s digit will be sorted.
The radix sort technique is also used when large lists of names are to be sorted alphabetically.
For example:
42 20 64 51 34 70 31 16 15 12 19 33
Number 0 1 2 3 4 5 6 7 8 9
42 42
20 20
64 64
51 51
34 34
70 70
31 31
16 16
15 15
12 12
19 19
33 33
In the second pass, the unit digits are sorted as shown below:
Number 0 1 2 3 4 5 6 7 8 9
20 20
70 70
51 51
31 31
42 42
12 12
33 33
64 64
34 34
15 15
16 16
19 19
12 15 16 19 20 31 33 34 42 51 64 70
(b) It is also known as partition exchange sort. It was invented by C.A. R Hoare. It is based on partition.
The method falls under the divide and conquer technique. The main list of elements is divided
into two sub-lists. For example, a list of X elements is to be sorted. The quick sort marks an ele-
ment in a list called as pivot or key. Consider the first element J as a pivot. Shift all the elements
whose value is less than J towards the left and elements whose value is greater than J to the right
of J. Now, the key element divides the main list into two parts. It is not necessary that the selected
key element be in the middle. Any element from the list can act as the key element. However, for
best performance, preference is given to middle elements. The consumption of quick sort depends
on the location of the key in the list.
ALGORITHM (Quicksort(A, P, R)
1. If P < R
2. Then Q ← PARTITION(A,P,R)
3. QUICKSORT(A,P,Q)
4. QUICKSORT(A,Q+!,R)
ALGORITHM (Quicksort(A,P,R)
1. X A[P]
2. Low P
3. Up R
4. While(Low < Up)
5. {
6. While(A[low]<=x)
7. Low=Low+1
8. While(A[up]>x)
9. Up=Up-1
10. If(Low<Up) then
11. Exchange(A[Low], A[Up])
12. Else
13. Return(Up)
14. }
The elements 5, 14, 2, 9, 21, 34, 17, 19, 1, 44 can be sorted by using quick sort as follows.
First, choose the first element as the pivot and find the first element larger than the pivot from the
left-hand side and find the smallest or equal number equal to the pivot from the right to the left.
Then swap the larger element with the smallest element and continue the above process till the
smallest element is found after the largest element. Once the smallest element is on the left-hand
side of the largest, interchange the smallest element with the pivot so that the array is sub- divided
into two sub-arrays. Implement the process in both the sub-arrays.
2 9 21 34 17 19 44
5 14 1
Pivot High Less
Swap 1 2 21 34 17 19 14 44
14 and Less 9
1 High
5
2 1 5 9 21 34 17 19 14 44
Pivot/High Less
Since pivot and high are both the same, interchange the value of less with high.
1 2 5 9 21 34 17 19 14 44
Since the left sub-array elements are sorted now implement the sorting logic to right sub-array.
1 2 5 34 17 19 14 44
9 21
Since pivot and less are the same, nothing will change, i.e. the left sub-array will have no
elements but the right sub-array will have the elements from 21 to 44
1 2 5 9 21 34 17 19 14 44
1 2 5 9 19 14 17 21 34 44
Pivot Less High
1 2 5 9 17 14 19 21 34 44
Pivot Less High
1 2 5 9 14 17 19 21 34 44
8. (a) Hashing technique is a complex searching technique. In this method, we fix the position of the
key (element) into the table or the file, which is determined by the hash function. The func-
tion in which we use this key is known as hashing function. For example, we want to search a
number from the ten numbers of a file. We must find the number throughout this range from
the first number to tenth number. When we use the hash key for fixing its position in a table
then the number can be searched very easily. Some of the hashing techniques are discussed
below:
Division method: This method is considered as the simplest method. In this method, the integer
x is divided by M and then by using the remainder. This method is also known as the division
method of hashing. The format of hash function is H(x) = x mod m;
This method works fine for just about any value of M. Care should be taken while choosing the
value of M. It is better to take a large prime number. The main advantage of the division method
is its simplicity.
Middle-square method: The middle-square method employs a hashing method that avoids the
use of division. In this method, a key is multiplied by itself and the address is obtained by choosing
an appropriate number of bits or digits from the middle of the square. The selection depends upon
the table size and also that they should fit into one computer word of memory. The same positions
in the square must be used for all keys.
Multiplication method: This method is a slight variation of the middle-square method. In this
technique, instead of multiplying the value itself, we have to multiply the number by a constant
and then extract the middle K bits from the result.
Folding method: In this technique, a key is divided into a number of parts. Each part should have
equal length. The split parts are then added together and the final carry should be ignored.
(b) A simple approach to resolving collisions is to store the colliding record in the next available space.
This technique is known as linear probing. Linear probing resolves has collision by sequential
searching. A has table beginning at the location is returned by the has function. We consider
the array as a circular structure and continue looking for an empty room at the beginning of the
array.
For example: Consider the hash function h(x) = x%7
The numbers 23, 50, 30, 38 are arranged in the hash table as follows:
0 1 2 3 4 5 6
50 23 30 38
The main disadvantage of linear probing is that the elements are stored in different locations in
case of collision. Hence, searching has to be done completely within the sequence starting from the
beginning. Since it is static in nature, the rest of the elements cannot be stored when the memory
allocated is full.
Question 1 is compulsory. Answer any five questions from the rest. The figures in the right-hand margin indicate
marks.
1. Answer the following questions. (2 10)
(a) Explain the term ADT with a suitable example.
(b) Consider the following stack: #4, 5, 6*. Here # represents bottom and * represents top stack. What
will be the contents of the stack after the following sequence of operations—Push(10), Pop(10),
Push(20), Pop(), Push(30)? Consider that the stack has the size 3.
(c) What is a dequeue? Explain its two variants.
(d) What will be the time complexity in Big oh notation for the following code segment:
int f(int k)
{
if (k==1)return 1;
else
return k+ f(k-1);
}
(e) What will be the minimum and maximum height of a binary tree having 16 key values?
(f ) What is a polynomial list? How can we represent a polynomial in memory?
(g) Given below are two sequences of data obtained from two different binary trees. These data are
inserted into two different one-dimensional arrays sequentially. Out of these two sequences, which
can be a heap?
(i) 42, 35, 37, 20, 14, 18, 7, 10
(ii) 42, 35, 18, 20, 14, 30, 10
(h) Find out the edge matrix for the following graph.
b
1 2
3 c
a
4 7
5
e d
6
(i) What is a height balanced tree?
(j) What is the maximum and minimum number of entries for a leaf node of a B tree of order 6?
2. (a) Why do you check the full and empty condition of a stack? Write a C program to perform the
insertion and deletion in a stack that is implemented using array. (5)
(b) Write a C program to convert a single link list to a circular link list. Why is a header node used in
a circular list? (5)
3. (a) Define linear queue. Let QUEUE be a non-empty queue implemented using a linear array. Write
a C program to delete m elements from the queue. (5)
(b) Sketch the binary search tree resulting from the insertion of the following integer keys: 39, 24, 12,
11, 43, 73, 26, 35, 29, 13, 6 (5)
(i) Is the tree almost complete?
(ii) Is the tree AVL?
(iii) What is the height of the tree?
(iv) Write the preorder traversal of the tree.
4. (a) Write an algorithm that computes the number of elements and the sum of elements in a linear link
list. (5)
(b) The inorder of the tree: D, B, H, E, A, I, F, J, C, G
The preorder of the tree: A, B, C, D, E, H, C, F, I, J, G
Construct the tree and give a linear array representation of the above tree. Define the node structure in
C, which can be used to implement the tree. (5)
5. (a) Describe the node structure of a double link list. Write an algorithm to delete the last node of a
double link list. (5)
(b) Define binary heap. Explain the process of heap sort. Write an algorithm to construct a min heap.
Construct a min heap from the list: {21, 6, 56, 61, 44, 7, 9, 76, 75, 32, 34, 4, 49, 33} (5)
6. (a) Define a graph. What are the different representations of a graph? Use a suitable data structure to
represent the following graph. (5)
A B C
D E F
G H I
(b) What are the preconditions to perform binary search on a linear array. Write a recursive function
in C to perform binary search in an array. (5)
7. (a) Write an algorithm to perform DFS on a graph. Find out the path from D to K using DFS. (5)
(b) Write a program in C to sort a list of floating point number using quick sort. Why is the pivot
chosen from the centre of the list rather than from one end? (5)
8. (a) Use linked lists to represent the following polynomial: (5)
P(X, Y, Z) = 2XY 2Z3 + 3X 2YZ 2 + 4XY 3Z + 5X 2Y 2 + 8XY 2Z 5 + 19
(b) Describe the data structure used to represent a general tree. (5)
9. (a) Define circular queue. Sketch to explain the placement of front and rear points when the queue is
full and the queue contains a single element. (5)
(b) Convert the following from infix to postfix using stack: (5)
(C – D) + (E ^ F) + F/(H + W)*A ^ B
Solutions
1. (a) Abstract data types or ADTs are the mathematical specification of a set of data and the set of
operations that can be performed on the data. They are abstract in the sense that the focus is on
the definitions of the constructor that returns an abstract handle that represents the data, and the
various operations with their arguments. The actual implementation is not defined and does not
affect the use of the ADT. For example, rational numbers (numbers that can be written in the form
a/b where a and b are integers) cannot be represented natively in a computer.
(b) The sequence of operations is as follows:
Initial state Push(10) overflow Push(20) Pop() Push(30)
* Pop() * *
6 * 20 * 30
5 5 5 5 5
4 4 4 4 4
# # # # #
(c) A dequeue is a double-ended queue in which insertions and deletions are possible at either end.
There are two forms of dequeue:
• Input restricted dequeue (one insertion end and two deletion ends)
• Output restricted dequeue (one deletion end and two insertion ends)
(d) The loop is executed k times. Since the value of k is decremented each time by one up to the value
of k = 1, the time complexity is O(k).
(e) The minimum height of a binary tree having 16 key values is 4 and the maximum is 15.
(f ) Polynomial expressions allow us to express equations with higher order expressions. In data struc-
tures polynomials are represented by using the concept of linked list where the coefficient part and
exponent part are arranged in the value part of the linked list and the address part refers to the next
node.
For example: The expression 5X3 + 7X2 + 18 = 0 can be represented as follows:
35 37 35 18
20 14 18 07 20 14 30 10
10
The first tree is a heap tree (max heap) because all the nodes are smaller or equal to their parent
node. However, in tree (ii), 18 is lesser than 30. So, it is not a tree.
(h) The edge matrix is shown below:
EDGES
1 2 3 4 5 6 7
V
a 1 1 1
E
R b 1 1 1
T c 1 1
E
d 1 1 1 1
X
e 1 1
# include<stdio.h>
# include<alloc.h>
static int *s, size, top= -1;
void push (int no)
{
if(top == size-1)
printf(“\n stack overflow”);
else
{
top = top+1;
*(s+top) = no;
}
}
void pop()
{
if(top == -1)
printf(“\n stack underflow”);
else
{
printf(“%d is deleted”, *(s+top));
-top;
}
}
void traverse()
{
int i;
if(top == -1)
printf(“\n stack is empty”);
else
for(i=top; i>=0;i--)
prinf((“%5d”, *(s+i));
}
void main(0
{
int opt;
printf(“\n enter the size of the stack”);
scanf(“%d”,&size);
s=int *) malloc(size * sizeof(int)):
while(1)
{
printf(“\n enter the choice”);
printf(“\n 1. push 2.pop 3.display 0.exit”);
scanf(“%d”, &opt);
if(opt==1)
{
printf(“\n enter the number to insert”);
scanf(“%d”,&opt);
if(opt ==1)
{
printf(“\n enter the number to insert”);
scanf(“%d”, &opt);
push(opt);
}
else
if(opt==2)
pop();
else
if(opt==3)
traverse();
else
if(opt==0)
exit(0);
else
printf(“\n invalid choice”);
}
}
(b) A program to convert a single link list to a circular link list is given below:
#include<stdio.h>
#include<alloc.h>
#include<conio.h>
struct link
{
int info;
struct link * next;
};
int i; /* represents number of nodes in the list */
int number=0;
struct link start,*node, *new1;
void create()
{
char ch;
node=&start; /* point to the header node in the list */
i=0;
do
{
node→next=(struct link*)malloc (sizeof(struct link));
node=node→next;
printf(“\n input the node %d”, (i+1));
scanf(“%d”, &node→info);
fflush(stdin);
printf(“\n do u want to create more[y/n]”);
ch=getchar();
i++;
} while(ch==’y’ || ch==’y’);
node→next = &start;
start.infor=i; /* assign total number of nodes to the header node */
}
void insertioin()
{
struct link *first;
first=&start;
node=start.next;
int opt;
int count = node→info;
int nod_number=1;
int insert_node;
node=node→next;
printf(“\n input node number your want to insert:”);
printf(“\n value should be less are equal to the”);
printf(“\n number of nodes in the list: “);
scanf(“%d”, &insert_node);
while(count--)
{
if(node_number == insert_node)
{
new1 = (struct link*) malloc(sizeof (struct link));
first→next=new1;
new1→next=node;
printf(“\n input the node value: “);
scanf(“%d”, &new1→info);
opt=1;
break;
}
else
{
node = node→next;
first=first→next;
}
node_number++;
}
if(opt==1)
{
node=&start; /* points to header node */
node→info=node→infor+1;
}
}
/* display the list */
void display()
{
node=&start;
int count=node→info;
do
{
printf(“ \n %5d”, node→info);
node=node→next;
}while(count--);
}
void main()
{
clrscr();
create();
printf(“\n before inserting a node list is as follows:\n”);
display();
insertion():
printf(“\n after inserting a node list is as follows:\n”);
display();
}
The header node is used in a circular list because it aids traversal and enables us to find out the number
of nodes inside the circular list.
3. (a)
#include<stdio.h>
int *q, size, front=-1,rear=-1;
void insert(int n)
{
if(rear==size-1)
printf(“\n queue overflow”);
else
{
rear ++;
*(q+rear)=n;
if (front==-1)
front=0;
}
}
/* function to delete an element from queue*/
void delete()
{
if (front ==-1)
{
printf(“\n underflow”);
return;
}
printf(“\n element deleted : %d” , *(q+front));
if(front == rear)
{
front=-1;
rear=-1;
}
else
front= front+1;
}
void display()
{
int i ;
if (front = = -1)
printf(“\n empty queue”):
else
{
printf(“\n the queue elements are “);
for(i=front;i<=rear;i++)
printf(“%4d” , * (q+i));
}
}
void main()
{
int opt;
printf(“\n enter the size of the queue”);
scanf(“%d” , &size);
q=(int *)malloc(size * size of (int)):
while(1)
{
printf(“\n enter the choice”);
printf(“\n 1.insert 2. delete 3. display 0 .exit”);
scanf(“%d” , &opt);
if(opt==1)
{
printf(“\n enter the number to insert”);
scanf(“%d”,&opt);
insert(opt);
}
else
if(opt==2)
delete();
else
if(opt==3)
display();
else
if(opt==0)
exit(0);
else
printf(“\n invalid choice”);
}
}
A queue is a linear data structure where elements are entered at one end and deleted from another
end. It follows the principle of first-in-first-out (FIFO). A queue can be implemented with an
array or a linked list.
(b) A binary search tree is a binary tree that is either empty or in which each node possesses a key that
satisfies the following properties:
• The element in the left subtree are smaller than the key in the root
• The element in the right subtree are greater than or equal to the root
• The left and right subtrees are also part of the Binary Search Tree.
Example:
39
24 43
12 26 73
11 13 35
6 29
(b) Inorder: D, B, H, E, A, F, C, I, G
Preoder: A, B, D, E, H, C, F, G, I
Choose the root from the preorder and find the nodes to the left and right from the inorder. This
process will continue until all the elements are chosen from the preorder/inorder.
Step 1: A is the root from the preorder. In the inorder, (D, B, H, E) are to the left of A and (F, C,
I, G) are to the right.
(D, B, H, E) (F, C, I, G)
Step 2: B will be chosen as the parent from the preorder. D will be to the left of B and (H, E) to
the right.
A (F, C, I, G)
D (H, E)
Step 3: E will be chosen as the parent from the preorder and H is present to the left in the
inorder.
A (F, C, I, G)
D E
Step 4: B will be chosen as the parent from the preorder. We observe from the inorder that (H, E)
are present to the right of B and D to the left.
A
B C
(H, E)
D F G
Step 5: In the postorder, we will choose E from the right as the parent. H which is to the left of E
from the inorder will be used.
A
B C
D E F G
H I
struct tree
{
int info;
struct link*left;
struct link *right:
}:
{
int info;
struct link*next;
struct link*previous;
};
Dellast (start,node)
Step 1: Node: = next[start]
Step 2: Count: = 1
Step 3: Repeat while (next[node]!=null)
Node: = next [node]
Count: = count+1
End of loop
Step 4: Node: next[start]
Step 5: Repeat while (count!=1)
Node: = next[node]
Count: = count–1
End of loop
Step 6: Write: info[node]
Step 7: Next [prev[node]]: = next[node]
Prev[next[node]]: =prev[node]
Step 8: Free (node)
Step 9: Return
(b) Heaps are based on the concept of a complete tree. Formally, a binary tree is completely full if it is
of height h and has 2h+1–1 nodes. A binary tree of height h is complete if:
1. It is empty, or
2. Its left subtree is completely full and of height h –1 and its right subtree is completely full and
of height h – 2, or
3. Its left subtree is completely full and of height h –1 and its right subtree is completely full and
of height h –1.
A binary tree has the heap property if
1. It is empty, or
2. The key in the root is larger than that of either child, and both subtrees have the heap prop-
erty.
A heap can be used as priority queue. The highest priority item is at the root and is trivially
extracted. But if the root is deleted, we are left with two sub-trees and we must efficiently re-create
a single tree with the heap property. The value of the heap structure is that we can both extract the
highest priority item and insert a new one in o (log n) time. A heap is an ordered balanced binary
tree (complete binary tree) in which the value of the node at the root of any sub-tree is less than or
equal to the value of either of its children.
Algorithm Max_Heap(A,i)
1. {
2. l←left_child(index)
3. r←right_child(index)
4. if l d ≤ heapsize [A] and A[l] >A[i]
5. then largest ← l
6. else
7. largest ← i
8. if r ≤ d heapsize [A] and A[r] >A[largest]
9. then largest ← r
10. if largest ≠ l then
11. swap(A[i], A[largest])
12. Max_Heap(A, largest)
13. }
Algorithm Heap_sort(A)
1. {
2. Build_Heap(A)
3. for i←lenghth[A] to 2 by -1
4. do swap(A[1],A[i])
5. heapsize[A] ← heapsize[A]-1
6. Max_Heap(A,1)
7. }
The construction of max heap from the list {21, 6 , 56, 61, 44,7, 9, 76, 75, 32, 34, 4, 49, 33} is shown
below:
Step 1: Step 2: 21
21
Step 3: Interchange 56
21
6 56 6 21
Step 4: 56
Interchange
56 61
6 21 61 21 56 21
61 Interchange 6 6
Step 5: 61
Step 6: 61
56 21 56 21
6 44 6 44 7
Step 7: 61
56 21
6 44 7 9
Step 8: 61 61
56 21 Interchange 56 21
6 44 7 9 76 44 7 9
Interchange
76 6
Step 8: Interchange
61 76
76 21 61 21
56 44 7 9 56 44 7 9
6 6
Step 9:
76 76
61 21 Interchange 61 21
56 44 7 9 75 44 7 9
Interchange
6 75 6 56
Step 10: 76 76
75 21 75 21
61 44 7 9 61 44 7 9
6 56 6 56 32
Step 11:
76
75 21
61 44 7 9
6 56 32 34
Step 12: 76
75 21
61 44 7 9
6 56 32 34 4
Step 13:
76
75 Interchange 21
61 44 49 9
6 56 32 34 4 7
76
75 21
61 44 7 9
Interchange
6 56 32 34 4 49
76
75 49
61 44 21 9
6 56 32 34 4 7
Step 14: 76
75 49
61 44 21 9
Interchange
6 56 32 34 4 7 33
76
75 49
61 44 21 33
6 56 32 34 4 7 9
6. (a) The major components of a graph are node and edges. Graphs can be represented using array and
linked representation. There are four major approaches to representing graphs:
• Adjacency matrix
• Adjacency lists
• Adjacency multilists
• Incedence matrix
Adjacency matrix: The adjacency matrix is the matrix that keeps the information of adjacent or
nearby nodes. We can say that this matrix keeps the information whether the node is adjacent to
any other node or not. An example of adjacency matrix is shown below:
V1 V2
V3
V4 V5
V6
V1 V2 V3 V4 V5 V6
V1 0 0 0 0 0 0
V2 1 0 0 0 0 0
V3 0 0 0 0 0 0
V4 1 1 0 0 0 0
V5 0 0 1 1 0 1
V6 0 0 0 0 1 0
V1 V2
V3
V4 V5
V6
V1 V2 V3 V4 V5 V6
V1 0 1 0 1 0 0
V2 1 0 0 1 0 0
V3 0 0 0 0 1 0
V4 1 1 0 0 1 0
V5 0 0 1 1 0 1
V6 0 0 0 0 1 0
Adjacency list: Here, we keep a list of all adjacent vertices for each vertex. In the adjacent list rep-
resentation of graphs, we maintain two lists. The first list keeps track of all the nodes in the graph.
In the second list, we maintain a list of adjacent nodes for every node. Each list has a header node,
which will be the corresponding node in the first list. The header nodes are sequential providing
easy random access to the adjacency list for any particular vertex. An example of an adjacency list
is shown below:
V1 V2
V3
V4 V5
V6
V1 V1 N
V2 V1 V3 V5 N
V3 Null
V4 V2 V5 N
V5 V3 V6 N
V6 Null
Incidence matrix: In this type of representation, the major focus is on the edges of the graphs.
The incidence matrix clearly signifies the vertices to which the edges are connected. The vertex
from where the edge starts is represented as 1 and the vertex where it ends is represented by –1. An
example of incidence matrix is shown below:
E1
V1 V2
E2 E4
E3
V6 V5
E6
E5 V3 V4 E8
E7
V1 V2 V3 V4 V5 V6
E1 1 –1 0 0 0 0
E2 –1 0 0 0 0 1
E3 0 –1 0 1 0 0
E4 0 1 0 0 –1 0
E5 0 0 1 0 0 –1
E6 0 0 1 0 -1 0
E7 0 0 1 –1 0 0
E8 0 0 0 –1 1 0
(b) The binary search method is a very fast and efficient method. This method requires the list of ele-
ments to be sorted. We search for an element using this method by comparing it with the element
present at the centre of the list. If it matches, the search is successful. Otherwise, the list is divided
into two halves. The first half is from the 0th position to the centre and the second half is from the
centre to the last element. As a result, all the elements in the first half are smaller than the centre
element, whereas all the elements in the second half are greater than the centre element.
Algorithm:
Step 1: Low = 0, up = n–1
Step 2: Repeat while low <= up
Mid = int(low + up)/2
If (no = arr[mid])
Print “searched element is found”
Exit
Else
If (no<arr[mid]) then
Up=mid-1
Else
Low=mid+1
Step 3: Print “searched element not found”
Step 4: Exit
7. (a) The DFS uses stack for the traversal of graph. The procedure is as follows:
1. Push starting node into the stack.
2. Pop an element from the stack. If it has not traversed, then traverse it. If it has already been tra-
versed, then just ignore it. After traversing, make the value of visited array true for this node.
3. Now push all the unvisited adjacent nodes of the popped element on stack. Push the element
even if it is already in the stack.
4. Repeat Step 3 and Step 4 until the stack is empty.
1 2 3
4 5 6
7 8 9
Top = 2 stack = 5, 4, 9
Traversal =1, 2, 3, 6, 5, 8
Step 8: Pop 9 and traverse it and push nothing into the stack
Traverse node = 9
Visited[9] = T
Top =1 stack = 5, 4
Traversal =1, 2, 3, 6, 5, 8, 9
Step 9: Pop 4 and traverse it and push 7 into the stack
Traverse node = 4
Visited[4] = T
Top = 1 stack = 5, 7
Traversal =1, 2, 3, 6, 5, 8, 9, 4
Step 10: Pop 7 and traverse it and push nothing into the stack
Traverse node = 7
Visited[7] = T
Top = 0, stack = 5
Traversal =1, 2, 3, 6, 5, 8, 9, 4, 7
Step 11: Pop 5 and traverse it. But since visited[5] = T, just ignore it
Top = 0, stack = empty
(b) A program to sort a list of floating point number using quick sort is given below:
#include<stdio.h>
#include<conio.h>
int split (int *, int, int);
void main()
{
int arr[10]={11,2,9,13,57,25,17,1,90,3};
int i;
void quicksort (int *, int, int);
clrscr();
printf(“\n the given array is\n”);
for(i=0;i<=9;i++)
printf(“%d\t”, arr[i]);
quicksort (arr, 0, 9);
printf(“\nsorted array is \n”);
for(i=0;i<=9;i++)
printf(“%d\t”,arr[i]);
getch();
}
void quicksort( inta[],int lower, int upper)
{
int i;
if(upper >lower)
{
i=split(a,lower,upper);
quicksort(a,lower,i-1);
quicksort(a,i+1,upper);
}
}
int split(int a[], int lower, int upper)
{
int i, p, q, t;
p=lower +1;
q=upper;
i=a[lower];
while(q>=p)
{
while (a[q] < i)
p++;
while (a[q] >i)
q--;
if(q>p)
{
t=a[p];
a[p]=a[q];
a[q]=t;
}
}
t=a[lower];
a[lower]=a[q];
a[q]=t;
return q;
}
Here, 200, 300, 400, 500 and 600 are the respective locations of the nodes.
(b) A binary tree can be represented by using arrays and linked lists. The array and linked representa-
tion of trees are discussed below with an example.
Array representation of a tree: An array can be used to represent a binary tree. The total number
of elements in the array depends on the total number of nodes in the tree. The root node is always
kept as the first element of the array, i.e. the root node will be stored in the 0th index. The left child
and right child are stored in the successive memory locations.
A
B C
D E F G
Array representation:
A
B
C
D
E
F
G
∗ B ∗
∗ C ∗
∗ D ∗ ∗ E ∗ ∗ F ∗ ∗ G ∗
9. (a) The rear and front end of the queue are interconnected in circular queues. After reaching the
rear end, if the front end is not at zero, the rear end will again be set to zero. The same will be
implemented with the front end.
Overflow: The situation is termed as overflow if one tries to insert an element in a filled queue.
The condition for overflow is:
Rear = Size –1 and front = 0 or Front = Rear +1 (for the queue starting with 0)
Rear = Size and front =1 or front = Rear +1 (for the queue starting with 1)
Underflow: If one tries to delete an element from an empty queue, then the situation is called
underflow. The condition for underflow is:
Front = –1 (for the queue starting with 0)
Front = 0 (for the queue starting with 1)
Examples:
C_Queue(5) Front = −1, rear = −1
0 1 2 3 4
Insert(5) 5 Front = 0, rear = 0
0 1 2 3 4
Insert(25) 5 25 Front = 0, rear = 1
0 1 2 3 4
Insert(53) 5 25 53 Front = 0, rear = 2
0 1 2 3 4
Insert(78) 5 25 53 78 Front = 0, rear = 3
0 1 2 3 4
Insert(99) 5 25 53 78 99 Front = 0, rear = 4
0 1 2 3 4
Insert(145) “Overflow” (rear = size –1 condition for overflow)
Step 6: Repeat Step 3, Step 4 and Step 5 till all the elements are scanned from the array.
Step 7: Print the postfix as the result.
Example: Convert (C – D)+(E^F) + F/(H + W)*A^B into postfix by using stack.
Array Stack Postfix
( (
( ((
C (( C
- ((- C
D ((- CD
) ( CD–
+ (+ CD–
( (+( CD–
E (+( CD–E
^ (+(^ CD–E
F (+(^ CD–EF
) (+ CD–EF^
+ (+ CD–EF^+
F (+ CD–EF^+F
/ (+/ CD–EF^+F
( (+/( CD–EF^+F
H (+/( CD–EF^+FH
+ (+(+ CD–EF^+FH
W (+(+ CD–EF^+FHW
) (+(+ CD–EF^+FHW+
* (+* CD–EF^+FHW+
A (+* CD–EF^+FHW+A
^ (+*^ CD–EF^+FHW+A
B (+*^ CD–EF^+FHW+AB
) CD–EF^+FHW+AB^*+
Question 1 is compulsory. Answer any five questions from the rest. The figures in the right-hand margin indicate
marks.
1. Answer the following questions. (2 10)
(a) What is a stack? Discuss the pop operation of a stack?
(b) Convert the following infix expression into its equivalent postfix and prefix expressions.
a++ –b++ + – –c/d + e*f
(c) Write at least two disadvantages of linear queues?
(d) How are collisions handled in linear probing? Discuss with a simple example.
(e) For a list L = {8, 99, 3, 4, 6, 10}, find the output list at the end of pass 1 using bubble sorting
method.
(f ) What is the use of a head node in a linked list?
(g) Show that the maximum number of nodes in a binary tree of height h is 2h –1 for h>= 1.
(h) Prove with an example that a tree T with n vertices has n–1 edges.
(i) There are 8, 15, 13, 14 nodes in 4 different trees. Which of them could have formed a full binary
tree? Explain in two sentences.
(j) Distinguish between diagraphs and undirected graphs?
2. (a) Suppose a two-dimensional matrix is represented using a row major order in C programming.
Write the formula and calculate the address of element A [10] [10]. Assume the dimension of the
matrix is 10 × 10 and is of floating type. (5)
(b) What is a circular queue? Why is it better than a normal queue? Give some practical examples of
the usage of circular queues. (5)
3. (a) Write an algorithm and program in C to create five nodes of a linked list. (5)
(b) How are priority queues implemented using a single queue? Discuss with an example. (5)
4. (a) Write an algorithm to find the largest node in a binary search tree (5)
(b) Create a binary search tree using the following data entered as a sequential set:
3, 79, 67, 58, 38, 29, 15, 11, 5 (5)
5. (a) Sort the following elements using heap sorting method:
42, 23, 92, 16, 11, 45, 40, 64, 29, 18 (5)
(b) Create an AVL tree using the following data and show the balance factor in the resulting tree:
14, 23, 7, 10, 33, 56, 80, 75, 90 (5)
6. (a) Given the following inorder and preorder traversals of a binary tree, construct the binary tree: (5)
Inorder: B, F, G, H, P, R, S, T, W, Y, Z
Preorder: P, F, B, H, G, S, R, Y, T, W, Z
(b) Define a graph. Represent the graph shown below using:
(i) Adjacency matrix
(ii) Adjacency list
(iii) Incidence matrix (5)
e5
A
B
e1 e2 E
e3
C D
e4
7. (a) What is dynamic memory management? Explain the buddy system method of memory manage-
ment with its advantages and disadvantages. (5)
(b) Draw a hash table with open addressing and a size of 9. Use the hash function “k%9.” Insert the
keys: 5, 29, 20, 0, 27 and 18 into your table (in that order). (5)
8. (a) An array contains the elements shown below:
44, 78, 22, 7, 98, 56, 34, 2, 38, 35, 45
Arrange the elements of the array using quick sort. (5)
(b) An array contains the elements shown below. Trace the steps followed to find 20 using the binary
search algorithm.
18, 13, 17, 26, 44, 56, 88, 97 (5)
Solutions
1. (a) A stack is a linear structure in which items may be added or removed at one end, which is called
the top of the stack. This means that the last item to be added to a stack is the first item to be
removed. So, stacks are also called last in first out (LIFO) lists. Pop is one of the basic operations
of stack and is used to delete an element from a stack.
(b) Conversion to postfix:
a ++ –b ++ + – –cd/ + ef*
a ++ –b ++ + – –cd/ ef*+
a ++b ++– –cd/ef*++–
Conversion to prefix:
a ++– b++ / – –cd + *ef
a ++ – b++ ++/– – cd*ef
– a ++ b++++/– – cd*ef
(c) A queue consists of a linear list of elements in which deletion takes place at one end called the front
and insertion takes place at another end called the rear. Queue is also called FIFO. The disadvan-
tages of queues are as follows:
• Whenever an element is deleted from the queue, the value of front is increased by 1. Front =
Front+1.
• Whenever an element is inserted in a queue, the value of the rear is increased by 1. Rear = Rear
+1. When the rear indicates the last location of the queue it means that the rear is full. If there
is space in the queue but the rear indicates the last location, we are unable to insert the element.
If we try to do so, the program will flash the message “Queue Full” though it is empty.
(d) The simplest way to resolve a collision is through closed hashing. Suppose there is a hash table of
size h and the key value of interest is mapped to an address location, i, with a hash function. Closed
hashing can be stated as indicated below:
Start with the hash address where the collision has occurred and let it be i. Then the sequence
below needs to be followed:
i, i +1 , i +2,…,h, 1, 2,…,i – 1
The search will continue until any one of the following cases occurs:
• The key value is found.
• Unoccupied (or empty) location is encountered.
• It reaches the location where the search was started.
Example: Assume there is a hash table of size 10. The hash function uses the division method with the
remainder module 7, namely H(K) = (k%7) + 1.
Let us consider the build up of the hash table (initially, which is empty) with the following set of key
values:
15, 11, 25, 16, 9, 8, 12
The hash table for the values above is shown below.
1 1 1 1 1 1 1 1
2 2 15 2 15 2 15 2 15 2 15 2 15 2 15
3 3 3 3 3 16 3 16 3 16 3 16
4 4 4 4 4 4 9 4 9 4 9
5 5 5 11 5 11 5 11 5 11 5 11 5 11
6 6 6 6 25 6 25 6 25 6 25 6 25
7 7 7 7 7 7 7 8 7 8
8 8 8 8 8 8 8 8 12
9 9 9 9 9 9 9 9
10 10 10 10 10 10 10 10
Initialty the Insertion Insertion Insertion Insertion Insertion Insertion Insertion
hash table of 15 of 11 of 25 of 16 of 9 of 8 of 12
is empty
(Buildings-up hash table)
(f ) A header linked list is a linked list that always contains a special node, called the header node at
the beginning of the list. Two widely used header lists are:
1. A grounded header list is a header list where the last node contains a null pointer.
Start Header
mode
x
(Grounded header list)
2. A circular header list is a header list in which the last node points back to the header node.
Start
Level 1
Level 2
Height = 3
(h) If n = 3, then edge = n –1, i.e = 2
An example to prove that a tree T with n vertices has n–1 edges is given below:
Case I:
A
B C
Case II:
A
Case III:
A
Case IV:
A
Case V:
A
(j) Diagraph: A diagraph is also called a directed graph. It is a graph G, such that G = <V, E>, where
V is the set of all vertices and E is the set of ordered pairs of elements from V.
V1 V4
V2 V3
Digraph
Undirected graph: In the case of an undirected graph, the pair (Vi, Vj) is unordered, i.e. (Vi, Vj)
and (Vj, Vi) are the same edges. However, in the case of a directed graph, they correspond to two
different edges.
V1 V4
V2 V3
An undirected graph
n−
2 1 01
n−
2
3
j+1 4
j−1 i−1
Rear Front i+1
Front
The following figure shows a circular queue with eight elements. Here we need two variables, front
and rear, to keep track of the elements to be deleted and inserted.
9[7] 9[0]
9[6] 9[1]
9[5] 9[2]
9[4] 9[3]
Circular queue: The following assumptions are made for circular queue.
• A front value of –1 indicates that the queue is empty.
• The rear is incremented by 1 each time a new element is inserted into the queue, i.e. rear = rear + 1.
• Each time an element is deleted from the queue the value of front is incremented by 1, i.e.
front = front+1.
• If front = rear, the queue contains only elements (except front = rear = –1)
A roundtable conference is a practical example of a circular queue.
3. (a) A program in C to create 5 nodes of a linked list is given below:
#include<stdio.h>
#include<malloc.h>
Struct link
{
int info:
struct link *next:
}
Void Create List(struct link*);
Void Display(struct link*);
/* Function main*/
Void main()
{
Strcut link *node;
Clrscr();
Node=(struct link*)malloc (sizeof (struct link));
If (node = = NULL)
{
Printf(“input of m/r space)
Exit(0);
}
Createlist(node);
Display(node);
}
/* Create the list*/
Void Create list (struct link* node)
{
Char ch;
int i=1;
3. (b) Priority queue is a queue in which it is possible to insert an element or remove an element at
any position depending on the priority in the queue. We can say that a priority queue is a series
of queues representing situations in which the priorities associated with the queue elements are
known.
Example: The telecom department maintains a list of telephone subscribers in alphabetical order.
If the list is very large, it is highly cumbersome to locate the numbers of subscribers with names
that begin with letters that appear in the middle of the alphabet. The remedy is to divide the list
into 26 sub-lists. The processing time will reduce automatically.
4. (a) Algorithm to find the largest node in BSI:
TREE-MAXIMUM(x):
1. while right[x] != NIL
2. do x<- right[x]
3. return x.
We will start from the root. The largest element will always be placed as the right child.
79
67
58
38
29
15
11
16 11 15 16 11 42 16 11 42 40
92 92 92
23 45 23 45 64 45
16 11 42 40 64 11 42 40 23 11 42 40
64 16 16 29
92 92 92
64 45 64 45 64 45
29 11 42 40 29 11 42 40 29 18 42 40
16 23 16 23 18 16 23 11
7(0) 56(0)
7(0) 7(−1)
7 7
14 56(−1) 14 56
10 33(1) 75(−2) 10 33 80
R R
23(0) 80(−1) 23 75 90
R
90(0)
F S
B H R T
G G Y W
e5
A E
B
e1 e2
e3
C D
e4
A C D B E
A 0 1 0 0 1
C 1 0 1 1 0
D 0 1 0 1 0
Adjacency matrix
B 0 1 1 0 0
E 1 0 0 0 0
Adjacency list
Node Adj
A C, E
C A, B, D
D C, B
B C, D
E A
7. (a) Dynamic memory management: In a single programming operating system, the main memory is
divided into two parts—one is for the operating system and the second is for the user process cur-
rently executed. The main features are as follows:
• In a multiprogramming environment the user space is divided into a number of partitions.
• Each portion is for one process. The task of sub-division is carried out dynamically by the
operating system.
• There are four functions of memory management:
º Keeping track of the parts of memory currently being used and by whom.
º Deciding which processes are to be loaded when memory space becomes available.
While(a[i]<key)
i++;
while(a[j]>key)
j--;
if(i<j)
swap(a[i],a[j])
else
swap(a[j],key)
44 45 22 7 98 56 34 2 38 35 78
44 35 22 7 98 56 34 2 38 45 78
44 35 22 7 38 56 34 2 98 45 78
44 35 22 7 38 2 34 56 98 45 78
34 35 22 7 38 2 44 56 98 45 78
34 2 22 7 38 35 44 56 45 98 78
7 2 22 34 38 35 44 45 56 98 78
2 7 22 34 35 38 44 45 56 78 98
The sorted array is as follows:
27, 22, 34, 35, 38, 44, 45, 56, 78, 98
(b) An array contains the elements:
18, 13, 17, 26, 44, 56, 88, 97
Steps to find 20:
18
13 26
20 44
27
56
88
97