0% found this document useful (0 votes)
3 views

class notes

The document provides an overview of searching techniques, specifically linear and binary search, along with their advantages and disadvantages. It also discusses memory allocation methods in C, including static and dynamic allocation, and details various memory management functions like malloc, calloc, realloc, and free. Additionally, it covers stack operations such as push and pop, along with concepts of stack overflow and underflow, and highlights applications of stacks in expression conversion.

Uploaded by

Kurshid Naseeba
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

class notes

The document provides an overview of searching techniques, specifically linear and binary search, along with their advantages and disadvantages. It also discusses memory allocation methods in C, including static and dynamic allocation, and details various memory management functions like malloc, calloc, realloc, and free. Additionally, it covers stack operations such as push and pop, along with concepts of stack overflow and underflow, and highlights applications of stacks in expression conversion.

Uploaded by

Kurshid Naseeba
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 16

UNIT 2

1) Searching

“What is searching? What are the various searching techniques?”


Definition: More often we will be working with large amount of data. It may be necessary to
determine whether a particular item is present in the large amount of data. This process of finding a
particular item in the large amount of data is called searching.
The two important and simple searching techniques are shown below:

1) Linear search (Sequential search)


2) Binary search

1) Linear search (Sequential search)


Definition: A linear search also called sequential search is a simple searching technique. In this
technique, we search for a given key in the list in linear order (sequential order) i.e., one after the
other from first element to last element or vice versa. The search may be successful or
unsuccessful. If key is present, we say search is successful, otherwise, search is unsuccessful.

 Successful search: If search key is 25, it is present in the above list and we return
its position 3 indicating “Successful search”.
 Unsuccessful search: If search key is 50, it is not present in the above list and we
return -1 indicating “Unsuccessful search”

But, once the value of i is greater than or equal to 5, it is an indication that item is not
present and we return -1. The code for this can be written as:
return -1; /*Unsuccessful search */
Note: The terminal condition in the for loop i.e., i < 5 can be replaced by i < n for a
general case. Now, the code can be written as:
for (i = 0; i < n; i++)
{
1
if (key == a[i] ) return i; /* Successful search */
}
return – 1; /* Unsuccessful search */

/* for program please refer the lab program*/

Advantages of linear search


 Very simple approach
 Works well for small arrays
 Used to search when the elements are not sorted
Disadvantages of linear search
 Less efficient if the array size is large
 If the elements are already sorted, linear search is not efficient.

Binary search

What is binary search? What is the concept used in binary search?”


Definition: A binary search is a simple and very efficient searching technique which can be applied
if the items to be compared are either in ascending order or descending order.

/*just for reference*/

The general idea used in binary search is similar to the way we search for the telephone number of
a person in the telephone directory. Obviously, we do not use linear search. Instead, we open the
book from the middle and the name is compared with the element at the middle of the book. If the
name is found, the corresponding telephone number is retrieved and the searching has to be
stopped. If the name to be searched is less than the middle element, search towards left otherwise,
search towards right. The procedure is repeated till key item is found or the key item is not found.

“How to search for key in a list of elements?”


Let key is the element to be searched in the array a consisting of n elements. In the array a, element
10 in position 0 is the first element and element 90 in position 8 is the last element as shown
below:

So, initial values are: low = 0 and high = 8


=9–1
= n – 1 (general)
So, in general, initial values are:
low = 0
high = n – 1
If low is the position of the first element and high is the position of the last element, the position of
the middle element can be obtained using the statement:
mid = ( low + high ) /2
The key to be searched is compared with middle element. The pictorial representation
and equivalent code can be written as shown below:

2
After executing the statement, if key is same as a[mid], we return the position of key. If key is not
same as a[mid], it may be present either in the left part of the array or right part of the array and
leads to following 2 cases:
Case 1) key towards left of mid: If key is less than the middle element, the left part of
array has to be compared from low to mid-1 as shown in figure below:
i.e.,

if ( key < a[mid] ) high = mid – 1;


Case 2) key towards right of mid: If key is greater than the middle element, the right
part of array has to be compared from mid + 1 to high as shown in figure below:
i.e.,

if ( key > a[mid] ) low = mid + 1;


