DS Basic, Array, Stack
DS Basic, Array, Stack
Examples of ADT
Linear ADTs
● Array
● Set
2. Characteristics of ADT
1. Abstraction
Only important details are shown.
Implementation details are hidden.
2. Encapsulation
Data and operations are combined together.
Data cannot be accessed directly.
3. Implementation Independent
Same ADT can be implemented using different methods.
Example: Stack using array or linked list.
4. Well-Defined Operations
Operations are clearly defined.
Example: Stack → push(), pop(), peek().
5. Reusability
ADTs can be reused in many programs.
Makes coding easier and faster.
6. Security
Data is protected because direct access is not allowed.
Core Operations of ADT
1. Create
o Creates a new instance of the ADT.
o Example: Creating an empty stack or list.
2. Insert
o Adds a new element to the ADT.
o Example: Pushing an element into a stack.
3. Delete
o Removes an element from the ADT.
o Example: Popping an element from a stack.
4. Search
o Finds a specific element in the ADT.
o Example: Searching a key in a list.
5. Access / Retrieve
o Gets an element without modifying it.
o Example: Peek operation in a stack.
6. Update / Modify
o Changes the value of an existing element.
o Example: Updating a record in a list.
7. Traverse
o Visits all elements one by one.
o Example: Displaying all elements of a queue.
8. Check Status
o Checks conditions like empty or full.
o Example: isEmpty(), isFull().
Algorithms
An algorithm is a well-defined, finite sequence of instructions designed to solve a
specific problem or perform a computation. Think of it as a recipe that transforms
input into output through a series of clear steps.
Characteristics of an Algorithm
1. Input
o An algorithm must take zero or more inputs.
o Example: Number n for factorial.
2. Output
o An algorithm must produce at least one output.
o Example: Factorial value.
3. Definiteness
o Each step must be clear, precise, and unambiguous.
o No confusion in instructions.
4. Finiteness
o The algorithm must end after a finite number of steps.
o It should not run forever.
5. Effectiveness
o Each step must be simple and executable.
o Steps should be basic and practical.
6. Correctness
o The algorithm should give the correct result for all valid inputs.
7. Generality
o It should work for all possible valid inputs, not just one case.
Property of Algorithm
Input
Output
Definiteness : clear instruction
Effectiveness : feasible
Finiteness : set of instructions
Example : Write a algorithm for addition of two number
1. Analysis of Algorithm
Analysis of an algorithm means studying how much time and memory (space) an
algorithm requires as the input size grows.
It helps to:
● Compare different algorithms
● Choose the most efficient one
● Predict performance before implementation
Types:
● Time Complexity – time taken by an algorithm
● Space Complexity – memory used by an algorithm
2. Asymptotic Notations
Asymptotic notations describe the performance of an algorithm for large input sizes
(n).
Why Asymptotic Notations are Needed
● To measure performance of algorithms
● To compare algorithms for large inputs
(a) Big-O Notation – O(n)
● Describes the worst-case time
● Upper bound of an algorithm
Example:
Linear search → O(n)
(b) Omega Notation – Ω(n)
● Describes the best-case time
● Lower bound of an algorithm
Example:
Best case of linear search → Ω(1)
(c) Theta Notation – Θ(n)
● Describes the average/exact case
● Tight bound
Example:
Binary search → Θ(log n)
3. Time and Space Trade-Off
A time–space trade-off means improving time at the cost of more space or saving
space at the cost of more time.
Example:
● Using hash tables:
o Faster search (less time)
o Requires extra memory (more space)
● Using linear search:
o Less memory
o More time
List ADT
The List ADT (Abstract Data Type) is a sequential collection of elements
It provides an ordered way to store, access, and modify data.
A List ADT is an abstract data type that represents a collection of elements
arranged in a linear order.
Lists are linear data structures stored in a non-continuous manner. The list is made
up of a series of connected nodes that are randomly stored in the memory. Here,
each node consists of two parts, the first part is the data and the second part
contains the pointer to the address of the next node.
if (isEmpty()) {
printf("List is empty.\n");
return;
}
if (position < 1 || position > count) {
printf("Invalid position.\n");
return;
}
for (i = position - 1; i < count - 1; i++) {
list[i] = list[i + 1];
}
count--;
printf("Element deleted.\n");
}
/* Retrieve element at given position */
void retrieve(int position) {
if (position < 1 || position > count) {
printf("Invalid position.\n");
return;
}
printf("Element at position %d is %d\n", position, list[position - 1]);
}
LOC(A[3]) = 1000 + 3 × 4
= 1000 + 12
A[3] = 1012 LOC(A[i]) = Base + i × size
Index 0 1 2 3 Size (in bytes) of one a
1 2 3
4 5 6
Stored as: 1 2 3 4 5 6
1 2 3
4 5 6
Stored as: 1 4 2 5 3 6
3. Dynamic Arrays
A dynamic array is an array whose size can change at runtime.
Features:
● Allocated in heap memory
● Size can grow or shrink
● Requires reallocation when full
Dynamic Arrays
Array-based Stack
Array-based Queue
How It Works
1. If enough space is available → memory is extended at same location.
int main() {
int *arr;
int n = 3;
// Increase size to 6
n = 6;
arr = (int*) realloc(arr, n * sizeof(int));
free(arr);
return 0;
}
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *a, *b, i;
free(a);
free(b);
return 0;
}
If
re
Th
Valu
mem
arr[i]
5. Advantages of Arrays
● Fast access using index
● Easy to implement
● Efficient memory usage for fixed-size data
6. Limitations of Arrays
● Fixed size (static arrays)
● Insertion and deletion are costly
● Memory wastage possible
1. Array ADT – Basic Example (1D Array)
#include <stdio.h>
int main() {
int marks[5] = {78, 85, 90, 66, 72};
int i;
printf("Student Marks:\n");
for(i = 0; i < 5; i++) {
printf("%d ", marks[i]);
}
return 0;
}
2. Row-Major Order Representation (2D Array)
#include <stdio.h>
int main() {
int a[2][3] = {{1,2,3},{4,5,6}};
int i, j;
printf("Row-major order:\n");
for(i = 0; i < 2; i++) {
for(j = 0; j < 3; j++) {
printf("%d ", a[i][j]);
}
}
return 0;
}
3. Column-Major Order
C does not support column-major storage directly, but we can access elements
column-wise.
#include <stdio.h>
int main() {
int a[2][3] = {{1,2,3},{4,5,6}};
int i, j;
printf("Column-major order:\n");
for(j = 0; j < 3; j++) {
for(i = 0; i < 2; i++) {
printf("%d ", a[i][j]);
}
}
return 0;
}
Dynamic Array Example
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr, n, i;
printf("Enter number of elements: ");
scanf("%d", &n);
arr = (int*)malloc(n * sizeof(int));
printf("Enter elements:\n");
for(i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
printf("Array elements are:\n");
for(i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
Real-Life Problem Implementation Using Array
Example: Calculate average temperature of a week
#include <stdio.h>
int main() {
float temp[7], sum = 0;
int i;
printf("Enter temperatures for 7 days:\n");
for(i = 0; i < 7; i++) {
scanf("%f", &temp[i]);
sum += temp[i];
}
printf("Average temperature = %.2f", sum / 7);
return 0;
}
Real-Life Example: Employee Salary System
#include <stdio.h>
int main() {
int salary[5], i;
int total = 0;
printf("Enter salaries of 5 employees:\n");
for(i = 0; i < 5; i++) {
scanf("%d", &salary[i]);
total += salary[i];
}
printf("Total salary = %d", total);
return 0;
}
Unit 2:
Stack Data Structure or Stack ADT
A stack is an ordered list or we can say a container in which insertion and deletion
can be done from the one end known as the top of the stack. The last inserted
element is available first and is the first one to be deleted. Hence, it is known as Last
In, First Out LIFO, or First In, Last Out FILO
A stack is called an abstract data type (ADT) because it defines a set of operations
(such as push and pop) and properties (such as Last-In-First-Out behaviour) without
specifying the implementation details.
A Stack is a linear data structure that follows a particular order in which the
operations are performed. The order may be LIFO(Last In First Out) or FILO(First In
Last Out). LIFO implies that the element that is inserted last, comes out first
and FILO implies that the element that is inserted first, comes out last.
It behaves like a stack of plates, where the last plate added is the first one to be
removed.
Pushing an element onto the stack is like adding a new plate on top
Popping an element removes the top plate from the stack.
LIFO(Last In First Out) Principle
The LIFO principle means that the last element added to a stack is the first one to be
removed.
● New elements are always pushed on top.
● Removal (pop) also happens only from the top.
● This ensures a strict order: last in → first out.
Basic Terminologies of Stack
● 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 else false.
● size(): returns the number of nodes present in the stack
Operations On Stack
The time complexity of all the given operations is constant, i.e. O(1).
Insertion: Push Operation:
Adds an item to the stack. If the stack is full, then it is said to be an
Overflow condition.
● Top in stack is a pointer variable
● Before pushing the element to the stack, we check if the stack is full.
● If the stack is full (top == capacity-1) , then Stack Overflows and we cannot
insert the element to the stack.
● Otherwise, we increment the value of top by 1 (top = top + 1) and the new
value is inserted at top position .
● The elements can be pushed into the stack till we reach the capacity of the
stack.
Algorithm: PUSH Operation (Stack using Array)
Step 1: Start
Step 2: Check if top == MAX – 1
If true, print “Stack Overflow” and go to Step 6
Step 3: Increment top by 1
Step 4: Insert the element at stack[top]
Step 5: Print “Element pushed successfully”
Step 6: Stop
return 0;
}
void push(int value) {
if (top == MAX - 1) {
printf("Stack Overflow\n");
} else {
top++;
stack[top] = value;
printf("%d pushed into stack\n", value);
}
}
Output
10 pushed into stack
20 pushed into stack
30 pushed into stack
40 pushed into stack
50 pushed into stack
Stack Overflow
Deletion: POP()
void pop() {
if (top == -1) {
printf("Stack Underflow\n");
} else {
printf("%d popped from stack\n", stack[top]);
top--;
}
}
int main() {
pop();
pop();
pop();
pop(); // Underflow condition
return 0;
}
30 popped from stack
20 popped from stack
10 popped from stack
Stack Underflow
Simple stack program using array in C both push and pop operation
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
/* Push operation */
void push(int value) {
if (top == MAX - 1) {
printf("Stack Overflow\n");
} else {
top++;
stack[top] = value;
printf("%d pushed into stack\n", value);
}
}
/* Pop operation */
void pop() {
if (top == -1) {
printf("Stack Underflow\n");
} else {
printf("%d popped from stack\n", stack[top]);
top--;
}
}
int main() {
push(10);
push(20);
push(30);
display();
pop();
display();
return 0;
}
10 pushed into stack
20 pushed into stack
30 pushed into stack
Stack elements are:
30
20
10
30 popped from stack
Stack elements are:
20
10
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
/* size function */
int size() {
return top + 1;
}
int main() {
printf("Size of stack: %d\n", size());
return 0;
}
peek()
peek() returns the top element of the stack without removing it.
The peek() operation returns the value of the topmost element of the stack without
modifying the stack. This can be useful when you need to check the value of the top
element before deciding whether to remove it or not.
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
/* push function */
void push(int value) {
if (top == MAX - 1) {
printf("Stack Overflow\n");
} else {
stack[++top] = value;
}
}
/* peek function */
void peek() {
if (top == -1) {
printf("Stack is empty\n");
} else {
printf("Top element is: %d\n", stack[top]);
}
}
int main() {
push(10);
push(20);
push(30);
peek(); // shows top element
return 0;
}
isFull()
It checks whether the stack is full or not.
The isFull() operation is used to determine if the stack is full or not. A stack is said to
be full if it has reached its maximum capacity and there is no more space to add new
elements to the stack.
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
/* isFull function */
int isFull() {
if (top == MAX - 1)
return 1; // stack is full
else
return 0; // stack is not full
}
int main() {
if (isFull())
printf("Stack is Full\n");
else
printf("Stack is Not Full\n");
return 0;
}
isEmpty()
The isEmpty() operation is used to check if the stack is empty or not. It returns a
boolean value, true when the stack is empty, otherwise false.
Algorithm for isEmpty() operation on the stack
begin
if top < 1
return true
else
return false
end procedure
int stack[MAX];
int top = -1;
/* isEmpty function */
int isEmpty() {
if (top == -1)
return 1; // stack is empty // condition satisfied
else
return 0; // stack is not empty
}
int main() {
if (isEmpty())
printf("Stack is Empty\n");
else
printf("Stack is Not Empty\n");
return 0;
}
#include <stdio.h>
#define MAX 5
/* initialize stack */
void init(Stack *s) {
s->top = -1;
}
/* push operation */
void push(Stack *s, int value) {
if (s->top == MAX - 1)
printf("Stack Overflow\n");
else
s->data[++s->top] = value;
}
/* pop operation */
int pop(Stack *s) {
if (s->top == -1) {
printf("Stack Underflow\n");
return -1;
}
return s->data[s->top--];
}
int main() {
Stack s;
init(&s);
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Popped element: %d\n", pop(&s));
return 0;
}
Stack ADT user define Structure
#define MAX 5
typedef struct {
int data[MAX];
int top;
} Stack;
PUSH Operation using Stack ADT
Algorithm
1. Check if stack is full (top == MAX - 1)
2. If full → print Stack Overflow
3. Else increment top
4. Insert element at data[top]
Program:
void push(Stack *s, int value) {
if (s->top == MAX - 1) {
printf("Stack Overflow\n");
} else {
s->top++;
s->data[s->top] = value;
printf("%d pushed into stack\n", value);
}
}
Main Function
int main() {
Stack s; (With typedef)
[Link] = -1;
push(&s, 10);
push(&s, 20);
push(&s, 30);
pop(&s);
pop(&s);
return 0;
}
10 pushed into stack
20 pushed into stack
30 pushed into stack
30 popped from stack
20 popped from stack
typedef struct {
int *data;
int top;
int capacity;
} Stack;
int main() {
Stack s;
[Link] = 3;
[Link] = -1;
if ([Link] == NULL) {
printf("Memory allocation failed\n");
return 0;
}
push(&s, 10);
push(&s, 20);
push(&s, 30);
push(&s, 40); // overflow
pop(&s);
pop(&s);
free([Link]);
return 0;
}
10 pushed into stack
20 pushed into stack
30 pushed into stack
Stack Overflow
30 popped from stack
20 popped from stack
Dynamic Stack using calloc()
Difference
● calloc() initializes memory to 0
● malloc() gives garbage values
Allocation line change only:
[Link] = (int *)calloc([Link], sizeof(int));
What is an Expression?
Types of Expressions
Type Example
Infix A+B
Postfix AB+
Prefix +AB
Rules
1. If operand → add to postfix
2. If ( → push to stack
3. If ) → pop until ( is found
4. Operator precedence:
*/>+-
Precedenc
Operator e
Parentheses () Highest
Exponents ^ High
Multiplication
Medium
*
Division / Medium
Mod % Medium
Addition + Low
Subtraction - Low
Example
Infix : A + B * C
Post fix : A B C * +
Role of Stack
● Stack temporarily stores operators
● Helps manage precedence and parentheses
Input: s = "a*(b+c)/d"
Output: abc+*d/
Input: s = "a+b*c/d"
Output: abc*+d/
Explanation: The expression a + b * c / d is converted by first doing b * c → bc*, then
adding a → abc*+, and finally adding d → abc*+d/.
Infix to Postfix and Prefix
1. a+b
2. a+b-c
3. a*b-c
4. a*b+c
5. a/b/c
6. a%b
7. a%b+c
8. a%b*c
9. a+b%c
10.a/b%c+d
11.a^b/c^d+e^f
12.a+(b-c)*d/e^f
13.((a-b)*c+d/e)
14.(a+b)*(c-d)
15.(a+b)/(c+d)-(d*e)
16.a-(b/c+(d%e*f)/g)*h
17.3+4*6
18.2+5/4-1*4
19.4-2+5*3/1
20.4-((4/2)*4-(8+1))
Step 2
Initialize:
● an empty stack for operators
● an empty postfix string
Step 3
Scan the infix expression from left to right, symbol by symbol.
Step 4
For each symbol:
a) If the symbol is an operand
Add it directly to the postfix expression.
b) If the symbol is (
Push it onto the stack.
c) If the symbol is )
Pop operators from the stack and add to postfix
Stop when ( is found
Remove ( from the stack.
Step 5
After scanning the entire infix expression,
Pop all remaining operators from the stack and add to postfix.
Step 6
The resulting string is the postfix expression.
#define MAX 50
//Stack and expressions maximum size 50 to set
char stack[MAX];
int top = -1;
//stack[MAX] → array to store operator
//top = -1 → stack empty
/* push operator */
void push(char ch) {
stack[++top] = ch;
}
//top=top+1;stack[top]=ch
//stack[top] = ch → push operator to stack top
// Example:
// if top = -1 then
// ++top = 0
// stack[0] = '+'
/* pop operator */
char pop() {
return stack[top--];
}
//stack[top] → remove top element from stack
// top-- → decrement top
//return popped operator
/* precedence function */
int precedence(char ch)
//to check the Operator priority {
if (ch == '^')
return 3;
if (ch == '*' || ch == '/' || ch == '%')
return 2;
if (ch == '+' || ch == '-')
return 1;
return 0;
}
/* check operand */
int isOperand(char ch)
//Check if operand or not {
if ((ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= '9'))
return 1;
return 0;
}
int main() {
char infix[MAX], postfix[MAX];
int i, j = 0;
//infix[] → user input expression
//postfix[] → converted expression
//i → infix scan
//j → postfix index
/* Operand */
if (isOperand(ch)) {
postfix[j++] = ch;
}
//if operand then push into postfix
/* Left parenthesis */
else if (ch == '(') {
push(ch);
}
//( → push in stack
/* Right parenthesis */
else if (ch == ')') {
while (top != -1 && stack[top] != '(')
postfix[j++] = pop();
pop(); // remove '('
}
// pop operator from stack
//till ( not find
// discard (
/* Operator */
else {
while (top != -1 &&
(precedence(stack[top]) > precedence(ch) ||
(precedence(stack[top]) == precedence(ch) && ch != '^')))
postfix[j++] = pop();
// when perform Pop
//when stack top precedence is greater
//or even same and operator is left associative then
// for ^ equal priority no pop is done
push(ch);
// push the Current operator to stack
}
}
postfix[j] = '\0';
// String end here
return 0;
//Successful execution
}
-------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#define MAX 50
char stack[MAX];
int top = -1;
/* Push into stack */
void push(char ch) {
top = top + 1;
stack[top] = ch;
}
/* Pop from stack */
char pop() {
char x = stack[top];
top = top - 1;
return x;
}
/* Operator precedence */
int precedence(char ch) {
if (ch == '+' || ch == '-')
return 1;
if (ch == '*' || ch == '/')
return
return 0;
}
/* Check operand */
int isOperand(char ch) {
if ((ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= '9'))
return 1;
return 0;
}
int main() {
char infix[MAX], postfix[MAX];
int i, j = 0;
printf("Enter infix expression: ");
scanf("%s", infix);
for (i = 0; infix[i] != '\0'; i++) {
char ch = infix[i];
if (isOperand(ch)) {
postfix[j++] = ch;
}
else if (ch == '(') {
push(ch);
}
else if (ch == ')') {
while (top != -1 && stack[top] != '(')
postfix[j++] = pop();
pop(); // remove '('
}
else { // operator
while (top != -1 && precedence(stack[top]) >= precedence(ch))
postfix[j++] = pop();
push(ch);
}
}
postfix[j] = '\0';
#define MAX 10
char stack[MAX];
int top = -1;
char pop() {
char x = stack[top];
top = top - 1;
return x;
}
int main() {
char infix[] = "a+b*c";
char postfix[MAX];
int i, j = 0;
char ch = infix[i];
Expression : 5 + 6 - 2 * 12 / 4
Scan Action Stack Postfix
5 Operand → output – 5
+ Push + 5
6 Operand → output + 56
Stack top + has equal precedence → pop +,
- - 56+
push -
2 Operand → output - 56+2
* Higher than - → push -* 56+2
12 Operand → output -* 56+212
/ Equal precedence with * → pop *, push / - / 56+212*
4 Operand → output -/ 56+212*4
End Pop remaining stack → pop / then - – 56+212*4/-
Final Answer is 5
Infix: 5 + 6 - 2 * 12 / 4
Postfix: 56+212*4/-
Evaluated Result: 5
=================================================================
Expression Conversion Using Stack (Polish Notation)
Algorithm:
Infix to Prefix Conversion ( 1 marks)
Step 1: Reverse the given infix expression.
While reversing, change ( to ) and ) to (.
Step 2: Convert the reversed infix expression into postfix expression using stack.
Step 3: Reverse the obtained postfix expression.
The result will be prefix expression.
char stack[100];
int top = -1;
char pop(){
return stack[top--];
}
int priority(char x){
if(x=='^') return 3;
if(x=='*' || x=='/') return 2;
if(x=='+' || x=='-') return 1;
return 0;
}
int main(){
char infix[100], postfix[100], prefix[100];
int i, k=0;
char stack[100];
int top = -1;
char pop(){
return stack[top--];
}
int main(){
char infix[100], postfix[100], prefix[100];
char rev[100];
int i, j=0, k=0;
// infix to postfix
for(i=0; rev[i]!='\0'; i++){
char ch = rev[i];
if((ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')){
postfix[k++]=ch;
}
else if(ch=='('){
push(ch);
}
else if(ch==')'){
while(stack[top]!='('){
postfix[k++]=pop();
}
pop();
}
else{
while(top!=-1 && priority(stack[top])>=priority(ch)){
postfix[k++]=pop();
}
push(ch);
}
}
while(top!=-1){
postfix[k++]=pop();
}
postfix[k]='\0';
return 0;
}