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

Unit - 1 DSA Notes

Uploaded by

bnradheshyam555
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Unit - 1 DSA Notes

Uploaded by

bnradheshyam555
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 39

Programming in C

C programming language is a general-purpose, procedural, high-level programming


language used to develop computer software, computer applications, system programming, game
development, web development, and more. C language was developed by Dennis M. Ritchie at
the Bell Telephone Laboratories in 1972. C is a powerful and flexible language. It is a widely
used programming language. C was developed for the programming of the UNIX operating
System.
key advantages of C language:
 Easy to learn.
 Versatile Language, which can be used in both applications and technologies.
 MIddle-level language.
 C is a structured language.
Features of C Language
There are some key features of C language that show the ability and power of C language:
 Simplicity and Efficiency: The simple syntax and structured approach make the C language
easy to learn.
 Faster Language: C is a static programming language, which is faster than dynamic
languages. Like Java and Python are dynamic languages, C is a compiler-based program.
That is the reason for faster code compilation and execution.
 Portable: C provides the feature that you write code once and run it anywhere on any
computer. It shows the machine-independent nature of the C language.
 Memory Management: C comes with the free() function to free the allocated memory at any
time.
 Pointers: C comes with the feature of pointers. Through pointers, we can directly access or
interact with the memory. We can initialize a pointer as an array, variables, etc.
 Structured Language: C provides the feature of structural programming, It allows you to code
into different parts using functions that can be stored as libraries for reusability.

Primitive Data Types


In C, each variable has a specific data type, where a data type tells us the size, range and
the type of a value that can be stored in a variable. In C, there are about seven primitive data
types. These data types are : short, int, long, char, float, double and few of their variants.
Primitive data types are the most basic data types that are used for representing simple values
such as integers, float, characters, etc.
Primitive data types are categorized into these parts
 integer data types, such as short, int, long.
 floating-point data types, such as float, double.
 character data type, such as char.

Primitive Data types in C

In C, the number of bytes used to store a data type depends on the Compiler(depending on the bit
size of a compiler and also the OS). But irrespective of the bit-size of the compiler and OS, the
following rules are followed, such as –
Data Type Size Range
char at least 1 byte -128 to 127
unsigned char at least 1 byte 0 to 255
short at least 2 bytes -32768 to 32767
unsigned short at least 2 bytes 0 to 65535
int at least 2 bytes -32768 to 32767
unsigned int at least 2 bytes 0 to 65535
long at least 4 bytes -2,147,483,648 to 2,147,483,647
unsigned long at least 4 bytes 0 to 4,294,967,295
float at least 2 bytes 3.4e-038 to 3.4e+038
double at least 8 bytes 1.7e-308 to 1.7e+308
long double at least 10 bytes 1.7e-4932 to 1.7e+4932

C struct
In C programming, a struct (or structure) is a collection of variables (can be of different types)
under a single name.
Define Structures
Before you can create structure variables, you need to define its data type. To define a struct,
the struct keyword is used.

Syntax of struct

struct structureName {
dataType member1;
dataType member2;
...
};

For example,
struct Person {
char name[50];
int citNo;
float salary;
};
a derived type struct Person is defined. Now, you can create variables of this type.

Create struct Variables


When a struct type is declared, no storage or memory is allocated. To allocate memory of a given
structure type and work with it, we need to create variables.
Here's how we create structure variables:
struct Person {
// code
};

int main() {
struct Person person1, person2, p[20];
return 0;
}

Another way of creating a struct variable is:


struct Person {
// code
} person1, person2, p[20];
In both cases,
 person1 and person2 are struct Person variables
 p[] is a struct Person array of size 20.

Access Members of a Structure


There are two types of operators used for accessing members of a structure.
1. . - Member operator
2. -> - Structure pointer operator (will be discussed in the next tutorial)
Suppose, you want to access the salary of person2. Here's how you can do it.
person2.salary

C structs
#include <stdio.h>
#include <string.h>

// create struct with person1 variable


struct Person {
char name[50];
int citNo;
float salary;
} person1;

int main() {

// assign value to name of person1


strcpy(person1.name, "George Orwell");

// assign values to other person1 variables


person1.citNo = 1984;
person1. salary = 2500;
// print struct variables
printf("Name: %s\n", person1.name);
printf("Citizenship No.: %d\n", person1.citNo);
printf("Salary: %.2f", person1.salary);
return 0;
}
Output
Name: George Orwell
Citizenship No.: 1984
Salary: 2500.00
In this program, we have created a struct named Person. We have also created a variable
of Person named person1.
In main(), we have assigned values to the variables defined in Person for the person1 object.

strcpy(person1.name, "George Orwell");


person1.citNo = 1984;
person1. salary = 2500;

Notice that we have used strcpy() function to assign the value to person1.name.
This is because name is a char array (C-string) and we cannot use the assignment
operator = with it after we have declared the string.
Finally, we printed the data of person1.

Nested Structures
You can create structures within a structure in C programming. For example,

