Data Structures: Unit-I
Data Structures: Unit-I
Data Structures
Definition:
A data structure is a specific way of organizing, storing, and managing data in a computer so
that it can be efficiently accessed and modified. It provides a framework to organize data
based on the operations and algorithms applied to it.
Uses of Data Structures:
1. Efficient Data Management: Allows data to be stored and retrieved efficiently.
2. Algorithm Implementation: Many algorithms depend on data structures to process and
manipulate data.
3. Memory Optimization: Helps in the efficient use of memory space.
4. Real-world Applications: Powers applications like databases, operating systems,
search engines, and networks.
5. Problem Solving: Provides tools for solving complex problems by structuring the
problem data.
Types of Data Structures:
Data structures are broadly classified into two categories:
c. Hashing
Uses a hash function to map keys to values.
Uses: Indexing, caches, associative arrays.
class Stack {
private:
int* arr; // Pointer to stack array
int top; // Index of the top element
int capacity; // Maximum size of the stack
public:
// Constructor
Stack(int size) {
arr = new int[size];
capacity = size;
top = -1;
}
// Destructor
~Stack() {
delete[] arr;
}
s.push(10);
s.push(20);
s.push(30);
cout << "Top element is: " << s.peek() << endl;
s.push(40);
while (!s.isEmpty()) {
cout << "Popped element: " << s.pop() << endl;
}
return 0;
}
Explanation:
1. Private Section:
o Encapsulates data (arr, top, capacity) to protect it from unauthorized access.
2. Public Section:
o Defines the interface for stack operations (push, pop, peek, etc.).
3. Implementation Independence:
o Users of the Stack class interact with its interface, unaware of the underlying
implementation (array-based in this case).
Array as an Abstract Data Type (ADT):
An array is a fundamental Abstract Data Type (ADT) that represents a
collection of elements, where each element is identified by an index or
subscript. The array ADT specifies the operations that can be performed on the
array but does not dictate the specific implementation.
Array ADT Definition:
Data: A collection of elements of the same data type stored in a
contiguous block of memory.
Operations:
1. Access an element by its index.
2. Update an element at a specific index.
3. Traverse through all elements.
4. Search for an element.
5. Insert or delete elements (though this is not efficient in static
arrays).
class Array {
private:
int* arr; // Pointer to the array
int size; // Current number of elements
int capacity; // Maximum capacity of the array
public:
// Constructor
Array(int capacity) {
this->capacity = capacity;
size = 0;
arr = new int[capacity];
}
// Destructor
~Array() {
delete[] arr;
}
// Example usage
int main() {
Array arr(5);
arr.insert(10);
arr.insert(20);
arr.insert(30);
arr.removeAt(1);
return 0;
}
Explanation of Implementation:
1. Encapsulation:
o Data (arr, size, capacity) is private to prevent direct modification.
o Public methods (insert, get, removeAt) define the interface.
2. Array Operations:
o get(index): Access an element.
o insert(value): Add an element (if space is available).
o removeAt(index): Delete an element and shift the rest.
3. Dynamic Behavior:
o Though the class uses a fixed-size array, dynamic resizing could be
implemented by allocating a larger array when the current one is
full.
2. Matrices
A matrix is a 2D array where elements are arranged in rows and columns.
Representation:
Row-Major Order: Store elements row by row.
o Example: Matrix [1234]\begin{bmatrix} 1 & 2 \\ 3 & 4 \
end{bmatrix}[1324]
Stored as: [1, 2, 3, 4]
Address of (a[i][j] = \text{Base Address} + [(i \times \
text{Number of Columns}) + j] \times \text{Element Size}`.
Column-Major Order: Store elements column by column.
o Stored as: [1, 3, 2, 4]
o Address of (a[i][j] = \text{Base Address} + [(j \times \text{Number
of Rows}) + i] \times \text{Element Size}`.
3. Special Matrices
These are matrices with specific patterns or properties that allow optimized
storage.
Types of Special Matrices:
1. Diagonal Matrix:
o Only the diagonal elements are non-zero.
o Example:
text
Copy code
1 0 0
0 2 0
0 0 3
o Storage: Store only diagonal elements in a 1D array: [1, 2, 3].
2. Triangular Matrix:
o Upper Triangular: Elements below the main diagonal are zero.
o Lower Triangular: Elements above the main diagonal are zero.
o Storage: Store non-zero elements in a 1D array (row or column-
wise).
3. Symmetric Matrix:
o Elements are symmetric about the diagonal (a[i][j]=a[j][i]a[i][j] =
a[j][i]a[i][j]=a[j][i]).
o Storage: Store only upper (or lower) triangular part.
4. Band Matrix:
o Non-zero elements are concentrated around the diagonal.
o Storage: Store only the bands in separate arrays.
4. Sparse Matrices
A sparse matrix is a matrix with a large number of zero elements.
Representation:
1. Array of Tuples:
o Each tuple stores (row, column, value) for non-zero elements.
o Example:
text
Copy code
Matrix:
0 0 3
0 4 0
5 0 0
Tuple Representation:
(row, col, value)
(0, 2, 3)
(1, 1, 4)
(2, 0, 5)
2. Compressed Sparse Row (CSR):
o Data Array: Stores non-zero values.
o Row Index Array: Indicates the start of each row in the data array.
o Column Index Array: Stores the column indices of non-zero values.
3. Compressed Sparse Column (CSC):
o Similar to CSR but organized column-wise.
5. Strings
A string is a sequence of characters terminated by a null character (\0) in
languages like C.
Representation:
Contiguous Memory:
text
Copy code
"hello" -> ['h', 'e', 'l', 'l', 'o', '\0']
o Stored in contiguous memory locations.
o Length is determined by counting characters up to the null
terminator.
String as Abstract Data Type:
Operations:
o Access: Retrieve a character by index.
o Concatenate: Join two strings.
o Substring: Extract a portion of the string.
o Compare: Check if two strings are equal.
Summary Table
Structure Description Storage Optimization
Array 1D collection of elements Contiguous memory
2D array (row-major or Use indexing formulas for
Matrix
column-major order) compact storage
Diagonal
Non-zero diagonal elements Store only diagonal elements
Matrix
Triangular Non-zero elements in triangular Store only non-zero triangular
Matrix regions elements
Use tuple or compressed
Sparse Matrix Large number of zeroes
formats
Contiguous memory with null
String Sequence of characters
termination
UNIT-II
Stacks and Queues
A stack is a linear data structure that follows the Last In, First Out (LIFO)
principle. Elements can only be added or removed from the top of the stack.
Key Operations:
1. Push: Add an element to the top of the stack.
2. Pop: Remove the top element of the stack.
3. Peek/Top: View the element at the top of the stack without removing it.
4. IsEmpty: Check if the stack is empty.
5. IsFull: Check if the stack is full (in case of a fixed-size stack).
Representation of Stacks:
Stacks can be represented using:
1. Arrays
2. Linked Lists
1. Stack Using Arrays
An array-based stack uses a fixed-size array to store elements.
Representation:
A stack is represented using:
o Array: To hold the stack elements.
o Top Pointer: Points to the top element in the stack.
Implementation:
Here is a conceptual representation of a stack using an array:
text
Copy code
Array: [10, 20, 30, _, _, _]
Top: ^
Algorithms:
Push:
1. Check if the stack is full (top == capacity - 1).
2. Increment the top pointer.
3. Add the new element at stack[top].
Pop:
1. Check if the stack is empty (top == -1).
2. Retrieve the element at stack[top].
3. Decrement the top pointer.
Peek:
1. Check if the stack is empty.
2. Return the element at stack[top].
class Stack {
private:
int* arr;
int top;
int capacity;
public:
// Constructor
Stack(int size) {
arr = new int[size];
capacity = size;
top = -1;
}
// Destructor
~Stack() {
delete[] arr;
}
// Push operation
void push(int value) {
if (top == capacity - 1) {
cout << "Stack Overflow\n";
return;
}
arr[++top] = value;
}
// Pop operation
int pop() {
if (top == -1) {
cout << "Stack Underflow\n";
return -1;
}
return arr[top--];
}
// Peek operation
int peek() {
if (top == -1) {
cout << "Stack is empty\n";
return -1;
}
return arr[top];
}
// Example usage
int main() {
Stack stack(5);
stack.push(10);
stack.push(20);
stack.push(30);
stack.push(40);
while (!stack.isEmpty()) {
cout << "Popped element: " << stack.pop() << endl;
}
return 0;
}
// Node structure
struct Node {
int data;
Node* next;
};
class Stack {
private:
Node* top;
public:
// Constructor
Stack() {
top = nullptr;
}
// Push operation
void push(int value) {
Node* newNode = new Node();
newNode->data = value;
newNode->next = top;
top = newNode;
}
// Pop operation
int pop() {
if (top == nullptr) {
cout << "Stack Underflow\n";
return -1;
}
int value = top->data;
Node* temp = top;
top = top->next;
delete temp;
return value;
}
// Peek operation
int peek() {
if (top == nullptr) {
cout << "Stack is empty\n";
return -1;
}
return top->data;
}
stack.push(10);
stack.push(20);
stack.push(30);
stack.push(40);
while (!stack.isEmpty()) {
cout << "Popped element: " << stack.pop() << endl;
}
return 0;
}