Vous êtes sur la page 1sur 100

LIST OF PROGRAMS

DATA STRUCTURE LABORATORY

Sub Code : 06CSL37 IA Marks : 25

Hrs / Week : 03 Exam Hours : 03

Total Hrs : 42 Exam Marks : 50

1. Write a C Program to create a sequential file with atleast 5 records, each record
having the structure shown below:

Name Marks1 Marks2 Marks3

Non-zero
25 Positive Positive Positive
positive
Characters Integer Integer Integer
integer

Write necessary functions

a. To display all the records in the file.

b. To search for a specific record based on the USN. In case the record is not found,
suitable message should be displayed. Both the options in this case must be demonstrated.

2. Write and demonstrate the following C functions:

a. newStrCpy that does the same job as strcpy

b. newStrCat that does the same job as strcat without using any library functions.

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.

100
4. Write a C Program to construct a stack of integers and to perform the following operations
on it:

a. Push

b. Pop

c. Display

The program should print appropriate messages for stack overflow, stack underflow, and
stack empty.

5. Write a C Program to convert and print a given valid parenthesized infix arithmetic
expression to postfix expression. The expression consists of single character operands
and the binary operators + (plus), - (minus), * (multiply) and / (divide).

6. Write a C Program to evaluate a valid suffix/postfix expression using stack. Assume


that the suffix/postfix expression is read as a single line consisting of non-negative
single digit operands and binary arithmetic operators. The arithmetic operators are + (add),
- (subtract), * (multiply) and / (divide).

7. Write a C Program to simulate the working of a queue of integers using an array. Provide
the following operations:

a. Insert

b. Delete

c. Display

8. Write a C Program to simulate the working of a circular queue of integers using an array.
Provide the following operations:

a. Insert

b. Delete

c. Display

100
9. Write a C Program using dynamic variables and pointers, to construct a singly linked
list consisting of the following information in each node: student id (integer), student
name (character string) and semester (integer). The operations to be supported are:

a. The insertion operation

i. At the front of a list

ii. At the back of the list

iii. At any position in the list

b. Deleting a node based on student id. If the specified node is not present in the list an
error message should be displayed. Both the options should be demonstrated.

c. Searching a node based on student id and update the information content. If the
specified node is not present in the list an error message should be displayed. Both
situations should be displayed.

d. Displaying all the nodes in the list. (Note: Only one set of operations among a, b and c
with d may be asked in the examination).

10. Write a C Program using dynamic variables and pointers to construct a stack of
integers using singly linked list and to perform the following operations:

a. Push

b. Pop

c. Display

The program should print appropriate messages for stack overflow and stack empty.

11. Write a C program using dynamic variables and pointers to construct a queue of
integers using singly linked list and to perform the following operations:

a. Insert

b. Delete

c. Display

The program should print appropriate messages for queue full and queue empty.

12. Write a C Program to support the following operations on a doubly linked list where
each node consists of integers:
100
a. Create a doubly linked list by adding each node at the front.

b. Insert a new node to the left of the node whose key value is read as an input

c. Delete the node of a given data, if it is found, otherwise display appropriate message.

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

a. To construct a binary search tree of integers.

b. To traverse the tree using all the methods i.e., inorder, preorder and postorder.

c. To display the elements in the tree.

14. Write recursive C Programs for

a. Searching an element on a given list of integers using the Binary Search method.

b. Solving the Towers of Hanoi problem.

Note: In the examination each student picks one question from a lot of all 14
questions.

Introduction
100
In computer science, a data structure is a particular way of storing and organizing data in a
computer so that it can be used efficiently.

Different kinds of data structures are suited to different kinds of applications, and some are highly
specialized to certain tasks. For example, B-trees are particularly well-suited for implementation of
databases, while compiler implementations usually use hash tables to look up identifiers.

Data structures are used in almost every program or software system. Specific data structures are
essential ingredients of many efficient algorithms, and make possible the management of huge
amounts of data, such as large databases and internet indexing services. Some formal design methods
and programming languages emphasize data structures, rather than algorithms, as the key organizing
factor in software design.

Basic principles
Data structures are generally based on the ability of a computer to fetch and store data at any place in
its memory, specified by an address — a bit string that can be itself stored in memory and
manipulated by the program. Thus the record and array data structures are based on computing the
addresses of data items with arithmetic operations; while the linked data structures are based on
storing addresses of data items within the structure itself. Many data structures use both principles

Language support
Assembly languages and some low-level languages such as BCPL generally lack support for data
structures. Many high-level programming languages, on the other hand, have special syntax or other
built-in support for certain data structures, such as vectors (one-dimensional arrays) in the C
programming language, multi-dimensional arrays in Pascal, linked lists in Common Lisp, and hash
tables in Perl. Many languages also provide basic facilities such as references and the definition
record data types, that programmers can use to build arbitrarily complex structures.

Most programming languages feature some sort of library mechanism that allows data structure
implementations to be reused by different programs. Modern programming languages usually come
with standard libraries that implement the most common data structures. Examples are the C++
Standard Template Library, the Java Collections Framework, and Microsoft's .NET Framework.

Modern languages also generally support modular programming, the separation between the
interface of a library module and its implementation. Some provide opaque data types that allow
clients to hide implementation details from client programs. Object-oriented programming
languages, such as C++ and Java, use classes for this purpose.

Before We begin to the Data Structures lets see Some Basic Concepts.

1. INTRODUCTION TO STRUCTURES:
100
A data structure is a group of data elements grouped together under one name. These data elements, known as
members, can have different types and different lengths

The first thing we have to know is that a data structure creates a new type: Once a data structure is
declared, a new type with the identifier specified as structure_name is created and can be used in the
rest of the program as if it was any other type. For example:

struct product {
int weight;
float price;
};

product apple;
product banana, melon;

We have first declared a structure type called product with two members: weight and price, each of a
different fundamental type. We have then used this name of the structure type ( product) to declare
three objects of that type: apple, banana and melon as we would have done with any fundamental data
type.

Once declared, product has become a new valid type name like the fundamental ones int, char or short
and from that point on we are able to declare objects (variables) of this compound new type, like we
have done with apple, banana and melon.

Right at the end of the struct declaration, and before the ending semicolon, we can use the optional
field object_name to directly declare objects of the structure type. For example, we can also declare the
structure objects apple, banana and melon at the moment we define the data structure type this way:

struct product {
int weight;
float price;
} apple, banana, melon;
100
It is important to clearly differentiate between what is the structure type name, and what is an object
(variable) that has this structure type. We can instantiate many objects (i.e. variables, like apple,
banana and melon) from a single structure type (product).

Once we have declared our three objects of a determined structure type ( apple, banana and melon) we
can operate directly with their members. To do that we use a dot (.) inserted between the object name
and the member name. For example, we could operate with any of these elements as if they were
standard variables of their respective types:

apple.weight
apple.price
banana.weight
banana.price
melon.weight
melon.price

Each one of these has the data type corresponding to the member they refer to: apple.weight,
banana.weight and melon.weight are of type int, while apple.price, banana.price and melon.price are of type
float.

2. A BRIEF NOTE ON STRINGS:

A string is an array of characters. Strings must have a 0 or null character after the last character to
show where the string ends. The null character is not included in the string.

There are 2 ways of using strings. The first is with a character array and the second is with a string
pointer.A character array is declared in the same way as a normal array.

char ca[10];

You must set the value of each individual element of the array to the character you want and you
must make the last character a 0. Remember to use %s when printing the string.

char ca[10];
ca[0] = 'H';
ca[1] = 'e';
ca[2] = 'l';
100
ca[3] = 'l';
ca[4] = 'o';
ca[5] = 0;
printf("%s",ca);

String handling functions

The strings.h header file has some useful functions for working with strings. Here are some of the
functions you will use most often:

strcpy(destination,source)
You can't just use string1 = string2 in C. You have to use the strcpy function to copy one string to
another. strcat copies the source string to the destination string.

s1 = "abc";
s2 = "xyz";

strcpy(s1,s2); // s1 = "xyz"

strcat(destination,source)
Joins the destination and source strings and puts the joined string into the destination string.

s1 = "abc";
s2 = "xyz";

strcat(s1,s2); // s1 = "abcxyz"

3. NOTE ON Strtok function:

It's fairly common for programs to have a need to do some simple kinds of lexical analysis and
parsing, such as splitting a command string up into tokens. You can do this with the strtok function,
declared in the header file `string.h'.

Function: char * strtok (char *newstring, const char *delimiters)

A string can be split into tokens by making a series of calls to the function strtok.

The string to be split up is passed as the newstring argument on the first call only. The strtok function uses this
to set up some internal state information. Subsequent calls to get additional tokens from the same string are
indicated by passing a null pointer as the newstring argument. Calling strtok with another non-null newstring

100
argument reinitializes the state information. It is guaranteed that no other library function ever calls strtok
behind your back (which would mess up this internal state information).

The delimiters argument is a string that specifies a set of delimiters that may surround the token being
extracted. All the initial characters that are members of this set are discarded. The first character that is not a
member of this set of delimiters marks the beginning of the next token. The end of the token is found by
looking for the next character that is a member of the delimiter set. This character in the original string
newstring is overwritten by a null character, and the pointer to the beginning of the token in newstring is
returned.

On the next call to strtok, the searching begins at the next character beyond the one that marked the end of the
previous token. Note that the set of delimiters delimiters do not have to be the same on every call in a series of
calls to strtok.

If the end of the string newstring is reached, or if the remainder of string consists only of delimiter characters,
strtok returns a null pointer.

Warning: Since strtok alters the string it is parsing, you always copy the string to a temporary buffer before
parsing it with strtok. If you allow strtok to modify a string that came from another part of your program, you
are asking for trouble; that string may be part of a data structure that could be used for other purposes during
the parsing, when alteration by strtok makes the data structure temporarily inaccurate.

The string that you are operating on might even be a constant. Then when strtok tries to modify it, your
program will get a fatal signal for writing in read-only memory. See section Program Error Signals.

This is a special case of a general principle: if a part of a program does not have as its purpose the
modification of a certain data structure, then it is error-prone to modify the data structure temporarily.

The function strtok is not reentrant. See section Signal Handling and Nonreentrant Functions, for a discussion
of where and why reentrancy is important.

Here is a simple example showing the use of strtok.

#include <string.h>

#include <stddef.h>

100
...

char string[] = "words separated by spaces -- and, punctuation!";

const char delimiters[] = " .,;:!-";

char *token;

...

token = strtok (string, delimiters); /* token => "words" */

token = strtok (NULL, delimiters); /* token => "separated" */

token = strtok (NULL, delimiters); /* token => "by" */

token = strtok (NULL, delimiters); /* token => "spaces" */

token = strtok (NULL, delimiters); /* token => "and" */

token = strtok (NULL, delimiters); /* token => "punctuation" */

token = strtok (NULL, delimiters); /* token => NULL */

4. ABSTRACT IDEA OF A STACK:

The stack is a very common data structure used in programs. By data structure, we mean something
that is meant to hold data and provides certain operations on that data.

One way to describe how a stack data structure behaves is to look at a physical analogy, a stack of
books... Now, a minimal set of things that we might do with the stack are the following:

100

Place a book Take one off See if the stack NOT Empty, there are
on the top... the top... is empty... books on the stack!
We'll consider other, more complex operations to be inappropriate for our stack. For example,
pulling out the 3rd book from the top cannot be done directly because the stack might fall over.

How might you get the 3rd book from the top using only the simple operations?

Abstraction

Now, let's think about a stack in an abstract way. I.e., it doesn't hold any particular kind of thing (like
books) and we aren't restricting ourselves to any particular programming language or any particular
implementation of a stack.

Stacks hold objects, usually all of the same type. Most stacks support just the simple set of
operations we introduced above; and thus, the main property of a stack is that objects go on and
come off of the top of the stack.

Here are the minimal operations we'd need for an abstract stack (and their typical names):

o Push: Places an object on the top of the stack.


o Pop: Removes an object from the top of the stack and produces that object.
o IsEmpty: Reports whether the stack is empty or not.

Because we think of stacks in terms of the physical analogy, we usually draw them vertically (so the
top is really on top).

2. Order produced by a stack:

Stacks are linear data structures. This means that their contexts are stored in what looks like a line
(although vertically). This linear property, however, is not sufficient to discriminate a stack from
other linear data structures. For example, an array is a sort of linear data structure. However, you can
access any element in an array--not true for a stack, since you can only deal with the element at its
top.

100
One of the distinguishing characteristics of a stack, and the thing that makes it useful, is the order in
which elements come out of a stack. Let's see what order that is by looking at a stack of letters...

Suppose we have a stack that can hold letters, call it stack. What would a particular sequence of Push
and Pops do to this stack?

We begin with stack empty:

-----
stack

Now, let's perform Push(stack, A), giving:

-----
| A | <-- top
-----
stack

Again, another push operation, Push(stack, B), giving:

-----
| B | <-- top
-----
|A|
-----
stack

Now let's remove an item, letter = Pop(stack), giving:

----- -----
| A | <-- top |B|
----- -----
stack letter

And finally, one more addition, Push(stack, C), giving:


100
-----
| C | <-- top
-----
|A|
-----
stack

You'll notice that the stack enforces a certain order to the use of its contents, i.e., the Last thing In is
the First thing Out. Thus, we say that a stack enforces LIFO order.

Now we can see one of the uses of a stack...To reverse the order of a set of objects.

Implementing a stack with an array:

Let's think about how to implement this stack in the C programming language.

First, if we want to store letters, we can use type char. Next, since a stack usually holds a bunch of
items with the same type (e.g., char), we can use an array to hold the contents of the stack.

Now, consider how we'll use this array of characters, call it contents, to hold the contents of the stack.
At some point we'll have to decide how big this array is; keep in mind that a normal array has a fixed
size.

Let's choose the array to be of size 4 for now. So, an array getting A, then B, will look like:

-----------------
|A|B| | |
-----------------
0 1 2 3

contents

Is this array sufficient, or will we need to store more information concerning the stack?

Answer: We need to keep track of the top of the stack since not all of the array holds stack elements.

What type of thing will we use to keep track of the top of the stack?

Answer: One choice is to use an integer, top, which will hold the array index of the element at the
top of the stack.

100
Example:

Again suppose the stack has (A,B) in it already...

stack (made up of 'contents' and 'top')


----------------- -----
|A|B| | | |1|
----------------- -----
0 1 2 3 top
contents

Since B is at the top of the stack, the value top stores the index of B in the array (i.e., 1).

Now, suppose we push something on the stack, Push(stack, 'C'), giving:

stack (made up of 'contents' and 'top')


----------------- -----
|A|B|C| | |2|
----------------- -----
0 1 2 3 top
contents

(Note that both the contents and top part have to change.)

So, a sequence of pops produce the following effects:

letter = Pop(stack)

stack (made up of 'contents' and 'top')


----------------- ----- -----
|A|B| | | |1| |C|
----------------- ----- -----
0 1 2 3 top letter
contents

letter = Pop(stack)

100
stack (made up of 'contents' and 'top')
----------------- ----- -----
|A| | | | |0| |B|
----------------- ----- -----
0 1 2 3 top letter
contents

letter = Pop(stack)

stack (made up of 'contents' and 'top')


----------------- ----- -----
| | | | | | -1| |A|
----------------- ----- -----
0 1 2 3 top letter

contents

so that you can see what value top should have when it is empty, i.e., -1.

5. ALGEBRAIC EXPRESSIONS, AN INTRODUCTION:


An algebraic expression is a legal combination of operands and operators. Operand is the quantity (unit of
data) on which a mathematical operation is performed. Operand may be a variable like x, y, z or a constant
like 5, 4,0,9,1 etc. Operator is a symbol which signifies a mathematical or logical operation between the
operands. Example of familiar operators include +,-,*, /, ^ ( throughout the tutorial '^' will be referred to as
Exponential Operator ) etc.

Considering these definitions of operands and operators now we can write an example of expression as x+y*z.
Note the phrase "LEGAL combination" in the definition of an Algebraic Expression, in aforementioned
example of expression x+y*z, the operands x , y, z and the operators +,* form some legal combination. Take
another example +xyz*, in this expression operators and operands do not make any LEGAL combination; this
expression is not a valid algebraic expression.

An Algebraic Expression can be represented using three different notations:


INFIX: From our school times we have been familiar with the expressions in which operands surround the
operator, e.g. x+y, 6*3 etc this way of writing the Expressions is called infix notation.
100
PREFIX: Prefix notation also Known as Polish notation, is a symbolic logic invented by Polish
mathematician Jan Lukasiewicz in the 1920's. In the prefix notation, as the name only suggests, operator
comes before the operands, e.g. +xy, *+xyz etc.

POSTFIX: Postfix notation is also Known as Reverse Polish notation. They are different from the infix and
prefix notations in the sense that in the postfix notation, the operator comes after the operands, e.g. xy+,
xyz+* etc.

Now, the obvious question that comes in our mind is, Why use these weird looking PREFIX and POSTFIX
notations when we have a sweet and simple INFIX notation?
To our surprise INFIX notations are not as simple as they seem specially while evaluating them. To evaluate
an infix expression we need to consider Operators' Priority and Associativity.
For example, will expression 3+5*4 evaluate to 32 i.e. (3+5)*4 or to 23 i.e. 3+(5*4). To solve this problem
Precedence or Priority of the operators were defined. Operator precedence governs evaluation order. An
operator with higher precedence is applied before an operator with lower precedence.
Following figure shows operator Precedence in descending order.

Now, as we know the precedence of the operators, we can evaluate the expression 3+5*4 as 23. But wait, it
doesn't end here what about the expression 6/3*2? Will this expression evaluate to 4 i.e. (6/3)*2 or to 1 i.e. 6/
(3*2).As both * and the / have same priorities, to solve this conflict, we now need to use Associativity of the
operators. Operator Associativity governs the evaluation order of the operators of same priority. For an
operator with left-Associativity, evaluation is from left to right and for an operator with right-Associativity;
evaluation is from right to left.

/, +, - have left Associativity. So the expression will evaluate to 4 and not 1.

100
N.B: We use Associativity of the operators only to resolve conflict between operators of same priority.
Due to the above mentioned problem of considering operators' Priority and Associativity while evaluating an
expression using infix notation, we use prefix and postfix notations. Both prefix and postfix notations have an
advantage over infix that while evaluating an expression in prefix or postfix form we need not consider the
Priority and Associativity of the operators. E.g. x/y*z becomes */xyz in prefix and xy/z* in postfix. Both
prefix and postfix notations make Expression Evaluation a lot easier. (we will discuss this in detail, later in
this tutorial)

But it is not easy to remember and manually write expressions in prefix or postfix form e.g. which one of
following equations is easy to remember (x+y)/z*a (Infix) or xy+z/a* (Postfix)?

So, what is actually done is that the expression is scanned from the user in infix form; it is converted into
prefix or postfix form and then evaluated without considering the parenthesis and priority of the operators.

Now let us move on the programming part, How to convert an expression from one notation to another? Well
there are two ways to convert expression from one notation to another. First one uses Stack and second
method uses Expression trees.

As there are 3 notations namely prefix, infix and postfix , there are a total of 6 conversions that can be done
,i.e. infix -> prefix, infix -> postfix, prefix -> infix, prefix -> postfix, postfix -> prefix, postfix -> infix. For the
first 2 conversions we will be using stacks and for remaining 4 conversions we will be using Binary
Expression Trees.

To convert an expression from infix to prefix and postfix, we are going to use stacks. Those who do not know
what a stack is, here are a few words about it. A stack is a special type of data structure in which items are
removed in reverse order from which they are added. Stack follows Last In First Out (LIFO) pattern. Adding
an element to a stack is called PUSH and removing an item from a stack is called POP.

Converting Expression from Infix to Postfix using STACK


To convert an expression from infix to postfix, we are going to use a stack.

Algorithm
1) Examine the next element in the input.
2) If it is an operand, output it.
3) If it is opening parenthesis, push it on stack.
4) If it is an operator, then
i) If stack is empty, push operator on stack.
ii) If the top of the stack is opening parenthesis, push operator on stack.
100
iii) If it has higher priority than the top of stack, push operator on stack.
iv) Else pop the operator from the stack and output it, repeat step 4.
5) If it is a closing parenthesis, pop operators from the stack and output them until an opening parenthesis is
encountered. pop and discard the opening parenthesis.
6) If there is more input go to step 1
7) If there is no more input, unstack the remaining operators to output.

Example
Suppose we want to convert 2*3/(2-1)+5*(4-1) into Prefix form: Reversed Expression: )1-4(*5+)1-2(/3*2

Char Scanned Stack Contents(Top on right) Postfix Expression

2 Empty 2

* * 2

3 * 23

/ / 23*
100
( /( 23*

2 /( 23*2

- /(- 23*2

1 /(- 23*21

) / 23*21-

+ + 23*21-/

5 + 23*21-/5

* +* 23*21-/5

( +*( 23*21-/5

4 +*( 23*21-/54

- +*(- 23*21-/54

1 +*(- 23*21-/541

) +* 23*21-/541-

Empty 23*21-/541-*+

So, the Postfix Expression is 23*21-/541-*+


Refer program #1 for infix to postfix Conversion
Converting Expression from Infix to Prefix using STACK
It is a bit trickier algorithm, in this algorithm we first reverse the input expression so that a+b*c will become
c*b+a and then we do the conversion and then again the output string is reversed. Doing this has an advantage
that except for some minor modifications the algorithm for Infix->Prefix remains almost same as the one for
Infix->Postfix.
Algorithm
1) Reverse the input string.
2) Examine the next element in the input.
3) If it is operand, add it to output string.
4) If it is Closing parenthesis, push it on stack.
5) If it is an operator, then
i) If stack is empty, push operator on stack.
ii) If the top of stack is closing parenthesis, push operator on stack.
iii) If it has same or higher priority than the top of stack, push operator on stack.
iv) Else pop the operator from the stack and add it to output string, repeat step 5.
100
6) If it is a opening parenthesis, pop operators from stack and add them to output string until a closing
parenthesis is encountered. Pop and discard the closing parenthesis.
7) If there is more input go to step 2
8) If there is no more input, unstack the remaining operators and add them to output string.
9) Reverse the output string.

Example
Suppose we want to convert 2*3/(2-1)+5*(4-1) into Prefix form: Reversed Expression: )1-4(*5+)1-2(/3*2

Char Scanned Stack Contents(Top on right) Prefix Expression(right to left)

) )

1 ) 1

- )- 1

4 )- 14

( Empty 14-

* * 14-

5 * 14-5

+ + 14-5*

) +) 14-5*

100
1 +) 14-5*1

- +)- 14-5*1

2 +)- 14-5*12

( + 14-5*12-

/ +/ 14-5*12-

3 +/ 14-5*12-3

* +/* 14-5*12-3

2 +/* 14-5*12-32

Empty 14-5*12-32*/+

Reverse the output string : +/*23-21*5-41 So, the final Prefix Expression is +/*23-21*5-41
Refer program #1 For infix to prefix Conversion.

Remaining Conversions
All the remaining conversions can easily be done using a Binary Expressions Tree. In fact the above 2
conversions, viz Infix-> Prefix and Infix -> Postfix, can also be done using Binary Expression Trees but that is
a very tricky thing and stacks can be used to handle the conversions easily. Now we will move a step ahead
and define a Binary Expression Tree.

N.B. A binary expression tree does not contain parenthesis, the reason for this is that for evaluating an
expression using expression tree, structure of tree itself decides order of the operations.

When we run Pre-order traversal (visit root, left child and then right child) on the Binary Expression Tree we
100
get prefix notation of the expression, similarly an Post-order traversal (visit left child, right child and then
root) will yield postfix notation. What will we get from an in-order Traversal (visit left child, root and then
right child)? Well, for the expressions which do not contain parenthesis, in-order traversal will definitely give
infix notation of expression but expressions whose infix form requires parenthesis to override conventional
precedence of operators can not be retrieved by simple in-order traversal.

Doing the Conversions with Expression Trees


Prefix -> Infix
The following algorithm works for the expressions whose infix form does not require parenthesis to override
conventional precedence of operators. 1) Create the Expression Tree from the prefix expression
2) Run in-order traversal on the tree.

Prefix -> Postfix


1) Create the Expression Tree from the prefix expression
2) Run post-order traversal on the tree.

Well you see how easy it is! Most important point here is to create Expression tree from Prefix expression,
following algorithm does that for you:

Algorithm for creating Expression Tree from a Prefix Expression


1) Reverse the prefix expression
2) Examine the next element in the input.
3) If it is operand then
i) create a leaf node i.e. node having no child (node- >left_child=node->right_child=NULL)
ii) copy the operand in data part
iii) PUSH node's address on stack
4) If it is an operator, then
i) create a node
ii) copy the operator in data part
iii) POP address of node from stack and assign it to node->left_child
iv) POP address of node from stack and assign it to node->right_child
v) PUSH node's address on stack
5) If there is more input go to step 2
6) If there is no more input, POP the address from stack, which is the address of the ROOT node of
Expression Tree.
Refer program #2 For prefix to infix and postfix conversion.

Postfix -> Infix


100
The following algorithm works for the expressions whose infix form does not require parenthesis to override
conventional precedence of operators. 1) Create the Expression Tree from the postfix expression 2) Run in-
order traversal on the tree.

Postfix -> Prefix


1) Create the Expression Tree from the postfix expression 2) Run pre-order traversal on the tree.

Algorithm for creating Expression Tree from a Postfix Expression


1) Examine the next element in the input.
2) If it is operand then
i) create a leaf node i.e. node having no child (node->left_child=node->left_child=NULL)
ii) copy the operand in data part
iii) PUSH node's address on stack

3) If it is an operator, then
i) create a node
ii) copy the operator in data part
iii) POP address of node from stack and assign it to node->right_child
iv) POP address of node from stack and assign it to node->left_child
v) PUSH node's address on stack
4) If there is more input go to step 1
5) If there is no more input, POP the address from stack, which is the address of the ROOT node of
Expression Tree.
Refer program #2 For postfix to infix and prefix conversion.
Well, at last we are done with converting the expressions from one type to another. As a summary here is
what we have done so far : 1) Infix -> Prefix using stack
2) Infix -> Postfix using stack
3) Prefix -> Infix using Expression Trees
4) Prefix -> Postfix using Expression Trees
5) Postfix -> Infix using Expression Trees
6) Postfix -> Prefix using Expression Trees
Now all we are left with is Evaluating an Expression. Evaluating an expression involves two phases: 1) Create
an expression tree for given expression
2) Evaluate the tree recursively
We already know how to create an expression tree for prefix and postfix notations. Algorithm for creating
100
Expression Tree from Infix expression is left as reader exercise and some help can be found at
http://www.seas.gwu.edu/~csci131/fall96/exp_to_tree.html
Following procedure will evaluate an expression tree in a recursive fashion:

Procedure Name: Evaluate Tree Arguments : Address of root of the tree int Evaluate Tree (struct node*
root)

IF root != NULL
IF current node contains an operator
x = Evaluate Tree(root -> left_child)
y = Evaluate Tree(root -> right_child)
Perform operation on x and y, specified by the
operator and store result in z
Return z
else Return root->data
Refer program #2 for evaluation of an Expression Tree.

6. POSTFIX NOTATION:

Postfix notation is a way of writing algebraic expressions without the use of parentheses or rules of
operator precedence. The expression above would be written as AB+CD-/ in postfix notation. (Don't
panic! We'll explain this in a moment.) Postfix notation had its beginnings in the work of Jan
Łukasiewicz* (1878-1956), a Polish logician, mathematician, and philosopher. Łukasiewicz
developed a parenthesis-free prefix notation that came to be called Polish notation and a postfix
notation now called Reverse Polish Notation or RPN. From these ideas, Charles Hamblin developed
a postfix notation for use in computers. Łukasiewicz's work dates from about 1920. Hamblin's work
on postfix notation was in the mid-1950's. Calculators, notably those from Hewlett-Packard, used
various postfix formats beginning in the 1960s.

In this mini-lecture we will use variables represented by single letters and operators represented by
single characters. Things are more complicated in real life. Variables have names composed of many
characters and some operators, like ** for exponentiation, require more than a single character.
However, we can make these simplifications without loss of generality.

In our simplified examples, each variable is represented by a single letter and each operator by a
single character. These are called tokens. In a more complex implementation, your program would
still scan a series of tokens, but they would likely be pointers into symbol tables, constant pools, and
procedure calls for operators.
100
Evaluating Postfix Notation

Evaluating an expression in postfix notation is trivially easy if you use a stack. The postfix
expression to be evaluated is scanned from left to right. Variables or constants are pushed onto the
stack. When an operator is encountered, the indicated action is performed using the top elements of
the stack, and the result replaces the operands on the stack.

Let's do an example using the postfix expression AB+CD-/. In order to see what's going on as the
expression is evaluated, we will substitute numbers for the variables. The expression to be evaluated
becomes 9 7 + 5 3 - /. This is equivalent to (9 + 7) / (5 - 3) so we expect the answer to be eight. Click the
"Go" button at the right to see an animation showing the evaluation of the expression.

You will see the expression scanned from left to right. A yellow highlight shows the term currently
being scanned. The digits in the expression are pushed onto the stack and the operators are
performed.

Experiment with the animation until you are sure you understand what's going on.

Now that you see how easy it is to evaluate an expression that's in RPN form, you will want to
convert ordinary infix expressions to RPN so that you can evaluate them. That turns out to be easy
too, if you use a stack.

7. QUEUES:

Overview of Queues

A queue is a first-in first-out data structure (FIFO). Conceptually, the queue data structure behaves
like a line. New data is placed at the rear of the queue and when data is removed it is taken from the
front. A printer maintains a list of jobs in a queue; the oldest job, the one that has been in the queue
the longest, is serviced first.

The operations supported by a queue are as follows:

• void enqueue(Object o) - place object o on the rear of the queue


• Object dequeue() - remove and return the object at the front of the queue
• Object front() - return the object at the front of the queue without removing it
• int size() - return the size of the queue

100
• boolean isEmpty() - return true if the queue contains no elements, false otherwise

Queues are typically depicted as follows:

The previous queue would be the result of the following operations:

enqueue(1)

enqueue(2)

enqueue(3)

A dequeue on the queue above would return the object 1 and the resulting queue would look as
follows:

A front on the queue above would return the object 2 and the queue itself would remain unchanged.

Implementation

A queue can be implemented using an array or a linked list. In essense, the queue operations simply
limit the way in which we access data stored in a data structure such as an array. A queue can also be

100
implemented using two stacks and a stack using two queues. Though these implementations are not
terribly efficient, it is a good exercise to consider how you would build such an implementation.

When choosing which rudimentary data structure to use for an implementation, it is imperative that
you consider efficiency. How many steps will enqueue and dequeue require?

To implement a queue, you will need to implement a Queue class that provides the queue operations
described above. You will also want to provide appropriate error reporting. Therefore, it might make
sense to have the dequeue operation throw a QueueEmptyException in the event that the programmer
tries to dequeue from a queue with no elements.

Abstract idea of a queue:

The queue is another data structure. A physical analogy for a queue is a line at a bank. When you go to the
bank, customers go to the rear (end) of the line and customers come off of the line (i.e., are serviced) from the
front of the line.

Aside: In fact, other English-speaking countries use this term for a line, e.g., they might say "Queue up!"
rather than "Get in a line!"

Like a stack, a queue usually holds things of the same type. We usually draw queues horizontally. Here's a
queue of characters with 3 elements:

queue

-------------

|a|b|c|

-------------

^ ^

| |

front rear

The main property of a queue is that objects go on the rear and come off of the front of the queue.

Here are the minimal set of operations we'd need for an abstract queue:

o Enter (or Insert)


100
Places an object at the rear of the queue.

o Delete (or Remove)

Removes an object from the front of the queue and produces that object.

o IsEmpty

Reports whether the queue is empty or not.

2. Order produced by a queue:

Queues are useful because they produce a certain order in which the contents of the queue are used. Let's
see what order that is by looking at a queue of characters. Now, what would a particular sequence of Enter
and Deletes do to this queue:

queue

-------------

|a|b|c|

-------------

^ ^

| |

front rear

Now, Enter(queue, 'd')...

queue

-----------------

|a|b|c|d|

-----------------

^ ^

| |

front rear

Now, ch = Delete(queue)...

queue ch
100
------------- -----

|b|c|d| |a|

------------- -----

^ ^

| |

front rear

You'll notice that the queue enforces a certain order to the use of its contents. Is the ordering of the queue
Last thing In is the First thing Out (LIFO) or First Thing In is the First thing Out (FIFO)?

Answer: Queues produce FIFO order. Remember that stacks produce LIFO order.

3. Implementing a queue with an array:

Since a queue usually holds a bunch of items with the same type, we could implement a queue with an
array. Let's simplify our array implementation of a queue by using an array of a fixed size,
MAX_QUEUE_SIZE.

What other pieces of data would you need (besides an array) to implement a queue in this way?

One of the things we'll need to keep track of is the number of elements in the queue, i.e., not all the
elements in the array may be holding elements of the queue at any given time.

So far, the pieces of data we need for our array implementation of the queue are:

an array

a count

Will these pieces be sufficient? Let's look at an example to find out...We'll start with a queue with 3
elements:

queue (made up of 'contents' and 'count')

----------------- -----

|a|b|c| | |3|

----------------- -----
100
0 1 2 3 count

contents

where a is at the front and c is at the rear. Now, we enter a new element with: Enter(queue, 'd')...

queue (made up of 'contents' and 'count')

----------------- -----

|a|b|c|d| |4|

----------------- -----

0 1 2 3 count

contents

All seems well. How about if we remove an element with: ch = Delete(queue)?...

queue (made up of 'contents' and 'count')

----------------- ----- -----

| |b|c|d| |3| |a|

----------------- ----- -----

0 1 2 3 count ch

contents

Hmmm, we have a problem because the front of the queue is no longer at array position 0. One solution would
be to move all the elements down one, giving:

queue (made up of 'contents' and 'count')

----------------- -----

|b|c|d| | |3|

----------------- -----

0 1 2 3 count

contents

We reject that solution though because it is too expensive to move everything down every time we remove an
element.
100
Instead, can we use an additional piece of information to keep track of the front?

Answer: Yes! We can use the index of the element at the front, giving:

queue (made up of 'contents', 'front' and 'count')

----------------- ----- -----

| |b|c|d| |1| |3|

----------------- ----- -----

0 1 2 3 front count

Contents

Now, what if we enter another element with: Enter(queue, 'e')? Currently, the rear of the queue holds 'd' and is
at the end of the array. Where will we put 'e'?

9. A CIRCULAR QUEUE:

A circular queue is a particular implementation of a queue. It is very efficient. It is also quite useful
in low level code, because insertion and deletion are totally independant, which means that you don't
have to worry about an interrupt handler trying to do an insertion at the same time as your main code
is doing a deletion.

How does it work?

A circular queue consists of an array that contains the items in the queue, two array indexes and an optional
length. The indexes are called the head and tail pointers and are labelled H and T on the diagram.

The head pointer points to the first element in the queue, and the tail pointer points just beyond the last
element in the queue. If the tail pointer is before the head pointer, the queue wraps around the end of the array.

Is the queue empty or full?


100
There is a problem with this: Both an empty queue and a full queue would be indicated by having the head
and tail point to the same element. There are two ways around this: either maintain a variable with the number
of items in the queue, or create the array with one more element that you will actually need so that the queue
is never full.

Operations

Insertion and deletion are very simple. To insert, write the element to the tail index and increment the tail,
wrapping if necessary. To delete, save the head element and increment the head, wrapping if necessary. Don't
use a modulus operator for wrapping (mod in Pascal, % in C) as this is very slow. Either use an if statement or
(even better) make the size of the array a power of two and simulate the mod with a binary and (& in C).

How does it work?

It works very much like a circular queue, except that it doesn't "wrap around". Instead, you make the array
much bigger than the maximum number of items in the queue. When you run out of room at the end of the
array, you move the entire contents of the queue back to the front of the array (this is the "bulk move" from
which the structure gets its name).

Operations

Insertion and deletion are very simple. To insert, write the element to the tail index and increment the tail. To
delete, save the head element and increment the head. If after an insertion the tail points beyond the end of the
array, then do the following:

1. Copy the contents of the queue to the front of the array (I recommend the move procedure in
Pascal or the memcpy function in C, rather than doing it manually).
2. Set the head pointer to the new head of the queue and the new tail pointer to the new tail of
the queue.

Note if the queue occupies more than half the array, then you must be careful that you don't overwrite the
frontmost queue elements before they are copied because the old and new queue will overlap (and memcpy
doesn't allow overlap - use memmove in this case).

100
9. SINGLY-LINKED LISTS:
The singly-linked list is the most basic of all the linked data structures. A singly-linked list is simply a
sequence of dynamically allocated objects, each of which refers to its successor in the list. Despite this
obvious simplicity, there are myriad implementation variations. Figure shows several of the most common
singly-linked list variants.

Figure: Singly-linked list variations.

The basic singly-linked list is shown in Figure (a). Each element of the list refers to its successor and
the last element contains the null reference. One variable, labeled head in Figure (a), is used to keep
track of the list.

The basic singly-linked list is inefficient in those cases when we wish to add elements to both ends of
the list. While it is easy to add elements at the head of the list, to add elements at the other end (the
tail ) we need to locate the last element. If the basic basic singly-linked list is used, the entire list
needs to be traversed in order to find its tail.

Figure (b) shows a way in which to make adding elements to the tail of a list more efficient. The
solution uses a second variable, tail , which refers to the last element of the list. Of course, this time
efficiency comes at the cost of the additional space used to store the variable tail.

The singly-linked list labeled (c) in Figure illustrates two common programming tricks. There is
an extra element at the head of the list called a sentinel . This element is never used to hold data and
it is always present. The principal advantage of using a sentinel is that it simplifies the programming
100
of certain operations. For example, since there is always a sentinel standing guard, we never need to
modify the head variable. Of course, the disadvantage of a sentinel such as that shown in (c) is that
extra space is required, and the sentinel needs to be created when the list is initialized.

The list (c) is also a circular list . Instead of using a null reference to demarcate the end of the list, the
last element of the list refers to the sentinel. The advantage of this programming trick is that insertion
at the head of the list, insertion at the tail of the list, and insertion at an arbitrary position of the list
are all identical operations.

Of course, it is also possible to make a circular, singly-linked list that does not use a sentinel.
Figure (d) shows a variation in which a single variable is used to keep track of the list, but this time
the variable, tail, refers to the last element of the list. Since the list is circular in this case, the first
element follows the last element of the list. Therefore, it is relatively simple to insert both at the head
and at the tail of this list. This variation minimizes the storage required, at the expense of a little
extra time for certain operations.

Figure illustrates how the empty list (i.e., the list containing no list elements) is represented for each
of the variations given in Figure. Notice that the sentinel is always present in list variant (c). On the
other hand, in the list variants which do not use a sentinel, the null reference is used to indicate the
empty list.

Figure: Empty singly-linked lists.

100
In the following sections, we will present the implementation details of a generic singly-linked list.
We have chosen to present variation (b)--the one which uses a head and a tail--since is supports
append and prepend operations efficiently.

10. IMPLEMENTING A STACK WITH A LINKED LIST:

Using a linked list is one way to implement a stack so that it can handle essentially any number of elements.

Here is what a linked list implementing a stack with 3 elements might look like:

list

-------- -------- ---------

| C | -+-->| B | -+-->| A | 0 |

-------- -------- ---------

Where should we consider the top of the stack to be, the beginning or end of the list, and why?

How will we represent an empty stack?

C implementation:

Now, think about how to actually implement this stack of characters in C.

It is usually convenient to put a data structure in its own module, thus, we'll want to create files stack.h and a
stack.c.

The operations needed for our stack are mainly determined by the operations provided by an abstract stack,
namely:

StackPush()

StackPop()

StackIsEmpty()
100
We may need to add a few other operations to help implement a stack. These will become apparent as we start
to implement.

11. QUEUE - LINKED-LIST IMPLEMENTATION – TYPES:

Here, besides discussing the queue data structure, we will demonstrate how to better hide the details
of a data structure using ADTs/CDTs and generic type definitions.

1. Abstract idea of a queue:

The queue is another data structure. A physical analogy for a queue is a line at a bank. When
you go to the bank, customers go to the rear (end) of the line and customers come off of the
line (i.e., are serviced) from the front of the line.

Aside: In fact, other English-speaking countries use this term for a line, e.g., they might say "Queue
up!" rather than "Get in a line!"

Like a stack, a queue usually holds things of the same type. We usually draw queues
horizontally. Here's a queue of characters with 3 elements:

queue
-------------
|a|b|c|
-------------
^ ^
| |
front rear

The main property of a queue is that objects go on the rear and come off of the front of the
queue.
100
Here are the minimal set of operations we'd need for an abstract queue:

o Enter (or Insert)

Places an object at the rear of the queue.

o Delete (or Remove)

Removes an object from the front of the queue and produces that object.

o IsEmpty

Reports whether the queue is empty or not.

2. Order produced by a queue:

Queues are useful because they produce a certain order in which the contents of the queue are
used. Let's see what order that is by looking at a queue of characters. Now, what would a
particular sequence of Enter and Deletes do to this queue:

queue
-------------
|a|b|c|
-------------
^ ^
| |
front rear

Now, Enter(queue, 'd')...

queue
-----------------
100
|a|b|c|d|
-----------------
^ ^
| |
front rear

Now, ch = Delete(queue)...

queue ch
------------- -----
|b|c|d| |a|
------------- -----
^ ^
| |
front rear

You'll notice that the queue enforces a certain order to the use of its contents. Is the ordering of
the queue Last thing In is the First thing Out (LIFO) or First Thing In is the First thing Out
(FIFO)?

Answer: Queues produce FIFO order. Remember that stacks produce LIFO order.

3. Implementing a queue:

Since a queue usually holds a bunch of items with the same type, we could implement a queue
with an array. However, here we'll use a linked list implementation.

Remember that a linked list can:

o hold as many objects as memory can hold (i.e., it's not a fixed size).
o grow easily without having to copy over the old contents.

Note: Copying over is the problem incurred with a dynamic array that has to be made bigger.

Recall that linked lists are made up of things called nodes:

A Node A node contains 2 main parts:

100
-----------------
o some data and,
| | |
| Data | Link -+--> o a link to the node after it (for a singly-linked list).
| | |
-----------------

In C, we know that these links will be pointers. Here is an example singly-linked list that
holds characters.

list
|
v

--------- --------- ---------


| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------

First, we must decide how the contents of this linked list corresponds to the structure of the
queue.

A logical choice would be to make the beginning of the linked list the front of the queue and
the end of the linked list the rear of the queue.

Given this representation, we'll rename the link to the beginning of the list to front:

front
|
v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------

Will this representation be sufficient for adding and removing things from the queue?

Answer: Yes, however, we see one inefficiency in that, unlike the stack, things aren't added
and removed from the same end. To add something to a queue, that thing must go on the
rear. Currently, the only way to get to the end is to traverse to the end of the list. We can be
more efficient if we keep track of the rear of the queue with a direct link.
100
Our augmented representation looks as follows:

front rear-----------------+
| |
v v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------

Empty Queue

Last, we'll need a representation of an empty queue. We'll make at least the front, but also maybe rear, link
to nothing when the queue is empty. In C, this means they will contain NULL pointers.

12. DOUBLY-LINKED LIST:

A more sophisticated kind of linked list is a doubly-linked list or two-way linked list. Each node
has two links: one points to the previous node, or points to a null value or empty list if it is the first
node; and one points to the next, or points to a null value or empty list if it is the final node.

A doubly-linked list containing three integer values: the value, the link forward to the next node, and the link
backward to the previous node

In some very low level languages, XOR-linking offers a way to implement doubly-linked lists using
a single word for both links, although the use of this technique is usually discouraged.

100
Doubly-linked lists

With doubly-linked lists there are even more pointers to update, but also less information is needed,
since we can use backwards pointers to observe preceding elements in the list. This enables new
operations, and eliminates special-case functions. We will add a prev field to our nodes, pointing to
the previous element, and a lastNode field to our list structure which always points to the last node in
the list. Both list.firstNode and list.lastNode are null for an empty list.

record Node {
data // The data being stored in the node
next // A reference to the next node; null for last node
prev // A reference to the previous node; null for first node
}
record List {
Node firstNode // points to first node of list; null for empty list
Node lastNode // points to last node of list; null for empty list
}

Iterating through a doubly linked list can be done in either direction. In fact, direction can change
many times, if desired.

Forwards

node := list.firstNode
while node ≠ null
<do something with node.data>
node := node.next

Backwards

node := list.lastNode
while node ≠ null
<do something with node.data>
node := node.prev

100
These symmetric functions add a node either after or before a given node, with the diagram
demonstrating after:

• If h = height of a binary tree,

max # of leaves = 2h

max # of nodes = 2h + 1 - 1

• A binary tree with height h and 2h + 1 - 1 nodes (or 2h leaves) is called a full binary tree

Figure 13.2: Level by level numbering of a binary tree

100
Figure 13.1 shows several examples of binary trees.

• The nodes of a binary tree can be numbered in a natural way, level by level, left to right. For example,
see Figure 13.2.
• A binary tree with n nodes is said to be complete if it contains all the first n nodes of the above
numbering scheme. Figure 13.3 shows examples of complete and incomplete binary trees.

• Total number of binary trees having n nodes

= number of stack-realizable permutations of length n

= number of well-formed parentheses (with n left parentheses and n right parentheses)

Fig:13.3

100
Binary Tree Traversal:

1. Preorder

Visit root, visit left subtree in preorder, visit right subtree in preorder

2. Postorder

Visit left subtree in postorder, right subtree in postorder, then the root

3. Inorder

Visit left subtree in inorder, then the root, then the right subtree in inorder

• Figure 13.4 shows several binary trees and the traversal sequences in preorder, inorder, and
postorder.
• We can construct a binary tree uniquely from preorder and inorder or postorder and inorder
but not preorder and postorder.

100
Figure 13.4.: Binary tree traversals

14.a A BINARY SEARCH ALGORITHM:

A binary search algorithm (or binary chop) is a technique for locating a particular value in a
sorted list of values. It makes progressively better guesses, and closes in on the sought value by
selecting the middle element in the span (which, because the list is in sorted order, is the median
value), comparing its value to the target value, and determining if the selected value is greater than,
less than, or equal to the target value. A guess that turns out to be too high becomes the new upper
bound of the span, and a guess that is too low becomes the new lower bound. Pursuing this strategy
iteratively, it narrows the search by a factor of two each time, and finds the target value or else

100
determines that it is not in the list at all. A binary search is an example of a dichotomic divide and
conquer search algorithm.

The most common application of binary search is to find a specific value in a sorted list. To cast this
in the frame of the guessing game (see Example below), realize that we now guess the index, or
numbered place, of the value in the list. This is useful because, given the index, other data structures
will contain associated information. Suppose a data structure containing the classic collection of
name, address, telephone number and so forth has been accumulated, and an array is prepared
containing the names, numbered from one to N. A query might be: what is the telephone number for
a given name X. To answer this the array would be searched and the index (if any) corresponding to
that name determined, whereupon it would be used to report the associated telephone number and so
forth. Appropriate provision must be made for the name not being in the list (typically by returning
an index value of zero), indeed the question of interest might be only whether X is in the list or not.

If the list of names is in sorted order, a binary search will find a given name with far fewer probes
than the simple procedure of probing each name in the list, one after the other in a linear search, and
the procedure is much simpler than organizing a hash table though once created, searching with a
hash table may well be faster, typically averaging just over one probe per lookup. With a non-
uniform distribution of values, if it is known that some few items are much more likely to be sought
for than the majority, then a linear search with the list ordered so that the most popular items are first
may do better than binary search. However, the choice of the best method may not be immediately
obvious. If, between searches, items in the list are modified or items are added or removed,
maintaining the required organisation may consume more time than the searches.

The binary search begins by comparing the sought value X to the value in the middle of the list;
because the values are sorted, it is clear whether the sought value would belong before or after that
middle value, and the search then continues through the correct half in the same way. Only the sign
of the difference is inspected: there is no attempt at an interpolation search based on the size of the
differences.

100
The method

In order to discuss the method in detail, a more formal description is necessary. The basic idea is that
there is a data structure represented by array A in which individual elements are identified as A(1),
A(2),...,A(N) and may be accessed in any order. The data structure contains a sub-element or data
field called here Key, and the array is ordered so that the successive values A(1).Key ≤ A(2).Key and
so on. Such a key might be a name, or it might be a number. The requirement is that given some
value x, find an (not necessarily the one and only) index p such that A(p).Key = x.
100
To begin with, the span to be searched is the full supplied list of elements, as marked by variables L
and R, and their values are changed with each iteration of the search process, as depicted by the
flowchart. Note that the division by two is integer division, with any remainder lost, so that 3/2
comes out as 1, not 1½. The search finishes either because the value has been found, or else, the
specified value is not in the list.

That it works

The method relies on and upholds the notion If x is to be found, it will be amongst elements (L + 1)
to (R - 1) of the array.

The initialisation of L and R to 0 and N - 1 make this merely a restatement of the supplied problem,
that elements 1 to N are to be searched, so the notion is established to begin with. Consider now that
some element p of the indicated range is selected. At this point, it does not matter which specific
element is selected, merely that one is. Compare x to A(p).Key. If x = A(p).Key then the method
terminates in success. Otherwise, suppose x < A(p).Key. If so, then because the array is in sorted
order, x will also be less than all later elements of the array, all the way to element (R - 1) as well.
Accordingly, the value of the right-hand bound index R can be changed to be the value p, since, by
the test just made, x < A(p).Key and so, if x is to be found, it will be amongst elements earlier than p,
that is (p - 1) and earlier. And contrariwise, for the case x > A(p).Key, the value of L would be
changed. Further, whichever bound is changed, the span remaining to be searched is reduced. If L is
changed, it is changed to a higher number (at least L + 1), whereas if R is changed, it is to a lower
number (at most R - 1) because those are the limits for p.

It remains to consider that a valid value for p is always chosen, which in turn depends on whether
there are any elements remaining in the search span (L + 1) to (R - 1). The number of such elements
remaining is clearly (R - L - 1) so computing (R - L) gives (number of elements + 1); halving that
number (with integer division) means that if one element remained, then p = 1, but if no elements
remain, p = 0, and in that case the method terminates with the report "Not found". Otherwise, for p
> 0, the search continues with p:=L + p, which by construction is within the bounds (L + 1) to (R -
1).

Accordingly, with each iteration, if the search span is empty the result is "Not found", otherwise
either x is found at the probe point p or the search span is reduced for the next iteration. Thus the
method works, and so can be called an Algorithm.

100
That it is fast

The interval being searched is successively halved (actually slightly better than halved) in width with
each iteration, so the number of iterations required is at most the base two logarithm of N. - Zero for
empty lists. More precisely, each probe both removes one element from further consideration and
selects one or the other half of the interval for further searching.

Suppose that the number of items in the list is odd. Then there is a definite middle element at p = (N
+ 1)/2 - this is proper division without discarding remainders. If that element's key does not match x,
then the search proceeds either with the first half, elements 1 to p - 1 or the second, elements p + 1 to
N. These two spans are equal in extent, having (N - 1)/2 elements. Conversely, suppose that the
number of elements is even. Then the probe element will be p = N/2 and again, if there is no match
one or the other of the subintervals will be chosen. They are not equal in size; the first has N/2 - 1
elements and the second (elements p + 1 to N as before) has N/2 elements.

Now supposing that it is just as likely that N will be even as odd in general, on average an interval
with N elements in it will become an interval with (N - 1)/2 elements.

Working in the other direction, what might be the maximum number of elements that could be
searched in p probes? Clearly, one probe can check a list with one element only (to report a match,
or, "not found") and two probes can check a list of three elements. This is not very impressive
because a linear search would only require three probes for that. But now the difference increases
exponentially. With three probes, seven elements can be checked, four for fifteen, and so forth. In
short, to search N elements requires at most p probes where p is the smallest integer such that 2p > N
Taking the binary logarithm of both sides, p > lb(N)

Or, lb(N) (with any fractional part rounded up to the next integer), is the maximum number of probes
required to search N elements.

14.b THE TOWER OF HANOII PROBLEM:

The History:
The puzzle is called "Towers of Hanoi" because an early popular presentation wove a fanciful legend around
it. According to this myth (uttered long before the Vietnam War), there is a Buddhist monastery at Hanoi
which contains a large room with three time-worn posts in it surrounded by 21 golden discs. Monks, acting
out the command of an ancient prophecy, have been moving these disks, in accordance with the rules of the
puzzle, once every day since the monastery was founded over a thousand years ago.

100
They are said to believe that when the last move of the puzzle is completed, the world will end in a clap of
thunder. Fortunately, they are nowhere even close to being done.

The Rules:

• There are n disks (1, 2, 3,..., n) and three towers (pegs). The towers are labeled 'A', 'B', and
'C'.
• All the disks are initially placed on the first peg (the 'A' peg).
• No disk may be placed on top of a smaller disk.
• You may only move one disk at a time and this disk must be the top disk on a peg.

100
Program 1.

Write a c program to create a sequential file with atleast 5 records. Write


necessary functions
a. To display all the records in the file.
b. To search for a specific record based on the USN. In case the record is not
found, suitable message should be displayed. Both options in this case must be
demonstrated.

#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<string.h>

typedef struct
{
int usn;
char name[25];
int marks1;
int marks2;
int marks3;
}STUDENT;

int search_record(int key_usn,FILE *fp)


{
STUDENT st;
for(;;)
{
fscanf(fp,"%d %s %d %d %d",&st.usn, st.name, &st.marks1,
&st.marks2, &st.marks3);
if (feof(fp))break;
if (key_usn==st.usn)return 1;
}
return 0;
}

100
void display_records(FILE *fp,int pass)
{
STUDENT st;
printf("USN\tName\t\t\t Marks1\tMarks2\tMarks3\n");
for(;;)
{
fscanf(fp,"%d %s %d %d %d" ,&st.usn, st.name, &st.marks1, &st.marks2, &st.marks3);
if(feof(fp))break;
if(pass==st.usn)
{
cprintf("%-5d %-10s",st.usn,st.name);
printf("\t\t");
cprintf(" %-5d %-5d %-5d",st.marks1,st.marks2,st.marks3);
printf("\n");
}
else
printf("%-5d %-10s\t\t %-5d %-5d %5d\n", st.usn, st.name, st.marks1, st.marks2,
st.marks3);
}
}

void append_record(FILE *fp)


{
STUDENT st;
printf("USN : ");
scanf("%d",&st.usn);
printf("Name : ");
scanf("%s",st.name);
printf("Marks1 : ");
scanf("%d",&st.marks1);
printf("Marks2 : ");
scanf("%d",&st.marks2);
printf("Marks3 : ");
scanf("%d",&st.marks3);
fprintf(fp,"%d %s %d %d %d\n",st.usn,st.name,st.marks1,st.marks2,st.marks3);
}

100
void main()
{
STUDENT st;
char fname[12];
FILE *fp;
int key_usn;
int key = 0;
int flag;
int(choice);
clrscr();
printf("Enter the file name : ");
scanf("%s",fname);
for(;;)
{
printf("\n\n1.Insert Record\n2.Search Record\n");
printf("3.Display Record\n4.Quit\n\n\n");
printf("Enter the choice : ");
scanf("%d",&choice);

switch(choice)
{
case 1:
fp = fopen(fname,"a+");
if(fp==NULL)
{
printf("File Open Failed");
break;
}
append_record(fp);
fclose(fp);
break;
case 2:
fp = fopen(fname,"r");
if(fp==NULL)
{
printf("File Open Failed");
break;
}

100
printf("Enter USN : ");
scanf("%d",&key_usn);
flag = search_record(key_usn,fp);
if (flag==0)
printf("Unsuccessful search\n");
else
{
printf("Successful search\n");
fp = fopen(fname,"r");
if(fp==NULL)
{
printf("File Open Failed");
break;
}
display_records(fp,key_usn);
}
fclose(fp);
break;

case 3:
fp = fopen(fname,"r");
if(fp==NULL)
{
printf("File Open Failed");
break;
}
display_records(fp,key);
fclose(fp);
break;
default:
exit(0);
}
}
}

100
Program 2.

Write and demonstrate the following C program.


a) newstrcpy that does the same job as strcpy
b) newstrcat that does the same job as strcat without using any library
functions.

#include<stdio.h>
#include<conio.h>

void newstrcpy(char *dst,char *source)


{
while(*source!='\0')
{
*dst=*source;
dst++;
source++;
}
*dst='\0';
}

void newstrcat(char *s1,char *s2)


{
while(*s1!='\0')
s1++;
while(*s2!='\0')
{
*s1=*s2;
s1++;
s2++;
}
*s1='\0';
}

100
void main()
{
char str1[20],str2[20];
int choice;
clrscr();
while(choice!=3)
{
printf("\nEnter the first string:");
scanf("%s",str1);
printf("\nEnter the second string:");
scanf("%s",str2);
printf("\nEntered string 1: %s , string 2 : %s",str1,str2);
printf("\nEnter1 to copy and 2 to concatinate:");
printf("\nEnter the choice:");
scanf("%d",&choice);
switch(choice)
{
case 1: newstrcpy(str1,str2);
break;
case 2: newstrcat(str1,str2);
break;
default:printf("undefined operation");
}

printf("String 1: %s\n",str1);
printf("String 2: %s\n",str2);
printf("press 2 to continue or 3 to exit:");
scanf("%d",&choice);
}
getch();
}

100
Program – 3

Write a C program which accepts the IP address in decimal dot format and
converts it into a 32 bit long integer using strtok library functions and unions.

#include<stdio.h>

union x
{
unsigned long add;
char c[4];
}ip;

main()
{
char ch, **end,inp[16],*p;
int i=0;
clrscr();
printf("Enter the IP address: ");
fflush(stdin);
gets(inp);
p=strtok(inp,".");
ip.c[3]=strtol(p,end,10);
for(i=2;i>=0;i--)
{
p=strtok(NULL,".");
ip.c[i]= strtol(p,end,10);
}
printf("Converted address is : %lu",ip.add);
getch();
}

100
Program 4.

Write a C program to construct a stack of integers to perform the


following operations:
a.Push
b.Pop
c.Display
It must print appropriate messages for Overflow, Underflow and empty.

#include<stdio.h>
#include<conio.h>
#include<process.h>
#define stack_size 2

int ele,i;
int flag=1;
struct stack
{
int item[5];
int top;
}s;

void push(struct stack *ptr, int ele)


{
if(ptr->top==stack_size-1)
printf("\n Overflow.\n");
else
{
ptr->top++;
ptr->item[ptr->top]=ele;
}
}

100
void pop(struct stack *ptr)
{
if(ptr->top= =-1)
{
printf("\nUnderflow.\n");
}
else
printf("\nThe popped value is %d.",(ptr->item[(ptr->top)--]));
}

void display(struct stack *ptr)


{
if(ptr->top= =-1)
printf("\nEmpty Stack.\n");
else
{
printf("\n\n");
for(i=0;i<=ptr->top; i++)
printf("%d\t",ptr->item[i]);
}
}

main()
{
clrscr();
s.top=-1;
while(1)
{
printf("\n\n1.PUSH\n2.POP\n3.DISPLAY\n4.Exit");
printf("\nEnter your Choice: ");
scanf("%d",&i);

100
switch(i)
{
case 1: printf("\nEnter the element: ");
scanf("%d",&ele);
push(&s,ele);
break;
case 2: pop(&s);
break;
case 3: display(&s);
break;
case 4: exit(0);
break;
default: printf("\nInvalid Input.");
}
}
getch();
}

100
Program – 5

Write a C program to convert & print a valid parenthesized infix expression to


postfix. The expression should consist of single character operands and binary
operators.

#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<ctype.h>

#define stack_size 25

int elem,i;
char str[25],res[25];

char stack[25];
int top;

void push(char ele)


{
if(top==stack_size-1)
printf("\n Overflow.\n");
else
{
top++;
stack[top]=ele;
}
}

char pop()
{
if(top==-1)
{
printf("\nUnderflow.\n");
return (-1);
}
100
else
return (stack[top--]);
}

void convert()
{
int i=0,j=0;

for(i=0;str[i]!='\0';i++)
{
if(isalpha(str[i]) || isdigit(str[i]))
res[j++]=str[i];
else
switch(str[i])
{
case '(': push(str[i]); break;
case '$': while(stack[top]=='$')
res[j++]=pop();
push(str[i]);
break;
case '/':
case '*':while(stack[top]=='$' || stack[top]=='/'||stack[top]=='*')
res[j++]=pop();
push(str[i]);
break;
case '+':
case '-':while(stack[top]=='$'||stack[top]=='*'||
stack[top]=='/'||stack[top]=='+'
||stack[top]=='-')
res[j++]=pop();
push(str[i]);
break;
case ')': while(stack[top]!='(')
res[j++]=pop();
pop();
break;
}
}
100
while(top>-1)
res[j++]=pop();
res[j]='\0';
printf("Postfix Expression is: %s ",res);
}

main()
{
clrscr();
top=-1;
printf("Enter a valid infix expression: ");
scanf("%s",str);
convert();
getch();
}

100
Program 6.

Wrie a C program to evaluate a valid postfix expression using a stack. Assume


that the postfix expression is read as a single line consisting of non negative singe
digit operands and binary arithmatic operators. The operators are +,-,*,/.

#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<ctype.h>
#define stack_size 10

int elem,i;
char str[20];

struct stack
{
int item[10];
int top;
}s;

void push(struct stack *ptr, int ele)


{
if(ptr->top==stack_size-1)
printf("\n Overflow.\n");
else
{
ptr->top++;
ptr->item[ptr->top]=ele;
}
}

int pop(struct stack *ptr)


{
if(ptr->top==-1)
{
printf("\nUnderflow.\n");
return (-1);
}
100
else
return (ptr->item[(ptr->top)--]);
}

int evaluate()
{
int a,b,res;
for(i=0;str[i]!='\0';i++)
{
if(isdigit(str[i]))
push(&s,str[i]-'0');
else
{
b=pop(&s);
a=pop(&s);
s

witch(str[i])
{
case '/': res=a/b;break;
case '*': res=a*b;break;
case '-': res=a-b;break;
case '+': res=a+b;break;
}
push(&s,res);
}
}
res=pop(&s);
return res;
}

100
main()
{
clrscr();
s.top=-1;
printf("Enter a valid postfix expression: ");
scanf("%s",str);
printf("\n\n Final result is : %d",evaluate());
getch();
}

100
Program 7:

Write a program to simulate the working of queue of integers using an array.


Provide the following operations: a] Insert b] Delete c] Display

#include<stdio.h>
#include<conio.h>
#define MAX 50
struct queue
{
int ele[MAX];
int rear;
};
void insert(struct queue *,int);
int removeElement(struct queue *);
int isEmpty(struct queue *);
int isFull(struct queue *);
void displayQueue(struct queue *);
int menu();
void main()
{
int element,choice;
struct queue q;
q.rear=-1;
do
{
choice=menu();
switch (choice)
{
case 1:
if(isFull(&q))
printf("\n The queue is full");
else
{
printf("\n Enter the element to be inserted : ");
scanf("%d",&element);
insert(&q,element);
}
break;
100
case 2:
if(isEmpty(&q))
printf("\n Queue is empty");
else
printf("\n Element removed = %d",removeElement(&q));
break;
case 3:
if(isEmpty(&q))
printf("\n The queue is empty");
else
printf("\n The queue is not empty");
break;
case 4:
if(isFull(&q))
printf("\n The queue is full");
else
printf("\n The queue is not full");
break;
case 5:
displayQueue(&q);
break;
case 6:
printf("\n Thank You");
break;
default:
printf("\n Please enter your choice correctly");
break;
}
}while(choice!=6);
getch();
}

100
int menu()
{
int ch;
getch();
clrscr();
printf("\n 1-Insert an element into the queue");
printf("\n 2-Remove an element from the queue");
printf("\n 3-Check whether the queue is empty");
printf("\n 4-Check whether the queue is full");
printf("\n 5-Display the elements of the queue");
printf("\n 6-Exit");
printf("\n Enter your choice : \t");
scanf("%d",&ch);
return ch;
}
void insert(struct queue *pq, int e)
{
pq->rear++;
pq->ele[pq->rear]=e;
}
int removeElement(struct queue *pq)
{
int eleRem=pq->ele[0],i;
for(i=1;i<=pq->rear;i++)
pq->ele[i-1]=pq->ele[i];
pq->rear--;
return eleRem;
}
int isEmpty(struct queue *pq)
{
if(pq->rear==-1)
return 1;
else
return 0;
}

100
int isFull(struct queue *pq)
{
if(pq->rear ==(MAX-1))
return 1;
else
return 0;
}
void displayQueue(struct queue *pq)
{
int i;
if(isEmpty(pq))
printf("\n The queue is empty");
else
{
printf("\n Elements of the queue from front to rear are : ");
for(i=0;i<=pq->rear;i++)
printf("\t %d",pq->ele[i]);
}
}

100
Program 8:

Write a program to simulate the working of circular queue of integers using an


array. Provide the following operations: a] Insert b] Delete c] Display

#include<stdio.h>
#include<conio.h>
#define MAX 50

struct queue
{
int ele[MAX];
int front,rear;
};

void insert(struct queue *,int);


int removeElement(struct queue *);
int isEmpty(struct queue *);
int isFull(struct queue *);
void displayQueue(struct queue *);
int menu();

void main()
{
int element,choice;
struct queue q;
q.rear=MAX-1;
q.front=MAX-1;

do
{
choice=menu();

100
switch (choice)
{
case 1:
if(isFull(&q))
printf("\n The queue is full");
else
{
printf("\n Enter the element to be inserted : ");
scanf("%d",&element);
insert(&q,element);
}
break;

case 2:
if(isEmpty(&q))
printf("\n Queue is empty");
else
printf("\n Element removed = %d",removeElement(&q));
break;

case 3:
if(isEmpty(&q))
printf("\n The queue is empty");
else
printf("\n The queue is not empty");
break;

case 4:
if(isFull(&q))
printf("\n The queue is full");
else
printf("\n The queue is not full");
break;

100
case 5:
displayQueue(&q);
break;

case 6:
printf("\n We are now going to exit the queue program");
printf("\n Thank You");
break;

default:
printf("\n Please enter your choice correctly");
break;
}
}while(choice!=6);
getch();
}
int menu()
{
int ch;
printf("\n\n\n\n Press any key to continue...........");
getch();
clrscr();
printf("\n Circular Queue Implementation Program");
printf("\n 1-Insert an element into the queue");
printf("\n 2-Remove an element from the queue");
printf("\n 3-Check whether the queue is empty");
printf("\n 4-Check whether the queue is full");
printf("\n 5-Display the elements of the queue");
printf("\n 6-Exit");
printf("\n Enter your choice (1-6) : \t");
scanf("%d",&ch);
return ch;
}

100
void insert(struct queue *pq, int e)
{
if(pq->rear==MAX-1)
pq->rear=0;
else
pq->rear++;
pq->ele[pq->rear]=e;
}
int removeElement(struct queue *pq)
{
if(pq->front==MAX-1)
pq->front=0;
else
pq->front++;
return (pq->ele[pq->front]);
}
int isEmpty(struct queue *pq)
{
if(pq->rear==pq->front)
return 1;
else
return 0;
}
int isFull(struct queue *pq)
{
int i;
if(pq->rear ==(MAX-1))
i=0;
else
i=pq->rear+1;
if(i==pq->front)
return 1;
else
return 0;
}

100
void displayQueue(struct queue *pq)
{
int i;
if(isEmpty(pq))
printf("\n The queue is empty");
else
{
if(pq->front==MAX-1)
i=0;
else
i=pq->front+1;

printf("\n Elements of the queue from front to rear are : ");


while(i!=pq->rear)
{
printf("\t %d",pq->ele[i]);
if(i==MAX-1)
i=0;
else
i++;
}
printf("\t %d",pq->ele[i]);
}
}

100
Program 9:

Write a C program using dynamic variables and pointers, to construct a singly


linked list containing of the following information in each node: student ID(int),
name(string) and semester(int). The operations to be supported are:
a] The insertion operation:
i] At the front of the list
ii] At the back of the list
iii] At any position of the list
b] Deleting a node based on student ID. If the specified node is not present
in the list, an error message to be displayed.
c] Searching a node based on student ID and update the information
content. If the specified node is not present in the list, an error message to
be displayed.
d] Displaying all nodes of a file.

#include<stdio.h>
#include<conio.h>
#include<alloc.h>

struct node
{
int id;
char name[20];
int sem;
struct node *next;
};

void insertfront(struct node**,struct node**);


void insertback(struct node**,struct node**);
void insertpos(struct node**,struct node**);
void rem(struct node**,struct node**);
void search(struct node*,int);
void display(struct node*);

100
void main()
{
int choice,id;
struct node *f=NULL;
struct node *r=NULL;
clrscr();
do
{
printf("\n1.Insert front\n2.Insert back\n3.Insert at specified position\n4.delete
id\n5.Search id\n6.Display\n7.Exit\n");
scanf("%d",&choice);
switch(choice)
{
case 1: insertfront(&f,&r);
break;
case 2: insertback(&f,&r);
break;
case 3: insertpos(&f,&r);
break;
case 4: rem(&f,&r);
break;
case 5: printf("\nEnter id to search...\n");
scanf("%d",&id);
search(f,id);
break;
case 6: display(f);
break;
case 7: break;
}
}while(choice!=7);
getch();
}

void insertfront(struct node**f,struct node **r)


{
struct node*p;
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter name...\n");
scanf("%s",p->name);
100
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);
if(*f==NULL)
{
(*f)=(*r)=p;
p->next = NULL;
}
else
{
p->next=(*f);
(*f)=p;
}
}

void insertback(struct node**f,struct node **r)


{
struct node*p;
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter name...\n");
scanf("%s",p->name);
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);
if((*f)==NULL)
{
(*f)=(*r)=p;
p->next=NULL;
}
else
{
p->next=NULL;
(*r)->next=p;
(*r)=p;
}
}

100
void insertpos(struct node**f,struct node**r)
{
int pos,count=0;
struct node*p,*q,*s;
q=(*f);
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter postion to enter...\n");
scanf("%d",&pos);
printf("\nEnter name...\n");
scanf("%s",p->name);
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);
while((q->next!=NULL)&&(count!=(pos-1)))
{
count++;
s=q;
q=q->next;
}
if(count<(pos-1))
{printf("\nInvalid postion\n");}
else
{
p->next=s->next;
s->next=p;
if(p->next==NULL)
{(*r)=p;}
}
}

void rem(struct node **f,struct node **r)


{
int i;
struct node *p,*q;
printf("\nEnter ID(integer) to delete...\n");
scanf("%d",&i);
p=*f;

100
while((p->next!=NULL)&&(p->id!=i))
{
q=p;
p=p->next;
}
if(p->id==i)
{
if((*f)==p && (*f)->next==NULL)
{printf("%s",p->name);}
else if ((*r)==p)
{
printf("%s",p->name);
q->next=NULL;
(*r)=q;
}
else {
printf("%s",p->name);
q->next=p->next;
}
free(p);
}
else
{printf("\nNODE NOT FOUND...\n");}
}

void search(struct node *f,int i)


{
printf("\nEnter ID to search...\n");
while((f->next!=NULL)&&(f->id!=i))
{
f=f->next;
}
if (f->id==i)
{
printf("\nRecord FOUND...\n");
printf("\nNAME-%s\nSEMESTER-%d",f->name,f->sem);
}

100
else
{
printf("\nSorry SEARCH FAILED...\n");
}
}

void display(struct node *f)


{
do
{
printf("\n%s\t%d\t%d",f->name,f->id,f->sem);
f=f->next;
}while(f!=NULL);
}

100
Program 10.

Write a C program using dynamic variables and pointers to construct a stack of


integers using singly linked list to perform the following operations:
a.Push
b.Pop
c.Display
It must print appropriate messages for Stack overflow and empty.

#include<stdio.h>
#include<conio.h>
struct node{
int info;
struct node* next;
};
int displayMenu();
void push(struct node**,int);
int pop(struct node**);
int isEmpty(struct node**);
void display(struct node**);
void main()
{
struct node *top;
int element,choice;
top=NULL;
do
{
choice=displayMenu();
switch (choice)
{
case 1:
printf("\n Enter the element to be pushed : ");
scanf("%d",&element);
push(&top,element);
break;
case 2:
if(isEmpty(&top))
printf("\n Stack is empty");

100
else
printf("\n Element removed = %d",pop(&top));
break;
case 3:
if(isEmpty(&top))
printf("\n Stack is empty");
else
printf("\n Stack is not empty");
break;
case 4:
display(&top);
break;
case 5:
printf("\n Exiting the program, thank you for using it");
break;
default:
printf("\n Please enter your choice correctly");
break;
}
}while(choice!=5);
getch();
}
int displayMenu()
{
int ch;
printf("\n Press any key to continue...........");
getch();
clrscr();
printf("\n Stack implementation using Singly Linked List");
printf("\n 1-Push an element on the top of the stack");
printf("\n 2-Pop out an element from the top of the stack");
printf("\n 3-Check whether the stack is empty");
printf("\n 4-Display the elements of the stack");
printf("\n 5-Exit");
printf("\n Enter your choice(1-5) : ");
scanf("%d",&ch);
return ch;
}
100
void push(struct node **t,int e)
{
struct node *p;
p=(struct node*)malloc(sizeof(struct node));
if(p==NULL)
printf("\n Stack Overflow");
else
{
p->info=e;
p->next=*t;
*t=p;
}
}
int pop(struct node **t)
{
int poppedElement;
struct node *p;
p=*t;
*t=p->next;
poppedElement=p->info;
free(p);
return poppedElement;
}
int isEmpty(struct node **t)
{
if(*t==NULL)
return 1;
else
return 0;
}
void display(struct node **t)
{
int i;
struct node *p;
if(isEmpty(t))
printf("\n The stack is empty");

100
else
{
p=*t;
printf("\n");
while(p!=NULL)
{
printf("\t %d",p->info);
p=p->next;
}
}
}

100
Program 11.

Write a C program using dynamic variables and pointers to construct a queue of


integers using a singly linked list to perform the following operations:
a. Insert
b. Delete
c. Display
It must print appropriate messages for queue full and empty.

#include<stdio.h>
#include<conio.h>
struct node
{
int info;
struct node *next;
};
void add(struct node **f,struct node **r,int a);
int rem(struct node **f,struct node **r);
int empty(struct node **f);
void display(struct node **f);
void main()
{
struct node *f=NULL,*r=NULL;
int choice,a,i,yes=1;
clrscr();
do
{
printf("\n1:Add\n2:Remove\n3:Display");
printf("\n Enter choice");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("Enter the element to be added ");
scanf("%d",&a);
add(&f,&r,a);
break;

100
case 2:
if(empty(&f))
printf("Queue Empty");
else
{
i=rem(&f,&r);
printf("%d",i);
}
break;
case 3:
display(&f);
break;
default:
printf("Wrong Choice ");
exit(0);
}
}while(yes);
getch();
}

void add(struct node **f,struct node **r,int a)


{
struct node *p;
p=(struct node *)malloc(sizeof(struct node));
p->info=a;
p->next=NULL;
if(*f==NULL)
*f=p;
else
(*r)->next=p;
*r=p;
}

int rem(struct node **f,struct node **r)


{
int i;
struct node *p;
p=*f;
i=p->info;
100
(*f)=(*f)->next;
if(*f==NULL)
*r=NULL;
free(p);
return i;
}

int empty(struct node **f)


{
if(*f==NULL)
return 1;
else
return 0;
}

void display(struct node **f)


{
struct node *p;
if(*f==NULL)
printf("\nQueue Empty");
else
{
p=*f;
while(p!=NULL)
{
printf(" %d ",p->info);
p=p->next;
}
}
}

100
Program 12:

Write a C program to support the following operations on a doubly linked list


where each node consists of integers.
a] Create a doubly linked list by adding each node at the front.
b] Insert new nodes to the left of the node whose key value is read as an
input
c] Delete the node of the given data, if it is found, otherwise display
appropriate message.
d] Display the contents of the file.

#include<stdio.h>
#include<conio.h>
#include<alloc.h>

struct node
{
int id;
char name[20];
int sem;
struct node *succ;
struct node *pred;
};

void insertfront(struct node**,struct node**);


void insertback(struct node**,struct node**);
void insertpos(struct node**,struct node**);
void rem(struct node**,struct node**);
void search(struct node*,int);
void display(struct node*);

void main()
{
int choice,id;
struct node *f=NULL;
struct node *r=NULL;
clrscr();

100
do
{
printf("\n1.Insert front\n2.Insert back\n3.Insert at specified position\n4.delete
id\n5.Search id\n6.Display\n7.Exit\n");
scanf("%d",&choice);
switch(choice)
{
case 1: insertfront(&f,&r);
break;
case 2: insertback(&f,&r);
break;
case 3: insertpos(&f,&r);
break;
case 4: rem(&f,&r);
break;
case 5: printf("\nEnter id to search...\n");
scanf("%d",&id);
search(f,id);
break;
case 6: display(f);
break;
case 7: break;
}
}while(choice!=7);
getch();
}

void insertfront(struct node**f,struct node **r)


{
struct node*p;
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter name...\n");
scanf("%s",p->name);
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);

100
if(*f==NULL)
{
(*f)=(*r)=p;
p->succ = NULL;
p->pred = NULL;
}
else
{
p->succ=(*f);
p->pred=NULL;
(*f)=p;
}
}

void insertback(struct node**f,struct node **r)


{
struct node*p;
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter name...\n");
scanf("%s",p->name);
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);
if((*f)==NULL)
{
(*f)=(*r)=p;
p->succ=NULL;
p->pred=NULL;
}
else
{
p->succ=NULL;
p->pred=(*r);
(*r)->succ=p;
(*r)=p;
}
}

100
void insertpos(struct node**f,struct node**r)
{
int pos,count=0;
struct node*p,*q,*s;
q=(*f);
p=(struct node*)malloc(sizeof(struct node));
printf("\nEnter postion to enter...\n");
scanf("%d",&pos);
printf("\nEnter name...\n");
scanf("%s",p->name);
printf("Enter ID(integer)...\n");
scanf("%d",&p->id);
printf("Enter Semester...\n");
scanf("%d",&p->sem);
while((q->succ!=NULL)&&(count!=(pos-1)))
{
count++;
s=q;
q=q->succ;
}
if(count<(pos-1))
{printf("\nInvalid postion\n");}
else
{
p->succ=s->succ;
p->pred=s;
s->succ->pred=p;
s->succ=p;
if(p->succ==NULL)
{(*r)=p;}
}
}

void rem(struct node **f,struct node **r)


{
int i;
struct node *p,*q;
printf("\nEnter ID(integer) to delete...\n");
scanf("%d",&i);
100
p=*f;
while((p->succ!=NULL)&&(p->id!=i))
{
q=p;
p=p->succ;
}
if(p->id==i)
{
if((*f)==p && (*f)->succ==NULL)
{printf("%s",p->name);}
else if ((*r)==p)
{
printf("%s",p->name);
q->succ=NULL;
(*r)=q;
}
else {
printf("%s",p->name);
q->succ=p->succ;
p->succ->pred=q;
}
free(p);
}
else
{printf("\nNODE NOT FOUND...\n");}
}

void search(struct node *f,int i)


{
printf("\nEnter ID to search...\n");
while((f->succ!=NULL)&&(f->id!=i))
{
f=f->succ;
}
if (f->id==i)
{
printf("\nRecord FOUND...\n");
printf("\nNAME-%s\nSEMESTER-%d",f->name,f->sem);
}
100
else
{
printf("\nSorry SEARCH FAILED...\n");
}
}

void display(struct node *f)


{
do
{
printf("\n%s\t%d\t%d",f->name,f->id,f->sem);
f=f->succ;
}while(f!=NULL);
}

100
Program 13:

Write a C program:
a] To construct a binary search tree of integers.
b] To traverse the tree using all methods i.e., inorder , preorder and
postorder.
c] To display the elements in the tree.

#include<stdio.h>
#include<conio.h>
struct node
{
int info;
struct node *left,*right;
};
void main()
{ void freenode(struct node **r) ;
void setright(struct node *p,int x);
void create(struct node **p) ;
void inorder(struct node *r) ;
void preorder(struct node *r);
void setleft(struct node *p,int x);
struct node* maketree(int x);
void postorder(struct node *r);
struct node *root=NULL;
create(&root);
preorder(root);
inorder(root);
postorder(root);
freenode(&root);
postorder(root);
getch();
}
struct node* maketree(int x)
{
struct node *r;
r=(struct node*)malloc(sizeof(struct node));
r->info=x;
r->left=r->right=NULL;
100
return(r);
}
void setleft(struct node *p,int x)
{
if(p==NULL)
printf("Invalid\n");
else
{
if(p->left!=NULL)
printf("Invalid\n");
else
p->left=maketree(x);
}
}
void setright(struct node *p,int x)
{
if(p==NULL)
printf("Invalid\n");
else
{
if(p->right!=NULL)
printf("Invalid\n");
else
p->right=maketree(x);
}
}

void create(struct node **r)


{
struct node *q,*p;
char ch;
int temp;
printf("Enter number\n ");
scanf("%d",&temp);
*r=maketree(temp);
do
{
printf("enter the number\n");
scanf("%d",&temp);
100
q=NULL;
p=*r;
while(p!=NULL && p->info!=temp)
{
q=p;
if(temp<p->info)
p=p->left;
else
p=p->right;
}
if(p->info==temp)
printf("Duplicate %d num & hence discarded",temp);
else
if(temp<q->info)
setleft(q,temp);
else
setright(q,temp);
printf("do you want to continue\n");
fflush(stdin);
scanf("%c",&ch);
}while(ch=='y');
}
void preorder(struct node *r)
{ printf("Preorder\n");
if(r!=NULL)
{
printf("%d\n",r->info);
preorder(r->left);
preorder(r->right);

}
}
void inorder(struct node *r)
{ printf("Inorder\n");
if(r!=NULL)
{
inorder(r->left);
printf("%d\n",r->info);
inorder(r->right);
100
}

}
void postorder(struct node *r)
{ printf("post order\n");
if(r!=NULL)
{
postorder(r->left);
postorder(r->right);
printf("%d\t",r->info);
}
}
void freenode(struct node **r)
{ struct node *p=*r;
printf("freenode\n");
freenode((*r)->left);
free(p);
freenode((*r)->right);
}

100
Program 14:

Write a recursive C program for


a] Searching an element on a given list of integers using binary search
method.

#include<stdio.h>
#include<conio.h>
#define MAX 50
int binarySearch(int [],int,int,int);
void main()
{
int a[MAX],i,n,found,element;
clrscr();
printf("\n Enter the number of elements : ");
scanf("%d",&n);
printf("\n Now enter the elements one by one in ascending order : ");
for(i=0;i<n;i++)
{
printf("\n a[%d] = ",i);
scanf("%d",&a[i]);
}
printf("\n Enter the element to be searched : ");
scanf("%d",&element);
found=binarySearch(a,0,n-1,element);
if(found==-1)
printf("\n The element is not found");
else
printf("\n The element is found at position %d",found);
getch();
}
int binarySearch(int arr[],int low,int high,int key)
{
int mid;
if(low>high)
return -1;
mid=(low+high)/2;
if(arr[mid]==key)
return mid;
100
else

{
if(key>arr[mid])
binarySearch(arr,mid+1,high,key);
else
binarySearch(arr,low,mid-1,key);
}
}

b] Solving the tower of Hanoi problem.

#include<stdio.h>
#include<conio.h>
#include<dos.h>
void towersOfHanoi(int n,char source,char destination,char temp);
void main()
{
int numOfDiscs;
clrscr();
printf("\n Enter the number of discs : ");
scanf("%d",&numOfDiscs);
towersOfHanoi(numOfDiscs,'s','d','t');
getch();
}
void towersOfHanoi(int n,char source,char destination,char temp)
{
static int i;
if(n>0)
{
towersOfHanoi(n-1,source,temp,destination);
delay(1000);
printf("\n Move disc %d from %c to %c %d",n,source,destination,i++);
towersOfHanoi(n-1,temp,destination,source);
}
}
___________________
100

Vous aimerez peut-être aussi