struct complex {
int imag;
float real;
};

struct number {
struct complex comp;
int integers;
} num1, num2;

Suppose, you want to set imag of num2 variable to 11. Here's how you can do it:

num2.comp.imag = 11;

#include <stdio.h>

struct complex {
int imag;
float real;
};

struct number {
struct complex comp;
int integer;
} num1;

int main() {

// initialize complex variables


num1.comp.imag = 11;
num1.comp.real = 5.25;

// initialize number variable


num1.integer = 6;

// print struct variables


printf("Imaginary Part: %d\n", num1.comp.imag);
printf("Real Part: %.2f\n", num1.comp.real);
printf("Integer: %d", num1.integer);

return 0;
}
Output
Imaginary Part: 11
Real Part: 5.25
Integer: 6
Why structs in C?
Suppose you want to store information about a person: his/her name, citizenship number, and
salary. You can create different variables name, citNo and salary to store this information.
What if you need to store information of more than one person? Now, you need to create different
variables for each information per person name1, citNo1, salary1, name2, citNo2, salary2, etc.

A better approach would be to have a collection of all related information under a single
name Person structure and use it for every person.

Structure Pointer in C

A structure pointer is defined as the pointer which points to the address of the memory block
that stores a structure known as the structure pointer. Complex data structures like Linked lists,
trees, graphs, etc. are created with the help of structure pointers. The structure pointer tells the
address of a structure in memory by pointing the variable to the structure variable.
Example:

// C program to demonstrate structure pointer


#include <stdio.h>

struct point {
int value;
};

int main()
{

struct point s;

// Initialization of the structure pointer


struct point* ptr = &s;

return 0;
}In the above code s is an instance of struct point and ptr is the struct pointer because it is
storing the address of struct point.
Accessing the Structure Member with the Help of Pointers
There are two ways to access the members of the structure with the help of a structure pointer:
1. With the help of (*) asterisk or indirection operator and (.) dot operator.
2. With the help of ( -> ) Arrow operator.
Below is the program to access the structure members using the structure pointer with the help
of the dot operator.
// C Program to demonstrate Structure pointer
#include <stdio.h>
#include <string.h>

struct Student {
int roll_no;
char name[30];
char branch[40];
int batch;
};

int main()
{

struct Student s1;


struct Student* ptr = &s1;

s1.roll_no = 27;
strcpy(s1.name, "Kamlesh Joshi");
strcpy(s1.branch, "Computer Science And Engineering");
s1.batch = 2019;

printf("Roll Number: %d\n", (*ptr).roll_no);


printf("Name: %s\n", (*ptr).name);
printf("Branch: %s\n", (*ptr).branch);
printf("Batch: %d", (*ptr).batch);

return 0;
}

Output:
Roll Number: 27
Name: Kamlesh Joshi
Branch: Computer Science And Engineering
Batch: 2019

Below is the program to access the structure members using the structure pointer with the help
of the Arrow operator. In this program, we have created a Structure Student containing structure
variable s. The Structure Student has roll_no, name, branch, and batch.

// C Program to demonstrate Structure pointer


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

// Creating Structure Student


struct Student {
int roll_no;
char name[30];
char branch[40];
int batch;
};

// variable of structure with pointer defined


struct Student s, *ptr;

int main()
{

ptr = &s;
// Taking inputs
printf("Enter the Roll Number of Student\n");
scanf("%d", &ptr->roll_no);
printf("Enter Name of Student\n");
scanf("%s", &ptr->name);
printf("Enter Branch of Student\n");
scanf("%s", &ptr->branch);
printf("Enter batch of Student\n");
scanf("%d", &ptr->batch);

// Displaying details of the student


printf("\nStudent details are: \n");

printf("Roll No: %d\n", ptr->roll_no);


printf("Name: %s\n", ptr->name);
printf("Branch: %s\n", ptr->branch);
printf("Batch: %d\n", ptr->batch);

return 0;
}
Enter the Roll Number of Student
27
Enter Name of Student
Kamlesh_Joshi
Enter Branch of Student
Computer_Science_And_Engineering
Enter batch of Student
2019
Student details are:
Roll No: 27
Name: Kamlesh_Joshi
Branch: Computer_Science_And_Engineering
Batch: 2019

Example:
#include<stdio.h>

struct dog
{
char name[10];
char breed[10];
int age;
char color[10];
};

int main()
{
struct dog my_dog = {"tyke", "Bulldog", 5, "white"};
struct dog *ptr_dog;
ptr_dog = &my_dog;

printf("Dog's name: %s\n", ptr_dog->name);


printf("Dog's breed: %s\n", ptr_dog->breed);
printf("Dog's age: %d\n", ptr_dog->age);
printf("Dog's color: %s\n", ptr_dog->color);

// changing the name of dog from tyke to jack


strcpy(ptr_dog->name, "jack");

// increasing age of dog by 1 year


ptr_dog->age++;

printf("Dog's new name is: %s\n", ptr_dog->name);


printf("Dog's age is: %d\n", ptr_dog->age);

// signal to operating system program ran fine


return 0;
}
Expected Output:
Dog's name: tyke
Dog's breed: Bulldog
Dog's age: 5
Dog's color: white