Note that entire step 4 has to be repeatedly executed as long as long as low is less than
3
or equal to high. The code for this can be written as shown below:
while ( low <= high )
{
mid = ( low + high ) / 2; /* Find the mid point */
if ( key == a[mid] ) return mid; /* Item found */
if ( key < a[mid] ) high = mid – 1; /* To search left part */
if ( key > a[mid] ) low = mid + 1; /* To search right part */
}
return -1; /* Unsuccessful search */
}
/* for program please refer the lab prgm*/
Advantages of binary search
 Simple technique
 Very efficient searching technique
Disadvantages of binary search
 The list of elements to be searched should be sorted.
 It is necessary to obtain the middle element which is possible only if the elements are stored in
the array. If the elements are stored in linked list, this method cannot be used.

MEMORY ALLOCATION FUNCTIONS


What are the various memory allocation techniques?”
Memory can be allocated for variables using two different techniques:
1) Static allocation (User declarations and definitions)
2) Dynamic allocation (Using predefined functions)
1)“What is static memory allocation?” If memory space to be allocated for various variables is
decided during compilation time itself, then the memory space cannot be expanded to
accommodate more data or cannot be reduced to accommodate less data. In this technique, once
the size of the memory space to be allocated is fixed, it cannot be altered during execution time.
This is called “static memory allocation”.
For example, consider the following declaration int a[10];

Disadvantages of static memory allocation


 The memory space to be allocated is fixed during compilation. Hence, the memory space cannot
be altered during execution time
 Leads to underutilization if more memory space is allocated
 Leads to overflow if less memory is allocated
 The static nature imposes certain limitations and can find their applications only when the data
is fixed and known before processing
2) “What is dynamic memory allocation?” Dynamic memory allocation is the process of
allocating memory space during execution time (i.e., run time). This allocation technique uses
predefined functions to allocate and release memory for data during execution time. So, if there is
an unpredictable storage requirement, then the dynamic allocation technique is used. Dynamic
allocation will be used when we create dynamic arrays, linked lists, trees
“What are the various memory management functions in C?”
The various memory management functions that are available in C are shown below:
a) calloc
b) malloc
c) realloc
d) free

4
1)malloc(size)
“What is the purpose of using malloc?” This function allows the program to allocate a block of
memory space as and when required and the exact amount needed during execution. The size of
the block is the number of bytes specified in the parameter.
#include <stdlib.h> /* Prototype definition of malloc() is available */
……..
ptr = (data_type *) malloc(size);

…….
where ,
 ptr is a pointer variable of type data_type
 data_type can be any of the basic data type or user defined data type
 size is the number of bytes required
 On successful allocation, the function returns the address of first byte of allocated memory.
Since address is returned, the return type is a void pointer. By type casting appropriately we can
use it to store integer, float etc.
 If specified size of memory space is not available, the condition is called “overflow of
memory”. In such case, the function returns NULL.
“What will happen if the following program segment is executed?
int *ptr;
ptr = (int *) malloc (10);

/*PLEASE REFER THE TEXT BOOK FOR SUCCESFULL ALLOCATION AND


UNSUCCESSFULL ALLOCATION and ALSO FOR THE PROGRAM*/

2) calloc(n, size)
This function is used to allocate multiple blocks of memory. Here, calloc – stands for contiguous
allocation of multiple blocks and is mainly used to allocate memory for arrays. The number of
blocks is determined by the first parameter n. The size of each block is equal to the number of
bytes specified in the parameter i.e., size. Thus, total number of bytes allocated is n*size and all
bytes will be initialized to 0.
The syntax is shown below:
“What is the purpose of using calloc?”
#include <stdlib.h>
……..
……..
ptr = (data_type *) calloc(n, size);
……..
……..
where
 ptr is a pointer variable of type data_type
 data_type can be any of the basic data type or user defined data type
 n is the number of blocks to be allocated
 size is the number of bytes in each block
“What does this function return?” The function returns the following values:
 On successful allocation, the function returns the address of first byte of allocated memory.
Since address is returned, the return type is a void pointer. By type casting appropriately we can
use it to store integer, float etc.
5
 If specified size of memory is not available, the condition is called “overflow of memory”. In
such case, the function returns NULL.
What will happen if the following program segment is executed?

What will happen if the following program segment is executed?


int *ptr;
ptr = (int *) calloc (5, sizeof(int));

Solution: The compiler reserves the space for the variable ptr in the memory. No initialization is
done if ptr is a local variable (It is initialized to NULL if it is global variable). Now, using the
function calloc() we can request specified number of blocks of memory to be reserved. The
specified blocks of memory may be allocated or may not be allocated.

