Introduction to Data Structures
What is a Data Structure?
A data structure is a way of organizing and storing data so that it can be accessed and
modified efficiently. It determines the way data is arranged in memory and how operations
such as retrieval, insertion, and deletion are performed.
Types of Data Structures
Data structures can be broadly classified into two types:
1. Primitive Data Structures: These are the basic building blocks for data
manipulation. Examples include:
o Integer
o Float
o Character
o Boolean
2. Non-Primitive Data Structures: These are more complex structures derived from
primitive types. They are further divided into:
o Linear Data Structures: Data elements are arranged sequentially. Examples:
Arrays
Linked Lists
Stacks
Queues
o Non-Linear Data Structures: Data elements are arranged hierarchically or
non-sequentially. Examples:
Trees
Graphs
o Hashing: Organizes data using hash functions for efficient searching.
Elementary Data Organization
Data: A collection of raw facts and figures.
Records: A group of related fields (e.g., a student’s record includes name, roll
number, and marks).
Fields: Individual pieces of data (e.g., a student’s name).
Files: A collection of records stored on a disk.
Data Structure Operations
Common operations performed on data structures include:
1. Insertion: Adding a new element to the data structure.
2. Deletion: Removing an existing element from the data structure.
3. Traversal: Accessing each element of the data structure exactly once to process it.
4. Searching: Finding the location of an element in the data structure.
5. Sorting: Arranging elements in a specific order (e.g., ascending or descending).
Analysis of an Algorithm
The efficiency of an algorithm is determined by analyzing its:
1. Time Complexity: The amount of time taken to complete the algorithm as a function
of the input size.
2. Space Complexity: The amount of memory required to execute the algorithm.
Asymptotic Notations
Big-O Notation (O): Represents the worst-case time complexity.
Omega Notation (Ω): Represents the best-case time complexity.
Theta Notation (Θ): Represents the average-case time complexity.
Time-Space Trade-off
There is often a trade-off between time and space in algorithms. For instance:
Time-efficient algorithms may require more memory.
Space-efficient algorithms may take more time to execute.
Searching Algorithms
Linear Search
Definition: A simple method that checks every element in the array sequentially until
the desired element is found.
Time Complexity: O(n)
Algorithm
void linearSearch(int arr[], int n, int key) {
for (int i = 0; i < n; i++) {
if (arr[i] == key) {
printf("Element found at index %d\n", i);
return;
}
}
printf("Element not found\n");
}
Binary Search
Definition: A more efficient method that works on sorted arrays by repeatedly
dividing the search interval in half.
Time Complexity: O(log n)
Algorithm
void binarySearch(int arr[], int n, int key) {
int low = 0, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key) {
printf("Element found at index %d\n", mid);
return;
} else if (arr[mid] < key) {
low = mid + 1;
} else {
high = mid - 1;
}
}
printf("Element not found\n");
}
Summary
Data structures help in organizing and storing data efficiently.
Operations like insertion, deletion, and traversal are essential.
Algorithm analysis uses asymptotic notations to measure efficiency.
Searching algorithms like linear and binary search have different use cases and
complexities.
1. Array
Insertion:
c
Copy code
#include <stdio.h>
void insert(int arr[], int *n, int value, int index) {
for (int i = *n; i > index; i--) {
arr[i] = arr[i - 1];
}
arr[index] = value;
(*n)++;
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[10] = {1, 2, 3, 4, 5};
int n = 5;
insert(arr, &n, 99, 2); // Insert 99 at index 2
printArray(arr, n); // Print array
return 0;
}
Deletion:
c
Copy code
#include <stdio.h>
void delete(int arr[], int *n, int index) {
for (int i = index; i < *n - 1; i++) {
arr[i] = arr[i + 1];
}
(*n)--;
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
delete(arr, &n, 2); // Delete element at index 2
printArray(arr, n); // Print array
return 0;
}
Traversal:
c
Copy code
#include <stdio.h>
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
printArray(arr, n); // Print array
return 0;
}
2. Stack
Insertion (Push) & Traversal:
c
Copy code
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
void push(int value) {
if (top == MAX - 1) {
printf("Stack Overflow\n");
} else {
stack[++top] = value;
}
}
void traverse() {
if (top == -1) {
printf("Stack is empty\n");
} else {
for (int i = top; i >= 0; i--) {
printf("%d ", stack[i]);
}
printf("\n");
}
}
int main() {
push(10);
push(20);
push(30);
traverse(); // Print stack
return 0;
}
Deletion (Pop):
c
Copy code
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
int pop() {
if (top == -1) {
printf("Stack Underflow\n");
return -1;
} else {
return stack[top--];
}
}
int main() {
push(10);
push(20);
push(30);
printf("Popped: %d\n", pop()); // Remove top element
return 0;
}
3. Queue
Insertion (Enqueue) & Traversal:
c
Copy code
#include <stdio.h>
#define MAX 5
int queue[MAX];
int front = -1, rear = -1;
void enqueue(int value) {
if (rear == MAX - 1) {
printf("Queue Overflow\n");
} else {
if (front == -1) {
front = 0;
}
queue[++rear] = value;
}
}
void traverse() {
if (front == -1) {
printf("Queue is empty\n");
} else {
for (int i = front; i <= rear; i++) {
printf("%d ", queue[i]);
}
printf("\n");
}
}
int main() {
enqueue(10);
enqueue(20);
enqueue(30);
traverse(); // Print queue
return 0;
}
Deletion (Dequeue):
c
Copy code
#include <stdio.h>
#define MAX 5
int queue[MAX];
int front = -1, rear = -1;
int dequeue() {
if (front == -1) {
printf("Queue Underflow\n");
return -1;
} else {
int value = queue[front++];
if (front > rear) {
front = rear = -1;
}
return value;
}
}
int main() {
enqueue(10);
enqueue(20);
enqueue(30);
printf("Dequeued: %d\n", dequeue()); // Remove front element
return 0;
}
4. Linked List
Insertion & Traversal:
c
Copy code
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void insert(struct Node** head, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = *head;
*head = newNode;
}
void traverse(struct Node* head) {
struct Node* temp = head;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
int main() {
struct Node* head = NULL;
insert(&head, 10);
insert(&head, 20);
insert(&head, 30);
traverse(head); // Print list
return 0;
}
Deletion:
c
Copy code
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void delete(struct Node** head, int value) {
struct Node* temp = *head, *prev = NULL;
if (temp != NULL && temp->data == value) {
*head = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->data != value) {
prev = temp;
temp = temp->next;
}
if (temp == NULL) return;
prev->next = temp->next;
free(temp);
}
void traverse(struct Node* head) {
struct Node* temp = head;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
int main() {
struct Node* head = NULL;
insert(&head, 10);
insert(&head, 20);
insert(&head, 30);
traverse(head); // Print list
delete(&head, 20); // Delete element 20
traverse(head); // Print list after deletion
return 0;
}