After changes

Dog's new name is: jack


Dog's age is: 6

Self Referential structures

Self Referential structures are those structures that have one or more pointers which point to
the same type of structure, as their member.

In other words, structures pointing to the same type of structures are self-referential in nature
struct node {
int data1;
char data2;
struct node* link;
};
int main()
{
struct node ob;
return 0;
}
In the above example ‘link’ is a pointer to a structure of type ‘node’. Hence, the structure
‘node’ is a self-referential structure with ‘link’ as the referencing pointer.
An important point to consider is that the pointer should be initialized properly before
accessing, as by default it contains garbage value.
Types of Self Referential Structures
1. Self Referential Structure with Single Link
2. Self Referential Structure with Multiple Links
Self Referential Structure with Single Link: These structures can have only one self-pointer
as their member. The following example will show us how to connect the objects of a self-
referential structure with the single link and access the corresponding data members. The
connection formed is shown in the following figure.

#include <stdio.h>

struct node {
int data1;
char data2;
struct node* link;
};

int main()
{
struct node ob1; // Node1

// Initialization
ob1.link = NULL;
ob1.data1 = 10;
ob1.data2 = 20;

struct node ob2; // Node2

// Initialization
ob2.link = NULL;
ob2.data1 = 30;
ob2.data2 = 40;

// Linking ob1 and ob2


ob1.link = &ob2;

// Accessing data members of ob2 using ob1


printf("%d", ob1.link->data1);
printf("\n%d", ob1.link->data2);
return 0;
}
Output
30
40
Self Referential Structure with Multiple Links: Self referential structures with multiple
links can have more than one self-pointers. Many complicated data structures can be easily
constructed using these structures. Such structures can easily connect to more than one nodes
at a time. The following example shows one such structure with more than one links.
The connections made in the above example can be understood using the following figure.

#include <stdio.h>

struct node {
int data;
struct node* prev_link;
struct node* next_link;
};

int main()
{
struct node ob1; // Node1

// Initialization
ob1.prev_link = NULL;
ob1.next_link = NULL;
ob1.data = 10;

struct node ob2; // Node2

// Initialization
ob2.prev_link = NULL;
ob2.next_link = NULL;
ob2.data = 20;

struct node ob3; // Node3

// Initialization
ob3.prev_link = NULL;
ob3.next_link = NULL;
ob3.data = 30;

// Forward links
ob1.next_link = &ob2;
ob2.next_link = &ob3;

// Backward links
ob2.prev_link = &ob1;
ob3.prev_link = &ob2;

// Accessing data of ob1, ob2 and ob3 by ob1


printf("%d\t", ob1.data);
printf("%d\t", ob1.next_link->data);
printf("%d\n", ob1.next_link->next_link->data);

// Accessing data of ob1, ob2 and ob3 by ob2


printf("%d\t", ob2.prev_link->data);
printf("%d\t", ob2.data);
printf("%d\n", ob2.next_link->data);

// Accessing data of ob1, ob2 and ob3 by ob3


printf("%d\t", ob3.prev_link->prev_link->data);
printf("%d\t", ob3.prev_link->data);
printf("%d", ob3.data);
return 0;
}
Output
10 20 30
10 20 30
10 20 30

In the above example we can see that ‘ob1’, ‘ob2’ and ‘ob3’ are three objects of the self
referential structure ‘node’. And they are connected using their links in such a way that any of
them can easily access each other’s data. This is the beauty of the self referential structures.
The connections can be manipulated according to the requirements of the programmer.
Applications: Self-referential structures are very useful in creation of other complex data
structures like:
 Linked Lists
 Stacks
 Queues
 Trees
 Graphs etc.

Dynamic Memory Allocation in C using malloc(),


calloc(), free() and realloc()
Since C is a structured language, it has some fixed rules for programming. One of them includes changing
the size of an array. An array is a collection of items stored at contiguous memory locations.

As it can be seen that the length (size) of the array above made is 9. But what if there is a requirement to
change this length (size). For Example,
 If there is a situation where only 5 elements are needed to be entered in this array. In this case, the
remaining 4 indices are just wasting memory in this array. So there is a requirement to lessen the
length (size) of the array from 9 to 5.
 Take another situation. In this, there is an array of 9 elements with all 9 indices filled. But there is a
need to enter 3 more elements in this array. In this case, 3 indices more are required. So the length
(size) of the array needs to be changed from 9 to 12.
This procedure is referred to as Dynamic Memory Allocation in C.

Therefore, C Dynamic Memory Allocation can be defined as a procedure in which the size of a data
structure (like Array) is changed during the runtime. C provides some functions to achieve these tasks.
There are 4 library functions provided by C defined under <stdlib.h> header file to facilitate dynamic
memory allocation in C programming. They are:
1. malloc()
2. calloc()
3. free()
4. realloc()
1. malloc() method