/*PLEASE REFER THE TEXT BOOK FOR SUCCESFULL ALLOCATION AND


UNSUCCESSFULL ALLOCATION and ALSO FOR THE PROGRAM*/

“What is the difference between malloc() and calloc()?”

6
3) realloc(ptr, size)

Now, let us see “What is the purpose of using realloc?”


Before using this function, the memory should have been allocated using malloc() or calloc().
Sometimes, the allocated memory may not be sufficient and we may require additional memory
space. Sometimes, the allocated memory may be much larger and we want to reduce the size of
allocated memory. In both situations, the size of allocated memory can be changed using
realloc() and the process is called reallocation of memory.
The reallocation is done as shown below:
 realloc() changes the size of the block by extending or deleting the memory at the end of the
block.
 If the existing memory can be extended, ptr value will not be changed
 If the memory cannot be extended, this function allocates a completely new block and copies
the contents of existing memory block into new memory block and then deletes the old memory
block.
The syntax is shown below:
#include <stdlib.h> /* Prototype definition of realloc() is available */
……..
……..
ptr = (data_type *) realloc(ptr, size);
…….
…….
where
 ptr is a pointer to a block of previously allocated memory either using
malloc() or calloc().
 size is new size of the block

if (ptr == NULL)
{ /* Memory is not allocated */
printf(“Insufficient memory\n”);
return;
}

7
Now, let us see “What does this function return?” The function returns the following
values:
 On successful allocation, the function returns the address of first byte of allocated
memory.
 If specified size of memory cannot be allocated, the condition is called “overflow
of memory”. In such case, the function returns NULL.

/*PLEASE REFER THE TEXT BOOK FOR SUCCESFULL ALLOCATION AND


UNSUCCESSFULL ALLOCATION and ALSO FOR THE PROGRAM*/

4) free(ptr)
Now, let us see “What is the purpose of using free()?”
This function is used to de-allocate (or free) the allocated block of memory which is allocated
using the functions calloc(), malloc() or realloc(). It is the responsibility of a programmer to de-
allocate memory whenever it is not required by the program and initialize ptr to NULL. The
syntax is shown below:
#include <stdlib.h> /* Prototype definition of free() is available */
……..
free(ptr);
ptr = NULL;
…….
…….
Here, ptr is a pointer to a memory block. Now, let us see how this function works. For
example, consider the following memory configuration where ptr points to a memory
block containing 200 integers ranging from 01 to 0200.

STACKS
8
“What is a stack?”

What are the various operations that can be performed on stacks?”


Stack operations
Push (Inserting an element on the top of stack)
Pop (Deleting an element from the top of stack)
Display (Display contents of stack)

1) Push operation

Now, let us see “What is push operation?”

Inserting an element into the stack is called push operation. Only one item is inserted at a time
and item has to be inserted only from top of the stack.

9
“What is stack overflow?”
Definition: When elements are being inserted, there is a possibility of stack being full. Once the
stack is full, it is not possible to insert any element. Trying to insert an element, even when the
stack is full results in overflow of stack.
For example, consider the stack shown in figure 6.1 with STACK_SIZE 4. After inserting 30, 20,
25 and 10 there is no space to insert any item. Then we say that stack is full. Trying to insert an
element into the stack when the stack is full is called overflow of stack.

“How to implement push operation using arrays (static allocation technique)?”

“When we cannot insert item into the stack?” We cannot insert any item into the stack when top
has already reached STACK_SIZE–1 (Fig. 6.2.d). In such situation, we have to display
appropriate message as shown below using the following code:

/* Check for overflow of stack */


if (top == STACK_SIZE – 1)
{
printf(“Stack overflow\n”);
return;
}

/*Code for push */


void push()
{
/* Check for overflow of stack */
if (top == STACK_SIZE – 1)
{
printf(“Stack overflow\n”);
return;
}
top = top + 1; /* Increment top by 1 */
s[top] = item; /* Insert into stack */
}
Pop operation
“What is pop operation?”
Definition: Deleting an element from the stack is called pop operation. Only one item can be
deleted at a time and item has to be deleted only from top of the stack.

10
“What is stack underflow?”
Definition: When elements are being deleted, there is a possibility of stack being empty. When
stack is empty, it is not possible to delete any item. Trying to delete an element from an empty
stack results in stack underflow.

For example, in the figure 6.3, after deleting 10, 25, 20 and 30 there are no elements in the stack
and stack is empty. Deleting an element from the empty stack results in stack underflow.

“How to implement pop operation using arrays (static allocation technique)?


