Systematic Approach To Data Structures Using C
Systematic Approach To Data Structures Using C
USING
A. M. PADMA REDDY
.----'--
l' 1C?30b~
Systematic Approach
To
Data structures
(with C)
aoot< sTO
Nt-fCE
B~ -~ Avaltab1a
BUY seOlRights are vritb us
Re~:~aU.ham. aeftgaluna
,.
FIRST EDITION SEP-1999
SECOND EDITION OCT -2002
THIRD EDITION AUG - 2003
FOURTH EDITION AUG - 2004
FIFTH EDITION AUG - 2005
SIXTH EDITION SEP - 2006
SEVENTH EDITION AUG,- 2007
EIGTH REVISED EDITION AUG - 2008
NINTH REVISED EDITION AUG - 2009
Note Due care and diligence has been taken while editing and printing this book.'
Neither the author nor the publishers of the book hold any responsibility for any
mistakes that may have inadvertently crept in and are not responsible for any
consequential damages caused. The book acts as just a reference to move forward
and understand the concepts.
to
Nly children
P. Mona1iJca
&
P.Mithil
Acknowledgements
-,
A.M. Padma Reddy
To Readers
I thank all the readers for providing encouragement by providing the feedback and
suggestions to improve the quality of the book. In this re-revised version, pointers,
structures, union file-handling chapters have been updated. The chapters on stacks,
queues, linked lists, trees, sorting and searching techniques have been updated and
most of the errors identified by readers are' corrected. The book has been updated
and completely modified so that it is easy to read and understand the concepts. 1
thank all those who gave me suggestions by reading this book and pinpointing the
mistakes. This time too I request all the readers to come with suggestions t(
improve the quality of this book thereby making this a best book on Datr
structures (with C).
III SEMESTER
Unit I
1. Pointers: Concepts, Pointer variables, Accessing variables through pointers,
Pointer declaration and definition, Initialization of pointer variables, Pointers
and functions, Pointer to pointers, Compatibility, Lvalue and Rvalue, Arrays
and pointers, Pointer arithmetic and arrays, Passing an array to a function,
Understanding complex declarations, Memory allocation functions, Array of
pointers. - 7 hours
Unit II
2.' Strings: String Concepts, C strings, String I/O functions, Array of strings,
String manipulation functions, Memory formatting - 2 hours
PART-B
(Data structures with C)
Unit III
5. The Stack: Defmition and Examples, Representing Stacks in C, An Example-
Infix, Postfix and Prefix - 7 hours
Unit IV
6. Recursion: Recursive Definition and Processes, Recursion in C, Writing
Recursive Programs, Simulating Recursion, Efficiency of Recursion
- 4 hours - 4 hours
Unit V
8. Lists: Linked Lists, Lists in C, An Example - Simulation using Linked Lists
- 6 Hours
Unit VI
9. Lists: Other List structures - 6 Hours
Unit VII
10. Trees: Binary Trees, Binary Tree Representations - 6 Hours
Unit VIII
11. Trees: Representing Lists as Binary Trees, Trees and their applications
- 7 Hours
Text Books
1. Behrouz A. Foruzan and Richard F. Gilberg, Computer Science A Structured
Programming Approach Using C, Second Edition, Thomson, 2003
(Chapter 9.1 to 9.9, Chapter 10.1 to 10.6, Chapter 11.1 to 11.6, Chapter 12.1 to
12.8, Chapter 13.1 to 13.3)
2. Data Structures using C, Aaron M. Tenenbaum, Yedidyah Langsam & Moshe
J. Augenstein, Pearson EducationIPHI, 2006
(Chapter 2, 3, 4, 5.1, 5.2, 5.4, 5.5)
Reference Books
3.. Richard F. Gilberg and Behrouz A, Forouzam, Data Structures A
pseudocode apparoach with C, Thomson 2005
4. Robert Kruse & Bruce Leung, Data Structures and Program Design in C,
Peason Education, 2007
Unit V
8. Lists: Linked Lists, Lists in C, An Example - Simulation using Linked Lists
- 6 Hours
Unit VI
9. Lists: Other List structures - 6 Hours
Unit VII
10. Trees: Binary Trees, Binary Tree Representations - 6 Hours
Unit VIII
11. Trees: Representing Lists as Binary Trees, Trees and their applications
- 7 Hours
Text Books
1. Behrouz A. Foruzan and Richard F. Gilberg, Computer Science A Structured
Programming Approach Using C, Second Edition, Thomson, 2003
(Chapter 9.1 to 9.9, Chapter 10.1 to 10.6, Chapter 11.1 to 11.6, Chapter 12.1 to
12.8, Chapter 13.1 to 13.3)
2. Data Structures using C, Aaron M. Tenenbaum, Yedidyah Langsam & Moshe
J. Augenstein, Pearson EducationJPHI, 2006
(Chapter 2, 3, 4, 5.1, 5.2, 5.4, 5.5)
Reference Books
3.. Richard F. Gilberg and Behrouz A, Forouzam, Data Structures A
pseudocode apparoach with C, Thomson 2005
4. Robert Kruse & Bruce Leung, Data Structures and Program Design in C,
Peason Education, 2007
Note: The question paper consists of two parts A and B containing 2 and 6
questions respectively. The student is required to answer any 5 questions
selecting at least one question from Part A
Contents
Part 1· Advanced C Concepts
Chapter 1
Introducing to C
1.1 Introduction : 1.1
1.1.1 Character set (Alphabets of C) 1.2
1.1.2 C tokens : 1.3
1.1.3 Keywords 1.3
1.1.4 Identifiers 1.4
1.1.5 Constants 1.5
1.2 Meaning of variables 1.10
1.3 Basic/SimplelPrimitive Data types 1.10
1.3.1 int 1.10
1.3.2 float 1.11
1.3.3 double 1.11
1.3.4 char : 1.11
1.3.5 void : 1.11
1.4 User defined data types 1.12
1.5 Declaration of variables 1.13
1.6 Operators ' .' 1.13
1.6.1 Classification based on the number of operands 1.14
1.6.2 Classification based on the type of the operation 1.15
1.7 Arithmetic operators 1.15
1.7.1 Arithmetic expressions : 1.16
1.7.2 Modes of expressions 1.17
1.7.3 Arithmetic operator's precedence (Precedence of operators) 1.17
1.7.4 Relational operators 1.20
1.7.5 Logical operators 1.21
1.7.6 Relational and Logical expressions ~..1.22
1.7.7 Assignment operator 1.22
1.7.8 Increment and decrement operators 1.23
1.7.8.1 increment operator , 1.23
1.7.8.2 decrement operator 1.25
1.7.9 Conditional operator 1.26
1.7.] 0 Bitwise operators 1.27
1.7.11 Special operators (comma, sizeof operators) : 1.28
1.8 The precedence of operators (Hierarchy of operators) 1.29
1.8.1 Left associative operator 1.29
1.8.2 Right associative orerator.. 1.30
1.9 Sequential statements 1.31
1.10. Branching statements 1.33
1.10..1 The if statement/simple-if statement... 1.33
1.10..2 The if-else statement 1.34
1.10..3 The nested if-statement : 1.36
1.10..4 The else-if-ladder statement... · 1.37
1.10..5 The switch statement 1.38
1.10..6 The for statement 1.41
1.10..7 The while statement.. 1.42
1.10..8 do-while statement., ..: 1.44
1.11 Arrays 1.45
1.12 Insertion sort ..: 1.50.
1.13 Why functions : 1.53
1.14 Memory organization in C l.68
1.15 Storage classes (Local/globallregister/static ) 1.69
PART-A
UNIT-l
Chapter 2
Pointers
UNIT-2
.Chapter 3
Strings
Chapter 4
Derived types-Enumerated, Structure, and Union
Chapter 5
Binary files
UNIT-4
Chapter 7
Recursion
Chapter 8
Queues
UNIT-5
Chapter 9
Lists
UNIT-6
Chapter 9 continued
)
hapter 11
Sorting and searching
11.1 Introduction 11.1
11.2 Bubble sort ~ 11.2
11.3 Selection sort : r: 11.5
2. Conduction 30
Execution of the program, and showing the
results in proper format
3. Viva-voce** ) 10
.
Total Max. Marks 50
I
3/'Write a C program, which accepts the Internet Protocol (IP) address in decimal
dot format (ex: 153.18.8.105) and converts it into 32-bit long integer
(ex:2568095849) using strtok library function and unions.
Note: Only one set of operations among a, band c with d may be asked in the
examination
·
integers using singly linked list and to perform the following operations: - ./
·-Push ~
~ /
• Display ~
The program should print appropriate messages for stack o,vrflo~ a;d stack
empty.
Solutiouz Same as example 9.3.5, Page 9.31, lXlt; instead of insert_frontO call
insert-rean) given in example 9.3.2
12.Write a C Program to support the following operations on a doubly linked list
where each node consists of integers: .
• Create a doubly linked list by adding each node at the front.
• Insert a new node to the left of the node whose key value is read as an input
• Delete the node of a given data, if it is found, otherwise display appropriate
message.
• Display the contents of the list.
Note: Only either (a,b and d) or (a, c and d) may be asked in the examination
13.Write a C Program
• To construct a binary search tree of integers.
• To traverse the tree using all the methods i.e., inorder, preorder and postorder.
• To display the elements in the tree.
1.1Introduction
Thischapter gives a brief description of various types of constants, data types,
operatorsand control statements available in C language. This chapter also gives a
briefdescription on arrays, functions and and memory organization of C language.
ote: The author assumes that the reader has fair knowledge of C language. In case,
thereader wants to know more fundamentals and the method of writing C programs
stepby step in a systematic manner before proceeding to Advanced C and data-
structures,he/she can refer the book "Computer Concepts and C Programming
Techniques" by Padma Reddy A.M
Step1: As a first step, before learning any language (such as English), we learn the
alphabets of a language. Here, we learn the alphabets of C language.
Step2: In the second step, we learn how one or more alphabets can be grouped
together to form meaningful words. In C language, these meaningful words
are called tokens.
Step3: In the third step, we learn how one or more words (called tokens) are
grouped together to form meaningful sentences. In C language, the sentences
are called instructions or statements.
Step 4: In the final step, we learn how one or more sentences can be grouped
together to form paragraphs. In C language, these collections of paragraphs
(group of statements) constitute a complete program.
1.2 Q Introduction to C
1 1 #include <stdio.h>
void maim)
Paragraphs Program {
int si, p = 1000, t = 2, r = 5;
Definition: The characters that are used while wntmg C programs are called
alphabets or character set. Using these alphabets, we can obtain meaningful numbers,
meaningful expressions and statements. For example, the alphabets of C language are
shown below:
• Letters: Upper case letters from 'A' to 'Z' and lower case letters from 'a' to 'z'
• Digits: from 0 to 9
1.2 .Q Introduction to C
1 1 #inc1ude <stdio.h>
void mairu)
Paragraphs Program {
int si, p = 1000, t = 2, r = 5;
Definition: The characters that are used while writing C programs are called
alphabets or character set. Using these alphabets, we can obtain meaningful numbers,
meaningful expressions and statements. For example, the alphabets of C language are
shown below:
• Letters: Upper case letters from 'A' to 'Z' and lower case letters from 'a' to 'z'
• Digits: from 0 to 9
Q Systematic Approach to Data Structures using C 1.3
• Symbols such as
- tilde # hash % percent
A caret ( left parentheses = assignment
- rmnus srgn ) right parentheses underscore
+ plus sign -' {left brace I vertical bar
/ division symbol } right brace ' single quote or apostrophe
* asterisk (or star) [left bracket dot
; semicolon ] right bracket " double quotation
: colon < less than ? question mark
, comma > greater than \ back slash
• White spaces: Characters such as space, tab(\t), new line(\n), carriage return(\r)
etc. are also the alphabets of C language and are called white spaces.
1.1.2 C Tokens
Oncewe know the alphabets of C language, the second step in learning C language is
"How these alphabets are combined to get meaningful words called tokens?" Now,
letus see "what are C tokens?"
DcfinitionA token is a smallest or basic unit of any program. One or more characters
are grouped in sequence to form meaningful words. These meaningful words are
called tokens. The tokens in C language are broadly classified as shown below:
Keywords Ex: if, for, while etc.
Identifiers Ex:sum, length, i etc.
Tokens __ -+---:~Constants Ex)O, 10.5, 'a', "padrna" etc.
1.1.3Keywords
In this section, let use see "What are keywords? What are the various keywords
available ill C?"
Dcfinition: The words which have predefined meaning in C language are called
.f words.Since they are reserved for specific purpose in C language, they are also
cal ed reserved words. The keywords have some specific purpose in C language and
1..4 Q Introduction to C
~ence can not be used as variable names, function names etc. The different keywords
available in C language are shown b~low:
Keywords
auto double int struct
break else long switch
case enum register typedef
char extern return umon
canst float short unsigned
continue for signed void
default go to sizeof volatile
do if static while
Note: The meaning of the keywords can not be changed by the user. One more
important thing to remember is that all keywords should be written, in lower case
letters. We should never use capital letters while using keywords. If 'we use capital
letters they are treated as just identifiers.
1.1.4 Identifiers
In this 'Section, let use see" What are identifiers?"
De nition: An identifier is the fancy term used in place of 'name '. An identifier is a
word consisting of sequence of one or more letters or digits along with "_" (read as
underscore). The rules to be followed to frame an identifier are:
• The first character in the identifier should be a letter or "_" (read as underscore)
and can be followed by any number of letters or digits or underscores.
• No extra symbols are allowed other than letters, digits and "_".
• The length of an identifier can be up to a maximum of 31 characters.
if'" fA'~.(~a'"~Id~ •• ~~ "<. ;,L • .~". ...d: s •• • Y ••
---
a--- 1 valid
Sum1 valid -
~ Systematic Approach to Data Structures using C 1.5
Note: Do not use only underscores as an identifier, even though it is valid identifier.
Trythis program:
void maim)
{
int -= 10', /* Note: Underscore is used as a variable *1
printf("%d",-.J; /* Output: 10*/
}
1.1.5 Constants
In this section let us see "What are constants? What are the different types oJ
constants?"along with examples of valid and invalid constants.
Definition: A constantis a data item which will not change during the execution of a
program.The constants in C language are classified as shown below:
Integer
Floating point
Constants
Character constant
String constant
should precede the number. An integer constant can be classified into three types as
shown below:
Integer
constantst Octal For example, 010, 0140, -0140 etc.
Now, let us answer the question "What is a decimal number? Explain with example"
Definition: A decimal integer constant can be any combination of digits from '0' to
'9'. No extra characters are allowed other than + and - sign. If + and - are present,
they should precede the number. For example, 100, -67, +989 etc. are valid. The
following are invalid:
Now, let us answer the question "What is an octal number? Explain with example"
Definition: An octal integer constant can be any combination of digits from '0' to '7'
with a prefix 0 (digit zero). No extra characters are allowed other than + and - sign.
If + and - are present, they should precede the number. For example, 010, 0777, -065
etc. are valid. The following are invalid:
Now, let us answer the question "What is a hexadecimal number? Explain with
example"
Q Systematic Approach to Data Structures using C 1.7
Note: A number starting with 0 is an octal number and should have digits from 0 to 7.
Usuallydecimal integer constants are used in programming. Octal and hexadecimal
constantsare rarely used.
Definition: The numeric quantities having fractional part are called floating point
constants.All negative 'numbers should have a prefix '-'. A positive number can have
an optional '+' sign. No other extra characters are allowed. The floating point
constantscan be represented using two forms as shown below:
~ Fractional form
Floating point
notations ~ Scientific notation
(Exponent notation)
Fractional form: Now, the question is "How to represent floating point numbers
usingfractional form? Give examples" A floating point number represented using
fractionalform has an integer part followed by a decimal point and a fractional part.
We can omit the digits before the decimal point or after the decimal point.
For example,0.5, -0.99, -.6, -9., +.9 etc are all valid floating point
numbers.
o ~Rt~ger(:!~
~yalid/invalid iReasons for invalidity
10,000 invalid .Comma not allowed
OxABGH invalid G and H are invalid characters
Ox123.45 invalid Decimal point not allowed
- -
01234 invalid Should precede with Ox or OX
Note: A number starting with 0 is an octal number and should have digits from 0 to 7.
Usuallydecimal integer constants are used in programming. Octal and hexadecimal
constantsare rarely used.
Definition: The numeric quantities having fractional part are called floating point
constants.All negative 'numbers should have a prefix '-'. A positive number can have
an optional '+' sign. No other extra characters are allowed. The floating point
constantscan be represented using two forms as shown below:
~ Fractional form
Floating point
notations ~ Scientific notation
(Exponent notation)
Fractional form: Now, the question is "How to represent floating point numbers
usingfractional form? Give examples" A floating point number represented using
fractionalform lias an integer part followed by a decimal point and a fractional part.
We can omit the digits before the decimal point or after the decimal point.
For example, 0.5, -0.99, -.6, -9., +.9 etc are all valid floating point
numbers.
Note: The backslash characters such as \a, \b, \t, \v, \n, \f, \r and \0 are non-printable
characters.
1.Z"Meaning of variables
Definition: A variable is defined as the name given to a memory location where the
data can be stored, accessed or manipulated. In short, an identifier which is used to
store a value is called variable. The value stored in the variable may change during
execution of the program.
Note: A variable name should not be a C keyword such as if, lor, else, while etc. See
section 1.1.4 for valid and invalid variables (identifiers).
Note: Always it is better to use the variables with meaningful names for easy
understanding of the program. Do not use only underscore as variable name, even
though it is valid.
int
char
Primitive
Data types --+--. float
double
void
Note: The primitive data types are also called basic data types or simple data types or
fundamental data types.
1.3:1 int
It is a keyword which is used to define integer numbers. Normally they are
associated with the variables to store signed integer values in memory locations. That
is, using this data type both positive and negative numbers can be stored in the
memory. To represent only unsigned numbers it is normally associated with a
qualifier unsigned.
•
,!;lSystematic Approach to Data Structures using C 1.11
For example, unsigned int is used to define only positive numbers. Negative
numberscan not be stored if a variable is associated with unsigned int. The size of
integer and range of integer varies from machine to machine as shown below:
or
o to 65535
32-bit machine 4 bytes o to 2 -1
or or
o to 4294967295 2147483648 to 2147483647
1.3.2 float
It is a keyword which is used to define floating point numbers. The floating point
numbersare also called real numbers. Normally they are associated with the variables
(alsocalled identifiers) to store floating point numbers in memory locations. That is,
usingthis data type both positive and negative floating point numbers can be stored in
thememory.
1.3.3 double
It is a keyword which is used to define BIG floating point numbers with much higher
precision.Normally they are associated with the variables to store BIG floating point
numbersin memory locations. That is, using this data type both positive and negative
BIG floatingpoint numbers can be stored in the memory.
1.3.4 char
It is a keyword which is used to define single character or a sequence of characters
calledstring. Normally, they are associated with the variables to store a character or a
stringin memory locations. Each character stored in the memory is associated with a
unique value called an ASCII (American Standard Code For Information
Interchange)value.
1.3.5 void
It is an empty data type. It is normally used in functions to indicate that the function
doesnot return any value. Since no value is associated with this data type, it does not
occupyany space in the memory. Normally, it is not associated with any variable
(exceptpointers).
•
1.12 Q Introduction to C
Definition: The programmer can define his/her own identifiers to represent a data
type using primitive data types or derived data types. These data types that are
renamed or defmed by the programmer for his/her convenience are called user
defined data types. The user defined data types car. also be used to declare the
variables. Consider the syntax shown below:
where
• typedef is the keyword.
• data_type can be basic or derived or another user defined data type.
• identifier is the new name given to the ~ata_type.
typcdcf f1oatAMOUNT~
Here, the identifier AMOUNT can be considered as a new data type, derived from the
basi data type float. For example, in the declaration
AMOUNT a;
Definition: The compiler reserves the memory for the variables so that the data can
be accessed, manipulated and stored using these variables. For this to happen, all the
variablesshould be declared in the beginning of the program. This way of declaring
all the variables in the beginning of the program is called. type declaration or
declarationoj variables. The. purpose of declaring the variable is to know the type of
datato be stored and to reserve the required amount of memory for the variables. The
reservingmemory for the variables based on the data type is done by the compiler.
Forexample,
int number, choice, flag;
float amount;
In this example, the variables such as number, choice and flag are integer variables.
This declaration indicates that these variables contain only integer values. The
variable amount should contain only floating point number since it is declared as
float.
Note: Always it is better to use the identifiers with meaningful names for easy
understandingof the program.
Now, we will see how these variables and constants are joined together usmg
operatorsto obtain the expressions and how to evaluate the expressions.
1.6 Operators
Now, let us see "What is an operator?" An operator is a symbol that specifies the
operation/activity to be performed. For example, + means add, - means subtract, *
1.14 Q Introduction to C
means multiply and so on. Here, + , -, * , I etc., are some of the operators in C. C
language includes large number of operators. The operators in C can be classified
based on
• The number of operands an operator has.
• The type of operation being performed.
Unary operators
Binary operators
Operators
Ternary (?:)
Definition: An operator which acts on only one operand to produce the result is
called unary operator. In the expressions involving unary operators, the operators
precede the operand. For example, -10, -a, *b, &c, -d etc., are all the expressions
with unary operators.
Definition: An operator which acts on two operands to produce the result is called a
binary operator. In an expression involving a bi~ary operator, the operator is in
between two operands.
For example, a + b, a * b, 10/5, 10/a etc., are all expressions with binary
operators.
In this example, there an, three operands a, band c (hence the name ternary). The two
operators are? and :
E
comma operator (,)
size of operator
Address operator (&)
etc.,
Now, let us discuss each of these operators one by one in detail.
1.7Arithmetic Operators
In this section, let us see ••What are arithmetic operators? What are the operations
performed by these operators? Explain with examples"
1.16 Q Introduction to C
Definition: The operators that are used to perform arithmetic operations such as
addition, subtraction, multiplication etc., are called arithmetic operators. Arithmetic
operators can be classified into two types based on the number of operands as:
• Arithmetic binary operators
• Arithmetic unary operators
The meaning of all the operators along with examples is shown below:
Description Symbols Meaning Example result
/"
Mod % remainder 4%2 0
Note: Modulus operation denoted by % is used only for integer values and not for
floating point numbers. This operator returns the remainder after division. Some other
examples involving modulus operations are shown below:
Example Result
3%2 1 [Divide 3 by 2. The remainder is 1]
5%3 2 [Divide 5 by 3. The remainder is 2]
2%5 2 [Divide 2 by 5. The remainder is 2]
-5%3 -2 [Divide -5 by 3. The remainder is -2]
For example
10 + 2 * 5 II Here, 10,2 and 5 are operands whereas +, * are operators
(a + b - c) 12 II Here, a, band c are operands and +, - are operators.
are arithmetic expressions.
Q Systematic Approach to Data Structures using C 1.17
Integer expression: If all the operands such as constants and variables are of type int,
theresult obtained will be of type integer. The resulting expression is called integer
expression. For example,
4/2 = 2 (In 412,4 and 2 are operands and / is an operator)
4/3 = 1 (Note: Answer is not 1.3333 because both operands are integer
and hence the result should be in integer format)
3/4= 0 (Note: It is not 0.75 because both operands are integer and
hence the result should be integer. The value after dot .',<,
should be discarded)
floating point expression: If all the operands such as constants and variables are of
typefloat or double, the result obtained will be of type float or double. The resulting
expressionis called floating point expression. For example,
4.0/2.0 = 2.'0 .
4.0/3.0 = 1.333
3.0/4.0 = 0.75
fixed mode expressions: If the operands such as constants and variables are of
differentdata types such as int.float, double etc .., the expression is called mixed mode
expression. DUring evaluation, all the values should be of same data type. So, data
conversion takes place from one data type to another data type (implicit type .
conversion).This is done normally done by converting/promoting lower data type to
higherdata type. For example,
4.0/3 = 1.333 ( Note:Here, the integer 3 is converted into float, then
4. / 3 = 1.333 (evaluation is carried out and hence, the result is float)
Exa mple 1.7.3.1: What is the result of 6 x (2 + 3) / 5? Based .on the BODMAS rule,
in this example, the following operations have to be carried out in sequence:
• First preference is to evaluate within Brackets
.• Second preference for Division .
• Third preference for Multiplication
Based on the BODMAS rule (priority), we evaluate -the given expression as shown
below:-
6x~/5
Brackets have the first precedence
6 x '---y--J
5 / 5
Division has the second precedence
6 x 1
'---.r---'
Multiplication has the third precedence
6
Thus, based on the priority given to each operation in the BODMAS rule, the
expressions are evaluated. These rules are called precedence rules or precedence of
operators or hierarchy of operators Before worrying about precedence of operators
in C, let us see the evaluation of an expression with two or more operators having the
same priority.
Example 1.7.3.2: What is the result of8 + 4 + 3? All of us evaluate the expression as
shown below:
8 + 4 + 3
'-y-' [First add 8 and 4 to get 12] /
12 + 3
'---y-U
[Next add 12 and 3 to get 15]
15
Observation: In the above expression, same operator "+".is used twice and hence
both operators have the same priority and the precedence rules are not applicable.
Q Systematic Approach to Data Structures using C 1.19
Sinceevaluation is done from left to right one after the other, we say that the operator
"+" is left-to-right associative (also called left associative). Now, let us "Define left-
to-right associative. Give examples"
I finition: In an expression, if there are two or more operators having the same
priorityand are evaluated from left-to-right, then the operators are called Left to Right
ciauve operators. We normally denote using L ~ R. They are also called left
associativeoperators. This process of evaluating from left to right is called left
associativity.
For example,
8+4+3=15
8-4-3=1
Inthisexample, the operators ,"+" and "-" are left associative operators.
Oncewe know various relational operators, the next question is "What are relational
pressions ?"
Now,the question is "What are the rules to be followed while evaluating relational
ressiolls?"The rules to be followed while evaluating the expression consisting of
arithmeticoperators and relational operators are shown below: .
• Evaluate the expression within brackets
• Evaluate unary operators
• Evaluate arithmetic expressions
• Evaluate relational expressions
fmition:As we have logic gates such as AND, OR and NOT whose output is 1 or
0, we also have logical operators. After evaluation, expression consisting of logical
operatorsresults in either true or false and hence such expressions are called a logical
expressions.The logical operators and the meaning associated with them are shown
below:
Description Operator Priority Associativity
Logical
Operators -E not (unary operator)
and (binary operator) &&
or (binary operator)
!
II
1
2
3
Left to right
Left to right
Left to right
otePuring evaluation, the ! operator has highest priority, the operator && has the
nexthighestpriority and the operator II has the least priority.
Nowthe question is "When logical operators are used?"'fhe logical operators are
usedto combine two or more relational expressions. Since the output of relational
expressionsis true or false, the output of logical expression is also true or false.
For examplejf a = b && b = c, then the triangle is equilateral triangle.
Here,two relational expressions are combined together by a logical && operator.
~1.22 Q Introduction to C
Definition: An operator which is used to copy the data or result of an expression into
a memory location (which is identified by a variable name) is called an assignmen
operator. Copying or storing into a memory location is called assigning and hence the
name. The assignment operator is denoted by '='...sign. The syntax is shown below:
horthand assignment operators We have observed that parents call their children
byshorthandnames. We also use shorthand names to call our friends. Likewise, we
canalsouse shorthand notations for assignment operator. For example, short hand
assignmentoperators such as +=, - =, *=, /= etc., can be used to assign values. The
tablebelow shows short hand operators, short hand statements and the meaning
associatedwith them.
Observethat in all these cases the value of the variable a changes. In fact, the value of
a variablemay change during the execution of the program.
If initialvalue of a is 20, in both cases after executing, the value of a willbe 21. If
finalvalues are same using both the types of operators, then "What is post increment?
What is pre increment?
1.24 Q Introduction to C
Post increment If the increment operator + Pre increment If the increme
++ is placed after (post) the operand, then operator ++ is placed before (pre) tht c
the operator is called post increment. As operand, then the operator is calla v
the name indicates, post increment means pre increment. As the name indicates.
increment after (post) the operand value pre increment means increment befon
is used. So, operand value is used first (pre) the operand value is used. So.
and then the operand value is the operand value is incremented by
incremented by 1. and this incremented value is used. I
} }
Note: Observe that the outputs of both the programs are same. In this context, there s
no difference between the two. In fact, if post increment or pre increment appear
alone along with the operand and are not part of any expression, then there is no
difference in the final value. But, the difference comes into picture, if they are part of
the expression or part of the assignment statement. Now, let us see "What is III
difference between post increment and pre increment?" by taking an example.
Post increment Pre increment
PROGRAM I Tracing I>ROGRAM Tracing
VOl mam void maim) I.
{ I Initialization { I Initialization
int a = 20; I a = 20 int a = 20; a=20
int b; I b =? intb; I b=?
I
b = a++; 1* b = a *11 b = 20 b=++a; 1* a=a+ 1 *1 I a = 21
1* a=a+l */1 a = 21 1* b = a *11 b = 21
IOlltput I Output
printf("%d" ,a); I 21 printf("%d" ,a); 21
I 21
printf("%d",b ); I 20· printf("%d" ,b);
} } I
I I
Note:b=a++ ~ b=a; Note:b=++a ~ a=a+l;
a= a + 1; b =a;
Q Systematic Approach to Data Structures using C 1.25
~nt OIl': Post increment means increment after (post) the value of a is used. So, first a is
he copied into b, then a is updated by 1. Pre increment means increment before (pre) the
ed valueof a is used. So, firs a is updated by 1 and then updated value is copied into b.
:s, Oncewe know what increment operator is, let us discuss about decrement operator.
re
1.7.8.2 decrement operator
In this section, let us see" What is a decrement operator? What are the different type»
1" innent operator?" .
Dctlnition: -- is a decrement
operator. This is an unary operator: It decrements the
valueof the operand by one. The decrement operator is classified into two categories
as shown below:
..!Yl!£ Operator Example result
Decrement
Operator
-+[ Post decre~ent a-- 19
If initial value of a is 20, in both cases after executing, the value of a will be 19. If
final values are same using both the types of operators, then" What is post decrement?
II, .. IS pre decrement?"
Definition: The conditional operator is also called ternary operator. As the narn
indicates, an operator that operates on three operands is called ternary operator.' Th
ternary operators are? and: The syntax is shown below:
(expl) ? exp2 : exp3;
where
• expl is an expression evaluated to true orfalse
.• if expl is evaluated to true, exp2 is executed
• if expl is evaluated to false, exp3 is executed
1.7.10Bitwise operators
ow, let us see "What is a comma operator and what is its advantage?"
These three different statements can be written as a single statement using cornm
operator as shown below:
This operator is used to fmd the number of bytes occupied by a variable or a constan
in the computer memory. The syntax is shown below:
sizeof (operand);
For example,
sizeof( char) 1 byte
sizeof(int) 2 bytes
sizeof(float) 4 bytes
Q Systematic Approach to Data Structures using C 1...22-
ow, let us see "What is associativity of operators? What are the different
elt fica/ions based 011 assosciativity? ..
1.30 Q Introduction to C
Definition: If all the operators in an expression have equal priority, then the directs
order chosen (left to right evaluation or right to evaluation) to evaluate an expressi
is calledassociativity of operators. They are classified into two categories based lY.
the direction of evaluation chosen as shown below:
• Left associative (Left to right associative)
• Right associative (Right to left associative)
Definition: In an expression, if there are two or more operators having the sam
priority and are evaluated from left-to-right, then the operators are called Left to Rig
associative operators. We normally denote using L ---+R. They are also called s
associative operators. This process of evaluating from left to right is called
For example,
8+4+3=15 A- Here, + and - are left associative operators
8-4-3=1 J
Now, let us see" What is right to left associative? Give examples"
,
Definition: In an expression, if there are two or more operators having the sam
priority and are evaluated from right-to-left, then the operators are calledRight to le
associative operators. We normally denote using R---+L.They are also called rig
associative operators. This process of evaluating from right to left is called rig
associativity.
For example,
i = j = k = 10; f/ The operator is right associative operator and
// assignment will be done from right to left
Now, we should see"What are control statel~ents? What are the types of contt
statements?"
Definition: The order in which the statements are executed is calledcolltroljlow. 11:
statements that are used to control the flow of execution of the program are calk
control statements. These statements alter the sequence of execution of the progra
Based on the order in which the statements are executed, the various con
statements are classified as shown below:
Q Systematic Approach to Data Structures using C l.l~Jl
Sequential
simple-if
statements
if-else
Conditional
branch statements Nested-if
Control else-if-ladder
Branching
Statements -+-~ statements switch
goto
Un conditional break
branch statements continue
Loop
Statements -f
.
or
wh'lIe
o-while
Now,let us concentrate on each of the control statement one by one.
return
1 9 Sequential statements
First,we shall see "What are ~eqllential statements? Why they are considered as
quential control statements?"
efinltionThe programmer writes a sequence of statements to do a specific activity.
Allthese statements in a program are executed in the order in which they appear in
the program. These programming statements that are executed sequentially, that is
one after the other, are called sequential control statements. Here, no separate
statementsare required to make these statements executed in sequence. The flowchart
andthe equivalent programming statements are shown below:
void maim)
{
( sti ft)
Statement-I; Statement -1 I
~
Statement-2; Statemen~2.1
I .
I
I
Statement-n;
}
1.32 Q Introduction to C
C = (5/9)(F-32)
Now, "flow 10 O\'C/'COlllC this advantage?" This is where branching statements will
make their existence. .
Q Systematic Approach to Data Structures using C \.33
1.10Branching statements
Now,thequestion is "What are branching statements? What are the different types of
'wenching statements?"
Definition:In sequential control, we know that all the statements are executed in the
orderspecifiedin the program one after the other. However, computer can skip the
executionof some statements. This skipping facility is provided by a group of
instructionscalled skip instructions. These skip instructions are also called branching
instructions/statements. Thus, the statements that alter the sequence of execution of
theprogramare called branching statements. The branching instructions are classified
asshownbelow:
simple-if
if-else
Conditional ----i
branch statements Nested-if
else- if-ladder
Branching . switch
statements
goto
Un conditional break
branch statements continue
return
Now,let us see" What are conditional and unconditional branch statements? Wliat
ire the various types?"
Definition: Normally, all the statements are executed one after the other. The
computercan skip the execution of some statements based on some condition. These
statementsthat alter the sequence of execution of the program based on some
conditionare called conditional branch statements. They are also called selection
statements or decision statements.
For example, if, if-else, else-if-ladder and switch statement.
Stat-Al';
~
Stat-A2;
I
I Stat-A21
,
I
I
't'
Stat-An;
If statement
false
if ( condition)
{
Stat_Bl;}
:
The statements
B 1 to Bn are
{I Stat-Bl
I
true
I The statements
Bl to Bn are
skipped if the
~ executed if the t condition is
Stat-Bn; condition is true Stat-Bn false
}
Stat-Cl;
Stat-C2;
I
I
T
I Stat-C21
I
I I
't' {t
Stat-Cn; I Stat-Cn I
After executing the statements AI, A2 An one after the other, the condition ii
checked. If the condition is true, the block of statements B 1, B2, ..... Bn are executed
otherwise they are skipped. After executing the statements B 1 to Bn or after skipping
the statements Cl, C2, .... Cn are executed.
way decision statement which does one thing or does another. If one set of activities
haveto be performed when the condition is true and another set of activities have to
beperformedwhen the condition isfalse, then if-else-statement is used. The syntax of
ifelse statement along with equivalent flowchart is shown below:
Syntax of if-else Equivalent flowchart
Stat-BI; ~
Stat-B2;
I
I
I Stat-B21
I
I
I
Y '+'
Stat-Bn;
If-else- statement 1----.1 •...•
-------------.
false
if ( condition)
{
Stat-TI;
. Execute T I to
Sta\-T2;
I
T2 if condition
I
'(
is true
Stat-Tn;
}
else
Stat-El ;
Execute E 1 to
Stat'jE2; E2 if condition
I
I is false I
'( '+'
}
Stat-En; I Stat-En I
I
<
Stat-Al:
I
I
I Stat-~
I
I
'f
Y
Stat-An;
I Stat-An I
Note: Stat-Bl , B2, ... Bn appear before if-else, Stat-Al , A2, ... An appear after if-else,
Stat-T1,T2,... Tn are executed if the condition is true, else Stat-El , E2, ..... En are
executed.
1.36 Q Introduction to C
After executing the statements B I, B2, .... Bn one after the other, the condition
checked. If the condition is true, the statements TI, T2, .... Tn are executed follow
by AI, A2, An. If the condition is false the statements EI, E2, En a;
executed followed by A I, A2, An. So, whether the condition is true or false,
statements A I, A2, ..... An following if-else-statement will be executed.
Advantages
• There are situations involving series of decisions where we are forced to use an if
or if-else in another if or if-else statement. In such situations, nested-if-statements
are used.
• Each and every condition may be evaluated to true or false and may involve
expressions of various data types.
Disadvantages
• The nested ifs are difficult to understand and modify
• As depth of nesting increases, the readability of the program decreases.
In this section, we discuss a variation of nested-if called else-if ladder. Now, let us see
,.H1wtis else-if ladder statement?"
Definition: When an action has to be performed based on many decisions, then this
statementis used. So, it is called multi-way decision statement. The else-if-ladder is a
formof nested if-statement. Here, nesting is allowed only in the else part. The orderly
nestingof if-statement only in the else part is called else-if-ladder.
Disadvantages
• The nested ifs are difficult to understand and modify
• As depth of nesting' increases, the readability of the 'program decreases.
• In situations involving series of decisions one after the other, ea
"-
decision/condition may involve expressions which results in integer value,
these situations the usage of else-if-ladder is not recommended. Instead, we go £
switch statement.
Advantages
• Improves readability of the program
• More structured way of writing the program
Q Systematic Approach to Data Structures usin C 1.39·
Disadvantages
• Usedonly if the expressions used for checking the conditions results in integer
value(char is also allowed). If the result of expression is not integer value, switch
statementcan not be used.
Thesyntax of switch statement is (same as else-if-Iadder. Both structures are
considered
as multiple selection statements) shown below:
Syntax' ~
Statement-B1; ~
Statement-B2; I Stat-B21
Statement-Bn;
Switch statement
switch (choice / expression)
{
case value 1: true I.BloCk-I; .~
Block-I; no-break in:Block-I
I
break; I
case value2:
Block-2;
break;
case valuen:
Block-n;.
no-break in'Block-n
break; ~-- .JI
default:
Block-d
Statement-AI;
~
Statement-A2;
I Stat-A21
o
I
I
'i'
Statement-An;
I Stat-An I
1.40 Q Introduction to C
After executing the statements B 1, B2, .... Bn, the expression in switch is evaluated
an integer value. The integer value thus obtained if it matches with any of the valu
value1, value2, ..... valuen, the control is transferred to the appropriate block. Duris
execution, if break statement is executed, then the control comes out of the swit
statement and the statements AI, A2, ... An which comes after the switch statem
are executed. During execution of a particular case, if break is not encountered, th
control goes to the subsequent cases and the statements under those cases will
executed till the break statement is encountered. If the value of the expression d
not match with any of the case values value1, value2, .... valuen then, control corn
out of the switch statement in the absence of default. If default case is part of tIK
switch, then the statements in default block will be executed. Some valid and inval'
ways of writing switch expressions and case constants are shown below by assumin
the following declarations:
int i:,
char c',
float f,
double d',
ormally,the statements in a program are executed one after the other. Now, let us
see What are looping/repetitive constructs? What are tile types oj looping
nstructs? "
Drill tion The statements that enable the programmer to execute a set of statements
repeatedly till the required activity is completed are called looping constructs or 1001'
( ·01 statements. These statements are also called repetitive constructs/statements
I'
r.: itive constructs/statements. The statements within a loop may be executed for a
fixed numberof times or until a certain condition is reached. The various types of
loopingconstructsthat are supported in C language are discussed below:
Deli ition: A set of statements may have to be repeatedly executed for a specified
numberof times. In such situations, we use for loop. Thus, a Jar loop is defined as an
iterativestatement that causes a set of statements to be executed repeatedly for a fixed
numberof times. If we know well in advance as how many times a set of statements
havetobe repeatedly executed, then Jar l.oop is the best choice. The syntax of the Jar
loop alongwith equivalent flowchart is shown below: . ~
Stat-BI; ~
I
I
Stat-B2; I
•••
Stat-Bn; Syntax I I Stat-Bn I flowchart
r----------~I
for (expl; exp2; exp3)
for loop
--::-!.--_------"'I'----~
L-. false
{ ~ for exp 1 to exp2 step exp3
Stat-I"'" ±true
Stat-2 ~
~ Body of the for-loop I Stat-2
I
I
I
'M
Stat-n...•
I Stat-n I
I
Stat-AI; I Stat-AI I
I
1'.42 Q Introduction to C
where
• for is a keyword (reserved word)
• expl- stands for initialization expression. It should end with semicol
denoted by the symbol ';'.
• exp2 - stands for terminal condition. Basically, it is an expression
should be terminated semicolon (;)
• exp3 - normally stands for step size. The expression exp3 may
. increment/decrement (updation)
• body of the loop - consists of various statements. All the statements Stat
Stat-2, .... Stan-n within the body of the loop ends with semicolon.
Note: The statements Stat-B'l , Stat-B2, ... Stat-Bn appear before the for-loop and tb
statements Stat-A I, Stat-A2, ..... Stat-An appear after while loop.
Working of for loop The following sequences of operations are carried out afte
executing the statements B I, B2, ... Bn that appear just before the for-loop.
• The expression expl is executed first It is always executed only once when th
execution of the for-loop begins.
• The expression exp2 is executed next. If the expression exp2 is evaluated to falSI
the control comes out of the loop without executing the body of the loop. Lata
the statements AI, A2, ... An are executed.
• If the expression exp2 is evaluated to TRUE, the body of the loop is execut
Alter executing the body of the loop, the expression exp3 is executed. It
normally updated by the specified step size. Then exp2 is executed and the proc
is repeated.
• Once the expression exp2 is evaluated to FALSE, the control comes out of
loop and the statements AI, A2 .... ~ are executed.
In this section, let us see "What is a while loop? When it is used? What is the sym
ofwhile loop?"
Definition: A set of statements may have to be repeatedly executed till a certai
condition is reached. In such situations, we do not know exactly how many times a s~
of statements have to be repeated. So, naturally we need to have a condition-
controlled loop. In C language, the condition-controlling is done generally by a whil
loop. That is why the while-loop is generally called condition-controlled loop. Th
syntax of the while-loop along with equivalent flowchart is shown below:
•
Q Systematic Approach to Data Structures using C 1.43
Stat-BI;
I Sta;-BI I
I
I
Stat-B2; I
'+'
Stat-Bn;
while loop
while (exp) false
{
Stat-I;
Stat-2;
Body of the while-loop
Stat-n;
Stat-Al; I Stat-AI I
I
I
I
Stat-An; , '+'
where
I
Stat-An I
• while is a keyword (reserved word)
• expis the expression which is evaluated to TRUE or FALSE. The expression exp
mustbe enclosed within "(" and ")".
· otl The statements BI, B2, ... Bn appear before while loop and the statements AI,
A2, ..... Anappear after while loop.
\\ orking of while 100pThe following sequences of operations are carried out after
executing the statements B I, B2, ... Bn that appear just before the while-loop.
• If the expression exp is evaluated to false at the time of entry into the loop, the
controlcomes out of the loop without executing the body of the loop. Later, the
statementsA I, A2; ... An are executed.
• Iftheexpression exp is evaluated to TRUE, the body of the loop is executed. After
executingthe body of the loop, control goes back to beginning of the while-
statementand exp is again evaluated to true or false.
1.44 Q Introduction to C
• Thus, the body of the loop is repeatedly executed as long as exp is evaluated
TRUE. Once the expression exp is evaluated to FALSE, the control comes out
the loop and the statements AI, A2 .... An are executed.
In this section, let us see" What is a do-while loop? When is it -used? What is I
svntax of do-while loop?"
Deflnition: do-while loop is similar to a while-loop and used when a set of stateme
may have to be repeatedly executed until a certain condition is reached. When we
not know exactly how many times a set of statements have to be repeated do-w
can be used. The syntax of the do-while loop along with equivalent flowchart
shown below:
Stat-BI;
I Stat-BI I
I
I
Stat-B2; I
'+'
Stat-Bn;
do-while loop
do
{ .I
,
Stat-I
I
I
Stat-I I
I
Stat-2 Body of do-while loop
I Stit-n I
Stat-n
} while (exp)
Stat-Al ; I Stat-AI I
I
I
I
Stat-An; '+'
I Stat-An I
where
• do and while are keywords (also called reserved word)
• exp is the expression which is evaluated to TRUE or FALSE. The expression
must be enclosed within "(" and ")",
Q Systematic Approach to Data Structures using C 1.45
Working of do-while loop The following sequences of operations are carried out
afterexecutingthe statements B I, B2, ... Bn that appear just before the do-while-loop .
• The body of the do-while loop consisting of statements Stat-I, Stat-Z,..... Stat-n
areexecuted one after the other.
• Then,the exp is evaluated. If the expression exp is evaluated to TRUE, the body of
the loop is executed again and, the process is repeated. The moment exp is
evaluatedto false; control comes out of the 'do-while and the ,statements AI,
A2, ... An which appear after the do-while loop are executed.
Sincethe expression exp is evaluated to TRUE or FALSE at the bottom of the do-
while loop, the do-while-loop is called bottom testing loop or exit-coil trolled loop.
Here,thebody of the loop is executed at least once.
Now, let us discuss about "What is an array? How array can be used and
manipulated?"
1.11Arrays
DefinitionAn array is defined as an ordered set of similar data items. All the data
itemsof an array are stored in consecutive memory locations -in RAM. The elements
ofanarrayare of same data type and each item can be accessed using the same name.
Forexample,consider an array of marks of 5 students as shown below:
aO I 90 I 45 I 99 I €>O I,
marks[O] marks[l] marks[2] marks[3] marks [4] ,
Toreferan item in the array, we specify the name of the array along with position of
theitem.The position of the item must be written within square brackets (m. The
positionof the item enclosed within square brackets is called "subscript" or "index".
1.11.1Single-dimensional arrays
Now, we shall answer the question "What is initialization? How arrays can
initialized?"
If the size of integer is 2 bytes, 10 bytes will be allocated for the variable a.
Other examples,
int a[5] = {1O, 15,20,25,30,40, 50}; Ilerror: Number of initial values
Ilare more than the size of array
Example 1.11.3.2: Consider character initialization shown below:
char b[8] = {'C', '0' , 'M' , 'P' , 'U' , 'T' , 'E' , 'R'}·,
During compilation, 8 contiguous memory locations are reserved by the compiler for
the variable b and all these locations are initialized as shown below:
Q Systematic Approach to Data Structures usin C 1.47
In fact, instead of characters, ASCII values are stored in the memory. Since a
characteroccupies 1 byte, 8 bytes will be allocated for the variable b.
Other examples,
charb[5] = {'M', '0', 'N', '1', 'K', 'A'}; //error: Number of initial values
/Iare more than the size of array
) .11.4 Partial array initialization
Partialarray initialization is possible in C language. If the number of values to be
initializedis less than the size of the array, then the elements are initialized in the
order from oth location. The remaining locations will be initialized to zero
automatically.For example, consider the partial initialization shown below:
int a[5] = { 10, 15};
char b[9] = "COMPUTER"; 1* Correct. Tll~ size of the array is string length + 1 *
char b[8] = "COMPUTER"; I*Wrong. Size of the array should be minimum 9*
For string initialization, usually the programmers prefer the following declaration:
char b[] = "COMPUTER";
1.11.7 Reading/writing single dimensional arrays
In this section, let us concentrate on how to read the data from the keyboard and ho
to display data Items stored in the array. Consider the declaration shown below:
int a[5];
Here, 5 memory locations are reserved and each item in the memory location can~
accessed by specifying the index as shown below:
Using a[O] through a[4] we can access 5 data items.
~ ~ ~
Note: III general, Using a[Oj through a[n-lJ we call access n data items.
Once we know how to access each location in the memory, next question is "How
store the data items ill these locations?" This is achieved by reading the n data ite
fro the keyboard using scanfO function as shown below:
scanf("%d", &a[O]);
scanf("%d", &a[I]);
scanf("%d", &a[2]);
scanf("%d",&a[n-l ]);
, I I
Y ..,
In general, scanf("%d",&a[i)) where i = 0, 1, 2, 3, n-I. So, in C language,'
we want to reap n data items from the keyboard, the following statement can be used:
for fi==Oi i c=n-It if+) li=O 1 2 n-I
{ I a[O] a[l] a[2] a[n-l]
scanf("%d", &a[i]); I 10 20 30 50
}
The above for loop can also be written by replacing '<=' relation by '< 'relation 3!
shown below:
~ Systematic Approach to Data Structures using C 1.49
Similarly
to display n data items stored in the array, replace scanfO by printfO
statement
as shown below: ,
for (i= 0; i < n; i++)
{
printf("%d", ajij);
}
ow,let us write a small program to read n elements from the keyboard and to
display
n elements on to the screen.
Example1.11.7.1:To read n elements from the keyboard and display the numbers on
themonitor ".
TRACING'
As we can arrange numbers in ascending order using bubble sort, using insertion sort
also we can arrange numbers in ascending order. Now, let us see "How insertion sort
works?" -
Procedure: The sorting procedure is similar to the way we play cards. After shuffling
the cards, we pick each card and insert it into the proper place so that cards in hand
are arranged in ascending order. The same technique is being followed while
arranging the elements in ascending order. The given list is divided into two parts:
sorted part and unsorted part as shown below:
( n elements -----3)~1
O .j i +1 - n-1
~ sorted -7 ~ un sorted )
'V
boundary
Note that all the elements from 0 to j are sorted and elements from i to n-1 are not
sorted. The ith item can be inserted into any of the positions from 0 to j so that
clements towards left of boundary are sorted. As each item is inserted towards the
sorted left part, the boundary moves to the right decreasing the unsorted list. Finally,
once the boundary moves to the right most position, the elements towards the left of
boundary represent the sorted list. -
Example 1.12.1: Sort the elements 25, 75, 40, 10,20 using insertion sort
75 is inserted after 25
10 is inserted before 25
~ '"
~ 20 is inserted between 10 and 25
~unsorted
Output
~
~ Final sorted list
ow, item has to be compared with a[j] with initial value of j = i -1. Now, as long as
item < a[j] and j >=0 it is required to perform following activities:
• copy a[j] to a[j+ lJ
• decrement j by 1.
1.5...2· Q Introduction to C
j=i-1;
while (item < a[j] && j >= 0
{
a[j+ 1] = a[j]; These statement should be executed for
j=j-1; each item = a[i], where i = 1 to n-1
{
}
Once the condition fails copy item into a[j+ 1] using the statement:
a[j+l] = item .
The complete program to arrange numbers in ascending order using insertion sort
shown below:
Q Systematic Approach to Data Structures using C 1.51
#include <stdio.h>
void mairu)
{
int i, n, j, item, a[lO];
priritf("Enter the number of elementsm'');
scanf("%d" ,&n);
/* Read n elements */
printf("Enter n elements\n");
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
-}
Since the standard library functions are limited, programmercan not completely rely
on these library functions. The programmer has to write his/her own programs called
user-defined functions. Now, let us see" What are User Defined E.l~llctiolls(fjDFs)?"
Definition: In most cases, the programmers need other than library functions' to
achieve some specific tasks. These functions which are written by the
programmer/user to do some specific tasks are called user defined functions (UDFs).
For example,
Suppose, we want to add two matrices a and b. There is no standard library
function in C to add two matrices like a library function sqrtt) which finds the square
Q Systematic Approach to Data Structures using C 1.55,
root of a number. So, we can write a function add _matrixt). This function is written
by the user and it is used by the user. So, it is called user-defined function. Now,
consider the program shown below:
PROGRAM I TRACING
#inc1ude <stdio.h> I
#inc1ude <math.h> I
void maim) : Execution starts from here
{
float n, s; I
I
n= 36; In = 36
s = sqrt(n); Is = 6.000000
printf("squareJoot(%d) = %t\n", n, s); ~squareroot/Jti) = 6.000000
}
Now, let us see "What will happen when a function sqrtt) is invoked?"For the
explanation purpose consider the program shown below:
r#;c;de<~i~h;---------l· ._' _
I #include <math.h> I r float sqrt(int n) I
II~oid maint) I { I
{ I stat-l ; I
I float n, s; JI stat-2; I
I' 1" I
I n = 36; II I
I s = sqrt(n);
II
~ return result· I
I
I} prlntfrvsquareroou'zsd) = %t\n", n, s);
;r;-'
III~ _I ~I _. J
I
I
----i calling function t- - - - J
. called function
1.56 Q Introduction to C
Here, we have not written the function sqru] which is shown on the right hand side.
Because, it is library function it is written by the compiler developers. We do not
know what statements they have used to compute square-root. But, we know that by
invoking the function sqrtt), we can get the result. Now, some points to remember
are:
• Invoking the function sqrtt) is done by simply writing the name of the function
as shown in function maint) using '=' operator. We say that the function sqrtt)
is called. So, the function sqrtt) is often .called "called function ",
• Who is calling the function sqrtt)? It is clear from the above program that the
function maini) is calling. So, the function maint) is called "calling function ",
• When a function is called, control is transferred as shown using the arrow from
maint) to sqrtt). Each statement in the function sqrtt) is executed and square
root of given number is computed.
• After executing the last statement i.e., return result, the control will be
transferred to the function maini) along with the result. This is shown by
drawing the line from sqrtt) to maint).
• The result obtained from sqru) is copied into variable s.
• The result is displayed on the screen.
Note: Execution always starts from the function maini). The way the function maini)
gets the job done from various functions can be explained using the following
analogy:
Now, using all these concepts, let us write some simple functions. Consider the
program shown below:
,!;lSystematic Approach to Data Structures using C L57
PROGRAM TRACING
#include <stdio.h> I
void maint)
I
{
I
int a, b, sum; I
I Input
printf("Enter the values for a and b\n"); IEnter the values for a and b
scanf("%d %d" ,&a, &b); 11020
sum = a + b; : sum = 10 ~ 20 = 30
Output
printf("%d", sum); I 30
t
"What does this program do?" It is very simple. It accepts two numbers, adds these
numbers and displays the result. Now, "Can we write a function to add two
numbers? " Yes, we can!!! The above program itself is a function. But, the name of
the function is maim). If we want to write our own function, then just change the
name of the function main to add as shown below:
#inc1ude<stdio.h>
void addt)
{
int a, b, sum;
sum = a + b;
printf("%d", sum);
Yes,we have written one function. In other words, we say we have defined a function
whose name is addt). This is called function definition. Since we have written this
function, it is called "user-defined function ".But, it is not complete. We know that
1.58 Q Introduction to C
- execution always starts from the function main. But, there is no main. So, we have to
write one function main which calls the function add as shown below:
void addt)
#inc1ude <stdio.h> {
int : a, b, sum;
void mainr) printf("Enter the values for a and b\n");
{
scanf("%d %d" ,&a, &b);
addt);
} sum=a+b;
printf("%d", sum);
}
The sequence operations that are performed when above program is executed ate:
• Execution of the program starts from maim)
• The function addt) is invoked
• Control is transferred from function maint) to function addi) as shown using
arrow mark.
• The function addt) accepts two numbers, adds those numbers and print the result
• Then, control is transferred from function addi) to function maint) as shown using
the arrow mark from right to left
• In the function maint), there is no other function/statement to be executed and
~ hence execution of the function maint) is terminated.
Note: we formally write above set of functions in one file as shown below:
~--~---------------
#inc1ude <stdio.h> I
void addt) I
{ .I
int a, b, sum; I
printf("Enter the values for a and b\n"); I
scanf("%d %d" ,&a, &b); ,...----------.
File name: add.e
sum = a+b;
printf("%d", sum); I
} I
void mairu)
I
{ I
addt); I
} I
------------------~
Q Systematic Approach to Data Structures using C l.S.~
1.13.1 Elements of user-defined functions
In this section, let us see "What are the elements of user-defined functions? ..The
elements of user-defined functions are shown below:
Elements of ~ser
defined functions -f Function definition
Function call i -
Function declaration
Function definition: The program module that is written to achieve a specific task is
calledfunction definition. For example, consider the function shown below:
void addt)
{ This function accepts two numbers,
int a, b, sum; adds those two numbers and prints
the result. Here, instead of giving
scanf("%d %d",&a, &b); main as the name of the function,
sum=a+b; we have given add as name of the
printf("%d\n", sum); function.
}
Function call: We know that execution always starts from function maint). If we '
want to use the function addt) that we have just discussed, it has to be invoked. This
is called function call. In general, a function callis nothing but invoking a function at
the required place in the program. For example, the function addi) that we have
written earlier can be invoked as shown below:
void maim)
{
addt); /* This statement transfers the control to addi) function */
}
Function declaration: Functions are like variables. As variables are declared before
they are used, we normally declare the functions before they are defmed and end with
semicolon. This is called function declarationvt function prototype.For example, the
function declaration for the function addi) can be written as: .
void addt);
l'
- I
Note the semicolon at the end. This is must for the function declaration. So, the
complete program usingfunction declaration,function call and function definition is
shown below: .
1.60 Q Introduction to C
j.
------------------~
Once we know the functional elements, let us discuss each of these in detail, in th
coming sections.
This is most important topic and hence, let us discuss in detail. Now, let us answer th
question "What isfunction definition? Explain in detail"
Definition: The program module that is written to achieve a specific task is calle
function definition. The various elements of the function definition are shown below:
-E
Function definition
Declaration statements
Function body Executable statements
return statement
.!;l Systematic Approach to Data Structures using C 1.61.
The general format of function definition along with example is shown below:
T
Function body Function body
It is clear from the above figure that Function Header= type + name + (parameters)
where
type e:::> can be int, float, double, void etc. This is the type of the value
that the function is expected to return. If the function is not
returning any value, then we need to specify the return type as
void. In the example, the function is expected to return an
integer value and hence the type is specified as int.
name e:::> can be any valid identifier. Therefore, the rules that are followed
to frame variables are followed to frame function name.
Normally, the name of the function itself should indicate the
activity being performed by the function .. In the example. add is
the name of the function and perform addition operation.
parameters e:::> Parameters are list of variables. All the variables should be
separately declared and each declaration should be separated by
by comma. All the parameters should be enclosed with in '(' and
')': These variables accept the values when the function is
invoked. These variables are often called formal parameters or"
dummy parameters. In the example, the variables m and n are
called formal parameters. .
m tiC
fum:ti defiDition, first part is the function header and second part is the
hotly. The rela1ion between jim-ction body and its elements is shown below:
declaration c:::) Apart from the parameters, whatever variables are used in
thefun£tion should be declared. These variables are called local
mri.oble. (auto variables). In the example, the variable sum is
declared as a local variable.
execution c:) This part contains the statements that 'perform the actual job of
e function. In the example, sum = m + n is the execution
statement.
return c::> Using this statement" the function returns the evaluated result. If
a fimdion 0 not retum any value, the return statement can be
omitted In the example, the function returns sum of two
numbers.
Example 1.13.2.1 Function to accept two values a and b using formal parameters,
to these 0 numbers and return the result is shown below:
return statement
end of the function body
"
Example 1.13.2.2: FWldion to accept two values a and b, add these two numbers,
. res.Wt. d 00 Tetum any value i shown below:
1.13.4Function calls
After writing the functions, appropriate functions have to be invo ell to ge j b
done from those functions. This invocation of a function is caIled.fiuwtio raJll For
example, in the previous section, we have written a function maximumO 110 !the
larger of two numbers. This function can be invoked using e ion I'TJlliin(j
shown below:
void maim)
{
int m, n, res;
}
1.64 Q Introduction to C
Execution starts from the function maint}. After reading the values of m and n, the
function is invoked using the statement:
nr----~-~)Actual parameters
res = maximum(m, n);
The variables that are used while calling the function are called actual parameters.
Next, control is transferred to the function maximums). The function fmds the larger
of two items and control is returned back to the function maint} along with the result.
The value returned is copied into the variable res and is displayed on the screen in
function mainO. This isillustrated in the figure shown below:
Calling function
. ~
l- - - - - -I
I
I 1
1
Called function r- - .J
I Actual parameters
Now, let us see the difference between actual parameters and formal parameters.
1. Actual parameters are used in calling 1. Formal parameters are used in the
function when a function is invoked. function header of a called function
Example: Example:
c = sum(a, b); int sum(int m, int n)
{
Here, a and b are actual parameters.
}
Here, m and n are called formal
parameters
~ Systematic Approach to Data Structures using C 1.65
- - - - - - - - -.....,
#include <stdio.h> void surm) /* No parameters */ 1
void sumt); /* declaration */
: { int a, b, c; :
r ~Oid ;;;;'inO- - - - I 1 printf("Enter the values of a and b"); 1
I scanf("%d %d",&a, &b); 1
I sumt); I
I } I 1 c = a + b; 1
L
[calling function I J
Il _I
printf("Sum = %d", c);
/* No return valu~ *.L - - J
1
Called function I
Observe from the above program that the function sumO do not receive any values
from the function main/) and it does not return any value to the function maint). But,
still we can input two numbers from the keyboard and perform addition of two
numbers.
Observe from the above program that the function sumO do not receive any values
from the function mairu). But, it accepts data from the keyboard, find the sum of
those numbers and returns the result to the calling function.
#include <stdio.h>
I-;oid
I{
~inO - - - - - - - - - - -'1---------
, void sum(int a, int b)
-1
,
I int m, n; " { /* With parameters */ ,
I , int c;
I printf("Enter m and n"); I' I
I scanf("%d %d",&m, &n . I' c = a + b; I
I sum(m, n); I~ printf("Sum = %d", c); :
I}
t., - -I Calling function r--- _..J
'( }
l
I*Noreturnvalue*f
Called function 1--
I
Observe from the above program that the function sumO receives two values from the
functionmaim), finds the sum of these numbers and display the result there itself. The
result is not passed to the calling function, but only control is transferred.
#incIude <stdio.h>
I l } r------~I--I
I
I }
printf("Sum = %d", c);
1 l Called function
I----- J
l
L--
Calling function
Observe from the above program that the function sumO receives two values from the
function maint), finds the sum of these numbers and sends the result back to the
calling function.
l
Note: Now we are aware that the called function receives the information from the
calling function through the parameters. The variables used while invoking the called
function are called actual parameters and the variables used in the function header of
the called function are called formal parameters.
Now, let us see "What is the memory organization in C?" Here, let us assume the
size of the memory is 1MB = 1024 x 1024 bytes = 1048576 bytes of memory. All
these locations are numbered sequentially from 0 to 1048575 as shown in figure 1.14.
ote: It is observed from the above figure that the addresses for program, data, global
•and static variables along with memory reserved for dynamic allocation will be in
..ncreasing order of the addresses. But, for the local variables, the memory is allocated
10 decreasing order of their addresses.
Q Systematic Approach to Data Structures using C 1.69
. -
Addresses
00000
Program Code 00001
Reserved for the program
Data
Definition: Global variables are the variables which are defined before all functions.
The global variables have the following features:
• These variables are declared before the functions.
• . Memory is allocated for these variables only once and all the allocated
memory is initialized to zero.
• These variables can be accessed by any functiori and are alive and active
throughout the program.
• Any function can use a global variable and change its value.
• Memory is de-allocated once the execution of the program is over.
#include <stdio.h>
int a= lO'}
int b = 20~ Stored in r:nemory area reserved for
int c:, global vanables.
. void addt)
h Stored in Program code area normally
c = a+ b; at the beginning of the memory
}
void subtracu)
{ Stored in Program code
c = a- b;
}
void maint)
{ Stored in Program code
addt);
Output
printf("a = %d\nb= %d\n", a, b); a= 10
printf("a + b = %d\n", c); b=20
a + b = 30
subtractt); a-b=-10
printf("a - b = %d\n",c);
.'
}
~ Systematic Approach to Data Structures using C 1.71_
In this program, the variables a, band c are defined before all the functions. So, these
variables can be accessed and modified by any function without passing them as
function arguments. We should remember that global variables are visible only from
the point of declaration to the end of the program. Now, let us see "How the addresses
are assigned to each of the global variable in the following program?" The memory
organization for global variables is shown on the right hand side of the program.
50006 I
I
double d = 1.12345678 1* 8 bytes *1 dO< 50008
1.12~5678
50010
50012 I,
iot j =20; 1* 2 bytes *1 J -I 50014 !o
char
char
c1 = 'A';
c2= 'B';
1* 1 byte *1
1* 1 byte *1
ct. c2 50016 A .,
I B
....... I
void maint)
{
printf("&i = %u i = %d\n", &i, i); &i = 50000 i = 10
printf("&f= %u f= %d\n", &f, f); &f = 50002 f= 1.150000
printf("&d = %u d = %If\n'',&d,d); &d = 50006 d = 1.123457
printf("&j = %uj = %d\n", &j,j); &j = 50014 j =20
printf("&c1 = %u c1 = %c\n", &c1, c1); &c1 = 50016 c1=A
. ~c2=~ c2=B
Increasing order
Note: Using & followed by variable name, we get the address of the variable. All the
addresses are assigned to variables one by one in the order in which they are declared.
So, the variables declared in the beginning will have lower addresses when compared
to the variables declared at the end. That is, the addresses of all global variables and
static variables will be in increasing orderas shown below:
&i < &f < &d < &j < &ct < &ct
50000 < 50002 < 50006 < 50014 < 50016 < 50017
1. 72 Q Introduction to C
Definition: Local variables are the variables which are defined within a function.
These variables are also called automatic variables. As and when control enters into
the function, memory is allocated for these variables and when control goes out of the
function, memory will be de-allocated. That is the reason, the local variables can not
be accessed by any other function. The local variables have the following features:
• These variables are declared inside the function.
• Memory is allocated for these variables when the control enters into the
function and memory is de-allocated for local variables when the control goes
out of the function. But, memory contents are not initialized to zero.
• These variables can not be accessed by any function and are alive and active
within the function. The scope of these variables is limited only to the function
in which they are declared and cannot be accessed outside the function.
• No other function Can use a local variable and change its value.
Note: A local variable is accessible only within the function or block in which it is
defined.
c = a + b;
printf("Result = %d\n", c);
}
void maint)
{
int a = 10, b = 20;
add(a,b);
}
Q Systematic Approach to Data Structures using C 1.73.
)
The variable c used in function addt) is a local variable as it is defined only in that
function. Even though the variable c holds the result, when the control goes out of the
function, the variable c can not be accessed. If we use the variable c outside the
function an error "undefined symbol c" is displayed by the compiler. Note that the
variables a and b are defined within the function maint) and are also local variables.
Now, let us see "What is the memory organization for local variables?" or
"How the addresses are assigned to each of the local variable in the following
program?" The memory organization for local variables is' opposite to that of global
variables. For local variables, memory is allocated only in the stack area. In stack
area, memory is allocated from higher address to lower address as shown in following
program:
lj
float 1* 4 bytes:" I f
50012 I 50011
50010A i - 50009 I
I
double d = 1.12345678 1* 8 bytes *1 .50008 1.123~S678 _5000_~
d
50006 I 500051
I,
50004
int j = 20; 1* 2 bytes *1 i 50002 20 ~50001 .
char
char
cl = 'A".,
c2 = 'B'; 1* 1 byte *1
=
1* 1 byte *1 cl. c2 50000
I~~
A I
,
!
8
.
4::9j
... ,. .....
Output
printf("&i = %u i = %d\n", &i, i); &i = 50016 i = 10
printf("&f= %u f= %d\n", &f, f); &f = 50014 f= 1.150000
printf("&d = %u d = %If\n'',&d,d); &d = 50010 d = 1.123457
printf("&j = %uj = %d\n", &j,j); &j = 50002 j = 20
printf("&cl = %u cl ::;:%c\n", &cl, cl); &c1 = 50000 c1=A
} &c2 = 49999 c2 =B
Note: Using & followed by variable name, we get the address of the variable.
1. 74 Q Introduction to C
Note: All the addresses are assigned to variables one by one in the order in which
they are declared. So, the variables declared in the beginning will have higher
addresses when compared to the variables declared at the end. That is, the addresses
of all local variables will be indecreasing order as shown below:
&i . > &f > &d > &j > &c1 > &c1
50016> 50014 > 50010 > 50002> 50000> 49999
Definition: Static variables can be declared outside the function or within the
function. They have the characteristics of both local and global variables. The'
declaration of a static variable should begin with {he keyword static.
For example,
static int a, b;
static float c;
Note: The static variable have some features of local variable and some features of
global variable.
#include <stdio.h>
void displayt)
{
static int i =0;
i++;
printf("%d ",i);
}
void maim)
{
intj;
Output
for (j = 0; j < 5; j++) displayt); 12345
}
Note: Memory organization for static variables is same as that of global variables.
Definition: Any variable declared 'with the qualifier register is called a register
variable. This declaration instructs the compiler that the variable under use is to be
stored in one of the registers but, not in main memory. Register accessing is much
faster compared to the memory access and hence, frequently accessed variables such
as loop variables are usually stored in register variables. This leads to faster execution
of the program. If the register is not free, the compiler can ignore this and the memory
is allocated as it is done for other variables. The register declaration is shown below:
register int x;
This declaration is allowed only for the local (automatic) variables and to the formal
parameters. So, all register variables are automatic. The registers are allocated for
these variables as the control enters into the function and registers are freed
immediately once the control comes out of the function. Their definitions are valid
only to the function in which they are defmed. .
1.76 Q Introduction to C
-Now, let us see "What are the differences between local and global variables?"
2. Global variables are alive and holds 2. Local variables are alive when control
the values as long as the program is .enters into the function during
being executed. execution and are destroyed when the
control comes out of the function.
3. The global variables are accessible 3. The local variables are accessible
throughout the program only in the function inside which they
have been created.
5. Global variables are always initialized 5. Local variables are not initialized
to zero automatically in some compilers.
6. Addresses are assigned in the order in 6. Addresses are assigned in the order in
." which the global variables are present which the local variables are present
in the program but in increasing order in the program but in decreasing order
\ .
Chapter 2: Pointers
I What are we studying in this chapter? I
• Pointer Concepts
• Pointer variables
• Accessing variables through pointers
• Pointer declaration and definition
• Initialization of pointer variables
• Pointers and functions
• Pointer to pointers
• Compatibility
• L-value and R-value
• Arrays and pointers
• Pointer arithmetic and arrays
• Passing an array to a function
• Understanding complex declarations
• Memory allocation functions
• Array of pointers
• Programming examples. - 7 hours
2.1 Introduction
Pointer is the one of the important feature available in C language. Almost all the
programs in the software industry are written only using pointers. But, many people
think that pointers concept is difficult. The correct understanding and use of pointers
is very much required for successful C programming. Pointers are one of the strongest
and also one of the most dangerous feature (if not used properly) available in C. Due
to its importance and dangerous feature, the concept of pointers is dealt in detail.
Now, let us understand the concept of pointers.
In this section, let us see "What is a pointer?" and "Explain the concept of pointer"
2.2 Q Pointers
Definition: The basic data types in C language are int, float, char, double and void.
l
Pointer is a special data type which is derived from these basic data types) So, pointer
is called derived data type. The pointer takes the values from 0 to 65535 if the size of
the RAM is 64K. The pointers are always associated with the following three
concepts:
Pointer
concept -E Pointer constants
.
Pomter values
Pointer variables
Logically, all 65536 locations in computer memory are numbered sequentially from 0
to 65535 but, physically they are divided into even bank and odd bank. Even bank is
set of memory locations with even addresses and odd bank is set of memory locations
with odd addresses as shown below:
These memory addresses are called pointer constants. We can not change them; but,
we can only use themto store data values.
For example, in the above memory organization, the addresses ranging from 0
to 65535 are pointer constants.
Note: The address of a memory location is a pointer constant and can not be changed.
The address of the variable can not be accessed directly. The address of the variable
can be obtained using address operator (denoted by &) in C language.
Note: The address operator can be used with any variable that can be placed on the
left side of an assignment operator. Since constants, expressions and array names can
not be used on the left hand side of the assignment and hence accessing address is
invalid for constants, expressions and array names. The following are invalid:
2.4 Q Pointers
Note: If a is an array, then address of the first location of the array is obtained either
usmg:
&a[O] or a.
Assume that the compiler has chosen the address 65530 for the variable i, the address
65532 for the variable j and 65534 for the variable k. These addresses assigned to
variables i, j and k are called pointer values. Now, let us "Define pointer values"
Definition: Memory is divided into number of storage cells called locations. All the
locations in computer memory are numbered sequentially from 0 to 65535 (with
memory size of 64K). Out of these addresses, the system assigns some addresses of
the memory locations to the variables. These memory addresses assigned to variables
by the system are called pointer values.
For example, the addresses 65530, 65532 and 65534 which are assigned to the
variables i, j and k are pointer values. These pointer values can be obtained using
address operator (&). Using &i, &j and &k we can obtain pointer values 65530,
65532 and 65534.
Note: Without address operator (&), it is not possible to obtain the pointer value of a
variable.
Note: The pointer values (i.e., the addresses assigned to variables) may vary each
time the program is executed. That is, the address of the variable may change from
one run of the program to another.
then the variable p is called apointer variable. The memory organization after
executing the above statement is shown below:
P 50000 651;34
I
:
65534 300
t t t
variables address Values
Physical representation Logical representation
ate that the address of variable i which is 65534 is assigned to variable p. So,
even though address of p is 50000 ( pointer value), the value stored in that location is
65534. Since p is a variable which contains address of a variable, the variable p is
called pointer variable. In the logical representation, the variable p points to the
variable i. So, we say that the variable p points to i and hence the name pointer. The
relationship between p and i is pictorially represented as shown below:
L....-__ =jr----.~1
p
__ 3-:-0_0_
Note: Note the difference between pointer value and value stored in memory location.
Pointer value is 50000 and value stored in memory location is 65534.
In C language, we know that all the variables should be declared before they are used.
Pointer variables also should be declared before they are used. In this section, let us
see "How to declare pointer variables?" The syntax to declare a pointer variable is
shown below: .
~
type * identifier ; .
I L~
..
Name given to the pointer variable
~ The asterisk (*) in between type and identifier
tells that the identifier is a pointer variable
'-------+ type can be any data type such as int, float,
char etc. It can be derived/user-defined data
type also.
int *p;
The above declaration can be read as ''p is pointer to integer variable" and this
declaration informs the following points:
Q Systematic Approach to Data Structures using C 2.7
double *pd;
.
This declaration informs the compiler that pd is a pointer variable and can hold the
address of a variable of type double.
Example 2.3.1.3: In the declaration, the position of * is immaterial. For example, all
the following declarations are same:
int ' *pa;
int * pa;
int* pa;
Any of the above declaration informs that the variable pa is a pointer variable and it
should contain address of integer variable.
otc: Here, most of the readers wrongly assume that the variables pa, pb and pc are
pointer variables. This is because * is attached to int. This assumption is wrong. Only
pa is a pointer variable, where as the variables pb and pc are ordinary integer
variables. For better readability, the above declaration can be written as shown below:
int *pa, pb, pc;
Now, we can easily say that pa is pointer variable because of * operator, whereas pb
and pc are integer variables and are not pointer variables. It is still better if the
variables are declared in separate lines as. shown below:
int *pa;
int . pb;
int pc;
2.8 Q Pointers
In the previous section, we have seen the method of declaring a pointer variable. For
example, consider the following declaration:
int *p;
This indicates that p is a pointer variable and the corresponding memory location
should contain address of an integer variable. But, the declaration will not initialize
the memory location and memory contains garbage value as shown below:
p I ~arbage value
Here, the pointer variable p does not contain a valid address and we say that it is' a
dangling pointer. Now, let us see "What is a dangling pointer?"
Example Z.3.2.1:Consider the following declarations and assume all are global
variables.
Solutiomaj] global variables are initialized by the compiler during compilation. The
pointer variables are initialized to NULL indicating they do not point to any memory
ocations as shown below:
pi
pf ~
pc
Q Systematic Approach to Data Structures using C 2.9
Example 2.3.2.2: Consider the following declarations and assume all are local
variables.
Solution:The local variables are not initialized by the compiler during compilation.
[This is because, the local variables are created and used only during
untimelexecution time. The pointer variables also will not be initializedand hence
hey normally contain some garbage values and hence are called dangling pointers.
Irhe memory organization is shown below:
pi ~ Garbage value
pf Garbage value
pc Garbage value
Note:The pointer variables pi, pf and pc does not contain valid addresses and hence
they are dangling pointers.
Note that the steps 1 and 2 can be interchanged i.e., we can first declare a pointer
variable, then declare a data variable and then initialize the pointer variable.
Here, the variable x is declared as integer data variable. Since px is a pointer variable
~of type integer, it should contain address of integer variable. So, using the statement:
px=&x
the valid address is stored in the pointer variable and hence the pointer variable px is
initialized.
Note: It is also possible to combine the declaration of data variable, pointer variable
and initialization of pointer variable in one step as shown below:
Example 2.3.3.4: Pointers are flexible i.e., a pointer can point to different data
variables by storing the address of appropriate variables. Consider the following
declaration:
int x = 10, Y = 20, z = 30;
int *p;
x
p=&x;
p y
p=&y;
z
p=&z;
ote: It is observed from above example that the pointer variable p points to Jifferent
memory locations by storing the addresses of different variables. Thus, same pointe
can be pointed to different data variables.
2.12 Q Pointers
2~3.4NULL Pointer
int *p = NULL;
Here, the pointer variable p is a NULL pointer. This indicates that the pointer
variable p does not point to any part of the memory. The value for NULL is defined
in the header file "stdio.h". Instead of using NULL we can also use '\0' or O. The
programmer can access the data using the pointer variable p if and only it does not
contain NULL. The error condition can be checked using the following statement:
if (p = = NULL)
printf("p does not point to any memory]n");
else {
printf("Access the value of p\n");
int *x;
int y;
x = y; /* Error */
Note: The value of data variable can not be assigned to a pointer variable. So, th
statementx = y; results in an error. The correct statement isx = &y.
Q Systematic Approach to Data Structures using C 2.13
Once a pointer variable is initialized with address of a variable, the question is "How
to access the value of a variable using pointer?" The value of a variable can be
accessed using pointer variable using unary operator * (called asterisk). This operator
is called indirection operator or dereferencing operator. For example, consider the
following program segment:
int x = 100, y;
int *p;
Note: By specifying *p, the value of the variable whose address is stored in p can be
accessed. Here, p should be a pointer variable.
Example 2.3.5.1: The data can be accessed usmg pointers after declaration and
initialization.
I Garbage val ue
I
int *q; q 5002 I
Garbage value
I
I
int *r; r 5004 I Garbage value
I
Afterdeclaration, the pointer variables p, q and r does not contain valid addresses and
hence are called dangling pointers. The variable x is initialized to 10 as shown in
figure above. Once declaration is over, the next step is to initialize the pointer
variables.
2.14 Q Pointers
Variables
1 Adress
I
p=&x; p 5000 I
I
I
q=&x; q 5002 I
I
r=&x; r 5004 I
x 5006
I
~O
+- U
After executing these statements, the pointer variables p, q and r contains the address
of integer variable x and logical representation is shown in the figure above.
Step 3: Accessing the item 10: The item 10 can be accessed using the variables, p, q,
r and x as shown below: -
'Using p:
I
printf("&p =%u, p = %u, *p = %d\n",&p, p, *p); I /* Output */
&p = 5000, P = 5006, *p = 10
I
Using q:
I
printf("&q =%u, q = %u, *q = %d\n",&q, q, *q); I /* Output */
I &q = 5002, q = 5006, *q = 10
Using r: I
Printf("&r =%u " r = %u *r = %d\n" ,&r " r "r);,
I /* Output */
I &r = 5004, r = 5006, *r = 10
Note: Even though the variables p, q and r have different addresses, they contain
address of x only. So, different pointer variables (p, q and r in this example) may
contain address of one variable (x in this example). So, the value of x can be accessed
and changed using the variables p, q, rand x. In general) there can be multiple
pointers to a variable. .
Q 'Systematic Approach to Data Structures using C 2.15
PROGRAM TRACING
1. #include <stdio.h>
2.
3. void rnainf) Execution starts from main
sum
4.{
5.
6
int a ~ 10, b = 20, sum;
IT]
7. int *pa, *pb;
8.
9. pa = &a;
10. pb = &b;
II.
12. sum = *pa + *pb; sum = 10 + 20 =30 -.J
13.
14. Output
15. printf("Sum = %d\n", sum); Sum = 30
16. }
Example 2.3.5.3: Program to read two numbers and then add two numbers using
pointers·
PROGRAM TRACING
1. #include <stdio.h>
2.
3. void mairu) Execution starts from main
4.{
5. int a, b.rsum;
6
7. int *pa, *pb;
8.
9. pa= &a; pb
10. pb = &b;
II. Input
12. scanf("%d %d",&a, &b); 10 20
13. a = 10 b = 20
14. sum = *pa + *pb; sum = 10 + 20 =30 -----~
15. Output
16. printf("Sum = %d\n", sunr); Sum = 30
17.}
Explanation After executing statement 12, the values 10 and 20 which are read from
the keyboard are copied into memory locations identified by. a and b. Then those
values are accessed using pointer variables pa and pb, added and result is stored in the
variable sum.
Note: there is no need of writing &pa and &pb, since pa and pb already contains the
addresses.
Q Systematic Approach to Data Structures using C 2.17
>
void main(void)
{
1* Local definitions *1
int a, b, c; aD bD cD
int *p, *q, *r; pD qD rD
1* statements *1
a~ ~ 4 I cD
a = 8, b = 4, P = &b;
pi qD rO
I
:§5
4
q = p, r = &c; a~ ~ ...
I
pi -1' ql
I
I
p = &a, *q = lO;
:ffi ~~ :ffi
*r = *p;
:ffi ~~ :ffi
*r = a + *q + *&c;
a I 8
I bl 10
I cI ,. I
26
pi I
Output
I ·ql I I rI I I
printf("%d %d %d\n",a, b, c); 8 10 26
printf("%d %d %d", *p, *q, *r); 8 10 26
}
.
2.18 ~ Pointers
Note: The pointer variable should be used only after initializing with address of a
data variable.
#include <stdio.h>
int sum(int a, int b); 1* prototype declaration *1
r ~oi~mainO ----------1
I {~ .I
I intm,n,c; I .I~--(-. -.--) --. -1
I I int sum int x, int y I
I printf("Enter m and n"); I :{ int z; I
I scanf("%d %d",&m, &n); I I I
I c = sum(m, n"
I I z =x + y; I
I .........:..' I I I
I = %d", c); I 1 I
r- - - J
printf("Sum return z;
Working of the function: The activities during execution are shown below:
• Execution starts from the function main/)
• Te values for data variables m and n are read from the keyboard
• The function sunu) is called
• The function sumO receives two values using the variables m and n are copied
into x andy
• The function computes the sum of x and y
• The result is returned to calling function maim)
• The function mairu) prints the result.
Now, let us see "What are the different ways of passing parameters to the functions?"
There are two ways of passing parameters to the function:
Definition: When a function is called we may send values of some variables to the
calling function. These variables are called actual parameters. In the called function,
variables should be declared with the same type as the actual parameters. These
variables are called formal parameters. The values of actual parameters are copied
into formal parameters. If the values of the formal parameters changes in the
function, the values of the actual parameters are not changed. This way of passing
parameters is called pass by value or call by value. -
To explain the concept of pass by value, let us type the following program
shown in example 2.4.1.1 and observe the output.
Note: This technique where in actual parameters have not been changed even though
the formal parameters have been changed is called pass by value. .
2.20 Q Pointers
Remember: In pass by value (call by value) any change done on formal parameters
will not have any affect on actual parameters.
Now, let us see "What is pass by reference?" and "Explain with example"
Definition: When a function is called we may send addresses of some variables to the
calling function. The variables that are used when a function is called are actual
parameters. In the called function, the corresponding variables should be declared as
pointers with the same type as the actual parameters. These variables are called
formal parameters. The addresses of actual parameters are copied into formal
parameters. Using these addresses, if the values of the formal parameters changes in
the function, the values of the actual parameters are also changed. This way of
passing parameters is called pass by reference or call by reference.
To explain the concept oipass by reference, let us type the program 2.4.2.1
and observe the output.
~ Systematic Approach to Data Structures using C 2.21
PROGRAM TRACING
#include <stdio.h>
I
void exchange (int *m, int *n); I
void exchangekint *rn, in! *nll : m IL-..+-....I
{ ~ Formal parameters I,/"
int temp;
temp = *m;
I
*m= *n; I
*n = temp; I
} I
void maint)
I
{ I
int a, b; I
a = 10, b = 20; :
exchangeIC&a, &b)~ Actual parametere+-j-s- a G b I 20 I
printf("a = %d and b = %d\n", a, b); II Output (After exchange)
} a=20 b=10
I '
Working Observe the following points when the program is executed:
• The addresses of actual parameters a and b are copied into formal parameters m
andn.
• So, in the function header of exchanger), the variables m and n are declared as
pointer variables.
• Since, they contain addresses, they can access values of a and b using indirection
operator *.
• Thus, the values of a and b can be changed using pointer variables m and n.
Note: This process of passing parameters so that the values of actual parameters are
changed using formal parameters is called pass by reference.
2.22 Q Pointers
Now, let us see "What are the differences between pass by value and pass by
reference?" The various differences between pass by value and pass by technique are
shown below:
2. The type of formal parameters should 2. The type of formal parameters should
be same as type of actual parameters be same as type of actual parameters,
,
but they have to. be declared as
pointers
:
PROGRAM TRACING
#include <stdio.h> values of x and yare copied
~ -
Working The sequence of operations that are carried during the execution of the
program are shown below:
• Execution starts from function maim).
• Accepts the values for x and y
• Control is transferred to the function largesn).
• The values of actual parameters x and yare copied to formal parameters a and
b.
• Larger of these two numbers is returned to the function maint) and is copied
into variable big.
• The function maint) displays the result
Note: In the above program, the function returns an integer value to the calling
function mainf).
2.24 .Q, Pointers
Since pointer is also a data type in C, we can return a pointer from a function. The
program to find the larger of two numbers and returning pointer to larger number is
shown below:
void mairu)
{ x
int x, y, *big; "'"'-.....-...•
Working The sequence of operations that are carried out during the execution of the
program are shown below:
~ Execution starts from function mairu) ..
• Accepts the values for x and y
• Control is transferred to the function largestt).
• The addresses of x and yare copied to formal parameters a 'and b respectively.
• Using pointer variables a and b with the help of indirection operator *, we
compute the larger of x and y and return the address oflarger number.
• Once control is returned to the function maim), the pointer variable big holds
the address of larger of x and y and .the result is displayed using indirection
operator *.
Q Systematic Approach to Data Structures using C 2.25
#include <stdio.h>
int *someO
{
int *p, x = 10; /* Local variables */
p=&x;
return p;
}
void mainr)
{
int *p;
p = sorne/);
Analysis: We may be thinking that our answer is "Result = 10". After executing the
program we may get "Result = 10". Even then, our program is 100% wrong because
of the following reasons.
• Note that in the function somet), the variables p and x are local variables.
• As control enters into the function, memory is allocated for these variables and
once control goes out of the function, memory for these variables is de-
allocated.
• So, we are returning address of a memory location which is de-allocated.
• This de-allocated memory may be used by other parts of the program.
Although, you get an answer for this simple program, but definitely for larger
program we get wrong answer.
Note: So, returning a pointer to a local variable is a serious logical error. But, not
syntax error.
We have used pointers which directly points to data. In this section, let us see "What
is pointer to a pointer?"
2.26 J;l, Pointers
• The first declaration instructs the compiler to allocate the memory for the variable
a in which integer data can be stored.
• The second declaration tells the compiler to allocate a memory for the variable pi
in which address of an integer variable can be stored.
•. The third declaration tells the compiler to allocate a memory for the variable p2 in
which address of a pointer variable which points to an integer can be stored. The
memory organization for the above three declarations is shown below:
p2
r Garbage
value
pI
r--=:J __....
~value
Garbage
a
r
p2
Garbage
value
pI
~Garbage
~value
a
The memory organization after executing the statement p.l = &a is shown below:
r
p2
Garbage
value I
pI
j-----+) 1 10
a
The memory organization after executing the statement p2 = &pI is shown below:
p2 pI a
I 1-------+1)1 j-~)I 10 I,
~ Systematic Approach to Data Structures using C 2.27
The data item 10 can be accessed using three variables a, pI and p2 are shown below:
The following program illustrates the way the data item 10 can be accessed using the
variable a, using a pointer variable p l and pointer to a pointer variable p2.
Example 2.4.4.2: Program to access 10, using a variable, pointer variable and pointer
to a pointer variable
#include <stdio.h> TRACING
void maim)
{
int
int
int
a',
*pl;
**p2; I
p2
r Garbage
value
pI
~Garbage
value
CJ
a
a = 10;
I
p2
r Garbage
value
pI
~Garbage
value
~
a
,.
pl = &a;
I
r
. p2
Garbage
value
I =j
pI
~I I
a
10
p~ pl a
p2 = &pl; I j ~I=j ~I I 10
Output
printf("a = %d\n",a); a = 10
printf("*pl = %d\n", *pl); *pl = 10
printf("**p2 = %d\n", **p2); **p2 = 10
}
2.28 Q, Pointers
Ans: a) The expression *&x and x are the same. This statement IS true for the
following reasons:
• &x represent the address of the variable x.
Note: The indirection operator denoted by * and address operator denoted by & are
the inverse of each other and cancel each other. So, the expression *&x can be can
also be represented by x.
Ans: b) The expression * &x and & *x are the same. This statement is false for the
following reason:
• The expression *&x and the expression x are the same. But, the expression &*x is
illegal. This is because, the operators & and * are unary operators and are right
associative. So, *x is evaluated first. But, *x can be used only if the variable x
.1
contains address. But, it is given that x is declared as an integer variable not as a
pointer variable. So, &*x is illegal.
1* Increment a *1 a
++a;
1* Decrement b *1 a
--b;
Example 2.4.4.5: What is the error (if any) in each of the following expressions?
a. int x = 5;
b. int *x = 5;
C. int a·,
int *x = &a;
d. int a·,
int **p = &a;
2.30 Q Pointers
Solution: The valid and invalid statements along with reasons for invalidity are
shown below:
a. int x= 5; 1* valid *1
Reason for invalid: In the first statement, the variable a is declared as an integer
variable. This is valid declaration. In the second statement, address of variable a
is stored in the variable p. In such case, p should be declared as pointer to integer
as shown below:
int *p = &a.
But, the variablep is declared as pointer to pointer variable which is an error.
Example 2.4.4.6: Which of the following program segments IS valid? Give the
reasons for invalidity:
a. int *x;
scanf("%d", &x);
b. int *x;
scanf("%d", &*x);
c. int *x;
scanf("%d", *x);
d. int a;
int *x = &a;
scanf("%d", x);
Q Systematic Approach to, Data Structures using C 2.31
Solution: The valid and invalid statements along with reasons for invalidity are
shown below:
a. int *x;
scanf("%d", &x); 1* Invalid *1
Reason: The function scanft) requires address of data variable as an argument. Here,
x is declared as a pointer variable and it has not been initialized. So, the program
segment can be modified as shown below:
int a; 1* Declare data variable *1
int *x; 1* Declare pointer variable *1
b. int *x;
scanf("%d", &*x); 1* Invalid *1
Reason: The variable x is declared as a pointer variable and it has not been initialized.
So, the program segment can be modified as shown below:
. int a; /* Declare a data variable *1
int *x; 1* Declare a pointer variable *1
scanf("%d", &*x);'
Note: &a, x and &*x are all one and the same. So, instead of using &*x, we can also
usex or&a as an argument.
c. int *x;
scanf("%d", *x); 1* Invalid : Explanation is same as the above *1
..
2.32 .Q Pointers
d. int a·,
int *x = &a; 1* x is a pointer variable and it has been initialized *1
Example 2.4.4.7: Write a program to read the data to an integer variable a using the
variables p, q and r given the following declarations:
int a;
int *p;
int **q;
int ***r;
Solution: The program to read a value into variable a using the variables a, p, q, r is
shown below:
#include <stdio.h>
r q p a
1* After initialize q *1
q=&p;
Q I 3--1 =j \ )[ij
Garbage value I a
1* After initializing r *1
r=&q;
l
Q Systematic Approach to Data Structures using C 2.33
r q p a
printf("Enter the number: ");
scanf("%d", *q);
printf("a = %d\n",a);
13-1 3-1 =tG
/* Read the value for variable a using variable r */
}
printf("Enter the number:");
scanf("%d", **r);
printf("a = %d\n", a);
r
I 3-1.3---i
q
--r~
p a
Output
Enter the number: 10
a = 10
Enter the number: 20
a=20
Enter the number: 30
a = 30
Enter the number: 40
a =40
Note: While reading a value into a variable a, it is required to send the address as an
argument to the function scanfi) as shown below:
scanf("%d",&a);
Example 2.4.4.8: In the following program segments identify the logical error if any?
a. int **x;
int *y;
y=&x;
b. int **x;
int *y;
x=&y;
c. int **x;
int **y;
x=&y;
d. char c='A';
char **x;
char *y;
y=&c;
printf("%c", *x);
Solution: The logical errors identified for each of the program segment are shown
below:
a. int **x; /* Valid */
int *y; /* Valid */
y=&a;
x=&y;
Reason: Logical error exists still in above program segment, since the variable y
has not been initialized. The correct code can be written as shown below:
int **x;
int *y;
, int a;
x=&y;
y=&a;
Note: All the pointer variables should be initialized. Otherwise, it results m
logical error.
c. int **x;
int **y;
x = &y; 1* Invalid: Logical error exists *1
Reason: Since x is a pointer to a pointer variable, it should contain address of a
pointer variable. But, y is declared as pointer to a pointer variable which is an
error. So, the correct declaration is shown below:
int **x;
int *y;
int a;
y=&a;
x=&y;
d. char c = 'A';
char **x;
char *y;
y=&c;
printf("%c", *x); 1* Invalid: It is a logical error *1
Note: The pointer to a pointer variable x is used in printfi). But, the variable x has not
been initialized. So, it is a logical error. The cor:rect code can be written as shown
below:
char c = 'A';
char **x;
char *y;
y=&c;
x=&y;
printf("%c", *x);
2.36 Q Pointers
In this section, let us see "What is compatibility with respect to data variable and
pointer variable?"
Definition: We should not store the address of a data variable of one type into a
pointer variable of another type. During assigning we should see that the type of data
variable and type of the pointer variable should be same or compatible. Otherwise, it
results in compile error also called syntax error. Consider the following examples:
x=&a;
Note: The type of pointer variable x and type of data variable a are same. Hence, we
say that type of both variables are compatible.
Note: The type of pointer variable x isfloat and type of data variable a is char. Since
their types are different, we say that types of both variables are incompatible.
Q Systematic Approach to Data Structures using C 2.37
Now, the question is "Is it possible to use incompatible pointer types while
assigning?" The answer is yes. This is achieved with type casting a pointer. Now, let
us see "What is type casting a pointer?"
Definition: As we convert the type of one data variable into type of another data
variable using type casting, we can make an assignment between incompatible pointer
types using explicit type casting. This process of converting the type of a pointer
forcibly to another type is called type casting a pointer. For example,
Note: But, we should be very careful when the type of pointer is converted into
another type. It is extremely dangerous and must be used very carefully. Otherwise, it
results in runtime error which is very difficult to debug and trace.
Now the question is "Is there a way of defining a pointer variable to point to any data
type?" Yes, the answer is void pointer. Now, let us see "What is a void pointer?"
Definition: A void pointer is a special type of pointer. It can point to any data type.
Also, any pointer can be assigned to a void pointer. Hence, void pointer is also called
2.38 Q Pointers
- universal pointer or generic pointer. For example, a void pointer can be created as
shown below:
void *p;
Now, the pointer variable p can contain address of data variable of any type such as
int, float, char, double etc. The only limitation is that the pointed data can not be
referenced directly using indirection operator. This is because, its length is always
undetermined. So, Note: type casting must be used explicitly to convert the void
pointer to a pointer of specific data type.
14. ,'Output
15.
16.
printf("x = %d\n", *((int *)p));
, x = 10
17. , x p y
18. p=&y;
19.
20.
~G ( -, _ >( 3.141561
2l. 'Output
22. pr!n!f("y = %f\n", *((doub-le *)p))~ y = 3.14156
23.}
Note: Using void pointer (generic pointer) p, we can print integer value.Iline f~) by
typecasting to int*. The double value is printed (line 22) by typecasting to double"
Q Systematic Approach to Data Structures using C 2.39
Note: The void pointer can point to any type of data. It can point to integer, character
etc. But, accessing using void pointer requires explicit type casting as shown in line
15 and line 22.
Definition: An object that that occurs on the left hand side of assignment operator is
called lvalue. In other words, lvalue (pronounced as L value) is defined as an
expression or a storage location to which a value can be assigned.
For example, variables or an array element such as a[O], a[i], a[i][j] can appear
on LHS of '=' sign and their values can be changed. So, they are called lvalues.
Note: lvalues can be loaded (or modified). Since they appear on LHS of assignment
operator, they are called lvalues.
Definition: Rvalue refers to the expression on right hand side of the assignment
statement. In other words anything that occur on the right hand side of assignment
statement is called rvalue, The R in Rvalue indicates read the value of the expression
or read the value of the variable.
For example: 5, a+2, a[2]+4, a++, --y etc., are rvalues since they can occur on
right hand side of the production.
Note: rvalues can only be read and can not be modified. They appear on RHS of
assignment operator.
Note: In short, anything that we can place on the left hand side of the assignment
statement is an lvalue and anything that we can place on the right hand side of the
assignment or whose value can only be read is an rvalue.
2.40 Q Pointers
Example 2.4.6.2: Identify the lvaues and rvalues in the following statements
a. sum = 10;
b. x =b + 2;
c. a[i] = 100 + a[i];
d. *p = 50;
Solution: The lvalues and rvalues for each of the above statements is shown below:
Statements lvalues rvalues
*p = 50; *p 50
lvalues can be modified and hence normally they occur on LHS of assignment
operator. So, in the above statements sum, x, a[i} and *p are lvalues since they occur
on left hand side of '=' operator.
rvalues can not be modified and they can be only be read and normally they occur on
RHS of assignement operator. So, in the given statements 10, b+2, 100+a[i} and 50
are rvalues since they occur on the right hand side of '='·operator.
Note: A variable can either be lvalue or rvalue depending on whether it is towards left
of assignment or to the right of assignment. For example,
a =b;
Q Systematic Approach to Data Structures using C 2.41
Here, the variable a is an lvalue and the variable b is an rvalue. In the following
statement:
b = a;
Note: Even if an expression is lvalue, if it is part of the larger expression from which
rvalue is obtained, then the whole expression is an rvalue. For example,
• a[2] is an lvalue but, a[2]+4 is an rvlaue
• b is an lvalue but, the expression b++ is an rvalue
Now, the question is "Is a function can be made an lvalue?" The I answer is yes. It is
possible to make an assignment to a function call also i.e., a function call can appear
on the left hand side of assignment operator ('='). But, this is possible only if the
function returns an address. For example, consider the following program:
Note: The maxt) function returns address of a larger of the two variables passed to it.
Since the return value is an address, *max(&m, &n) acts as a pointer to location m.
So, assigning 55 to the expression *max(&m, &n) is equivalent to assigning it to m
itself. '
Note: If the value returned by function is an address of a variable (should not be local
to the function), then it clearly indicates that the returned value is actually an address
whose value can be modified. Hence, a function if it returns an address its value can
be changed and hence it is an lvalue.
Solution:
a. A pointer variable pi pointing to an integer along with its definition IS
shown below:
int i:, 1* i is an integer variable *1
int *pi 1* pi is a pointer variable *1
c. q=&x; /* Invalid */
The variable q should contain address of a variable of type
double. But, it contains address of integer variable. So, it is
invalid.
d. q = &d; /* Valid */
l
The variable q is pointer to a double. So, it should contain
address of double variable.
e. p=x; /* invalid */
-
Since p is a pointer to integer, it should contain address of
integer variable. Instead of storing the address, we are storing
the value of x. So, it is invalid.
Solution:
a. x /* lvalue and rvalue. */
Since x a variable, its value can be read and modified. So, when x value
is read, it is rvalue and when x value is being modified it is lvalue.
b. *x /* Ivalue and rvalue .:*/
The expression *x can be used if x is a pointer variable. The expression
*x means the value pointed to by x. The value pointed to by x can be
read as well as modified. So, when the value is being modified it is
lvalue and when the value is being read it is rvalue.
c. &x /* rvalue. *1
Since &x represent address of x and this value can not be modified. So,
it is rvalue.
d. *x + 2/* rvalue. */
Here, *x+2 is an expression. An expression can not be modified and
hence it is rvalue. -
Example 2.4.7.5: What are the operators that require an lvalue as an operand.
Solution: The operators that require an lvalue as an operand are shown below:
-Example 2.4.7.6: The following are invalid rvalue expressions? Give reasons.
a. a + 2 = 6
b. &(a + 2)
c. &4
d. (a+2)++;
e. ++(a+2);
Solution:
a. a + 2 = 6. /* Invalid rvalue */
An expression is always rvalue. Its value can be read but can not
be modified. So, it is invalid.
Example 2.4.7.7: If x and yare variable names and a is an array, identify the syntax
errors in the following statements with respect to lvalues and rvalues.
a. *x = *x + 2;
b. &x = &a[O];
c. y = &(x + 2);
d. a[5] = 5;
Solution:
a. *x = *x + 2; /* Valid */
,!;1,Systematic Approach to Data Structures using C 2.47
d. a[5] = 5; /* Valid */
Example 2.4.7.8: Write a function prototype statement with function name calc that
returns void and contains a reference parameter to an integer p and a reference
parameter to a long double q.
~ ~ ~
void calc int *p, long double *q
Example 2.4.7.9: Write a function prototype statement with function name spin
that returns a pointer to an integer and contains a reference parameter to an intger
p and a pointer parameter to the address of a long double q.
2.48 Q Pointers
~ ~ ~
int * spin int *p, long double *q
So, the function prototype is shown below:
int * spin (int *p, long double *q);
Example 2.4.7.10: In the following program, show the configuration of all the
variables and the output
#include <stdio.h>
void main(void)
{
.). /* Local definitions */
int a',
int *x;
int **y;
/*Statements */
a = 100;
x=&a;
y=&x;
printf("%d\n", a);
printf("%d\n", *x);
printf("%d\n", **y);
printf("%p", x);
printf("%p", y);
}
Solution: The configuration of all variables along with output is shown below:
Q Systematic Approach to Data Structures using C 2.49
#include <stdio.h>
x=&a; -,
y X a
4J I =j )1 100
I
Garbage val ue
y X a
y=&x;
< r
1
1
.1
'I
1
1 :: 100 I
]002 1004 ]006
Output
printf("%d\n", a); 100
printf(" %d\n", *x); 100
printf'%d\n", **y); 100
printf(" %p\n", x); 1006
printf'%p\n",y); 1004
return 0;
}
Example 2.4.7.11: Write a program that creates the structure shown in following
figure and then reads an integer into variable a and prints it using each pointer in
turn i.e., the program must read an integer into variable a and print it using t, u, v,
w, x, y and z.
2.50 ~ Pointers
Solution: The complete program to read the value for a and print it using all
pointer variables. The complete program is shown below:
#include <stdio.h>
void maint)
{
int a·, /* An integer variable */
int *t; /* pointer to integer */
int **u, **v; /* Pointer to pointers */
int ***w, ***x, ***y, ***z; /* Pointer to pointer to pointers */
t=&a;
u= v= &t;
w = x = &l+;
y= z= &v;
,
printf("Enter the value for a\n");
scanf("%d" ,&a);
printf("*t = %d\n", *t);
printf("**u = %d\n", **u);
printf("**v = %d\n", **v);
printf("***w = %d\n", ***w);
printf("***x = %d\n", ***x);
printf("***y = %d\n", ***y);
printf("***z = %d\n", ***z);
}
Q Systematic Approach to Data Structures using C 2.51
L With arrays
~ in dynamic memory
There is one to one correspondence between arrays and pointers. So, there exists a
close relationship between arrays and pointers. Now, let us see the relationship
between arrays and pointers.
a = 0100
Note: Assuming size of integer
&a[O] -+ O~ ie
I a[O] is 2 bytes, two bytes are reserved
&a[l] ~ 0102 (
W a[l] . for each memory location
I
Once the memory is allocated, the variable a contains 0100 which is the starting
address of the Oth item. This is called base address. But, the value 0100 stored in a
can not be changed. So, even though a contains an address, since its value can not
be changed, we call a as pointer constant.
Note: The array a is pointer constant only to the first element and not for the
whole array.
Note: &a[O] and a have the same pointer value 0100 and hence they are same.
To justify above points, now let us see "What is the output of the following
program?"
PROGRAM TRACING
I a = 0100
#inc1ude <stdio.h> I
I &a[O] -+ O~ ~O
I
void maint)
l&a[l] ~ 0102 jo
{ I
40
&a[3] ~ 0106 I
I &a[4] ~ 0108 SO
I
I Output
printf("%p %p\n", &a[O], a, a+O); I 0100 0100 0100
} I
Note: We may get different answer in our computer. But, whatever it is, observe that
the value of &a[O] or a or a+O are same.
Now, let us, see "How to access the address of each element?" The address of each
item can be accessed using two different ways:
..~
In general~ ~
&a[i] ( ) (a+i) wherei=Oto4
is same as o to 5-1
o to n - 1 (in general)
~ Systematic Approach to Data Structures using C 2.53
Note: The various ways of accessing the address of ith item in an array a is shown
below:
Normally used
&i[a] is same as i + a
So, &ali] or a+i or i+a or &i[a] are one and the same.
Once we know the address, we can obtain the data stored in that address using the
indirection operator The indirection operator * and & are inverse of each other and
hence they cancel each other. Fore example,
*(&a[O])' is same as a[O]
*(&a[4]) is same as a[4] and so on.
In general, t~a[i]) is same as a[i]
cancel each other to get a[i]
Note: The various ways of accessing the data of ithitem in an array a is shown below:
So, *(a+i) or *(i+a) or a[i] or i[a] are one and the same.
jo
&a[l] ~ 0 102 a[l]
{ I I
int a[5] = {10, 20, 30, 40, 50}; I &a[2] ~ 0 104 30 a[2]
int i = 3; I &a[3] ~ 0 106
I
40 a[3]
I
I &a(4) ~ 0 108 So a[4)
I
r
printfC"%d %d %d %d %d %d \n", *(&a[i)), a[iJ, *(a+i), *(i+a), i[a], *&i[a]);
}
Output
+
40
+
40
+
40
+ 40+
40
+
40
2.54 g Pointers
Assuming base address of a = 0100, the memory map along with calculation of
address of a[i] is shown below:
a~ 0100
-
a+O or&a[O] ~ A 0100=0100+0=0100+0*1
-
a + 1 or &a[1.] ~ B 0101 = 0100 + 1 = 0100 + 1*1
-
a+2 or&a[2] ~ C 0102=0100+2=OJOO+2*1
-
a + 3 or &a[3] ~ D 0103 = 0100 + 3 = 0100 + 3*1
-
a+4or&ar41~ E 0104 = 0100 + 4 = 0100 + 4*1
-
,Index
Base address<
ofitem~
~I II
! SiZeOf(!ar)
Assuming base address of a = 0100, the memory map along with calculation of
address of a[i] is shownbelow:
Q Systematic Approach to Data Structures using C 2.55
a = 0100
~
a + 0 or &a[O] ~ 0100 = 0100 + 0 = 0100 + 0*2
a + 1 or &a[l] ~ 0102 = 0100 + 2 = 0100 + 1*2
a + 2 or &a[2] ~ 0104 = 0100 + 4 = 0100 + 2*2
a + 3 or &a[3] ~ 0106 = 0100 + 6 = 0100 + 3*2
a+4 or&ar41 ~ 0108 = 0100 + 8 = 0100 + 4*2
r----
Base addres: ~
Index of item ~
J II
sizeof (int)
!
I In general, &a[i] = Base address + Index of item >;< sizeof (int) I
Example 2.5.2.1.3: Consider an array of floating point numbers with following
statement:
float a[S] = {lO.S, 20.S, 30.S, 40.S, SO.S};
Assuming base address of a = 0100, the memory map along with calculation 0
address of a[i] is shown below:
a = 0100
~
a + 0 or &a[O] ~ ;10.51 0100 = 0100 + 0 = 0100 + 0*4
I I I
r
I I
....---
Base address
Index of item~
II
sizeof (float)
!
I In general, &a[i] = Base address + Index of item * sizeof (float) -1
Note: In general, &a[iJ = Base address + Index of item * size of (data type)
2.56 Q Pointers
Assuming base address of a = 0200, the memory map along with calculation 0
address of a[i] is shown below:
p = a = 0200
P + 0 or &p[O]~. O~ To a[O]
f---:t-::-
p + 1 or &p[l]~ 0202 ~O a[l]
p + 2 or &p[2]~ 0204 r--ro- a[2]
p + 3 or &p[3]~ 0206 no
~
I
a[3]
p + 4 or &p[4]~ 0208 ~O a[4]
Using pointer variable p each item in the array can be accessed as shown below:
• Othitem can be accessed using *(p+O) or p[O] or O[p]
• l" item can be accessed using *(p+1) or p[l] or l[p]
• 2nd item can be accessed using *(p+2) or p[2] or 2[p]
• 3rd item can be accessed using *(P+3) or p[3] or 3[p]
• 4thitem can be accessed using *(P+4) or p[4] or 4[p]
Note: With respect to above memory configuration, let p = &a[2] (which is same as
p = a + 2) This indicates that pointer variable p points to a[2]. When a pointer is not
pointing to the Othitem in an array, the index can be negative.
For example, p[-2] or p[-I] etc., are valid.
Consider 5 elements 10, 20, 50, 2,5 and 15. It is required to find the largest of these 5
numbers. Now, let us see "How to write the program to find largest ofN numbers?"
Design: Assume the variable big contains 10 which is the Othelement of the array and
°
pos is which is the position of that element. The equivalent code can be written as:
°
big = a[O]; 1* Assume first item is big *1
pos = 0; . 1* Store as the position of Oth item *1
}....
Inltlalizatlon
Q Systematic Approach to Data Structures using C 2.57
Since Othitem 10 is in big, the rest of the items such as a[l}, a[2], a[3] and a[4}
should be compared with big as shown in figure:
J J J ~
I
if ( a[i] > big)
a[O] a[l] a[2] a[3] a[4]
_20--LOD--:-50~ 25
I:.....~~I;,;..:.:..' I 15
{
big
pos
=
= t;
a[i]; a~ ~l~
}
big = 10
If the above condition is true, then a[i] is larger and it is stored in big along with its
position i. It is clear from the above figure that i varies from 1 to 4 which can be
written as shown below:
i = 1 to 4.
t same
i = 1 to 5-1 where 5 indicates the number of elements
~
In general, i = 1 to n-I where n = 5 indicates the number of elements. So, the code can
be written as shown below:
Note: When we know the program using arrays, we can easily write the program
using pointers. \Ve have seen that a[i} is same,as *(a+i) or *(i+a) or i[a]. So replace
a[i] by *(a+i) to get the program using pointers.
Now, the complete program to find the largest of N elements using an array and
using pointer with indexing is shown below: _ -
2.58 Q Pointers
Example 2.5.2.3.2: Suppose, we want to divide the value pointed by p l by the value
pointed to by p2, then we may write the following statement:
x = *pl l*p2; II Error: This expression is wrong because 1* before p2
I; is treated as beginning of the comment in C
The error in the above statement can be eliminated by re-writing the statement as
shown below:
x = *pl I *p2 II Correct: Since there exists space between I and *, it is
II treated as division operation not as beginning of the
II comment
Note: Even though various operations can be performed on *pl and *p2 (since they
represent the values to be manipulated), the operations are restricted on p l and p2
since they contain only the addresses. The various operations that can be performed
on pointer variables are shown below:
Adding an integer to a pointer
Operations Subtracting an integer from pointer
performed
on pointers Subtracting two pointers
Comparing two pointers
2.5.2.4 Adding an integer to a pointer
Note: Addition can be performed when one operand is a pointer and other operand
is an integer.
p2 = a;
The various valid and invalid statements are shown, below:
• p l = p l + 1; 1* Valid *1
• p l = pl + 3; 1* Valid *1
int a[5]={IO, 20, 30, 40, 50}; float a[5]={10, 20, 30, 40, 50};
int *p; float *p;
a~ a~
p~ 0100 1--------;-~_0_____1 p~ 0100 iO$55
1
20
0102 1---+-----1 0104 20 :
~O
0104 1----;-,:-----1 0108 : ~O: I
40
0106 1----+---1 0112 : 40 :
0108 '---_5_0----' 0116 Li 50 i~
After executing the statement:
After executing the statement:
p++;
p++;
the pointer variable p will not
the pointer variable p will not
contain 0101. Instead, it contains
contain 0101. Instead, it contains
0104 because, if p is incremented
0102 because, if p is incremented
it will point to the next floating
it will point to the next integer.
point number.
Note: Each time p++ is executed, Note: Each time p++ is executed,
its value will be incremented by 2 its value will be incremented by 4
because size of integer is 2 bytes. because size of floating point
In other words, p points to the number is 4 bytes. In other words,
next item. p points to the next item.
Q Systematic Approach to Data Structures using C 2.61
Now, let us see "How to write a program to display array elements using pointer?"
Design: 'Consider the following array and assume p points to the beginning of the
array. To start with p points to 0100 and *p refers to 10. Let us observe the outputs in
various iterations shown below:
tp
I 30
tp
up 50
tp
I
t
p p P
Iterations: i = 0 1 2 4
print 10 and increment p using ~(__ ...JI
printf("%d\n", *p); II 10
p++; II 0102
print 20 and increment p using +- --J
printf("%d\n", *p); II 20
p++; II 0104
print 30 and increment p using +------------'
printf("%d\n", *p); II 30
p++; II 0106
print 40 and increment p using+--------------I
printf("%d\n", *p); II 40
p++; II 01Q8
print 50 and increment p using~(---------------'
printf("%d\n", *p); q II 50
p++; II 0110
2.62 ~ Pointers
In general, if statements:
r-::tf("%dln'
U~; " *p);
°
are repeatedly executed for i = to 4, we get the output 10, 20, 30, 40 and 50. The
C equivalent statement using for loop is shown below:
#include <stdio.h>
void maint)
{
int a[] = {l0, 20, 30, 40, 50 };
int *p;
int 1;
p = a; /* same as p = &a[O] */
In the previous example, instead of printfi) within the for loop, if we use the
statement
sum = sum + *p;
then we add all the elements of the array. The complete program to add n elements is
shown below:
#include <stdio.h>
void maim)
{
int a[] = {10, 20, 30, 40,50 };
int *p;
int 1, sum;
Note: Observe that by executing p++, we can point p to the next element. On similar
lines by executing p--, we can point p to the previous element in an array.
Note: Subtraction can be performed when first operand is a pointer and the second
operand is an integer.
2.64 Q Pointers
.
Example 2.5.2.7.1: Consider the following declaration and initialization:
int a[5] = {l0, 20, 30, 40, 50};
int *pI;
p l = &a[4];
• pI = p l - 3; /*Valid */
• pI--; /*Valid:: Same as pI = p l - ~ */
• --pI; /*Valid:: Same as p I = p l - I */
• pl = I - pl; /*Invalid:: Illegal pointer subtraction
Note: The first operand should be a pointer where as
second operand should be integer. */
Example 2.5.2.7.2: Write a program to display array elements using pointer fro
last element to first element.
Note: ~s we execute p++, pointer variable p points to next element, if we execute p--
pointer variable p points to the previous element.
Design: To get the array elements in reverse order, point the variable p to point to th
end of the array and replace p++ by p- - in the previous program. The complet
program is shown below:
#include <stdio.h>
void mainf)
{
int a[] = {10, 20, 30, 40,50 };
int *p;
int 1;
p l = a; 1* same as p l = &a[O] *1
p2 = &a[4];
If two pointers are associated with the same array, then comparison of two pointers is
allowed using relational operators. But, if the two pointers are associated with
different arrays, even though comparisons of two pointers is allowed, the result is
meaningless.
p l = a;
p2 = a;
~ Systematic Approach to Data Structures using C 2.67
Now, let us see "How to write a program to display array elements by comparing
of two pointers?"
Design: Let us use two pointers p and q where p points to the first element of array a
and q points to the last element of array a as shown below:
p q
~ ~
0100 0102 0104 0106 0108
a I to I i-o 01 3.0 40 I 50
Observe from the above figure that as long as p <= q, value pointed to by p can be
printed and updated using the following statements:
while (p <= q)
{ I Output
printf("%d ", *p); I 10 20 30 40 50
p++; I
} I
So, the complete program is shown below:
#include <stdio.h>
void maim)
{
int a[] = {10, 20, 30, 40, 50 };
int *p;
int *q;
2.68 Q Pointers
Note: Two pointer subtractions and two pointer comparisons are generally performed
ifboth the pointers point to the same array.
The two ways of declaring and using the array in the called function are:
• using pointer declaration ---------}
• using array declaration
!
void function _name(int a[]} void function_name(int *a)
{ {
/* ith item can be accessed /* ith item can be accessed
using a[i] using *(a+i)
*/ */
} }
The key to be searched is compared with middle element. This can be done using the
statement
if (key = a[mid] )
{
*position = mid; /* Obtain position of item in the array*/
return;
}
After executing the statement, if they are equal, the position of item in the array is
returned. If this condition. is false, key may be in the left part of the array or in the
right part of the array. If key is less than the middle element, the table in the left part
has to be compared from low to mid-I. Otherwise, the right part of the array has to be
compared from mid + 1 to high. This can be achieved by using the following
statement.
2.70 ,!;l Pointers
void search(int item, int a[], int n, int *pos) void search(int item, int *a int n, int *pos
{ {
int low, high; int low, high;
1* Initialization *1 1* Initialization *1
low = 0; low = 0;
high = n-l ; high = n-I;
*pos = -1; 1* Item not found *1 *pos = -1; 1* Item not found */
} }
Q Systematic Approach to Data Structures using C 2.71
The complete C program to search for an item using binary search is shown in
example:
Example 2.6.2: C program to search for any item in array using binary search
#incIude <stdio.h>
#incIude <process.h>
printff'Enter n valuesvn");
for ( i = 0; i < n; i++) scimf("%d",&a[i]);
if (position = -1)
printf("Item not found\n");
else
printf("Item found at %d position\n", position);
}
where
2.72 Q Pointers
where
• data_type refers to the data type of the array
• ptr is the name of the pointer variable which replaces [exp 1] in the earlier
notation
• exp2 represent the maximum number of elements in that column.
For example, consider the declaration with a 2-dimensional array initialized with 3
rows and 4 columns along with a pointer variable p as shown below:
int a[3][ 4] = {
{10, 20, 30, 40},
{50, 60, 70, 80},
{25, 35, 45, 55}
};
Row 2
rr
&a[O][O]
lOOO 1033
r
p p+2 P +3
r
Q Systematic Approach to Data Structures using C 2.73
I t t t + (Columns)
A nm o 1 2 3 n=4
o a[O][O] a[O][I] a[O][2] a[O][3]
Note: To access each item row wise in 2-dimensional array, the row index i should be
in the outer loop and column indexj should be in the inner loop. So, the C statements
to access any element a[i][j] can be written as:
for (i = 0; i <= m-l; i++) for (i = 0; i < m; i++) /*m -Rows */
{ {
for U = 0; j <= n-l; j++) for U = 0; j < n; j++) /*n-Columns*/
{ ~ {
a[i][j]; a[i][j]; /*access */
} }
} }
Procedure: Let a and b are two matrices of size m x n. We know that each item of a
can be accessed using the following code:
for (i = 0, i < m; i++) 1* m - rows *1
{
for (j = 0; j < n; j++) 1* n - columns *1
{
a[i][j]; 1* Access each item of a *1
}
}
2.76 Q Pointers
-
Instead of accessing only the elements of matrix a, we access the corresponding
elements of b, perform the addition and store it in the matrix c. This is achieved by
replacing a[i][j] in the above program segment inside the for loop by the statement:
c[i][j] = a[i][j] + b[i][j];
So, the complete C function to add two matrices a and b of size m x n is shown
below:
The various operations that can be performed to add two matrices are shown below:
The complete program to add two matrices along with various inputs and outputs is
shown below:
Q Systematic Approach to Data Structures using C 2.77
Example 2.7.4: C Program to add two matrices using conventional method and using
pointer to I-dimensional array
#include <stdio.h>
add_matrix(m, n, a, b, c);
end
start
Example 2.8.1: Interpret the declaration: int x. The declaration can be pictoriall
represented as shwon below:
Example 2.8.2: Interpret the declaration: .int *p. The declaration can be pictoriall
represented as shwon below:
to
Reading in the direction of
arrow along with the labels we
have:
p is a * to int
i.e., p is a pointer to an integer
start [By reading * as pointer, int as
integer]
Q Systematic Approach to Data Structures using C 2.79
Example 2.8.3: Interpret the declaration: int a[10]. The declaration can be pictorially
represented as shwon below:
is an array of
Reading in the direction of arrow
along with the labels we have:
a is an array of 10 int
i.e., a is an array of 10 integer
start
[By reading int as integer]
Example 2.8.4: Interpret the declaration: int *p[5]. The declaration can be
pictorially represented as shwon below:
to
Reading in the direction of
arrow along with the labels we
have:
p is an array of 5 * to int
i.e., p is an array of 5 pointers
start to integers where * is pointer,
int is integer
Example 2.8.5: Interpret the declaration: int (*p) [5]. The declaration can be
pictorially represented as shwon below:
Example 2.8.6: Interpret the declaration int a (int b). The declaration can be
pictorially represented as shwon below:
is a function
end I ~
I int a I I (int b) I
II I...---r---i with b as integer parameter
i
start
returning
Note: If an identifier is followed by ( .... ), it indicates a function call or function
declaration. So, reading in the direction of arrow along with the labels we have:
a is a function with (int b) and returning int
i.e., a is a function which accepts b an integer as a parameter and returning an integer
Memory can be reserved for the variables either during compilation or during
execution time (run time). This gives rise to various memory allocation techniques.
Now, let us see "What are the various memory allocation techniques?" Memory can
be allocated for variables using two different techniques:
Definition: If the memory is allocated (i.e., reserved) for various variables during
compilation time itself, the allocated memory space can not be expanded to
accommodate more data or can not be reduced to accommodate less data. In this
technique, once the size of the memory allocated is fixed, it can not be altered even
during execution time. This method of allocating the memory during compilation time
is called "static memory allocation". For example, consider the following declaration:
int a[lO];
Q Systematic Approach to Data Structures using C 2.81
During compilation, the compiler will allocate 10 memory locations (each location
consisting of 2 bytes say) for the variable a. In the worst case, 10 elements can be
inserted. Less than 10 elements lead to under utilization of allocated space and more
than 10 elements can not be inserted.
Disadvantages
• The memory is allocated during compilation time. Hence, the memory allocated is
fixed and can not be altered during execution time
• Leads to under utilization if more memory is allocated
• Leads to overflow if less memory is allocated
• The static nature imposes certain limitations and can find their applications only
when the data is fixed and known before processing.
The linked lists and trees are inherently dynamic in nature, growing and shrinking as
needed. So, to implement these data structures, the program should be able to allocate
and free memory as and when required.
Now, let us see "What are the differences between static memory allocation and
dynamic memory allocation?" The various differences between static allocation and
dynamic allocation technique are shown below:
Before we discuss various memory allocation functions and the concept of dynamitic
memory allocation, let us see "What is the memory allocation process associated with
C program?" or "What is the memory map of C program?" A compiled C program
creates and uses the following logical distinct regions of memory:
Program memory
global/static
Memory
heap
stack
Let us assume the size of the memory is 1MB = 1024 x 1024 bytes = 1048576 bytes
of memory. All these locations are numbered sequentially from 0 to 1048575
(memory organization in C) is shown below:
Q Systematic Approach to Data Structures using C 2,83
Addresses
00000
Program Code 00001
+ ~
Heap area grows downwards
Note: Addresses are in ascending order from 0 to 1048575. The addresses for
program, data, global and static variables along with memory reserved for dynamic
allocation will be in increasing order. But, the addresses of the local variables will be
in decreasing order.
• Program memory This is the first region in the memory which is reserved for
various functions such as maim), user defined functions and standard functions.
Before execution all the necessary functions are loaded into this part of the
memory (Note: Loading is the process of copying machine code of the program
to be executed from the secondary devices into main memory).
• Global/static memory The second region is the memory where global variables
and static variables are stored. Since the lifespan of these variables is till the end
of the program, this part of the memory is reserved for these variables till the end
of the program. Note: For the detailed example, see the section 1.15.1, page 1.70.
2.84 Q Pointers
• Heap The third region is the large free memory available to the program only
during execution. This free memory available between stack area and global data
area is called heap. If memory is allocated from this area, size of heap decreases
and when memory is de-allocated the size of heap increases. In other words,
during execution size of heap changes. So, if we keep allocating the memory and
never de-allocate, there i~ possibility of "overflow" during dynamic allocation
process. In such situations, the memory allocation functions returns NULL
pointer.
• Stack This is the last area in the memory. This part of the memory holds local
variables, return address of the function calls along with arguments to functions if
any. The stack memory, heap and global memory together is called data memory:
Memory calloc
management
functions realloc
free
Using the functions malloct), calloct) and realloc/) additional memory space can be
allocated whereas using the function freet) unwanted space can be deleted there by
optimizing the use of storage space.
Note: If there is no space in the heap region, memory can not be allocated and this.
situation is called "Overflow of memory". In such situations, the functions malloct),
calloct) and realloct) returns NULL. It is the responsibility of the programmer to
check for overflow of memory ..
Q Systematic Approach to Data Structures using C 2.85
2.9.2.1 malloc(size)
Now, let us see "What is the purpose of using malloc?" This function allows the
program to allocate memory explicitly as and when required and the exact amount
needed during execution. This function allocates a block of memory. The size of the
. block is the number of bytes specified in the parameter. The syntax is shown below:
#include <stdlib.h> 1* Prototype definition of malloct) is available *1
where
• ptr is a pointer variable of type data_type
• data_type can be any of the basic data type or user defined data type
• size is the number of bytes required
• On successful allocation, the function returns the address of first byte of allocated
memory. Since address is returned, the return type is a void pointer. By type
casting appropriately we can use it to store integer, float etc.
• If specified size of memory is not available, the condition is called "overflow of
memory". In such case, the function returns NULL.
Note: Many data structures, such as linked lists, trees etc., uses this function to
allocate the memory from heap area.
2.86 Q Pointers
Example 2.9.2.1.1: Now, let us see "What will happen if the following program
segment is executed?
int *ptr;
ptr = (int .*) malloc (10);
The compiler reserves the space for the variable ptr in the memory. No initialization
is done if ptr is a local variable (It is initialized to NULL if it is global variable).
Now, using the function malloct) we can request a block of memory to be reserved. A
block of memory may be allocated or may not be allocated. Now, let us discuss both
the situations.
Case 1: Un successful allocation of memory: Consider the following memory
status:
0098 0100 0102 0104 0106 0108 0110 9999
~p~~r~1 J1'ree: f
~ -7 ~(---- Used by other programs )
memory
Since ptr contains NULL, it indicates that memory has not been allocated and the
user should not use ptr to store some data. Instead, if ptr is NULL appropriate
message has to be displayed using the following statement:
if (ptr = NULL) )
{
printf("Insufficient memory\n"); .
return;
}
Q Systematic Approach to Data Structures using C 2.87
If ptr is not NULL, it indicates that memory has been allocated successfully and
allocated memory can be used to store the data. This situation is discussed below:
I ?
p;r I-~(=~~~---,-_
I
Free memory ---------7)
~ I
Since 10 bytes of free memory space is available, the function malloct), allocates a
block of 10 bytes of memory and returns address of the first byte. This returned
address is stored in pointer variable ptr as shown below:
Note: ptr points only to the first byte of memory block of 10 bytes reserved using
malloct)'
. function. Butthe allocated ·memory is not
. . .
ihi·tialized.
Now, let tis' see "How to r~ad the data into allocated memory?" Because of type cast
(int *), every two bytes are used to store an integer. So, totally 5 integers can be read
and stored in the allocated memory of 10 bytes using the following statement:
Note: The expression ptr + i can be written using array notation as &p[i] or &i[p].
Now, let us see "How to access the data from allocated memory?" The contents of
the above memory locations can be accessed using *(ptr+i) with the help of de-
reference operator or using p[i] or i[p] using array notation. The equivalent
statements are: .".
Definition: A dynamic array allows us to keep the number of elements of the array
unspecified at the time of declaration. The number of elements that it can hold can be
specified during execution time. Using malloct), required number of bytes can be
allocated and operate on that memory as if it were an array using indexing. Thus, we
can have -a dynamically allocated array Later, required data can be stored in these
locations during execution time. The allocated memory can also be de-allocated if not
used.
Note: Example 2.9.2.1.2 (see previous page) shows how memory can be dynamically
allocated for an array of n elements and shows how data can be written into these
memory locations and how the data can be read from these memory locations.
Now, let us see "What is the purpose of using calloc?" This function is used to
allocate multiple blocks of memory. Here, calloc - stands for contiguous allocation of
multiple blocks and is mainly used to allocate memory for arrays. The number of
blocks is determined by the first parameter n. The size of each block is equal to the
number of bytes specified in the parameter i.e., size. Thus, total number of bytes
allocated is n*size and all bytes will be initialized to O. The syntax is shown below:
where
• ptr is a pointer variable of type data_type
• data_type can be any of the basic data type or user defined data type
• n is the number of blocks to be allocated
• size is the number of bytes in each block
Now, let us see "What does this function return?" The function returns the following
values:
• On successful allocation, the function returns the address of first byte of allocated
memory. Since address is returned, the return type is a void pointer. By type
casting appropriately we can use it to store integer, float etc.
2.90 Q Pointers .
• "If specified size of memory is not available, the condition is called "overflow of
memory". In such case, the function returns NULL.
void function_nameO
{
Example 2.9.2.2.1: What will happen if the following program segment' is executed?
int *ptr; . ,
ptr= (int *) calloc (5, sizeoflintj);
'Sol~iioQ: Th~.:compiler. rese~v'~s th~ space for'the variable. ptr 'In thernemory. No
initializationis done it ptr is alocal 'variable (It is initialized to NULL, if it is' global
variable) .. 'Now~ using the function calloct) we can request specified number of
blocks of memory to be reserved. The specified blocks of memory may be allocated
or may not be allocated. Now, let us discuss both the situations.
Since5*2 = 10 bytes of free memory space is not available, the function calloct), can
notallocate memory and hence the function returns NULL and is copied into ptr. The
memorystatus after execution of above statement is shown below:
0098
ptr ,~ ~I(
0100
Free
I
memory
0102 0104
~---
0106 0108 0110
Since ptr contains NULL, it indicates that memory has not been allocated and the
user should not use ptr to store some data. Instead, if ptr is NULL appropriate
messagehas to be displayed using the following statement:
if (ptr = NULL)
{
printf("Insufficient memory\n");
return;
}
If ptr is not NULL, it indicates that memory has been allocated successfully and
allocatedmemory can be used to store the data. This situation is discussed below:
Since 5*2 = 10 bytes of free memory space is available, the function calloct),
allocates 5 blocks of 2 bytes each, initializes each byte to 0 and returns the address of
the first byte. This returned address is stored in pointer variable ptr as shown below:
Note: ptr points only to the first byte and the allocated memory is initialized to O's.
Now, let us see "How to read the data into allocated memory?" Because of type cast
(int *), every two bytes are used to store an integer. So, totally 5 integers can be read
and stored in the allocated memory of 10 bytes using the following statement:
If we input 10, 30, 20, 60 and 50 after executing the above statement, these numbers
are stored in memory as shown below:
0100 0102 0104 0106 0108 0110 9999
'-------=:l~~~~~~~~ I I J
=>fE= Free memory ~ I
i ote: The expression ptr + i can be written using array notation as &p[i) or &i[p).
Now, let us see "How to access the data from allocated memory?" The contents of
the above memory locations can be accessed using *(ptr+i) with the help of de-
reference operator or using p[i) or i[p) using array notation. The equivalent
statements are: .~..
for(i=0;i<5;i++) ~
~'~~.:2~l~.~
1l~~~~~~~;,:,';'~
-.~
~o
printf("%d ", *(ptr+i) ); '''~f1;''..> " ••
The program below shows how to find maximum of n numbers which uses the
concept of dynamic arrays using calloct) function. This program prints the largest of n
elements along with its position. Note that the value of n is supplied only during
execution time. This is straightforward program and it is self-explanatory.
#include <stdio.h>
#include <stdlib.h>
void mairu)
{
int *a, i, j, n;
Q Systematic Approach to Data Structures using C 2.93
ow, let us see "What is the difference between malloct) and callocf)?' Even though
malloct) and callocr) are used to allocate memory, there is significant difference
between these two techniques. The difference between these two lies in the way the
memory is allocated' and initialized along with the number of parameters passed to
them.
2.94 Q Pointers
rnalloc '{',...
. --
calloc
1. The syntax of mallocr) is: 1. The syntax of calloc is:
ptr = (data_type*) malloc(size); ptr = (data_type *) calloc(n, size);
- calloci).
p = rnalloc(sizeof(int) * n); i
p = calloc(sizeof(int) * n);
memset(p, 0, sizeof(int) * n)
Before using this function, the memory should have been allocated using malloct) or
calloct). Sometimes, the allocated memory may not be sufficient and we may require
~ Systematic Approach to Data Structures using C 2.95
additional memory space. Sometimes, the allocated memory may be much larger and
we want to reduce the size of allocated memory. In both situations, the size of
allocated memory can be changed using realloci) and the process is called
reallocation of memory. The reallocation is done as shown below:
• realloct) changes the size of the block by extending or deleting the memory at
the end of the block.
• If the existing memory can be extended, ptr value will not be changed
• If the memory can not be extended, this function allocates a completely new
block and copies the contents of existing memory block into new memory
block and then deletes the old memory block. The syntax is shown below:
where
• ptr is a pointer to a block of previously allocated memory either using
malloct) or calloct).
• size is new size of the block
if (ptr == NULL)
{ /* Memory is not allocated */
printft'Tnsufficient memory\n");
return;
}
Now, let us see "What does this function return?"This function returns the following
values:
• On successful allocation, the function returns the address of first byte of allocated
memory.
• If specified size of memory can not be allocated, the condition is called "overflow
of memory". In such case, the function returns NULL.
Case 1: Reducing the size of the allocated memory. Consider the following
memory status
0098 0100 0102 0104 0106 0108 0110 9999
allocated by
malloct) or calloct) programs
Assume, ptr points to the address of the first byte of memory block. After executing
the statement:
ptr = (int *) realloc(ptr, 3*sizeof(int) )
only 3*2 = 6 bytes are re-allocated starting from 0100 and last block of memory
starting from 0106 is de-allocated and the resulting memory status is shown below:
l'
ptr ~re-allocated---7~ Free -7 ~ Used by other -7
by realloc memory programs
Observe from the above memory status that memory is deleted from the end of block
t ere by free memory space is increased.
Case 2: Extend the allocated memory without changing the starting address.
Consider the following memory status
Assume, ptr points to the address of the first byte of memory block. After executing
the statement:
ptr = (int *) realloc(ptr, 4*sizeof(int) )
only 4*2 = 8 bytes are re-allocated starting from 0100 and the resulting memory
status is shown below: .
Q Systematic Approach to Data Structures using C 2.97
Used by other -7
programs
Observe from the above memory status that free memory space is reduced by re-
allocating the extra memory.
Case 3: Extend the allocated memory by changing the starting address. Consider
the following memory status.
4*2 = 8 bytes should be re-allocated. But, the existing memory block can not be
extended because there is no free space at the end of the current block. So, realloc
allocates a completely new block, copies the existing memory contents to the new
block and deletes the old allocation as shown below:
After copying the data from old block to new block, the remaining locations of new
block are not initialized and hence are represented using ?? Thus, the function
realloct) guarantees that re-allocating the memory will not. destroy the original
contents of memory.
2.98 Q Pointers
2.9.2.4 free(ptr)
This function is used to de-allocate (or free) the allocated block of memory which is
allocated by using the functions callocr), malloct) or realloct). It is the responsibility
.lof a programmer to de-allocate memory whenever it is not required by the program
and initialize ptr to NULL. The syntax is shown below:
free(ptr);
ptr = NULL;
Here, ptr is a pointer to a memory block. Now, let us see how this function works. For
example, consider the following memory configuration where ptr points to a memory
block containing 200 integers ranging from 01 to 0200.
11
ptr
Q Systematic Approach to Data Structures using C 2.99
free(ptr);
i...--....-.il-( ---L...........--L--...;...F-L..--.-.J.-----L..----'-------i, ~
l' ree memory 7"
ptr
This shows the memory allocated for 200 integers is de-allocated and is available as
part of the free memory (heap area).Observe from the above figure that even after
freeing the memory, the pointer value stored in ptr is not changed. It still contains the
address of the first byte of the memory block allocated earlier. Using the pointer ptr
even after the memory has been released is a logical error. Note: These logical errors
are very difficult to debug and correct. So, immediately after freeing the memory, it is
better to initialize to NOLL as shown below:
Example: 2.9.2.4.1: Sample program to shown the problems that occur when freet) is
not used.
1. #iocIude <stdlib.h>
2.
3. void maim)
4. {
5. int *a;
6.
7. a = (int *) malloc(sizeof(iot)); a
8. *a = 100; ".
9. ......•.. ;l.
Now, let us see "What will happen if the above program is executed?" The various
activities done during execution are shown below:
• When control enters into the function main, memory for the variable a will be
allocated and will not be initialized.
• When memory is allocated successfully by malloc (line 7), the address of the
first byte is stored in the pointer a and integer 100 is stored in the allocated
memory (line 8).
• But, when the memory is allocated successfully by using the function malloe
in line 10, address of the first byte of new memory block is copied into a
(shown using dotted lines.)
Observe that the pointer a points to the most recently allocated memory, thereby
making the earlier allocated memory inaccessible. So, memory location where the
value 100 is stored is inaccessible to any of the program and is not possible to free so
that it can be reused. This problem where in memory is reserved dynamically but no~
accessible to any of the program is called memory leakage. So, care should be taken
while allocating and de-allocating the memory. It is the responsibility of the
programmer to allocate the memory and de-allocate the memory when no longer
required.
In the section 2.7, we have seen how a 2-dimensional array can be represented using
pointer to an array. But, a 2-dimensional array can be expressed in-terms of array of
pointers also. The conventional array definition is
data_type array_name[expl][exp2];
~ Systematic Approach to Data Structures using C 2.101
data_type *array_name[expl];
where
• data_type refers to the data type of the array
• array_name is the name of the array
• expl is the maximum number of elements in the row.
Note that exp2 is not used while defining array of pointers. Consider a 2-dimensional
vector initialized with 3 rows and 4 columns as shown below:
int p[3][4] = {
{10, 20, 30, 40},
{50, 60, 70, 80},
{25, 35, 45, 55}
};
The elements of matrix p are stored in memory row-wise (can be stored column wise
also) as shown below:
1033
prO] p[l)
i
p[3]
int *p[3];
Here, p is an array of pointers. p[O] gives the address of the first row, p[l] gives the
address of the second row and p[2] gives the address of the third row. Now, p[O]+O
gives the address of the element in Othrow and Othcolumn, p[O]+ 1 gives the address of
the element in Othrow and 1stcolumn and so on. In general,
• address of ithrow is given by a[i]
• address of an item in ithrow and fh column is given by
p[i] + j
• the element in i" row and r
column can be accessed using the indirection
operator * a by specifying
*(p[i] + j)
2.102 Q Pointers
ote: Even though standard array indexing notation is easier to understand, pointer
arithmetic is faster and more efficient. Since efficiency is most important in most of
the applications, CIC++ programmers commonly use pointers to access array
elements.
The main program which calls all above functions and to find sum of two matrices is
shown below:
#include <stdio.h>
void maint)
{
int i, m, n, a[10][20], b[10][20], c[1O][20], *p[10], *q[10], *r[10];
add_matrix(m, n, p, q, r);
Now, let us see "What is the advantage of array of pointers?" The advantage can be
explained by considering the following example:
Example 2.10.5: Give the memory representation for the following initialization
statement:
char a[5] [11] = {
"DHARMARA YA",
"BHIMA",
"ARJUNA" ,
"NAKULA",
"SAHADEV A"
};
Solution: From the declaration, it is clear that a is a 2-dimensional array with 5 rows
and 15 columns. These five memory locations are initialized with values terminated
by \0 as shown below:
o 2 3 4 5 6 7 8 9 10
D H A R M A R A Y A \0
B H I M A \0
A R J U N A \0
a~~
N A K U L A \0
4 S A H A D E V A \0
t
Iri general, using a[i] for i ~ 0 to 4, we can access each name.
Note: The array a is 2-dimensional vector of 5xll size thus requiring 55 memory
locations. The size of each row is fixed. If the string size is less than the given size, it
results in ragged array as shown in above figure
Q Systematic Approach to Data Structures using C 2.105
ow, let us see "What is ragged array? What is the disadvantage of ragged array?"
Observe from previous example that totally 5 x 11 = 55 memory locations are
reserved so that each name can have at most 11 characters including the \0 character.
Here, 11 memory locations are fixed for each string. So, even if the string is small, 11
memory locations are fixed for that small string. The remaining locations on the right
of the string may be empty giving rise to uneven (ragged) right border. For ragged
right border see the memory representation shown in example 2.10.5. This type of
array is called ragged array.
Example 2.10.6: Give the memory representation for the following initialization
statement:
char *a[5] = {
"DHARMARA YA",
"BHlMA",
"ARJlJNA",
"NAKULA",
"SAHADEV A"
};
Solution: From the declaration, it is clear that a is an array with 5 rows. These five
memory locations are initialized with values terminated by \0. Observe that an array a
has 5 memory locations each of which points to a name. The memory representation
is shown below:
r--
o 1 2 3 4 5 6 7 8 9 10
a[O] r--
D H A R M A R A IY IA I \0 I
a[l] f-- B H I M A \0
a[2] r-- A R J U N A \0
a[3] N A K U L A \0
r--
a[4] ~ S A H A D E V A I \0 I
Observe from this memory representation that there is no wastage of memory.
2.106 Q Pointers
Pointer to a function (also called function pointer) is a very powerful and confusing
feature of C/C++. Function pointers provide some extremely interesting, efficient and
elegant programming techniques. Unfortunately, probably due to their complicated
syntax, step motherly treatment is given in most of the books and addressed quite
briefly and superficially. Actually, function pointers are less error.prone than normal
pointers since we will never allocate or de-allocate memory for the functions. All we
have to do is to understand what they are and learn their syntax.
A function, like a variable, has a physical location in the memory. Like
assigning address of a variable to pointer, it is possible to assign address of a function
to a pointer. This address is the entry point of a function and this is the address used
when the function is invoked.
Once a pointer points to a function, the function can be invoked using this
pointer. Using function pointer, it is possible to pass functions as parameters (similar
to the way the variables are passed as parameters). The address of a function can be
obtained by specifying the name of the function without any parentheses or arguments.
This is similar to the wayan array's address is obtained with only the array name,
without using indices. The syntax to declare a pointer to a function is shown below:
type (*fp) ( );
were
• type is the type of value returned from the function
• fp is pointer to a function
Note: The parentheses around *fp is mandatory. In case parentheses around *fp are
missing, then the meaning changes (It means fp is a function which returns a pointer
to the specified type).
type *fp( );
and
type (*fp) ();
Now, let us write various functions to evaluate arithmetic functions and see how these
functions can be called normally and using function pointers.
Q Systematic Approach to Data Structures using C 2.107
/* Function divide a by b */
float Divide (float a, float b)
{
return aIb;
}
The function which can call all the above functions can be written as shown below:
Example 2.11.2: C functions to perform add, subtract, multiply and divide using
switch
The complete program to add, multiply, divide and subtract two numbers is shown
below:
#include <stdio.h>
void maint)
{
Operation(10, '+', 20); 1* Perform 10 + 20 *1
Using function pointer, the above program can be written as shown in example 2.11.4.
Even though this is a simple example, the usage of function pointer is very clear from
this example.
Example 2.11.4: C Program to add, subtract, multiply and divide two numbers using
function pointer
#include <stdio.h>
.
1* Display the result *1
printf("The result using function pointer = %f\n", result);
void maint)
{
1* Function Plus is invoked to get 10 + 20 *1
Operation( 10, Plus, 20);
By this time, we should have understood the full concepts of C pointers and given
any problem we should be in a position to solve. After understanding the full
concepts of pointers, we should be in a position to answer the question "What are
the adv antages and disadvantages of pointers?"
Ad" antages
• More than one value can be returned using pointer concept (pass by reference).
• Very compact code can be written using pointers.
• Data accessing is much faster when compared to arrays.
• Using pointers, we can access byte or word locations and the CPU registers
directly. The pointers in C are mainly useful in processing of non-primitive
data structures such as arrays, linked lists etc.
Disadvantages
• Un-initialized pointers or pointers containing invalid addresses can cause
system crash.
• It is very easy to use pointers incorrectly, causing bugs that are very difficult to
identify and correct.
• They are confusing and difficult to understand in the beginning and if they are
misused the result is not predictable.
Exercises
10. What are the similarities and differences between pointer to an int and pointer
to a float? . (Feb/Mar 2005)
11. What is the difference between *a[10], (*)[10], (*a)O?
12. Give the memory map for the following declarations assuming variables as
a. Global variables
b. Local variables
int *pi; 1* Pointer to an integer *1
float *pf; 1* Pointer to a float number *1
char *pc; 1* Pointer to a character *1
13. Find the final values stored in the variables x, y and z at the end of the
program:
void mainO
{
int x, y, z, *p, *q;
x = 10;
y = 15;
z= 20;
p=&x;
q=&z;
*q = *p + y - 3;
y = y- (*p);
*p = *q-z;
}
14. What do you mean by dynamic memory allocation? Explain the different
functions used for this. (VTU) July/Aug 2004
15. What is a pointer? Explain the conceptof pointer
16. What are pointer constants?
17. What is a pointer variable? How to initialize a pointer variable? How to
access the value of a variable using pointer?
18. Defme the following: pointer, pointer value, pointer constant, pointer variable
19. What are the steps to be followed while using pointers?
20. How to declare pointer variables?
21. What is a dangling pointer?
22. What is a NULL pointer? What is its use?
23. How pointers can be passed as arguments to the functions?
24. What are the different ways of passing parameters to the functions?
25. What is pass by value? Explain with example
26. What is pass by reference? Explain with example.
27. What are the differences between pass by value and pass by reference?
2.112 Q Pointers
Here, the sequence of characters enclosed within a pair of double quotes (shown in
red color) is a string. Strings are generally used to store and manipulate data in text
form like words or sentences. Before proceeding further, let us see "What is a
string? How strings are represented?"
Storage
represe~tation of
-C Fi~ed length string
~ Delimited string
3.2 .I;l Strings
Now, let us see "What are the disadvantages of fixed length strings?" The various
drawbacks of this format are shown below:
• If the length reserved for string is too small, it is not possible to store the larger
data.
• If the length reserved for string is too large, too much memory is wasted (see
the above figures).
• Once the string is defined, the length of a string cannot be changed. It is
"fixed" for the duration of program execution.
• The problem of distinguishing data from nondata. Normally, nondata
characters such as spaces are added at the end of the data
The above disadvantages can be overcome using variable length format.
Definition: A length controlled string is a string whose length is stored as part of the
string itself. This technique uses a count that specifies string length. The count is
normally stored as the first byte followed by the' string. This count is used by the
string manipulation functions to determine the actual length of the data.
For example, the strings "RAMA"and "KRISHNA" are stored using length-
controlled format as shown below:
string
___
----A'----....,
4 A
5 bytes
string
string
length ~
r--"--o. r ------- .
Note: The first byte contains string length. Since its length is 1 byte (8 bits) we can
have a string length whose range is from 0 to 255. So, the string length should not
exceed 255 characters. This disadvantage can be overcome using delimited string.
As we use various delimiters such as semicolon, colon, comma, dot/period (denoted
by.), in a sentence in English, the string also can have delimiters.
3.4 Q Strings
Definition: In a variable-length string, the string ends with a delimiter NULL( denoted
by \0) character. This string which ends with a delimiter denoting the end of the string
is called a delimited string.
For example, the strings "RAMA"and "KRISHNA" are stored using a
delimiters as shown below:
string
r__
------A ---
5 bytes
. string delimiter
r------~ ------""~
~
3.2 C Strings
l
Now, let us see "What is a string in C language?"
Now, let us see, "How the strings are stored in memory?" A string is stored as a
sequence of characters in an array terminated by \0. For example, consider the string:
"VIVEKANANDA"
l;~l_t---~----t
~------~~~--~
__
'~SE~L~]Ql
_~~~?TA"[l]
"SEETA"[2]
E
E
S
"RAMA"[3] A "SEETA"[3] T
"RAMA"[ 4] \0 "SEET A"[ 4] A
L---------_.
"SEET A" [5] \0
3.2.2 Referencing string literal
Now, let us see "How to reference string literals?" As array of integers are stored in
contiguous memory locations, it is clear from the above figures that a string literal is
also stored in memory contiguously one after the other. So, each character of the
literal can be accessed using indexing (array or pointer concept can be used). For
example, consider the following program:
3.6 Q Strings
#include <stdio.h>
Note: Since a string is an array of characters, a string can be accessed one character at
a time using the index as shown in example 3.2.1. or the entire string can be treated as
one unit and can be printed as shown in example 3.2.2.
Definition: There is no separate data type for strings in C. They are treated as arrays
of type char. So, a variable which is used to store an array of characters i.s called a
3.8 Q Strings
string variable. As the numbers can be stored and manipulated using variables, the
strings can also be declared, initialized and manipulated using arrays. Consider the
following example:
char a[80];
Here, the string variable a can hold maximum of 80 characters including NULL (\0)
character.
char a[80];
Using this declaration the compiler allocates 80 memory locations for the variable a
ranging from 0 to 79 as shown below:
a
78 79
Since the size of a character is 1 byte, each character in the array occupies I byte.
The array thus declared can hold maximum of 80 characters including NULL
character.
Note:During declaration, we must provide enough storage for the data and the
delimiter. The storage space must be 1 byte larger than the maximum data size so
as to accommodate \0 at the end of the string.
char a[lO];
U sing the above declaration, 10 bytes are allocated. If we use the statement:
strcpy( a, "HELLO");
Q Systematic Approach to Data Structures using C 3.9
the string "HELLO" is copied into the variable a as shown below:
,,6 8 9,/
y
junk characters
Now, let us see "What is initialization? What are the various ways of initializing
strings?" Initialization is the process of assigning values to a variable before doing
manipulation. The initialization can be done in various ways.
• Initializing locations character by character
• Partial array initialization
• Initialization without specifying the size
• Array initialization with a string
• Using character pointer
char b[9] = {'C', '0' , 'M' , 'P', 'U' , 'T', 'E', 'R'}·,
The compiler allocates 9 memory locations ranging from 0 to 9 and these locations
are initialized with the characters in the order specified. The remaining locations
are automatically initialized to null-characters as shown below:
3.10 ~ Strings
char b[] = {'C', '0' , 'M', 'P', 'U', 'T', 'E', 'R'}',
For this declaration, the compiler will set the array size to the total number
of initial values i.e., 8. The characters will be stored in these memory locations in
the order specified as shown below:
Note: sizeof(b) will be 8. Note that \0 is not included at the end of the string.
Here, the string length is 8 bytes. But, string size is 9 bytes. So, the compiler
reserves 8+ 1 memory locations and 'these locations are initialized with the
characters in the order specified. The string is terminated by \0 by the compiler.
The array b is initialized as shown below. .
7
~
Even though the string "COMPUTER" contains 8 characters, because it is a string,
it always ends with null character. So, the array size is 9 bytes (i.e., string length +
1 byte for null character).
Now, let us look at the various ways of initialization and generally made mistakes
during initialization:
I
char b[9] = "COMPUTER"; Correct
char b[8] = "COMPUTER"; Wrong. This is because, string length is 8. So,
array size should be 8+1 which is 9. But, 8 has
been specified as the size of the array.
char b[8] = 'computer'; Wrong. The string should be enclosed between
two double quotes
char b[lO]= {'R',A','M','A'}; Correct. This is partial initialization
char b[8] = COMPUTER; Wrong. String should be enclosed within two
double quotes
-,....
3.12 ~ Strings
Since, string is an array of characters, the name of the string is a pointer constant. The
constants can not be used on the left hand side of assignment operator. For example,
Example 3.3.3.1: Copy a string from one memory location to other memory location
Note: If we want string str1 to copy into str2 then it can be done using two different
ways (discussed in section 3.6.2)
~ • Using string function strcpy(str2, str1) as shown in example 3.6.2.1
• By copying the individual items of str1 to str2 as shown in example 3.6.2.2
The strings can be read from the keyboard and can be displayed onto the monitor
using various functions. "What are various I/O functions used in case of strings?"
The various input and output functions that are associated with strings can be
classified as shown below:
Earlier we have used scanf() to read integer, floating point numbers etc. Now, let
us see "How a string can be read using scanfi)?" The string can be read using the
following conversion codes:
Conversion codes
of strings
-C String conversion code %s
Now, let us see "What is the role of string conversion code %s while reading the
strings?" The %s conversion code does the following things in sequence:
• If white spaces are present before the string, then they are removed from
the input stream (Note: A white space can be blank char cter, new line
character \n, tab \t etc.)
• All nonwhite space characters are copied into the array till it finds another
white space. Then the copied string is terminated by '\0'. But, the white
space and characters following are left in the input stream.
char sl[15];
scanf("%s", sl); 1* Note: We should not use & operator while reading
string. Specifying &s 1 is an error *1
Solution: The sequence of operations that are carried out when above string is read
using %s conversion code are shown below:
3.14 Q Strings
c--
&
&
& D is a blank character and represent white space. All white space
>- characters are ignored and will not be copied into variable sl.
~ D
D
~
"\
R These characters and are copied into variable one after and string sl
A can be represented as shown below:
~
f----
A
IR f A I M I A I \0 I? I? I? I? I? I? I? I? I? I.? I
D .~ iD is converted into \0 and stored in sl
K
R
I
S } These characters remain in the input stream and will not be copied into
H sl
N
A
.J .I
Input stream
Nete: Observe that after copying "RAMA", all. other subsequent characters are still
in the input stream. All these characters from the input stream can be removed using
the function fflush(stdin). The complete program segment is shown below: .
char sl[15];
scan f("%s", s 1); 1* Read non white space characters *1
fflush(stdin); 1* Remove the remaining characters from
the input stream *1
Example 3.4.2.1.1: What strings are available in variables sl and s2 if the following
statements are executed?
char sl[15];
char s2[1 0];
scanf("%s", s 1);
scanf("%s",s2);
if the input through the keyboard is:
@@8@@@RAMADKRISHNA.J
Q Systematic Approach to Data Structures using C 3.15
Solution: The sequence of operations that are carried out when above string is read
are shown below:
R }r-T__hTes_e~Ch~a_r_a~ct_e_rs~ar_e-r-co_p_iTe_d_i~nt~o
can be represented as shown below: __va~r_ia_b~1_~_s_1r-0_n_eT"""a_ft~er~a_nd~s_tr
.
M: s1
K
R
I
S s2
H
N
A
.J
void maim)
{
char sl [15];
char s2[15];
scanf("%4s", sl);
scanf("%s", s2);
printf("%s\n",sl );
printf("%s\n",s2);
}
3.16 Q Strings
Solution:
Step 1:
Let us see "What is the significance of %4s in scanf("%4s", sl)?" Here, the
field width specification %4s is used to protect the user entering too much data
for the first string sl. The scan! reads 4 characters from the keyboard and
insert \0 at the end. Now, if the user accidentally enters more than 4 characters,
the extra characters are ignored by this statement but, will be left in the input
stream. For example,
Input
RAM~SHNAPARAMAHAMSA
PROGRAM TRACING
void rnaint)
{
char sl[5];
char s2[15];
Input
scanf("%4s", s 1); RAMAKRISHNA PARAMAHAMSA
sl = "RAMA"
scanf("%s", s2); s2 = "KRISHNA"
Output
printf("%s\n",sl ); RAMA
printf("%s\n",s2); KRISHNA
}
~ Systematic Approach to Data Structures using C 3.,17
Suppose, we want only first name in s1 and we do not want the remaining characters
of first string to be copied into string s2. In such case, it is required to remove the
extra characters entered for s1 from the input stream. This can be achieved using the
following code:
PROGRAM TRACI G
#include <stdio.h>
void maim)
{
char sl[5];
char s2[15];
1* Accept 4 characters *1 \
scanf("%4s", sl); Input
RAMAKRISHNA PARAMAHAMSA
s1 = "RAMA"
1* Remove rest of s 1
characters from input *1
fflush(stdin); KRISHNA PARAMAHAMSA
is removed from the input stream
Step 1: scanf("%4s", s1): The first four characters namely "RAMA" ending with \0 is
copied into s1. So, s1 = "RAMA"
Step 2: fflush(stdin) removes all characters following "RAMA" i.e., the string
"KRISHNA PARAMAHAMSA" is removed from the input stream
ow, the question is "What is the disadvantage of scanf("%s", str)?" If the variable
str is declared as array of characters, a string without white spaces should be entered
i.e., it is not possible to store a string having white spaces such as \t and blank
characters. For example,
If the input entered through keyboard is "RAMA KRISHNA
PARAMAHAMSA" then only "RAMA" is stored in str. The remaining characters
are left unused in the input stream. If we want the entire string to be stored in str, it is
not possible using %s. This disadvantage we can over come using C format
specification known as edit set conversion code %[ .... J discussed in next section.
Advantage Using edit set conversion code, it is possible to enter a line of text with
white spaces such as blank space, \t etc. For example, the text:
"Hello! How you feel about the book?"
can be read into a variable.
Now, let us see "What is the syntax of edit set conversion code?" The syntax is shown
below:
scanf( % [ J, str)
~
edit characters
where
edit characters represent the valid characters that are allowed in the string
and should be enclosed within' [' and ']'
ow, let us see "How edit set conversion code helps in reading the string?" The string
is read as shown below:
• Each character read by scanf is compared with edit characters.
• If the 'character read is in edit character set, it is copied into string str.
• The above procedure is repeated as long as the character read is In edit
character set.
• If the character read does not match with edit character set, the reading process
is stopped.
• The characters following the no matched character remain in the input stream.
Note: If the first character read is not in edit characters, the scanf is terminated and a
\0 is copied into str indicating it is a null string.
~ Systematic Approach to Data Structures using C 3.19
Note: The reading process can also be stopped with the following terminating
conditions:
• If end-of- file is detected the reading stops.
• If the field-width is specified and maximum number of characters have been
read, then also reading stops.
Now, the question is "How to read all the characters except \n using scanfi)?" This
can be done by including all the characters within square brackets as shown below:
or
Observe that it is difficult to include all characters except \n using the first scanf
Instead, it is easier to follow the second syntax including an invalid character
preceded by caret (").
Solution: The scanf reads all the characters until the user types enter key (which
represent \n character). Once the user types enter key, all the characters typed except
\n are stored in str. The field width 81 is used to read and store first 81 characters.
The rest of the characters are not stored in str but are left in input stream. To delete
all these extra characters from the input stream, we use rtlush(stdin).
, Now, let us see "What are the various options that can be used with printfl" The
various options associated with printf are shown below:
Various options
used in printf
-E Filed width specification
Precision specifier
Left justification
3.4.2.1 Field with specification
The syntax to use field width specification in printf statement is shown below:
%ws
3.20 ,!;1,Strings
where
• w is the field with specified (number of columns used for printing string)
• s indicates that the string is being printed.
Tote: If the string to be printed is larger than the field width w, the entire string will
be printed.
ote: If the string to be printed is smaller than the field width w, then appropriate
number of blank spaces are padded at the beginning of the string so as to ensure that
field width w is reached.
void maini)
{
char s[] = "RAMANANDA";
printf("%4s\n", s);
printf("% 15s\n" ,s);
}
Solution: The tracing of each statement along with output is shown below:
.l
PROGRAM TRACING-
#include <stdio.h>
void maint)
{
char s[] = "RAMANANDA";
Output
printf("%4s\n", s); RAM A NAN 0 A
Note: Since the string is larger than the field
width 4, entire string is printed
void mainO
{
char str[] = "RAMANANDA";
printf("%O.2s\n", str);
printf("%9.4s\n",str);
printf("%9.3s\n", str);
printf("%3.5s\n",str);
}
Solutions The tracing of each statement along with output is shown below:
PROGRAM TRACING
#include <stdio.h>
void mainO
{
char str[] = "RAMANANDA";
printf("%O.2s\n", str);
printf("%9.4s\n",str);
printf("%3.5sn\",str);
3.22 ~ Strings
The syntax "to use field width specification in printf statement is shown below:
%-w.ns
where
• - just before w indicates that string is printed using left justification.
• w is the field with specified (number of columns used for printing string)
• s indicates that the string is being printed.
void maint)
{
char str[] = "RAMANANDA";
printf("%O.2s\n", str);
printf("%9.4s\n" ,str);
printf("%9.3s\n", str);
printf("%3.Ss\n",str);
}
Solution: The output remains same as the previous problem except that the string is
printed in left justified manner as shown below:
#include <stdio.h>
void maint)
{
char s[] = "RAMANANDA";
,
Q Systematic Approach to Data Structures usi
Note: The newline(\n) character in the input indicates end of string. But, it j
by null(\O) character in the memory by the function getst).
Example 3.4.3.1: Program to convert all lower case letters to upper case letters.
PROGRAM TRACING
#include <stdio.h>
#include <ctype.h>
void maint)
{
char text[80];
int 1;
Input
printf("Enter the text\n"); Enter the text
gets(text); Hello How are you
/* convert the text into upper case */
for ( i = 0; text[i] l= '\0'; i++)
{
1
converted to uppercase
Note· Each character in the array text is accessed, converted into upper case using the
built in function touppert). Since, its prototype declaration is available in a header
file "ctype.h", this header file is included.
Example 3.5.1: Give the memory representation for the following initialization
statement:
char a[5] [11] = {
"DHARMARA YA",
"BHIMA",
"ARJUNA",
"NAKULA",
"SAHADEV A"
};
Solution: From the declaration, it is clear that a is a 2-dimensional array with 5 rows
and 15 columns. These five memory locations are initialized with values terminated
by \0 as shown below:
o 1 2 3 4 5 6 7 8 9 10
D H. A R M A R A Y A \0
B H I M A \0
A R J U N A \0
a~~ N A K U L A \0
4 S A H A D E V A \0
t
In general, using a[i] for i = 0 to 4, we can access each name.
Note: It is clear from the above figure that each name can be accessed using array a
along with index values 0, 1, 2, 3 and 4. Thus using a[O], a[l], a[2], a[3] and a[4] we
can access all the names. The array elements can be printed as shown below:
Observe that an array a has 5 memory locations each of which points to a name. The
memory representation is shown below:
1
o 2 3 4 5 6 7 8 9 10
a[O]
- D H A R M A R A IY IA I \0 I
a[l]
- B H I M A \0
a[2]
- A R J U N A \0
a[3]
- - N A K U L A \0
a[4]
- - S A H A D E V A I \0 I
-
Observe from this memory representation that there is no wastage of memory.
3.6 String" handling functions
Now, let us see "What is the need for string handling functions" String is not a
standard type, but it can be treated as a derived data type. So, we can not manipulate
the strings directly using C operators. For this reason, C provides a rich set of string
handling functions similar to mathematical functions such as sqrtt), powt), sinet) etc.
Now, let us see "What are string handling functions in C?" The vanous string
handling functions that are supported in C language are shown below:
Q Systematic Approach to Data Structures using C 3.27
Note:All these functions can be accessed by including the header file string.h in the
beginning of the program. Before using these functions, the programmer should
ensure that the string is terminated by null character '\0'. Otherwise, the result will be
unpredictable and we get wrong results. The descriptions of some of these functions
are provided in the next section.
Let us see "What is the role of the function strlen( str)?" This function returns the
length of the string str i.e., It counts all the characters up to '\0' but, except '\0'. So,
an empty string has length zero. The program to show its usage is shown below:
3.28 Q Strings
Now, let us see "How to implement strlent) function?" Consider the string "SSVVIT"
str
To start with, the index variable i is zero. Now, keep incrementing the index variable i
as long as str[i] is not equal to '\0' using the statement:
i = 0;
while (str[i] != '\0')
i++;
Once control comes out the loop, the index variable i gives the length of the string.
The complete C function is shown 15elow:
The complete program which reads the string and compute the length using user-
defined function is shown below:
#include <stdio.h>
PROGRAM TRACING
void maint) I
{ I
char str[20];
int 1;
I
I Input
printf("Enter the string\n"); I Enter the string
gets(str); I Rama
i= my_strlen(str) ;
I i= 4
I
I Output
printf("Length = %d\n", i); I Length = 4
I
}
First, let us see "What is the prototype of the function strcpy?" The prototype of
strcpy defined in header file "string.h" is shown below:
Now, we shall see "What is the role of the function strcpy?" The function strpcy
copies the contents of source string src to destination string dest including '\0'. So,
the size of destination string dest should be greater or equal to the size of source
string src. After copying, the original contents of destination. string dest are lost. The
program to show its usage is shown below:
3.30 Q Strings
Design: Copy each character of source string src to destination string dest as long as
chara ter is not '\0'. This is pictorially represented as shown below:
dest ~ src
SIT ~ I
o dest[O] = src[O]
1 dest[l] = src[l]
2 dest[2] = src[2]
3 dest[3] = src[3]
4 dest[4] = src[4]
5 dest[5] = src[5]
6 t t
In general, dest[i] = src[i]
where initial value of i = 0
Observe from above figure that as long as src[i] != '\0' it is required to copy src[i] to
dest[i] and increment i after copying each character. The partial code can be written
as shown below:
~ Systematic Approach to Data Structures using C 3.31
Note: It is the responsibility of the programmer to attach null character '\0' at the end
of the string. The equivalent code for this is shown below:
So, the final function to copy the contents of source string src to destination string
dest using arrays as well as using pointers is shown below:
Example 3.6.2.2: Function to copy string src to string dest using 3 m~thods.
Note: Observe the null statement ";" in the third version of my_strcpy. It does nothing.
The condition in the while loop i.e., *dest++ = *src++ is repeatedly executed and
each character of the source is copied into destination including \0. Once \0 is reached,
the condition fails and control comes out of the loop.
3.32 Q Strings
The complete program which uses the user defined function is shown below:
Now, let us take some examples and see how strcpy works.
strcpy(sl, s3);
Solution: The memory representation for the strings s1, s2 and s3 before executing
strcpyi) is shown below:
,;:::,--K~~~~y-~::=~==~=~=--:7
s1 s2 s3
ow, let us execute the statement:
strcpy(sl, s3);
Q Systematic Approach to Data Structures using C 3.33
After executing, the memory organization for the strings sl, s2 and s3 is shown
below:
Copy from s3 to sl
r 'r------ ------
~K~~ __ ~~~~~~
sl s2 s3
Observe that the string s2 has the value "NA" and original contents of s2 are
destroyed after strcpyt).
Note: For correct operation of strcpyt), the destination .string sl should be greater
than or equal to source string s3. But, this is not so in this example. The size of sl is
too small to hold the string of s3. Here, the contents of s3 including \0 is accessed and
will be copied into string sl. Since there is no space is sl to hold the data, the
remaining data of s3 is copied to string s2 also. This destroys the contents of string s2.
First, let us see "What is the prototype of the function strncpy'l" The prototype of
strncpy defined in header file "string.h" is shown below:
where
• dest is the destination string
• src is the source string
• n is the number of bytes to be copied into destination string
Let us see "How does the function stmcpy work?" The basic functionality of this
function is to copy n characters from source string to the destination string. Based on
the value of n, the copying activity is performed as shown below:
• If source string is less than n, the entire string is copied from source to
destination and then null characters are placed in the destination string until n
characters have been copied.
• If the source string src is longer than n characters, copying stops after n
characters have been copied. Then, the destination string will not have a
delimiter \0 and hence it will be invalid string.
3.34 Q Strings
Example 3.6.3.1: Show the memory representation before and after executing
strncpy in the following program segment:
char sl[10];
char s2[8] = "RAMA";
strncpy(sl, s2, sizeof(sl));
Solution: The memory representation for the variables sl and s2 before executing
strncpy is shown below:
Example 3.6.3.2: Show the memory representation before and after executing
strncpy in the following program segment:
char sl[5] = "RAMA";
~ char s2[5] = "VEER"
char s3[1 0] = "BHISHMA";
Solution: The memory representation for the variables sl, s2 and s3 before executing
strncpy is shown below:
r.---- sl,A•..••
-- •..••.
"
s2
r~---A---..." s3
~_--------"--------_"'
8 9
After strncpy(sl, s3, 5);
~ Systematic Approach to Data Structures using C 3.35
Note that sl is invalid string since it has no delimiter \0. If we print sl, s2 and s3 the
following output is obtained: .
s 1 = BHISHVEER (Invalid string s 1: It is not ending with \0)
s2 = VEER .
s3 =BHISHMA
Note: Care should be taken so that the string sl (destination string) is sufficiently
larger.
First, let us see "What is the prototype of the function strcat? and how does it work?"
The prototype of strcat defined in header file "string.h" is shown below:
789 5 6 7
Note: Length of resultant string = length of string sl + length of string s2 and the
copied string in the destination string is shown in boldface
Now, let us see "How to implement strcau) function?" Let us design our own
function strcat and call this function as my_strcat.
Design: This is very simple and straightforward approach. Given two strings sl and
s2 perform the following activities:
3.36 .Q Strings
Note: The strcat using pointers can be written more efficiently and in very compact
manner as shown below: -,
~ Systematic Approach to Data Structures using C 3.37
"The complete program which uses the my_strcat is shown in example 3.6.4.2.
#include <stdio.h>
First, let us see "What is the prototype of the function strncat? and how does it
work?" The prototype of strncat defmed in header file "string.h" is shown below:
Working: If the length of string s2 is less than n, the function copies all characters of
string s2 to the end of string sl. The delimiter of string sl is replaced by the first
character of s2. The working is exactly same as strcat shown in previous section. But,
if the length of string s2 is grater than n:then first n characters of string s2 are copied
to the end of sl attaching \0 at the end. For example, the memory representation for
the variables sl and s2 before executing strncat is shown below:
First, let us see "What is the prototype of the function strcmp? and how does it
work?" The prototype of strcmp defined in header file "string.h" is shown below:
Working: This function is used to compare two strings. The comparison starts with
first character of each string. The comparison continues till the corresponding
characters differ or until the end of the character is reached. The following values are
returned after comparison:
• If the two strings are equal, the function returns 0
• If sl is greater than s2, a positive value is returned
• If sl is less than s2, the function returns a negative value.
Now, let us see "How to implement strcmp without using built in function?"
Design: Given the two strings sl and s2, the comparison starts with first character of
each string and continues as long as they are equal. During this process, both the
pointers sl and s2 are incremented to point to the next character and if end of the
character is encountered, with respect to sl, control comes out of the loop. The
equivalent statement for this can be written as shown below:
Q Systematic Approach to Data Structures using C 3.39
The control comes out of the loop if corresponding two characters differ or null
character \0 is encountered. Since the characters are represented by numerical values
(ASCII), let us take the difference of corresponding characters which results in zero,
positive or negative numbers using the follo:ving statement:
Now, "How to use the above value in the calling function?" The function my_strcat
should be called as shown below:
3.40 Q Strings
The complete program showing the usage of user defined function my_strcmp is
shown below:
if (difference = 0)
printf("String sl = string s2\n");
else if (difference >0)
printf("String sl > string s2\n");
else
printf("String sl < string s2\n");
}
s1 R =~ R s2
sl A =~ A s2
sl M =~ M s2
sl A =~ A s2
r sl \0 =~ \0
Note: By executing sl ++ and s2++ we can access subsequent characters and compare
the two strings. But, once \0 is encountered in sl, control comes out of the loop.
*sl - *s2 = \0 - \0 = 0 and hence two strings are equal.
Q Systematic Approach to Data Structures using C 3.41
I---
s1;t=~R
s1 A =~ A s2 s2
>-=-=-
s1 M =~ M s2
r s1 \0 f.~X
~
s2
Note: By executing sl++ and s2++ we can access subsequent characters and compare
the two strings. Control comes out of the loop since '\0' is not equal to 'A'. So,
Example 3.6.6.5: What would be printed from the following program block?
char sl [5] ::;:"xyzt";
char *s2 = "xyAt";
int dif;
dif= strcmp (sl, s2);
p rintfC"%d\n" ,dif);
Solution: The two strings s1and s2 are compared as shown below:
=~ x s2
=~ s2
f.~
r
A s2
t
\0
Note: Comparison stops at this point since 'z' is not equal to 'A' and control comes
out of the loop. So, diff'= *sl - *s2
='z'-'A'
= 122 - 65 = 57
Output: 57 which is positive and indicates s1 is greater than s2.
3.42 ~ Strings
First, let us see "What is the prototype of the function strncmp'l and how does it
work?" The prototype of strncmp defined in header file "string.h" is shown below:
where
• sl is the first string
• s2 is the second string
• n is the number of characters to be compared
The following table shows the results for strncmp(sl, s2, n) with different values of
sl, s2 and n.
First, let us see "What is the prototype of the function strchr? and how does it work?"
The prototype of stchr defined in header file "string.h" is shown below:
where
• s is the first stririg
• ch is character to be searched for
Working: This function searches for the first occurrence from the beginning of the
string and following values are returns:
• On success, a pointer to the character is returned.
• On failure, NULL is returned
Example 3.6.8.1: Let s = "MONALIKA". What is the output obtained after executing
each of the statements one after the other?
p = strchr(s, 'A');
q = strchr(s, '\0');
r = strchr(s+5, 'A');
Solution: The memory representation and the pointer p after executing the statement:
p = strchr(s, 'A');
p
Note: Here, 'A' is the character to be searched and p points to the first occurrence of
'A'. Also, P - s = 3 gives the position of character 'A' in the string "MONALIKA"
The memory representation and the pointer q after executing the statement:
q = strchr(s, '\0');
Note: Here, '\0' is the character to be searched and q points to the first occurrence of
'\0'. Also, q - s = 9 gives the position of character '\0' in the string "MONALIKA"
3.44 Q Strings
The memory representation and the pointer r after executing the statement:
r = strchr(s+5, 'A');
Note: Here, 'A' is the character to be searched from s+5 onwards and r points to the
first occurrence of 'A' from s+5. Also r-s = 7 gives the position of character 'A' in
the string.
Example 3.6.9.1: Let s = "MONALIKA". What is the output obtained after executing
each of the statements one after the other?
p = strrchr(s, 'A');
Solution: The memory representation and the pointer p after executing the statement'
p = strrchr(s, 'A');
p
~ Systematic Approach to Data Structures using C 3.45
ote: Here, 'A' is the character to be searched and p points to the first occurrence of
'A' in the string "MONALIKA" from the end, Also, p - s = 7 gives the position of
character 'A' in the string "MONALIKA"
Example 3.6.9.2: What would be printed from the following program block?
char s 1[50] = "xyzt";
char *s2 = "uabefgnpanm";
char *s3;
char *s4;
char *s5;
char *s6;
s3 = sl;
s4 = s2;
strcat (s1, s2);
s5 = strchr(sl, 'y');
s6 = strrchr(s2, On');
printf("%s\n", s3);
printf("%s\n", s4);
printf("%s\n", s5);
printf("%s\n", s6);
Solution: The memory representation for the various variables when the program is
being executed is shown below:
sl
char sl[50] = xyzt"; s3[IJ ~
char *s2 = uabefgnpanm"; sS[IJ
char *s3'
char *s4~ s4 [IJ
char *s5; s6 [IJ
char *s6;
1* After executing *1 s3
s3 = sl;
s4 = s2; ss[IJ
s4
s6 [IJ
-,
3.46 Q Strings
/*After executing */
strcat (sl, s2); s3
/* After executing */
s5 = strehr'(s l , 'y');
s6 = strrchr(s2, On');
s3
s5
s2
S4G-+~
s6 [:J
Output
printf("%s\n", s3); xyztuabefgnpanrn
printf("%s\n", s4); uabefgnpanrn
printf("%s\n", s5); yztuabefgnpanrn
printf("%s\n", s6); nrn
Solution: The memory representation and the pointer p after executing the statement:
p = strrchr(s, 'ALlKA');
o 1 2 345 6. 7 8
slMlolNIAILIIIKIAlwl
i
p
Note: Here, p-s = 3 gives the position of the substring "ALIKA" in the string
"MONALIKA"
Example 3.6.10.2: What would be printed from the following program block?
char sl[50] = "uabefgnpanm";
char *s2 = "ab";
char *s3 = "pan";
char *s4 = "bef';
char *s5 = "panam"
char *s6;
char *s7;
char *s8;
char *s9'
, ,
s6 = strstr(sl, s2);
s7 = strstr(sl, s3);
s8 = strstr(sl, s4);
s9 = strstr(sl, s5); .
printf("%s\n", s6);
printf("%s\n", s7);
printf("%s\n", s8);
printf("%s\n", s9);
3.48 Q Strings
Solution: The memory representation for the various variables when the program i
being executed is shown below:
s6
s7 = strstr(sl, s3);
s7 s3~~
s8 s4~~
s9 = strstr(sl, s5);
s9B-+INULL I ~~
s5
ote: Since string panam is not present in string sl, the function returns NULL
Q Systematic Approach to Data Structures using C 3.49
Output
printf("%s\n", 86); abefgnpanrn
printf("%s\n", s7); panrn
printf("%s\n", s8); befgnpanrn
printf("%s\n", s9); (null)
Example 3.6.11.1: Let strl = "BHISHMACHARI" and str2 = "IHSBM" What is the
output if the following statement is executed:
Solution: The memory representation for these two strings is shown below:
strl str2
o 1 2 3 4 5 6 7 8 9 10 11 12 o 1 2 3 4 5
IBIHIIISIHIMIAlclHIAIRlllwl ~
y
First, let us see "What is the prototype of the function strcspn'l and how does it
work?" The prototype of strcspn defined in header file "string.h" is shown below:
Solution: The memory representation for these two strings is shown below:
Example 3.6.12.2: Let str1 = "BHISHMACHARI" and str2 = "GOD". What is the
output if the following statement is executed:
len = strcspn (str l, str2);
Solution: The memory representation for these two strings is shown below:
strl str2
o 1 2 3 4 5 6 7·8 9 10 1112 o 1 2 3
~
Example 3.6.12.3: What would be printed from the following program block?
char *sl = "abefgnpanm";
char *s2 = "ab";
char *s3 = "pan";
char *s4 = "bef';
char *s5 = "panarn";
int dl;
int d2;
int d3;
int d4;
dl = strspn(sl, s2);
d2 = strspn(sl, s3);
d3 = strcspn (sl, s4);
d4 = strcspn (sl, s5);
printf("%d %d %d %d\n", dl, d2, d3, d4);
Solution: The memory representation for the various variables when the program is
being executed is shown below:
*s3 = "pan";
char
char *s4 = "bef';
s3G---+~
char *s5 = "panam"; s4G---+~
int dl; s5G---+~
int d2;
int d3;
int d4; s2
Available in s2
G---+~
r-"-..
dl = strspn(sl, s2);
First, let us see "What is the prototype of the function strpbrk? and how does it
work?" The prototype of strpbrk defined in header file "string.h" is shown below:
Working: This is exactly similar to strcspn. This function keeps comparing each
character of string s1 in order from left to right with contents of string s2. As long as
the corresponding character of s1 is not available in s2, the search continues with next
character. The search stops when a character in s1 match with any of the characters in
s2. The function returns the following values:
• returns pointer to the first matched character
• return NULL - if no characters in s1 match with any of the characters of s2.
Solution: The memory representation for these two strings is shown below:
Memory formattmg
functions in C
.-c sscanf
sprintf
Let us see "What is the need for the function sscanfl" We know that we can read
integers, floating point numbers and strings from the keyboard. Instead of reading
these data from the keyboard, we can also read from a string stored in memory. For
this purpose, we use sscanf
Before studying various details, let us see "What is the prototype of the
function sscanf and how does it work?" The prototype of sscanf defined in header
file "string.h" is shown below:
convert into copy
appropriate converted
input type data
~r A ,~
Working: The function is same as the scanf function except that data is read from
memory instead of from the keyboard. The fiiiiction sscanf accepts the input from the
string in memory and based on the format string, the input data is converted into
appropriate data type and will be copied into respective variables as shown below:
sscanf
string
Ex: str = "Krishna 900 98.99"
n n n n/ name = Krishna
variables ~ total_marks = 900
average = 98:99
Q Systematic Approach to Data Structures using C 3.55
The program that displays the name, total_marks and average value obtained from
the string str is shown below: .
sscanf (str,
"%s %d %f', Convert the string based on
name, &total_marks, &average format specifiers and copy
); into variables.
name = "KRISHNA"
total marks = 90
average = 98.99 -
Output
printf ("Name = %s\n",name); Name = "KRISHNA"
printf("Total marks = %d\n", total_marks); Total marks = 90
printf("Average = %f\n", average); Average = 98.99
3.56 Q Strings
Note: Except the first parameter, the rest of the syntax is same as the syntax used to
read a string, an integer and a float value from the keyboard. With the presence of the
first parameter str, the data is read from str instead of from the keyboard.
Let us see "What is the need for the function sprintfl" We know that we can display
integers, floating point numbers and strings on the display unit. Instead of displaying
on the display unit, we can also write the same data into a string in the memory. For
this purpose, we use sprintf.
Before studying various details, let us see "What is the prototype of the
function sprintf and how does it work?" The prototype of sprintf defined in header
file "string.h" is shown below:
convert from
appropriate
output typeto string input
,.---A--,. r A , ,--A----..
int sprintf(char *str, "format string", variables);
where
• variables It is a list of variables from which the data is read from the function
sprintf.
• format string consists of control string that is used in printf. For example,
.1 %d, %f, %s etc.
• str is the destination string. The various types of data are converted into string
format and stored in str. The string may be written into a file or can be printed
on the screen using puts function.
'Working: The function is same as the printffunction except that data is written into a
string instead on the display unit. The function sprintf accepts various data types from
the list of variables, and based on the format string, the data is converted into string
and will be stored in str as shown below:
sprintf
string
Ex: str = "Krishna 900 98.99"
nnnn name = Krishna
variables ~ total_marks ~ 900
average = 98.99
~ Systematic Approach to Data Structures using C 3.57
char str[30];
char name[lO] = "KRISHNA";
int total_marks = 900;
float average = 98.99;
The program that displays the name, total_marks and average value represented in
the form of string str is shown below:
#include <stdio.h>
Output
printf ("string = %s\n", str); string = KRISHNA 90 98.99
}
3.58 ~ Strings
First, let us see "What is the prototype of the function strrev? and how does it work?"
The prototype of strrev defined in header file "string.h" is shown below:
where str is the string to be reversed. This function reverses all characters in the
string str except the terminating null character '\0'. The function returns pointer tothe
reversed string. For example, consider the following program
Now, let us see "How to write a C function to check whether the string is a
palindrome or not?"
\
Design: Let us write a function is~ali which returns 1 if the string is palindrome or
zero if not a palindrome. If the given string and reversed string is same let us return1
Q Systematic Approach to Data Structures using C 3.59
indicating the string is a palindrome. Otherwise, we return 0 indicating the given
string is not a palindrome. So, first let us see "How to reverse the string?" Consider
the following string strl.
Now, access each item of strl from left to right and store in str2 from right to left as
shown below:
where n is the string length of given string. Thus, in general the string can be reversed .
using the following code:
Once the string is reversed, if strl is same as str2, the string is a palindrome.
Otherwise, the string is not a palindrome. Now, let us compare each character of strl
with each character of str2. During comparing if corresponding characters do not
match the string is not a palindrome. So, the function should return O. The complete
function is shown below:
· 3.60 Q Strings
n = strlen(str1);
for(i == 0; i < n; i++) 1* Reverse the given string *1
str2[n-l-i] = strl[i];
1* Compare the given string and reversed string */
for (i = 0; i < n; i++)
if (str1 [i] != str2[i]) /* If there is a mismatch */
return 0; /* String is not palindrome */
return 1; /* String is a palindrome */
}
Let us write a program to read a sentence and count the number. of vowels and
consonants. The solution to this problem is simple. Examine each character read,
check whether the character is an alphabet. If so, convert to lowercase and check
whether the character belongs to anyone of 'a', 'e', 'i', '0', 'u'. If so, increment
vowel_count else increment con_count. The corresponding C program is shown
below:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
ch = tolower(str[i]);
if (ch = 'a'[lch = 'e'llch = 'i'[lch = 'o'llch = 'u')
vowel_ count++; vowel count = 5
else ,/
}
}
Output
printf("No. of vowels = %d\n",vowel_count); No. of vowels = 5
printf("No. of consonants = %d\n" ,con_count); No. of consnants = 6
}
3.62 Q Strings
Let us write a program to read the names and sort them using bubble sort. The
function to sort the names using bubble sort is shown below:
The complete program to sort the names using the above function is shown below:
#include <stdio.h>
#include <string.h>
void mairu)
{
char a[ 10] [30];
int 1, n;
Q Systematic Approach to Data Structures using C 3.63
sort_names(a, n);
printf("Sorted Names\n");
for (i =' 0; i < n; i++)
printf("%s\n" ,a[i]);
}
Let us write a program to read a sentence and replace the lowercase letters by
uppercase and vice versa. The solution to this problem is simple. Examine each
character read, check whether the character is uppercase letter. If so, convert it into
lower case else convert into to uppercase. The complete C program is shown in
below:
Example 3.8.5.1: Program to convert lower to upper and upper to lower case letters
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void maim)
{
char str[30], dest[30];
int i;
else
dest[i] = toupper(dest[i]);
}
Exercises
27. How strings are processed in C? How are they declared and initialized? Explain
with example. (Jul/Aug 2004)
29. Write a program to convert all alphabets into upper case in a sentence
30. Write a program to count the number of vowels and consonants in a sentence.
31. Write a program to arrange names in ascending order.
32. Write a C function to count the number of vowels and consonants in a string
33. Write a C function to convert lower case to upper case and vice-versa
Chapter 4: Derived Types-enumerated,
Structure and union
I What are we studying in this chapter? I
• The type definition
• Enumerated types
• Structure
• Accessing structures
• Complex structures
• Array of structures
• Structures and functions
• Unions
• Bit-fields - 5 hours
4.1 Introduction
In this chapter let us discuss about the need for the various derived types such as
enumerated, structures andunions. First we shall see"What is a derived data type?"
Definition: The various primitive data types that are available in C language are int,
float, char, double and void. Using these primitive data types, we can derive some
other data types. Such data types that are derived from the basic data types are called
deriveddata types. The various derived data types are shown below:
Arrays
Pointers
Derived
data Enumerated
types
Structure
Union
Arrays and pointers have already been discussed in earlier chapters. The derived data
types are normally derived from basic data types.
4.2 ~ Derived types - enumerated, Structure and Union
Now, let us see "What is type definition?" or "What are user-defined data types?"
Definition: The typedefis a keyword that allows the programmer to create a new data
type name for an existing data type. So, the purpose of typdef is to redefine the name
of an existing variable type. The general syntax of the typedef is shown below:
keyword.J
Any existing C
r
typedef ~ata ytyPJt.new_name 1, new _name2,. :;'..
I·
New names for the existing data type
data type
Note that using typedef, we are not creating new data types. Instead, we are creating
only new name for the existing data type. These new data type names are called user-
defined data types. For example, consider the following type definition:
In the above example, MARKS and ID_NUMBER are the new data type names given
to the data type int and are called user-defined data types. Now, let us see "How to
deelare the variables using user-defined data types?"
Using the user-defined data types, the variables can be declared as shown below:
typedef int MARKS, ID_NUMBER;
MARKS sub1, subj2, subj3;
.ID_NUMBER sl, s2, s3;
Note: Normally, the user defined data types will be in uppercase. This alerts the
programmer that there is something unusual about the type:
First we shall see "What is the need for enumeration data types?" We know that
wordsspeak more than numbers (as pictures speak more than words). For example,
wemayuse integers 1, 2, 3, 12 to represent months for easy programming. It is
morereadable and understandable if we replace these numbers by some meaningful
anddescriptive names such as Jan, Feb, Mar, ..... Dec. This concept of replacing
integers by some meaningful and descriptive names gives rise to new data type called
enumerated data type. Now, let us see "What is enumerated data type? What is the
vntax of enumerated data type?"
Definition:An enumerated data type is a user defined data type which can take the
integervalues from a list. These integer values are replaced by meaningful and
descriptivenames so as to enhance the readability of the program. These descriptive
namesare called enumerators or enumerator constants. The syntax is shown below:
/{
enumerators
curly braces
"<, }; Note:
constants
Enumerators are· integer
but represented using
meaningful and descriptive names.
where
• enum is the keyword in C language
• enum tag_name together represent the user defined data type
• memberl, member2, are integer constants but represented using
descriptive names. These are called enumerator constants or enumerators. The
list of these enumerators is called enumerator list.
4.4 Q Derived es - enumerated, Structure and Union
Note: The members inside the braces are not variables; instead they are intege
constants but represented using descriptive names. Here, the compiler automaticallj
°
assigns integer value to member l. The subsequent members have the values 1,2, :
and so on.
Once we know how to define enumerated data type, the next question is "How tc
declare the variables?" The syntax of declaring the variables are shown below:
where
• enum ta~name is the user defined data type
• variables are list of identifiers separated by commas and terminated by
semicolon.
Example 4.3.1: Give the enumerated definition for the months of the year and declare
two variables ml and m2 using the enumerated data type.
Solution: The enumerated defmition for the months of the year along with the
declaration is shown below:
enum months
{
jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};
Instead of writing the definition and declaration in two separate lines, we could have
written as shown below:
enum months
{
jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
} mI, m2;
Note: In the above declaration, enum months is the data type. The compiler assigns
the values 0, 1, 2, 3 11 to the names j an, feb, mar, dee.
-
Example 4.3.2: Give the enumerated definition for the days of the week and declare
two variables dl and d2 using the enumerated data type.
Solution: The enumerated definition for the days of the week along with the
declaration is shown below:
Instead of writing the definition and declaration in two separate lines, we could have
written as shown below:
enum days
{
sun, mon, tue, wed, thu, fri, sat
} dl, d2;
Note:In the above declaration, enum days is the data type and the variables dl and
d2 are the variables of type enum days. The compiler assigns the values 0, 1,2, ... 6 to
thenames sun, mon, tue, ..... sat.
It is also possible to explicitly assign any value to enumerator. In such case, the
successive unassigned enumerators will take values one greater than the value of the
previous enumerator.
enum days {sun = 4, mon, tue, wed, thu=O, fri, sat} d1, d2;
Observethat the first enumerator sun = 4. So, for the subsequent enumerators the
compiler assigns the values 5, 6, 7 as shown below:
But, observe that the enumerator, thu is initialized to 0. So, the subsequent
enumerators will have the values 1, 2,,3 and so on. So, the final enumerators along
withtheir values are shown below:
sun = 4, mon = 5, tue = 6, wed = 7, thu = 0, fri = 1, sat = 2
Any manipulation done on integer variables can also be done on the variables dl and
d2. It is also possible to associate typdef with enumerated data type as shown below:
4.6 ~ Derived types - enumerated, Structure and Union
Example 4.3.4: Declare the variables dl and d2 using typedef for enumerated data
type by considering the example 4.3.2
typedef enum {sun = 4, mon, tue, wed, thu = 0, fri, sat } DAYS;
DAYS dl, d2;
Note: Using typedef we can write more readable code. In this example, we can say
the variables dl and d2 are of type DAYS.
We know that an array is a collection of similar data items such as integer, float, char
etc. If more number of data items of same data type are grouped, we normally use
arrays. For example, the marks of 5 students can be stored using array as shown
below:
marks[5] = {80, 90, 45, 99, 100};
he ordinary variables and arrays can handle variety of situations. But, in real world,
we often deal with entities that are collection of dissimilar data types. For example,
suppose we want to have the information related to a student. We may be interested
Ill:
Note that the above information is a collection of various items of dissimilar data
types. So, arrays can not be used (because, array is a collection of similar data types).
This is where the structures are used.
struct tag_name
/{ type 1 memberl;
type2 member2;
curly braces
where
• struct is the keyword which tells the compiler that a structure is being defined.
• memberl, member2,..... are called members of the structure. They are also
called fields of the structure.
• The members are declared within curly braces. The members can be any of the
data types such as int, char, float etc.
• There should be semicolon at the end of closing brace
Since the above information is related to student and all the above information is
logically related to a student, we can use the structure. The structure definition to hold
the information of student is shown below: (
struct student
The structure definition does not reserve any space in memory for the members. So, it
is called a structure template and memory will not be allocated for the template. This
is pictorially represented as shown below:
average_marks float
I 4 bytes
Note: The various items in a structure can be same or different data types. But, all the
items should be logically related. Do not combine un-related data. For example, for
the ab ve structure definition, let us have a field zebra. The field zebra is in no way
related to student information and hence has to be avoided.
Once we know how to define the structure, the next question is "How to declare a
structure?" As variables are defined before they are used in the function, the
structures are also should be defined and declared before they are used. A structure
can be declared using three different ways as shown below:
t
Tagged structures
Structure variables
Typedefmedstructures
4.4.2.1 Tagged Structure
Definition: The structure definition associated with structure name is called tagged
structure.The syntax of tagged structure definition is shown below:
struct tag_name
/{ type! memberl;
type2 member2;
curly braces
where
~ };Not~;::~~coi~~;smust
• struct is the keyword which tells the compiler that a structure is being defined.
• tag_name is the name of the structure
• member l , member2, 00 are called members of the structure. They are also
•••
called fields of the structure. The members are declared within curly braces.
The members can be any of the data types such as int, char, float etc. In
general, they are represented as type l , type2,oo...typen.
• The closing brace must end with semicolon.
char name[lO];
Structure definition using
int roll_number;
tagged structure
float average_marks;
};
Once we know how to define a structure , next we should know "How to declare the
variables using tagged structure?" The declaration of a structure variable has four
parts:
• keyword struct
• followed by tag name (also called structure name)
• followed by list of variables separated by commas
• followed by terminating semicolon
keyword
tag name
.J
E
T T'-»
struct student cse, ise ;
Terminated by semicolon
Structure variables
The complete structure definition along with structure declaration is shown below:
1* Structure definition *1
struct student
{
char name[lO];
int roll_number;
float average_marks;
}; 1* No memory is allocated for structure *1
1* Structure declaration *1 .
struct student cse, ise; 1* Memory is allocated for the variables */
Q Systematic Approach to Data Structures using C - 4.11
'ote:Memory is not reserved for the structure definition since no variables are
associatedwith the structure definition. But, once the structure definition is associated
with variables such as cse and ise, the compiler allocates memory for the structure
variables.
The number of bytes allocated for the variable cse is shown below:
• 10 bytes are allocated for the field name. }
• 2 bytes for the field roll_number. 10 + 2 + 4 = 16 bytes
• 4 bytes for the field average_marks.
~ote:The total size of the memory allocated is the sum of sizes of individual
members.So, totally 16 bytes are reserved for the variable cse and another 16 bytes
arereserved for the variable ise.
Letus see "How to define the structure along with structure variables?" The syntax of
structure definition and declaration is shown below:
struct
typel member l ;
type2 member2;
curly braces
• struct is the keyword which tells the.compiler that a structure is being defined .
.• memberl, member2, ..... are called members of the structure. They are also
called fields of the structure.
• The members are declared within curly braces. The members can be any of the
data types such as int, char, float etc.
• v l, v2, ..... vn are structure variables that follow curly brace. Each variable
should be separated by a comma and the last variable must be followed by
semicolon.
Note: The tag name is missingin this method of defining and declaring the structure
variables.
4.12 Q Derived types - enumerated, Structure and Union
Note: The size of the memory allocated is the sum of size of individual members. So,
totally 16 bytes are reserved for the variable cse and another 16 bytes are reserved for
the variable ise.
Note: This structure definition and declaration is normally not used because of the
following reasons:
• Without a tag, it is not possible to declare variables in later stages especially in the
functions.
• Normally structure definitions are placed at the beginning of the program file,
before the functions. In such case, the variables associated with this structure will
Q Systematic Approach to Data Structures using C - 4.13
Definition: The structure definition associated with keyword typedef is called type-
defined structure. This is the most powerful way of defining the structure. The syntax
of type-defined structure is shown below:
typedef struct
/{ type! member l ;
type2 . member2;
curly braces
where
• typedef is the keyword added to the beginning of the definition
• struct is the keyword which tells the compiler that a structure is being defmed.
• member l , member2,..... are called members of the structure. They are also
called fields of the structure. The members are declared within curly braces.
• The closing brace must end with type definition name (TYPE_ID in the syntax
shown) which in turn ends with semicolon.
Note: Using typedef it is not possible to declare a variable. But, we can have user-
defined data types. Here, TYPE _ID can be treated as the new data type.
Note: Normally all typedef statements are defined at the beginning of the file
immediately after #includes and #define statements in a file.
typedef struct
{
char name[ 10];
int [011_ number;
float average_marks;
} STUDENT;
Now, let us see "How to declare the variables using type-defined structure?" The
declaration of a structure variable has three parts:
• First part contains the user-defined data type
• followed by list of variables separated by commas
• followed by terminating semicolon
I :. 'Terminated by semicolon
Structure variables
In the above declaration, STUDENT is the user-defined data type. This statement
declares that the variables cse and ise are variables of type STUDENT. The complete
structure definition along with typedef definition and declaration is shown below:
Q Systematic Approach to Data Structures using C - 4.15
1* Structure definition *1
typedef struct
{
char name] l 0]; Structure definition
int ro11_number; using typedef
float average_marks;
} STUDENT; 1* No memory is allocated for structure *1
1* Structure declaration *1
STUDENT cse, ise; 1* Memory is allocated for the variables *1
The above structure definition and declaration can also be written as shown below:
1* Structure definition *1
struct student
{
char name[lO];
int roll_number;
float average_marks;
}; 1* No memory is allocated for structure *1
typedef struct student STUDENT;;I* STUDENT is user-defined data type *1
1* Structure declaration *1
STUDENT cse, ise; 1* Memory is allocated for the variables *1
Note: The word student which is written using fully lowercase letters is the tag name
of the structure whereas, STUDENT which is written using fully capital letters is the
user-defined data type.
Now, let us see "How are structures initialized?" The syntax of initializing structure
variables is similar to that of arrays i.e., all the elements will be enclosed within curly
braces i.e., '{' and '}' and are separated by commas. The syntax is shown below:
where
• "struct tag_name" is derived data type.
• vl , v2, vn are all the initial values. These values are called
initializers; they should be separated by commas and should be
enclosed between '{' and '}'
Solution: Consider the structure definition for an employee with three fields name,
salary and id as shown below:
struct employee
{
char name[20];
int salary;
int id;
, };
Note: The various members of the structure have the following initial values:
• The first member name has the value "Shivashankar"
• The second member salary has the value 10950
• The last member in the structure id has the value 2001.
Solution: Consider the structure defmition for an employee with three fields name,
salary and id as shown below:
Q Systematic Approach to Data Structures using C - 4.17
struct employee .
{
char name[20];
int salary;
int id;
};
Note: The various members of the structure have the following initial values:
• The first member name has the value "Shivashankar"
• The second member salary has the value 10950
• The last member in the structure id has the value 2001.
Example 4.4.3.3:' Initialization during structure declaration with more than one
variable
Solution: Consider the following initialization:
structemployee
{
char name[20];
int salary;
int id;
} a, b = {"Shivashankar", 10950, 200!};
Note: The various members of the structure have the following initial values for the
variable b:
• The first member name has the value "Shivashankar"
• The second member salary has the value 10950
• The last member in the structure id has the value 2001.
Note: Only the variable b is initialized and the variable a is not initialized. Now,
"How to initialize the variable a as well as b?" To get the answer, look at the
following example.
Example 4.4.3.4: Initialization. during structure declaration with more than one
variable
The initialization of both the variables a and b can be achieved as shown below:
4.18 Q Derived types - enumerated, Structure and Union
struct employee
{
char name[20];
int salary;
int id;
};
Note: Structures with automatic storage class (i.e., structures that are local variables)
can also be initialized .
.1
Now, let us see some important points to remember when we initialize the structures.
• The members of the structure can not be initialized in the structure definition. For
example,
struct employee
{
char nanie[20] = "Shivashankar";
int salary = 20000;
int id = 25;
};
is invalid.
• The initial values are assigned to members of the structure on one-to-one basis
i.c . the ilucs are assigned to various members in the order specified from the
fir t J1lCl1l ~ . For example, the following statement:
.Q Systematic Approach to Data Structures using C - 4.19
is valid. Here, the string "Shivashankar" will be assigned to the first member,
10950 is assigned to the second member and 2001 will be assigned to the third
member.
• During partial initialization (i.e., if there are fewer initial values i.e., few
initializers, than the members of a structure), the values are assigned to members
in the order specified and the remaining members are initialized with default
values. If the remaining members are of type integer or float, they are initialized
with the default value zero and if the remaining members are of type string or
character, they are initialized with default value of '\0'. For example, in the
initialization statement
We know that variables can be accessed and manipulated using expressions and
operators. On similar lines, the ~tructure members can be accessed and manipulated.
Before manipulating let us see "How structure members are accessed?" The members
of a structure are separate entities and so should be processed individually. A member
of a structure can be accessed by specifying the variable name followed by a period
(also called dot) which in turn is followed by the member name using the syntax
shown below: .
variable. member
For example, consider the structure definition and initialization shown below:
struct employee
{
char name[lO];
float salary;
int id;
};
The various members can be accessed using the variable a as shown below:
• By specifying a.name we can access the string "RAMA".
• By specifying a.salary we can access the value 10950.000060
• By specifying a.id we can access the value of 2001
Now, the question is "How to display the various members of a structure?" The
various members of a structure can be accessed and printed as shown below:
Q Systematic Approach to Data Structures using C - 4.21
Once we know how to display the members of a structure, let us see "How to read the
values for various members of a structure?" We know that format specifications such
as %s %d %f are used to read a string, an integer and a float. The same format
specifications can be used to read the members of a structure. For example, we can
read the name of an employee, the salary and id as shown below:
Now, let us see "How to write a program to simulate the multiplication of two
fractions?" Before writing the program, let us give the design procedure by taking
example:
Design: Consider two fractions 2/8 and 3/7. These two numbers can be multiplied and
as shown below:
~*~=~
8 7 56
Since there are no fractional data types in C language, this can be simulated using
structures. A fraction number can be defined as a structure with two members namely
numerator and denominator as shown below:
typedef struct
{
int numerator;
int denominator;
} FRACTION;
Let a and b are two given fractions and c is the resultant fraction. The resultant
fraction c can be computed as shown below:
c.nurnerator = a.numerator * b.numerator
c.denominator = a.denominator '" b.denominator
4.22 Q Derived types - enumerated, Structure and Union
#include <stdio.h>
typedef struct
{ .
int numerator;
int denominator;
} FRACTION;
void maint)
{
I
FRACTION a, b, c; I TRACING
I Input
printf("Enter fraction! in the form x/y: "); I Enter fraction 1: 3/8
scanf("%d/%d" ,&a.numerator, &a.denominator); I
printf("Enter the fraction2 in the form x/y: "); I Enter fraction2: 4/7
scanf("%d/%d" ,&b.numerator, &b.denominator); I
I
c.numerator = a.numerator * b.numerator; , I
c.denominator = a.denominator*b.denominator; I Output
.1 3/8 * 4/7 = 12/56
printf("%d/%d * %d/%d = %d/%d\n",a.numerator, a.denominator,
b.numerator, b.denominator,
c.numerator.c.denorninator);
}
Definition: The size of a structure is defined as the sum of sizes of each member of
the structure. For example, consider the structure declaration shown below:
struct student
If 2000 is the starting address of the variable a, then the starting address of each
member depends on sum of sizes of previous members' and the complete memory
'map is, shown below:
i
,,
2000
, ,
2002 .
I
2004
,,
I
a.name (10 bytes)
,
+ 10 2006 I
I
I
2008 I
+4L
I
2010 I
2016 ,
I
- ,,
2020 •. , .
"'~--""'y""'----~
2 bytes
Fig. 4.5.1 Memory map for members of a structure
. Note: The address of each member is greater than the address of its previous member
and hence, address of each member is different.
Some times, the size of the structure will not be equal to-sum of sizes of the individual
members. This is because of slack bytes. Now, let us see "yv'hat are slack bytes?"
struct student
{
char namej l O]; 1* Assume size of char is 1 byte *1
int roll_number; 1* Assume size of int is 4 bytes *1
double average_marks; 1* Assume size of double is 8 bytes *1
}a ;
The size of each member of the structure is shown below:
Let us see "What is the size of structure if slack bytes are introduced?" If 2000 is the
starting address of the variable a, then the starting address of each member depends
on sum of sizes of previous members along with the word boundary and the complete
memory map is shown below:
2 bytes
Fig. 4.5.2 Memory map showing slack bytes
Note: The size of the structure is 32 bytes which is greater than the sum of sizes of
each member. So, when slack bytes are used, the size of a structure is greater than or
equal to the sizes of its individual members,
Note: Data can be accessed much faster way if present in word boundaries. So, even
though slack bytes do not contain valid information, they are useful so that data is
available always in word boundaries and can be accessed very fast.
4.26 Q Derived types - enumerated, Structure and Union
Nete: In the structure definition, double is the largest data type with size 8 bytes. So,
the starting address of each member should be divisible by 8.
Note: The starting address of each member is divisible by 8. But, we can access only
the specified number of bytes of a member. The extra bytes that are not used are slack
bytes. The shaded region represents the slack bytes.
The various operations can be performed on structures and structure members. Any
operation that can be performed on ordinary variables can also be performed on
structure members. But, some operations 'Cannot be performed on structure variables.
The following sections deals with operations that are carried on structure and
structure members \
4.5.2.1 Copying of structure variables
Copying of two structure variables is achieved using assignment operator. But, one
structure variable can be assigned to another structure variable of the same type.
struct struct
.1
{ {
char name[lO]; char name[lO];
int salary; int salary;
int id; int id;
} a, b; } c, d;
Note: Observe that even though the members of both structures are same in number
and type, both are considered to be of different structures.
So, the statements
a =b; c = d;
and
b==a: . d=c:
are valid. But, the following statements
a=c; c = a;
b=d; and d=b;
are invalid.
Q Systematic Approach to Data Structures using C - 4.27
In the previous section, we have seen that copying of two structure variables of same
type is allowed. But, we should remember that comparing of two structure variables
of same type or dissimilar type is not allowed.
For example, the following operations are invalid, even though a and b are of the
same type.
a=b;. 1* Invalid: Comparison is not allowed between structure variables *1
a !=b; 1* Invalid: Comparison is not allowed between structure variables *1
However, the members of two structure variables of same type can be compared using
relational operators. For example, if a and b are two structures, then the following
relational operations are valid:
Operation Meaning
a.member l = b.member2 Compare member! of a with member2 of
b and return true if they are same, false
otherwise .
a.member I != b.member2 Return true if the member! of a and
member2 of b are not same and false
otherwise.
, .
Note: The arithmetic, relational, logical and other various operations can be
performed on individual members of structures but not on structure variables. But, if
we want to compare we can do so by comparing the individual members of a structure.
The members of a structure are similar to anyother variables and so any operation
that can be performed on a variable, the same operation can be performed on structure
members also. So, members of a structure can be manipulated by using expressions
and operators.
For example, ++a.salary is equivalent to ++(a.salary) and &a.salary is
equivalent to &(a.salary). The expression &a denotes the starting address of the
structure variable. Some of the expressions involving the variable a and its members
are shown below:
4.28 Q Derived types - enumerated, Structure and Union
- f
Expression Meaning
++a.salary Increment the salary before accessing its value.
Now, let us see "What are the differences between arrays and structures?" Both arrays
and structures are derived data types. The various differences between the two are:
Arrays Structures
• An array IS a collection of
related data elements of the
• Structure IS a collection of
variables of similar or dissimilar
same type. In other words, array data type. In other words,
is used to represent homogenous structures are used to represent
data # the heterogeneous data.
• An array items can be accessed
by using its subscript or using
• Structure items can be accessed
using ''." (read as dot operator) or
de-referencing/indirection (*) "->" (called selection operator in
operator for pointers case of pointers)
-
4.5.3 Pointer to Structures
typedef struct
{
char name[lO];
int ro 11_number;
float average_marks;
} STUDENT;
Q Systematic Approach to Data Structures using C - 4.29
void mairu)
{
STUDENT a;
STUDENT *p;
p=&a;
Here, p is a pointer to a structure. Using this pointer, various members of the structure
can be accessed using two methods:
If p is pointer to a structure, then the structure itself can be accessed using indirection
operator as shown below:
Once the structure is accessed, each member of the structure can be accessed using
dot operator as shown below:
(*p).name 1* Access the name *1
(*p).roll_number 1* Access roll number *1
(*p ).average _marks 1* Access average marks *1
Note: The parentheses in all the three expressions are necessary. We should not omit
the parentheses. For example,
*p.name 1* Invalid way of accessing the member >1:1
*p.roll_ number 1* Invalid way of accessing the member *1
*p.average _marks 1* Invalid way of accessing the member *1
If P is pointer to a structure, then the members of the structure can also be accessed
using selection operator denoted by -> (which is formed by the minus sign and
4.30 Q Derived types - enumerated, Structure and Union
greater- than symbol). Using this operator, various members of the structure can be
accessed as shown below:
p->name 1* Access the name *1
p->roll_ number 1* Access roll number *1
p->average_marks 1* Access average marks *1
Now, let us see how to design and write the program to simulate the digital clock.
Design: A structure with three members representing seconds, minutes and hours is
shown below:
typedef struct
{
int see; 1* Represent seconds *1
int mm; 1* Represent minutes *1
int hrs; 1* Represent hours *1
} CLOCK;
CLOCKc;
The following condition and codes help us to simulate the digital clock:
• If value of seconds reaches 60, then we reset the seconds value to 0 and increment
minutes. The code for this case can be written as shown below:
if (c->sec = 60)
{
c->sec = 0;
c->min++;
}
,!;l Systematic Approach to Data Structures using C - 4.31
+ When minutes is being incremented, if value of minutes reaches 60, then we reset
the minutes value to 0 and increment hours. The code for this case can be written
as shown below:
if (c->min = 60)
{
c->min = 0;
c->hrs++;
}
+ When hours is being incremented, if the value reaches to 24, it is reset to O. The
code for this case can be written as shown below:
if (c->hrs = 24)
{
c->hrs = 0;
}
#include <stdio.h>
if (clock->sec = 60)
{
clock->sec = 0;
clock ->t:nin++;
4.32 ~ Derived types - enumerated, Structure and Union
if (clock->min = 60)
{
clock->min = 0;
clock ->hrs++;
void maim)
{
CLOCK clock = {II, 59, 57}; /* Initialize time to 11:58:57 */
int 1, 0
Using structures we can deal with various complex problems. The complex structures
include the following:
• Nested structures
• Structures containing arrays
• Structures containing pointers
• Arrays of structures
Let us discuss each of these one by one
Q Systematic Approach to Data Structures using C - 4.33
Note that the above structure consists of a member identified by marks whose type is
struct subject (shown using arrow mark). So, this is nested structure. Consider the
following declaration:
STUDENTs; 1* Variable s is of type STUDENT *1
Using the variable s, we can access the various fields using dot operator as shown
below:
s.name ~* Access name of the student *1
s.USN 1* Access USN of a student *1
s.marks.marks 1 1* Marks of subjectl *1
s.marks.marksz 1* Marks of subject 2*1
s.marks.marks3 I~ Marks of subject 3 *1
4.34 Q Derived types - enumerated, Structure and Union
Note: To access marksl using the variable s we have to use the dot operator twice
i.e., s.marks.marksl I
The program to arrange the student details in alphabetical order is shown below:
typdef struct
{
char name[lO]; /* Name of the student */
int USN; /* University serial number */
struct subject marks; /* Marks of 3 subjects */
} STUDENT;
STUDENT temp, a[9];
int i, j, n;
printf("Enter the number of students\n");
scanf("%d", &n);
printf("Enter the student details\n");
for (i = 0; i < n; i++)
{
printf("Details of%d student\n",i+l);
printf("Name = "); scanf(" %[I\\n]", a[i].name);
printf("USN = "); scanf("%d", &a[i].USN);
printf("Marksl = "); scanf("%d", &a[i].marks.marksl);
printf("Marks2 = "); scanf("%d", &a[i].marks.marks2);
printf("Marks3 = "); scanf("%d", &a[i].marks.marks3);
}
~ Systematic Approach to Data Structures using C - 4.35
Now, the question is "Is- it possible to use arrays within structures?' The answer is yes.
Arrays can be used within structure as members. A single or multi-dimensional array
of int, float, char and double can be used within the structures.
Consider the student information consisting of name, USN and marks of 3 subjects.
The structure definition is shown below:
typedef struct
{
char name [10]; /* Name of the student */
int USN; /* University serial number */
int marks[3] 1* Marks of 3 subj ects* ~
} STUDENT;
4.36 Q Derived types - enumerated, Structure and Union
Note: Here, STUDENT is user-defined data type. Observe that the member marks is
an array of integers. This array is used to store the marks of three subjects. The
member name is also an array. But, it is array of characters.
Consider the following declaration:
STUDENTs;
Using the variable s, we can access the various fields using dot operator as shown
below:
s.name 1* Access name of the student *1
s.USN 1* Access USN f a student *1
s.marks[O] 1* Marks of subjectl *1
s.marks[l] 1* Marks of subject 2*/
s.marks[2] /* Marks of subject 3 *1
#include <stdio.h>
#include <string.h>
void maint)
{
struct student
{
char name[lO]; 1* Name of the student *1
int USN; 1* University serial number *1
int marks[3]; 1* To hold marks of 3 subjects *1
};
Note: Compare example 4.6.12 and 4.6.2.2. Observe that the logic of both programs
remain same. The difference is in the structure definition and the method of accessing
the marks of 3 subj ects while reading and printing. Rest of the program remains same.
4.38 Q Derived types - enumerated, Structure and Union
We have seen that members of a structure can be arrays, ints, floats etc. Now, the
question is's It possible to use pointers \\ ithin structures?" Yes, a pointer can also be
used as a member of the structure. In fact, pointers are common when we use
structures.
typedef struct
{
int day;
int month;
int year;
} DATE;
In the above type definition, day is represented as integer. Suppose, we have the
following declarations in our program:
ote that the days are represented as strings. If it is required to store the days as
strings, then the earlier type definition and declaration with respect to DATE can be
written as shown below:
typdef struct
{
char *day;
int month;
int year;
} DATE;
,/
DATEd;
Q Systematic Approach to Data-Structures using C - 4.39
Suppose we want to store the date as "Thursday 11 1965". This can be done using the
following statements:
To store the information of more number of students, we can have the following
declaration:
STUDENT a[10];
Now, the next question is "How to access the elements of array and information
related to a particular student?" Since a is an array, each student information can be
accessed by specifying the index along with member name. The information of ith
student can be accessed by specifying:
a[i].name 1* Access the name ofith student *1
a[i].marks 1* Access the marks of ith student *1
a[i].year 1* Access the year *1
Now, let us see "How to arrange the student information in alphabetical order?" using
insertion sort.
4.40 Q Derived types - enumerated, Structure and Union
lote: The program along with design has already been discussed using arrays in
section 1.12, page 1.50. Here, it is being written using structures and pointers.
#include <stdio.h>
#include <string.h>
typedef struct
{
char name[20];
int marks;
int year;
} STUDENT;
*U + 1) = item;
}
}
Q Systematic Approach to Data Structures using C - 4.41
void mairu)
{
int n,l;
STUDENT a[lO];
1* Read n elements *1
printf("Enter n students details\n");
for (i = 0; i < n; i++)
{
scanf("%s %d %d", a[i].name, &a[i].marks, &a[i].year);
}
insertion_sort(n, a);
Input Output
Enter the number of students The sorted student records are
5 Aruna 700 1065
Enter n students details Mona 999 1999
Mona 999 1999 Nirmala 800 1972
Suguna 800 1970 Padma 850 1965
Nirmala 800 1972 Suguna 800 1970
Aruna 700 1065
Padma 850 1965
#include <stdio.h>
typed f struct
{
int numerator;
int denominator;
} FRACTION;
void maint)
{
FRACTION a, b, c;
priotf("Enter fraction! in the form xly: ");
scaof("%d/%d",&a.numerator, &a.denominator);
Now, the question is "Is there any better way of writing the above program?" Yes,
there is. The better way is to represent the fractional number using structure as shown
below:
typedef struct
{
int numerator;
int denominator;
}FRACTION;
Note: Here, FRACTION is the user-defined data type
Then, use three variables x, y and z of type FRACTION .. Pass two variables x and y
as parameters to the function. The function can multiply the given two fractions
storing the result as shown below:
• Multiply the numerators and store the result as numerator
i.e., z.numerator = x.numerator * y.numerator;
#include <stdio.h>
typedef struct
{
int numerator;
int denominator;
} FRACTION;
return z;
}
void mainf)
{
~ FRACTION a, b, c;
Note: The above program is most efficient way to multiply two fractions when
compared with program given in 4.8.1.1.
,!;lSystematic Approach to Data Structures using C - 4.45
#include <stdio.h>
typedef struct
{
int numerator;
int denominator;
} FRACTION;
void maint)
{
FRACTION a, b, c;
typedef struct
{
float r; 1* real part *1
float i; 1* imaginary part *1
} COMPLEX;
Note: Here, COMPLEX is the new data type which is derived from structure. Since
we have framed this data type, COMPLEX is called user-defined data type.
Now, to store two complex numbers we need to use two variables. This can be done
as shown below:
COMPLEX a, b; '1* a and b represent 2 complex variables *1
.
Now, let us see "How to add two complex numbers?" Given two complex numbers
( x + yi ) and ( a + bi), in mathematics addition can be performed as shown below:
.
x + Y1
+ a + bi
(x + a ) + (y + b)i
Implementation in C: Observe that real part of result = Real part of 151 number +
Real part of 2nd number and Imaginary part of result = Imaginary part of 151 number +
Imaginary part of 2nd number. So, if a and b are two complex numbers with r as the
real part and i as imaginary parts respectively, then the resulting complex number c
which is sum of a and b is given by
return c;
}
Implemen tation in C: Observe that real' part of result = Real part of 1sr number-
Real part of 2nd number and Imaginary part of result = Imaginary part of 1sr number-
Imaginary part of 2nd number. So, if a and b are two complex numbers, then the
resulting complex number c is given by
Example 4.9.2.1: C function to subtract one complex number from the other
COMPLEX subtract(COMPLEX a, COMPLEX b)
{
COMPLEXc;
c.r = a.r - b.r;·
c.i = a.i - b.i;
return c;
}
Q Systematic Approach to Data Structures using C - 4.49
Imaginary part is obtained by multiplying real part of 1st number with imaginary part
of 2nd number and adding to the product of imaginary part of 1sl number with real part
of 2nd number i.e.,
return c;
}
(x + yi) (x + yi)(a - bi) xa - xbi + yai - ybi2 (xa + yb) (ya - xb)i
= +
(a + bi) (a + bi)(a - bi)
So, the real part is obtained by adding the product of two real parts with the product
of two imaginary parts and dividing the result by a2 + b2 which is sum of squares of
reat part and imaginary part of the dividend. If a and b are two complex numbers,
then the real part of c (which is the result) is given by
The imaginary part of the result is obtained by multiplying the imaginaty part of 1st
number with real part of 2nd number and then subtract the product of real part of 1st
ttutnber and imaginary part of 2nd number. Finally, the whole result should be divided
by a2 + b2. If a and b are two complex numbers, then the imaginary part of c(which is
~t result) is given by
I
•Thus, the function to divide a by b and storing the result in c is shown below:
return c;
I }
f
The function to tead a complex number is shown below:
••
~ Systematic Approach to Data Structures using C - 4.51
Note: The function read_complexO reads a complex number and the function
write_complexO is used to display a complex number. These two functions are self-
explanatory and left as an exercise to the reader to understand the logic.
The complete program to add, subtract, multiply and divide two complex numbers is
shown below:
4.52 Q Derived types enumerated, Structure and Union
Example 4.9.7.1: C program to perform arithmetic operations on complex numbers
#include <stdio.h>
/* Structure definition of a complex number */
typedef struct
{
float r; /* real part */
float i: /* imagi nary part */
} MP~.-
/* Irrcf ucio: xnrnpfe 4.9.1.1: C function to add two complex numbers */
/* Include: Example 4.9.2.1: function to subtract a complex number from the other */
/* Include: Example 4.9.3.1: C function to multiply two complex numbers *
/* Include: Example 4.9.4.1: C function to divide a by b *1
/* Include: Example 4.9.5.1: C function to read a complex number */
/* Include: Example 4.9.6.1: C function to display a complex number *1
void maint)
{
COMPLEX a; /* First complex number *1
cOMPLEXb; 1* Second complex number *1
cOMPLEXc; 1* Resultant complex number */
IInput
printf("Enter first complex number\n"); I Enter first complex number
a = read complext); Rpart = 4
I Ipart = 2
printf("Enter second complex number\n");1 Enter the second complex no.
b = read_complexO; I Rpart = 2
I Ipart = 2
printf("First complex number = ");. I First number = 4.00 + 2.00i
write_complex(a);
I
printf("Second complex number = "); I Second number = 2.00 + 2.00i
write _complex(b);
I
c = add(a,b); I
printf("Sum of complex numbers = "); I Sum = 6.00 + 4.00i
write _complex( c); I
c = subtract(a 'b)' J
printf("Diffc Ten e = ''); I Difference = 2.00 + O.OOi
write_complex(c); I
Q Systematic Approach to Data Structures using C - 4.53
•
c = multiply(a,b);
I
printf("Product of complex numbers = "); : Product = 4.00 + 12.00i
write _complex( c);
I
c = divide(a,b);
printf("Division of complex numbers = "); : Divsn result = 1.50 + -0.50i
write _ complex( c);
I
In this section, we shall see another concept called union which is derived from
structures. Let us see "What is a union?"
Definition: A union is a derived data type like structure. So, union can also be treated
as a collection of variables under a single name. All these variables may contain data
items of similar or dissimilar data types. Each variable in the union is called a
member or a field.
Now let us see "How union is declared and used in C?" The syntax,
declaration and use of union is similar to the structures but its functionality is totally
different. Thus, the general format (syntax) of a union definition is shown below:
union tag_name
{
type 1 member 1;
type2 member2;
• union is the keyword which tells the compiler that a union is being defined.
• member1, member2, ..... are called members of the union. They are also called
fields of the union.
• The members are declared within curly braces. The members can be any of the
data types such as int, char, float etc.
• There should be semicolon at the end of closing brace
4.54 Q Derived types - enumerated, Structure and Union
F or the pointer
arrow/selection op
Example 4.10.1: Consider the following definition and declaration:
pointer variable x,
typedef union x->i
{ x->c
int 1; union definition x-><
double .d:'
char c', Let us consider SI
Note: For the union definition memory is not allocated
} ITEM; union.
For the pointer variables, the indirection operator * and dot operator or
arrow/selectionoperator -> can be used to access the members. For example, using
pointervariable x, the members can be accessed as shown below:
x->i or (*x).i
x->d or (*x).d
x->c or (*x).c
not allocated Let us consider some examples to understand the difference between structure and
uruon.
; allocated
ITEM. The
I Example4.10.3: Program to access various members of a union
#include<stdio.h>
t associated voidmaint)
h a variable {
typedef union
{
int marks;
elow: char grade;
float percentage;
} STUDENT;
•.. After executing the statement x.grade = 'A', the member grade will hold the
value 'A' whereas other members marks and percentage contains garbage value Example 4.1
and should not be accessed following stn
• After executing the statement x.percentage = 99.5, the member percentage will
hold the value 99.5 whereas other members marks and grade contains garbage typed
value and should not be accessed. {
ote: It is observed from the above output that only one member of union can hold a
value at a time. It is not possible to access all the members simultaneously. So, the
variable of type STUDENT can be treated as integer variable or char variable or float } DA'
variable. Now, consider the same program with structure
DAT)
Example 4.10.4: Program to access various members of a structure
Solution: Th
#include <stdio.h> member ofth
bytes. In this
void maint) are allocated
{ members nan
typedef struct
{
int marks;
char grade;
float percentage;
1
} STUDENT;
STUDENTx~
x.marks = 100;
x.grade = 'A';
x.percentage = 99.5; I Output The memory
I
printf("Grade : %c\n",x.grade); arks: 100
printf("Marks: %d\n",x.marks); rade: A
1000
printf'('Percentage: %f\n", x.percentage); JL-----+: ercentage: 99.5
} I
I ~
Fig
Note: It is observed from the above output that the all the members of a structure can
hold individual values at a time. It is possible to access all the members of a structure
simultaneously.
~ Systematic Approach to Data Structures using C - 4.57
DATA x; DATA x;
Solution: The memory allocated by the compiler is large enough to hold the largest
member of the union. Suppose, int occupy 2 bytes, char occupy 1 byte, float occupy 4
bytes. In this example, the member d occupies more memory (4 bytes) and so 4 bytes
are allocated for the variable x. But, this allocated memory is shared by all the
members namely i, d and c as shown below:
e f ,
The memory representation for the structure with same fields is shown below:
1000
~c~r------T_
1001
;]----.o.-J
1004
--,+---I
1005 1006 1007
Now, let us see "What are the differences between a structure and union?" consider
the following examples:
Definition: The
Structure Union normally is usee
1. The keyword struct is used to define a 1. The keyword union is used to define together using a
structure a union. the various bits c
or union. So, bit
2. When a variable is associated with a 2. When a variable is associated with a allows the user t(
structure, the compiler allocates the umon, the compiler allocates the
memory for each member. The sizeof memory by considering the size of the
Bit-fields can pn
structure is greater than or equal to largest member. So, size of union is
tighter packing
the sum of sizes of its members. The equal to the size of largest member
reasons:
smaller members may end with (Fig. 4.1 O.a)
• Data can
unused slack bytes (see section 4.5.1,
• Some de
figure 4.5.2)
within a'
3. Each member within a structure IS 3. Memory allocated IS shared by reset me
assigned unique storage area individual members of union prograrm
(Fig. 4.1 O.b) (Fig. 4.1 O.a) I • Certaine
• Even th:
4. The address of each member will be 4. The address IS same for all the using bit
in ascending order This indicates that members of a union. This indicates and undt
memory for each member will start at that every member begins at offset
different offset values (Fig 4.1 O.b) values (Fig. 4.1 O.a) Now, let us see
1
5. Altering the value of a member will 5. Altering the value of any of the part of the struc
not affect other members of the member will alter other member
s
structure: See example 4.10.4 values. See example 4.10.3
7. Several members of a structure can be 7. Only the first member of a union can
initialized at once be initialized
where
4.11 Bit-field • data_~
• Identif
We can access and manipulate individual bits using bit-wise operators in C language.
• biUen
Another alternative method of accessing bits is using bit-fields. Now, let us see "What
is a bit-field?" Note:The col
d union?" consider ~ Systematic Approach to Data Structures using C - 4.59
Now, let us see "What is the syntax of bit-field?" The bit-field can be defined" as a
of any of the part of the structure in the following manner.
r other member
4.10.3 struct tag
{
an be accessed at data_type identified :bit_Iength;
4.10.3 data_type identifier2: bit_length;
er of a union can
data_type identifier3:bit_Iength;
};
where
• data_type if of type int or unsigned int
• identifierl, identifier2 etc., are the names of the bit-fields
orsin C language.
• bit_length is the number of bits used for the identifiers
, letus see "What
Note:The colon (':') is in between the identifiers and bit_lengths
4.60 Q Derived types - enumerated, Structure and Union
typedef struct
{ } STU
char name[30]; 1* Name of the student *1
char sex; 1* Sex of the student *1 STUD
int age; 1* Age of the student */
int rollno; 1* Roll number of the student *1 ote: The b
char branch[30]; /* Branch of the student *1 structure or u
} STUDENT;
Consider the
STUDENT a[100]; 1* Capacity to store info of 100 students */ message.
Q Systematic Approach to Data Structures using C - 4.61
Ifint occupies 2 bytes and a character 1 byte, the size of the structure is 65 bytes. So,
totalmemory required, to store the information of 100 students will be 6500 bytes.
Suppose we want to compress the information to occupy minimum amount of
memory.This can be done as follows:
So, in total 30 bytes for the name and 16 bits i.e., 2 bytes for the rest of the
information can be used. In this technique, we use 32 bytes per student and 3200
bytes for 100 students. Thus, we can reduce the usage of the memory significantly by
compressing the information. The fields sex, age, rollno and branch can be
combined into a single integer value called bit-field. The bit-field can be declared as a
part of the structure in the following manner.
typdef struct
{
char name[30]
unsigned sex : 1;
unsigned age : 5;
unsigned rollno : 7;
unsigned branch: 2;
} STUDENT;
STUDENT a[100];
Note: The bit-fields can be accessed III the same way we access members of a
structure or union.
Consider the program to enter the values for bit-fields and display the appropriate
message.
Q Systematic Approach to Data Structures using C - 4.61
Ifint occupies 2 bytes and a character 1 byte, the size of the structure is 65 bytes. So,
totalmemory required, to store the information of 100 students will be 6500 bytes.
uppose we want to compress the information to occupy minimum amount of
memory.This can be done as follows:
So, in total 30 bytes for the name and 16 bits i.e., 2 bytes for the rest of the
information can be used. In this technique, we use 32 bytes per student and 3200
bytesfor 100 students. Thus, we can reduce the usage of the memory significantly by
compressing the information. The fields sex, age, rollno and branch can be
combined into a single integer value called bit-field. The bit-field can be declared as a
part of the structure in the following manner.
typdef struct
{
char name[30]
unsigned sex : 1;
unsigned age : 5;
unsigned rollno : 7;
unsigned branch: 2;
} STUDENT;
STUDENT a[100];
ote: The bit-fields can be accessed In the same way we access members of a
structure or union.
Consider the program to enter the values for bit-fields and display the appropriate
message.
4.62 ~ Derived types - enumerated, Structure and Union
Example 4.11.2: Program to show the usage of bit-fields and its use
#include <stdio.h>
/* Structure definition to represent student information */
typdef struct
{
char name[30];
unsigned sex :1;
unsigned age :5;
unsigned rollno :8;
unsigned branch :2;
} STUDENT;
void maint)
{
STUDENT a[lO];
int i, n, sex, rollno, branch, a ge;
printf("Enter the no: of students\n");
scanf("%d" ,&n);
for (i = 0; i < n; i++)
{
printf("Enter the information of student = %d\n" ,i+ 1);
printf("Name : "); /
scanf(" %s",a[i].name);
printf("Sex : ");
scanf("%d",&sex);
printf("Age : ");
scanf("%d" ,&age);
printf("Roll no : ");
scanf("%d",&rollno );
printf("Branch : ");
scan f("%d ",&branch);
a[i].sex = sex;
a[i].age = age;
a[i].rollno = rollno;
a[i].branch = branch;
}
~ Systematic Approach to Data Structures using C - 4.63
switch(a[i].sex)
{
case 0:
printf(" Female");
break;
case 1:
printf(" Male ");
break;
}
switch(a[i].branch)
{
case 0:
printf(" Computer Science\n");
break;
case 1:
printf(" Information Sciencein");
break;
case 2:
printf(" Electrical Science\n");
break;
default:
I
ow, let us see "What are the various restrictions imposed while using bit-fields?"
The various restrictions in using the bit-fields are shown below:
4.64 Q Derived types - enumerated, Structure and Union
Exercises:
• Classification of Files
• Using Binary Files
• Standard Library functions for files - 2 hours
We know that the function scanf() is used to enter the data from the keyboard and
printl'() is used to display the result on the video display unit. This works fine when
the input data is very small. But, as the volume of input data increases, in most of the
applications. we find it necessary to store the data permanently on the disk and read
from it for the following reasons:
• It is very di Ificult to input large volume of data through terminals
• It is time consuming to enter large volume of data using keyboard.
• When we are entering the data through the keyboard, if the program is
-terminatcd for any of the reason or computer is turned off, the entire input data
is lost.
To overcome all these problems, the concept of storing the data in disks was
introduced. Here, the data can be stored on the disks; the data can be accessed as and
when required and any number of times without destroying the data. The data is
stored in the form of a file. Now, let us see "What is a file? How the data is stored in a
file?"
01 00 oon 1 01 00 001 0 0100 0011 0000 1101 0011 0001 0000 110 I f
'if
EOI'
Note: EOF stands for End Of f.ile
S.! tl inary files
Once we know the definition of file, let us see "What are the various types of files?"
When the data stored in auxiliary device is read by the program, the data (stored in
terms of as and Is) can be interpreted as either a text file or a binary file. Thus, there
are two types of files as shown below: EOF
'---------------------------~--------------------------~
File
~TextFile
4 Binary File
Thus, it is very clear that even though the data is stored in terms of as and 1s on a
disk, wc can interpret them either as text file or binary file.
Definition: A text file is the one where data is stored as a stream of characters that
can b processed sequentially. In the text format, data are organized into lines
terminated by newline character. The text files are in human readable form and they
can be created and read using any text editor. Since text files process only characters,
they can read or write only one character at a time. A newline may be "converted to
carriage return and line feed character. Note: newline character = carriage return +
linefeed
Because of these translations, the number of characters written/read may not
be the same as the number of characters written/read on the external device.
Therefore, there may not be a one-to-one relationship between the characters
written/read into the file and those stored on the external devices.
For example, the data 2345 requires 4 bytes with each character occupying
exactly one byte. A text file can not contain integers, floating-point numbers etc. To
store the data such as integers, floating point numbers etc., they must be converted to
their character-equivalent formats, For example, the data 2345 in text file is stored as
sequence of4 characters '2', '3', '4' and '5'. The following figure shows
• How 6 characters are entered from the keyboard
• How the 6 characters entered from keyboard are stored in file
t How the 6 characters stored in a file are displayed on the monitor/printer
41(A) 42(B) 43(C) OD(.J) 31(l) OD(.J) EOF
~~~~~~
010000010100001001000011 0000 1101 0011 0001 00001101 [:.,
Note: In the above figure, the symbol.J represent the return key and its ASCIl
character is osoo (13 in decimal). The ASCII values of A, B, Care Ox41 (65
decimal),Ox42 (66 in decimal), Ox43 (67 in decimal) respectively. The ASCII values
of 1,2,3 etc. are Ox31 (48 in decima\) ,Ox32(49 in decima\) and so on. The symbol 6.
represents end of file.
Definition: A binary file is the one where data is stored on the disk in the same way
as it is represented in the computer memory. The binary files are not in human
readable form and they can be created and read only by specific programs written for
them. The binary data stored in the file can not be read using any of the text editors.
For example, the data 2345 takes 2 bytes of memory and is stored as Ox0929
i.e., Ox09 is stored as one byte and Ox029 is stored as other byte. The number of
characters written/read is same as the number of characters written/read on the
external device. Therefore, there is one-to-one relationship between the characters
written/read into the file and those stored on the external devices.
Now the question is "What is the difference between a text file and a binary file?"
TIle differences between these two types of files are shown below:
2. Data is stored as lines of characters 2. Data is stored on the disk in the same
with each line terminated by \n which way as it IS represented III the
may be translated into carriage return computer memory
-I- line feed
4. Data can be read using any of the text 4. Data can be read only by specific
editor programs written for them
Normally the binary files are associated with structures as shown in figure below:
File
~II,----
structures
Q Systematic Approach to Data Structures using C 5.5
Each rectangle in the figure represents a structure. At the end of a file there is a file
marker (6) which denotes end of file (EOF) marker. The four steps that are used
during the manipulation of binary files are shown below:
Open a file
Steps in using
the files Read the data from the file or write the data into file
We know that all the variables are declared before they are used. Likewise, a file
pointer variable also should be declared. Now, let us see "How to declare a file
pointer variable?" A file pointer variable fp should be declared as a pointer to a
structure of type FILE as shown below:
#include <stdio.h>
void maint)
{
/* File operations */
}
Note that rILE is the derived data type which is defined already in header file stdio.h.
as a structure. The structure details are hidden from the programmer and this structure
is used to store information about a file.
Note: The file pointer fp can be used either as a local or global variable. In the above
syntax, the file pointer fp is used as a global variable. The file pointer fp can be used
to open a file, update a file and to close a file in sequence.
Analogy: The information of all the students in a college is maintained by the college
office. The information of a particular student such as name, address, branch and
semester details along with CET ranking, percentage of marks in PUC etc. is stored in
a file. If we want to read, write or update the student details we have to open a file,
5.6 Q, Binary files
update a file and finally close it. The same sequence of operations can be carried out
with respect to files in C also. Here too, before updating a file, it has to be opened and
after updating the file, it has to be closed.
Note: The first operation to be done before updating the file is opening a file. The last
operation to be done after updating is closing the file. .
Note: It is clear from the above figure that the file can be either in read or write state.
If the file can not be opened successfully or can not be created then it is an error and
the file state will be error state. This results in three possible file states as shown
below: .
Q Systematic Approach to Data Structures using C 5.7
r (read mode) This mode is used for opening an existing file to perform read
operation. The various features of this mode are shown below:
• Used only for the text file
• If the file does not exist, an error is returned IEOF
• The contents of the file are not lost 1..-----------, '+'
• The file pointer points to the beginning File contents 16
~----------~
I
File position after opening
w (write mode) This mode is used to create a file. The various features of this mode
are shown below: .
• Used only for the text file
• If the file does not exist, a file is created. .
• If the file already exists, the original contents of the file are lost
• The file pointer points to the beginning EOF
a (append mode) This mode is used to insert the data at the end of the existing file.
The various features of this mode are shown below:
• Used only for the text file
• If the file does not exist, a file is created
• If the file already exists, the file point er pomts to teen
h d I EOF
'
,
• The new data' is inserted at the end 'V
Note: lfwe have + as the suffix for the above modes, then in all the above three cases
the file is opened for read/write operation with the same features as specified earlier.
For example, r+ (read/write), w+Iread/write), a+(read/write at the end)
rb (read mode) This mode is used for opening an existing file to perform read
operation. The various features of this mode are shown below:
+ Used only for the binary file
+ If the file does not exist, an error is returned IEep
+ The contents of the file are not lost r----------------, ~
File contents 6
• The file pointer points to the beginning T---------------~1
Note: A mode with substring + is called update mode. This is because; the substring
which is part of the mode as explained earlier represent read/write operation.
-I-
Now, let us see "What are the standard .library functions that are used for binary
files?" The various standard file library functions that we discuss with, respect to
binary files are shown below:
File open and close functions
~-~ Block read and write functions
Categories of
tile functions . ---11---" File positioning functions
- System file operations
•...••
-~ File status functions
Once we know various modes in which a file can be opened or created, let us see
"How to open a file?" The file should be opened before reading a file or before
writing into a file. The syntax to open a file for either read/write operation is shown
below: .
#include <stdio.h>
FILE *fp;
where
• fp is a file pointer of type FILE
• filename holds the name of the file to be opened. The filename should be a
valid identifier.
• mode details are provided in section 5.2.2. This informs the library function
about the purpose of opening a file.
If the file pointer fp is not NULL, the necessary data can be accessed from the
specified file. If a file cannot be opened successfully for some reason, the function
retuins NULL. We can use this NULL character to test whether a file has been
successfully opened or Hot using the statement:
if (fp = NULL)
{
printf("Error in opening the file\n");
exit(O);
}
Now, let us see "When to close d how to close a file?" When we no longer need a
file, we should close the file. This is the last operation to be performed on a file. A
file can be closed using close function. This ensures that all buffers are flushed and all
the links to the file are broken. Once the file is closed, to access the file, it has to be
re-opened. If a file is closed successfully, 0 is returned otherwise EOF is returned.
The syntax is shown below:
#include <stdio.h>
void maim)
{
FILE *fp;
fp = fopen("t.c", "rb");
}
~ Systematic Approach to Data Structures using C 5.11
ote: If a program is terminated, all the opened files will be closed automatically.
But, as a programming practice, it is better to close all the opened files once we know
that the file pointers are not required.
Categories of
I/O functions
-C freadr)
fwrite/)
Now, let us discuss each of these one by one.
Working: When the freadt) function is executed, the function reads n number of
items, each of length size bytes (n * size bytes) from the file associated with fp. The
data read from the file is copied into the memory location pointed to by ptr.
Returns
• Number of items successfully read
• If no items have been read or when error has occurred or end-of-file IS
encountered, the function returns 0 (zero).
5.12 Q Binary files
Note: Since this function will not return EOF, we have to check for possible error
using feof'() or ferrort) functions.
Example 5.3.2.1.1: Read a floating point value from the file and store it in variable f.
fread( &f, sizeof(float), 1, fp);
Example 5.3.2.1.2: C function to read 5 integers from the file and store it in array a.
#include <stdio.h>
void main/)
{
inta[lO];
............
rTfl6
III:: . [JJJJ'
[JJJJ III :' [JJJJ<:'
! II WJJ
4*5 = 20 bytes
::
! II :'[[0'I!; I DID'
[[[J III:: <
[JJJJ
< • <
Note: 20 bytes (5 integers) are read from the file from the file pointer fp to the buffer
area identified by a. Here, location a is the place where the data read from the file is
stored. So, a is called buffer area. ote the position of file pointer fp before and after
fread operation.
Q Systematic Approach to Data Structures using C 5.13
ow, let us write a program to read n integers from the binary me. Assume the file
"test.dat" contains the integers stored in binary form,
Example 5.3.2.1.3: Program to read a file of integers from the file "test.dat"
#include<stdio.h>
.
Note: The sequence of operations carried out during the execution of above program
are:
• The file "test.dat" is opened and fp points to the first byte of opened file as shown
in figure.
• Since n = 3, three numbers are read from the file
• The three integers are copied into a[O], a[l] and a[2] respectively.
• After reading the file pointer fp points to 40 as shown in figure.
• Three items 10, 20 and 30 are displayed on the screen.
Now, let us see "How to read the data from a file into a structure?" For this to happen,
let us consider the following structure defmition:
Example 5.3.2.1.4: Read the information of a structure from the file using freadt).
typdef struct
{ Pictorial representation of structure
char name[25];
int marks1;
int marks2;
int marks3; marks3
} STUDENT; ~--+ marks2
'----- .• marks 1
STUDENT a, b[10];
Note: The structure has four fields. The memory is allocated for the structur
variables a and b. The pictorial representation of the memory allocated for variable a
and file contents before freadt) is executed is shown below:
a I__IJJI]]] buffer
Q Systematic Approach to Data Structures using C 5.15
The function reads a single structure from the file and store it in the variable a. The
pictorialrepresentation of the file pointer and variable a after fread operation is
shown below:
a/ OTIIIJ buffer
ote:After reading the first structure from the file, the file pointer fp is automatically
lpointsto next structure.
I Example 5.3.2.1.5:
For the structure definition shown in previous example, "What
willhappen when the following statement is executed?"
Soiution:The function reads 10 structures from the file and store them in the variable
b one after the other starting from b[O], b[l], b[9]. The file pointer fp points to
theeleventh structure in the file.
Now, let us see "What is the syntax of fwrite? How does the function workTThis
function is used to write a block of data into a given file. The prototype of fwritet)
whichis defined in header file "stdio.h" is shown below:
Working: When fwritef) function is executed, the function reads n number of items,
each of length size bytes (i.e., n*size bytes) from the buffer pointed to by ptr and
writes into the file associated with fp. In other words, the data from the buffer which
is pointed by ptr is written into the file associated with fp.
Note: If number of items written is less than n, then an. error has occurred. It is the
responsibility of the programmer to use this value to check whether the write
operation is successful or not.
Example 5.3.2.2.1: Write a floating point value from variable f into file
Example 5.3.2.2.2: C function to write 5 integers stored in array a into the file
#include <stdio.h>
void maint)
{
int a[8] = {10, 20, 30, 40, 50, 60, 70, 80};
·: .. ... .;::..
· .. ..
::
ITIIJ" ITIIJ"
·~ ~;
..
;::
!'...~ ~ ITIIJ"
. ..
.~ ..~~ ......... ···ITIIJ·
::
. ..
tJ.
4*5 = 20 bytes
:· :
III::' ITIIJ'
ITIIJ. III :: ITIIJ'
III :: ITIIJ
, , ,
Note: 20 bytes (5 integers) are read from the buffer a and are written into the file
associatedwith file pointer fp. Note the position of file pointer fp before and after
executingfwrite function.
Now, let us write a program to write n integers into the binary file. Assume the file is
"test.dat" .
#include<stdio.h>
{
printf("Error in creating the file\n"); 6. (EOF)
return;
}
fp test.dat
10 20 30 6. (EOF)
Note:The sequence of operations carried out during the execution of above program
are:
• The file "test.dat" is opened in write mode and fp points to the beginning of file as
shown in figure. Observe that the file is empty.
• Since n = 3, three numbers are read from the buffer and are written into file
associated with file pointer fp.
• After writing the file pointer fp points to end-of-file
• Three items 10, 20 and 30 are displayed on the screen.
Now, let us see "How to write the data from a structure into a file?" For this to
happen, let us consider the following structure definition:
Example 5.3.2.2.4: Write the information of a structure into the file using fwritet).
typdef struct
{ Pictorial representation of structure
char name[25];
int marks1;
int marks2;
int marks3; marks3
} STUDENT; L-_-+ marks2
L.- + marks 1
STUDENT a, b[lO];
Q Systematic Approach to Data Structures using C 5019
Note: The structure has four fields. The memory is allocated for the structure
variables a and b. Assume the structure a is initialized to appropriate values. The
pictorial representation of the memory allocated for variable a and file contents before
fwriteO is executed is shown below:
~/
8.
a I'--_.....IITIIIIJ buffer
Now, let us see what will happen if the following statement is executed.
The function writes the values of structure identified by variable a into the file
associated with file pointer fp. The contents of file after executing fwrite is shown
below:
al . ITIIIIJ buffer
structure
Note: After writing the structure into the file, the file pointer points to end-of-file.
5.20 Q Binary files
Example 5.3.2.2.5: For the structure definition shown in previous example, "What
will happen when the following statement is executed?"
Solution: The function reads 10 structures stored in the variable from b[O],
b[1], b[9] and writes into the file associated with file pointer fp. Finally, the file
pointer fp points to end-of-file.
Now, let us see "What is the need for file status functions? What are the various
functions that inform the status of the file?" During input/output operations, the
following operations may be performed by the programmer:
• Reading beyond end of file marker
• Opening a file which is not existing
• Input invalid file name
• Attempting to write to a write-protected file etc.,
Such read and write errors results in abnormal behavior of the program and so should
not be ignored. If proper error checking is not provided, there may be premature
termination of the program or we may get incorrect output. For this reason Chas been
provided with various file status functions which can detect such errors. The various
file status functions provided in C language are shown below:
File status
functions ~
r feofi)
ferrort)
clearern)
5.3.3.1 feofO
Now, let us see "Why feoft) is used? How to use the function feofi)?" After opening
the file in read mode, the contents of the file can be read. During, this process we may
encounter end of file. This function is used to detect whether end of file is reached.
The syntax to check whether end of file is reached or not is shown below:
Q Systematic Approach to Data Structures using C 5.21
#inc1ude <stdio.h>
void mainO
{
FILE *fp;
r-~~~----------------l
I if (feof(fp)) Syntax I
I { I
: printf("End of file is reached\n"); I
I ~~~; :
I}
L . _I
where
• fp is a file pointer of type FILE
• filename holds the name of the file to be opened
• mode details are provided in section 5.2.2. This informs the library function
about the purpose of opening a file.
Return values The function accepts file pointer fp as a parameter and following
valuesare returned.
• true if end of file is detected (true - nonzero value)
• false if end of file is not detected. (false - zero value)
5.3.3.2 ferrur()
Now, let us see "Why ferrort) is used? How to use the function ferron)?" A file can
be opened in read or write mode. During reading from a file or writing into a file
following errors may occur:
• Modifying read only file
• Trying to read a file which is opened in write mode and so on.
5.22 Q Binary files
Th~ macro ferron) is used to detect an error that occurred during read or write
operations on a file. The syntax to check whether any error has occurred or not is
shown below:
#include <stdio.h>
void maint)
{
FILE *fp;
int c;
fp = fopen("test", "w");
c = getc(fp);
,----------------------1
I if (ferror(fp)) Syntax I
I { I
: printf("Error in reading the file\n"); :
I return; I
IL } JI
Note: Here, file is opened in write mode and using the function getct), we are trying
to read from a file. This is an error and this error is detected by the macro ferron) and
displays the message "Error in reading the file". The prototype declaration
Return values The function accepts file pointer fp as a parameter and following
values are returned.
• true if an error is detected. (true - nonzero value)
• false if an error is not detected. (false - zero value)
Q Systematic Approach to Data Structures using C 5.23
5.3.3.3 clearerr'()
Now, let us see "What is the significance of the function clearert)?" During reading
and writing of a file an error may occur. Once an error occurs, the subsequent calls to
ferron) returns true until the error status is cleared. So, to clear the error status the
function clearem) is used. The prototype of this function is:
The various file status functions provided in C language are shown below:
File Positioning
functions
-E rewindt)
ftellO
fseekt)
ow, let us see "What is the need for the function rewindt)?" Explain. The function
rewind/) is used to set the file pointer to the beginning of the file. When the data is
written into the file, the file pointer points to the end of file as shown in figure below:
I EOF
'+'
/).
I File contents
For this to happen, it is required that file should have' been opened in write mode.
Now, to process the entire file, we may have to start reading from the beginning of the
5.24 Q Binary files
file. In such situations, we have to close the file and open it in read mode. Instead, we
can use the function rewindt) which will move the file pointer to the beginning of the
file without closing and re-opening the file. After executing the function rewindt), the
file pointer points to the beginning of the file as shown below:
IEOF
Ir----------, '+'
File contents I fj,
Earlier we wrote the data and now we are reading the data i.e., read and write
operations are being performed on file. So, always the file should be opened in
read/write mode using the mode "r+" or "rb+". The syntax is shown below:
rewind(fp);
Now, let us see "What is the need for the function ftellf)?" Explain. The function
ftellt) gives the current position of the file pointer from the beginning of the file. So, it
always gives the number of bytes from the beginning of the file relative to zero. The
syntax is shown below:
ftell(fp)
where fp is the file pointer. The prototype is defined in header file "stdio.h" as
I ,I fj,
fp fp
Fig. 5.3.4.a Fig. 5.3.4.b
.!;l Systematic Approach to Data Structures using C 5.25
Notethat with respect to figure 5.3.4.a, ftell(fp) returns 0 and with respect to figure
5.3.4.b,the function ftell(fp) returns 3.
Now, let us see "What is the need for the function fseekf)?" Explain. The function
fseekt) is used to set the file pointer at the specified position. In general, it is used to
move the file pointer to the desired position within the file. The file pointer can be
movedbackwards or forward any number of bytes. The syntax is shown below:
Syntax
where
• fp is a file pointer.
• offset can take positive, negative or zero value and specifies the number of
bytes to be moved from the location specified by start-point.
• startpoint can take one of the values as shown below:
If offset is positive, the file pointer fp moves forwards. If it is negative, the file
pointer fp moves backwards.
5.26 Q Binary files
Example 5.3.4.3.1: Various methods of moving the file pointer fp within the file
1* Move file pointer to end of the file *1 I file pointer is moved to end
fseektfp, OL, 2);
: I MONALIKAN
I EOF
I fp
1* Obtain the current position of file pointer *1 I
length = ftelltfp); I length = 8
return length; I
I
I
Q Systematic Approach to Data Structures using C 5.27
void main(void)
{
FILE *fp;
char file_name[20];
fp = fopen(file_name,"r");
if(fp = NULL)
{
printf("Error in opening the file\n");
exit(O);
}
fcIose(fp );
Note: What is the difference between rewind(fp) and fseek(fp,OL,O)? Both the
functionsmoves the file pointer to the beginning of the file. So, Functionality is same,
butsyntax and number of parameters are different.
So far we have seen various file functions that are used to access the contents or the
file. There are some functions that operate on whole files instead of contents. These
functions use operating system calls and are called system file operations. Now, let us
see "What are system file operations?" The various system file operations that .uv
supported are shown below:
remove a file
System. file operations -
f
Now, let us discuss each of these one by one.
rename a file
create a temporary file
5.28 Q Binary files
Now, let us see "How to remove or delete a file?" The function removet) is used to
delete or remove a file. The prototype declaration is shown below:
This declaration is available in the header file "stdio.h". The parameter filename isa
pointer to the name of the file. "What does this function return?"
#include <stdio.h>
void main(void)
{
char file_name[20];
int status;
status = remove(file_name);
if (status != 0)
{
printf("Error: File can not be deleted\n");
return;
}
Now, let us see "How to rename a file?" The function renamet) is used to rename a
file. The prototype declaration is shown below:' ,
This declaration is available in the header file "stdio.h". After executing the function,
old_filename is renamed as new filename and old file name does not exist "What
does this function return?"
#include <stdio.h>
void main(void)
{
char 0Id_file_name[20];
char new file name[20];
int status;
if (status != 0)
{
printf("Error: File can not be renamed\n");
return;
}
Now, let us see "What is the purpose of using tmpfilet) function?" This function is
used to create a temporary binary file in "wb+" mode. So, the user can read or write
from this temporary file. This file is created temporarily because; it is automatically
deleted when the program is terminated. The prototype declaration is,shown below:
FILE *tmpfileO;
This declaration is defined in the header file "stdio.h "What does this function
return?"
To create a temporary file, we must define a file pointer and then open it as shown
below:
FILE *fp;
fp = tempfilet);
1
The various file handling functions that are associated with text files are shown
below:
Now, let us see "Wllat is the use of getct) and putct) functions? Explain with syntax"
The function getct) is a macro defined in stdio.h that gets one character from the file
pointer specified. The function putcf) is also a macro that outputs a character to a file
stream specified using file pointer. The syntax of getct) and putct) functions are
shown below:
Q Systematic Approach to Data Structures using C 5.31
Syntax Syntax
Example5.4.1.1:Program to copy one file to other file using getct) and putct)
#include<stdio.h>
#include<process.h>
voidmaim)
I
FILE *fpl; /* Points to the input file */
FILE *fp2; /* Points to the output file */
char file 1[10]; /* Name of the input file */
char file_2[10]; /* Name of the output file */
int ch; /* used to store character read */
printf("Enter the input file\n");
scanf("%s" ,file_1);
if ( fp2 = NULL )
{
printf("Opening outfile failed\n");
exit(O);
}
putc( ch, fp2); /* write each character read into ou put file */
}
Note: The above program is self-explanatory with proper comments and the reader
should type the program, execute and understand how the program works.
#include <stdio.h>
#include <process.h>
void maim)
{
FILE *fp 1; /* Pointer to first input tile */
FILE *fp2' /* Pointer to second il}l2Ut-.tHe---*I
FILE *fp3: I P~UtPutfile */
--------~-
char file 1[20]; /* First input file name */
char file2[20]; i* Second input file name */
char file3[20]; /* Output file name */
char ch; /* Holds the character from both input files */
fclose(fp 1);
fclose(fp2);
fclose(fp3);
5.34 Q Binary files
Example 5.4.1.3: Program for counting the characters, blanks, tabs and lines in file.
#include <stdio.h>
void maim)
{
FILE *fp; 1* Pointer to the input file name *1
char file_name[20]; 1* Input file name *1
char ch; 1* Holds the character read from file *1
int char_count = 0; 1* Holds count of all characters typed *1
int blank count = 0; 1* Holds count of blank characters *1
int tab_count = 0; 1* Holds count of only tabs *1
int line_count = 0; 1* Holds count of lines *1
printf("Enter the file name\n");
scanf("%s" ,file_name);
1* Open file only in read mode *1
if ( (fp = fopentfile jiame,"r") = NULL)
{
printf("Error in opening the file\n");
exit(O);
}
1* read each character till end of file encountered *1
while ( ( ch = getc(fp) ) != EOF)
{
1* Update character count *1
if ( ch = ' , ) blank _count++; 1* Update blank count *1
if ( ch = '\n' ) line_count++; 1* Update line count *1
if ( ch = '\t' ) tab_count++; 1* Update tab count *1
}
fclose(fp);
printf("Number of characters = %d\n", char_count);
printf("Number of tabs = %d\n", tab_count);
printf("Number oflines = %d\n", line_count);
printf("Number of blanks = %d\n", blank_count);
}
Q Systematic Approach to Data Structures using C 5.35
5.4.2 fscanf'(), fprintft)
Now, let us see "What is the use of fscanfi) function? Explain with syntax"
The function of fscanf and scanf are exactly same. But, instead of getting the input
from the keyboard, we get the input from the file specified. Because input is read
from the file, extra parameter file pointer has to be passed as the parameter. Rest of
the functionality of fscanf remains same as scanf. The syntax of fscanfl) is shown
below:
fscanf(fp, "control string", list);
where
• fp is a file pointer
• format string and list have the same meaning as in scanfi) i.e., the variables
specified in the list will take the values from the file specified by fp using the
specifications provided in format string.
Example 5.4.2.1: Explain what will happen if the following statement is executed.
Solution: After executing fscanf, the values for the variables id, name and salary are
obtained from the file associated with file pointer fp. This function returns the number
of items that are successfully read from the file.
Now, let us see "What is the use of fprintfi) function? Explain with syntax"
The function of fprintf and printf are exactly same. But, instead of displaying the
resulton the screen, the result is sent to the file. Since file is used, extra parameter file
pointer has to be passed as parameter. Rest of the functionality of fprintf remains
sameas printf. The syntax of fprintfi) is shown below:
fprintf'(fp, "format string", list);
where
• fp is a file pointer associated with a file that has been opened for writing.
• format string and list have the same meaning as in printf() function i.e., the
values of the variables specified in the list will be written into the file associated
with file pointer fp using the specifications provided in format string.
5.36 Q Binary files
Example 5.4.2.2: Explain what will happen if the following statement is executed.
Solution: After executing fprintf, the values of the variables id, name and salary are
'written into the file associated with file pointer fp. This function returns the number
of items that are successfully written into the file.
Solution: The above table can be represented using structure definition as shown b
elow:
typedef struct
{
int usn;
char name[20];
int marks l ;
int marks2;
int marks3;
} STUDENT;
Example 5.4.2.4: C function to search for key_usn in the table shown in example
5.&2.3.
Example 5.4.2.5: C function to append the student record at the end of file
for the structure shown in example 5.6.2.3. -
Example 5.4.2.6: C function to display the contents of the file for the structure shown
in example 5.6.2.3.
if (feof(fp)) break;
Example 5.4.2.7: The complete C ·program to create a sequential file with at least 5
records, each record having the following structure:
where USN stands for University Seat Number, Marksl, Marks2 and Marks3 are the
marks scored by a student in subjectl , subject2 and subject3 respectively. Let us write
necessary functions to
• Display all the records in the file
• To search for a specific record based on USN. Display suitable message
#include <stdio.h>
#include <process.h>
#include <string.h>
typedef struct
{
int usn;
char name[20];
int marks1;
int marks2;
int marks3;
} STUDENT;
~ Systematic Approach to Data Structures using C 5.39
1* Include: Example 5.4.2.4: C function to search for the key USN *1
for (00)
, "
{
printf(" 1:Insert Record 2:Search record\n");
printf("3:Display record 4:Quit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch( choice)
{
case 1:
fp = fopen(fname,"a+");
if (fp = NULL)
{
printf("Fopen failed\n");
break;
}
fclose(fp);
break;
5.40 Q Binary files
case 2:
fp = fopen(fuame,"r");
if (fp = NULL)
{
printf("File open failed\n");
break;
}
printf("Enter USN:");
scanf("%d" ,&key _usn);
if (flag = 0)
printf("Unsuccessful search \n ");
else
printf("Successful search\n");
fclose(fp);
break;
case 3:
fp = fopen(fuame,"r");
if (fp = NULL)
{
printf("File opened failed\n");
break;
}
display Jecords(fp);
fclose(fp );
break;
default:
exit(O);
}
}
}
,!;lSystematic Approach to Data Structures using C 5.41
Note: Even though the program is lengthy, the program is straightforwardand easier
to understand. By looking at the comments the reader is required to understand the
logic.
monitor
Command prompt
The above function ,COPY accepts the file Tl.C and copies the contents, of Tl.C to
the T2.C. In the command prompt, if the .user types "COpy ,Tl.C T2.C" the main
function of COpy program accepts three parameters namely COPY, Tl.C and T2.C.
These parameters are called command line arguments ..Now, we shall see "What are
command line arguments?"
}
5.4~ Q Binary files
where
• argc is an integer value. Here, argc means argument count
• argv is an array of strings. Here, argv means argument vector. Those strings that
are part of the command prompt are copied into argv[O], argv[l] and so on.
Example 5.5.1: Argument vector and argument count with respect to the command:
argv[O]~ COPY
argv[l]~ Tl.C
argv[2]~ T2.C
Since there are three strings at the command line prompt, integer variable argc has the
value 3. The value for the variable argc is not explicitly passed as the parameter and
argv[O] will always have the first parameter i.e, program name. In our example,
argv[O] will have the string "COPY".
Now, let us write a program to display the contents of file. The file if specified in the
command line has to be opened and the contents have to be displayed. If file is not
specified in the command line, the user should be prompted for the file and that file
should be opened and displayed. The complete program is shown below
#include <stdio.h>
#include <process.h>
#include <string.h>
if (argc = 1)
{
printf("Enter the file name:"); /* No command line file name */
scanf("%s" ,fname);
}
else
{
strcpy(fname, argv[l]); /* Command line file exists */
}
fp = fopen(fname, "r"); .
if (fp = NULL)
{
printf("File opening error\n");
exit/O);
}
Note: Let us assume, the file name of the above program is "disp.c". Before
executing this program make sure that the file by name "Hello" exists in the current
directory from where we are running the executable file "disp". Also make sure that
some text is stored in the file "Hello". Use any editor, create a file called "Hello" and
enterthe following text:
Hello how are you
This is the first program using command line arguments
Good luck
Have Fun
Finally save the program in the file name "Hello". Now, compile and execute the
program as shown below:
5.44 Q Binary files
Output
..
I
C:\>disp hello
The contents of the file are:
Oufput
C:\>disp
Enter the file name: Hello
The contents of the file are:
Hello how are you
This is the first program using command line arguments
Good luck
Have Fun
Note: In the first output argv[O] corresponds to disp and argv[ 1] corresponds to the
string "Hello". In the second output argv[l] is NULL. In this ,program, in the
beginning of the function main we are checking whether a file has been specified in
the command line. If file is not specified, the value of argc will be 1 and so the user is
prompted to enter the file name during run time. But, if the value of argc is greater
than 1, the control comes to else statement and the file specified in the command line
is copied to fname which will be used for opening. Rest of the statements in the
program are self-explanatory. .
5.5.2 Obtain text and create file (use only command line parameters)
Let lis consider another program which accepts a file name followed by a text only
through command line parameters. The text appearing in the command line should be
stored in a file which is also passed as a command line parameter. The corresponding
C program is shown below:
Q Systematic A roach to Data Structures usin C 5.45
Example 5.5..2: C Program to accept a file name and text through command line
arguments and create a file with the text
#inc1ude<stdio.h>
#inc1ude<process.h>
void main(int argc, char *argv[])
{
FILE *fp; /* File pointer used to display a file */
int 1; /* To access command line parameters */
char sp-[30]; /* Temporary storage */
if (argc = 1)
{
printf("File name missing in the command iine\n");
exit(O);
}
if (argc = 2)
.
{
printf("File name should be followed by some text\n");
exit(O);
}
/*Command line has file name and text */
/* So, create and open the file */
fp = fopen(argv[l], "w");
if (fp = NULL)
{
printf("The file can not be opened for writing\n");
exit(O);
}
/* Write text in command line into file */
for (i = 2; i < argc; i++)
{
fprintf(fp,"%s ",argv[i]); /* Note: A space after %s is required */
}
fclose(fp);
II
5.46 Q Binary files
5.5.3 Copy from one file to other file (Implementing copy command)
Let us write a C program to copy contents of a file to another file accepting the file
names as the command line arguments.
6.1 Introduction
The digital computers are electronic devices that can transmit, store and manipulate
information. Information is obtained from data and it is the result of processing,
manipulatingand organizing the data.The various types of data that are processed by
the computer are numeric or character (address, names etc.,) in nature. In the coming
chapters, we discuss extensively on various types of data structures. First, we shall see
"What is data structure? What are the various types of data structures?"
Double
Pointers
Data Arrays
structures
Stacks
Linear
Queues
Non Linked lists
primitive
Definition: The Primitive Data structures are data structures that can be manipulated
directly by machine instructions.
For example, the integers, floating point numbers (real), characters, pointers
etc., are some of the primitive data structures. In C language, the different primitive
data structures are defined using the data types such as int, float, char and double.
Definition: The Non-primitive data structures are data structures that cannot be
manipulated directly by machine instructions.
For example, arrays, structures, stacks, queues, linked lists, files etc., are some
of the non- primitive data structures. The non-primitive data structures are classified
into linear data structures and non-linear data structures. The data structures that show
the relationship of logical adjacency between the elements are called linear data
structures. Otherwise, they are called non-linear data structures .
•
6.2 Stacks
We have seen that, normally all the plates in a hotel/canteen are placed one above the
other. The plates are inserted from the top only. If a plate is required, the top most
plate is selected. This process of inserting the plates at the top and removing the plates
from the top is called stacking of plates. Let us see how stacks are used in the field of
computer science. Before proceeding further, let us see "What is a stack? What are
the various operations that can be performed on stacks?"
!
Definition: A stack is a special type of data structure (an ordered collection of items)
'where elements are inserted from one end and elements are deleted from the same
end. \'fhis position from where elements are inserted and from where elements are
deleted is called top of the stack. losing this approach, the last element inserted is the
first element to be deleted out, and hence, stack is also called Last In First Out
(LIFO) data structure. The various operations that can be performed on stacks are
shown below:
Definition: Inserting an element into the stack is called Only one item
is inserted at a time and item has to be inserted only from top of the stack.
Example 6.2.1.1: Stack contents after inserting 4 items 30, 20, 25 and 10 one after
the other with a STACK SIZE 4.
STACK SIZE = 4
~3
~El ~El~~~
oEl ~0m3 000
to
~t 2 25
3~
1
o
20
30
2
1
o
top
~ -I S S S s s
Empty stack insert 30 insert 20 insert 25 insert 10
When items are being inserted, we may get stack overflow. Now, let us see "What is
stack overflow?"
Definition: When elements are being inserted, there is a possibility of stack being
full. Once the stack is full, it is not possible to insert any element. Trying to insert an
element, even when the stack is full results in.overflow of stack.
For example, consider the stack shown in figure 6.1 with STACK_SIZE 4. We
can insert at the most four elements. After inserting 30, 20, 25 and 10 there is no
space to insert any item. Then we say .that stack is full. This condition is called
overflow of stack.
Now, let us see "How to implement push operation using arrays (static allocation
technique)?"
Design: Before inserting any element, we should ask the question "Where and how
the item has to be inserted?" If we know the answer for this question we have the
6.4 Q Stacks
push function ready. So, let us consider the situation where two elements 30 end 20
are already inserted into the stack as shown in figure 6.2.a. with top = 1.
3 ~3
~2 ~2
t 3~ 25 2
to 2
~ 3~
1 20 1 1 20 1
o 30 0 o 30 0
-1 S -1 S -1 S -1 S
\ .
Suppose, we have to insert an element say item. Now, let us ask "Where this item has
to be inserted?" We know that it has to be inserted at top = 2. For this to happen, we
have to increment top.by 1 (Fig. 6.2.b). This is achieved using the statement:
Then we ask "How item has to be inserted at top position?" This is achieved by
copying item to s[top] (Fig 6.2.c) using the following statement:
But, as we insert an item, we must ask "When we can not insert item into the stack?"
We can not insert any item when top has already reached STACK_SIZE - 1(Fig.
6.2.d) . In such situation, we have to display appropriate message as shown below:
void puslu)
{
/* Check for overflow of stack */
if (top = STACK_SIZE -1)
{
printf("Stack overflow\n");
return;
}
top = top + 1;
/* Increment top by 1 */ }
I
s[++top] = item;
s[top] = item; /* Insert into stack */
Note: The array s, the variable top and the variable item are global variables and
should be declared before all the functions. As far as possible let us avoid the usage of
global variables in this book.
Let us re-write the above code by passing parameters. It is clear from the above code
that as the item is inserted, the contents of the stack identified by s and the index top
pointing to topmost element are changed and so they should be passed as parameters
(pass by reference) as shown below:
Note: By comparing functions shown in 6.2.1.3 and 6.2.1.4 note that body of the
function has not been changed. But, the type of formal parameters changes.
Example 6.2.2.1: Performing pop operation when stack already contains 30, 20, 25,
and 10.
to
3 3 3 3
2 2 2
1 ~ ~ 20 1
top 1
o o 30 ~O 30 top 0
1 -I -I -I -+-1
Stack full After After After
deleting 25 deleting 20 deleting 30
(stack empty)
Figure 6.3 Sequence of Deletion operations
Q Systematic Approach to Data Structures using C - 6.7
The items can be deleted one by one as shown in figure 6.3.The contents of stack and
the top which contains the position of topmost elements are also shown. When items
are being deleted, we may get stack underflow. Now, let us see "What is stack
underflow?"
Definition: When elements are being deleted, there is a possibility of stack being
empty. When stack is empty, it is not possible to delete any item. Trying to delete an
element from an empty stack results in stack underflow.
For example, in the figure 6.3, after deleting 10, 25, 20 and 30 there are no
elements in the stack and stack is empty. Deleting an element from the stack results in
stack underflow.
Now, let us see "How to implement pop operation using arrays (static allocation
technique)?"
Design: Let us delete an item from the top of the stack. This can be achieved by
accessing the top element s[top] as shown below:
The above two statements can also be written using single statement as shown below:
Each time, the item is deleted, top is decremented and finally, when the stack is
empty the value of top will be -1 and so, it is not possible to delete any item from the
stack. Hence, the above statement has to be executed only if stack is not empty and
the code to delete an item from stack can be written as shown below:
Now, let us write the above function without using global variables and by passing
appropriate parameters. We know that to access the top item, we need the index top
and the stack s. So, we have to pass top and s as the parameters. As the value of top
changes every time the item is deleted, top can be used as a pointer variable. The
complete C function (by passing parameters) is shown below:
The following function shows how to delete a character item from the stack.
! ~.'
In the display procedure, if the stack already has some items, all those items are
displayed one after the other. If no items are present, the appropriate error message is
displayed. Let us design the display function.
Design: Assume that the stack contains three elements as shown below:
~~
1
o
-1 S
Usually, the contents of the stack are displayed from the bottom to top. So, first item
to be displayed is 30, next item to be displayed is 20 and final item to be displayed is
25. This can be achieved using the following statements:
Design I, Output
printf("%d\n", illlQ[1);); I 30
printf("%d\n", 20
printf("%d\n", 2); I 25
ote: Please note how the value of i change from 0 to top. Now, the code takes the
following form:
But, the above statement should not be executed when stack is empty i.e., when top
takes the value .,...1. So, the above code can be modified to include this error condition
andcan be written as shown below:
6.10 Q Stacks
Example 6.2.3.1: C function to display the contents of stack (using global variables)
void displayt)
{
int i;
1* If stack is empty *1
if (top = -1)
{
printf("Stack is empty\n");
return;
}
1* Display contents of stack *1
printf("Contents of the stack\n");
for (i = 0; i <= top; i++)
{
printf("%d\n", s[i]);
}
}
By passing parameters, the function above function can be written as shown below:
Example 6.2.3.2: C function to display contents of the stack (by passing parameters)
void display(int top, int sm
{
int i;
1* Is stack empty *1
if(top = -1)
{
prtntf(t'Stack is empty\n");
return;
}
1* Display contents of stack*l·
printf("Contents of the stack\n");
1 for (i = 0; i <= top; i++)
{
printf("%d\n", s[i]);
}
}
~ Systematic Approach to Data Structures using C - 6.11
In the previous sections we have seen how the stacks can be implemented using
global variables and by passing parameters. The complete program to perform
operations such as push, pop and display using global variables is shown below:
Example 6.2.9: C Program to implement stack using arrays (Using global variables)
#include <stdio.h>
#inelude <process.h>
1* Global variables */
int top; /* index to hold the position of the top most item */
int s[10]; /* Holds the stack items */
int item; /* Item to be inserted into the stack */
void mainO
{
int item; /* Item to be inserted */
int item_deleted; /* Item to be deleted from the stack */
int choice; /* user choice for push, pop and display */
for (;;)
{
prtntf(" 1:Push 2:Pop\nlt);
printf(1t3 :Display 4:Exit\nIt);
printf'(vlinter the choice'n");
scallf(lt%dlt ,&choice);
6.12 Q Stacks
Continued .....
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
pusht);
break;
case 2:
item __deleted = popt);
if (item_deleted = 0)
printf("Stack is empty\n");
else
printf("ltem deleted = %d\n",item_deleted);
break;
case 3:
displayt);
break;
default:
exit(O);
}
}
}
The program to perform operations such as push, pop and display by passing
parameters is shown below:
Example 6.2.10: C Program to implement stack using arrays (by Passing parameters)
.1
#include <stdio.h>
#include <process.h>
switch( choice)
{
case 1:
printf("Enter the item to.be inserted\n");
scanf("%d",&item); .
push(item,&top,s );
break;
case 2:
item_deleted = pop(&top,s);
if (itemdeleted = 0)
printf("Stack is empty\n");
else
printf("Item deleted = %d\n",item_deleted);
break;
6.14 Q Stacks
Continued .....
case 3:
display(top,s);
break;
default:
exit(O);
}
}
}
• Recursion: A function which calls itself is called recursive function. Some of the
problems such as tower of Hanoi, problems involving tree manipulations etc., can
be implemented very efficiently using recursion. It is. a very important facility
available in variety of programming languages such as C, c++ etc.,
• Other applications: There are so many other applications where stacks can be
used: For example, to find whether the string is a palindrome, to check whether a
given expression is valid or not and so on.
1
Now, we shall discuss these applications in detail in the subsequent sections.
First, let us see "What is an expression? What are the various types of expressions?"
Q Systematic Approach to Data Structures using C - 6.15
Definition: The sequence of operators and operands that reduces to a single value
after evaluation is called an expression. The operands consists of constants and
variables whereas the operators consists of symbols such as +, -, *, / and so on. The
operators indicate the operation to be performed on the operands specified. The
expressions can be represented as shown below:
Re resentation
fe . .
o expressions -E Infix expression
.
Postfix expression
Prefix expression
In the expression, a + b
operand l ~ t 4 operand2
operator
Note: Since the operator is in between two operands, it is called infix expression.
Now, let us see "What is postfix expression?"
While evaluating or converting an expression into other forms •.we should know the
precedence of operators and the associativity of operators. So, let us see "What is
precedence of operators?"
Definition: We know that while evaluating the expressions, some expressions are
given precedence over other expression and are evaluated first and some are evaluated
later. These rules that determine the order in which different operators are evaluated
are called precedence rules or precedence of operators.
6*~-5 Brackets have the highest precedence and are evaluate first
Normally we associate values to determine the order in which the operators have to
evaluate. Highest precedence operators will have the highest value and lowest
precedence operators will have the least value. The table below shows arithmetic
operators along with priority values
ote: The symbol '$' or '1\' is considered as the operator to perform exponentiation
and should be given first precedence since it has higher priority value. The addition or
subtraction is given the least precedence since it has least priority value as shown in
above table.
TOW, "What if different operators have the same precedence?" During evaluation, if
two or more operators have the same precedence, then precedence rules are not
applicable. Instead, we go for associativity of the operators. Now, we shall see "What
is associativity?
Definition: The order in which the operators with same precedence are evaluated in
an expression is called associativity of the operator. In such case, precedence rules are
not considered. .
Example 6.4.1.2: What is the result of8 + 4 + 3? All of us evaluate the expression as
shown below:
8 + 4 + 3
"--y-J [First add 8 and 4 to get 12]
12 + 3
'--y--' [Next add 12 and 3 to get 15]
15
Observation: In the above expression, same operator "+" is used twice and hence
both operators have the same priority and the precedence rules are not applicable.
Then the question is "How to evaluate?" Normally, we do the evaluation as shown
below:
First we compute 8 + 4 to get 12
Next we compute 12 + 3 to get 15 thus moving from Left to Right.
Since evaluation is done from left to right one after the other, we say that the operator
"+" is left-to-right associative (also called left associative).
Definition: In an expression, if there are two or more operators having the same
priority and are evaluated from left-to-right, then the operators are called left
associative operators (Left to Right associative operators). We normally denote using
L ~ R. The process of evaluating from left to right is called left associativity.
6.18 Q Stacks
not 64. So, to get the answer 512, the computer has to evaluate the expression 2$3$2
as shown below:
Observation: In the above expression, same operator "$" is used twice and both
operators have the same priority and the precedence rules are not applicable. Then the
question is "How to evaluate?" Normally, we do the evaluation as shown below:
First we compute 3$2 to get 9
Next we compute 2$9 to get 512 thus moving from right to left.
Since evaluation is done from right to left one after the other, we say that.the operator
"S" is right-to-left associative (also called right associative).
Definition: In an expression, if there are two or more operators having the same
priority and if they are evaluated from right-to-left, then the operators are called right
associative operators.
For example, $ (representing exponentiation operator Note: This is not C
language operator) is a right associative operator
Now, we shall see, "How the precedence of operators and associativity of operators
are used to convert the expressions from infix to postfix or from infix to prefix?"
Note: "Why to convert from infix to postfix or infix to prefix?" We normally evaluate
the expressions by considering infix expressions. But, evaluating postfix or prefix
expressions is much easier and efficient. Also, while evaluating postfix or prefix
expressions, we need not worry about the precedence of operators and associativity of
operators where as .while evaluating infix expressions, we should know the
precedence of operators and whether the operators are left associative or right
associative,
Q Systematic Approach to Data Structures using C - 6.19
Tl T,=BC-
T2 = T} D *
T3=A T2+
T4= T3 E 1\
T4F+
T3 E 1\ F+ {Replacing T4 by T3 E 1\)
AT2+EI\F+ (Replacing T3 by A T2 +)
A r, D*+EI\F+ (Replacing T 2 by T 1 D *)
ABC- D*+EI\F+ (Replacing T 1 by B C -)
TI T\ =Y Z$
T2 = X 'r, $
T3 = P Q /
Ts T3 +
T4 N + P Q / (Replacing 'r, by T4 N + and T3 by P Q /
T2 M - N + P Q / (Replacing T4 by T2 M-)
X TI $ M - N + P Q / (Replacing T2 by X TI $)
XYZ$$M-N + PQ / (Replacing T I by Y Z $)
Hence, equivalent postfix expression is X Y Z $ $ M - N + P Q /
On similar lines, we can convert all infix expressions into their equivalent postfix
expressions. Now, let us discuss "How to write a program to convert a given infix
expression to its equivalent postfix expression?"
1
Design: Let us use two precedence functions F and G. The function F contains the
precedence values of symbols on top of the stack and function G contains the
precedence values of symbols in the input string. The precedence values associated
with these two functions F and G are shown ill following table.
Q Systematic Approach to Data Structures using C - 6.21
By looking at above table, we can easily write two functions F and G that return the
precedence based on whether the symbol is on top of the stack or it is an input
symbol as shown below:
Example 6.4.2.3: C function which returns stack and input precedence values
1* Stack precedence function F *f f* Input precedence function G *f
int F(char symbol) int G( char symbol)
{ {
switch(symbol) switch( symbol)
{ {
case '+': case '+':
case '-': return 2; case '-': return 1;
Note: Observe the following points from the above table/function with respect to
associativity.
• If an operator is left associative, stack precedence value is greater than input
precedence value.
• If an operator is right associative, stack precedence value is less than the input
precedence value.
Procedure: General procedure to convert from infix to postfix form is shown below:'
,
• As long as the precedence value of the symbol on top of the stack is greater than
the precedence value of the current input symbol, pop an item from the stack and
place it in the postfix expression. The code for this statement can be of the form:
if (F(s[top]) != G(symbol) )
s[++top] = symbol; 1* Push symbol on the stack *1
l else
top--; 1* Drop an element from stack *1
Note: These two steps have to be performed for each input symbol in the infix
expression. _
Q Systematic Approach to Data Structures using C - 6.23
;0
Note: Before conversion starts, stack is empty denoted by symbol '#' on the stack and
output is empty. The formal version of the algorithm can be written as:
if (F(s[topD != Gtsymbolj )
I Infix to postfix conversion I
sf ++top] = SYmbol;
else
Output configuration
top--;
} . Stack Input Output
# empty ABC-D*+
Example 6.4.2.4: C function to convert from infix expression into postfix expression
if ( F(s[top]) != G(symbol) )
s[ ++top] = symbol; 1* push the input symbol *1
else
top--; 1* discard '(' from stack *1
}
postfix[j] = '\0'; 1* Attach NULL char .at the end to frame a string *1
}
#inclllde <stdio.h>
#include <string.h>
void maint)
{
char infix[20];
char postfix[20];
I Input and output
printf("Enter a valid infix expression\n");
I Enter a valid infix expression
scanf("%s" ,infix);
I (a+b)* c-(d-e))" (f+g)
/* Convert infix to postfix expression */ I The postfix expression is
infix yostfix(infix,postfix); I ab+c=de=fg+?
Let us trace the above program for the given expression: ( A + ( B - C ) *D)
Example 6.4.2.6: The complete trace of the program for the expression:
(A+(B-C)*D)
# # ( (-1<9)Push(
#( ( A (0 < 7) Push A
# (A A + (8) I ) Pop A A
#( ( (0 < I ) Push +
#(+ + ( ( 2 < 9 ) Push ( A
#(+( ( B ( 0 < 7) Push B A
# (+ (B B (8) I ) Pop B AB
II- (+ ( ( ( 0 < I ) Push -
# (+ ( - C (2 < 7) Push C AB
#(+(-C C ( 8> 0) Pop C ABC
# (+ (- (2) 0) Pop - A BC-
#(+( (0 = 0) Pop (
don't place in postfix. expr.
#(+ + * (2 < 3 ) Push * A B C-
# (+ * * D (4 < 7) Push D
#( + * D D ) (8) 0) Pop D ABC-D
# (+ * >I<
(4) 0) Pop * ABC-D*
#t+ + (2) 0) Pop + ABC-D*+
#( (0 = 0) Pop
(don't place in postfix exp.)
# # ABC-D*+
6.26 Q Stacks
Example 6.4.3.1: Obtain the prefix expression for « A -i- (B:'- C) * D) " E +F )
Tl TI=-BC
T4 = "T3 E
+T4 F
+ " T3 E F (Replacing T 4 by /\,T 3 E)
+ /\, + A T2 E F (Replacing T3 by + A T2)
+ /\, + A * T, D E F (Replacing T2 by * T, D )
+/\'+A*-BCD EF (Replacing T I by - B C)
Example 6.4.3.2: Obtain the prefix expression for X" Y " Z - M + +P/Q
X$T1-M+N+P/Q
Ly-J
x $ T 1 has highest precedence
T2 T2 = $ X 'r,
P I Q has the highest precedence
T3 = / P Q
On similar lines, we can convert all infix expressions into their equivalent prefix
expressions. Now, let us discuss "How to write a program to convert a given infix
expression to its equivalent prefix expression?"
Design: To obtain a prefix expression from infix expression, the procedure that is
followed while converting from infix to postfix expression is used with the following
modifications:
• The precedence functions are different from that of the precedence functions
shown in table 6.1. The precedence functions used to obtain a prefix expression
are shown below: .
6.28 Q Stacks
+.- 1 2 Left
"*, I 3 4 Left
$ or" 6 5 Ri{!ht
operands 8 7 Left
( - 0 Left
) 0 9 -
# -1 - -
Table 6.2 Precedence values of symbols in the stack and input
• Reverse the infix expression .and follow the same procedure that we have used to
convert from infix to postfix
• The prefix expression is obtained by reversing the result.
Example 6.4.3.3: C function which returns stack and input precedence values
/* Stack precedence function F *1 1* Input precedence function G *1
int F(char symbol) int G(char symbol)
{ {
switch( symbol) switch( symbol)
{ {
case '+': case '+':
case '-': return 1; case '-': return 2;
Note. Observe the following points from the above table/function with respect to
associativity.
• If an operator is left associative, stack precedence value is less than input
precedence value.
• If an operator is right associative, stack precedence value is greater than the input
precedence value.
Procedure: General procedure to convert from infix. to prefix form is shown below:
• As long as the precedence value of the symbol on top of the stack is greater than
the precedence value of the current input symbol, pop an item from the stack and
place it in the prefix expression. The code for this statement can be of the form:
• Once the condition in while-loop is failed, if the precedence of the symbol on top
of the stack is not equal to the precedence value of the current input symbol, push
the current symbol on to the stack. Otherwise, delete an item from the stack but do
not place it in the prefix expression. The code for this statement can be of the form
if (F(s[top]) != G(symbol) )
s[++top] = symbol; /* Push symbol on the stack */
else
top--; /* Drop an element from stack */
Not: These two steps have to be performed for each input symbol in the infix
expression,
6.30 Q Stacks
Example 6.4.3.4: C function to convert from infix expression into prefix expression
void infix-'prefix( char infix]'], char prefix[])
{ .
int top; 1* Points to top of the stack *1
char s[30]; 1* Acts as storage for stack elements *1
int J; ;* Index for prefix expression *1
int 1- , 1* Index to access infix expression *1
char symbol; 1* Holds scanned char from infix expression *1
-.top = -1; 1* Stack is empty *1
s[++top] = '#'; 1* Initialize stack to # *1
j =0; 1* Points to first char of prefix expression *1
strrev(infix); 1* Reverse the infix expression *1
fore i = 0; i < strlen(infix); i++)
{
symbol = infix[i]; 1* Scan the next symbol *1
while (F(s[top]) > G(symbol) )
{
prefixjj] = s[top--]; 1* Pop from stack and place *1
j++; 1* into prefix *1
}
if ( F(s[top]) != G(symbol) )
s[++top] = symbol; 1* push the input symbol *1
else
top-«; 1* discard '(' from stack *1
}
while ( s[top] != '#')
{
prefix [i++] = s[top--];
}
Example 6.4.3.5: C program to convert from infix expression into prefix expression
#include <stdio.h>
#include <string.h>
void mainO
{ Input and Outputs
char infix[20];
char prefix[20]; Enter a valid infix expression
((a+b )*c-( d-e»)"(f+g)
printf("Enter a valid infix expression\n"); The prefix expression is
scanf("%s" ,infix); A_*+abc-de+fg
Note: The precedence values of the operands in functions G and F in example 6.4.2.3
are 7 and 8. The same values are used in example 6.4.3.3. This is because the order
of the operands in the prefix, postfix and infix expressions are same. Only the order of
the operators changes. So, care should be taken while taking the precedence values
for the operators. For example, consider the following expressions:
a+b*c ( Infix expression)
abc*+ ( Postfix expression)
+a*bc ( Prefix expression)
If we look at above expressions, the operands a, band c appear in the same sequence
where as the operators are not is same sequence. So, the precedence value of the
operands is same, but the precedence values of operators are different while
converting from infix to postfix and from infix to prefix.
6. 2 ~ Stacks
Let us see "What is the problem in evaluatmg the infix expressions? What is the need
for evaluating postfix/prefix expressions?" The evaluation of infix expression is not
recommended because of the following reasons:
. • Evaluation of infix expression requires the knowledge of precedence of
operators and the associativity of operators.
• The problem becomes complex, if there are parentheses in the expression
because they change the order of precedence.
• During evaluation, we may have to scan from left to right and right to left
~ repeatedly thereby complexity of the program increases.
All these problems can be' avoided if the infix expression is converted to its
corresponding postfix or prefix expression and then evaluate. Evaluation of a postfix
expression or prefix expression is yery simple. Now, let us see "How to evaluate the
postfix expression?" The postfix expression can be evaluated using the following
procedure:
• Scan the symbol from left to right
• If the scanned symbol is an operand, push it on to the stack
• If the scanned symbol is an operator, pop two elements from the stack. The
first popped element is operand2 and the second popped element is operand 1.
This can be achieved using the statements
op2 = s[top--]; /* First popped element is operand2 *i
opl = s[top--]; 1* Second popped element is operand l *1
If ( symbol is an operand)
push(symbol,top,s); 1* Push the operand *1
else
op2 = pop(top,s); 1* Pop into second operand *1
opt = pop(top,s); 1* Pop into first operand *1
res = opt op op2; 1* Perform the operation *1
push(res,top,s); 1* Push the result *1
endif
#include <stdio.h>
#include <math.h>
#include <string.h>
1* Function to evaluate *1
double compute( char symbol, double op l, double op2)
{
switch(symbol)
{
case '+': return op 1 + op2; 1* Perform addition operation *1
case '$':
case "":return pow(opl,op2); 1* Computer power *1
}
void mairu)
{
double s[20]; 1* Place for stack elements *1
double res; 1* Holds the result of partial or final result */
6.34 Q Stacks
Output
Enter the postfix expression
1 231
A postfix expression can be converted into its equivalent prefix expression. Let us see
"What is the procedure to convert a postfix expression to prefix expression?" The
procedure to be followed is shown along with the example below. Consider the
postfix expression:
ab+
• If the scanned character is an operand, push it on to the stack. For example, in the
above expression a and b are pushed on to the stack
+ab
Note: The following two functions of push and pop operations are used ill
subsequent sections:
Thus, a postfix expression can be converted into prefix expression and the
corresponding program is shown below:
switch( symbol)
{
case '+': 1* If the scanned symbol is an operator *1 I
ca e '-':
case '*':
case 'I':
case '''':
op2 = pop(&top,s); 1* Obtain the second operand *1
op1 = pop(&top,s); 1* Obtain the first operand *1
Q Systematic Approach to Data Structures using C - 6.37
break;
default:
push( temp,&top,s); 1* Push the operand on to stack *1
}
}
Note: The array s[20][20] is used as stack of strings. So, two-dimensional array is
used.
void maim)
{
char postfix[20]; 1* Input: has postfix expression *1
char prefix[20]; 1* Output: will have prefix expression *1
Output! Output2
A postfix expression can be converted into its equivalent infix expression. The
procedure to be followed is shown along with the example below. Consider the
postfix expression
ab+
• If the scanned character is an operand, push it on to the stack. For example, in the
above expression a and b are pushed on to the stack
• If the scanned character is an operator, build an infix expression by enclosing the
expression within 'C' and ')' as shown below:
a. Copy the symbol '(' into partial infix expression.
b. Concatenate first operand
c. Concatenate the operator
d. Concatenate the second operand
e. Concatenate the string ")"
(a+b)
Thus, a postfix expression can be converted into infix expression and the
corresponding program is shown below:
#include <stdio.h>
#include <string.h>
switch(symbol)
{
1* If the scanned symbol is an operator *1
case '+':
case '-':
case '*':
case 'I':
case '1\':
op2 = pop(&top,s); 1* Obtain the second operand *1
op1 = pop(&top,s); 1* Obtain the first operand *1
break;
default:
push(temp,&top,s); 1* Push the operand on to stack *1
}
}
6.40 Q Stac::.,:k,::s ...!- _
void mairu)
{
char postfix[20]; /* Input: has postfix expression */
char infix[20]; /* Output: will have infix expression */
Output
Enter the postfix expression
abcde/\*/-
The infix expression is (a-(b/(c*(d/\e))))
Enter the postfix expression
ab+c-
The infix expression is «a+b)-c)
A prefix expression can be converted into its equivalent postfix expression. The
procedure to be followed is shown along with the example below: Consider the prefix
expression
+ab
• Reverse the prefix expression. In this example, the expression after reversing will
be
ba+
• If the scanned character is an operand, push it on to the stack. For example, in the
above expression b and a are pushed on to the stack
• If the scanned character is an operator, pop two operands into op 1 and op2
ryspectively and concatenate opi, op2 with t e operator. In this example, the two
operands popped from stack are a and b and are concatenated with + symbol to
get the expression
ab+
which is a postfix expression.
~ Systematic Approach to Data Structures using C - 6.41
Thus, a prefix expression can be converted into postfix expression and the
corresponding program is shown below:
#include <stdio.h>
#include <string.h>
strrev(prefix) ;
for (i = 0; i < strlen(prefix); i++)
{
symbol = prefix[i]; 1* Obtain the next symbol *1
switch( symbol)
{
default:
push(temp,&top,s);
- }
}
-}
void maint)
{
char prefix[20]; /* Input: Has prefix expression *1
char postfix[20]; !* Output: Will have postfix expression */
Output
Enter the prefix expression
-a/b=c+de
The postfix expression is
abcdeA'~/-
.1
A prefix expression can be converted into its equivalent infix expression. The
procedure to be followed is shown along with the example below. Consider the prefix
expression
+ab
• Reverse the prefix expression. In this example, the expression after reversing will
be
ba+
• If the scanned character is an operand, push it on to the stack. For example, in the
above expression b and a are pushed on to the stack
• If the scanned character is an operator, build an infix expression by enclosing the
expression within '(' and ')' as shown below:
a. Copy the symbol '(' into partial infix expression.
b. Concatenate first operand
c. Concatenate the operator
d. Concatenate the second operand
e. Concatenate the string ")"
(a+b)
Thus, a prefix expression can be converted into infix expression and the
corresponding program is shown below:
#include<stdio.h>
#include<string.h>
strrev(prefix) ;
for (i = 0; i < strlen(prefix); i++)
{
symbol = prefix[i]; 1* Obtain the next symbol *1
switch( symbol)
{
1* If the scanned symbol is an operator *1
case '+':
case '-':
case '*':
case 'I':
case '1\':
opl = pop(&top,s); 1* Obtain the first operand *1
op2 = pop(&top,s); 1* Obtain the second operand *1
default:
push( temp,&top,s);
}
}
void maim)
{
char prefix[20]; /* Input: Has prefix expression */
char infix[20]; /* Output: Will have infix expression */
Output
wherex is a string consisting of the letters 'A' and 'B' and y is the reverse ofx. (i.e.,
ifx = 'ABABBA',
.
y will be 'ABBABA') by reading only one character at a time. ,
6.46 Q Stacks
Design: In the given string xCy note that the string y is reverse of x. During scanning
of a symbol, let us keep on pushing all A's and B's onto the stack till we encounter C.
If symbol C is not encountered, the string is not of the form x C y and the function
should return 0_ The equivalent code is shown below:
if (str[i] != stk_item)
return 0; 1* Not in the form xCy *1
Once control comes out of the loop, if stack is empty, the string is of the form xCy. If
stack has some elements, definitely the string is not of the form xCy. The equivalent
statements are shown below:
The complete program to check whether the string is of the form x C y where y is the
reverse of x is shown below:
Q Systematic Approach to Data Structures using C - 6.47
#include <stdio.h>
#include <string.h>
int isyalindrome( char str[])
{
int t:, 1* Index to access each character */
int top = -1; 1* Points to topmost element */
char s[30];
char stk_item; 1* Item deleted from the stack */
for (i = 0; i < strlen(str); i++) 1* Keep pushing A's and B's *1
{
if (str[i] = 'A' II str[i] = 'B')
s[++top] = str[i];
else if (str[i] = 'C') break; 1* If C, come out of loop */
}
if (i = strlen(str)) return 0; /* Not in the form xCy */
i++; /*Point one character beyond C*/
while ( i < strlen(str))
{
stk_item = s[top--]; 1* Pop an item from stack *i
if (str[i] != stk_item) return 0; /* Not in the form xCy *1
i++; /* To read the next character */
}
if (top = -1) return 1; 1* The string is of the form xCy *"1
return 0; 1* Stack not empty So, not of the form xCy */
void rnaint)
{
char str[20];
printf("Enter strings of A's and B's with C at the center\n");
scanf("%s" .str);
if (isyalindrome(str))
printf("The string is of the form xC y \n");
else
printf("The string is not of the form x C yvn'');
6.48 Q Stacks
#include <stdio.h>
#include <string.h>
int isyalindrome( char str[])
{
int 1·, 1* Index to access each character *1
int top = -1; 1* Points to topmost element *1
char s[30]; 1* Used for stack operations*1
char stk_item; 1* Item deleted from the stack *1
;* Push all the characters of given string *1
for (i = 0; i < strlen(str); i++)
{
s[++top] = str[i];
}
* Check
whether the string is a palindrome or not *1
for (i = 0; i < strlen(str); i++)
{
stk_item = s[top--]; 1* Pop an item from stack *1
void maint)
{
char str[20];
printf("Enter strings of A's and B's with C at the center\n");
scanf("%s" ,str);
if (isyalindrome(str))
printf("The string is a palindrome");
else
printf("The string is not a palindrome");
}
Q Systematic Approach to Data Structures using C - 6.49
Example 6.4.9.3: Show how to implement a stack -of integers using C language by
using an array of integers s[STACK_SIZE], where s[O] is used to store the index of
the topmost element of the stack and s[l] through s[STACKSIZE-l] contain the
elements on the stack. Write a declaration and routines push, pop, empty, popandtest,
stacktop and pushandtest functions.
#include <stdio.h>
#include <process.h>
* Function to insert
an item into the stack and check for overflow *1
void pushandtest(int item, int sm
{
if (s[O] = STACK_SIZE -1)
{
printf("Stack overflow\n");
return;
}
6.50 Q Stacks
return item rleleted; 1* Send the item deleted to the calling function *1
}
if (emptyfs)
{
printf("Stack is empty\n");
return;
}
#include <stdio.h>
#include <string.h>
switch (symbol)
{
case '(I:
case '(':
case '{I:
6.52 Q Stacks
s[++top] = symbol;
break;
case')':
case ']':
case I}':
if (top = -1) return 0; 1* No matching left brace */
return 1;
}
void maint)
{
int valid; 1* Holds 1, if the expression is correct *1
1* Holds 0, if the expression is not correct *1
char infix[20]; 1* Expression to be checked *1
if (valid)
l printf("Expression is perfectly paranthesied\n");
else
printf(,'Parantheses rnismatch\n");
}
Q Systematic Approach to Data Structures using C - 6.53
Now, let us see "What is the disadvantage of implementing stacks using arrays? How,
it can be overcome?" So far we have seen how a stack is implemented using an array
and various applications of stacks. In this approach whenever a function puslu) is
called, we have to pass three parameters namely item, top and S, where item is the
element to be pushed, top is an integer value which is the index of the top most
element in the array S. But, as the number of parameters increases, the overhead of
passing parameters in programming also increases and efficiency decreases.
In such cases, we group all related items under a common name using a
structure and pass structures as parameters, which eventually reduces the burden and
increases the efficiency. In our stack implementation, instead of passing two
parameters top and S, we can pass only .one parameter if we use a structure. So, a
stack can be declared as a structure containing two objects viz., an array to store the
elements of the stack and an integer indicating the position of the topmost elem~nt in
the array. The declaration can take the following form:
struct stack
{
int items[STACK_SIZE];
int top;
};
Oncethis definition is done, we can use a variable s to access the contents of the stack
and to obtain the position of the top most element. The declaration for this can take
theform
STACKs;
Theposition of the top most element and the element itself can be accessed (using the
'.' operator) by specifying
STACK '8;
the position of the top most element and the top most element can be accessed by
~ifyiDs
int is_etnpty(STACK·s)
{
If(s->top=-l) retum-t: 1* Stackempty+/
The function is MIO retUrns true if stack is full and returns false if the stack is not
full. This function is shown below: .
The function to insert an integer item into the stack is shown below:
Q Systematic Approach to Data Structures using C.- 6.55
The function to insert a character item into the stack is shown below:
The function to delete an integer item from the stack is shown below:
intpop(STACK *s)
{
iot item;
6.56 Q Stacks
if ( is_ empty( s) )
{
printf("Stack Underflow\n");
return 0;
}
The function to delete a character item from the stack is shown below:
void display(STACKtfsf
{
lnt i;
,!;l Systematic Approach to Data Structures using C - 6.57
if (is_empty(&s) )
{
prtntf(t'Stack is emptym'');
return;
}
printf("The contents of the stack\n");
The C program to simulate the working of a stack using structures is shown below:
#include <stdio.h>
#include <process.h>
struct stack
void maim)
{
iot item; 1* Item to be inserted *1
int choice; 1* Push, pop, display or quit *1
STACK s; 1* To store items *1
for (;;)
{
priotf(" 1:Push 2:Pop\n");
priotf("3:Disply 4:Exit\n");
priotf("Enter the choice\n");
scaof("%d" ,&choice);
switch( choice)
{
case 1:
priotf("Enter the item to be inserted\n");
scaof("%d" ,&item);
push(item,.s );
break;
case 2:
item = pop(as);
if (item != 0)
{
priotf("Item deleted = %d\n",item);
}
break;
case 3:
display(s);
break;
1
default: exit(O);
}
}
}
Q Systematic Approach to Data Structures using C - 6.59
s.top = -1;
push('#',&s);
postfix[j] = '\0';
6.60 Q Stacks
The complete program to convert a given infix expression to its equivalent postfix
expression is shown below:
#include <stdio.h>
#include <string.h>
struct stack
{
- int items[STACK_SIZE];
int top;
};
void maint)
{
char ihfix[30], postfix[30];
The program to evaluate the postfix expression using structures is shown below:
Q Systematic Approach to Data Structures using C - 6.61
#include <stdio.h>
#include <math.h>
#include <string.h>
struct stack
int top;
double items[STACK_SIZE];
};
1* function to evaluate *1
double op(char symbol, double opl, double op2)
{
switch( symbol)
{
case '+': return opl + op2;
case '$':
case '/\I: return pow(opJ,op2);
}
}
void maint)
{
int i;
double res,opl,op2;
~STACKs;
char symbol,postfix[20];
res = pop(&s);
printf("The result is %f\n",res);
Output
Enter the postfix expression
23/
The result is 0.666667
Enter the postfix expression
23+
The result is 5.00QOO
Example 6.5.12: Give the tracing to evaluate the following postfix expression
AB C-D*+E$ F+
corresponding to the infix expression ( ( A + ( B - C ) * D ) $ E + F ) with following
values assigned: A = 6, B = 3, C= 2, D = 5, E = 1, F = 7
Solution: After substituting the given values, the resulting postfix expression is:
632-5*+1$7+
The tracing of the above algorithm is shown below:
hexadecimal) from an operand i.e., if '6' is an operand its integer value is '6' -'0' i.e.,
6. This integer value will be pushed on to the stack. The assumption is that the input
consists of only non-negative, single digit integer numbers and the expression is valid.
Example 6.5.13: Apply the evaluation algorithm and trace for the valid postfix
expression
ABC+*CBA-+*
Solution: Substituting the values for the variables we have the following postfix
expression:
123+*321-+*
+-,
Postfix Expression Symb Op Op Result = Stack
01 2 1 Op1 op Contents
Scann op2
ed
123+*321-+* 1 1
23+*321-+* 2 12
3 + * 3 2 1 -+ * 3 123
+*321-+* + 3 2 2+3=5 15
*321-+* * 5 1 1*5=5 5
321-+* 3 53
21-+* 2 532
1-+* 1 - 532 1
-+* 1 2 2-1=1 531
+* + 1 3 3+1=4 54
* * 5 4 4*5= 20
20
--
result = 20
I • e
Q Systematic Approach to Data Structures using C - 6.65
Example 6.3.17: Apply the evaluation algorithm discussed in section 6.3.13 and trac
for the valid postfix expression
AB+C-BA+C$-
.Solution: Substituting the values for the variables we have the following postfi
expression:
1 2 +3 - 2 1 + 3 $ -
and the complete tracing is shown in table below:
I result = -27 I
Exercises
1. What is a stack? Mention the operations that are performed to put an element on
to a stack and remove an element from a stack. Using C language implement the
above functions. (VTU-July/Aug 2004)
6.66 Q Stacks
2. Given the following expressions give their postfix and prefix forms
i. (A +B)*(D-C)
u. X$Y * Z - M + N + PI Q I (R+S)
(VTU-July/Aug 2004)
3. Obtain the prefix expressions and postfix expressions for the following.
a) A+B-C*D
b) (A+B)*(C+D)$(A+B)
c) ((6+(2-3)*2$4$2)+8)
d) A+B*C-D/E*H
e) (A+B"C"D)*(E+F/D)
4. Convert the following prefix expressions to corresponding infix expressions.
a) -A+*$CDB/-FE*GHI
b) "-*+ABC-DE+FG
c) -+ABC
5. Convert the following postfix expressions to corresponding infix expressions.
a) AB"C*D-EF/GH+/+
b) AB+C*DE-FG+"
. c) EDBCA-+"*AB-*
6. Write a C program to implement a stack of character strings.
7. What is a stack? Discuss the applications of stacks.
8. Write an algorithm for converting infix expression to postfix expression. Trace the
algorithm indicating content of stack for expression (a-b) / (c*d) + e
(VTU July/Aug 2004)
9. What are the advantages of converting an infix expression to postfix expression
10. Write an algorithm for evaluating a valid postfix expression. Trace the same on
i. A B +C- B A +C $-
ii. ABC + * C B A - + *
for given value A=l, B=2, C=3 (VTU Jan/Feb 2004)
11. What are the applications of stacks? Using stack write an algorithm to determine
if a given string is palindrome and print suitable message as output.
(VTU July/Aug 2004)
12. Write a C program to convert an infix expression to its equivalent prefix
expression
13. V{rite a C program to convert prefix expression to infix expression
14. Write a C program to convert a prefix expression to its postfix expression
15. Write a C program to convert a postfix expression to its equivalent prefix
expression
16. Write a C program to convert a postfix expression to its equivalent infix expression
Chapter 7: Recursion
I What are we studying in this chapter? I
• Recursive definition and processes
• Recursion in C
.• Writing recursive programs
• Simulating recursion
'. Efficiency of recursion" - 4 hours
(
7.1 Introduction
Recursion is a powerful tool but least understood by most novice students. It is an
important facility that is available in most of the programming languages such as
Pascal, e, Cf+ etc. In this chapter, let us discuss the meaning of recursion; how a
recursive definition for a problem can be obtained, the designing aspects and how
they are implemented in e language. Finally we compare the iterative technique with
recursion, their merits and demerits along with properties of recursive functions. First,
let us see "What is recursion?"
and then compare iterative version of the program and recursive version of the
program.
7.2 Q Recursion
1 ifn = 0
Fact(n) =
{ n x (n-1) x (n-2) x (n-3) x 3x2x1 ifn>O
Design: Let us design the program to compute factorial of a number. According to the
definition, if n >0, n! can be computed as shown below:
1*2 * 3 * ....n
The above series can be multiplied as shown below:
1 * 2 * 3 * ....n
Initial: fact =
y 1
Step 1: fact =
Step 2: fact =
~
, fact
v
*
1
2/
Step 3: fact = Jact *
v
"---y----J
Step n: fact = fact * n
#include <stdio.h>
void maint) I
{
int n·,
I
I
I Input
printf ("Enter the value ofN\n"); Enter the value ofN
scanf ("%d",&n); I 5
I Output
printf("Factorial (%d) = %d\n", n, factorial(n)); I Factorial 5 = 120
t.
I I
7.4 Q Recursion
Now, let us see"What is recursive definition 0 compute factorial of n?' The factorial
of a number n is the product of integer values from 1 to n. The recursive definition to
compute n! is shown below:
l ifn = 0
fact(n) =
{ n x fact(n-l) ifn> 0
5! = 5 * 4!
Decompose the
----------------~-!-_~4 *3~! = 3 * 2' problem from
-----------~---2 ,.= 2 * l' top to bottom
-----------:-- 1;= 1 * 0'.
................
Note: Thus, it is clear from the above computations that, the recursive solution for a
problem involves a two-way journey with a stop at the middle:
• Decompose the problem from top to bottom which involves reducing the problem
into smaller problems of same type
• Arrive at the solution which does not involve any recursion (Base case)
• Compute the solution from bottom to top using the previous solutions.
J.
Observation: It looks like recursive calculations are difficult and take more time.
This is true if we do the calculations using pen and paper. But, using computers it is
easier to write the solution. and looks more elegant. So, let us see "How to write the
recursive function to compute factorial of n?" Using recursive definition, the function
can be written as shown below:
~ Systematic Approach to Data Structures using C - 7.5
int fact(int n)
{
if ( n == 0 ) return 1; 1* factorial ofn when n = 0*1
return n*fact(n-l); 1* factorial of n when n > 0*1
In the recursive version, we let the function fact call itself, each time with a different
set of parameters. Observe that recursive solution does not need a loop. Because,
recursion is itself repetition. The main program to compute factoria] is shown below:
#include <stdio.h>
void mainf)
{
int n;
I Input
printf(t'Enter nxn''): I Enter n
scanf(If%dlf,&n); I 5
I Output
printft'The factorial of %d = %d\nlf, n, fact(n)); I Factorial of 5 = 120
I
Let us visualize how the control glows from one function call to another call. The
figure 7.1 shows exactly what happens when recursive function factt) gets called. The
execution sequence for the above factorial function is shown below.,
7.6 Q Recursion
void maint)
{
int n;
n = ~act (3); Q
l' printfl:d\n", ---2--'
nlr-;
1 o
int fact(int n) int fact(int n) int fact(int n) int fact/int n)
{ { { {
if ( n = 0 )
return 1; b
if ( n = 0 )
return 1; c
if ( n = 0 )
return 1; G) if(n=O) 1
return 1; -..;;......,
return n* fact(n-l); return n* fact(n-l); return n* fact(n-l); return n* fact(n-l)
3*2 } 2*1 } 1*1}
• The return address so that after executing the function, control should be returned
back to the calling function
• The variable that receive the value from the recursive function.
For each call of the function, the corresponding stackframe consisting of above items
is placed on top of the stack and then removed from the stack when the execution of
the called function is completed as shown below:
Calls
framel
Step 2:
framel
frame21
1
Returns
Stack after 1st call is executed
framel
Step 3:
framel Calls
frame21
framel
frame31
Step 4: Step 5: frame21
Stack after 3rd call '-- __ ....J After 3rd call is executed
Fig 7.1 Sequence of events that take place during function execution
Once we know how recursion works, the next question is "How to design recursive
functions?" Observe the following points from the factorial function:
7.8 Q Recursion
+ Each time the function fact is called, the size of the problem is reduced. Here, the
value of n is reduced by 1. This is often called general case.
+ Finally, we solve the problem by returning the value 1 to the calling function. This
is called base caseor base/terminal condition.
otc: Every recursive call must solve one part- of the problem using base case or
reduce the size of the problem using general case. Now, let us see "What is base case?
What is a general cease?"
Dcfinition: A base case is a special case whose solution can be obtained without
using recursion. This is also called base/terminal condition. Each recursive function
must have a base case. A base case serves two purposes:
+_ It acts as terminating condition.
+ The recursive function obtains the solution from the base case it reaches.
For example, in the function factorial, fact of 0 is 1 is the base case or terminal
condition.
Definition: In any recursive function, the part of the function except base case is
called general case. This portion of the code contains the logic required to reduce the
size of the problem so as to move towards the base case or terminal condition. Here,
each time the function is called, the size of the problem is reduced.
For example,in the function fact, n*fact(n-l) is general case. By decreasing
the value of n by 1, the function fact is heading towards the base case.
Once we reached the base case, the solution begins. We know one part of the answer
and can return that part of the answer to the more general case. As we solve each
general case, we are able to solve the next higher general case and move towards the
most general case which is the original problem as shown below:
Decompose the
problem from top
to bottom
r-- ....
iL.-, B_a_s_e_c_a_s_e --,..;-~~;:;..~~-::.--o-!
__ -_--~---
.•• _=_1---,} Arrive at the solution
~~., •• #'###
--------------U = 1 * O! = 1
_--------- 2! = 2 * I! = 2 Compute the
_------------3! = 3 * 2! = 6
.-------------4! = 4 * 3! = 24 General rase
solution from
bottom to top
5! = 5 * 4! = 120
~ Systematic Approach to Data Structures using C - 7.9
ote: In our factorial program, base case is fllCt(()) and general case is n*fact(n-l).
So, the general rules that we are supposed to follow while designing any recursive
algorithm are:
• Determine the base case. Careful attention should be given here, because: when
base case is reached, the function must execute a return statement without a call to
recursive function.
• Determine the general case. Here also careful attention should be given and see
that each call must reduce the size of the problem and move it towards base case.
• Combine the base case and general cascinto a function.
otc: A recursive function should never generate infinite sequence of calls on itself.
An algorithm exhibiting this sequence of calls will never terminate. If a base case
does not exist, no recursive function can ever be computed.
Definition: The Fibonacci numbers are a series of numbers such that each number is
the sum of the previous two numbers except the first and second number. These
numbers are named after an Italian mathematician "Leonardo Fibonacci" who lived in
earlythirteenth century. The Fibonacci numbers are shown below:
0, 1, 1,2,3, 5, 8, 13, .
Now, let us see "What is the base case and general rase to compute Fibonacci
numbers?"To write a recursive defmition we should know the base case and general
case.
Base casc:
Fib(O) = O} These numbers are given. Since the solution already exists it
Fib(l) = 1 is the base case
General case:
NthFibonacci number = N-l thFibonacci number + N_2"d Fibonacci number
i.e., Fib(n) = Fib(n-l) + Fib(n-2)
o ifn = 0
Fib(n) = 1 ifn = 1
{
fib(n-l) + fib(n-2) ifn > 2
The way to find fib(4) is shown below: The thick arrows represent either a call to
function fibt) or to the value. The dotted arrows point to the result after computation.
The iterative technique for this is much more efficient than the recursive technique.
fib(5) = 3
! ? .
fib(3) +
/* I fif(2)
~+
fib(2)
1
fib(1)
=
·""""""""1
3
+ +
1 1.•......
" ...,1 = 2 1 0 = 1
fib(2) + fib( 1) I
+ + 1
1 0 = 1
The C function to fmd the nth Fibonacci number using recursive definition is below:
int fib(int n)
{
if ( n = 0 ) return 0; /* Base case: l" number */
#include <stdio.h>
void maint)
{
I
int n; I
I Input
printf("Enter n\n"); I Enter n
scanf("%d",&n); I 6
I Output
printf("fib(%d) = %d\n",n,fib(n)); I Fib(6) = 8
I.
#include <stdio.h>
void maint)
{
int .i, n;
I Input
printf("Enter n\n"); I Enter n
scanf("%d",&n); I 6
I Output
printf("%d Fibonacci numbers are\n", n); I 6 Fibonacci numbers are
for (i = 0; i < n; i++) I fib(O) = 0
{ fib(l) = 1
printf("fib(%d) = %d\n",i,fib(i)); I fib(2) = 1
}
I fib(3) = 2
I fib(4) = 3
I fib(5) = 5
7.12 Q Recursion
Now, let us see "What is GeD 0 r two numbers?" What is the recursive definition to
compute GCD of two numbers?"
Definition: TheGCD of two given numbers is the largest integer that divides both of
them, GCD of two numbers is defined only for positive integers but, not defined for
negative integers and floating point numbers.
For example, GCD of two numbers 6 and 10 can be computed as shown
below:
M N Description
Remainder
6-· ....10 ........
6
Given two numbers 6 and 10, R = 6 % 10 = 6
10k'" ....6.c"....4 M (-- 10 and N (-- 6, R = 10 % 6 = 4
6k" 44:"
......
2 M(-- 6 and N (-- 4, R = 6 % 4 = 2
v
4k' / 2ot::
M(-- 4 and N (-- 2, R = 4 % 2 = 0
" 0
,/ M (-- 2 and N (-- O. Since N = 0 the GCD will be
2/ 0 0 the value of M which is 2
'---y-----J
GCD = 2. Note that GCD = M = 2, whenever N = O. We know one part of the
answer. In general, we stop computing GCD of two numbers when N = O.When N =
O. M is the GCD of two Ilumh, Now , let us see "What is the base case" The base
case to compute GCD of two numbers is shown below:
Base case:
GCD (M, N)=M whenever N = 0
Now, let us see HI low (0 0111, uc the gene' I case'!" Note the following points from
the table:
•
I III
GCD(6, 10) = GCD(10, 6). This can be written in general, as shown below
1 I I I I
i.e., GCD(M,N) = GCD(N, M) whenever M < N
I' t J
Q Systematic Approach to Data Structures using C - 7.1~
I l
• GCD(IO, 6) = GCD (6, 4). This can be written in general, as shown below:
I I I
GCD(10, 6) = GCD (6, 10 % 6) whenever M > N .
.J. .J. .J..J..J.
GCD(r'~ = GCD(N, M % N) whenever M > N
_I t J
General case:
GCD(M,N) = GCD( ,M) whenever M <
GCD(M,N) = GCD(N, M % N) whenever M > N
So, "What is the recursive definition to compute GCD of two numbers?" The
recursive definition to compute GCD of two numbers M and N using EUCLID's
algorithm is shown below:
GCD(n, m) ifm<n
GCD(m, n) m ifn = 0
{
GCD(n, m mod n) ifm > n
The C function to compute GCD of two numbers for the above recursive definition
can be written as shown below:
#include <stdio.h>
void maim)
{
int m, n, res;
Input
printf("Enter the value of m and n\n"); Enter the value of m and n
, scanf("%d %d" ,&m , &n)·, 106
Let us see, "What is tower of Hanoi problem?" In this problem, there are three
needles say A, Band C. There are n discs of different diameters in the needle A and
are placed one above the other such that always a smaller disc is placed above the
larger disc. The two needles Band C are empty. All the discs from needle A are to be
transferred to needle C using needle B as temporary storage. The rules to be followed
while transferring the discs are
• Only one disc is moved at a time from one needle to another needle
• Smaller disc is on top of the larger disc at any time.
• Only one needle can be used to for storing intennediate disks
General case. Now , let us see "What is the general case?" This case occurs if there
are more than two disks to be transferred from source to destination. If there are n
disks, then all n disks from source to destination using the specified constraints can be
transferred recursively using following three steps:
• Move 0-1 discs recursively from source to temp .
• Move nth from source to destination.
• Move n-I discs from temp to destination.
void maint)
{
int n;
Let us write an algorithm to evaluate m*n by repetitive addition where m and n are
non-negative integers.
Design: The product of m*n can be obtained by adding m to itself n times. Here, n
can be used as a counter. For example, 4*3 can be achieved by adding 4 to 4 three
times. The iterative C function to implement this is shown below:
0 if m = 0 or n = 0 1* Base case *1
mul (m, n) = m if n = 1 1* Base case *1
{
mul (m, n-1) + m Otherwise 1* General case *1
The recursive function for the above recursive definition is shown below:
#include <stdio.h>
void rnairu)
{
int m, n;
I Input
printf("Enter the value ofm and n\n"); I Enter value of m, n
scanf("%d %d",&m, &n); I 4 3
I Output
printf("Prod(%d, %d) = %d\n", m, n, mul(m, nj); I Prod( 4,3) = 12
I
7.18 Q Recursion
Now , let us see "What is binary search? What is the concept used in binary search?"
Base, case (Item not found):We assumed that low is the position of the first element
and high is the position of the last element. So, whenever low is greater than high it
means there are no items present and search definitely fails. The equivalent code for
this can be written as shown below:
if ( low> high) return -1; 1* Key item not found *1
Base case (Item found): If low is considered as the position of the first element and
high as the position of the last element, the position of the middle element can be
obtained using the statement
Genera c s . If key item is not present at mid position, the key may be in the left
part of the array or in the right part of the array. If key is less than the middle element,
the elements in the left part of the table have to be compared ranging from low to
mid-I. Otherwise, the right part of the array has to be compared from mid + 1 to
high. This can be achieved recursively using the following statements:
Thecomplete C program to search for an item using binary search is shown below:
7.20 Q Recursion
Example 7.5.2: C program to search for an key using binary search (recursive search)
#include <stdio.h>
_ printf("Enter %d items\n",n);
for (i = 0; i < n; i++)
{
scanf("%d",&a[i]);
}
if (pos = -1)
printf("Item not found\n");
else
printf("Item found at %d position\n", pos);
}
Now, let us see, "How to determine the general case?" The item found using base
case has to be compared with all the elements starting from 1 to n-l recursively.
I;l Systematic Approach·to Data Structures using C - 7.21
Using recursion, we can access all the elements starting from 1 to n-I by executing
the statement:
Keep comparing the all the elements from 1 to n-I with big as shown below:
The above statement is repeatedly executed for the values ranging from 1 to n-I. The
corresponding c function is shown below:
/* General case: */
big = large(a, n-I); /* Recursively find big */
if (a[n] > big) return a[n]; /* Keep comparing all the items */
return big; 1* Return the largest number */
The complete program to read n elements and displaying biggest element is shown
below:
#include <stdio.h>
#include <stdio.h>
void maim)
{
int n,i,a[l O],res; I
I
printf("Enter the number of items to add\n"); I Enter number of items to add
scanf("%d" ,&n); I 5
printf("Enter n items\n"); I Enter n items
for (i = 0; i < n; i++) .. I 10 20 30 40 50
scanf("%d",&a[i]); I
res = sum(a, n-1); I
I
printf("Sum = %d\n",res); I Sum = 150
I
7.24 Q Recursion
printf("%d", n % lO);
if (nil 0 = 0) return;
reverse(nllO);
}
Q Systematic Approach to Data Structures using C - 7.25
void maim)
{
int n;
t
I
I Input
printf("Enter the number to be reversed\n"); Enter the number to rerverse
scanf("%d",&n); I 12345
I Output
reverse(n); I 54321
printf("\n"); I
I
The array elements can be printed in reverse order using the program shown below:
#inc1ude <stdio.h>
printf("%d\n", ajnj);
reverse(n-l, a); .
void maint)
{
int i,n,a[IO];
printf("Enter n elements\n");
for (i = 0; i < n; i++) scanf("%d",&a[i]);
Now, let us "Compare iterative and recursive functions?" and discuss why the
programmer choose one approach over the other in a particular situation.
Iteration Recursion
1.
repetrtron structures such as selection structures such
loop, while-loop or do-while statement, if-else or
statement
5. There are many situations in which 5. There are many situations in which
iteration is not best suited for recursion is best suited for
problems such as tower of Hanoi algorithmic descriptions while
or tree traversals etc. Even though solving problems such as Tower of
these can be solved using iterative Hanoi, or tree traversal techniques
functions, they are difficult to (Refer Trees chapter 10). In these
design, take more programmers' cases, recursive functions are more
time and are more error prone. efficient and can be understood
easily.
6. Iterative functions execute much 6. In recursion, every time the
faster and occupy less memory and function is called, all the local
can be designed easily. variables, formal parameters and
~ return address will be pushed on to
the stack. So, it occupies more
stack and most of the time is spent
in pushing and popping. Thus,
recursion is expensive in terms of
processor time and memory usage.
7.28 Q Recursion
ote: There is a conflict between the machine efficiency and programmer efficiency.
Sometimes recursive programs are recommended and sometimes non-recursive
programs are recommended. This depends on whether we want machine efficiency or
programmer efficiency.
EXERCISES
1
Chapter 8: Queues
I What are we studying in this chapter? I
• Queue and its sequential representation
• Definition and various types of queues .
• Implementation of ordinary queue using arrays
• Implementation of Circular queue using arrays
• Implementation of Double ended queue using arrays
• Implementation of Priority queue using arrays
• Implementation of ordinary queue using structures
• Implementation of Circular queue using structures
• Implementation of Double ended queue using structures
• Implementation of Priority queue using structures
- 4 hours
8.1Introduction
This chapter deals with an important data structure namely queue. The term queue is
very familiar to us in day to day life, as we see people standing in a queue to board
the bus, or we see people standing in a queue near cinema hall to purchase the tickets
etc., In any situation a person who just arrives will stand at the end of the queue and
the person who is at the front of the queue is the first person to board the bus or to get
the ticket etc., The same concept, of queue is used in the field of computer science
also.
For example, if a number of print jobs are given from various computers to
take printout from a network printer, all the print jobs are diverted to a queue and are
stored in the order of their arrivaL So, a new print job will be at the end of queue and
so, the print job, 'which is at the front of the queue, gets the services of the network
printer. There are so many such instances, where the queue concept is used in the
field of computer science. First, let us see "What is a queue? What are the various
operations that can be performed on queues?
Definition(A queue is defined as a special type of data structure where elements are
inserted from one end and elements are deleted from the other end. The end from
where the elements are inserted is called rear end and the end from where elements
are deleted is called front end. Since the first item inserted is the first item to be
8.2 Q Queues
deleted from queue, queue is also called Eirst !n first Out (FIFO) data structure. In
a queue, elements are always inserted from the rear end and elements are deleted from
the front end. The pictorial representation of the queue is shown in tigure 8.1.
f r
o 1 2 3 4
delete +--1 10 20 I 30 J~ I +-- Insert
Front end Rear end
Here, the front end is denoted by f and rear end is denoted by r. So, the first item
inserted into the queue is 10, the second item inserted is 20 and the last item inserted
is 30. Any new element to be inserted into this queue has to be inserted towards right
of 30 and that item will be the last element in the queue. The first item to be deleted
from the queue is the item, which is at the front of the queue i.e., 10. So, it is very
clear from the operations performed on queues that First item Inserted is the First
item to be deleted Out from the queue. So, queue is also called First In First Out
(FIFO) data structure.
This data structure is useful in time-sharing systems where many user jobs will
be waiting in the system queue for processing. These jobs may request the services of
CPU, main memory or external devices such as printer etc. All these jobs will be
given a fixed time for processing and are allowed to use one after the other. This is
the case of an ordinary queue where priority is same for all the jobs and whichever
job is submitted first, that job will be processed. Now, let us see "What are the
various operations that can be performed on queues?" The various primitive
operations that can be performed on queues are
Note: Sometimes, based on the preference, jobs may have to be processed. Such a
queue where a job is processed based on the priority is called a priority queue. Now,
let us see "What are the different types of queues?"
Q Systematic Approach to Data Structures using C - 8.3
Definition: A queue is defined as a special type of data structure where elements are
inserted from one end and elements are deleted from the other end. The end from
where the elements are inserted is called rear end and the end from where elements
are deleted is called front end. Since the first item inserted is the first item to be
deleted from queue, queue is also called .first !n ,Eirst Out (FIFO) data structure.
Here, first element inserted is the first element to go out of the queue. A queue
can be represented using an array as shown in fig.8.2. The operations that can be
performed on these queues are
Now, let us see "How to implement insert operation using arrays (static allocation
technique)?"
Design: Before inserting any element, we should ask the question "Where and how
the item has to be inserted?" If we know the answer for this question we have the
8.4 Q Queues
insert function ready. So, let us consider the situation where four elements 10, 20, 30
and 40 are already inserted into queue as shown in figure 8.2.a. with f = a and r = 3.
Suppose, we have to insert an element say item. Now, let us ask "Where this item has
to be inserted?" We know that it has to be inserted at r = 4. For this to happen, we
have to-increment r by 1 (Fig. 8.2.b). This is achieved using the statement:
r = r + 1; 1* Increment r by 1 *1
Then we ask "How item has to be inserted at rear end?" This is achieved by copying
item to q[r] (Fig 8.2.b) using the following statement:
But, as we insert an item, we must ask "When we can not insert item into the queue?"
We can not insert any item when r has already reached QUEUE_SIZE - l(Fig. 6.2.b).
In such situation, we have to display appropriate message as shown below:
void insertrean)
{
1* Check for overflow of stack *1
if (r =QUEUE_SIZE - 1)
{
printf("Queue overflow\n");
return;
}
Note: The array q, the variable r and the variable item are global variables and should
be declared before all the functions. As far as possible let us avoid the usage of global
variables in this book.
Let us re-write the above code by passing parameters. It is clear from the above code
that as the item is inserted, the contents of the queue identified by q and r changes.
So, the variables q and r should be passed as parameters (pass by reference) as shown
below:
Note: Inserting an element at the rear end of queue IS called enqueue. So, to
implement enqueue is same as the above function.
8.2.1.2 Delete from the front end
Now, let us see "How to implement delete front operation using arrays (static
allocation technique)?" Consider the following situation where 3 items are already
inserted into queue and one item is deleted at a time. The sequence of operations are
shown below:
o 3 4
r
After inserting 3 items After deleting 1st item
(a) (b)
f, r r
nd
After deleting 2 item After deleting 3rd item
(c) (d)
Design: Item has to be deleted from the front end inaqueue. This can be achieved by
accessing the front element q[f] as shown below:
printf("Item deleted = %d\n", q[f]); /* Access and print first item */
The above two statements can also be written using single statement as shown below:
.
1 printf("Jtem deleted = %d\n", q[f++]); /* Access and update queue */
Observe that as each item is deleted, the index variable f is incremented so that it
always contains the position of first item (see figure 8.2). Finally, when the queue is
empty the value of f will be greater than r. Once f is greater than r (see Fig. 8.2.d) , it
is not possible to delete any item. This condition is called underflow of queue. Hence,
Q Systematic Approach to Data Structures using C - 8.7
the above statement has to be executed only if queue is not empty and the code to
delete an item from queue can be written as shown below:
void delete_frontO
{
if( f> r)
{
printf("Queue underflow\n");
return;
}
printfC"The element deleted is %d\n",q[(f)++]);
Note: After deleting an item, it may so happen that queue may be empty. So,
immediately after deleting an element, if queue is empty, it is better to initialize f = 0
and r = -1 as shown in the above function. This is the initial status of queue when no
items are inserted and it indicates that queue is initially empty.
Now, let us write the above function without using global variables and by passing
appropriate parameters. Note that f and r contents changes as we delete the items. So,
they should be declared as pointers. The complete C function (by passing parameters)
is shown below:
Example 8.2.1.2.2: C function to delete an integer item (by passing parameters)
In the display procedure, if the queue already has some items, all those items are
displayed one after the other. If no items are present, the appropriate error message is
displayed. Let us design the display function.
Design: Consider the following situation where 4 items were inserted and first item
has already been deleted.
Usually, the contents of the queue are displayed from the front end and moving
towards right till we get the rear end. So, first item to be displayed is 20, next item to
be displayed is 30 and final item to be displayed is 40. This can be achieved using the
following statements:
Design Output
printf("%d\n"'!IDl ); 20
printf("%d\n", [2); 30
printf("%d\n", 3); 40
Note: Please note how the value of i change from f to r (i = 0 to r is wrong). Now, the
code takes the following form:
But, the above statement should not be executed when queue is empty i.e., when f is
greater than r. So, the above code can be modified to include this error condition and
can be written as shown below:
Q Systematic Approach to Data Structures using C - 8.9
1* If queue is empty */
if(f>r)
{
printf("Queue is empty\n");
return;
}
By passing parameters, the function above function can be written as shown below:
if ( f> r)
{
printf("Queue is empty\n");
return;
}
#include.<stdi~.h>
#include <process.h>
#define QUEUE_SIZE 5
for (;;)
{
printf("1 :Insert 2:Delete\n");
printf("3:Display 4:Exit\n");
printf("Enter the choice\n");
scanf("%d':" &choice);
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d", &item);
insert_rear(item, q, &r);
. break;
case 2:
delete_front(q, &f, &r);
Q Systematic Approach to Data Structures using C - 8.11
break;
case 3:
display( q, f, r);
break;
default:
exit(O);
}
}
#include<stdio.h>
#include<process.h>
#defineQUEUE_SIZE 5
for (;;)
{
printf("1 :Insert 2:Deiete\n");
printf("3:Display 4:Exit\n");
printf("Enter the choice\n");
scanf("%d", &choice);
8.12 Q Queues
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d", &item);
insert_rear(item, &r, q);
break;
case 2:
delete_front(q, &f, &r);
break;
case 3:
display( q, f, r);
break;
default:
exit(O);
}
}
}
1
Display the contents of queue
Note: The three operations insert rear, delete_front and display operations have
already been discussed in section 8.2.1. In this section, other two operations i.e., insert
an item at the front end and delete an item from the rear end are discussed.
.Q Systematic Approach to Data Structures using C - 8.13
Now, let us see "How to implement insert front function using arrays (static allocation
technique)?"
Design: Before inserting any element, we should ask the question "Where and how
the item has to be inserted?" If we know the answer for this question we have the
insert front function ready. So, let us consider various situations shown in figure
below:
Case 1: Queue empty: Note that queue is empty. Here, an item can be inserted at the
front end first by incrementing r by 1 and then insert an item.
-1 o 1 2 3 4 -1
r f
Case 2: Some items are deleted: Consider the following situation where 10, 20 and
30 were inserted earlier and 10 and 20 have been deleted from the front end. Now,
there is only one item in queue. Here, an item can be inserted by decrementing the
front index f by 1 and then inserting an item at that position as shown below:
f, r
After deleting 2 items After inserting 20
(a) (b)
8.14 Q Queues
Case 3: Some items are inserted (not deleted): Consider the following situation
where 10, 20 and 30 were inserted into queue.
o 1 2 3 4
I 10 I 20 I 30 I I I
f r
Q after inserting 10, 20, 30
Now, Observe that, it is not possible to insert an any item at the front end and we
should display the appropriate message:
printf("Front insertion not possible\n");
I
The complete C function to insert an item at the front end is shown below:
Example 8.2.2.1.1: Function to insert an item at the front end (using global variables)
void insertfrontt)
{
if (f= 0 && r = -1) '/* Case 1: Insert when Q empty */
{
q[++(r)] =item;
return;
}
Example 8.2.2.1.2: Function to insert an item at the front end (by passing parameters)
void insert_front(int item, int q[], int *f, int *r)
{
if (*f= 0 && *r = -1) 1* Case 1: Insert when Q empty *1
{
q[++(*r)] = item;
return;
}
if ( *f!= 0) 1* Case 2: Insert when items are present *1
{
q[--(*f)] = item;
return;
}
printf("Front insertion not possible'n'');
1* Case 3: Insertion not possible at front end *1
Now, let us see "How to implement delete rear operation using arrays (static
allocation technique)?" Consider the following situation where 3 items are already
inserted into queue and one item is deleted.
o 1 2 3 4 o 1 2 3 4
I 10 I 20 I 30 I I I I 10 I ·20 I I I I
f r f r
Design: Item has to be deleted from rear end. This can be achieved by accessing the
rear element q[r] as shown below:
printf("ltem deleted = %d\n", q[r]); 1* Access and print rear item *1
and then decrementing r by one as shown below:
8.16 Q Queues
The above function can be written by passing the parameters as shown below:
Example 8.2.2.2.2: Function to delete an item from the rear end of queue (By passing
parameters)
void drlete Jear(int q[],int *f, int *r)
{
if (*f > *r)
{
printf("Queue underflow\n");
return;
}
Q Systematic Approach to Data Structures using C - 8.17
printf("The element deleted is %d\n",q[(*r)-- J);
#include <stdio.h>
#include <process.h>
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d ,&item);II
insert _fronn);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert JearO;
break;
case 3:
delete _ fronu);
break;
case 4:
deleterean);
break;
case 5:
displayt);
break;
default:
exit(O);
}
}
}
I Example 8.2.2.2.4:
parameters.
C program to implement double-ended queue by passing
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
Q Systematic Approach to Data Structures using C - 8.19
f=O;
r = -1;
for (;;)
{
printf(" 1:Insert _front 2:Insert Jear\n");
printf("3 :Delete _front 4:Delete Jear\n");
printf("5:Display 6:Exit\n");
printf("Enter the choice\n");
.s~anf("%d" ,&choice);
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%djl,&item);
insert_front(item, q, &f, &r);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insertrearutem, &r, q);
break;
case 3:
deletefronuq, &f, &r);
break;
8.20 Q Queues
case 4:
delete_rear(q, &f, &r);
break;
, case 5:
• display( q, f, r);
break;
default:
exit(O);
}
". }
}
We know that queue is a data structure where elements are inserted from one end and
elements are deleted at the other end. Now, let us see "What is the disadvantage of
representing a queue using array?" Consider the queue shown below:
o 1 2
f r
This situation will arise when 5 elements say 10, 20, 30, 40 and 50 are inserted and
then deleting first two items 10 and 20. Now, if we try to insert an item we get the
message
"Queue overflow"
This is because, in our function insertrear (see example 8.2.1.1.1) before inserting r
is compared with QUEUE_SIZE - 1. Since r is same as QUEUE_SIZE -1, insertion
not possible. Note that "Rear insertion is denied in a queue even if room is available
at the front end". Thus, even if free memory is available, we can not access these
memory locations. This is a disadvantage. This disadvantage can be overcome by
moving the item at the front end of the queue to the very first location of queue and
then shifting the remaining element from second location onwards. But; shifting the
data is costly in terms of computer time if the data being stored is very large. S , this
method is not recommended. Let us see, how these disadvantages and difficulties can
be overcome using "Circular queue represented as an array".
Q Systematic Approach to Data Structures using C - 8.21
Definition: In circular queue, the elements of a given queue can be stored efficiently
in an array so as to "wrap around" so that end of the queue is followed by the front of
queue. The pictorial representation of a circular queue and its equivalent
representation using an array are given side by side in figure shown in next page. This
circular representation allows the entire array to store the elements without shifting
any data within the queue. This is an efficient way of implementing a circular queue
usmg arrays. .
Assume that circular queue contains only one item as shown in fig. 8.6.a. Note
that the value of f = r = O. We know that item is inserted only at the rear end. So,
before inserting the value of r should be -1.
The configuration shown in fig.8.6.b is obtained after inserting 20 and 30. To insert
an item, the rear index r has to be incremented first. For this, any of the two
statements shown below can be used.
r=r+1 or r = (r + 1) % QUEUE_SIZE
Both statements will increment r by 1. But, we prefer the second statement. Let us
see, why we consider the second approach. The queue shown in fig.8.6.c is obtained
after inserting 40 and 50. Note that at this point, the queue is full. Suppose we delete
two items 10 and 20 one after the other. The resulting queue is shown in fig.8.6.d.
Now, try to insert an item 60. If the statement
r=r+1
is used to increment rear pointer, the value of r will be 5. Since it is it circular queue r
shouldbe O.This can be achieved using the statement
r = (r + 1) % QUEUE_SIZE
After execution of the above statement, r will be O. If this approach is used, to check
foroverflow or underflow, we use a variable count that contains the number of items
in the queue at any instant. So, as an item is inserted, increment count by 1 and as an
item is deleted, decrement count by 1. By this it is ensured that at any point of time,
count always contains the total number of elements in the queue. So, if queue is
empty, count will be 0 and if queue is full, count will be QUEUE_SIZE.
8.22 Q Queues
,2
4
@ 1
o
1 ~
o
f, r
1 2 3 4
(a)
f,r
2r
@
o 1 2 3 4
4' 20 1 ~
f r
10
o (b) After inserting 20 & 30
f
2
4 0
o 1 2 3 4
r 4 5 2 1 ~
f r
~ 10
o (c) After inserting 40 & 50
f
@ 4 0 f o 1 2 3 4
4 5 1 ~
r f r
@ 4 0 f o 1 2 3 4
4 5 1 ~
1
6 r f
o
r (e) After inserting 60
Note: As usual, the various operations that can be performed on circular queues are:
+ Insert rear
• Delete front
• Display
Now, let us see ''What are the various steps to be followed while inserting?" The
following steps are followed:
Step 1: Check for overflow: Now, let us see "How to check for overflow?" This is
achieved using the following statements:
if (count = QUEUE_SIZE)
{
printf("Queue is ful1\n");
return;
}
So,the complete function to insert an item at the end of circular queue is shown
below:
Now, let us see "What are the various steps to be followed while deleting an
element?" The following steps are followed:
Step 1: Check for undeflow: Now, let us see "How to check for underflow?" This is
achieved using the following statements:
if (count = 0)
{
printf("Queue is empty\n");
return;
}
Step 2: Delete item: This is achieved by accessing the element using index f and then
incrementing f by 1 (As we did in other queues). The equivalent statements can be
written as shown below: incremeriting r by 1 and then inserting as shown below: '
Step 3: Update count; As we insert an element update count by '1: This is achieved
using the following statement:
count++;
So, the complete function to delete an item from the front end of circular queue is
shown below:
Example 8.2.3.2.1: Function to delete an item from the front end of circular queue
; .
void delete _front(int q[], int *f, int *count)
{ ~
if( *count = 0)
{
printf("Underflow of queue\n");
return;
}
Q Systematic Approach to Data Structures using C - 8.25
The complete C program to implement circular queue using arrays is shown below:
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
/* Include: Example 8.2.3.2.1: Function to delete an item from the front end */
void maint)
{
int choice,item,f,r,count,q[20];
f=O;
r = -1;
count = 0; /* queue is empty */
for (;;)
l {
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insertrearfitem.q.ecr.eccount);
break;
case 2:
delete_front(q, &f, &count);
break;
case 3:
display( q, f, count);
break;
default:
exit(O);
}
}
Now, let us see "How to implementation of priority queues?" There are vanous
methods of implementing priority queues using arrays.
It is left as an exercise to the reader to implement this. Now, let us implement priority
queues using another technique.
Design 2: The second technique is to insert the items based on the priority. In this
technique, we assume the item to be inserted itself denotes the priority. So, the items
with least value can be considered as the items with highest priority and items with
highest value can be considered as the items with least priority. So, to implement
priority queue, we insert the elements into queue in such a way that they are always
ordered in increasing order. With this technique the highest priority elements are at
the front end of the queue and lowest priority elements are at the rear end of queue.
Hence, while deleting an item, always delete from the front end so that highest
priority element is deleted first. The function to insert an item at the appropriate place
is shown below:
Note: To insert an element into appropriate place so that elements are arranged in
ascending order, we use the insertion sort technique.
Example 8.2.4.1: Function to insert an item at the correct place in priority queue
l
Note: To implement priority queue, insert an item such that items are arranged in
ascending order. But, always delete an item from the front end. The functions
delete_front{) and display{) remains unaltered. The complete program to implement
priority queue is shown below:
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
/
/* Include: Example. 8.2.4.1: Function to insert item at the correct place in queue *1
8.30 Q Queues
void maint)
{
int choice,item,f,r,q[10];
f= 0;
r = -1;
for (;;)
{
printf(" 1:Insert 2:Delete\n");
printf("3 :Display 4:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch (choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert_item(item, q, &r);
break;
case 2:
delete_front(q, &f, &r);
break;
case 3:
display( q,f,r);
break;
default:
exit(O);
}
}
}
Note: We can input in any order. But, the items are inserted at the appropriate
position and are arranged in ascending order. The item at Oth position is having
highest priority; the item at 15t position is having next highest priority and so on.
Finally, the item at the end of the queue is having the least priority. So, if we remove
from front, an item with highest priority is removed.
,!;l Systematic Approach to Data Structures using C - 8.31
Note: The priority queue need not have the numbers or characters. They can represent
complex structures and elements can be ordered based on some common property. In
this sense, a stack can be considered as a priority queue. If we take the time as an
ordering on the elements, an element that is inserted first has to spend more time in
the stack where as the element which is inserted recently will spend less time in the
stack. This is because stack is a last in first out data structure. So, while deleting, the
element that has spent less time is removed first.
Even an ordinary queue can also be considered as a priority queue where the
priority is based on the order in which they are inserted. Here, the first element
inserted into queue is the first element to go out of the queue. So, an item that is
inserted first can be considered as the item with highest priority and so it is removed
first from the queue.
Double ended queue has already been discussed. Here, let us see "How to implement
deques using structures?" Here, we need two index variables front and rear and one
more variable q to store the elements of the queue. Instead of passing front, rear, a
and item to the function insertreart) as discussed earlier, we can pass only two
parameters item and a. Here, the variable a is of type structure with three fields front,
rear and q where
• froat is an integer field which contains the index of the first item in the queue.
• rear is an integer field which contains the index of the last item in the queue.
• q is an array and is used to store the elements of the queue.
#defIne QUEUE_SIZE 5
typedef struct
{
int q[QUEUE_SIZE];
int front;
int rear;
} QUEUE;
the value of each member can be obtained using'.' operator as shown below:
a.front
a.rear
a.q
the value of each attribute can be obtained using' ->' operator as shown below:
a->front
a->rear
a->q
Note: We know that initial values of front and rear are 0 and -1 respectively. Queue
is empty whenever front is greater than rear.
Note: The design aspects of dequeue are provided in the section 8.2.2.
The function to insert an item at the rear end of the queue is shown below:
The function to delete an item from the front end of the queue is shown below:
The function to delete an item from the rear end of the queue is shown below:
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
typedef struct
{
int q[QUEUE_SIZE];
int front;
int rear;
}QUEUE;
/* Include: Example 8.3.1: C function to insert an item at the front end of queue */
/* Include: Example 8.3.2: C function to insert an item at the rear end of queue */
/* Include: Example 8.3.3: C function to delete an item from front end of queue */
/* Include: Example 8.3.4: C function to delete an item from rear end of queue */
/* Include: Example 8.3.5: C function to display the contents of queue*/
void maim)
{
QUEUE a;
int item.choice;
a.front = 0;
a.rear = -1;
for (;;)
{
printf(" 1:Insert front 2:Insert rear\n");
printf("3:Delete front 4:Delete rear\n");
printf("S:Display 6:Exit\n");
printf("Entcr the choice\n");
scanf("%d" ,&choice);
8.36 Q Queues
switch ( choice)
{
case 1:
printf("Enter the item to be, inserted\n");
scanf("%d", &item);
insert_front(item, &a);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert_rear(item, &a);
break;
case 3:
delete _front( &a);
break;
case 4:
delete_rear(&a);
break;
case 5:
display( &a);
break;
default:
exit(O);
}
}
}
The design detail of circular queue is already discussed in section 8.2.3. The various
attributes associated with circular queue are: front end identified by f, the rear end
identified by r, the number of elements in queue identified by count and the storage
for the items inserted which is identified by q. The structure declaration for this can
be of the form:
Q Systematic Approach to Data Structures using C - 8.32
typedef struct
{
int f,
int r:,
int q[QUEUE _ SIZE];
int count; ,
} QUEUE;
QUEUE a;
the value associated with each attribute of the structure can be obtained using' .'
operator and if there is a declaration of the form
QUEUE *a;
the value associated with each attribute of the structure can be obtained using' :->'
operator as discussed in the previous section.
Example 8.4.2: Function to delete an item from th front end of circular queue
The complete program to implement circular queue using structures is shown below:
Q Systematic Approach to Data Structures using C - 8.39
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
typedef st ruet
{
jut f,
int r:,
int q[QUEUE_SIZE];
int count;
} QUEUE;
void maint)
{
int choice, item;
QUEUE a;
a.f= 0;
a.r=-l;
a.count = 0; I" quen ·1 em *1
for (;;)
{
printf(" 1:lnse t c: lcl 1 J ");
printtC'3:D'splay ~.:"xit\n' .
priDtf("Entu the c. ice\ ").
scan .(" fod", "ell rc e);
8.40 Q Queues
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert ..:..rear(item,&a);
break;
case 2:
delete _front( &a);
break;
case 3:
display( &a);
break;
default:
exit(O);
}
}
}
Each item is inserted at the appropriate position based on the priority and the item is
always deleted from the front end. The design details are provided in section 8.2.4. In
this section, the priority queue is implemented using structures. The structure
declaration for this can be of the form:
typedef struct
{
int f',
int r:,
int
} QUEUE;
1
QUEUE a;
Q Systematic Approach to Data Structures using C - 8.41
the value associated with each attribute of the structure can be obtained using' .'
operator and if there is a declaration of the form
QUE~ *a;
the value associated with each attribute of the structure can be obtained using' ->'
operator as discussed in the previous section.
J--;
}
The function to delete an item from priority queue using structures is shown below:
The function to display the contents of priority queue using structures is shown
I below:
The complete program to implement priority queue using structures is shown below:
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
typedef struct
{
int f·,
int r:,
int
}QUEUE;
void maint)
{
int choice,item;
QUEUE a;
a.f=O;
a.r = -1;
for (;;)
{
printf(" 1:Insert 2:Delete\n");
printf("3 :Display 4:Exit\n");
prtntf(t'Enter the choice\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d", &item);
insert _item(item, &a);
break;
8.44 Q Queues
case 2:
delete _front( &a);
break;
case 3:
display( &a);
break;
default:
exit(O);
}
}
}
Note: In this program the input can be in any order. But, the items are inserted at the
appropriate position and are arranged in ascending order. The item at Oth position is
having highest priority; the item at 1st position is having next highest priority and so
on. Finally, the item at the end of the queue is having the least priority.
Note: Input is restricted only to front end. Here, rear insertion is not permitted. Hence
the name input restricted queue. If rear insertion is permitted then front insertion
should not be permitted. The C program to implement input restricted queue is shown
below:
#include <stdio.h>
#include <process.h>
#define QUEUE_SIZE 5
.Q Systematic Approach to Data Structures using C - 8.45
Note: Output is restricted only to front end. Here, rear deletion is not permitted.
Hence the name output restricted queue. If rear deletion is permitted then front
deletion should not be permitted. The C program to implement output restricted queue
is shown below:
#include <stdio.h>
#include <process.h>
f= 0;
r = -1;
l for (;;)
{
printf(" 1:Insert _front 2:Insert_rear\n");
printf("3:Delete front \nil);
printf(II4:Display 5:Exit\n~I);
~ Systematic Approach to Data Structures using C - 8.47
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert_front(item, q, &f, &r);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insertrearfitem, &r, q);
break;
case 3:
delete_front(q, &f, &r);
break;
case 4:
display(q, f, r);
break;
default:
exit(O);
}
}
EXERCISES
1. What is a queue? What are the various operations that can be performed on
queues?
2. What are the different types of queues?
3. Explain an ordinary queue and write a C program to implement the same
(VTU Jul/ Aug 2004)
8.48 Q Queues
4. What is the disadvantage of an ordinary queue? How you can overcome this
disadvantage?
5. What is a circular queue? How it is different from an ordinary queue?
(VTU Jan/Feb 2004)
6. Write a C program to implement circular queues using arrays
(VTU JullAug 2003)
7.. Write a C program to implement circular queues using structures
8. What is a double ended queue or deque? What are the operations that can be
performed on double-ended queues?
9. Write a C program to implement deques using arrays
10. Write a C program to implement deques using structures
11. What is a priority queue? How, it is different from an ordinary queue?
<. (VTU Jan/Feb 2003)
Explain how a priority queue can be implemented? (VTU Jul/Aug 2005)
13. Write a C program to implement priority queues using arrays
14. Write a C program to implement priority queues using structures
15. Define input restricted queue and output restricted queue with suitable diagrams
(VTU July/Aug 2006)
16. If we define an input-restricted deque as a queue which performs the operations
delete_front, delete Jear and insert_front, how we can implement a stack and how
we can implement a queue?
17. If we define an output-restricted deque as a queue which performs the operations
delete_front, insert front and insert right, how a stack can be implemented? How
a queue can be implemented?
18. How a stack of queues can be implemented?
19. How a queue of stack can be implemented?
20. How a queue of queues can be implemented?
21. A Circular queue the size of which is 5 has 3 elements 10,40,25, where F = 2 and
R = 4. After inserting 50,60, what is the value of F and R? Trying to insert an
element 30 at this stage what will happen? Delete 2 elements from the queue and
insert 100. Show the sequence of steps with necessary diagrams with the value of
F and R.
22. What is an ascending priority queue and descending priority queue?
23. Implement an ascending priority queue using arrays
24. Implement an ascending priority queue using structures
25. Explain how elements can be arranged in ascending order in priority queue while
inserting? After arranging them in ascending order, what other functions are
necessary to implement priority queues?
Chapter 9: Linked Lists
What are we studying in this chapter?
9.1 Introduction
In the previous chapters, we have seen various types of linear data structures such as
stacks, queues and their representations and implementations using sequential
allocation technique i.e., using arrays. Let us see, "What are various advantages of
usingarrays?" The advantages of using arrays in implementing various data structures
areshown below:
• Data accessing is faster: Using arrays, the data can be accessed very efficiently
just by specifying the array name and the index of that item. For example, we can
access ith item in the array A by specifying A[i]. The time taken to access a[O] is
same as the time taken to access [10000].
• Simple: Arrays are simple to understand and use.
9.2 Q Linked Lists
Now, let us see "What are the disadvantages of arrays?" Even though arrays are very
useful in implementing various data structures, there are some disadvantages:
• The size of the array is fixed: A fixed amount of memory is allocated before the
start of execution for static arrays and during execution for dynamic arrays. The
memory required for most of the applications often cannot be predicted while
writing a program. If more memory is allocated and the application requires less
. memory, it results in wastage of memory space. If less memory space is allocated
and if the applications require more memory space during execution, it is not
possible to allocate extra memory for arrays during execution. .
• Array items are stored contiguously: Some times enough contiguous memory
locations may not be available. There are situations where a number of chunks of
contiguous memory locations are available. Even though the total free memory
space is sufficient, this space cannot be used since arrays require contiguous
storage space.
• Insertion and deletion operations involving arrays is tedious job: Consider an
array consisting of more than 100 or 200 elements. If we have to insert an item in
the ithposition, all the elements from (i+ l)'h position have to be moved to the next
subsequent locations to make room for the item to be inserted. Only after this
movement, item can be inserted into ithlocation. Similarly, to delete an ith item, all
elements from (i+ l)'h position, should be moved to their corresponding previous
locations. So, if the applications require extensive manipulation of stored data, in
the sequential representation, more time is spent only in the movement of data.
All the disadvantages just now listed can be overcome using linked lists. First, let us
see "What is a linked list?"
Definition: A linked list is a data structure which is collection of zero or more nodes
where each node has some information. The pictorial representation of the node is
shown below:
u:ya-
Node = info + link
r A .•••.•
"
info link
Note that a node in a list consists of two fields namely info and link:
• info - This field is used to store the actual data or information to be manipulated
Q Systematic Approach to Data Structures using C - 9.3
I
• link -A link basically contains address of the next node. So, given address of a
node, we can easily obtain addresses of subsequent nodes. Thus, the adjacency
between the nodes is maintained by means of links.
Now, let us see "What are the different types of linked list?" The linked lists are
classified as shown below:
Definition: A singly linked list is a linked list, where each node has designated field
called link field which contains address of the next node. If there exists only one link
field in each node of the list, the linked list is called singly linked list. A singly linked
list may have a collection of nodes with various fields. But, each node should have
onlyone link field which contains the address of the next node.
The pictorial representation of a singly linked list consisting of the items 50,
20, 45, 10 and 80 is shown below:
I
1r-50---'~ 20 ~ 45 ~
I 10 ~ 80 ~
L
~nfO link info link info link info link info link
first ~
The pointer first contains the address of the first node of the list. So, first itself can be
considered as the name of the list. Using the pointer first, we can access any node in
the list. But, pointer first in Fig. 9.2.1.b, contains \0 (null) and it is called empty list.
Now, let us "Defme an empty linked list"
Definition: An empty linked list is a pointer containing a null pointer. This indicates
that list does not exists. Ex: Pointer first in Fig.9.2.1.b. is an empty list.
The following points are observed from the linked list shown in Fig. 9.2.1.a.
• This list contains 5 nodes and each node consists of two fields, info and link.
• The first field of each node i.e., info contains the data. In this list, the items 50, 20,
45, 10 and 80 represent the data.
• The second field i.e., link of each node contains address of the next node. Note
the arrow originating from link field of each node. This arrow indicates that the
address of the next node is stored. So, using link field, any node in the list can be
accessed. Note: The link field of a last node contains \0 (null). The null (\0) in
the link field indicates that it is the last node in the list.
• Nodes that are physically adjacent need not be logically adjacent in the list. For
example, the nodes identified by the addresses 1004, 1020, 1012, 1008 and 1016
they are physically not adjacent but they are logically adjacent. Note: In the
figure, it appears as if the nodes with addresses 1004, 1020, 1012, 1008 and 1016
are physically adjacent. But, by observing the addresses, we know that they are
physically far apart. But, logically adjacent.
The nodes in a linked list are self-referential structures. So, let us see "What are self-
referential structures? How to define a node in C language?"
SYntax
Note: Here, struct is keyword
struct node and node is tagname.
{
type1
type2
info;
*link;
,.
};
~ Systematic Approach to Data Structures using C - 9:5
Let us see, "What is the type of members?" The members of the above structure can
be declared as shown below:
• info - Since it contains the information, it can be of type int, float, char, double
etc. Let us assume this field is of type into So, it can be declared as:
int info;
• link - It contains address of the next node. So, link field must be a.pointer to a
node which can be declared as shown below:
struct node *link;
"---v----'
type of pointer link
Note: The header of the structure defmition and type of a member shown in
rectangular box are same. So, it is a self-referential structure
Now, let us see "How to declare a variable? and How to access the members of
structure?"
Both the declarations are same, since NODE and struct node * can be
interchangbly used because of typedef. So, while programming one of the
two declarations can be used. The info field and link fields can be accessed
using the following notations:
Notation 1: Using -> operatior
first->info 1* Access info field *1
first->link 1* Access link field *1
Note: In the text book, let us use method 1 for declaring pointer variables
and notation 1 for accessing the members of structure. But, remember
any method or notation mentioned here can be used.
Note: We just now saw that using arrow operator -> or using * operator we can
access members of a structure. The following notations are used to access the
members of a structure while writing the algorithms:
So far we have seen, what is a linked list, how to represent a node and how to access
various fields of a node. Most important thing is creating a list and manipulating the
list. To create a list, we require nodes. Now, we have too many questions:
• Where the required number of nodes are present? Answer is available list
• How to· get these nodes? Answer is using getnodet)
• When nodes are not required, how to return back? Answer is using freenodet)
Q Systematic Approach to Data Structures using C - 9.7
Definition: The free memory space available which is not used by any program is
considered a pool of empty nodes. Since free memory space is finite, we have a finite
pool of empty nodes. This list of free available nodes is called available list. The
available list is pictorially represented as shown below:
AVAIL
EQ] AVAIL
The nodes in the available list can not be accessed by the programmer directly. Then
how to get these nodes to store the information. This is where the functions getnodet)
and freenodet) makes their existence. Using getnode and freenode, the user can
access the available list.
Definition: The getnodeO is a function which removes the first node from the
available list and makes it available for the user. The getnode operation can be treated
as a machine that manufactures nodes. Each time the getnode is invoked, it gives a
new node from the available list. Once a node is obtained from the available list, the
user can use this node to store information,
Now, let us see "How does getnode work when available list has more free nodes?"
The syntax to use getnode function is shown below:
The following actiyities take place after executing the above statement:
• Memory is allocated for pointer variable p. The memory does not contain valid
address. So, it is a dangling pointer and can be represented pictorially as shown
below:
p~ invalid address
AVAIL
t G4----iG--1_~ .
1000 1040 1020
• The function getnode obtains the first free node from the available list pointed to
by AVAIL. The address of the empty node thus obtained is copied into pointer p
as shown below:
1000
p~----iD
Now, pointer p contains valid address and now, it is not a dangling pointer.
• The pointer AVAIL points to the next free node (avail = linktavail) as shown
below:
AVAIL
-i-~""""""'"
Note: The node shown in dull- color represent that it has been removed from the
available list and given to the user. Now, removed node is not part of the available list
What to do next? Once the node is obtained, it is the responsibility of the user to store
the information by accessing info and link fields. For example, suppose it is required
~ Systematic Approach to Data Structures using C - 9.9
to store only one item 10. Since our list has only one item, info field should contain
10and the link field of this node should contain \0 (null) indicating it is the last node
inthe list. The equivalent code for this can be written as shown below:
pG-110 GJ
Now,let us see "How does getnode work when available list is empty?"
Theempty available list is shown in figure 9.2.2.b. Note that pointer AVAIL points
to\0 (null) which indicates that the available list is empty and there are no free nodes
tobe given to the user. If the user request a free node using the getnode function, the
getnodefunction returns \0 (null). This means that all free nodes are currently in use
by various programs and memory is used to full extent resulting overflow condition.
• If the available list is not empty, get node function returns the free node pointed to
by AVAIL using the statement:
p = AVAIL;
9.10 Q Linked Lists
• The pointer AVAIL points to the next free node using the statement:
Algorithm getnodet)
{
- if(AVAIL==NULL) 1* If available list does not exist, it is overflow */
{
printf("Overflow of memory\n");
return '\0';
}
Example 9.2.1.3: C Function to get a new node from the availability list
NODE getnodet)
{
NODE x;
ote: The return type of getnodet) is NODE and its type definition is available in
example 9.2.1.1 (see this example for details on NODE)
AVAIL
t G-1"------1~ .
1040 1020
Assume the variable p points to a node as shown below and it is no longer used. So, it
is required to return this node back to the available list.
1000
p~
p invalid address
~ Systematic Approach to Data Structures using C - 9.13
Now, the pointer p does not contain valid address. So, pointer p is now a
dangling pointer.
• The returned node is thus added at the beginning of the available list as shown
below:
AVAIL
~-~ .
1000 1040 1020
It is clear from above figure that link(p) should contain address of the first node of the
available list. This is achieved using the following statement:
link(p) = AVAIL;
Then make the new returned itself as the starting address of the available list by
executing the statement:
AVAIL =p;
So, the final available list is shown below:
AVAIL
t 1000
~1..--G-1L.-G--+"""""'"'' ~L.-0
1040 1010
Now, let us see "How to implement the function freenodet) III the form of an
algorithm" The corresponding algorithm is shown below:
Algorithm freenode(p)
{
link(P) = AVAIL;
AVAIL=p;
}
9.14 Q Linked Lists
void freenode(NODE x)
{
free(x);
}
So far we learnt how to get a node from the available list and how to return that node
to the available list whenever it is not being used.
Now, let us see "What are the various operations that can be performed on singly
linked lists?" The basic operations that can be performed on a linked list are shown
below:
In this section , let us see "How to insert a node at the front end of the list?"
Design: To design the function easily, let us consider a list with 4 nodes. Here,
pointer first contains address of the first node of the list. Let us try to insert the item
50 at the front end of the list. The sequence of steps to be followed are shown below
Step 1: Obtain a free node from the availability list and -identify the node with
variable temp using the following statement:
1
temp = getnode();
Step 2: Copy item = 50 to the info field of temp using the following statement:
info(temp) = item;
Q Systematic Approach to Data Structures using C - 9.15
Step3: Attach pointer first to the link field of temp using the statement:
Step4: Now, a node temp has been inserted and observe from the figure that temp is
thefirst node. Let us always return address of the first node using the statement:
return temp;
Thesesteps and the changes made as per these steps are shown in figure below:
Thealgorithmic function to insert an item at the front of the list is shown below:
•
Example9.2.2.1.1: Algorithm to insert an item at the front of list
Algorithminsert_fropt(item, first)
{
temp = getnodei); /* Get a node from the availability list */
ote: in the calling function (say main), let us use first always to point to the first
node of the list. This can be achieved by calling the function insert _ frontt) as shown
below
9.16 Q Linked Lists
After executing this statement, we know that function insertfrontt) returns temp
which is the address of the first node in that function. This returned value is copied
into first in the calling function. The entire linked list as seen in calling function is
shown below:
..., .
1 llir.,t
~.":".:
ote: The variable first in the calling function (say main) always points to the first
node. The previous node which was pointed to by first before executing insert _ frontt)
is shown in gray color.
Note: The algorithm insert_frontO has been designed by assuming that the list already
exists. Assume now that the list is empty i.e., the pointer variable first contains
NULL. Now try to insert an item into the empty list and see that the function
insertfrontt) works for this case also.
Just now, we have inserted one node at the front end of the list. Now, the question is
"How to create a linked list?" The answer is very simple. Consider the following
statement:
first = insert _front(item, first);
• If first is NULL and the above statement is executed, a linked list with only one
node is created.
• If it is executed for the second time, a new node is inserted at the front end and
there by number of nodes' in the list is 2
• If it is executed for the third time, the number of nodes in the list will be 3. Thus,
if the function insert _ fronn) is called n times, we have a list with n nodes.
ote: Thus, repeated inserting an element at the front end of the list we can create a
linked list. The C function to insert an item at the front end of the list is shown below:
~ Systematic Approach to Data Structures using C - 9.17
Example 9.2.2.1.2: C Function to insert an item at the front end of the list
Now, let us see "How to display the contents of list?" Consider the list shown in
fig.9.2.2.2. Here, first contains address of the first node. Instead of updating first
from left to right to access each node, let us use another pointer temp which initially
points to the first node of the list. By pointing temp to each subsequent nodes and
printing the info field of each node, entire contents of list can be displayed.
~
. ..
----------~----
temp
Fig. 9.2.2.2 Singly linked list to be displayed
Now,let us see "How to point temp to point to next node?" Note that link(temp) is
1004. If we copy this value to temp, now temp has the address 1004 which is the
addressof the second node. Thus, using the statement:
temp = link( temp)
wecan point temp to next node. Now, the items to be displayed are 20, 45, 10 and 80.
This is achieve by repeatedly printing info field of temp and updating temp to point
tonextnode as shown below:
9.18 Q Linked Lists
Output
write (info(temp)) 20 45 10 80
temp = link(temp)
[
After displaying 80, note that temp points to NULL, indicating all the nodes in the
list have been displayed. Now, there is no need to go back and execute those
statements i.e., those two statements have to be repeatedly executed, as long as temp
is not NULL. So, the next version of the algorithm can be written as
temp = first
while ( temp != NULL)
{
write (info(temp))
temp = link( temp)
}
Note that all these statements have to be executed only if the list is existing. If first is
NULL, display the message "List is empty" The algorithmic function to display the
list is shown below:
Algorithm displaytfirst)
{
if (first = NULL) 1* Check for empty list *1
{
write('List is empty')
return;
}
write('The contents of the list')
temp = first 1* Holds address of the first node *1
while ( temp != NULL) 1* As long as no end oflist *1
{
write (info(temp)) 1* Display the info field of a node *1
temp = link( temp) 1* Update to point to next node *1
}
}
The complete C program to create a list and to display the contents of list is shown
below:
Example 9.2.2.2.3: C program to create a list and to display the contents oflist
#include <stdio.h>
#include <alloc.h>
#include <process.h>
struct node
int info;
struct node *link;
};
1* Include: Example 9.2.1.3: C Function to get a new node from availability list */
/* Include: Example 9.2.2.1.2: C Function to insert an item at the front end of list */
1* Include: Example 9.2.2.2.2: C function to display the contents oflinked·list */
void maim)
{ . NODE first = NULL; /* To start with list is empty. */
int choice, item;
for (;;)
{
printf(" 1:Insert front 2:Display\n");
printf("3 :Quit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch ( choice)
{
case l:
" printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
break;
case 2:
displaytfirst);
break;
default:
exit(O);
}
}
}
This section describes the implementation of stacks, queues and dequeues using
linked representation and various operations that can be performed on linked lists
along with design aspects.
Q Systematic Approach to Data Structures using C - 9.21
Now, let us see "How to delete a node from the front end of the li t?"
Design: To design the function easily, let us consider a list with 5 nodes. Here,
pointer first contains address of the first node of the list. Let us try to delete an item
from the front end of the list. The sequence of steps to be followed are shown below:
first (b)
Step 1: An auxiliary pointer temp should point to the first node of the list using the
following statement
temp = first;
9.22 Q Linked Lists
Step 2: Update the pointer temp to point to the next node. This is achieved using the
statement:
temp = link( temp);
Step 3: Note that first points to first node of the list and temp points to the second
node of the list. Using the pointer first, the first node can be deleted as shown below:
freenode( first);
The node pointed to by first is deleted and is returned to the availability list. The
resulting linked list is shown in fig.9.3.l.c.
Step 4: Once the node first is deleted, the node temp will become first node. So,
return temp as the first node to the calling function using the statement:
return temp;
Note that all these statements have to be executed, only when the list exists. If the list
is empty, display the message "List empty, can not delete" and return NULL. The C
function to delete an item from the front end of the list is shown below:
Example 9.3.1: C function to delete an item from the front end of the list
Now,let us see "How to insert a node from the rear end of the list?"
Design:To design the function easily, let us consider a list with 5 nodes. Here,
pointer first contains address of the first node of the list. Let us try to insert an item at
the rear end of the list. The sequence of steps to be followed are shown below:
tempe!)
(a)
temp
,
--!-----..:....
(b)
9 first 0 9 temp
Step 1: Create a node to be inserted and insert the item using the following
statements:
temp = getnodet);
temp->info = item;
temp->link = NULL;
,Step 2: If the node temp is inserted into the list for the first time (i.e., into the empty
llist), then return temp itself as the first node using the following code.
Step 3: The moment control comes here, we have an existing list. We need to obtain
the address of the last node. This is possible only if we start from the first node. So,
we use pointer cur which initially points to the first node using the statement:
cur = first;
Step 4: Now, we should obtain the address of the last node. This is possible by
repeatedly updating cur till last node is reached.
I cur; cur->link;
Note: if cur->link contains \0 (null) then cur points to the last node. So, keep
updating cur as long as cur->link is not NULL. This is achieved using the following
statement:
while ( cur->link != NULL)
cur = cur->link;
Step 5: Once cur points to the last node, we can easily insert temp at the end of the
list. This is achived by copying temp to link field of cur using the statement:
cur->link = temp;
Step 6: After updating always return address of the first node of the list using:
return first;
The complete C function to insert an item at the rear end of the list is below:
Q Systematic Approach to Data Structures using C - 9.25
Example 9.3.2: Function to insert an item at the rear end of the list
Step 1: It is not possible to delete an item from the empty list. Appropriate error
messagecan be displayed as shown below:
if (first = NULL)
{
printf("List is empty can not delete\n");
return first;
}
9.26 Q Linked Lists
Step 2: Consider a list with single node shown in figure 9.3.3.a. After deleting this
node, the list should be empty. The code for this case is shown below.
I*If only one node is present, delete it *1
if ( first ->link = NULL)
{
printf ("The item to be deleted is %d\n",first->info);
freenode(first); 1* Delete and return to availability list */
return NULL; /* return empty list */
}
first cv
50 After deleting first = NULL
first
--- ---
-.z..
r---....,...- ....•
prev
Note: cur and prev points to last and last but one node (final dotted lines)
(b)
9firSl0 ~cur 4
\50 ~20
~ 45 ~ 10 r;:] , -;;-~\O-·
.L ~ node deleted
~ prey
Step 3: Once control comes to this step, the list has more than one node. To delete the
last node, we need to find the address of the last node and address of the last but one
node. For this reason, we use two pointers: cur and prev,
Initially, cur points to the first node and prey points to \0 (null). Now, let us
point cur to point to last node and prey to last but one node. This is possible by
repeatedly updating cur and prey as long as link field of cur is not NULL. Just
before updating cur, assign cur to prey so that prev->link always contains address of
cur node. This can be achieved by using the following sets of statements.
prev=NULL;
cur = first;
After executing the above statements, cur points to the last node and prey points to
last but one node as shown in figure 9.3.3.b.
Step 4: To delete the last node pointed to by cur, the function freenodet) is used as
shownbelow: (gray color in 9.3.3.c)
Step 5: Once the last node is deleted, the node pointed to by prey should be the last
node.This is achived by copying NULL to link field of prey as shown below:
Thecomplete C function to delete a node from the rear end of the list is shown below:
9.28 Q Linked Lists
Example 9.3.3: Function to delete an item from the rear end of the list
prev->link = NULL; 1* Make last but one node as the last node *1
Now, "How to implement queues using linked lists?" We have discussed the concept
.of queues in chapter 8. It is a FIFO data structure. Here, insertion is done from one
end and deletion is done from the other end. So, we can utilize the functions
• insert_rearO
• delete_ frontt)
• displayt)
to implement queues using linked list. The complete C program to implement queues
is shown below:
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
struct node
{
int info;
struct node *link; .
};
1* Include: Example 9.2.1.3: C Function to get a new node from availability list */
./* Include: Example 9.2.1.4: C Function to free a node to the availability list */
1* Include: Example 9.3.1: C function to delete an item from the front end oflist */
1* Include: Example 9.3.2: Function to insert an item at the rear end of the list */
void maint)
{
NODE first = NULL;
int choice, item;
9.30 Q Linked Lists
for (;;)
{
printf(" 1:Insert rear 2:Delete front \nil);
printf("3:Display 4:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
first = insert Jear(item,first);
break;
case 2:
first = delete_front(first);
break;
case 3:
display(first);
break;
default:
exit(O);
}
}
}
Note: Using the functions insertfrontt), delete_rearO and displayt) also, we can
implement queues.
Now, "How to implement stacks using linked lists?" We have discussed the concept
of stacks in chapter 6. It is a LIFO data structure. Here, insertion is done from one end
and deletion is done from same end. So, we can utilize the functions
• insert _frontt) • insert _rearO ,.
• delete _frontt) or. delete JearO .
• displayt) • displayt)
~ Systematic Approach to Data Structures using C - 9.31
to implement stacks using linked list. The complete C program to implement stacks is
shownbelow:
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
struct node
int info;
struct node *link;
};
1* Include: Example 9.2.1.3: C Function to get a new node from availability list */
1* Include: Example 9.2.2.1.2: C Function to insert an item at the front end of list */
1* Include: Example 9.3.1: C function to delete an item from the front end of list */
voidmaint)
{
NODE first = NULL;
int choice, item;
for (;;)
{
printf("1 :Insert front 2:Delete front \n");
printf("3 :Display 4:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
9.32 Q Linked Lists
switch ( choice)
{
case 1:
printf(t'Enter the item to be insertedm'');
scanf(tI%dtl ,&item);
first = insert _front(item,first);
break;
case 2:
first = delete_front(first);
break;
case 3:
display(first);
break;
default:
exit(O);
}
}
1* Include: Example 9.2.1.3: C Function to get a new node from availability list */
1* Include: Example 9.Z.2.1.2: C Function to insert an item at the front end oflist */
1* Include: Example 9.3.2: Function to insert an item at the rear end of the list */
1* Include: Example 9.3.3: Function to delete an item from the rear end oflist */
void maim)
{
NODE first = NULL;
int choice, item;
for (;;)
{
printf("l :Insert front 2:Jnsert rear\n");
printf("3:Delete front 4:Delete rear\n");
printf(" 5 :Display 6:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
first = insertfronuitem.first);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
first = insert Jear(item,first);
break;
9.34 Q Linked Lists
case 3:
first = delete _ front(first);
break;
case 4:
first = delete. reanfirst);
break;
case 5:
display(frrst);
break;
default:
exit(O);
}
}
}
Definition: A linked list in which the items are stored in some specific order is an
ordered linked list. The elements in an ordered linked list can be in ascending or
descending order or based on key information. A key is one or more fields within a
structure that are used to identify the data. For example, an ordered linked list shown
in fig.9.3.7.a.
Design: To design the function easily, let us consider a list with 4 nodes. Here,
pointer first contains address of the first node of the list. After inserting any item into
this existing list, the order of list should be maintained i.e., the items in the list should
be arranged in ascending/descending order only. The pointer first, contains the
address of the first node of the list. To insert an item into the list, the sequence of
steps to be followed are:
Step 1: Create a node to be inserted and insert the item usmg the following
statements:
temp = getnodet);
temp->info = item;
temp->link = NULL;
Q Systematic Approach to Data Structures using C - 9.35
Step 2: If the node temp is inserted into the list for the first time (i.e., into the empty
llist), then return temp itself as the first node using the following code.
(e)
Fig.9.3.1 To create an ordered linked list
Case 1: Inserting an item at the front end of the list: Let the item to be inserted be 5.
Step 3: Consider the list with 4 nodes having 10, 20, 30, 40 as the info of each
node. The item 5 is less than i0 which is the info of first node of the list. To
maintain the order, the created node temp should be inserted at the front of the
list as shown in fig.9.3.7.b. The code corresponding to this is shown below.
9.36 Q Linked Lists
CD temp->link = firs~; /* Insert the node at the front end of the list */
Step 4: Once the node temp is inserted at the front end, let us return temp
indicating temp itself is the first node of the list using:
Step 5: Find the appropriate position (Fib 9.3.1b or Fib 9.3.1c): The node
temp containing item 35 should be inserted between two nodes prey and cur.
So, it is required to find appropriate positions so that the node temp is inserted
maintaining the order. This is possible if traversing is done from the first node of
the list. Let us use two pointers cur and prey with cur pointing to first node and
prey pointing NULL as shown below:
prev = NULL;
cur = first;
Then, as long as the item to be inserted is greater than info of cur, keep updating
the cur and prey to point to their successor nodes one after the other. The code
to find the appropriate position is shown below.
Note: The moment we find the appropriate place, control comes out of the while
loop. Now, we have cur and prey between which node temp has to be inserted.
Step 7: Always we return the address of the first node as shown below:
The complete C function to insert an item into an ordered linked list is below:
1* find prey and cur locations so that node temp has to be inserted *1
prey = NULL; STEP 5
cur = first;
Before writing the algorithm/program for searching, let us see "What is searching?"
Definition: More often we will be working with large amount of data. It may be
necessary to determine whether a particular key or item is present in the large amount
of data. This process of finding a particular key or item in the large amount of data is
called searching.
Now, let us see "How to search for an item in a linked list?" Searching for a key item
is very simple and straight forward technique. We know how to display info of each
node. The partial code can be written as: .
Note: Replacing the printf by the above statement our searching is over. The partial
code for searching can be written as shown below:
cur = first;
while (cur!= NULL) 1* As long as no end oflist */
{
if (key = cur->info) break; /* If found go out of the loop *1
cur = cur-c-link; /* point cur to the next node */
\
This section gives a more general way of deleting a node based on the information
given. This involves traversing the list for the specific information. If the item is
present, the corresponding node is deleted. Otherwise, appropriate message is
displayed. The main aim is to search for the specified item in the list and delete the
corresponding node.
ow, let us see "How to delete a node whose info field is specified?"
Design: To design the function easily, let us follow the sequence of steps shown
below:
9.40 Q Linked Lists
Step 1: Check for empty list. The equivalent code can be written as shown below:
if (first = NULL)
{
printf("List empty, Search fails\n");
return NULL;
}
Let us compare key with info field of the first node in the list. If there is a
match, the node at the front end of the list has to be deleted as shown below:
G Save the address of first node into cur using the statement:
cur = temp;
25
Step 3: Control comes to step 3, if key is not present in the first node of the list. It
may be present or may not be present. So, we have to search for key in the list. The
code can be written as shown below:
prev=NULL;
cur = first;
I pre v -
cur; I 1* Save the address of cur node *1
cur - cur-e-link; 1* point cur to the next node *1
}
Step 4: Once search is successful, the node pointed to by cur has to be deleted. Also,
observe that if cur is the node to be deleted, prev is the predecessor of the node to be
deleted. Assume key = 30. Now, to delete the node pointed to by cur consider the
figure shown below:
9.42 Q Linked Lists
Now, to delete the node pointed to by cur, we have to establish a link between the
successor and the predecessor of the node to be deleted. This can be done using the
following statement:
CD prev->link = cur->link;
Step 5: Once a link has been established, the node pointed to by cur is isolated and it
can be deleted by calling the function freenodet)
o freenode( cur);
Step 6:_ Finally return the address of the first node. The code for this is shown below.
o return first;
Thisproblem is similar to the previous method. Instead of deleting a node whose info
fieldis specified, we obtain a node at the specified position and then delete from that
position. If the specified position is 1, the node at the front end of the list has to be
deleted as shown in fig.9.11.a. The code corresponding to this can be
Step 1: Check for empty list. The equivalent code can be written as shown below:
if (first = NULL II pos <= 0)
{
printf("Invalid position\n");
return NULL;
}
9.44 ~ Linked Lists
Step 2: This step is exactly similar to step 2 of previous problem. But, instead of
comparing key with first node of the list, we compare whether the node to be deleted
is at position 1. The code corresponding to this is shown below.
if( pos = 1 )
{
cur = first; /* Save the address of the first node */
first = first->link; /* Point first to second node in the list */
freenode( cur); /* Delete the first node */
return first; /* Return second node as the first node */
}
Step <3: This step is also exactly similar to the previous problem. But, instead of
comparing key with each node, we compare the position. If specified position is
obtained come out of the loop. Only the difference is that we have to keep track to
find whether the specified position is reached or not. For this we have to keep track of
count. The corresponding code is shown below:
prev = NULL;
cur = first;
count = 1; /* Start from the first node */
while (cur != NULL) /* As long as no end oflist */
{
if (count = pos) break; /* If found go out of the loop */
prev = cur; /* Save the address of cur node *1
cur = cur-c-link; 1* point cur to the next node */
}
Definition: Concatenation of two lists is nothing but joining the second list at the end
of the first list.
Design: Concatenation of two lists is possible if two lists exist. If one of the lists is
empty, return the address of the first node of the non-empty list as shown below:
if (first = NULL) return second;
if (see = NULL) return first;
If both lists exist, obtain the address of the"last node of the first list and attach this to
the first node of the second list as shown in figure 9.3.1l. The code to obtain address
of the last node of the first list is shown below:
cur = first;
while (cur->link != NULL) I*Traverse till the end *1
{
cur ~ cur->link;
}
Once control comes out of the loop, pointer cur contains address of the last node of
the first list (see the figure below). Now, attach address of the first node of the second
list identified by second to cur node as shown in red line. The code corresponding to
this can be written as
cur->link = second;
Q Systematic Approach to Data Structures using C - 9.47
Now, first contains the address of the first node of the concatenated list and return
this node to the calling function using the statement:
return first;
cur->Iink = sec; /* Attach first node of second list to end of 1irst list */
return first;
}
printf(" Invalid position\n");
return first;
}
A priority queue can be implemented by using an unordered linked list and an ordered
linked list as well. Using an unordered linked list, an ascending priority queue can be
implemented by selecting a node with least value and then deleting it from the list. To
implement a descending priority queue, select a node with highest item and delete that
node.
A priority queue can also be implemented using an ordered linked list. If the
elements of the list are in ascending order, deleting a node from the front end results
in ascending priority queue. Here, a node containing a least item is considered to be a
node with the highest priority and that is the node to be deleted first. To implement a
descending priority queue, create an ordered ·list in descending order and delete from
the front end. Here, a node with highest item is considered to be a node with the
highest priority and that is the node to be deleted first.
Definition: A linked list with two or more fields with different data types IS
considered as non-homogenous list.
Q Systematic Approach to Data Structures using C - 9.51
For example, in the linked lists so far we have seen, the type of info field of each node
in the list was integer. But, the data type can be float? double or array of characters of
real or any other complex entity. For example, to store the student names,
corresponding id's and semester using a linked list, we can have the following
declaration
struct student
{
char name[lO];
int id;
int sem;
struct student * link;
};
The pointer variable first points to the first node of the list where each node of the list
contains unique information of a student. All the basic operations of this problem are
already discussed and the code for this is shown below:
9.52 Q Linked Lists
STUDENT getnode(void)
{
STUDENT x;
void freenode(STUDENT x)
{
freetx);
}
Example 9.3.15.5: Function to insert item into specified position linked list
STUDENT insertpost char name[], int id, int sem, int pos, STUDENT first)
{
STUDENT temp; 1* Node to be inserted */
STUDENT prey, cur; 1* A node is inserted between these two nodes *1
int count; 1* To find the exact position to insert an item *1
l
return first;
}
printf("Invalid positionvn");
return first; 1* return the first node *1
}
Q Systematic Approach to Data Structures using C - 9.55
if ( first = NULL)
{
printf("No students in the organization\n");
return NULL;
}
free(cur);
return first; .
9.56 Q Linked Lists
if (first = NULL)
{
printf("No students in the organization\n");
return NULL;
}
if ( first = NULL)
{
printf("No students in the organization\n");
return;
}
printf("\n");
f1inciullc <stdio.h>
flindlldc <alloc I -.
/Ii,lclllcle <proeess.h>
I!incillclc (.Sir'io8,11 .
/
9.58 Q Linked Lists
struct student
{
char name[20];
int id;
int sem;
struct student* link;
}~
typedef struct student* STUDENT;
*(1nclude: Example 9.3.15.7: Function to search for the studen 0 0 ril based on id*/
void maint)
{
0-
for (;;)
{
prtntf'("l .Insert front 2: Insert Rearm");
printf(lf3:Insert at position 4: Delete'n");
printf(lf5:Search 6: Displayvn");
printf(lf7:Exit\nlf);
printffEnter the choicein'');
scanf(If%dlf ,&choice);
Q Systematic Approach to Data Structures using C - 9.59
switch( choice)
{
case 1:
first = insert_front(name,id,sem,first);
break;
case 2:
first = insert reartnamc.id.sem.first);
break;
case 3:
printf("Enter poistion to insert the entry\n");
scanf("%d" ,&pos);
first = insertyos(name,id,sem,pos,first);
break;
. case 4:
printf("Delete student details for ID :");
scanf("%d" ,&id);
break;
case 5:
printf("Search with ID :");
scanf("%d" ,&id);
search(id,first);
.', .' break;
9.60 Q Linked Lists
case 6:
display( first);
break;
default:
exit(O);
}
}
}
The previous sections describe how a singly linked list can be represented in C and in
othersections we have seen how lists can be traversed(i.e., visiting each node) and
manipulated using links between the nodes. This section describes the way to
represent a linked list using an array. In this case, an array is considered to be a
collection of nodes. Each node-has two fields, info and link as discussed earlier.
Consider the list shown in fig.9.3.16.a. This list can be stored in an array L as
shown in fig. 9.3.16.b. Note that each location in this array has two fields, info and
link. The variable first is used as an index variable from which different nodes can be
accessed. In fig. 9.3.16.b, the initial value of index variable first is 2. The first item
20, in this location can be accessed using L[2].info. The index of the next item is
stored in L[2].link which in this case is O. Using this index value 0, the next item
L[O].info i.e., 45 can be obtained. The next item can be accessed by the index
obtained li'OI11 L[O].link i.e., 4. L[4].info gives 10 and so on. Proceeding this way all
the items 20, 45, 10; 80 and 50 can be accessed.
first
~mJ3-[@]3--~~
(a)
first -m~
L 3
4
50 -I
20 0
80 I
10 J
(b)
Figure. 9.3.16 Array representation of a linked list
Q Systematic Approach to Data Structures using C - 9.61
avair-,c+O
I
2
first =-1 3
indicates user list is empty L ~
BE
initially.
(a) 99 -I
fi,,' ~
m
0
-0 30 I
I
I 40 2 2
, 2 avail
20 -I 3
,
.
L ,
i
!
(b)
99 EfB -I
first ---:... 0
x--:..
.
avail
~.....
....
index info link
'
1
30
40
20
I
2 .....................................
-I .... avail--+'~3
L
o
1
-................ 2
i
.r !
c=ho
(c) 99 CIIJ
index info link
first_o
index info link
~
'0
1 Bp
I~ avail_~
L
2
3 BE ,
,
cJ98l
99 CIIJ
(d)
Fig.9.3.17 Array representation of linked list
9.62 Q Linked Lists
In general, given an index i, L[i].info gives the actual data and L[i].link gives the
index of the next item. If L[i].link has the value -1, then end of list is reached. If an
index variable i has-the value 1, L[i].link has the value -1 indicating it is the last node
and the last item 50 can be accessed using L[i].info. An availability list L, which is an
array of free nodes can have the following structure:
struct node
{
int info;
NODE link;
.where info field contains the actual information and link field contains the index of
the next node and is of type NODE (since index is an integer, NODE is defined as int
using typedef). We use an array L to represent the availability list and the index
variable avail, which contains 0; the index of the first free node in the list L. Assume
L and avail are global variables. Assume the size of the availability list is
MAX_SIZE. The initial organization of the availability list is
avail = 0;
L[MAX_SIZE-l].link = null;
Note that null can be defined as -1. If L[i].link is null i.e., -1, then the node
corresponding to the index i is the last node. The pictorial representation of the
availability list is shown in fig.9.3.17.a.
To insert an item, a node has to be obtained from the availability list. Note that
initia value of the global index variable avail is O. So, the node at the index 0 is
returned if there is a request from the user. Once the node is returned, avail should
point to the next free node. This can be achieved by accessing the link field of avail
and assigning this back to avail. In linked list representation this can be achieved by
specifying avail = avail->link. But, using an array representation, this can be
achieved by the following statements.
Q Systematic Approach to Data Structures using C - }:?~
x = avail;
avail = L[avail].link;
return x;
In the linked list while updating the pointer variable, it may point to NULL, indicating
last node in the list is reached. In this representation, while updating avail, this may
point to null, which in this case is -1. If avail has the value -1, it indicates that
availability list is empty and memory cannot be allocated. The function to allocate
memory is shown below:
NODE gctnode(void)
{
NODE x;
if ( avail = null )
{
printf(tlOut of memoryvn");
cxit( 1);
x = avail;
avail= L[avail].link;
return x;
Suppose3 items 30, 40 and 20 are inserted into the list in the same order. The user list
anti the availability list is shown in fig.9.3.17.b. Note that the index variable first
containsthe index of the first item i.e., 30.
To delete a node and to return this to the availability list, consider the array
shown in fig.9.3.17.c. Suppose x is the node to be returned to the availability list-
Then, the node x should be the first node in the availability list and avail should point
tox, so that when a new node is requested, the node just returned to the availability
listcan be re used. This can be achieved by assigning avail to link field of the noele to
be deleted i.e., x and then assigning x to avail. After deleting the node x, the resulting
9.64 Q Linked Lists
user list and the availability list is shown in fig.9.3.17.d. The C function to return a
node to the availability list is shown below:
Example 9.3.16.2: Function to return a node to the availability list using arrays
void freenode(NODE x)
{,
L[x].link = avail;
avail = x;
return;
'-
Given an array representation of a linked list, we see how to implement deques. The
algorithm remains same. But, in the program, instead of using pointers and dynamic
variables, we use an array representation. Note that to access info field of a node x, in
linked representation we use the notation x->info where as in the array representation
we use the notation L[x] .info. The complete program to implement deques using an
array representation of a linked list is shown below:
#inc1ude <stdio.h>
#inc1ude <process.h>
struct node
{
int info;
NODE link;
};
1
return;
avail = 0;
NODE getnode(void)
{
NODE x;
if ( avail = null )
{
printf("Out of memory\n");
exit(1);
}
x = avail;
avail = L[avail].link;
return x;
9.66 Q Linked Lists
if ( first =-~null )
{
printf("List is cmpty\n");
return;
}
temp = gctnoder);
L]temp]. info = item;
L[temp].link = null;
L[cur].link = temp;
return first;
}
Q Systematic Approach to Data Structures using C - 9.6~
if ( first == null )
{
printf("List is empty\n");
return first;
}
prey = null;
cur = first;
while (L[ cur].link != null)
{
prcv = cur;
cur = L[cur].link);
}
if ( prey = null )
first = null;
else
L[prcv].link = null;
return first;
'*ODE
function to delete an item at the front end */
dcletc_1i"ont(NODE first)
{
NODE temp;
if ( first == null )
{
printf("List is empty\n");
retu rn first;
9.68 Q Linked Lists
temp = first;
first = L[ first].link;
printf("The item deleted is %d\n",L[temp].info);
freenode( temp);
return first;
} .
/*function to insert item at the front end */
NODE insert_front(int item, NODE first)
{
NODE temp;
.. temp = getnodet);
L[temp].info = item;
L[temp].link = first;
return temp;
}
void maim)
{
NODE first = null;
int choice, item;
initializet);
for (;;)
{
printf("l:Insert front 2:Insert rear\n");
printf("3:Delete front 4:Delete rear\n");
printf("5:Display 6:Exit\n");
printf("Enter the choice\n");
scanf("%d",&choice);
switch ( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d",&item);
first = insert_front(item,first);
break;
Q Systematic Approach to Data Structures using C - 9.69
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
first = insert_rear(item,first);
break;
case 3:
first = delete _front(first);
break;
case 4:
first = deletereanfirst);
break;
case 5:
display(first);
break;
default:
exit(O);
}
}
Note: Compare this program with the program given in example 9.11. All statements
are same. The only difference is that, if x is the address of the node then in the linked
representation link (x) and info(x) can be accessed using the statements
It is left to the reader to implement rest of the programs on linked lists using an-array
representation.
Now, we should be in a position to answer the question "What are the drawbacks of
using sequential storage (arrays) to represent stacks and queues?". The various
drawbacks are listed below:
• A fixed amount of storage is allocated: The compiler allocates only specified
number of memory locations for stacks and queues. If more memory is allocated
9.70 Q Linked Lists
but using only small amount of memory results in wastage of memory space. II'
less memory is allocated when we insert more items, there is possibly of ovcrl1ow
• Items are stored contiguously: Some times enough contiguous memory locations
may not be available. There are situations where a number of chunks 01
contiguous memory locations are available. Even though the total free memory
space is sufficient, this space cannot be used since stack items and queue items
. require contiguous storage space.
Now, let us see "What are the drawbacks of representing a stack or a queue using
linked list?"The various drawbacks are listed below:
• A node in a linked list may have two or more tields and naturally occupies more
storage space than the corresponding element in the array.
• Accessing the data related to stack or a queue is much slower using linked list
since additional time is spent in managing the available list. Only when required
memory is allocated. So, time is required to get a node ti"OI11 the available list.
When a node is returned using free, the unused node has to be added to the
beginning of available list. Thus, each addition and deletion of an clement
involves corresponding deletion and addition in available list.
Now, let us see "What are the differences between static allocation and linked
allocation?"The differences are listed below:
ote: For certain operations and applications linked allocation is more useful ail I
efficientand for some other operations and applications sequential allocation is mor.:
usefuland efficient. So, it is the responsibility of the programmer to choose the cI~![;!
structuresproper! y for the efficient utilization of the resources.
9.72 Q Linked Lists
EXERCISES
1. Write a C function which will perform an insertion to the immediate left of the klh
node in the singly linked list.
2. Write a C function to count the number of nodes' in a singly linked list. . - '
3•. Write a C function to change theinfo field of the k" node to the value given by X.
4. Write a C function to concatenate two lists into a single list.
5. Write a C function to reverse a given. singly linked list without creating new
nodes.
(l. Write a C function search(p,x) that accepts a pointer p to a list of integers and an
integer x and .rctums a pointer to a node containing x, if it exists, and the NULL
pointer otherwise.
7. Write a C function srchinsrt(p,x) that adds an item x to the list pointed-to by p,
provided x is not in the list. Insertion can be done at the front end or rear end.
8. Write a C function to delete a node from the front end and insert it at the-end of
the list.
9. Write a recursive program to implement the following functions.
a. To insert an item into an ordered singly linked list
b. To insert an item into an ordered doubly linked list
c. To display the contents of singly linked list
d. To display the contents of doubly linked list
e. To search for a specific node.
f. To find the union of two linked lists
g. To find the intersection of two linked lists
h. To delete a node in a singly linked list whose position is specified
1
Q Systematic Approach to Data Structures .uslag C.,. 9;7~
Note: IQ singly linked .list that we have discussed earlier, note that link field of las
node contains \0 (null) indicating there are no more nodes from this point onwards.
Now, let us see "What are the disadvantages of singly linked lists?" The variou:
disadvantages of singly linked lists are listed below: ,
• In a singly linked list, there is only one link field and hence traversing (visitin]
each node) is done only in one direction. So, given the address of a node x, onh
those nodes which' follow x are teachable but, the nodes 'that precede x are nr
reachable. To reach the nodes that precede x, it is required to preserve a pointe
first which contain the address of the first node of the list. .
Note: Since traversing is only in one direction, singly linked lists are also caller
one W~ly lists. '
• To delete a designated node cur, address of the first node of the list should b
provided. This is necessary because, to delete node cur, the predecessor of thi:
node has to be found. For this, search has to begin from the first node ofthe list.
These disadvantages can be overcome using special type of list called circular list
Now, let us see "What is circular list?"
Definition: In singly linked list, the link field of the last node contains \0 (null)
Instead of storing \0 in the link field. of last node, a number of advantages can be
gained if link field of the last node contains starting address of the first node. Such l
list is called circular list. In general, a circular list is a variation of ordinary linked Ii:
in which link field of the last node contains address of the first node.
This list is primarily used in structures that allow access to nodes in the middle
of the list without starting from the first node. The' pictorial representation of l
circular list is shown below:
9.7~ Q Linked Lists
Now.Jet us see "What are the advantages of circular lists?" The various advantages of
circular list are shown below: . . -
• Every node is accessible from agiven node by traversing successively using the
link . field .
• To delete a node cur, the address of the first node is not necessary (but it is
•. • •• ." •• < J
necessary in singly linked list). Search tor the predecessor of node cur, can be
initiated from cur itself.
• Certain operations on circular lists such as concatenation and splitting of lists etc.
will be more efficient
In singly linked list, the link field of last node contains \0 (null) designating end of
list. But, in circular list, this \0 is replaced by the address of the first node of the list.
There is no special indication to find which is the last node. So, some care has to be
taken during processing. The disadvantage 0 f these circular lists is that if proper care
is not taken it is possible that we may end up in an infinite loop unless we detect the
end ofthc list. In circular list, it is very important to detect the end of tile list.
Since the list is circular, any node can be considered as first node and its predecessor
is considered as last node. The following two conventions can be used: .
• Approach 1: A pointer variable first can bc used to designate the starting point of
the list. Using this approach, to getthe address of the last node, the entire list has
to be traversed from the first node. The pictorial representation of the circular list
using this approach is shown in figure. 9.5.1 (see top of this page)
Q Systematic Approach to Data Structures using C- 9.75
Observe from the above figure that, given pointer last which points to last node, by
using link field of last node, i.e, last->link, we can get the address of the first node.
Note: link field oflast gives address of first node. So, we use the second approach in
our discussions for convenience.
Whatever operations are possible using singly linked lists, all those operations can be
performed using 'circular lists also. .
A circular list can be used as a stack, queue or a dequeue (double ended queue). To
implement these data structure we require the following functions:
• insel·t_fron~- To insert an clement at the front end of the list
• lnsertjrear - To insert an element at the rear end of the list
• delete_front _. To delete an clement from the front end of the list
• dclete_rear - To delete an' element from the rear end of the list
• display - To display the contents of the list
Now,let us see "How to insert a node at the front end of the list?"
Design: To design the function easily, let us consider a list with 4 nodes. Here,
pointer last contains address of the last node of the list. Let us try to insert an item at
thefront end of the list. The sequence of steps to be followed are shown below:
9.76 Q Linked Lists
(a)
last@
(b)
Fig. 9.5.1.1 To insert at the front end
Step 1: To insert an item 50 at the front of the list, obtain a free node temp from the
available list and store the item in info field as shown in figure (gray color). This can
be accomplished using the following statements
temp = getnodet);
temp-e-info = item;
.
Step 2: Copy the address of the first node(i.e., last->Iink) into link field of newly
obtained node temp and the statement to accomplish this taskis
temp->link = last->Iink;
Step 3: Establish a link between the node temp and the last node. This is achieved
by copying the address of the node temp into link field of node last. The
corre ponding code for this is
last->link = temp;
Step 4: Finally, we return address of the last node using the statement:
retu rn last;
~ Systematic Approach to Data Structures using C - 9.7'
The above steps have been designed by assuming that the list is already existing. I
the list is empty, make temp itself as the node last and establish a link between th:
first node and the last node. The C function to insert an item at the front of thl
circular linked list is shown below:
Example 9.5.1.1 Function to insert an item at the front end of the list
Now, let us see "How to insert a node at the rear end of the list?"
Design: To design the function easily, let us consider a list with 4 nodes. Here,
pointer last contains address of the last node of the list. Let us try to insert an item at
the rear end of the list. The sequence of steps to be followed are shown below:
Let us insert the item 80 at the end of this list. Follow the steps shown below to insert
an item at the rear end of the list.
Step 1: Obtain a free node temp from the availability list and store the item in info
field as shown in figure below. This can be accomplished using the following
statements
temp = getnodei);
temp->info = item;
9.78 Q Linked Lists
(b)
Fig. 9.5.2.1 To insert at the rear end
Step 2: Copy the address of the first node(i.e., last->link) into link field of newly
obtained node temp and the statement to accomplish this task is
temp->link = last->link;
Step 3: Establish a link between the newly created node temp and the node last. This
is achieved by copying the address of the node temp into link field of node last. The
corresponding code for this is
last->link = temp;
Step 4: The new node is made as the last node using the statement:
return temp;
These steps have been designed by assuming the list is already existing. If the list is
empty make temp itself as the first node as well as the last node. The C function for
this is shown below:
Q Systematic Approach to Data Structures using C - 9.79
Example 9.5.2.1: Function to insert an item at the rear end of the list
fil'StG)
Now, the node first is deleted. These steps have been designed by assuming the list is
already existing. If there is only one node, delete that node and assign NULL to last
indicating the list is empty. The code corresponding to this is can be
All these steps designed so far have to be executed provided. the list is not empty. If
the list is empty, display the appropriate message. The complete code to delete an
item from the front end is shown below:
Step 1:Obtain the address of the predecessor of the node to be deleted. This can be
accomplished by traversing from the first node till the link field of a node contains
address of the last node. The code corresponding to this is
prey = last->link;
while (prev->link != last)
{
prey = prev->link;
}
9.82 Q Linked Lists
Step 2: The first node and the last but one node( i.e., prev) are linked. This can be
accomplished using the statement
prev->link = last->link;
Step 3:The last node can be deleted using the statement
freenode(last );
Step 4: Return prey itself as the last node of the result using the statement:
return(prev);
All these steps have been designed by assuming that the list is already existing.
• If there is only one node, delete that node and assign NULL to the pointer variable
last indicating that the list is empty. The equivalent statements are:
if ( last->link = last) 1* Delete if only one node ./
{
printf("The item deleted is %d\n", last->info);
freenode(last) ;
return NULL;
}
• If the list is empty in the beginning display the appropriate message.
The C function to delete an item from the rear end of circular list is shown below:
Example 9.5.4.1: Function to delete an item from the rear end ; ..•.
The C function to display the contents of circular list is shown below: The read
required to understand how the function is working.
temp = last->Iink; .
while (temp != last)
{
printf("%d ",temp->info);
temp ~ temp->link;
}
#include <stdio.h>
#include <alloc.h>
#include <process.h>
struct node
{
int info;
struct node * link;
};
1* Include: Example 9.2.1.3: Function to get new node from the availability list *1
1* Include: Example 9.2.1.4:C Function to fi:ee a node to the availability list *1
1* Include: Example 9.5.1.1:Function to insert item at front end of the list *1
1* Include: Example 9.5.2.1:Fullction to insert item at rear end of the list *1
1* Include: Example 9.5.3.1:Function to delete an item from the front end *1
1* Include: Example 9.5.4.1:Function to delete an item from the rear end */.
1* Include: Example 9.5.4.2: Function to display the contents of the circular queue *1
void maint)
{
NODE last;
int choice, item;
last = NULL;
for (;;)
{
printf("l :Insert_Front 2:Insert_Rear\n");
printf("3:Delete _Front 4:Delete _Rear\n");
printf("5:Display 6:Exit\n'.');
printf("Entei the choice\n");
scanf("%d", &choice);
Q Systematic Approach to Data Structures using C - 9.85
switch( choice)
{
case 1:
printf("Enter the item to De inserted\n");
scanf("%d", &item);
last = insert_front (item, last);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d", &item);
last = insert_rear (item, last);
break;
case 3:
last = delete _front(1ast);
break;
case 4:
last = delete Jear(1ast);
break;
case 5:
display(last);
break;
default:
exit(O);
}
}
9.6Header Node
Before seeing what is a header node, let us ask one question ourselves "Have we
faced any problem when we manipulate the singly linked lists?" To answer this
question, consider the program to delete a node whose information field is specified
insection 9.3.9. Observe that during programming we have to take too many extreme
casessuch as:
• If list is empty what to do?
• If item to be deleted is present in the first node what to do?
• Otherwise what to do?
Wehave written the code for all these cases. If we have too many cases like this, the
lengthof the program will increase and naturally it will be difficult to read and
9.86 Q J :nked Lists
understand. So, it is better to minimize the number of checking for various cases like
this. This is possible with a special type of node called Header node. Now, we shall
see' \\'hat is a header node?"
lrefluitlon: A header node in a linked list is a special node whose link field always
contains address of the first node of the list. If list is empty, then link field of header
node contains \0 (null). The header node may have one or more links pointing to
di fferent types of linked lists. Using header node, any node in .the list can be accessed.
The info field of such a node usually does not contain any information and such a
node does not represent an item in the list. Sometimes, useful information such as,
n urn her 0 f nodes in the list can be stored in the info field.
For example, consider the list with a header node shown in figure below:
I 13-1 G4 G4 [3-4 0 20 4S 10 80
(b)
Number of
nodes in list (c)
Observe the following points with respect to lists shown in above figure:
• Since the link field of the header node shown in figure 9.6.1.a contains \0, it
represent an empty list. .
• In figure 9.6.l.b, link field of a node contains address of the first node of the list
which has 4 items.
• The info field of header node in figure 9.6.1.c contains 4, which represents the
total number of nodes present. in the list. In such a case, there is an overhead of
altering the info field of this node. Each time a node is added or deleted, the count
in this field must be readjusted so as to contain actual number of nodes to be
accessed in the list.
Q Systematic Approach to Data Structures using C - 9.87
Now, let us see "What are the advantages of a list with a header node?" The
advantages of a header node are shown below:
• Simplifies Insertion and deletion operations
• Avoid the usage of various cases such as "if only one node is present what to do"
• Designing of program will be very simple (compare the program in section 9.3.9
and section 9.6.2 )
• Circular lists with header node are frequently used instead of ordinary linked lists
because many operations can be easily implemented
Note: Given a header node, there is no need of a pointer variable that points to the
firstnode of the list or the last node of the list.
Now, let us see "What is circular singly linked list with header node?" In a circular
list with a header node, the link field of the last node contains address of the header
node and the link field of the header node contains the address of the first node.
Usually the info field of a header node does not contain any information. Sometimes,
some useful information such as number of nodes in the list can be stored.
Fore example, consider the list shown in fig.9. 7.l.a. The info field of this
header node contains 4, which is the number of nodes that can be accessed from the
header node. An empty circular list with a header node is shown in fig.9.7.l.b. When
the list is empty, the link field of a header node contains the address of itself.
(a)
(b)
Fig. 9.7.1 Circular singly linked list
9.88 Q Linked Lists
Let us see "What are the advantages of circular singly linked list with a header node?"
A circular list with a header node is very useful since some of the problems can be
solved very easily and efficiently.
For example, problems such as addition of long positive numbers, merging of
two ordered lists into a single ordered list etc. are much easier to design. In this
section let us concentrate on some problems that can be solved using a circular list
with a header node.
9.7.1 Insert a node at the front end
Now, let us see "How to insert an item at the front end?" Consider the circular list
with a header node shown in fig.9.7.1.1. Following the sequence of steps shown in
fig.9.7.1.1, an item 50 can be inserted at the front end of the list.
- -
Step 1: Obtain a node from the availability list and store the item. The following
statements will do this job.
temp = getnodet);
temp->info = item;
Step 2: Establish a link between the newly created node pointed to by temp and the
first node. The corresponding code to accomplish this tusk is
temp->link = head->link;
Step 3: The newly created node pointed to by temp is made the first node by
establishing a link between header node and temp using the following statement.
head->link = temp;
Step 4: Finally return the address of the header node using the following statement:
return head;
Q Systematic Approach to Data Structures using C - 9.89
Now, let us see "How to insert an item at the rear end?" To insert an item at the rear
end of the list, it is necessary to obtain the address of the last node. This can be
achieved by traversing the list from the first node. An auxiliary pointer variable cur,
which initially points to the first node, can be used to traverse till the end. The
statements to accomplish this task are:
cur = head->link;
while ( cur->link != head)
{
cur = cur->link;
}
Once the address of the last cur is known, the node pointed to by temp can be easily
inserted by following the sequence of steps shown in figure 9.7.2.1. The steps to be
followed are shown below:
Step 1: Establish a link between the last node cur and the node to be inserted at the
end temp. This can be accomplished using the following statement
cur->link = temp;
Step 2: Establish a link between the new last node temp and the header node. This
canbe achieved by using the following statement.
temp->link = head;
Step 3: Finally return address of the header node using the following statement:
return head;
9.90 Q Linked Lists
Head
IPG)
Note:" All these steps have been designed by assuming the address of the node to be
inserted i.e., temp is known. So, just before these steps, obtain the node to be inserted
and store the item.
The C function to insert an item at the rear end of the circular linked list is shown
below:
Note: Refer section 9.3.9 for the details. As we discussed earlier in section 9.3.9, it is
required to search for the node whose info field is specified and obtain the address of
the node to be deleted along with its predecessor. So, two auxiliary pointer variables
prey pointing to the predecessor of the node to be deleted and cur pointing to the
node to be deleted are used. The search starts from the first node of list (head->link).
So, point cur to the first node and prey to header node initially. When cur finally
points to the header node, stop searching and indicate that the specified item is not
there in the list. Otherwise, delete the node. The C function for this is shown below:
Step 1: As in the previous problem, obtain the address of the cur node and prey
based on the position. We have to keep track of count to find the exact position. The
equivalent statements are shown below:
prey = head;
cur = head->link;
count = 1;
".while (cur != head)
{
if (count = pos) break;
prey = cur;
cur = cur->link;
count++;
}
Step 2: Check for the appropriate position. This IS done usmg the following
statement:
if (count != pos)
{
printf("Invalid position\n");
return head;
}
Note: If count is same as pos, it is valid position and it is required to insert a newly
created node between cur and prey as shown in figure 9.7.4.1
" Step 4 and 5: /* insert the newly created node between prey and cur */
temp->link = cur;
prev->link = temp;
Step 6: Finally, return the address of the first node of the list.
retu rn head;
Q Systematic Approach to Data Structures using C - 9.93
The complete C function to insert an it~m at the specified position is shown below:
. n \
prev->link = temp; /* Insert between prev an /
temp->link = cur;
return head;
}
Note: Already discussed in section 9.3.10. But, because we are using header node, the
design becomes simple and complexity of the program will be simple.
Step 1: Check for empty list and display the appropriate messages usmg the
following code:
if (head->link = head)
{
hi
printf("List is empty\n");
return head;
3001'
}
Step 2: As in the previous problem, obtain the address of the cur node-to be deleted
along with its prev node (predecessor of cur node). For this we have to keep track of
counter to find the exact position. The equivalent statements are shown below:
Step 3: Check for the appropriate position. This IS done usmg the following
statement:
if (count != pos)
{
printf("Invalid position\n");
return head;
}
Note: If count is same as pos, it is valid position and using cur node can be deleted
asshown below:
Step 4: Isolate the node pointed to by cur by establishing the link between the
predecessor and successor of the node to be deleted using the statement:
prev->link = cur->link;
freenode (cur);
return head
return head;
}
The C function to display the contents of circular list with a header node is shown
below:
Q Systematic Approach to Data Structures using C - 9.97
Example 9.7.5.2: Function to display the contents of circular list with header node
if (head->link = head)
{
printf("List is empty\n");
return;
}
printf("\n");
#include <stdio.h>
#include <alloc.h>
#include <process.h>
struct node
int info;
struct node *link;
};
1* Include: Example 9.2.1.3: Function to get new node from the availability list *1
1* Include: Example 9.2.1.4: C Function to free a node to tOOa ai t list.>!<
L
1* Include: Example 9.7.1.1: Function to insert at the front end of the list *1
1* Include: Example 9.7.2.1: Function to insert at the rear end of the list k/( II
1* Include: Example 9.7.3.1: Function to delt node whose info field is specified *1
1* Include: Example 9.7.4.1: Function to insert an item at tlle speBiJ1ea po 'itidn *1
1* Include: Example 9.7.5.1: Function to deletean item at the specified position *1
1* Include: Example 9.7.5.2: Function to display the cbrhcnts of it ular *1
.n ut 1
void maim)
{
" t' I f
"NODE head;
int choice, item, pos;
head = getnodet);
head-s-info = 0;
head-c-link = head; 1* Empty header node *1
for (;;)
{
printf(" 1:Insert_front 2:Insert_At_Position\n");
printf{"3:Delete item 4:Delete_At_Position\n");
printf("S:Display 6:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
rIh
scanf("%d" ,&item);
head = insert_front(item, head);
tni
break;
')U1.
case 2:
printf("Enter the item to be inserted\n");
scanf("%d ,&item);
II
scanf("%d",&pos);
head = insert ---'position(item, pos, head);
break;
case 3:
printf("Enter the item to be deleted\n");
scanf("%d" ,&item);
head = delete_item(item,head);
break;
case 4:
printf("Positon of node to be deleted = ");
scanf("%d",&pos); .
head = delete ---'position(pos,head);
break;
case 5:
displaytheadj. II
break;
default:
exit(O);
}
}
Note: Whatever operations are possible using ordinary linked list, the same
operations can be performed using linked list with a header node. Also observe that
elegant programs.
.
using a list with ,a header node simplifies the design and we can write simple and
.
' .
, r
Now, let us see "What are the disadvantages of singly linked lists whether circular or
with or without the header?" Some of the disadvantages of singly linked lists/circular
lists are: 'I J' "I ~ t:
• Using singly linked lists and circular lists it is not possible to traverse the list
backwards. Two way traversing is not possible.
• Insertion/Deletion to the left of a designated node x is difficult. This requires
finding the predecessor of x which takes more time.
r u J
• Given a node x, it is difficult to fmd the predecessor ofx. To fmd the predecessor,
it is required to traverse the list from the first node in case of singly linked list. In
case of circular list, -the predecessorcan be obtained by traversing the whole list
from the node specified. For example, if the position of the current node is 15, to
find the position of node 14, the whole list has to be traversed. All these
disadvantages can be overcome by using doubly linked lists. "
9.100 Q Linked Lists
Definition: One of the most powerful variations of linked lists is the doubly linked
list. A doubly-linked list is a linear collection of nodes where each node is divided
into,three parts:
• info - This is a field where the information has to be stored
• llink - This is a pointer field which contains address of the left node or
previous node in the list
• rlink - This is a pointer field which contains address of the right node or next
node in the list
Using such lists, it is possible to traverse the list in forward and backward directions.
Such a list where each node has two links is also called a two way list. The pictorial
representation of a doubly linked list is shown in figure 9.8.l.a. In this list, observe
that the llink field of the leftmost node and rlink field of rightmost node points to
NULL. The two variations of doubly linked lists are:
• Circular doubly linked list
• Circular doubly linked list with a header node
Definition: A circular doubly linked list is a variation of doubly linked list in which
• rlink (right link) of the last node contains address of the first node
• llink (left link) of the first node contains address of the last node
The pictorial representation of circular doubly linked list is shown in figure 9.8.l.b
Now, let us see "What is circular doubly linked list with a header?"
Definition: A circular doubly linked list (can also be called doubly linked circular
list) is a variation of doubly linked with a header node in which:
• Ilink of header contains address of the last node of the list
• rlink of header contains address of the first node of the list.
• llink of last node contains the address of last but one node of the list
• rlink of last node contains the address of the first node of the list
The pictorial representation of circular doubly linked list with a header node is shown
in figure 9.8.l.c. This list is primarily used in structures that allow access to nodes in
both the directions. For example, addition of long positive numbers (discussed in the
next section). .
~ Systematic Approach to Data Structures using C - 9.H
Note: An empty list with a header node can be represented as shown in fig.9.8.I.I
where llink and rlink of a header node points to itself.
(b)
head
t
(c)
(d)
Fig. 9.8.1 Variations of doubly linked lists
Note: All the problems that can be solved using singly linked lists can also be solve
using doubly linked lists. It is left to the reader to implement all the problems solve
so far, using doubly linked lists and doubly linked circular list. Given any problem It
us implement them using doubly linked lists and with a header node. Using a heade
node, problems can be solved very easily and effectively.
9.102 Q Linked Lists
Now, let us see "How to insert an item at the front end of the list" Consider the list
shown in fig.9.8.1.1. To insert a node pointed to by temp at the front of the list,
address of the first node i.e., pointed to by cur should be known. This can be
accomplished by using the statement
cur = head->rlink;
The node pointed to by temp can be easily inserted between head node and the node
pointed to by cur (follow the dotted lines). This can be accomplished by using the
following statements.
head->rlink = temp;
temp->llink = head;
temp->rlink = cur;
cur->llink = temp;
The C function to insert at the front of a doubly linked circular list with a header node
is shown in example 9.8.1.1.
Example 9.8.1.1: Function to insert a node at the front end of the list
To insert a node at the rear end of the list, consider the list shown in fig.9.8.2.1 and
try to write the corresponding code. After writing the code compare this with the
function insert _ frontt). Note that the two functions are same except that llink and
rlinkhave been exchanged.
Note: The reader should know the simplicity of the code using double linked circular
list with header node. The C function to insert an item at the rear end of the list is
shownbelow:
Consider the list shown in fig.9.8.3.l. Find the address of the first node to be deleted
using the statement:
cur = head->rlink;
Also obtain the successor of the node to be deleted using the statement:
next = cur->rlink;
Once the addresses of the node to be deleted, its predecessor and successor are
known, following the sequence of numbers shown in fig.9.8.3.1, the node at the front
end can be deleted. The steps to be followed are shown below.
~ Systematic Approach to Data Structures using C - 9.105
Step 1, 2: Establish a link between the header node and successor of the node to be
deleted i.e., next in both directions. This can be accomplished using the statement
head ->rlink = next;
next->llink = head;
Step 3: Once these statements are executed, the node to be deleted namely cur is
isolated and it can be deleted. The corresponding statements are:
printf("The item deleted is %d\n",cur->info);
freenode( cur);
Finally return the address of the header node. Note that all these steps have been
designed by assuming list is already existing. If list is empty display the appropriate
message. The C function to delete an item from the front end of the list is shown
below:
The C function to insert an item at the rear end of the list is shown below:
The node pointed to by cur is the node to be deleted, the node pointed to by prey is
the predecessor and the node pointed to by next is the successor of the node to be
deleted. Once the addresses of these nodes are known, establish the link between
predecessor and successor nodes and delete the node pointed to by cur. Following the
sequence of steps shown in fig.9.8.5.!, the node cur can be deleted. The
corresponding statements are:
prev->rlink = next;
next->llink = prev;
freenode( cur);
Finally, return the address of the header node. All these steps have been designed by
assuming list is present. If list is not present, display the appropriate message. The C
function to delete a node whose information field is specified is shown below:
if ( head->rlink = head)
{
printf("List is empty\n");
return head;
}
if ( cur = head)
{
printf("Item not found\n");
return head;
}
Q Systematic Approach to Data Structures using C - 9.109
freenode( cur);
The procedure for this problem is same as discussed in previous section. But,
immediately after deleting the specified node, search for the key specified again from
successor node onwards. If the key is found, delete the node and repeat the process.
The modified C function to delete all nodes whose information is same as the key
specified is shown below:
Example 9.8.6.1: Function to delete all nodes whose info is same as key item
if ( head->rlink = head)
{
printf("List is empty\n");
return head;
}
if( count = 0)
printf("Key not found\n");
else
printf("Key found at %d positions and are deleted'n't.count);
return head;
}
In this function the variable count is used to find the number of occurrences of the
key in the list. If key is found, the variable count is incremented by one and the
corresponding node is deleted and search for the key from the next node onwards and
repeat the process. Even after searching the entire list, if count is zero, it means that
key is not there in the list.
9.8.7 Insert a node after the key and before the key
Search for the specified key as explained in the section 9.7.5. Insert the specified item
towards right of the node whose info is same as the key. Similarly an item can be
inserted to the left of a designated node. The C functions, to insert an item towards
right of a node and towards left of a.node are shown in examples 9.48 and 9.49
respectively. The reader is supposed to write the appropriate figures and trace these
functions. The code is straightforward and simple.
Q Systematic Approach to Data Structures using C - 9.111
cur->rlink = temp; /* insert this new node between cur and next */
temp->llink = cur;
next->llink = temp;
temp->rlink = next;
temp = getnodet);
scanf("%d" ,&temp->info);
TheC program to implement all these operations discussed in this section, is shown
below:
#include<stdio.h>
#include<alloc.h>
#include<process.h>
struct node
int info;
struct node *llink;
struct node *rlink;
};
typedef struct node* NODE;
1* Include: Example 9.2.1.3: Function to get new node from the availability list */
1* Include: Example 9.8.3.1: Function to delete a node from the front end */.
1* Include: Example 9.8.4.1: Function to delete a node from the rear end */
/
1* Include: Example 9.8.5.1: Function to delete a node whose info is provided */ /
1* Include: Example 9.8.6.1: Function to delete all nodes after searching for key *1,
if ( head->rlink == head)
{
printf("Deqye is empty\n");
return;
}
void maim)
{
NODE head;
int choice, item;
head = getnodet);
head->rlink = head;
head->l1ink = head;
for (;;)
{
printf(" 1:Insert_ front 2:InsertJear\n");
printf("3:Delete _front 4:Delete _rear\n");
printf("5:Display 6:Delete _info\n");
printf("7:Delte_keys 8:Insert after\n");
printf("9:Insert before lO:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
Q Systematic Approach to Data Structures using C - 9.115
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
head = insert_ front(item,head);
break;
case 2:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
head = insertrearutem.head);
break;
case 3:
head = delete_front(head);
break;
case 4:
head = delete Jear(head);
break;
case 5:
display(head);
break;
case 6:
printf("Enter the item to be deleted\n");
scanf("%d" ,&item);
head = delete_item(item,head);
break;
case 7:
printf("Enter the key to be delted\n");
scanf("%d" ,&item);
head = delete _ all(item,head);
break;
case 8:
printf("Enter the key\n");
scanf("%d" ,&item);
head = insert _right(item,head);
break;
case 9:
printf("Enter the key\n");
scanf("%d" ,&item);
9.116 Q Linked Lists
head = insert_Ieft(item,head);
break;
default: exit(O);
}
}
}
Now, let us see "What is the difference between singly linked and doubly linked list?"
directions
2. While deleting a node, its predessor is 2. While deleting a node x, it
required and can be found only after predecessor can be obtained using
traversing from the beginning of list llink of node x. No need to traverse
the list
3. Occupy less memory 3. Occupy more memory
4. Programs will be lengthy and need 4. Using circular linked list with header, .
more time to design efficient and small programs can be
written and hence design is easier
5. Care is taken to modify only one link 5. Care is taken to modify both links of a
ofa node node
Now, let us see "What are the advantages of doubly linked lists?" The advantages of
doubly linked lists are shown below:
• Using doubly linked lists with a header node most of the problems can be
solved very easily and effectively.
• The doubly linked lists are extensively used in trees. The hierarchical structure
of the tree can be easily represented using a doubly linked list.
• Graphs can be represented using doubly linked list.
Now, let us see "What are the disadvantages of doubly linked lists?" Some of the
disadvantages of doubly linked lists are:
• Each node in the list requires two links one is forward link and the other
backward link requiring additional storage for each field.
• While manipulating the lists extra care should be taken to manipulate both
links.
.Q Systematic Approach to Data Structures using C - 9.117
Exercises
In this section, let us see "What are the applications of linked lists?" The various
applications of linked lists are shown below:
Arithmetic operations on long positive numbers
Manipulation of polynomials I
Applications of
linked lists Evaluation of polynomials
In symbol table construction (Compiler design)
Now, Ietus see "Why very large numbers can not be added using + operator?" Some
applications may require various operations on very large numbers. But, all the
computers have a certain maximum number of bits required for representing an
integer, float etc. If the size of the numbers exceed this limit, ordinary arithmetic
operations can not be performed. Using linked lists, we can represent these large
numbers and any arithmetic operation can be performed on these large numbers.
Now, let us see "How to write algorithm/function to add two long positive numbers?"
This achieved by splitting the program into various modules based on activity
performed as shown below:
• Reading a long positive number
• Writing a long positive number
• Adding tw9 long positive numbers
Now, let us see "How to read a long positive number using linked list?"
9.118 Q Linked Lists
Design: Let us design by taking the number 6698274. This number can be split into
groups of one digit each as shown below from the most significant digit to least
significant digit.
6,6,9,8,2,7,4
Since there are 7 digits, a singly linked list with seven nodes can be used to store each
digit in each node. Consider the way the numbers are displayed on the display portion
of the calculator. If the number 6698274 is typed, display portion of the calculator
may look like as shown below.
6
66
669
6698
66982
669827
6698274
Note that as the new digits are typed, old digits will be shifted one digit left thereby
making room for the most recently typed digit. Thus the first digit typed will be the
leftmost digit of the number and the last digit typed will be the right most digit of the
number. Ifthe position of the least significant digit i.e., right most digit of a number is
considered as the front end, the digits that are typed will be inserted at the front end.
The linked representation for the number typed i.e., 669827 using circular
singly linked list with a header node is shown in fig.9.9.1.1. The next digit typed i.e.,
4 will be the new least significant digit and has to be inserted immediately after the
header node. Inserting immediately after the header node is considered as inserting an
element from the front end. Inserting temp, a node containing the digit 4 at the front
end of the list is shown using gray lines in fig.9.9.1.1. The resultant integer stored in
the result will be 6698274.
~)<Jo
..•. ~......... 1£.•.••.•••.•.•••.• head
CJIJ
temp
To accomplish this task the function insertfrontt) (example 9.5.1.1 in section 9.5.1)
can be used. Using the function insertfrontt), a function read_numberO to read a
long positive number can be obtained and is shown below:
.!;1,Systematic Approach to Data Structures using C - 9.119
return head;
Note: In this function the characters are read from the keyboard and if the character
typed is a digit, it is inserted into the list. Otherwise, appropriate message is
displayed.
Now, the next question is "How to display the number stored in a linked list?" Note
that the digits have to be displayed not from the first node, but from the last node to
the first node. This is the point where doubly linked list will be useful while
traversing backwards. This is not possible in singly linked list. So, to display the
linked list in reverse order, traverse the list in forward direction, copy the digits from
each node into an array and finally display the array items in reverse order. The code
corresponding to this is shown below:
printf("\n");
}
Now, we know the method of reading and displaying a number. Now, let us see "How
to add two numbers using linked list?" Suppose the two numbers to be added are 987
and 86. These two numbers can be represented using circular linked list with a header
node as shown below:
~14--- --IETIJ----IEb Hl
dTIJ~-----1ETIJ------1Eb H2
These two numbers should be added digit by digit from right to left i.e., from least
significant digit to most significant digit. A pointer variable c1 can be used to access
the digits from the first list and a pointer variable c2 can be used to access the digits
Q Systematic Approach to Data Structures using C - 9.121
fromthe second list. The corresponding digits along with a carry (initial carry 0 ) are
added. From the result obtained say sum, extract the digit using:
digit = sum % 10;
Add the resulting digit at the end of the partial result obtained so far. So, function
insertrear shown in example 9.5.2.1 section 9.5.2 can be used. Move on to the next
digit in both the numbers by updating c1 and c2 and repeat the process. When the end
of one of the list is encountered, add the carry to the remaining digits of the other list.
The C function to add two long positive numbers is shown below:
1* Include: Example 9.2.1.3: Function to get new node from the availability list *1
1* Include: Example 9.9.1.1: Function to read a long positive number *1
~ Systematic Approach to Data Structures using C - 9,]
struct node
{
float cf;
float px;
float py;
struct node *link;
};
Now'; let us see "How to represent a polynomial 5x\,z + 3x2 + 4x3y -5 using a
linked?" The following figure shows how the given polynomial is stored in a linked
list. .
cf px py link
~---------------~---------------~
term
(a)
5xlj -5
(b)
Since there are four terms in the polynomial, it is represented as. a linked list
consisting of four nodes. To design the algorithm, for the sake of simplicity we
assume each term in the polynomial is unique i.e., each term has different powers ofx
and y. Each term in the polynomial can have the same or different coefficient. We use
a singly linked circular list with a header node.
The pointer variable poly is used which contains address of the header node.
1
Each term of the polynomial can be inserted into the list from the rear end as .
discussed in section 9.5.2. The C function to add a term from the rear end of the list is
shown below:
~ Systematic Approach to Data Structures using C - 9.125
By repeatedly invoking the function insertreari), while adding a term, a linked list
representing a polynomial can be created. Once the co-efficient of term is -9999,
stop adding the term to the list. If the co-efficient of a term is not equal to -9999, it is
end of the polynomial and address of the header node is returned. The C function to
read a polynomial is shown below:
for(i = 1; ; i++)
{
printf("Enter the %d term\n",i);
printf("Coeff= "); scanf("%f',&cf);
if ( cf = -999 ) break;
printf("pow x = "); scanf("%f' ,&px);
printf("pow y = "); scanf("%f' ,&py);
head = insert_rear(cf,px,py,head);
}
return head;
}
Now, let us see "How to evaluate the polynomial?" To evaluate the polynomial we
should know the values of the variables x and y. Once these values are known,
substitute these values for the corresponding variables in each term and by adding all
the terms, the result is obtained. The function to evaluate the polynomial is shown
below:
Example 9.9.2.3:Function to evaluate a polynomial with two variables
float evaluate(NODE head)
{
float x,y,sum = 0;
NODE poly;
printf("Enter the value ofx and y\n");
scanf("%f %f' ,&x,&y);
poly = head->link; /* Access each term, substitute x and y */
while (poly != head)
{
sum += poly->cf * pow(x,poly->px) * pow(y,poly->py);
poly = poly->link;
}
return sum;
1
}
Now, let us see "How to display a polynomial represented as a linked list?" The
function to display the polynomial represented as a circular linked list with a header
node is shown below:
Q Systematic Approach to Data Structures using C - 9.127
float cf;
float px;
float py;
struct node *link;
};
typedef struct node* NODE;
1* Include: Example 9.2.1.3: Function to get new node from the availability list */
1* Include: Example 9.9.2.4: Function to display a polynomial */
1* Include: Example 9.9.2.3: Function to evaluate a polynomial with two variables */
'* Include: Example 9.9.2.2: Function to read a polynomial */
1* Include: Example 9.9.2.1: Function to insert a term at the rear end */
9.12& Q Linked Lists
void mairu)
{
NODE head;
float res;
head = getnodet);
head->link = head;
Output
Enter the polynomial
Enter the co-efficient as -999 to end the polynomial
Enter the 1stterm
Coeff = 5 pow x = 3 pow y = 2
Design: Let use circular list with a header node to represent a polynomial as
discussed in previous section. The pointer variable h I contains address of the header
node of the first polynomial and pointer variable h2 points to the header node of the
second polynomial. The first polynomial can be accessed from link of h l and the
second polynomial can be accessed from link of h2. The procedure to be followed
while adding two polynomials is shown below.
• Search for the next term of first polynomial in the second polynomial
• if found then
add the coefficients of both terms.
If the result is not zero add the result to the resulting polynomial.
else
add the term of first polynomial to the resulting polynomial
endif
This process has to be repeated until all the terms of the first polynomial are over. So,
the first version of the algorithm can be
pI = link(hI)
while p l !=hI
{
Search for the next term of first polynomial in the second polynomial
if found then
add the coefficients of both terms. If the result is not zero i.e., two
terms can not be cancelled, add the result to the resulting polynomial.
else
add the term of first polynomial to the resulting polynomial
endif
p l = link(pl);
Let us discuss how to search for the next term of the first polynomial in the second
polynomial. The first term of the first polynomial can be obtained using the
statements
9.130 Q Linked Lists
xl = px(PI);
yl = py(pI);
cfl = cf(PI);
provided the pointer variable p l contains the address of the first term in the
polynomial. To obtain the successive terms using the above sets of statements, point
the
, pointer variable p l to successive nodes one after the other. This term should be
compared with each term of the second polynomial. This can be done using the
following set of statements
p2 = link(h2);
while (p2 != h2 )
{
x2 = px(P2);
y2 = py(P2);
cf'2 = cf(P2);
if(xl =x2&&yl =y2) break;
p2 = link(p2)
}
After executing these statements, if p2 and h2 are same, it means that the term of the
first polynomial is not present in the second polynomial. Otherwise, the term is
present. Searching for the next term in the first polynomial is over. After searching,
the following statement has to be executed.
if found then
add the coefficients of both terms.
If the result is not zero add the result to the resulting polynomial.
else
add the term of first polynomial to the resulting polynomial
endif
The code for the above statement can be written as
if (p2 != h2) 1* term is present */
{
cf= cfl + cf'2
if ( cf!= 0) h3 = insertreartcf.x l.y l.hf);
}
else
h3 = insert Jear( cfl,x 1,yl ,h3);
Q Systematic Approach to Data Structures using C - 9.131
The complete algorithm to add two polynomials is shown below:
pl = link(bl)
while (p l != h l )
{
/* obtain the current term */
xl = px(pl)
yl = py(pl)
cfl = cf(Pl)
while ( p2 != h2 )
{
x2 = px(P2)
y2 = py(p2);
cf2 = cf(P2);
p2 = link(p2)
}
if( p2 != h2)
{
/* if found add the co-efficients and the result to the
resultant polynomial */
When all the terms in the first polynomial are compared with the second polynomial,
the remaining terms of the second polynomial can be simply inserted into the
resultant polynomial.
To check for the remaining terms of the second polynomial, one more field
called flag can be added. Initially, flag field of each node in the second polynomial is
zero. Once the coefficients of two terms are added make flag field of the current term
in. the second polynomial as 1. So, the flag field of each node in the second list
corresponding to the second polynomial may be 1 or 0. If flag field is 0, it means that
the corresponding term is not there in the first polynomial. The code to copy the
remaining terms of the second polynomial is
p2 = link(h2)
while (p2 != h2 )
{
if ( flag(p2) = = °)
{
h3 = insert_rear(cf(p2),px(p2),py(p2),h3);
}
p2 = link(p2)
}
Finally address of the header node of the resultant polynomial IS returned. The
function to add two polynomials is shown below:
p l = h i -;-1:1li(;
while (pI !=h1)
{
1* obtain the next term of the first polynomial *1
xl = p1->px;
y1 = p1->py;
cfl = p l->cf;
Q Systematic Approach to Data Structures using C - 9.1
p2 = p2->link;
}
void maint)
{
NODE hI,h2,h3;
Q Systematic Approach to Data Structures using C - 9.13!
h l = getnodet);
h2 = getnodet);
h3 = getnodet);
hl->link = hl ; h2->link = h2; h3->link = h3;
h3 = add--'poly(hl,h2,h3);
ote: Consider the two polynomials: 5x\,2 + 6xy + 3xy + 5 and 10x3y + 4x3 + 3xl
+ lO~ + 7. The sum of these two polynomials will be 5x\,2 + 9xy + 3xy + 12 +
10x3~ + 4x3 + 101. The polynomials should be entered as shown below:
Output
Enterthe first polynomial
Enterthe co-efficient as -999 to end the polynomial
Enterthe I5t term
Coeff= 5 pow x = 3 pow Y = 2
Enterthe 2nd term
Coeff= 6 pow x = I pow y = 3
Enterthe 3rd term
Coeff= 3 pow x = I pow Y = I
Enterthe 4 tit. term
Coeff= 5 pow x = 0 pow y = 0
Enterthe s" term
-999
Enterthe second polynomial
9.136 Q Linked Lists
Exercises
1. How long positive numbers can be represented using linked lists? Explain with an
example
2. Write a C program to subtract two long positive numbers using circular singly
linked lists
3. Write a C program to add two long positive numbers using doubly linked list.
4. Write a C program to subtract two long positive numbers using doubly linked list.
5. How a polynomial with 3 variables x, y and z can be represented using linked
lists. Explain with an example
6. Modify the program given in example 9.61 to add two polynomials with 3
variables x, y and z.
7. Write a program to subtract two polynomials with 2 variables.
\
n
Chapter 10: Trees
I What are we studying in this chapter? I
• Definition of a tree and related terminologies
• Array and linked representation of a tree
• Traversal techniques
• Insert an element
'. Delete an element
• Copy the tree
• Search for a specified item
• To find the height of a tree
• To find the number of nodes
• To find the number ofleaves
• To find maximum and minimum in a tree
• Implementation of a priority queue
• Conversion and evaluation of expressions
• Iterative traversal techniques
• Threaded binary trees -12 hours
10.1 Introduction
Sofar we have discussed the data structures where linear ordering was maintained
usingarrays and linked lists. For some of the problems it is not possible to maintain
thislinear ordering using linked lists. Using non-linear data structures such as trees,
graphs,multi-linked structures etc., more complex relations can be expressed. In this
chapteron trees, we begin with some definitions, the various operations that can be
performedon trees along with various applications. In the previous chapter, we have
seena doubly linked list with two link fields.
Now, we concentrate on a tree which is also a doubly linked list, but the field
lIink does not point to predecessor and field rlink does not point to successor, instead
theypoint to some other trees. A tree is a data structure which is collection of zero or
morenodes and finite set of directed lines called branches that connect the nodes. The
firstnode in the tree is called root node and remaining nodes are partitioned into
varioussubtrees. Trees are normally divided into two groups: General trees and
Bmarytrees
10.2 Q Trees
Note: The number of branches associated with each node is called degree of a node.
When a branch is directed towards the node, we say it has indegree branch and when
a branch is directed away from the node, we say it has outdgree branch.
In this chapter, let us concentrate on binary trees. First we shall see "What is a binary
tree?"
Definition: A binary tree is a tree which is collection of zero or more nodes and finite
set of directed lines called branches that connect the nodes. A tree can be empty or
partitioned into three subgroups namely root, left subtree and right subtree.
• Root - If tree is not empty, the first node is called root node.
• left sub tree - It is a tree which is connected to the left of root. Since this tree
comes under root, it is called left subtree.
• right subtree - It is a tree which is connected to the right of root. Since this tree
comes under the root, it is called right subtree.
Note: In general, a tree in which each node has either zero, one or two subtreesis
called a binary tree. If any node has more than two subtrees, then it is not a binary
tree. The pictorial representation of a typical node in a binary tree is shown below:
Node = llink + info + rlink
Address of ~ ._ Address of
left subtree right subtree
llink info rlink
Note that a node in a tree consists of three fields namely llink, info and rlink:
• llink - basically contains address of left subtree
• info - This field is used to store the actual data or information tobe manipulated
• rlink - basically contains address of right subtree.
The pictorial representation above node takes more space. So, to minimize the space,
we can represent the above node as shown below:
info info
~.
-~,
Ilinr - '{.link lIiny . ",ink
left subtree right subtree left subtree right subtree
(a) (b)
Fig 10.1: A node in a binary tree
Q Systematic Approach to Data Structures using C - 10.3
Thefigure 10.1.a is shown with directions to left and right subtrees. But, the second
figureshows without directions. In the text book, we show without directions. But, it
is implied that directions are present moving away from root. The following figure
showssome of the binary trees:
Empty tree Tree with 1 node Tree with 2 nodes Tree with 3 nodes
(a) (b) (c) (d)
ote: An empty tree is also a binary tree. Binary here means at most two i.e., zero,
one or two subtrees are possible. But, more than two subtrees are not permitted.
Considerthe trees shown below: Are they binary trees? No.
Now, let us see "What are the various terminologies normally associated 'with trees?
Define each of them" The various terminologies that are associated with a tree shown
below:
• Root node: A node with indegree 0 is called root node. It is the first node in the
tree. For example,
• node 100 is the root of the tree (shown in Fig. lOA)
• Child: The nodes, which are all reachable from a node x using only one edge are
called children of node x and node x is the parent for all those children. The
word son can also be used in place of child. [Let use the word child, otherwise
daughters may feel bad]. for the tree shown in Fig. lOA
• 50 and 60 are children of 100
• 80 and 40 are children of 60
• 70 is child of 50
• 35 and 30 are children of 80
• Siblings: Two or more nodes having the same parent are called siblings. For
example, for the tree shown in Fig. lOA
• 50 and 60 are siblings since they have same parent 100
• 80 and 40 are siblings since they have same parent 60
• 35 and 30 are siblings since they have same parent 80
• Ancestors: The nodes in the path from root to the specified node x, are all
ancestors of node x. For example, for the tree shown in Fig. 10.4
• 100 is the ancestor of 50
• 60 is the ancestor of 100
• 50 and 100 are the ancestors of 70
• 60 and 100 are the ancestors of 80 and 40
• 80, 60 and 100 are the ancestors of 100 and 30
• Descendents: The nodes in the path below the parent are called descendents.In
other words, the nodes that are all reachable from a node x are all called
descendents ofx. For example, for the tree shown in Fig. 10.4
• All the nodes below 100 are descendents of 100
• All the nodes below 50 are descendents of 50 and so on.
• Left descendents: The nodes that lie towards left subtree of node x are calledJeft
descendents. For example, for the tree shown in Fig. 10.4
• 50 and 70 are left descendents of 100
• 80, 35 and 30 are the left descendents of 60
• 35 and 80 are left descendents of 60
,!;lSystematic Approach to Data Structures using C - 10.5
.Lex.el
Level 2 Hei ht 2
Level 3
• Right descendents: The nodes that lie towards right subtree of node x are called
right descendents. For example, for the above tree
• 60, 80, 40, 35 and 30 are right descendents of 100
• 30 is the right descendent of 80
• 40 is the right descendent of 60
• Leftsubtree: All the nodes that are all left descendents of a node x form the left
subtree of x. For example, for the above tree
• 50 and 70 form the left subtree of 100
• 80, 35 and 30 form the left subtree of 60
• Right subtree: All the nodes that-are all right descendents of a node x form the
right subtree of x. For example, for th'f above tree
• 60, 80,40,35 and 30 are right descendents of 100
• 30 is the right descendent of 80
• 40 is the right descendent of 60
• Parent: A node having left subtree or right sub tree or both is said to be a parent
node for the left subtree and/or right subtree. The word father also used in place of
parent. [Let us use the word parent, otherwise mothers may feel bad]. For
example, for the above tree
10.6 Q Trees
• Internal nodes: The nodes except leaf nodes in a tree are called internal nodes.
For example,
• 100, 50, 60 and 80 are the internal nodes
• External nodes: The leaf nodes in a tree are called external nodes. For example,
• 70,35, 30 and 40 are the external nodes
• Level: The distance of a node from the root is called level of the node. In a tree,
the root has a level 0 (zero) and level of any other node is one more than the level
of its father. For example,
• level of each node is shown in Fig. 10.4
• Height (Depth): The height of the tree is defined as the maximum level of any
leaf in the tree. For example,
• height of tree shown in Fig 10.4 is 3
Once we know the terminologies or nomenclature used for trees, now let us see
"What are the different types of binary trees?" The binary trees are classified as
shown below:
Definition:If the outde~~ of every ~de in a tree is either 0 or 2, then the tree is
saidto be strictI binary tree i.e., each node can have maximum two children or no
children(no children indicate empty left and empty right child).
For example, the trees shown in fig.1O.5.b and fig.l 0.5.c are strictly binary
treeswhereas the tree shown in figure 10.5.a is not strictly binary tree (because, the
node5 should have two children or no children. But, it has one child)
Definition:A strictly binary tree in which the number of nodes at any level i is z', is
saidto be a complete bina tree. The trees shown in fig.10.6 are all complete binary
trees.
Solution: Observe the following factors from the complete binary tree:
,Number of nodes at level 0 = 1 = 2°
Number of nodes at level 1 = 2 = 21
Number of nodes at level 2 = 4 = 22
Number of nodes at level 3 = 8 = 23
• The total number of nodes at level d may be equal to 2d• If the total number of
nodes at level d is less than 2d, then the number of nodes at level d-l should be
2d-1 and in level d the nodes should be present only from left to right.
For example, the trees shown in fig.l0.7.a to fig.lO.7.d are almost complete binary
trees and the rest are not. The nodes in an almost complete binary can be numbered
level by level from left to right as shown in fig.l 0.7 .d.
(d) (e)
Fig. 10.7: Almost complete binary tree Fig. 10.7: Not almost complete binarytre
Now, let us see "What is the storage representation of binary trees?" The storage
representation of binary trees can be classified as shown below:
• sequential allocation technique (using arrays)
• Linked allocation technique (using dynamic allocation)
10.10 Q Trees
Now, let us see "How a tree is represented using linked allocation technique?" In a
linked allocation technique a node in a tree has three fields:
• info - which contains the actual information
• lIink - which contains address of the left subtree
• rlink - contains address of the right sub tree.
struct node
{
int info;
struct node *llink;
struct node *rlink;
};
A pointer variable root can be used to point to the root node always. If the treeis
empty, the pointer variable root points to NULL indicating the tree is empty. The
pointer variable root can be declared and initialized as shown below:
Memory can be allocated or de-allocated using the functions getnodet) and freenode)
as we discussed in linked lists in chapter 9. Now, let us see "How a tree is represented
using static allocation(using arrays) technique?"
(b)
Figure 10.8: Sequential representation of a tree
Atree can be represented using arrays in two different methods as shown below:
Method 1: In the first representation shown below, some of the locations may be
usedand some may not be used. For this, a flag field namely, used is used just to
indicatewhether a memory location is used to represent a node or not. If flag field
used is 0, the corresponding memory location is not used and indicates the absence of
nodeat that position. So, each node contains two fields:
• info where the information is stored
• used indicates the presence or the absence of a node
An array a of type NODE can be used to store different items and the declaration
for this is shown below:
NODE a [MAX_SIZE];
Now, let us see "What are the various operations that can be performed on binary
trees?" The various operations that can be performed on binary trees are shown
below:
Insertion (Insert a given item into a tree)
Traversal (Visiting the nodes of the tree one by one)
Operations on __ -+-_~
Search (Search for the specified item)
binary trees
Copying (To obtain exact copy of the given tree)
Deletion (To delete a node from the tree)
10.4.1 Insertion
Suppose the node pointed to by temp has to be inserted whose information field
contains the item'!' as shown in fig.10.9.
.•...
(j")
...... temp
Figure 10.9: To insert an item I
~ Systematic Approach to Data Structures using C - 10.13
Let'd' be an array, which contains only the directions where the node temp has to be
inserted.If 'd' contains 'LRLR', from the root node, first moves towards left(L), then
right(R),then left(L) and finally move towards rightrR). Finally if the pointer points
toNULL, at that position, node temp can be inserted otherwise, node temp can not
be inserted. To achieve this, one has to start from the root node. Let us use two
pointersprev and cur where prey always points to parent node and cur points to
childnode. Initially cur points to root node and prey points to NULL. To start with,
onecan write the following statements.
prev=NULL
cur = root
Now,keep updating the node pointed to by cur towards left if the direction is ('L')
otherwise,update towards right. The pointer variable prey always points to the parent
nodeand cur points to the child node. Once all directions are over, if cur points to
NULL, insert the node temp towards left or right based on the last direction.
Otherwise,display an error message. In the following algorithm, an index variable i is
usedto access the directions. The C function to insert a node is shown below:
Example 10.4.1.1: Function to insert an item into a binary tree based on direction
if ( root = NULL) return temp; 1* Node is inserted for the first time *1
prev=NULL;
cur = root;
return root;
}
10.4.2 Traversals
1
Now, let us see "What are the different traversal techniques of a binary tree?" The
various traversal techniques of a binary tree are shown below:
Binary tree
traversals -E Pre order
Inorder
Postorder
Now, let us see "What is preorder traversing of a binary tree?"
Definition: The pre order traversal of a binary tree can be recursively defmed as
follows:
1. Process the root Node [NJ
2. Traverse the Left subtree in preorder [LJ
3. Traverse the Right subtree in preorder [RJ
The C function to traverse the tree in pre order can be recursively defined as shown
below:
voidpreorder(NODE root)
{
if ( root = NULL) return;
Solution: Easiest method of writing pre order traversal is to start from innermost
square and visit each node in the sequence specified as shown below:
.Q Systematic Approach to Data Structures using C - 10.17
2 3 '---..r---'
D G H E I F
B D G H C ElF
ABDGHCEIF
10.18 Q Trees
The preorder traversal by observing the tree and following the numbers in sequence is
shown below:
A B ID
1 2
G HI
3
\0 c I E \0 I I F
1 3 \..1
\.. 2
V
.J
2
V
3
2 3
Example 12.4.2.5: Easiest method of writing inorder traversal is to start from
innermost square and visit each node in the sequence specified as shown below:
G D H E I F
The above ee now can be written as:
G D H B E I C F
Q Systematic Approach to Data Structures using C - 10.1
Theabove tree can be now written as shown below:
In order traversal is G D H B A E I C F
The inorder traversal by observing the tree and following the numbers in sequence
shownbelow:
tG
: HI: 3
C
2 3
F
I
I 2 3
Example 12.4.2.6: Easiest method of writing postorder traversal is to start frc
innermost square and visit each node in the sequence specified as shown below:
1 2
-
G H DIE F
printf("%d\n", root->info);
10-4.3 Searching
Now, let us see "How to search for a key in a binary tree?" By traversing the tree in
any of the order one can visit each node. As we visit the node we can compare the
information field of each node with the key to be searched. If found, display
successful search, otherwise display unsuccessful search. Here, we use inorder
traversal technique. The equivalent C function is shown below:
Note: In the above function, apart from item and root the other parameter passed is
flag which is a pointer to an integer. If an item is present in the tree, its value is set to
1 and the control is returned to the calling function. So, just before invoking the
function searcht), the flag should be set to 0 and should be invoked as shown below:
10.22 Q Trees
flag = 0;
search(item, root, &flag);
if (flag = 1)
printf("Search successful");
else
printf("Unsuccessful search");
Note: .The program to search for an item using preorder is shown below:
Note: The program to search for an item using postorder is shown below:
The complete C program for creating a tree, traversing the tree and searching for a
specified item in the tree is shown below:
Example 10.4.3.4: C Program to create a tree, traverse and search for an item
#include <stdio.h>
#indude <stdlib.h>
#include <process.h>
#include <string.h>
struct node
int info;
struct node *llink;
struct node *rlink;
};
1* Include: Example 9.2.1.3:C Function to get a new node from availability list *1
1* Include: Example 10.4.2.1: Display the contents of the tree in preorder *1
1* Include: Example 10.4.2.2: Function traverse the tree in inorder *1
1* Include: Example 10.4.2.3: Function traverse the tree in postorder *1
1* Include: Example 12.4.2.7: C function to print the tree in tree form *1
1* Include: Example 10.4.3.1/2/3: Function to search for item *1
1* Include: Example 10.4.1.1: Function to insert item into tree based on direction *1
void maim)
{
NODE root = NULL;
int choice, item, flag;
~ Systematic Approach to Data Structures using C - 10.25
case 4:
if ( root = NULL)
printf("Tree is empty\n");
else
{
printf("The given tree is\n");
display(root, 1);
case 5:
if ( root = NULL)
printf("Tree is empty\n");
else
{
printf("The given tree is\n");
display(root, 1);
default: exit(O);
}
}
ote:Let us create the tree shown in figure 10.5.a. The sequence of input to be
providedto create the tree is shown below:
Q Systematic Approach to Data Structures using C - 10.27
10-4.4 Copying a tree
The C function to obtain the exact copy of the given tree using recursion is shown
below: It is self-explanatory. In this function address of the root node is given and
after copying, it returns address of the root node of the new tree.
,
Definition: A binary search tree is a binary tree in which for each node say x in the
tree, elements in the left-subtree are less than info(x) and elements in the right subtree .
are greater or equal to info(x). Every node in the tree should satisfy this condition, if
leftsubtree or right subtree exists. Fig. 10.4.4.1 shows some of the binary search trees.
10-5.1 Insertion
Now, let us see "How to create a binary search?" Creating a binary search tree is
nothing but repeated insertion of an item into the existing tree. So, we concentrate on
how to insert an item into the tree. In a BST (Binary Search Tree), items towards left
subtree of a node x will be less than info(x) and the items in the right subtree are
greater or equal to info(x). Consider the BST shown below:
l
prev=NULL
root 100 cur
••••
•••
.. ..
-~
{140·:
<:»
t
temp
Figure 10.8: Inserting an item into a binary search tree
Q Systematic Approach to Data Structures using C - 10.29
Suppose the node pointed to by temp (with item 140) has to be inserted. The item 140
is compared with root node 100. Since it is greater, the right subtree of root node has
to be considered. Now compare 140 with 200 and since it is less, consider the left
subtree of the node 200. Now, compare 140 with 150. Since it is less, consider the left
subtree of node 150. Since, left subtree of node 150 is empty, the node containing the
item 140 has to be inserted towards left of a node 150. Thus, to find the appropriate
place and insert an item, the search should start from the root node. This is achieved
by using two pointer variables prey and cur. The pointer variable prey always points
to parent of cur node. Initially cur points to root node and prey points to NULL i.e.,
prev = NULL
cur = root
Now, as long as the item is less than info(cur), keep updating the node pointed
to by the pointer variable cur towards left. Otherwise, update towards right. The
pointer variable prey always points to the parent node and cur points to the child
node. Once cur points to NULL, insert the node temp towards left(prev) if item is
less than info(prev), otherwise insert towards right. The code corresponding to this
canbe
prev = NULL;
cur = root;
Example 10.5.1.1: Function to insert an item into a binary search tree (with duplicate
elements)
NODE insert(int item, NODE root)
{
NODE temp,cur,prev;
temp = getnodet); 1* Obtain new node from the availability list */
temp->info = item; 1* Copy appropriate data *1
temp->llink = NULL;
temp->rlink = NULL;
if ( root = NULL) return temp; 1* Insert a node for the first time *1
prev = NULL; 1* find the position to insert *1
cur = root;
while ( cur != NULL)
{
pre v = cur; 1* Obtain parent position *1
if (item < cur->info )
cur = cur->llink; 1* Obtain left child position *1
else 1* or *1
cur = cur->rlink; 1* Obtain right child position *1
}
if ( item < prev->info ) 1* Ifnode to be inserted < parent *1
prev->llink = temp; 1* Insert towards left of the parent *1
else
prev->rlink = temp; 1* else, insert towards right of the parent */
Note: In the above function, duplicate items are inserted towards ri h1. To avoid
insertion of duplicate elements, the above function can be modified as s own below:
Example 10.5.1.2: Function to insert an item into a binary search tree (No duplicate
items are allowed)
if ( root = NULL) return temp; 1* Insert a node for the first time *1
prey = NULL; 1* find the position to insert *1
cur = root;
while ( cur != NULL )
{
prey = cur; 1* Obtain parent position *1
if ( item = cur->info ) 1* do not insert duplicate item *1
{
printf("Duplicate items not allowed\n");
free(temp);
return root;
}
10-5.2 Searching
Now, let us see "How to search for a key in binary search tree?" Start searching from
the root node and move downward towards left or right based on whether the key lies
towards left subtree or right subtree. This is achieved by comparing this key with root
node of an appropriate subtree (left subtree or right subtree). If two items are same
then search is successful and address of that node is returned. If the key is less than
info(root), search in the left subtree, otherwise search in the right subtree. If key is
not found in the tree, finally root points to NULL and return root indicating search is
unsuccessful. The iterative function to search for the key is shown below:
10.32 Q Trees
Example 10.5.2.1: Function to search for an item in binary search tree using iteration
return root;
}
Let us see "How to fmd maximum value in a binary search tree?" Given a binary
search"tree, a node with maximum value is obtained by traversing and obtaining the
right most node in the tree. If there is no right subtree then return root itself as the
node containing the item with highest value. The corresponding C function is shown
below:
cur = root;
while ( cur->rlink: != NULL)
{
cur = cur->rlink:; /* obtain right most node in BST */
}
return cur;
Height of the tree is nothing but the maximum level in a tree. The recursive definition
to compute the height of the tree is shown below:
-I if root = NULL
Height(root) =
{ 1 + max ( height(root->llink), height(root->rlink )) otherwise
The corresponding C function to find the height of the tree is shown below:
Example 10.5.3.3.1: Function to find the height of the tree
1* function to find maximum of two numbers *1
int max(iot a, int b)
{
return ( a> b ) ? a : b;
}
1* Function to find the height of the tree *1
int height(NODE root)
{
if ( root = NULL) return -1;
return 1 + max( height(root->llink), height(root->rlink) );
}
Q Systematic Approach to Data Structures using C - 10.35
Now, let us see "How to count the nodes in a tree?" The number of nodes in the tree
is obtained by traversing the tree in any of the traversal technique and increment the
counter whenever a node is visited. The variable count can be a global variable and it
is initialed to zero before calling this function. In this example, the inorder traversal is
used to visit each node. The C function to obtain the number of nodes in a tree is
shown below:
count_ node(root->llink);
count++;
count_ node(root->r1ink);
Now, let us see "How to compute the number of leaves in a tree?" As in the previous
case visit each node in a given tree. Whenever a leaf is encountered update count by
one. A leaf or a terminal node is one, which has an empty left and empty right child.
The variable count can be a global variable and it is initialed to zero to start with. The
function to count the leaves in a binary tree is shown below:
It is very important to remember that once the required node is found and deleted, ina
binary search tree the ordering of the tree should be maintained i.e., even after
deleting a node, elements in the left subtree should be lesser and elements in the right
subtree should be greater or equal. Let us see "What are the activities to be performed
to delete a node?" The various activities to be performed are shown below:
Case 1: An empty left subtree and non empty right subtree or an empty right subtree
and nonempty left subtree Note: A node having empty left child and empty right child
is also deleted using this case.
Consider the figures shown below where cur denotes the node to be deleted
and in both cases one of the subtrees is empty and the other is non-empty. The node
identified by parent is the parent of node cur. The non empty subtree can be
obtained and is saved in a variable q. The corresponding code is:
parent
Case 2: Non empty left subtree and non empty right subtree: Consider the figures
shown below where cur denotes the node to be deleted. In both cases both the
subtrees are non-empty. The node identified by parent is the parent of the node cur.
~ Systematic Approach to Data Structures using C - 10.37
parent parent
parent
90 q
••
••
11
o
Obtain right subtree of
The node can be easily deleted using the following procedure in sequence:
Step 1: Find the inorder successor of the node to be deleted. The corresponding code
is shown below:
Step 2: Attach left sub tree of the node to be deleted to the left of successor of the
node to be .deleted. The corresponding code is:
If parent exists for the node to be deleted, attach the subtree pointed to by q, to the
parent of the node to be deleted. In this case, attach q to parent based on the
direction. If cur is the left child, attach q to left(parent) otherwise attach q to
right(parent). This can be achieved by using the following statement
1* connecting parent of the node to be deleted to q *1
if ( cur = parent->llink )
parent->llink = q
else
parent->rlink = q;
Once the node q is attached to the parent, the node pointed to by cur can be deleted
and then return the address of the root node. The corresponding statements are:
Q Systematic Approach to Data Structures using C - 10.39
free (cur);
return root;
All these statements have been written by assuming the node to be deleted cur and its
parent node is known. So, just before deleting, search for the specified node, obtain
its parent and then delete the node. The complete function to delete an item from the
tree is shown below:
if ( root = NULL)
{
printf("Tree is empty! Item not found\n");
return root;
}
parent = NULL, cur = root; I*obtain node to be deleted, its parent *1
while (cur != NULL)
{
if (item = cur->info) break;
parent = cur;
cur = ( item < cur->info ) ? cur->llink : cur->rlink;
}
if ( cur = NULL)
{
printf("Item not found\n");
return root;
}
1* Item found and delete it *1
1* CASE 1 *1
if ( cur->llink = NULL) 1* If left subtree is empty *1
q = cur->rlink; 1* obtain non empty right sub tree *1
else if ( cur->rlink = NULL) 1* If right subtree is empty *1,.
q = cur->l1ink; 1* obtain non empty left subtree *1
else
{ 1* CASE 2 *1
suc = cur->rlink; 1* obtain the inorder successor *1
10.40 Q Trees
Example 10.5.3.6.2: C program to create a tree, traverse a tree and delete an item
from the tree
#include <stdio.h>
#include <stdlib.h> .
#include <process.h>
struct node
{
int info;
struct node *llink;
struct node *rlink;
};
void maim)
{
NODE root, temp;
int choice, item;
root = NULL;
for (;;)
{
printf(" 1:Insert 2:Inorder 3:Preorder\n");
printf("4:delete 5:Exit\n");
printf("Enter the choice\n");
scanf("%d", &choice);
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d", &item);
root = iterative_insert(item, root);
break;
case 2:
if ( root = NULL)
printf("Tree is empty\n");
else
{
printf("The given tree in tree form is\n");
display(root, 1);
printf("Inorder traversal is\n");
inorder(root);
printf("\n");
}
break;
case 3:
if ( root = NULL)
printf("Tree is empty\n");
10.42 Q Trees
else
{
printf("The given tree in tree form is\n");
display(root, 1);
printf("Preorder traversal is\n");
preorder(root);
printf("\n");
}
break;
case 4:
printf("Enter the item to be deleted\n");
scanf("%d" ,&item);
root = delete _item(iten:I, root);
break;
default: exit(O);
}
}
}
Let us see "How a binary search tree can be used as a priority queue?" Binary search
tree can be used as a priority queue. To implement ascending priority queue, create a
binary search tree such that elements in the left subtree of any node say x will be less
than info(x) and elements in the right subtree will be greater than or equal to info(x).
Once the tree is created search for the least item and delete from the tree. All these
functions are already implemented and the C program for this is shown below:
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <string.h>
struct node
{
int info;
struct node *llink;
struct node *rlink;
};
Q Systematic Approach to Data Structures using C - 10.43
void mainC)
{
NODE root, temp;
int choice,item;
root = NULL;
for (;;)
{
printf(" 1:Insert 2:Display\n");
printf("3 :delete 4:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch( choice)
{
case 1:
prlntf(t'Enter the item to be inserted\n");
scanf("%d~' ,&item);
root = insert (item, root);
break; .
case 2:
if ( root = NULL)
printf("Priority que is empty\n");
else
{
printf("Contents of priority que\n");
inorder(root);
printf("\n");
}
break;
10.44 Q Trees
case 3:
temp = minimum(root);
if ( temp = NULL )
printf("Priority que is empty\n");
else
{ -
item = temp->info;
printf("The deleted item is %d\n",item);
root = delete_item(item, root);
}
break;
default:
exit(O);
}
}
}
10-6 Applications
Now, let us see "What are the applications of binary search trees?" Some of the
applications of binary search trees are shown below:
• searching and sorting
• manipulation of arithmetic expressions
• Constructing symbol table.
• The trees are also used in syntax analysis of compiler design and are used to
display the structure of a sentence in a language.
10.6.1 Searching
In binary tree to search for an item, each node has to be compared. But, if the treeis
binary search tree then the comparison will be restricted to either the left subtree or
the right subtree. Comparison starts from the root node and moves downward towards
the leaves. The number of comparisons can be greatly reduced using binary search
tree. It has been discussed in detail in section 10.5.2
10.6.2 Sorting
Once the binary search tree is created, traversing this tree in inorder, the elements will
be displayed in ascending order.
Q Systematic Approach to Data Structures using C - 10.45
Definition:An expression tree is a binary tree that satisfy the following properties:
t Any leaf is an operand
t, The root and internal nodes and operators
t The subtrees represent sub expressions with root of the subtree as an operator.
Letus see "How an infix expression can be written using expression tree?" An infix
expressionconsisting of operators and operands can be represented using a binary tree
withroot as the operator. The left and right subtrees are the left and right operands of
thatoperator. A node containing an operator is not a leaf where as a node containing
anoperand is a leaf. The tree representation for the infix expression
((6+(3-2)*5) /\ 2 + 3)
isshown in figure below: Let us traverse the tree in preorder and postorder as shown:
3
"---y-----/
-3 2 = A 32-=A
10.46 Q Trees
*A5=B A5*=B
+6B=C 6B+=C
$C2=D C2 $ =D
Q Systematic Approach to Data Structures using C - 10.47'
2 3
+ D 3 D 3+
+$C23 C2 $3+
+$+6B23 6B+2$3+
+$+6*A523 6A5*+2$3+
+$+6*-32523 632-5*+2$3+
( Prefix expression) (postfix expression)
Letus see "What is the procedure to obtain an expression tree from the postfix
expression?" The procedure to be followed while creating an expression tree using
postfixexpression is shown below:
• Scan the symbol from left to right.
• Create a node for the scanned symbol.
• If the symbol is an operand push the corresponding node onto the stack.
• If the symbol is an operator, pop one node from the stack and attach to the
right of the corresponding node with an operator. The first popped node
represents the right operand. Pop one more node from the stack and attach to
the left. Push the node corresponding to the operator on to the stack.
• Repeat through step 1 for each symbol in the postfix expression. Finally when
scanning of all the symbols from the postfix expression is over, address of the
root node of the expression tree is on top of the stack.
Thefollowing C function creates a binary tree for the valid postfix expression.
10.48 Q Trees
Example 10.6.4.1: Function to create an expression tree for the postfix expression
if( isalnum(symbol) )
st[k++] = temp; 1* Push the operand node on to the stack */
else
{
temp->rlink = st[--k]; 1* Obtain 2nd operand from stack */
temp->llink = st[--k]; 1* Obtain 1st operand from stack */
st[k++] = temp; i* Push operator node on to stack */
}
}
Now, let us see "How to evaluate the expression?" In the expression trees, whenever
an operator is encountered, evaluate the expression in the left subtree and evaluate the
expression in the right sub tree and perform the operation. The recursive definition to
evaluate the expression represented by an expression tree is shown below:
The C function for the above recurrence relation to evaluate the expression
represented by an expression tree is shown below:
~ Systematic Approach to Data Structures using C - 10.49
floateval(NODE root)
I
float nurn;
switch( root->info)
{
case '+': return eval(root->llink) + eval(root->rlink);
case '$':
case '''I: return pow (eval(root->llink),eval(root->rlink));
default :
if (isalpha(root->info) )
{
printf("%c = ",root->info);
scanf("%f' ,&nurn);
return num;
}
else
return root->info - '0';
}
#inc!ude<stdio.h>
#include<ctype.h>
#include<process.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
10.50 Q Trees
struct node
{
char info;
struct node *llink;
struct node *rlink;
};
1* Include: Example 9.2.1.3: C Function to get a new node from availability list */
1* Include: Example 10.6.5.1: Function to evaluate the tree *1
1* Include: Example 10.6.4.1: Function to create expression tree for postfx exprsin*1
void maim)
{
char postfix[40];
float res;
Insection 10.3 we have seen as to how a tree can be represented using an array. The
creation of a binary search tree and different traversal techniques using array
representation is ~iscussed in this section. The advantages and disadvantages of
sequentialrepresentation over linked representation is also discussed.
The second approach discussed in section 10.3 to represent a binary tree using
sequential allocation is used. The complete program in C to create the binary search
treeand to traverse the tree in inorder, preorder and postorder is shown below:
Example 10.7.1.1: C Program to create a tree and traverse the tree using array
representation
#inc1ude<stdio.h>
include<process.h>
i = 0; 1* root node */
while (i < MAX_SIZE && a[i] != 0) /* obtain position where to insert */
{
if (item < a[i] )
i = 2*i + 1; /* Move towards left link */
else
i = 2*i + 2; /* Move towards right link */
}
void maim)
{
NODE a[MAX_SIZE];
int item,choice,i;
fore;;)
{ priotf(" 1:Insert 2:Inorder\n");
printf("3:Preorder 4:Postorder\n");
printf(" 5:Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
Q Systematic Approach to Data Structures using C ~ 10.53
switch( choice)
{ case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
insert(item, a);
break;
case 2:
if( a[O] = 0)
printf("Tree is empty\n");
else
{
printf("The inorder traversal is\n");
inorder( a,O);
printf("\n");
}
break;
case 3:
if (a[O] = 0)
printf("Tree is empty\n");
else
{
printf("The preorder traversal is\n");
preorder(a,O);
printf("\n");
}
break;
case 4:
if( a[O] = 0)
printf("Tree is empty\n");
else
{
printf("The postorder traversal is\n");
postorder(a,O);
printf("\n");
}
break;
default: exiteD);
}
}
10.54 Q Trees
The sequential representation is simple and it saves space if the tree is complete or
almost complete as there is no need to have fields such as left-link, right-link etc.If
the tree is not complete or not almost complete binary tree, too much space may be
wasted. The index i used to move downward while doing some operations should not
exceed the array bound i.e., MAX_SIZE. This is advantageous only when the number
of items in the tree is known in advance.
The linked representation is useful most of the time during repeated deletionor
manipulations, which can be easily done by adjusting the links and where the number
of items in the tree is unpredictable.
This chapter deals with, mostly of problems involving recursion. In this sectionwe
develop the functions for traversal of trees using iterative technique.
We know that in preorder traversal, node is visited first, then the left subtreeis
traversed in preorder and finally right subtree is traversed in preorder. Visiting the
node here is nothing but display the corresponding item in the node. This canbe
achieved using the statement
printf("%d ",cur->info);
only if cur points to root node. So, initially cur points to the root node. Once thenode
is visited, next is to traverse the left subtree in preorder. For this descend the tree
towards left. Once all the nodes in the left subtree are displayed, it may be requiredto
ascend the parts of the tree to display the nodes in the right subtree, To ascend thetree
later, just before descending towards left, push the address of current node cur and
then descend the tree left. This process has to be repeated till end of left subtreeis
encountered. The code corresponding to this can be written as
In the tree shown in figure below, after executing these statements 100,50 and 25 will
be displayed.
Once traversing the left subtree in pre order is over, traverse the right subtree in
preorder i.e., ascend the tree by poppipg the address of the node most recently pushed
and descend towards right subtree. This is possible only when the stack is not empty.
If the stack is empty traversing the tree in preorder is complete and the program
terminates. The code corresponding to this is shown below:
After descending the tree towards right, entire right sub tree has to be traversed in
preorder. So, the above set of statements has to be repeated. The complete C function
forthis is shown below:
if ( root = NULL)
{
printf("Tree is empty\n");
return;
}
cur = root;
for (;;)
{
while (cur != NULL)
{
printf("%d ",cur->info); /* Visit the node */
s[++top] = cur; /* push(cur, &top, s) */
cur = cur->llink; /* traverse left */
}
In this traversal technique, first left subtree is traversed in inorder, then the node is
visited and finally the right subtree is traversed. As in iterative preorder traversal,
traverse the left sub tree in inorder by descending the tree towards left until cur which
initially points to root node is NULL. Before updating the pointer cur towards left,
push its address so as to descend towards right subtree later. The code corresponding
to this can be
Oncethe left subtree is traversed, visit the node by popping the most recently pushed
node on to the stack and then traverse towards right if the stack is not empty. If the
stackis empty, traversing the tree in inorder is over and the procedure is terminated.
Thecode to achieve this can be
Aftertraversing towards right, traverse the tree in inorder and repeat the process. This
canbe achieved by executing all the above statements. The C function to traverse the
treein inorder is shown below:
if ( root = NULL)
{
printf("Tree is empty\n");
return;
}
cur = root;
for (;;)
{
while ( cur != NULL)
{
s[++top] = cur; /* push(cur,&top,s); */
cur = cur->l1ink; /* traverse left*/
}
10.58 Q Trees
In postorder traversal, traverse the left subtree in postorder, then traverse the right
subtree in postorder and fmally visit the node. Here also stack is used. But, each node
will be stacked twice once during traversing the left-subtree and another while
traversing towards right. Just to distinguish that traversing the right subtree is over,
we set the flag to -1. So, check the flag field of the corresponding node. If flag field
of a node is -ve, right subtree is traversed and one can visit the node. Otherwise
traverse the right subtree. This process is repeated till the stack is empty. The C
function for this is shown below:
NODE cur;
struct stack s[20];
int top = -1;
for (;;)
{
while ( cur != NULL)
{
top++;
s[top].address = cur; /* push(cur,&top,s) */
s[top].f1ag = 1;
cur = cur->llink; /* traverse left */ ,r
if (top = -1 ) return;
}
Thecomplete C program to create the tree and traversing using iterative technique is
shownbelow:
#include<stdio.h>
#include<stdlib.h>
#include <process.h>
10.60 Q Trees
struct node
{
int info;
struct node *llink;
struct node *rlink;
};
void maint)
{
NODE root = NULL;
int choice, item;
for (;;)
{
printf("l :Insert 2:Preorder\n");
printf("3 :Inorder 4:Postorder\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scan f("%d" ,&item);
root = insert(item,root);
break;
case 2:
preorder( root);
printf("\n");
break;
Q Systematic Approach to Data Structures using C - 10.61
case 3:
inorder(root);
printf("\n");
break;
case 4:
postorder( root);
~ printf("\n");
break;
default:
exit(O);
}
}
Now, let us see "What are the disadvantages of binary trees?" The various
disadvantages of the binary tree are shown below:
• In a binary tree, more than 50% of link fields have \0 (null) values and more
memory space is wasted by storing \0 (null) values
• Traversing a tree with binary tree is time consuming. This is because, the traversal
of a tree either uses implicit stack (in case of recursive programs) or explicit stack
(in case of iterative programs). Whatever it is stack is must. Most of the time is
spent in pushing and popping activities during traversing.
• Computations of predecessor and successor of given nodes is time consuming
• In binary trees, only downward movements are possible
All these disadvantages can be overcome using threaded binary tree. Now, let us
see"What is threaded binary tree?"
Definition: In a binary tree, more than 50% of link fields have \0 (null) values and
more space is wasted by the presence of \0 (null) values. These link fields which
contains \0 characters can be replaced by address of some nodes in the tree which
facilitate upward movement in the tree. These extra links which contains addresses of
somenodes (pointers) are called threads and the tree is termed as threaded binary tree.
In general, a threaded binary tree is a binary tree which contains threads (i.e.,
addresses of some nodes) which facilitate upward movement in the tree.
10.62 Q Trees
Now, let us see "What are the various types of threaded binary trees?" A binary treeis
threaded based on the traversal technique. Since there are three traversal techniques,
threaded binary trees are classified into three types as shown below:
Types of threaded
bi tr
mary ees -f In-threaded binary trees
.
Pos-threaded bmary trees
Definition: In a binary tree, if llink (left link) of any node contains \0 (null) andifit
is replaced by address of the inorder predecessor, then the resulting tree is calledleft
in-threaded binary tree. In a binary tree, if rlink (right link) of a node is NULL andif
it is replaced by address of inorder successor, the resulting tree is called right in
threaded binary tree, An in-threaded binary tree or inorder threading of a binary tree
is the once which is both left in-threaded and right in-threaded. For example, consider
the binary tree shown below:
In the above binary tree, if the right link of a node is NULL and if it is replaced by the
address of the inorder successor as shown using dotted lines in fig. 10.9.2, then the
tree is said to be right in-threaded binary tree.
~ Systematic Approach to Data Structures using C - 10.63
r--------------,
I I
I I
I I
h~
[ _~_- ~~ r I 1
~ ....----I=lllfc:;-r=I:J...----.
! ,,';;~ead 1 t 1
[ZTIIJ ~ j lZJJ'IJ
.~
(b)
Now, let us see "How to implement right in-threaded binary tree in C language?" To
implement a right in-thread an extra field rthread is used. If rthread is 1 the
corresponding right link represents a thread and if rthread is 0 the right link
represents an ordinary link connecting the right subtree. Thus a node can be defined
as shown below:
struct node
{
int info;
struct node *llink; /* Pointer to the left subtree */
struct node *rlink; /* Pointer to the right subtree */
int rthread; /* 1 indicates the presence of a thread*/
/* 0 indicates the absence of a thread */
};
For a binary tree shown in figure 10.9.1, if the left field of a node is NULL and is
replaced by the inorder predecessor as shown in fig.10.9.3, then the tree is said to be
left in-threaded binary tree.
Here also an extra field [thread is used where 1 indicates the presence of a thread and
o indicatesordinary link connecting the left subtree.
10.64 Q Trees
r-------------------------------------, I
I
lthread ~
rI A 1
I
~ I ~'~"'" 1
~ 1 DIE]
tTI:J2J
Figure 10.9.3 Left in threaded binary tree
r--------------,
I I
I I
r----------------------------------~
I I
I
I
I
I
\ ~head !
1 /,:1 AI!
~!l leil\., 1!
CIJIIJ ~1 \
1
I
o::rr:J
.~
struct node
{
int info;
struct node *llink; 1* Pointer to the left subtree */
struct node *r1ink; 1* Pointer to the right subtree *1
int lthread; 1* 1 indicates a thread else ordinary link */
};
typedef struct node* NODE;
An inorder threading of a binary tree or in-threaded binary tree is one which is left
in-threaded and right in-threaded and is shown in fig. 10.9.4. Here two extra fields
[thread and rthread as defined earlier are used and the structure of the node can be
defined as shown below:
struct node
{
int info;
struct node *llink; 1* Pointer to the left sub tree *1
struct node *r1ink; /* Pointer to the right subtree *1
int lthread; 1* 1 indicate a thread else ordinary link */
int rthread;
};
It is desirable to have a header node so as to solve the problems more easily. In this
case, the header node serves as the predecessor of the first node and successor of the
last node. This imposes a circular structure on the tree. When the tree is empty, the
header node is represented as shown in fig. 10.9.5. The tree is attached always to the
left field of the header node.
head
Now let us see, "How to find the inorder successor and predece sor?' and "How to
traverse the tree in inorder?"
10.66 Q Trees
Consider the right in-threaded binary tree shown in fig. 10.9.2. Given a node X, if
rthread is 1, which indicates the presence of thread, its rlink gives the address of the
inorder successor. If rthread is 0, rlink contains the address of the right subtree. Left
most node in the right sub tree is the inorder successor. The C function is shown
below:
NODE inorder_successor(NODE x)
{
NODE temp;
temp = x->rlink;
return temp;
}
The C function to traverse the tree in inorder is straightforward and the reader is
required to trace this program. The C function is shown below:
if ( head->llink = head)
{
printf("Tree is empty\n");
return;
}
Q Systematic Approach to Data Structures using C - 10.67
for (;;)
{
temp = inorder_successor(temp);
printf("%d ",temp->info);
}
This function inserts item towards left of node x only if its left child is empty and is
shownbelow:
temp = getnodet);
temp->info = item;
x->llink = temp;
temp->llink = NULL;
temp->rlink = x;
temp->rthread = 1;
Thisfunction inserts an item towards right of node x only if its right child is a thread
and is shown below:
for (;;)
{
temp = inorder_successor(temp);
printf("%d ",temp->info);
}
This function inserts item towards left of node x only if its left child is empty and is
shownbelow:
temp = getnodet);
temp->info = item;
x->llink = temp;
temp->Hink = NULL;
temp->rlink = x;
temp->rthread = 1;
Thisfunction inserts an item towards right of node x only if its right child is a thread
and is shown below:
temp = getnodet);
temp->info = item;
r = x->rlink;
x->rlink = temp;
x->rthread = 0;
temp->llink = NULL;
temp->rlink = r;
temp->rthread = 1;
}
This function finds the appropriate position to insert an item based on the directions.
. Initially the pointer variable cur points to the root node. If the direction is '1'('L') and
if left child is NULL then the item is inserted towards left of cur, otherwise cur is
updated to point towards left subtree. Similarly if the direction is 'r' and rthread of
cur is 1 indicating a thread, item is inserted towards right at that point, otherwise cur
is updated to point towards the right subtree. The function to insert an item into a right
in-threaded binary tree is shown below:
if ( head->llink = NULL) .
{
insert _left( item,head);
retu rn head;
}
cur = head->llink;
for (;;)
{
printff'Tnsert %d towards left",item);
printf(" or right of%d (l/r) ",item,cur->info);
Q Systematic Approach to Data Structures using C - 10.69
direction = getchet);
printf("\n");
fflush(stdin);
if ( direction = '1')
{
if (cur->llink = NULL)
{
insert_left(item,cur);
return head;
}
else
cur = cur->llink;
}
else
{
if ( cur->rthread = 1 )
{
insert Jight(item,cur);
return head;
}
else
cur = cur->rlink;
}
}
The complete program to create a right in-threaded binary tree and traversing the tree
in inorder is shown below:
#include <stdio.h>
#inc1ude<stdlib.h>
#inc1ude<process.h>
#include <conio.h>
10.70 Q Trees
/* Include: Example 9.2.1.3: C Function to get a new node from availability list */
/* Include: Example 10.9.1.1: Function to find the inorder successor */
/* Include: Example 10.9.2.1: Function to traverse the tree in inorder */
/* Include: Example 10.9.3.1: Function to insert towards left of a node */
/* Include: Example 10.9.3.2: Function to insert towards right */
/* Include:Example 10.9.4.1: Function to construct the threaded binary tree */
void maint)
{
NODE head;
int choice, item;
head = getnodet);
head->rlink = head;
head->rthread = 0;
head->llink = NULL;
for (;;)
{
printf(" 1:Insert 2:Inorder\n");
printf("3 :Exit\n");
printf("Enter the choice\n");
scanf("%d" ,&choice);
switch( choice)
{
case 1:
printf("Enter the item to be inserted\n");
scanf("%d" ,&item);
head = insert(item,head);
break;
Q Systematic Approach to Data Structures using C - 10.71
case 2:
inorder(head) ;
printf("\n");
break;
default:
exit(O);
}
}
Itis symmetric to the function inorder_successorO i.e., llink is replaced by rlink and
viceversa and rthread is replaced by lthread. The C function for this is shown below:
NODE inorderyredecessor(NODE x)
{
NODE temp;
temp = x->llink;
return temp;
Definition:In a binary tree, if llink (left link) of any node contains \0 (null) and if it
isreplacedby address of the preorder predecessor, then the resulting tree is called left
re-threadedbinary tree In a binary tree, if rlink (right link) of a node is NULL and
if itis replaced by address of preorder successor, the resulting tree is called right pre-
hreadedbinary tree. A pre-threaded binary tree or preorder threading of a binary tree
istheonce which is both left pre-threaded and right pre-threaded.
10.72 Q Trees
Definition: In a binary tree, if Ilink (left link) of any node contains \0 (null) and if it
is replaced by address of the postorder predecessor, then the resulting tree is called
left post-threaded binary tree. In a binary tree, if rlink (right link) of a node is NULL
and if it is replaced by address of postorder successor, the resulting tree is called right
post-threaded binary tree. A post-threaded binary tree or postorder threading of a
binary tree is the once which is both left pro-threaded and right pro-threaded.
Now, let us see "What are the advantages of threaded binary trees?" The various
advantages of the binary tree are shown below:
• In a binary tree, more than 50% of link fields have \0 (null) values and more
memory space is wasted by storing \0 (null) values. This wastage of memory
space is avoided by storing addresses of some nodes.
• Traversing of a threaded binary tree is very fast. This is because, it does not use
implicit or explicit stack.
• Computations of predecessor and successor of given nodes is very easy and
efficient
• Any node can be accessed from any other node. Using threads, upward movement
is possible and using links downward movement is possible. Thus, in a threaded
binary tree, we can move in either directions. This is not possible in un-threaded
binary trees.
• Even though insertion into a threaded binary tree and deletion from a threaded
binary are time consuming operations, they are very easy to implement.
Now, let us see "What are the disadvantages of threaded binary trees?" The various
disadvantages of threaded binary trees are shown below:
• Here extra fields are required to check whether a link is a thread or not and hence
occupy more memory when compared with un-threaded binary trees.
• Insertion and deletion of a node consumes more time than its counter part because
many fields have to be modified.
EXERCISES
1. What is a tree? What are the various operations that can be performed on trees?
2. Write recursive functions to implement various traversal techniques
3. Write an iterative function to traverse the tree in inorder
Q Systematic Approach to Data Structures using C - 10.73
Lab Problem 3: Write a C program, which accepts the Internet Protocol (IP) address
in decimal dot format (ex:153.18.8.105) and convert it into 32-bit long integer
(ex:2568095849) using strtok library function and unions.
LJii4L~Llitl1bJ
(Decimal) 153 18 8 105
(Binary) ~1 ~1 ~ Q2!9 Q9,Q9 t.QQ9 Q1J.9 19.5D
(Hexadecimal) 99 12 08 69
'-----------------v----------------~
2568095849
So, given the string 153.18.8.105, extract 153 and copy into field4, extract 18 and
copy into field3, extract 8 and copy to field2 and extract 105 and copy to fieldl.
Then, print 32-bit long integer as shown in following program.
10.74 Q Trees
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
unsigned field4:8;
unsigned field3:8;
unsigned field2:8;
unsigned field 1:8;
} IP-DOT - FORMAT;
typedef union
{
IP_DOT_FORMAT ip;
long int ip_32_bit;
} IP_ADDRESS;
void maint)
{
char a[13];
IP ADDRESS t:,
char *p; Input
p = strtok(a,".");
i.ip.fieldl = atoi(p);
p = strtok(NULL, ".");
i.ip.field2 = atoi(p);
p = strtok(NULL, ".");
i.ip.field3 = atoi(p);
p = strtok(NULL, ".");
i.ip.field4 = atoi(p); Output
• Bubble sort /
• Selection sort
• Simple merge ~
• Merge sort
• Quick sort
• Tree sorts such as binary tree sort and heap sort
• Radix sort
11.1 Introduction
After sorting them in ascending order, the elements are rearranged as shown below:
10, 15,20,25,50
After sorting them in descending order, the elements are rearranged as shown below:
50,25,20,15,10
Before writing the algorithm or the program let us know the answer for" What is the
concept used in bubble sort (Why it is also called sinking sort)?"
Procedure: This is the simplest and easiest sorting technique. In this technique, the
r two successive items A[i] and A[i+ 1] are exchanged whenever A[i] »= A[i+ 1]. For
example, consider the elements shown below:
40,50,30,20,10
The elements can be sorted as shown in figure 11.2.1. In the first pass 40 is compared
with 50 and they are in order. So, no exchange has been done. Next 50 is compared
with 30 and they are exchanged since 50 is greater than 30. If we proceed in the same
manner, at the end of the first pass the largest item occupies the last position. On each
successive pass, the items with the next largest value will be moved to the bottom and
thus elements are arranged in ascending order.
Note: Observe that after each pass, the larger values sink to the bottom of the array
and hence it is called sinking sort.
Original Out put of each pass Note: Observe that at the end of each
Array 1st 2nd 3rd 4th
pass smaller values gradually
"bubble" their way upward to the top
A[O] = 40 40/30/20/10 (like air bubbles moving to surface of
A[I] = 50 30/20/10 20 water) and hence called bubble sort.
A[2] = 30 20/10 30 30
A[3] = 20 10 40 40 40
A[4] = 10 50 50 50 50
i
i = 0 to 3
t
i = 0 to 2
i
i = 0 to 1
t
i = 0 to 0
5-2 5-3 5-4 5-5
5-(1+1) 5-(2+1) 5-(3+ 1) 5-(4+1)
n- (j+l) n -0+1) n-(j+ 1) n-(j+ 1)
In general we can say i = 0 to n-(j+ 1) or i = 0 to n - j - 1. Here, j = 1 to 4 represent
pass number. In general j = 1 to n-l. So, the partial code can be written as:
Example 11.2.1: Program to arrange numbers in ascending order using bubble sort
Before writing the algorithm/program let us know the answer for" What is the concept
used in selection sort?"
Procedure: As the name indicates, we first find the smallest item in the list and we
exchange it with the first item. Obtain the second smallest in the list and exchange it
with the second element and so on. Finally, all the items will be arranged in ascending
order. Since, the next least item is selected and exchanged appropriately so that
elements are finally sorted, this technique is called Selection sort.
For example, let us take the following elements and are sorted using selection
sortas shown below: 45 20 40 5 15
. : :~J ::J
I I
A[2] = 40
A[3] = 5 1 :~
A[4] = 15 I 15 I 20 40 I 45
I .
pt smallest is 12nd smallest is 13Tdsmallest is 14th smallest is I All elements I
5. Exchange it 115. Exchange 120. Exchange 140. Exchange it I are sorted
withl" item I it with 2nditem I it with 3Tditem I with 4th item I
Design:The smallest element from ith position onwards can be obtained using the
followingcode.
pos = i;
for tj =i+l;j <n;j++)
{
if ( a[j] < a[pos] ) pos = j;
}
Afterfinding the position of the smallest number, it should be exchanged with ith
position.The equivalent statements are shown below: '
temp = a[pos];
a[pos] = a[i];
a[i] = temp;
Theabove procedure has to be performed for each value of i in the range 0 ~ i < n-l.
Theequivalent C program is shown below:
11.6 ~ Sorting and searching
scanf("%d" ,&a[i]); 45 te 40 5 15
} 1 ~ ': \J7<
'ri "(if .: '2
/* Sort the elements */ .J I Sort usine selection sort
for (i = 0; i < n-l ; i++) D(frOS c- ~6
{ I See Page number 13.29 for
pos_= 1; () ~zt)sorting the given numbers using
fot'{j - i +1; j < n; j++) selection sort
{
if ( a[j] < a[pos]) pos = j;
/'
temp = ajpos];
aGJos] ,,; a[i]; ,
a[i] = temp;
}
Output
printf("The sorted items are\n"); The sorted items are
for ( i = 0; i < n; i++)
{
printf("%d\n",a[i]); 5 15 20 40 45
}
}
Q Systematic Approach to Data Structures using C - 11.7
11.4Merge Sort
First,let us discuss the way to merge two sorted vectors and later concentrate on merge sort. The
processof merging of two sorted vectors into a single sorted vector is called simple merge. The
onlynecessary condition for this problem is that both vectors should be sorted (either in
ascendingor descending order).
Design:Suppose we have two sorted vectors A and B. Assume m elements are in vector A and n
elementsare in vector B. To obtain a sorted vector, compare ith item of vector A with t item of
vectorB and copy the lesser item into kthposition of resultant vector C (with 0 as the initial value
forthe variables i, j and k).That is, if A[i] is less than B[j], copy the item A[i] to the resultant
vectorC[k] and update the index variables i and k by 1. Otherwise, copy an item B[j] to C[k] and
updatethe index variables j and k. This process of comparing and copying is repeated, for 0::;; i <
m and0 ~ j < n. When either i or j exceeds its limits, the elements remaining in other vector, can
becopiedinto the vector C. Consider the two sorted vectors A and B shown below.
A B C
~~
i J k
Inthis example, m IS 4 ana n IS j. set l,j ana k to U minauy. Arter comparrng AlIJ ana tlUJ we
knownthat A[i] is less than B[j] (10 < 15). So, copy A[i] to C[k] and increment i and k. Then we
compare20 and 15. Since 20 is greater than 15, copy 15 to C and increment j and k by 1.
Repeatingthis process, we have 10, 15,20, 25 and 30 in the vector C. The complete tracing is
shownbelow:
A B C
10 20 40 50 15 25 30 10 10<15,10is
;,f\ It\: copied to C. Now
:J k
k
update i and k
I J
10 20 40 50 15 25 30 10 15 15 < 20 and so 15 is
/I'\. /I'\. copied to C. Now update
j and k
1 J k
20 < 25 and so 20 is
10 20 40 50 15 25 30 10 15 20
copied to C. Now update
IT\
'" i and k
1 J k
25 < 40 and so 25 is
10 20 40 50 15 25 30 10 15 20 25 copied to C. Now update
JI\ IT\
j and k
1 i k
30 < 40 and so 30 is
10 20 40 50 15 25 30 10 15 20 25 30 copied to C. Now update
JI\ JI\
j and k
1 J k
Copy remaining items
10 20 40 50 15 25 30 10 15 20 25 30 45 50 from A to C
m elements n elements m+n elements
11.8· Q Sorting and searching
Since, there are no more elements in B to compare, the remaining elements of A can be
copied into C. Now 10, 15, 20, 25, 30,40, 50 are stored in the vector C. In general, when eitheri
or} exceeds its limits, the elements remaining in other vector, can be copied into the vector C and
the corresponding pseudocode is shown below:
Thus using simple merge, one can merge two sorted vectors into a single sorted vector. The
algorithm is shown below:
Example 11.4.1: Algorithm to merge two sorted vectors into a single sorted vector(simple
merge)
Algorithm SimpleMerge(A, B, C, m, n)
II Purpose: Merge two sorted arrays into a single sorted array
II Input:
II A is a sorted vector with m elem~nts
liB is a sorted vector with n elements
II Output:
II C is a sorted vector obtained after merging the elements of A and B.
i~j~k~O
while ( i < m and j < n )
if.! A[i] < B[j] ) then
C[k] ~ A[i]. II Copy the lowest element from A to C
i ~ i+ 1 II Point to next item in A d
else
C[k] +- Bfj] II Copy the lowest element from B to C
j+-j+l II Point to next item in B
k+- k + 1; II Point to next item in C
end if
end while
In the above algorithm we have used two sorted arrays A and B with index values of vector A and
vector B starting from O. Now, consider a situation of a single vector which is divided into two
sorted parts as shown below:
Vector A
mid mid+ 1
It is clear from the figure that the elements from low to mid are sorted and elements from mid+ 1
to high are also sorted. But, if we take the elements from low to high the elements are not sorted.
Note that instead of considering the vector A as single, we can assume that there are two arrays
with single name A, but one array starts from low to mid and other array starts from mid+ 1 to
high. Now, if we want to merge the two sorted vectors into a single sorted vector, we can modify
the algorithm shown in example 4.4 by replacing the vector B by A and the limits of the first part
of the array being low and mid whereas the limits of second part of the array being mid+ 1 and
high. The modified algorithm to merge two sorted vectors identified by a single vector between
low to mid and (mid + 1) to high into a single sorted vector is shown below:
Example 11.4.2: Algorithm to merge two sorted vectors (a single array divided into two sorted
arrays) into a single sorted vector
!/Purpose : Sort the elements of the array between the lower bou~d and upper bound
//Input: A is an unsorted vector with low and high as lower bound and upper bound
Themerge sort technique can be easily understood by taking the example. Consider the elements:
60,50,25,10,35,25,75,30
The above elements can be sorted using merge sort and the trace for the same is given using
recursiontree as shown in figure 4.2.
- - - - - - - - - - - - - - =-==-==-===-===-==-==-=-=-='-9
Unsorted vector I 60,5~25, 1~35,25,75,30 I
~ ~
1r--6-0,-5-0-,
2-5-,-1-0
-, ,...--------,
~ ~
#include <iostream.h>
void simple jncrgetint a[], int low, int mid, int high)
{
int 1 low; II Points to first element of left part of the array
int j = mid+l; II Points to first element of second part of the array
int k = low; II Points to first element of resulting array
int c[MAX]; II Temporarily holds the sorted elements
while ( i <= mid ) II Copy the remaining items from left part of A to C
{
c[k++] = a[i++];
while (j <= high ) II Copy the remaining items from right part of A to C
{
c[k++] = a[j++];
}
void mainO
(
int a[MAX]; II Elements to be sorted
int n; II Number of elements to sort
int 1; II Used as an index to access the elements
merge_sort(a,O , n-1);
Thissorting technique works very well on large set of data. The first step in this technique is to
partitionthe given table into two sub-tables such that the elements towards left of the key element
areless than the key element and elements towards right of the key element are greater than key
element.After this step, the array is partitioned into two sub tables. The elements in the left sub
tableare lesser and elements in the right sub table are greater than the key element. For example,
the array obtained after partition may look like shown below:
36 37 1110 42 72 65 98 88 78
-<4~ -->42-
11.14 Q Sorting and searching
low i j, high
42 37 11 9.8 36 72 65 10 88 78 ( 42> 37 so, increment i)
i
42 37 11 98 36 72 65 10 88 78 ( 42> 11 so, increment i)
36 37 11 10 42 72 65 98 88 78
- <42 - -->42-
Here, the elements towards left of the key item 42 are less than 42 and elements towards right of
it are greater. Now, sort the left sub-table and right-sub-table. After sorting the left and right sub-
tables the position of the key item i.e., 42 is not changed and the entire array is sorted. The same
process is repeatedly applied for the left sub-table and right-sub tables, till the elements in both
the sub tables are in their appropriate positions so that the entire array is sorted.
Q Systematic Approach to Data Structures using C - 11.15
To partition the array into two sub tables, two index variables i and) are used. The index variable
i points to low+ 1 where low points to the first element and the index variable) points to high
which points to the last element. The item a[low] is used as a key element. This key item has to
be placed in a position) such that a[k] <= a[j] for low <= k < j and a[j] >= a[l] for j+ 1 <= I <=
high. This can be achieved by comparing the item key with a[i] and aU]' Keep incrementing the
index i whenever key >= a[i]. Immediately when this condition fails, keep decrementing the
index) whenever key < aU]' At this stage if i is less than), exchange a[i] with a[j] and repeat the
process. Ifi is greater than or equal to) then exchange a[low] with a[j] and return) which gives
the position of the partitioned element. Now the elements towards left of aU] are all less than a[j]
and elements towards right of it are greater. Consider the elements shown below:
3711 98 36 72 65.10 88 78
In a table or sub-table, the leftmost element is considered to be a key element always. In the items
shown above, 42 is the key item. Manipulate this table such that elements towards left of 42 are
lesser and elements towards right of it are greater. The fig.II.5.1 shows how partition can be done
for the above elements. This can be done by keep incrementing i byl whenever key is greater than
or equal to a[i] and while updating i, it should not exceed high and the equivalent statement is
shown below:
while (i < high && key >= a[i]) i++;
Once this condition is false, keep decrementing) by 1 as long as key is less than a[j]' This can be
achieved using the statement
while ( key < a[j] ) j--;
Once the control comes out of this loop, if i is less than), exchange a[i] with a[j]' Otherwise,
exchange a[low] with a[j] and return) as the position of the partitioning item. The complete C
function to partition the array as explained is shown below:
After partitioning, the original table is partitioned into two sub tables as shown below:
{36 37 11 10} and {72 65 98 88 78}
The same procedure is applied to each of these sub tables repeatedly until the table is completely
sorted. This method of sorting is called partition exchange sort or quick sort. The divide and
conquer approach is used to sort each of these sub-tables as discussed in merge sort. Using this
approach, after ,partitioning the table into two sub-tables, sort the left sub-table recursively and
then sort the right sub-table recursively. Note that as in merge sort, there is no need of merging
the left sub-table and right sub-table. The C function to sort using quick sort is shown below:
Example 11.5.2: Function to sort the numbers in ascending order using quick sort
The complete C program to arrange numbers in ascending order using quick sort is shown below:
Example 11.5.3: C program to sort the numbers in ascending order using quick sort
#include <stdio.h>
/* Include: Example 11.5.1: Function to partition the array for quick sort */
/* Include: Example 11.5.2: Function to sort the numbers in ascending order using quick sort */
void maint)
{
int i,n,a[20];
quicksort(a,O,n-l );
A priority queue is a special type of data structure wherein the elements are inserted into the
queuebased on the priority and elements are deleted from the queue based on the priority. While
deleting,a highest priority element has to be deleted. To implement a priority queue efficiently, a
heapis used. Apart from implementing priority queue using a heap, it is also possible to sort the
elements in ascending order or descending order which is often called heap sort. Before
discussingheap sort, let us define a heap.
A heap is a complete binary tree where all the levels are full except possibly the last level
i.e.,if the maximum level in the tree is i, upto (i-1)Ihlevel the tree is complete so that the number
of elements in (i_l)th level should be i-I. In level i, number of nodes can be less than or equal to
i If the number of nodes are less than z' at ilh level, then the nodes in that level should be
completely filled, only from left to right (only right most leaves are missing at the last level).
~
(a) (b) (c)
l\ ~
"0
(d)
d (e)
Figure 11.6
A heajs can either be an ascending heap or a descending heap. In an ascending heap, the root
node contains the least element and each node is less than its left child and the right child as
shown in fig 11.6.a. Observe that the items in the path from the root to a leaf are in ascending
order. In descending heap, the root node contains the largest element and each node is greater
than its left child and right child as shown in fib.ll.6.b and fig.l1.6.c and so the items in the path
11.18 Q Sorting and searching
from root node to a leaf are in descending order. Since a heap is an almost complete binary tree,
the nodes can be numbered from the root, one level at a time. In each level the nodes are
numbered sequentially from left to right as shown in figure II.6.c.
In a heap, for a given node i, the parent position say j is obtained by (i - 1)/2, the position
of left child is given by 2i+ 1 and that of right child is 2i + 2. So, in an ascending heap, each a[j]
is less than or equal to a[i], where a[j] is the parent and a[i] can either be left or right child. The
position of each child varies from 1 to n-l (i.e. 1 ~ i < n) and j = (i-I) I 2. Similarly, in a
descending heap, each a[j] is greater than or equal to a[i] where a[j] is the parent and a[i] is the
child. .
ote: The trees shown in fig.II.6.d and fig.ll.6.e are not heaps.
The heap shown in fig.II.6.c can be represented using an array as shown below:
A[O] [1] [2] [3] [4] [5] [6]
100 75 80 25 50 45 30
To sort the elements the first condition is that, the elements should be in a heap. Let us discuss the
way to obtain a heap given an array of arbitrary elements. The elements in an array can be
represented using a tree. A tree consisting of only one element is a heap (it is a root element). To
this initially created heap, insert an item a[k] at the positions 1 ~ k < n. Here, k is assumed to be
the child's position in the heap and a[k] is the item to be inserted. A heap is created using the
following steps for each a[k] for 1 ~ k < n.
• Assign k to i and a[k] to item initially
• Obtain the position of the parent usingj = (i-I) I 2
• Repeat while parent exists and item is greater than the parent
1. Move the parent down to the position of child.
2. Make parent node as the child node
3. Obtain the position of new parent.
• Insert the item into child's position.
Note:The index i indicates the position of the child and} indicates the position of the parent.
,,
® ~
,,
c] ~ ."'~
k=2 k=3
(a) (b) . (c)
k=4 (d)
(e)
//7
@
k=6 (t)
k=7 (g)
Now, let us see how to obtain a heap shown in fig.1I.7.c from the elements shown below.
50,25,30,75,100,45,80
Initial heap is the tree, which consists of the first item as shown in fig.1I.7.a. Next items to be
inserted are 25 and 30 and are inserted in positions 2 and 3 respectively and the resulting heaps
are shown in fig. 1I. 7.b and fig.1I.7.c. If an item 75 is inserted at 4th position, the tree is no more
a heap. To make it a heap, compare 75 and 25 and since 75 is greater than 25, move 25 down and
75 up and the resulting tree is shown in fig.1I.7.d. Again compare 75 with 50 and since 75 is
greater than 50, move 50 down and 75 up to form the heap as shown in fig.1I.7d. Similarly, the
items 100,45 and 80 are inserted in the appropriate positions and the corresponding heaps are
shown in fig.l l. 7 .e, fig. 1I. 7.f and fig. 1I. 7 .g.
end while
Once the heap is created, then root has the highest value. Next step is to sort the elements in
ascending order. Since the root has the highest value, this value can be exchanged with the last
elementso that the item in the last position is sorted. Now, sort the remaining n-I elements. This
canbe achieved by reconstructing the heap for n-I elements and the formal algorithm for this is:
The steps 2 and 3 are performed repeatedly as long as the child exists or item is greater than the
children.When one of the conditions is failed, insert the item into appropriate position. The next
versionof the formal algorithm is shown below:
11.22 ~ Sorting and searching
Algorithm Adjust(a,n)
{
item = a[O] II node to be inserted to recreate heap
j =0 II and its position ( parent)
obtain left child
. while left child exists do
{
if right child also exists obtain the position of the largest child
if item less than the largest child
move largest child to parent position
Make largest child as parent
Obtain position of the left child for the new parent.
Else Proper position found and insert
}
Insert item into parents position.
Once the heap is created and after exchanging 1st item (at position 0) with nth item (which is at
position n-l), the tree is no more a heap. At this point, the root item i.e., a[O] should be moved
down the tree and should be inserted at the appropriate position to get back a heap for remaining
items. For this reason, j that initially points to root is considered as parent and i its child. The
position of left child is given by 2j+ 1 and right child is given by 2j+2. If i points to left child, i+l
gives the position of right child. When we move down the tree, note that the value of i, should not
exceed n-l. The detailed algorithm for adjusting the heap is shown below:
Algorithm Adjust(a,n)
{
item = a[O] II node to be inserted to recreate heap
j=O II and its position
i = 2*j+1 Ilobtain left child
while i <= n-I do II As long as left child exists do
j = 0; /* Parent */
item = afj];
i = 2*j+l; /* Left child */
Consider the situation shown in fig.l1.8.a. After exchanging a[O] with a[n-l] i.e., 100 and 45, the
tree shown in fig.l1.8.b is obtained and (n_lyh item is sorted. Now, reconstructing the heap for
remaining n-l elements using the above procedure the tree shown in fig.l1.8.c is obtained. We
see that the item 45 is moved down the tree and is inserted at the appropriate position so as to get
the heap for n-l elements.
Using the two functions i.e., to create a heap and reconstructing the heap the elements can be
easily sorted and the heap sort algorithm is shown below.
2. Once the heap is created, the root has the highest value and exchange root with the nill
element. Now the nth item is sorted and we have to sort the remaining n-I elements. This is
achieved by reconstructing the heap by calling the function adjusu) for n-I elements. Then
the root is exchanged with (n-I yh element. Now last two elements are sorted. Recreate the
heap for the remaining n-2 elements by calling the function adjusu) for n-2 elements. By
repeating this process, fmally all the elements will be arranged in ascending order.
The fig.l1.9. clearly shows how the elements are sorted. Initially, using the function heapify(a,n)
create the heap as in fig.l1.9.a. Exchange a[l] with a[7]. The last item is sorted. Recreate the
heap for remaining 6 elements. Exchange a[l] with a[6]. Now last two items are sorted. Then
recreate the heap for 5 elements. Repeating this way all the items are arranged in ascending order.
Exchange
==>
a[O],a[6]
Exchange a[O],a[5]
c::=:======~
sorted part
Exchange a[O],a[ 4]
sorted part
Q Systematic Approach to Data Structures using C - 11.25
Exchange a[O],a[3]
sorted part
25
Exchange a[O],a[2]
4
art ~
The figures (fig.ll.9) on the left hand side, shows the heap after reconstructing and the
figure on the right hand side shows the sorted part shown using rectangular region. The elements
outside the region, indicates the tree elements for which the heap has to be created. The trace for
the algorithm heap sort is shown in fig.ll.9. The algorithm to sort items and the C function is
shown in the example 1l.6.3. The complete C program to arrange numbers in ascending order
using heap sort is shown below:
#include <stdio.h>
void maint)
{
int a[20],n,temp,i;
heapsort( a,n);
This can be achieved by creating a heap. The function heapifyt) creates an ascending heap. Delete
the root node after creating a heap. Now, recreate the heap by calling the function adjustt) forthe
remaining elements. This way, one can easily implement an ascending priority queue. Similarly,a
descending priority queue can also be implemented.
The insertion sort works very well when the input elements are partially ordered and the numbers
to be sorted are very less. The different techniques using this method are:
This technique was invented in 1959 by D.L.Shell and hence the name Shell Sort. This technique
is similar to a simple insertion sort. Instead of comparing the adjacent elements one after the other
as in insertion sort, far apart elements with equal distance are compared. Suppose, the following
elements have to be sorted:
Let the gap between the elements to be compared is 3. The subfiles generated with this gap are
shown below:
I 20 I 5 I 30 I 45 I 10 I 75 I 50 I 36 I 75 I 80 I 65 I 90 I 85 I
1 I I I I I I
Output of pass 3
Each sub file is sorted using insertion sort as shown in fig. 11.7.1. Now, reduce the gap between
the elements to be compared by 1. Generate the subfiles and sort these files based on this
technique. Repeat the same process each time by reducing the gap. Finally, if the gap is one, it
works as simple insertion sort and final output obtained will be the sorted vector.
It is observed from the example shown in figure 11. 7.1 that, in this sorting technique,
instead of comparing adjacent elements as in insertion sort, the elements with an equal gap or
equal distance are compared. The simple insertion technique is used with an exception that the
distance between the elements to be compared is same. The first version of the algorithm/function
is shown below:
item = a[i];
j = i-gap
while (j >= 0 && item < a[j] )
{
a[j+gap] = a[j];
j = j - gap; •
}
a[j+gap] = item;
Note that in the above function, if gap has the value 1, this function is reduced to the insertion
sort technique discussed in the previous section. The above set of statements have to be executed
for each a[i] where gap s i < n. So, the next version of the function can be written as
a[j+gap] = item;
}
It has been seen earlier that an insertion sort is very efficient if the array is almostsorted
and if the size is small. Initially, make the gap between individual items to be compared aslarge
say gap = (n -1) / 2 so that the sub files are small and the insertion sort technique can be used.In
the next pass, reduce the gap by a factor of 2 and sort again using the above technique. Finally
when gap is 1, note that it works as a simple insertion sort and all the elements are arrangedin
ascending order. The complete C function for this is shown below:
~ Systematic Approach to Data Structures using C - 11.29
In the above function, the statements in the middle loop can be written using the for statement as
shown below:
The functions provided in example 11.7.1 and 11.7.2 are equivalent. In these functions there are
three nested loops: The outermost loop is controlling the gap between the elements to be
compared, reducing the gap by a factor of 2 in each pass. Once the gap is zero, all the elements
are sorted in ascending order. The middle loop is used to access each item in each sub-file. The
complete C program to sort the items using Shell Sort is shown below:
Example 11.7.3: C program to sort n numbers using shell sort
#include <stdio.h>
/* Insert the function given in example 11.7.1 or 11.7.2 */
void maint)
{
int n,i,a[20];
printf("Enter the value for n\n");
scanf("%d" ,&n);
printf("Enter n values\n");
for ( i = 0; i < n; i++)
scanf("%d ,&a[i]);
II
shell_ sort(n,a);
The Address Calculation Sort is also called Hash Sort. In this technique, a particular kind of
hashing is used. The hashing function hashO should have the property that if xl :$; x2, then
hash(x1) :$; hash(x2). The function which exhibits this property is called order-preserving or 1101/-
decreasing hashing function. If this hashing function is used to hash an item to which some
previous items have already been hashed, then this item is placed in such a way that all the items
hashed to a particular value should be arranged in ascending or descending order. Thus all the
items in one sub-file will be less than or equal to the elements in the subsequent sub-files. When
all the items have been placed in this order into sub-files, finally concatenate the items in each
sub-file to get the sorted elements. For example, consider the items shown below.
57,45,36,91,28,79,35,68,89,20,62,43,84,55,86
Since the digits available are 0 to 9, we use an array of pointers a[10], where each location in this
array points to the first item in the sub-file whose first digit is same.
~ Systematic Approach to Data Structures using C - 11.31
Hash value for this problem can be obtained by selecting the largest number and finding at which
position the most significant digit is present. For example, for the item 45 hash value is 10
because the digit 4 is in tens position. For the item 199 hash value is 3 since the digit 1 is in
hundredth position. The hash value of a largest number can be obtained using the function shown
below:
i = logI0(big);
After finding the hash value, divide each item to be sorted using this hash value and insert the
item into the appropriate location. For example, if item is 45 and hash value is 10,45110 is 4. So,
insert 45 into the 4th location. If some items are already present in this location, insert it in the
appropriate position such that items in that location are in order. For this reason, we use an array
of ordered linked list. Once all the items are inserted in this way, obtain the items present in each
list in the array one by one and display. The C function to sort the items using Address
Calculation Sort(Hash Sort) is shown below:
The C program to sort the items using Address calculation sort (hash sort) is shown below:
Example 11.8.3: C program to sort numbers using Address Calculation Sort (Hash Sort)
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <math.h>
struct node
int info;
struct node *link;
};
1* Include Example 9.2.1.3: C Function to get a new node from the availability list *1
1* Include Example 9.3.7: C function to create an ordered linked list *1
1* Include Example 11.8.1: C function to find the hash value *1
1* Include Example 11.8.2: C function for Address Calculation Sort (Hash Sort) *1
void maint)
{
int i,n,a[40];
hash_sort(a,n);
This is the method of sorting technique used in card-sorting machines, the machines that are not
available now and are found only in museums. Here we assume that all the numbers to be sorted
°
are of equal digits. There are 10 pockets ranging from to 9. Initially all the packets are empty.
Scan the numbers one by one sequentially, separate the least significant digit and insert into the
appropriate packet. If a packet is not empty, the new item has to be inserted at the rear end of the
pocket. This technique can be explained easily by taking an example. Consider the elements
shown below that we want to sort.
57,45,36,91,28,79,35,68,89,20,62,43,84,55,86,96,78,67
Scan the first number. Here it is 57. Separating the 151 least significant digit we get 7 and we
insert 57 into the 71h pocket. Likewise, scan each item one by one, separate the 151 least significant
digit and insert the item into the appropriate pocket. After this pass is over, the items stored in all
10 pockets are shown below:
55 96 78
35 86 67 68 89
20 91 62 43 84 45 36 57 28 79
Pockets-7 0 I 2 3 4 5 6 7 8 9
20,91,62,43,84,45,35,55,36,86,96,57,67,28,68,78,79,89
In the second pass, empty all the pockets, scan all the numbers one by one, separate the 2nd least
significant digit and insert into the appropriate pocket. The contents of the pockets after the 2nd
pass is shown below:
68 89
28 36 45 57 67 79 86 96
20 35 43 55 62 78 84 91
Pockets-s 0 I 2 3
Copying the items from pockets
4
°
5 6 7
to 9 we get
8 9
20,28,35,36,43,45,55,57,62,67,68,78,79,84,86,89,91,96
While designing the algorithm we should choose the appropriate data structures. We have 10
pockets, which are empty initially. An item to be inserted into a pocket has to be inserted at the
end and we can insert any number of elements. For this we can use an array of linked lists. We
also know that items are inserted at the end of every pocket and while copying we are accessing
the elements which are at the beginning. So, another data structure that we can use is FIFO data
structure which is queue. While designing we can assume the following functions are available.
11.34 ~ Sorting and searching
• separate(item,j) - 111isfunction returns the jth least significant digit. For example, if item
is 345 and j is 2, the function returns 4.
• insert_rear(start,item) - This function inserts an item at the end of the list identified by
start and returns address of the first node in the list.
2. Obtain the jlh least significant digit from each item to be sorted one after the other and insert
into the appropriate pocket. This can be done using the statement shown below:
Repeat for i '= 0 to n-I
digit = separate(a[i],j) II separate jib digit from a[l]
p[digit] = insert_rear(a[i],p[ digit)) II insert a[i] at the end of digit pocket
End for
3. Copy all the items from each pocket one after the other
Repeat for i = 0 to 9
temp = p[i]; II Obtain starting location ofith pocket
k=O
/* If items are in the pocket, copy them *1
While (temp != NULL)
a[k] = info(temp)
k=k+l
temp = link(temp J
End while
End for
The three statements have to be executed for each pass j where 1 ~ j ~ m where In is the
maximum number of digits in the largest item. The complete algorithm is shown below:
big = largest(a,n)
m = loglO(big) + 1
Repeat for j = 1 to m
Repeat for i = 0 to 9 II Initialize all the pockets
P[i] = NULL
End for
Repeat for i = 0 to n-I
digit = separate(a[i],j) II separate j'h digit from a[l]
p[digit) = insert reartafij.p[digitj) II insert a[i] at the end of digit pocket
End for
Q Systematic Approach to Data Structures using C - 11.35
Repeat for i = 0 to 9
temp = p[i]; II Obtain starting location of i'" pocket
k=O
I*If items are in the pocket, copy them *1
While (temp != NULL)
ark] = info(temp)
k=k+1
temp = link(temp)
End while
End for
End for
TIle statement m = log10(big) + 1 is used to obtain the number of passes necessary. After
executing the statement 111 contains the number of passes required. For example, if big is 234,
after the execution of this statement 111 will be 3 which is the number of digits in the number. TIle
above algorithm we have written by assuming the functions shown below are existing.
• separate(itemJ)
• insert_rear(start,item)
• largest(a,n)
The function to separate jth digit from the item is shown below:
Algorithm separate(itemJ)
{
return item 11oi-1 mod 10
If item is 235 and j is 3, the function returns 235 I 102 mod 19 which is 2.
Algorithm largest(a,n)
{
big = a[O];
repeat for i = 1 to n-1
if ( a[i] > big) big = a[i]
end for
return big
The C function to arrange numbers in ascending order using radix sort is below:
big = largest(a,n);
rn = log10(big) + 1;
k=O;
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <process.h>
struct node
{
int info;
struct node *Iink;
};
typedef struct node *NODE;
.!;lSystematic Approach to Data Structures using C - 11.37
/* Include Example 9.2.1.3: C Function to get a new node from the availability list */
/* Include: Example 9.3.2: Function to insert an item at the rear end of the list */
/* Include: Example 11.9.1: C function for Radix Sort */
void maint)
{
int n,i,a[20];
printf("Enter n items\n");
scanf("%d ,&n);
II
radix_sort(a,n);
11.10 Searching
Before writing the algorithm/program for searching, let us ask the question "What is searching?
What are the various searching techniques?"
Definition: More often we will be working with large amount of data. It may be necessary to
determine whether a particular item is present in the large amount of data. This process offinding
11.38 Q Sortin and searchin
a particular item 1/1 the large (1/1/011111 oj data is called searching. The two important and simple
searching techniques art' show n below:
Before writing the algorithm or the program it is better to know the answer for "What is linear
search?"
Definition: A linear search is a simple searching technique. In this technique we search for a
given key item in the list In linear order (sequential order) i.e., one after the other. The item to be
searched is 0 ften called key item. The linear search is also called sequential search. For example,
if key = 10 and the list is 20,10,40,25 after searching we say that key is present. Ifkey = 100
after searching we say that key is not present.
Once we know the concept of linear search, the next question is "How to search Jor an
item in a list oj elements?" ow, let us see how to search for an item. The procedure is shown
below:
Procedure: Let us take an example. Assume 10 is the item to be searched in the list of items 50,
40, 30, 60, 10. Observe from above figure that, 10 has to be compared wit a[O], a[ 1], a[2], a[3]
and a[4] as shown below:
Item = 10
50 40 30 60 10
a[O] a[l] a[2] a[3] a[4]
i (Note: i = 0, 1,2,3,4)
~A ,
But, once the value of i is greater than or equal to 5, it is an indication that item is not present and
display the message"UnsuccessJul search",
~ Systematic Approach to Data Structures using C - 11.39
In general the terminal condition in the for loop i < 5 can be replaced by i < n. The C
program for linear search is shown below:
#include <stdio.h>
#include <process.h>
Note: When the elements are not sorted and size is very less, linear search is used. If the
elements are sorted, a better algorithm such as binary search can be used.
11.40 Q Sorting and searching
Note: The binary search has already been discussed in section 2.6
Now, let us see the difference between binary search and linear search.
The search time for a given key can be reduced significantly if the record keys are stored in
ascending or descending order in a table. For example, assume that a table has n records but not
sorted and assume the item to be searched is not present. Now, to say that item is not present in
the table it requires n number of comparisons (see the previous section for details). On the other
hand, if the record keys are sorted, the number of comparisons are reduced significantly. This is
because, if the records are arranged in ascending order and if the key item being searched is
greater than the item in the table, we can immediately say that search is unsuccessful. In other
words, a given item is missing from the table (which is sorted in ascending order) as soon as we
encounter an item that is greater than the item being searched. Thus, the searching involves few
lookups even during sequential searching. The corresponding C program is shown below:
Note: For simplicity and efficiency of sequential processing, it is worthwhile to arrange the tables
in ascending order or descending order.
~ Systematic Approach to Data Structures using C - 11.41
Example 11.10.3: C function showing linear search on sorted table
#include <stdio.h>
int linear(int item, int n, int a[])
{
int t:,
void maim)
L
int i, n, item, a[20], pos;
printf("Enter the value ofn\n");
scanf("%d",&n);
printf("Enter the elements in ascending order\n");
for (i = 0; i < n; i++)
scanf("%d",&a[i]);
printf("Enter the item to be searched\n");
scanf("%d",& item);
pos = linear(item, n, a);
if (pos = -1)
printf("Item not found\n");
else
printf("ltem found at %d position\n", pos);
The search efficiency for sorted tables can be improved further using indexed sequential search.
This technique- requires additional storage space for storing the index of group of items. In this
technique apart from the sorted table, an auxiliary table say t that holds the key as well as
corresponding pointer to the table is used. The basic requirement in this method is that the
elements in the auxiliary table t as well as the elements in the main table where actual search is
being carried out must be sorted on key. Assume that the auxiliary table t is l/Sth the size of the
sorted table. The auxiliary table t, the sorted table and the relationship between the two is shown
below:
11.42 Q Sorting and searching
The algorithm for indexed sequential search is simple and straight forward and is shown below:
for (i = low; i <= high; i++) /* Search for the key in master record */
{
if (a[i] = key) return i + 1; /* Key found */
}
return -1; /* Key not found */
~ Systematic Approach to Data Structures using C - 11.43
Using this method, any record can be sequentially searched but with more efficiency with few
lookups. A sequential search is performed on smaller index (left table of figure 12.4.1). Once the
correct position has been found, using the auxiliary table which holds kindex and pindex, it is
required to find the range low and high which is very important step. Once this step is over,
normal sequential search is done for this small table identified (between low and high) thus
increasing the efficiency.
Advantages: The main advantage of indexed sequential search is that the items present in the
table are examined sequentially. But, the search time for an item being searched is reduced
sharply. The technique is very simple. By using flags, deletions from the table can be done very
easily.
Disadvantages: The table that gives the index where to search for can be implemented efficiently
using linked lists (table t shown in figure 12.4.1) and the larger one using arrays. Since linked list
is used, even though insertions and deletions are performed easily, there is an overhead of extra
space for the pointers. If the table is too large to search, then secondary indexing can be used. The
secondary index, actually provide an index to the primary index which in turn points to the entries
in the table we are searching for. Even though, deleting an entry from the table is easy (by using
flags), inserting an item into the table is very difficult and requires movement of large quantity of
data. So, the complexity of the program increases during insertion.
This technique also works if the items are arranged either in ascending order or descending order.
This technique is better than binary search when the items are uniformly distributed between a[O]
and a[n-l]. The same technique used in binary search is used here also with one exception. In
binary search, the mid value is obtained using the relation:
The complete C program to search for an item using interpolation search is shown below:
#inc\ude <stdio.h>
int search(item, a, low, high)
int item; /* Element to search */
int a[]; /* Elements to be searched */
int low; /* Points to the first element */
int high; /* Points tot the last element */
void maint)
{
int n, i, a[20], item, pos;
printf("Enter the number of elements\n");
scanf("%d", &n);
printf("Enter %d items\n",n);
for (i = 0; i < n; i++)
{
scanf("%d" ,&a[i));
}
Exercises
1. What are the various sequential searching methods? Explain with example
2. Write a C program to implement iterative and recursive sequential searching technique
3. What sequential technique can be used to search an ordered table
4. What is the advantage of indexed sequential search over searching an ordered table without
index?
5. Write C programs to arrange the numbers in ascending or descending order using various
techniques
6. Write C programs to search for an item using linear search, binary search, interpolation search
and indexed sequential search.
QUESTION PAPERS - 12
12.1 Linear queue and its applications
Now, let us see "What is a linear queue? What arc the applications of linear arrays?"
Applications: The linear queues are widely used in waiting lines. Some applications
of queues are shown below:
+ Print Server: In a computer network where there are multiple users normally share
the printer. Even in a single user environment we request the OS to print many
files. When we give the command to print various files, those files are added to
the print queue. The file which reaches the front of the queue is printed.
+ Disk driver: In a computer network, disk driver maintains a queue of disk input
and output requests
• Scheduler: The operating system maintains a linear queue of processes in which
the various processes wait for CPU for execution.
Now, let us "Write a C program to implement multiple stacks using single array". The
multiple stacks can be implemented using a single array. This is implemented using
array of singly linked lists as shown below:
#include <stdio.h>
#include <stdlib.h>
/* Function to get a node: Include: example 9.2.! .3, page 110. 9.11 */
12.2 Q Question papers
void maim)
{
int item, choice, n,i;
NODE a[5] = {NULL};
for (;;)
{
printf(" 1: Insert 2:Delete\n");
printf("3: Display 4:Exit\n");
printf("Enter the choicexn");
scanf("%d",& choice);
switch (choice)
{
case 1:
printf("Enter stack number and item: \n");
scanf("%d%d",&i, &item);
a[i] = insert_front(item, ajij);
break;
case 2:
printf("From which stack number to delete: ");
scanf("%d" ,&i);
printf("Stack %d : ",i);
a[i] = delete_front(a[iJ);
break;
case 3:
display(a, n);
break;
default:
exit(O);
}
}
}
All the nodes in a list whose information value is greater than or equal to krnin and
less than kmax can be obtained by modifying the function shown in page 9.42 as
shown below:
12.4 Q Question papers
while (1)
{
if (first = NULL) return NULL;
prey = NULL;
cur = first;
prey = cur;
cur = cur->link;
}
prov-e-link = cur-c-link;
free (cur);
continue;
}
}
Q Systematic Approach to Data Structures using C - 12.5
This function is obtained by modifying the function shown in page 9.49. Replacing
pos by pos-l, we can insert to the left of the immediate left of Klh node. The complete
function is shown below:
if (first == NULL II pos-l == 1) /* Insert the node for the first time */
{
return temp;
}
if (first = NULL)
{
printf'(t'lnvalid positionin");
return first;
}
if (pos-l = 0)
{
temp-c-link = first;
return temp;
}
if (count = pos-l )
{
prev->link = temp;
temp->link = cur;
return first;
}
printf("Invalid position\n");
return first;
}
Now, let us "Implement concatenation of two circular singly linked lists List 1 and
List 2. Use header nodes to implement the list" Suppose two circular lists are created
with header nodes identified by list I and list 2. The following function concatenates
two lists:
!* Obtain the address of the last node of the first 11::;! ,~.
curl = listl->link;
while (curl-c-link != listl)
{
curl = curl->link;
}
/'~ Obtain the address of the last node of the second lisi":
cur2 = list2->link;
while (cur2->link != list2) cur2 = cur2->link;
Q Systematic Approach to Data Structures using C ~ 12.7
Let us "Write another program that accepts a string as a command line parameter and
prints its length." The C program is shown below:
#include <stdio.h>
#include <string.h>
Note: Let us assume the file name corresponding to the above program is "length.e".
It is required to create an executable file and execute the program in the command
line as shown:
Output
C:\>length ComputerScience
String length of ComputerScience = 15
C:\>length InfoScience
String length of InfoScience = 11
This is similar to the display function shown 111 page 9.19 with the following
modifications: .
count = 0;
temp = first; 1* Holds address of the first node */
return count;
}
Now, let us "Write a C program to read the following information for n students in a
class: Student Name, regno, marks scored in three subjects. Pass the structure through
the function to update percentage by 10%" The percentage is obtained by dividing the
marks obtained by totalmarks and multliplying by 100 as shown below:
percentage = (float) tot_marks_obtained /300 * 100;
Since there are three subjects, we are dividing by 300. After calculating the
percentage, to update percentage of marks by 10% we use the relation:
percentage + percentage* 10/100;
#include <stdio.h>
#include <string.h>
I*Update percentage by 10 %) *;
updated ..J)ercentage[i] = percentage + percentage * 10/ 100;
}
}
void maint)
{
int n; t= Number of students =t
int 1; t= Used to access the student record *!
STUDENT a[lO]; /* Holds the details of 10 students */
float updated ..J)erccntage[l 0];
Now, let us see "What is the di fference between *a[l 0]. ('~3)[10]. (*a)( )?,.
Columns
° 2 3 4 5 6 7 8 9 10 II 12 13 14 15 16
a \0
a[O]?
a[I]?
b a I I I I \0 I
a[2]? C 0 m . r p 1 ul t T e T r T I sic I i I ern I c I e I \0 I
Memory representation
Consider (*a)( 101: In the declaration (*a)[lO], (*a) indicates that a is a pointer
variable. So, (*a) [10] indicates that a is a pointer to a group of contiguous 1-
dimesnional array elements as shown in figure. lIere, the value 10 indicates the
number of elements in each array.
a ?1 L-.._L....,.._c=.l____l_._:=J. _
I" array
a + I~I ......L.
__ LI ,-;:-;r_.LI __ ..L-_--t _
2nd array
a +2 1 I 1 L- ---.JL- _
~ 3ril array
.......... and so on
12.12 Q Question papers
Consider (*a) () : Here, (*a)( ) indicates that a is a pointer variable and can store the
address of a function. Here, a is a function pointer and the function do not have any
parameters. If we have the declaration
.'
it indicates that a is pointer variable and can store the address of a function which has
two parameters both of type char *. For details, refer to page 2.106
Now, let us see "What is the difference between int (*ptr) ( ) and int * ptr ( )7"
Consider int (*ptr) 0: Here, ptr is a pointer to a function which does not accept any
parameters and returning an integer value
Consider int ''<ptr 0: Here, ptr is the name of the function which does not accept any
parameters and the function returning an integer value.
12.11 Sum of +ve and +ve numbers using dynamic memory allocation
Now, let us "Develop a program to determine the sum or positive and negative
elements of an array using dynamic memory allocation" The complete program is
shown below:
#include <stdio.h>
#include <stdlib.h>
void maint)
{
int n', !* Number of elements to input From the keyboard *!
int *a; /* To hold starting address of 11 elements */
int i:, 1* Used as index to array *i
Now, let us "Write a C program to find the average of a set or values stored in a
singly linked list" The complete program is shown below:
n = sum = 0;
temp = first;
12.14 Q Question papers
while(temp != NULL)
{
sum += temp->info;
n++;
temp = temp->link;
}
Now, let us "Write a program 10 replace all occurrences of a given value by other
value". The complete function is shown below:
cur = first;
while (cur != NULL)
{
if (item = cur->info) cur->info = other_item;
cur = cur->link;
}
return first;
}
Q Systematic Approach to Data Structures using C - 12.15
Note: Answer any 5 full questions. All questions carry equal marks
1. (a) What is a structure? How does a structure differ from an array? How are structure
members assigned values and are accessed? Explain. Hence or otherwise develop a C
function to represent a complex number using structure and to add two complex numbers
ADS: Page 4.6: section 4.5, 4,4 (8 marks)
Page 4.20
_ Page 4.47
(b) Write a' c program to accept a string as a command line parameter and to print its
length, . (6 marks)
Ans:Page 12.8
2. (a) Define a stack. List the operations on stack and give C implementation of these
operations (10 marks)
Ans: Page 6.2, Page 6.11, Example 6.2.9 or Example 6.2.10
Page 6.57, Example 6.5.8
(b) Write an algorithm for evaluating a valid postfix expression. Trace the same on
AB+C-BA+C$- for given values A=I, B=2, C=3 (10 marks)
3. (a) What is recursion? Write a recursive function for computing nth term of a fibbonocci
sequence. Hence or otherwise give the trace of stack contents for n = 4
Ans: Page 7.1, Page 7.9 and 7.10 (10 marks)
(b) What is a circular queue? give the static implementation of the same and write the
CQINSERT routine (10 marks)
Ans:Page 8.21, Page 8.23
4, (a) What is a linked list? Compare static and dynamic implementation oflinked lists in C
Ans: Page 9.2, Page 9.70 (10 marks)
12.16 Q Question papers
(b) How can an ordinary queue is represented using a singly linked list? Give algorithms
for inserting as well as deleting elements into a singly linked list
Ans: Page 9.29 (10 marks)
5. (a) What are different methods to represent a binary tree and compare them? (10 marks)
Ans: Page 10.9, section 10.3
6. (a) Give an algorithm for constructing as binary search tree. while constructing
the tree take care that duplicate values are not added. Trace the algorithm on
8, 13, 10, 12,6,9,5,2. (10 marks)
Ans: Page 10.30, example 10.5.1.2
7. (a) Explain the shell sort procedure by using a suitable data set. (10 marks)
Ans: Page 11.26
(b) Wheat is Hashing? Explain any two techniques for resolving hash collisions.
(10 marks)
Note: Answer any 5 full questions. All questions carry equal marks
4. a) What are different types of linked lists? Write a C function to count number of
elements present in single linked list?· (10 marks)
Ans:Page 9.3, page 12.8
12.18 ,!;l Question papers
b) Write advantages of doubly lined list over singly lined list. Write C function
that will insert a given integer value into an ordered doubly linked list.
Ans: Page 9.116,Similar to Page 9.37 (10 marks)
b) Write C functions for following tree traversals: Inorder, Preorder and Post
order. Ans: Page 10.5, 10.6 (6 marks)
6. a) Explain binary tree sort with suitable example. Comment on its efficiency
Ans: Page 10.44, section 10.6.2 (10 marks)
7. a) What is hashing? Explain any two hashing methods for resolving hash
collision. (10 marks)
b) Write an algorithm for deleting a node from binary search tree considering all
possibilities. (10 marks)
Ans: Page 10.36
Note: Answer any 5 full questions. All questions carry equal marks
b) Write a C program to read the following information for n students in a class: Student
Name, regno, marks scored in three subjects. Pass the structure through the function to
update percentage by 10%(6 marks) (6 marks)
ADS: Page 12.9
b) Write a recursive C function to find the sum of all the elements in an array
with N integer values. (6 marks)
Ans:Page 7.23
6. a) Enlist the advantages and disadvantages of double linked list over singly linked
list. (4 marks)
Ans: Page 9.116
b) Write a program to insert a given value into an ordered doubly linked list into
its proper position (6 marks)
ADS:Page 9.37
Q Systematic Approach to Data Structures using C - 12.21
b) What do you mean by a threaded binary tree? Discuss the impact of such a
representation on the tree traversal procedure (8 marks)
Ans:Page 482 onwards, Page 491:Section 10.9.6
b) What are the disadvantages of not freeing the memory during dynamic
allocation? (4 marks)
Ans: Page 2.100
2. a) Discuss the various operations that could be used for string manipulations
Ans: Page 3.26 (8 marks)
3. a) Discuss the various exceptional conditions that should be handled while using
stacks. (8 marks)
Ans: Overflow, underflow. For details refer sections: 6.2.1, 6.2.2, 6.2.3 (Page
6.5, 6.8, 6.10)
b) Convert the following postfix expressions to its corresponding infix and prefix
expressions:
i) ABCDE/* - F/G++
i i) 234& & 93 / + 4 3 * 2 + 5 -
iii) abcdg/*++ (12 marks)
Hint: Scan from left to right till we get an operator and scan backwards till we get
two operands. Convert the two operands along with operator into infix or prefix
expression. Repeat the above step
}
void mairu)]
char s[80];
printf("Enter something in");
gets(s);
do_something(s);
(6 marks)
Ans: If input is VTU Belgaum the output is reverse of the string which is
muagleB UTV
Ans: Hint: Have 2 pointers, the first one pointing to the first node and the second
one pointing to the last node. Compare the info fields. If not same the string is not
12.24 Q Question papers
a palindrome. If equal, update the first pointer towards right and second pointer
towards left and repeat the process. Finally, if all characters match the string is a
palindrome. Write the program for this.
b) Write the results after traversing the following tree in in order, preorder and
postorder (6 marks)
Ans: Inorder: d g b a h e i c f
Postorder:g d b hie f c a
Preorder: a b d gee h i f
8. a) List the various searching techniques. Explain in detail the interpolation search
technique (10 marks)
Ans: 4 5 7
7 6 junk value
2) a. Implement i) copying one string to another ii) Reversing the given string
Without using sring library functions in C (12 marks
b. Write a C program to represent a complex number using structure and add two
complex numbers. (08 marks)
Ans: Page 4.47, section: 4.9,4.9.1
3) a. Define a stack and operations over stack. Implement reversing a string using stack
(array implementation) in C (12 marks)
Ans: Definition: page 6.2
reversing a string using stack: same as program given in question 2.a
4) a. Write a C program to implement multiple stacks using single array (12 marks)
Ans: 12.1
b. What is a linear queue? What are the applications of linear queue? Implement insert
and delete operations. (08 marks)
Definition: page 8.1, section 8.1
Applications:
Implementation: page 8.3, section 8.2.1.1
page 8.6, section 8.2.1.2
5. a. Given an ordered lined list whose first node is denoted by 'start' and node is
represented by 'key' as information and 'link' as link field. Write a C program to
implement deleting number of nodes (consecutive) whose 'key' values are greater than or
equal to 'KOlin' and less than Kmax. (12 marks)
Ans: Page 12.4
b. Write a C program to implement insertion to the immediate left of Kth node in the
list ( 08 marks)
Ans: Page 12.5
~ Systematic Approach to Data Structures using C - 12.27
6. :1. Write a C program to implement doubly linked list with following operations:
i) Create ii) Insert (10 marks)
ns: Page 9.100
b. Implement concatenation of two circular singly linked lists Li t 1 and List 2. Use
header nodes to implement the list (10 marks)
Ans: Page 12.6
b. What are the applications of binary tree? Implement binary search tree and check for
duplicate data (10 marks)
Ans: applications of binary tree (page no 10.44)
Program: page no. 10.31