The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large
block of memory with the specified size. It returns a pointer of type void which can be cast into a
pointer of any form. It doesn’t initialize memory at execution time so that it has initialized each block with
the default garbage value initially.

Syntax of malloc() in C

ptr = (cast-type*) malloc(byte-size)


void *malloc(size_t size);
For Example:
ptr = (int*) malloc(100 * sizeof(int));
Since the size of int is 4 bytes, this statement will allocate 400 bytes of memory. And, the pointer ptr holds
the address of the first byte in the allocated memory.

If space is insufficient, allocation fails and returns a NULL pointer.

Example of malloc() in C

#include <stdio.h>
#include <stdlib.h>

int main()
{

// This pointer will hold the


// base address of the block created
int* ptr;
int n, i;

// Get the number of elements for the array


printf("Enter number of elements:");
scanf("%d",&n);
printf("Entered number of elements: %d\n", n);

// Dynamically allocate memory using malloc()


ptr = (int*)malloc(n * sizeof(int));

// Check if the memory has been successfully


// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {

// Memory has been successfully allocated


printf("Memory successfully allocated using malloc.\n");

// Get the elements of the array


for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}

// Print the elements of the array


printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}

return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using malloc.
The elements of the array are: 1, 2, 3, 4, 5,
2. C calloc() method
1. “calloc” or “contiguous allocation” method in C is used to dynamically allocate the specified number
of blocks of memory of the specified type. it is very much similar to malloc() but has two different
points and these are:
2. It initializes each block with a default value ‘0’.
3. It has two parameters or arguments as compare to malloc().
Syntax of calloc() in C

ptr = (cast-type*)calloc(n, element-size);


here, n is the no. of elements and element-size is the size of each element.
For Example:
ptr = (float*) calloc(25, sizeof(float));
This statement allocates contiguous space in memory for 25 elements each with the size of the float.

If space is insufficient, allocation fails and returns a NULL pointer.

Example of calloc() in C

#include <stdio.h>
#include <stdlib.h>

int main()
{

// This pointer will hold the


// base address of the block created
int* ptr;
int n, i;

// Get the number of elements for the array


n = 5;
printf("Enter number of elements: %d\n", n);

// Dynamically allocate memory using calloc()


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

// Check if the memory has been successfully


// allocated by calloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {

// Memory has been successfully allocated


printf("Memory successfully allocated using calloc.\n");

// Get the elements of the array


for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}

// Print the elements of the array


printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}

return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,

What is malloc()?
The malloc is also known as the memory allocation function. malloc() dynamically allocates a
large block of memory with a specific size. It returns a void type pointer and is cast into any
form.

What is calloc()?
The calloc() function allocates a specific amount of memory and initializes it to zero. The
function can be cast to the desired type when it returns to a void pointer to the memory
location.
Difference between malloc() and calloc()

S.No. malloc() calloc()

1. malloc() function creates a single calloc() function assigns multiple


block of memory of a specific size. blocks of memory to a single variable.

2. The number of arguments in The number of arguments in calloc()


malloc() is 1. is 2.

3. malloc() is faster. calloc() is slower.

4. malloc() has high time efficiency. calloc() has low time efficiency.

5. The memory block allocated by The memory block allocated by


malloc() has a garbage value. calloc() is initialized by zero.

6. malloc() indicates memory calloc() indicates contiguous


allocation. allocation.

C free() method
“free” method in C is used to dynamically de-allocate the memory. The memory allocated using functions
malloc() and calloc() is not de-allocated on their own. Hence the free() method is used, whenever the
dynamic memory allocation takes place. It helps to reduce wastage of memory by freeing it.

Syntax of free() in C

free(ptr);
#include <stdio.h>
#include <stdlib.h>