Each time, the item is deleted, top is decremented and finally, when the stack is empty the value
of top will be –1. In this situation, it is not possible to delete any item from the stack. The code to
delete an item from stack can be written as shown below:
int pop()
{
int item_deleted;
if (top == -1) return 0; /* Stack underflow? */
item_deleted = s[top--]; /* Access the item and delete */
return item_deleted; /* Send the item deleted to the calling function */
}

Display stack items


In the display procedure, if the stack already has some items, all those items are displayed one
after the other. If no items are present, the appropriate error message is displayed.

11
for (i = 0; i <= top; i++)
printf(“%d\n”, s[i]);

/* Code to display the content of stack */


void display()
{
int i;
if (top == -1) /* Is stack empty */
{
printf(“Stack is empty\n”);
return;
}
/* Display contents of stack */
printf(“Contents of the stack\n”);

for (i = 0; i <= top; i++) printf(“%d\n”, s[i]);

6.2 Applications of stack


“What are the applications of stack?” The various applications in which stacks are used are:
Conversion of expressions: When we write mathematical expressions in a program, we use
infix expressions. These expressions will be converted into equivalent machine instructions by
the compiler using stacks. Using stacks, we can efficiently convert the expressions from infix to
postfix, infix to prefix etc.
Evaluation of expressions: An arithmetic expression represented in the form of

12
either postfix or prefix can be easily evaluated.
Recursion: A function which calls itself is called recursive function. Some of the problems
such as tower of Hanoi, problems involving tree manipulations etc., can be implemented very
efficiently using recursion. It is a very important facility available in variety of programming
languages such as C, C++ etc.,
Other applications: There are so many other applications where stacks can be used:
For example, to find whether the string is a palindrome, to check whether a given expression is
valid or not and so on.

Conversion of expressions
Definition: The sequence of operators and operands that reduces to a single value after
evaluation is called an expression. The operands consist of constants and variables whereas the
operators consist of symbols such as +, -, *, / and so on.

Representation of expressions
1) Infix expression
2) Postfix expression
3) Prefix expression

“What is an infix expression?”


Definition: In an expression, if an operator is in between two operands, the expression is called
an infix expression. The infix expression can be parenthesized or un-parenthesized. For
example,
a + b is an un-parenthesized infix expression
(a + b) is a parenthesized infix expression

“What is postfix expression?”


Definition: In an expression, if an operator follows the two operands (i.e., operator comes after
the two operands), the expression is called postfix expression. No, parenthesis is allowed in
postfix expressions. The postfix expression is always unparenthesized. It is also called suffix
expression or reverse polish expression.
For example,
ab+

“What is prefix expression?”


Definition: In an expression, if an operator precedes the two operands (i.e., operator comes
before the two operands), the expression is called prefix expression. No, parenthesis is allowed
in prefix expressions. The prefix expression is always unparenthesized.
It is also called polish expression. For example,
+ab

13
Precedence and associativity of the operators

“What is precedence of operators?”

Conversion from infix to postfix

14
Hence, equivalent postfix expression is A B C – D * + E ^ F +

15
/*CONVERSION FROM INFIX TO POSTFIX USING STACKS<EVALUATION OF
POSTFIX EXPRESSION USING STACKS PLEASE DO REFER THE WORKOUT
PROBLEMS*/

Application of Stack in Function Calls

1. Function Call Management:


o The stack is used to manage function calls and their executions. Each time a
function is called, a new stack frame is created containing:
 The return address (where to resume after the function finishes).
 Function parameters (arguments passed).
 Local variables declared within the function.
2. Call Stack:
o The call stack keeps track of the function execution flow. When a function is
called, its frame is pushed onto the stack. When the function returns, its frame is
popped off, and execution continues from the return address.
3. Recursive Function Calls:
o In recursion, each recursive call creates a new stack frame. The stack stores the
state of each recursive call, allowing the program to return to the correct state
when the base case is met, and recursion starts to unwind.
4. Memory Management:
o The stack helps in automatic memory management by allocating memory for
function calls. Each function’s local variables are automatically deallocated when
the function returns, making it efficient.
5. Function Execution Order:
o The stack ensures that functions are executed in the LIFO (Last In, First Out)
order. The most recently called function is executed first and returns control back
to the previous one.
6. Stack Overflow:
o If too many function calls occur (e.g., deep recursion without base case), the stack
may overflow due to limited space, resulting in a stack overflow error.

16

You might also like