int main()
{

// This pointer will hold the


// base address of the block created
int *ptr, *ptr1;
int n, i;

// Get the number of elements for the array


n = 5;
printf("Enter number of elements: %d\n", n);

// Dynamically allocate memory using malloc()


ptr = (int*)malloc(n * sizeof(int));

// Dynamically allocate memory using calloc()


ptr1 = (int*)calloc(n, sizeof(int));

// Check if the memory has been successfully


// allocated by malloc or not
if (ptr == NULL || ptr1 == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {

// Memory has been successfully allocated


printf("Memory successfully allocated using malloc.\n");

// Free the memory


free(ptr);
printf("Malloc Memory successfully freed.\n");

// Memory has been successfully allocated


printf("\nMemory successfully allocated using calloc.\n");

// Free the memory


free(ptr1);
printf("Calloc Memory successfully freed.\n");
}

return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using malloc.
Malloc Memory successfully freed.

Memory successfully allocated using calloc.


Calloc Memory successfully freed.

C realloc() method
“realloc” or “re-allocation” method in C is used to dynamically change the memory allocation of a
previously allocated memory. In other words, if the memory previously allocated with the help of malloc or
calloc is insufficient, realloc can be used to dynamically re-allocate memory. re-allocation of memory
maintains the already present value and new blocks will be initialized with the default garbage value.

Syntax of realloc() in C

ptr = realloc(ptr, newSize);

where ptr is reallocated with new size 'newSize'.

If space is insufficient, allocation fails and returns a NULL pointer.


#include <stdio.h>
#include <stdlib.h>
int main()
{

// This pointer will hold the


// base address of the block created
int* ptr;
int n, i;

// Get the number of elements for the array


n = 5;
printf("Enter number of elements: %d\n", n);

// Dynamically allocate memory using calloc()


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

// Check if the memory has been successfully


// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {

// Memory has been successfully allocated


printf("Memory successfully allocated using calloc.\n");

// Get the elements of the array


for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}

// Print the elements of the array


printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}

// Get the new size for the array


n = 10;
printf("\n\nEnter the new size of the array: %d\n", n);

// Dynamically re-allocate memory using realloc()


ptr = realloc(ptr, n * sizeof(int));

// Memory has been successfully allocated


printf("Memory successfully re-allocated using realloc.\n");
// Get the new elements of the array
for (i = 5; i < n; ++i) {
ptr[i] = i + 1;
}

// Print the elements of the array


printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}

free(ptr);
}

return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,

Enter the new size of the array: 10


Memory successfully re-allocated using realloc.
The elements of the array are: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,

Write a C program for multiplication of two matrices using dynamic memory allocation
/*Matrix multiplication using dynamic memory allocation*/
#include <stdio.h>
#include<stdlib.h>
/* Main Function */
int main()
{
/* Declaring pointer for matrix multiplication.*/
int **ptr1, **ptr2, **ptr3;
/* Declaring integer variables for row and columns of two matrices.*/
int row1, col1, row2, col2;
/* Declaring indexes. */
int i, j, k;
/* Request the user to input number of columns of the matrices.*/
printf("\nEnter number of rows for first matrix : ");
scanf("%d", &row1);
printf("\nEnter number of columns for first matrix : ");
scanf("%d", &col1);
printf("\nEnter number of rows for second matrix : ");
scanf("%d", &row2);
printf("\nEnter number of columns for second matrix : ");
scanf("%d", &col2);
if(col1 != row2)
{
printf("\nCannot multiply two matrices.");
return(0);
}
/* Allocating memory for the matrix rows. */
ptr1 = (int **) malloc(sizeof(int *) * row1);
ptr2 = (int **) malloc(sizeof(int *) * row2);
ptr3 = (int **) malloc(sizeof(int *) * row1);
/* Allocating memory for the col of the matrices. */
for(i=0; i<row1; i++)
ptr1[i] = (int *)malloc(sizeof(int) * col1);
for(i=0; i<row2; i++)
ptr2[i] = (int *)malloc(sizeof(int) * col2);
for(i=0; i<row1; i++)
ptr3[i] = (int *)malloc(sizeof(int) * col2);
/* Request the user to input members of first matrix. */
printf("\nEnter elements of first matrix :\n");
for(i=0; i< row1; i++)
{
for(j=0; j< col1; j++)
{
printf("\tA[%d][%d] = ",i, j);
scanf("%d", &ptr1[i][j]);
}
}
/* request to user to input members of second matrix. */
printf("\nEnter elements of second matrix :\n");
for(i=0; i< row2; i++)
{
for(j=0; j< col2; j++)
{
printf("\tB[%d][%d] = ",i, j);
scanf("%d", &ptr2[i][j]);
}
}
/* Calculation begins for the resultant matrix. */
for(i=0; i < row1; i++)
{
for(j=0; j < col1; j++)
{
ptr3[i][j] = 0;
for(k=0; k<col2; k++)
ptr3[i][j] = ptr3[i][j] + ptr1[i][k] * ptr2[k][j];
}
}
/* Printing the contents of third matrix. */
printf("\n\nResultant matrix :");
for(i=0; i< row1; i++)
{
printf("\n\t\t\t");
for(j=0; j < col2; j++)
printf("%d\t", ptr3[i][j]);
}
return 0;
}

OUTPUT

Enter number of rows for first matrix : 3

Enter number of columns for first matrix : 3

Enter number of rows for second matrix : 3

Enter number of columns for second matrix : 3

Enter elements of first matrix :


A[0][0] = 1
A[0][1] = 1
A[0][2] = 1
A[1][0] = 1
A[1][1] = 1
A[1][2] = 1
A[2][0] = 1
A[2][1] = 1
A[2][2] = 1

Enter elements of second matrix :


B[0][0] = 1
B[0][1] = 1
B[0][2] = 1
B[1][0] = 1
B[1][1] = 1
B[1][2] = 1
B[2][0] = 1
B[2][1] = 1
B[2][2] = 1

Resultant matrix :
3 3 3
3 3 3
3 3 3

What is Data Structure?


A data structure is a storage that is used to store and organize data. It is a way of arranging data on a computer so that it
can be accessed and updated efficiently. A data structure is not only used for organizing the data. It is also used for
processing, retrieving, and storing data. There are different basic and advanced types of data structures that are used in
almost every program or software system that has been developed.

Classification of Data Structure:

Classification of Data Structure

 Linear data structure: Data structure in which data elements are arranged sequentially or linearly, where
each element is attached to its previous and next adjacent elements, is called a linear data structure.
Examples of linear data structures are array, stack, queue, linked list, etc.
 Static data structure: Static data structure has a fixed memory size. It is easier to access the elements in
a static data structure.
An example of this data structure is an array.
 Dynamic data structure: In dynamic data structure, the size is not fixed. It can be randomly updated
during the runtime which may be considered efficient concerning the memory (space) complexity of the
code.
Examples of this data structure are queue, stack, etc.
 Non-linear data structure: Data structures where data elements are not placed sequentially or linearly are
called non-linear data structures. In a non-linear data structure, we can’t traverse all the elements in a single
run only.
Examples of non-linear data structures are trees and graphs.
For example, we can store a list of items having the same data-type using the array data structure.
Array Data Structure

Abstract Data Types


Data types such as int, float, double, long, etc. are considered to be in-built data types and we can
perform basic operations with them such as addition, subtraction, division, multiplication, etc. Now there
might be a situation when we need operations for our user-defined data type which have to be defined.
These operations can be defined only as and when we require them. So, in order to simplify the process of
solving problems, we can create data structures along with their operations, and such data structures
that are not in-built are known as Abstract Data Type (ADT).
Abstract Data type (ADT) is a type (or class) for objects whose behavior is defined by a set of
values and a set of operations. The definition of ADT only mentions what operations are to be performed
but not how these operations will be implemented. It does not specify how data will be organized in
memory and what algorithms will be used for implementing the operations. It is called “abstract” because it
gives an implementation-independent view.
The process of providing only the essentials and hiding the details is known as abstraction.

Think of ADT as a black box which hides the inner structure and design of the data type. Now we’ll define
three ADTs namely List ADT, Stack ADT, Queue ADT.
List ADT

Lists are linear data structures that hold data in a non-continuous structure. The list is made up of data
storage containers known as "nodes." These nodes are linked to one another, which mean that each node
contains the address of another block. All of the nodes are thus connected to one another via these links.
You can discover more about lists in this article: Linked List Data Structure.

Some of the most essential operations defined in List ADT are listed below.

 front(): returns the value of the node present at the front of the list.

 back(): returns the value of the node present at the back of the list.

 push_front(int val): creates a pointer with value = val and keeps this pointer to the front of the linked list.

 push_back(int val): creates a pointer with value = val and keeps this pointer to the back of the linked list.

 pop_front(): removes the front node from the list.

 pop_back(): removes the last node from the list.

 empty(): returns true if the list is empty, otherwise returns false.

 size(): returns the number of nodes that are present in the list.

Stack ADT

A stack is a linear data structure that only allows data to be accessed from the top. It simply has two
operations: push (to insert data to the top of the stack) and pop (to remove data from the stack). (used to
remove data from the stack top).
Some of the most essential operations defined in Stack ADT are listed below.

 top(): returns the value of the node present at the top of the stack.

 push(int val): creates a node with value = val and puts it at the stack top.

 pop(): removes the node from the top of the stack.

 empty(): returns true if the stack is empty, otherwise returns false.

 size(): returns the number of nodes that are present in the stack.

Queue ADT

A queue is a linear data structure that allows data to be accessed from both ends. There are two main
operations in the queue: push (this operation inserts data to the back of the queue) and pop (this operation is
used to remove data from the front of the queue).

Some of the most essential operations defined in Queue ADT are listed below.
Circular Queue

In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue except that the
last element of the queue is connected to the first element. It is also known as Ring Buffer, as all the ends
are connected to another end. The representation of circular queue is shown in the below image -

Priority Queue

It is a special type of queue in which the elements are arranged based on the priority. It is a special type of
queue data structure in which every element has a priority associated with it. Suppose some elements occur
with the same priority, they will be arranged according to the FIFO principle. The representation of priority
queue is shown in the below image -

Insertion in priority queue takes place based on the arrival, while deletion in the priority queue occurs based
on the priority. Priority queue is mainly used to implement the CPU scheduling algorithms.

Deque (or, Double Ended Queue)

In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the queue either
from the front or rear. It means that we can insert and delete elements from both front and rear ends of the
queue. Deque can be used as a palindrome checker means that if we read the string from both ends, then the
string would be the same.
Deque can be used both as stack and queue as it allows the insertion and deletion operations on both ends.
Deque can be considered as stack because stack follows the LIFO (Last In First Out) principle in which
insertion and deletion both can be performed only from one end. And in deque, it is possible to perform both
insertion and deletion from one end, and Deque does not follow the FIFO principle.

The representation of the deque is shown in the below image -

The fundamental operations that can be performed on queue are listed as follows -

 enqueue() – Insertion of elements to the queue.


 dequeue() – Removal of elements from the queue.
 peek() or front()- Acquires the data element available at the front node of the queue without deleting it.
 rear() – This operation returns the element at the rear end without removing it.
 isFull() – Validates if the queue is full.
 isEmpty() – Checks if the queue is empty.
 size(): This operation returns the size of the queue i.e. the total number of elements it contains.

Advantages of ADT in Data Structures

The advantages of ADT in Data Structures are:

 Provides abstraction, which simplifies the complexity of the data structure and allows users to focus on the
functionality.

 Enhances program modularity by allowing the data structure implementation to be separate from the rest of
the program.

 Enables code reusability as the same data structure can be used in multiple programs with the same
interface.

 Promotes the concept of data hiding by encapsulating data and operations into a single unit, which enhances
security and control over the data.

 Supports polymorphism, which allows the same interface to be used with different underlying data
structures, providing flexibility and adaptability to changing requirements.

Disadvantages of ADT in Data Structures

There are some potential disadvantages of ADT in Data Structures:


 Overhead: Using ADTs may result in additional overhead due to the need for abstraction and
encapsulation.

 Limited control: ADTs can limit the level of control that a programmer has over the data structure, which
can be a disadvantage in certain scenarios.

 Performance impact: Depending on the specific implementation, the performance of an ADT may be
lower than that of a custom data structure designed for a specific application.

Conclusion
In conclusion, Abstract Data Types in data structures is a fundamental concept that provides a high level of
abstraction and encapsulation for data and operations. Understanding and using ADTs can help
programmers develop more efficient and maintainable code that is better suited to changing requirements
and scalability needs.

Basic Operations in the Arrays


The basic operations in the Arrays are insertion, deletion, searching, display, traverse, and update. These
operations are usually performed to either modify the data in the array or to report the status of the array.
Following are the basic operations supported by an array.
 Traverse − print all the array elements one by one.
 Insertion − Adds an element at the given index.
 Deletion − Deletes an element at the given index.
 Search − Searches an element using the given index or by the value.
 Update − Updates an element at the given index.
 Display − Displays the contents of the array.
Linked List Representation
Linked list can be visualized as a chain of nodes, where every node points to the next node.
As per the above illustration, following are the important points to be considered.
 Linked List contains a link element called first (head).
 Each link carries a data field(s) and a link field called next.
 Each link is linked with its next link using its next link.
 Last link carries a link as null to mark the end of the list.
Types of Linked List
Following are the various types of linked list.

Singly Linked Lists


Singly linked lists contain two “buckets” in one node; one bucket holds the data and the other bucket
holds the address of the next node of the list. Traversals can be done in one direction only as there is
only a single link between two nodes of the same list.

Doubly Linked Lists


Doubly Linked Lists contain three “buckets” in one node; one bucket holds the data and the other buckets
hold the addresses of the previous and next nodes in the list. The list is traversed twice as the nodes in
the list are connected to each other from both sides.

Circular Linked Lists


Circular linked lists can exist in both singly linked list and doubly linked list.
Since the last node and the first node of the circular linked list are connected, the traversal in this linked
list will go on forever until it is broken.

Basic Operations in the Linked Lists


The basic operations in the linked lists are insertion, deletion, searching, display, and deleting an element
at a given key. These operations are performed on Singly Linked Lists as given below −
 Insertion − Adds an element at the beginning of the list.
 Deletion − Deletes an element at the beginning of the list.
 Display − Displays the complete list.
 Search − Searches an element using the given key.
 Delete − Deletes an element using the given key.
 Insertion Operation
 Adding a new node in linked list is a more than one step activity. We shall learn this with
diagrams here. First, create a node using the same structure and find the location where it has to
be inserted.


 Imagine that we are inserting a node B (NewNode), between A (LeftNode) and C (RightNode).
Then point B.next to C −
 NewNode.next −> RightNode;
 It should look like this −

 Now, the next node at the left should point to the new node.
 LeftNode.next −> NewNode;

Deletion Operation
Deletion is also a more than one step process. We shall learn with pictorial representation. First, locate
the target node to be removed, by using searching algorithms.
The left (previous) node of the target node now should point to the next node of the target node −
LeftNode.next −> TargetNode.next;

This will remove the link that was pointing to the target node. Now, using the following code, we will
remove what the target node is pointing at.
TargetNode.next −> NULL;

We need to use the deleted node. We can keep that in memory otherwise we can simply deallocate
memory and wipe off the target node completely.

Stack Representation
A Stack ADT allows all data operations at one end only. At any given time, we can only access the top
element of a stack.
The following diagram depicts a stack and its operations −
A stack can be implemented by means of Array, Structure, Pointer, and Linked List. Stack can either be a
fixed size one or it may have a sense of dynamic resizing. Here, we are going to implement stack using
arrays, which makes it a fixed size stack implementation.

Basic Operations on Stacks


Stack operations usually are performed for initialization, usage and, de-initialization of the stack ADT.
The most fundamental operations in the stack ADT include: push(), pop(), peek(), isFull(), isEmpty().
These are all built-in operations to carry out data manipulation and to check the status of the stack.
Stack uses pointers that always point to the topmost element within the stack, hence called as
the top pointer.

Insertion: push()
push() is an operation that inserts elements into the stack. The following is an algorithm that describes the
push() operation in a simpler way.

Algorithm
1 − Checks if the stack is full.
2 − If the stack is full, produces an error and exit.
3 − If the stack is not full, increments top to point next empty space.
4 − Adds data element to the stack location, where top is pointing.
5 − Returns success.
Deletion: pop()
pop() is a data manipulation operation which removes elements from the stack. The following pseudo
code describes the pop() operation in a simpler way.

Algorithm
1 − Checks if the stack is empty.
2 − If the stack is empty, produces an error and exit.
3 − If the stack is not empty, accesses the data element at which top is pointing.
4 − Decreases the value of top by 1.
5 − Returns success.

isFull()
isFull() operation checks whether the stack is full. This operation is used to check the status of the stack
with the help of top pointer.
isEmpty()
The isEmpty() operation verifies whether the stack is empty. This operation is used to check the status of the
stack with the help of top pointer.

https://www.tutorialspoint.com/data_structures_algorithms/stack_algorithm.htm

Operations on different Data Structure:


There are different types of operations that can be performed for the manipulation of data in every data
structure. Some operations are explained and illustrated below:
 Traversing: Traversing a Data Structure means to visit the element stored in it. It visits data in a
systematic manner. This can be done with any type of DS.
Below is the program to illustrate traversal in an array, stack, queue and linkedlist:
// C++ program to traversal in an array
#include <bits/stdc++.h>
using namespace std;

// Driver Code
int main()
{
// Initialise array
int arr[] = { 1, 2, 3, 4 };

// size of array
int N = sizeof(arr)/sizeof(arr[0]);

// Traverse the element of arr[]


for (int i = 0; i < N; i++) {

// Print the element


Printf(“%d”, arr[i] );
}
return 0;
}
Output
1 2 3 4
Searching:
Searching means to find a particular element in the given data-structure. It is considered as successful when
the required element is found. Searching is the operation which we can performed on data-structures like
array, linked-list, tree, graph, etc.
Below is the program to illustrate searching an element in an array, stack, queue and linkedlist:

Insertion: It is the operation which we apply on all the data-structures. Insertion means to add an element in
the given data structure. The operation of insertion is successful when the required element is added to the
required data-structure. It is unsuccessful in some cases when the size of the data structure is full and when
there is no space in the data-structure to add any additional element. The insertion has the same name as an
insertion in the data-structure as an array, linked-list, graph, tree. In stack, this operation is called Push. In
the queue, this operation is called Enqueue.

Deletion: It is the operation which we apply on all the data-structures. Deletion means to delete an element
in the given data structure. The operation of deletion is successful when the required element is deleted from
the data structure. The deletion has the same name as a deletion in the data-structure as an array, linked-list,
graph, tree, etc. In stack, this operation is called Pop. In Queue this operation is called Dequeue.
Sorting : Sorting is the process of arranging the elements of an array so that they can be placed either in
ascending or descending order.
Merging: List of two sorted data items can be combinedto form a sigle list of sorted data items.

Asymptotic Notation
Asymptotic Notations are the expressions that are used to represent the complexity of an
algorithm.
Asymptotic Notation is used to describe the running time of an algorithm - how much time an algorithm
takes with a given input, n. There are three different notations: big O, big Theta (Θ), and big Omega (Ω).
There are three types of analysis that we perform on a particular algorithm.
Best Case: In which we analyse the performance of an algorithm for the input, for which the
algorithm takes less time or space.

Worst Case: In which we analyse the performance of an algorithm for the input, for which the
algorithm takes long time or space.

Average Case: In which we analyse the performance of an algorithm for the input, for which the
algorithm takes time or space that lies between best and worst case.

Types of Data Structure Asymptotic Notation


1. Big-O Notation (Ο) – Big O notation specifically describes worst case scenario.
2. Omega Notation (Ω) – Omega(Ω) notation specifically describes best case scenario.
3. Theta Notation (θ) – This notation represents the average complexity of an algorithm.

Big Oh Notation, Ο
Big-O notation represents the upper bound of the running time of an algorithm. Thus, it
gives the worst-case complexity of an algorithm.
Big Omega Notation, Ω
Omega notation represents the lower bound of the running time of an algorithm. Thus, it
provides the best case complexity of an algorithm.
Theta Notation, θ
Theta notation encloses the function from above and below. Since it
represents the upper and the lower bound of the running time of an algorithm,
it is used for analyzing the average-case complexity of an algorithm.

You might also like