1. Basic Concepts of Data Structures
1.1 DefinitionsWhat: A data structure is a way to organize and store data to enable efficient operations (e.g., insertion, deletion, search). Examples include arrays, linked lists, stacks, queues, trees, and graphs.Why: In mobile software, data structures manage device information (e.g., lists of connected devices), optimize performance (e.g., fast searches for device IDs), and reduce resource usage on resource-constrained devices like smartphones.How: No specific code is needed for definitions, but we’ll use arrays as a basic example to store device IDs.
Code Example (Array for Device IDs):
Explanation:
1.2 Data AbstractionWhat: Data abstraction hides implementation details, exposing only essential operations (e.g., a stack’s push/pop without showing the underlying array).Why: Simplifies code for mobile developers, allowing focus on functionality (e.g., managing a call stack) without worrying about internal storage.How: Use a struct with functions to abstract a stack.Code Example (Stack Abstraction):
Explanation:
1.3 Performance Analysis - Time & Space ComplexityWhat: Time complexity measures the execution time of an algorithm (e.g., O(n) for linear search). Space complexity measures memory usage.Why: Critical in mobile software to ensure fast performance (e.g., quick device searches) and low memory usage on constrained devices.How: Analyze a simple linear search for a device ID.Code Example (Linear Search):
Explanation:
1.4 Asymptotic Notations - Big Oh, Omega, ThetaWhat: Asymptotic notations describe algorithm performance:
Explanation:
1.5 Polynomial Representation Using ArraysWhat: Polynomials (e.g., 3x² + 2x + 1) are represented as arrays where each index corresponds to a degree and stores the coefficient.Why: Useful in mobile software for calculations, like signal processing or battery usage models.How: Store coefficients in an array.Code Example:
Explanation:
1.6 Sparse Matrix (Tuple Representation)What: A sparse matrix (mostly zeros) is stored as a list of tuples (row, col, value) to save space.Why: In mobile software, sparse matrices optimize storage for large datasets, like network connectivity maps.How: Use an array of structs for tuples.Code Example:
Explanation:
1.7 Stacks and QueuesWhat:
Explanation:
1.8 Evaluation of Expressions (Infix to Postfix, Postfix Evaluation)What: Convert infix expressions (e.g., 3 + 4 * 2) to postfix (e.g., 3 4 2 * +) and evaluate postfix expressions.Why: In mobile software, expression evaluation is used in calculators or configuration parsers.How: Implement infix to postfix conversion.Code Example:
Explanation:
2. Linked List and Memory Management2.1 Singly Linked ListWhat: A linked list is a sequence of nodes, each containing data and a pointer to the next node.Why: In mobile software, linked lists manage dynamic data (e.g., a list of active devices) with flexible size.How: Implement insertion and display.Code Example:
Explanation:
2.2 Stacks and Queues Using Linked ListWhat: Stacks and queues implemented with linked lists for dynamic size.Why: More flexible than array-based stacks/queues for mobile apps handling variable tasks.How: Implement a linked list-based stack.Code Example:
Explanation:
2.3 Polynomial Representation Using Linked ListWhat: Polynomials are represented as linked lists where each node stores a coefficient and degree.Why: More efficient for sparse polynomials in mobile apps (e.g., signal processing).How: Implement evaluation.Code Example:
Explanation:
2.4 Doubly Linked ListWhat: A linked list where each node has pointers to both the next and previous nodes.Why: Allows bidirectional traversal, useful for mobile apps navigating device lists.How: Implement insertion.Code Example:
Explanation:
2.5 Circular Linked ListWhat: A linked list where the last node points to the first, forming a loop.Why: Useful for cyclic tasks in mobile apps, like rotating device status checks.How: Implement display.Code Example:
Explanation:
2.6 Memory Allocation (First-Fit, Best-Fit, Worst-Fit)What: Memory allocation schemes assign memory blocks to processes:
Explanation:
3. Trees and Graphs3.1 Trees - Representation and Binary TreesWhat: A tree is a hierarchical structure with nodes connected by edges. A binary tree has at most two children per node.Why: In mobile software, trees organize hierarchical data (e.g., file systems, device menus).How: Implement a binary tree.Code Example:
Explanation:
3.2 Tree Traversals (Inorder, Preorder, Postorder)What: Traversals visit nodes in specific orders:
Explanation:
3.3 Binary Search Trees (BST)What: A binary tree where left child < root < right child, enabling efficient search.Why: In mobile software, BSTs enable fast lookups (e.g., finding a device by ID).How: Implement search.Code Example:
Explanation:
3.4 Binary HeapsWhat: A complete binary tree where parent nodes are smaller (min-heap) or larger (max-heap) than children.Why: Used in mobile software for priority queues (e.g., scheduling device tasks).How: Implement min-heap insertion.Code Example:
Explanation:
3.5 Graphs - Representation, DFS, BFSWhat: A graph is a set of nodes (vertices) connected by edges. Represented as adjacency lists or matrices. DFS (Depth-First Search) and BFS (Breadth-First Search) traverse graphs.Why: In mobile software, graphs model networks (e.g., device connections).How: Implement BFS using an adjacency list.Code Example:
Explanation:
4. Sorting and Searching4.1 Sorting TechniquesWhat: Sorting arranges data in order (e.g., ascending). Techniques include:
Explanation:
4.2 Searching TechniquesWhat: Searching finds an element in a dataset:
Explanation:
4.3 HashingWhat: Hashing maps keys to indices using a hash function. Collisions (multiple keys mapping to the same index) are resolved using techniques like linear probing, quadratic probing, double hashing, or open hashing (chaining).Why: Provides O(1) average-case lookup for mobile apps (e.g., device ID lookups).How: Implement hashing with linear probing.Code Example:
Explanation:
Teaching Session Plan (90–120 Minutes)
Code Example (Array for Device IDs):
#include <stdio.h>
#define MAX_DEVICES 5
// Array to store device IDs
int deviceIds[MAX_DEVICES] = {101, 102, 103, 104, 105};
int main() {
// Print device IDs
printf("Device IDs: ");
for (int i = 0; i < MAX_DEVICES; i++) {
printf("%d ", deviceIds[i]);
}
printf("\n");
return 0;
}
- What: The array deviceIds is a simple data structure storing integers.
- Why: Arrays provide fast access (O(1)) for device IDs in a mobile system.
- How: Fixed-size array stores IDs, accessed by index. In mobile software, arrays could store device configurations or status codes.
1.2 Data AbstractionWhat: Data abstraction hides implementation details, exposing only essential operations (e.g., a stack’s push/pop without showing the underlying array).Why: Simplifies code for mobile developers, allowing focus on functionality (e.g., managing a call stack) without worrying about internal storage.How: Use a struct with functions to abstract a stack.Code Example (Stack Abstraction):
#include <stdio.h>
#define MAX_SIZE 5
// Stack structure
typedef struct {
int items[MAX_SIZE];
int top;
} Stack;
// Initialize stack
void initStack(Stack *s) {
s->top = -1; // Empty stack
}
// Push operation
void push(Stack *s, int value) {
if (s->top < MAX_SIZE - 1) {
s->items[++(s->top)] = value;
printf("Pushed %d\n", value);
} else {
printf("Stack overflow\n");
}
}
// Pop operation
int pop(Stack *s) {
if (s->top >= 0) {
return s->items[(s->top)--];
}
printf("Stack underflow\n");
return -1;
}
int main() {
Stack s;
initStack(&s);
push(&s, 1); // Push device status code
push(&s, 2);
printf("Popped: %d\n", pop(&s)); // Retrieve status
return 0;
}
- What: The Stack struct and functions (push, pop) hide the array implementation.
- Why: Abstraction allows mobile developers to use a stack for tasks like managing undo operations in a device app without handling array indices.
- How: Users call push/pop without accessing items directly, ensuring safe usage.
1.3 Performance Analysis - Time & Space ComplexityWhat: Time complexity measures the execution time of an algorithm (e.g., O(n) for linear search). Space complexity measures memory usage.Why: Critical in mobile software to ensure fast performance (e.g., quick device searches) and low memory usage on constrained devices.How: Analyze a simple linear search for a device ID.Code Example (Linear Search):
#include <stdio.h>
#define MAX_DEVICES 5
int linearSearch(int arr[], int size, int key) {
for (int i = 0; i < size; i++) { // O(n) time
if (arr[i] == key) {
return i; // Found device ID
}
}
return -1; // Not found
}
int main() {
int deviceIds[MAX_DEVICES] = {101, 102, 103, 104, 105}; // O(n) space
int key = 103;
int result = linearSearch(deviceIds, MAX_SIZE, key);
printf(result >= 0 ? "Device found at index %d\n" : "Device not found\n", result);
return 0;
}
- What: Time complexity is O(n) (linear scan), space complexity is O(1) (no extra space beyond input array).
- Why: In mobile software, understanding complexity helps choose algorithms (e.g., linear search vs. binary search) for device lookups.
- How: The code iterates through the array, checking each element, demonstrating linear time.
1.4 Asymptotic Notations - Big Oh, Omega, ThetaWhat: Asymptotic notations describe algorithm performance:
- Big Oh (O): Upper bound (worst-case time/space).
- Omega (Ω): Lower bound (best-case).
- Theta (Θ): Tight bound (average case).
#include <stdio.h>
#define MAX_SIZE 5
typedef struct {
int items[MAX_SIZE];
int size;
} DeviceList;
// Insert at end: O(1) time
void insertDevice(DeviceList *list, int deviceId) {
if (list->size < MAX_SIZE) {
list->items[list->size++] = deviceId; // O(1)
printf("Inserted %d\n", deviceId);
} else {
printf("List full\n");
}
}
int main() {
DeviceList list = {{0}, 0};
insertDevice(&list, 101); // O(1)
insertDevice(&list, 102);
return 0;
}
- What: Insertion at the end is O(1) (Big Oh), as it’s constant time. Best case (Ω) and average case (Θ) are also O(1) for this operation.
- Why: In mobile software, O(1) operations are preferred for real-time tasks like adding a device to a list.
- How: The code appends to the array’s end, requiring only one operation.
// Insert into sorted array: O(n)
void insertSorted(DeviceList *list, int deviceId) {
if (list->size < MAX_SIZE) {
int i = list->size - 1;
while (i >= 0 && list->items[i] > deviceId) { // Shift elements
list->items[i + 1] = list->items[i];
i--;
}
list->items[i + 1] = deviceId; // Insert
list->size++;
}
}
1.5 Polynomial Representation Using ArraysWhat: Polynomials (e.g., 3x² + 2x + 1) are represented as arrays where each index corresponds to a degree and stores the coefficient.Why: Useful in mobile software for calculations, like signal processing or battery usage models.How: Store coefficients in an array.Code Example:
#include <stdio.h>
#define MAX_DEGREE 10
typedef struct {
int coeffs[MAX_DEGREE + 1]; // coeffs[i] is coefficient of x^i
int degree;
} Polynomial;
// Evaluate polynomial at x
double evaluatePolynomial(Polynomial *p, double x) {
double result = 0;
for (int i = 0; i <= p->degree; i++) {
result += p->coeffs[i] * pow(x, i); // O(n) time
}
return result;
}
int main() {
Polynomial p = {{1, 2, 3}, 2}; // 3x² + 2x + 1
printf("Value at x=2: %.2f\n", evaluatePolynomial(&p, 2.0)); // 3*4 + 2*2 + 1 = 17
return 0;
}
- What: Array coeffs stores coefficients (e.g., {1, 2, 3} for 3x² + 2x + 1).
- Why: Efficient for polynomial operations in mobile apps (e.g., modeling battery drain).
- How: The code evaluates the polynomial by computing each term, with O(n) time complexity.
1.6 Sparse Matrix (Tuple Representation)What: A sparse matrix (mostly zeros) is stored as a list of tuples (row, col, value) to save space.Why: In mobile software, sparse matrices optimize storage for large datasets, like network connectivity maps.How: Use an array of structs for tuples.Code Example:
#include <stdio.h>
#define MAX_ELEMENTS 10
typedef struct {
int row, col, value;
} Tuple;
typedef struct {
Tuple elements[MAX_ELEMENTS];
int numElements, rows, cols;
} SparseMatrix;
// Add non-zero element
void addElement(SparseMatrix *m, int row, int col, int value) {
if (m->numElements < MAX_ELEMENTS && value != 0) {
m->elements[m->numElements].row = row;
m->elements[m->numElements].col = col;
m->elements[m->numElements].value = value;
m->numElements++;
}
}
int main() {
SparseMatrix m = {{0}, 0, 3, 3}; // 3x3 matrix
addElement(&m, 0, 1, 5); // Matrix[0][1] = 5
addElement(&m, 2, 2, 7); // Matrix[2][2] = 7
for (int i = 0; i < m.numElements; i++) {
printf("(%d, %d, %d)\n", m.elements[i].row, m.elements[i].col, m.elements[i].value);
}
return 0;
}
- What: Stores non-zero elements as tuples (e.g., (0, 1, 5)).
- Why: Saves memory in mobile apps for large, sparse datasets (e.g., device network graphs).
- How: The code stores only non-zero values, reducing space from O(rows * cols) to O(non-zero elements).
1.7 Stacks and QueuesWhat:
- Stack: LIFO (Last In, First Out) structure (e.g., push/pop).
- Queue: FIFO (First In, First Out) structure (e.g., enqueue/dequeue).
- Circular Queue: Queue with wrap-around to reuse space.
- Double-Ended Queue (Deque): Allows insertion/deletion at both ends.
#include <stdio.h>
#define MAX_SIZE 5
typedef struct {
int items[MAX_SIZE];
int top;
} Stack;
void push(Stack *s, int value) {
if (s->top < MAX_SIZE - 1) {
s->items[++(s->top)] = value;
}
}
int pop(Stack *s) {
if (s->top >= 0) {
return s->items[(s->top)--];
}
return -1;
}
int main() {
Stack s = {{0}, -1};
push(&s, 1); // Push device task
push(&s, 2);
printf("Popped: %d\n", pop(&s)); // Process task
return 0;
}
- What: Stack uses an array with top to track the last element.
- Why: Useful for mobile apps to manage call stacks or undo actions (e.g., navigation history).
- How: Push adds to the top (O(1)); pop removes from the top (O(1)).
1.8 Evaluation of Expressions (Infix to Postfix, Postfix Evaluation)What: Convert infix expressions (e.g., 3 + 4 * 2) to postfix (e.g., 3 4 2 * +) and evaluate postfix expressions.Why: In mobile software, expression evaluation is used in calculators or configuration parsers.How: Implement infix to postfix conversion.Code Example:
#include <stdio.h>
#include <string.h>
#define MAX_SIZE 100
typedef struct {
char items[MAX_SIZE];
int top;
} Stack;
void push(Stack *s, char c) {
if (s->top < MAX_SIZE - 1) s->items[++(s->top)] = c;
}
char pop(Stack *s) {
if (s->top >= 0) return s->items[(s->top)--];
return '\0';
}
int precedence(char op) {
if (op == '+' || op == '-') return 1;
if (op == '*' || op == '/') return 2;
return 0;
}
void infixToPostfix(char *infix, char *postfix) {
Stack s = {{0}, -1};
int k = 0;
for (int i = 0; infix[i]; i++) {
if (infix[i] >= '0' && infix[i] <= '9') {
postfix[k++] = infix[i]; // Operand
} else if (infix[i] == '(') {
push(&s, infix[i]);
} else if (infix[i] == ')') {
while (s.top >= 0 && s.items[s.top] != '(') {
postfix[k++] = pop(&s);
}
pop(&s); // Remove '('
} else { // Operator
while (s.top >= 0 && precedence(s.items[s.top]) >= precedence(infix[i])) {
postfix[k++] = pop(&s);
}
push(&s, infix[i]);
}
}
while (s.top >= 0) {
postfix[k++] = pop(&s);
}
postfix[k] = '\0';
}
int main() {
char infix[] = "3+4*2";
char postfix[MAX_SIZE];
infixToPostfix(infix, postfix);
printf("Postfix: %s\n", postfix); // Output: 342*+
return 0;
}
- What: Converts infix to postfix using a stack to manage operators.
- Why: Postfix eliminates parentheses, simplifying evaluation in mobile apps.
- How: Operands go directly to output; operators are pushed/popped based on precedence.
2. Linked List and Memory Management2.1 Singly Linked ListWhat: A linked list is a sequence of nodes, each containing data and a pointer to the next node.Why: In mobile software, linked lists manage dynamic data (e.g., a list of active devices) with flexible size.How: Implement insertion and display.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int deviceId;
struct Node *next;
} Node;
Node* createNode(int deviceId) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->deviceId = deviceId;
newNode->next = NULL;
return newNode;
}
void insertNode(Node **head, int deviceId) {
Node *newNode = createNode(deviceId);
newNode->next = *head; // Insert at head
*head = newNode;
}
void displayList(Node *head) {
Node *current = head;
while (current) {
printf("%d -> ", current->deviceId);
current = current->next;
}
printf("NULL\n");
}
int main() {
Node *head = NULL;
insertNode(&head, 101);
insertNode(&head, 102);
displayList(head); // Output: 102 -> 101 -> NULL
return 0;
}
- What: Each node holds a device ID and a pointer to the next node.
- Why: Linked lists allow dynamic resizing, unlike arrays, for managing device lists.
- How: insertNode adds at the head (O(1)); displayList traverses the list (O(n)).
2.2 Stacks and Queues Using Linked ListWhat: Stacks and queues implemented with linked lists for dynamic size.Why: More flexible than array-based stacks/queues for mobile apps handling variable tasks.How: Implement a linked list-based stack.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct {
Node *top;
} Stack;
void push(Stack *s, int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = s->top;
s->top = newNode;
}
int pop(Stack *s) {
if (!s->top) return -1;
Node *temp = s->top;
int data = temp->data;
s->top = s->top->next;
free(temp);
return data;
}
int main() {
Stack s = {NULL};
push(&s, 1); // Push task
push(&s, 2);
printf("Popped: %d\n", pop(&s)); // Output: 2
return 0;
}
- What: Stack uses linked list nodes for LIFO operations.
- Why: Dynamic size is ideal for mobile apps with unpredictable task counts.
- How: push adds at the head (O(1)); pop removes from the head (O(1)).
2.3 Polynomial Representation Using Linked ListWhat: Polynomials are represented as linked lists where each node stores a coefficient and degree.Why: More efficient for sparse polynomials in mobile apps (e.g., signal processing).How: Implement evaluation.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int coeff, degree;
struct Node *next;
} Node;
typedef struct {
Node *head;
} Polynomial;
void addTerm(Polynomial *p, int coeff, int degree) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->coeff = coeff;
newNode->degree = degree;
newNode->next = p->head;
p->head = newNode;
}
double evaluatePolynomial(Polynomial *p, double x) {
double result = 0;
Node *current = p->head;
while (current) {
result += current->coeff * pow(x, current->degree);
current = current->next;
}
return result;
}
int main() {
Polynomial p = {NULL};
addTerm(&p, 3, 2); // 3x²
addTerm(&p, 2, 1); // 2x
addTerm(&p, 1, 0); // 1
printf("Value at x=2: %.2f\n", evaluatePolynomial(&p, 2.0)); // 17.00
return 0;
}
- What: Each node stores a term (coefficient, degree).
- Why: Saves space for sparse polynomials in mobile calculations.
- How: addTerm inserts terms; evaluatePolynomial computes the value (O(n)).
2.4 Doubly Linked ListWhat: A linked list where each node has pointers to both the next and previous nodes.Why: Allows bidirectional traversal, useful for mobile apps navigating device lists.How: Implement insertion.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *prev, *next;
} Node;
Node* createNode(int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->prev = newNode->next = NULL;
return newNode;
}
void insertNode(Node **head, int data) {
Node *newNode = createNode(data);
if (!*head) {
*head = newNode;
} else {
newNode->next = *head;
(*head)->prev = newNode;
*head = newNode;
}
}
int main() {
Node *head = NULL;
insertNode(&head, 101);
insertNode(&head, 102);
for (Node *current = head; current; current = current->next) {
printf("%d ", current->data);
}
printf("\n"); // Output: 102 101
return 0;
}
- What: Nodes have prev and next pointers.
- Why: Useful for mobile apps needing to traverse device lists backward/forward.
- How: insertNode adds at the head, updating pointers (O(1)).
2.5 Circular Linked ListWhat: A linked list where the last node points to the first, forming a loop.Why: Useful for cyclic tasks in mobile apps, like rotating device status checks.How: Implement display.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
void insertNode(Node **head, int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
if (!*head) {
*head = newNode;
newNode->next = newNode;
} else {
newNode->next = (*head)->next;
(*head)->next = newNode;
}
}
void displayList(Node *head) {
if (!head) return;
Node *current = head;
do {
printf("%d ", current->data);
current = current->next;
} while (current != head);
printf("\n");
}
int main() {
Node *head = NULL;
insertNode(&head, 101);
insertNode(&head, 102);
displayList(head); // Output: 101 102
return 0;
}
- What: Last node links to the first.
- Why: Ideal for cyclic operations in mobile apps (e.g., polling devices).
- How: displayList loops until it reaches the head again.
2.6 Memory Allocation (First-Fit, Best-Fit, Worst-Fit)What: Memory allocation schemes assign memory blocks to processes:
- First-Fit: Allocates the first block large enough.
- Best-Fit: Allocates the smallest block that fits.
- Worst-Fit: Allocates the largest block.
#include <stdio.h>
#define MAX_BLOCKS 5
typedef struct {
int size;
int allocated;
} Block;
void firstFit(Block blocks[], int n, int processSize) {
for (int i = 0; i < n; i++) {
if (!blocks[i].allocated && blocks[i].size >= processSize) {
blocks[i].allocated = 1;
printf("Allocated %d to block %d (size %d)\n", processSize, i, blocks[i].size);
return;
}
}
printf("No block found for size %d\n", processSize);
}
int main() {
Block blocks[MAX_BLOCKS] = {{100, 0}, {50, 0}, {200, 0}, {150, 0}, {300, 0}};
firstFit(blocks, MAX_BLOCKS, 120); // Allocates to block 2 (200)
return 0;
}
- What: firstFit finds the first block large enough.
- Why: Simple and fast for mobile apps allocating memory for tasks.
- How: Iterates blocks, allocates the first suitable one (O(n)).
3. Trees and Graphs3.1 Trees - Representation and Binary TreesWhat: A tree is a hierarchical structure with nodes connected by edges. A binary tree has at most two children per node.Why: In mobile software, trees organize hierarchical data (e.g., file systems, device menus).How: Implement a binary tree.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *left, *right;
} Node;
Node* createNode(int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
int main() {
Node *root = createNode(1); // Root
root->left = createNode(2); // Left child
root->right = createNode(3); // Right child
printf("Root: %d, Left: %d, Right: %d\n", root->data, root->left->data, root->right->data);
return 0;
}
- What: Each node has data and pointers to left/right children.
- Why: Binary trees are used in mobile apps for efficient data organization.
- How: createNode builds a tree structure.
3.2 Tree Traversals (Inorder, Preorder, Postorder)What: Traversals visit nodes in specific orders:
- Inorder: Left, Root, Right.
- Preorder: Root, Left, Right.
- Postorder: Left, Right, Root.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *left, *right;
} Node;
Node* createNode(int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
void inorder(Node *root) {
if (root) {
inorder(root->left);
printf("%d ", root->data); // Visit root
inorder(root->right);
}
}
int main() {
Node *root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
inorder(root); // Output: 2 1 3
printf("\n");
return 0;
}
- What: Inorder visits left subtree, root, then right subtree.
- Why: Produces sorted output for binary search trees, useful in mobile apps.
- How: Recursive function traverses the tree (O(n)).
3.3 Binary Search Trees (BST)What: A binary tree where left child < root < right child, enabling efficient search.Why: In mobile software, BSTs enable fast lookups (e.g., finding a device by ID).How: Implement search.Code Example:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *left, *right;
} Node;
Node* createNode(int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
Node* insertBST(Node *root, int data) {
if (!root) return createNode(data);
if (data < root->data)
root->left = insertBST(root->left, data);
else
root->right = insertBST(root->right, data);
return root;
}
Node* searchBST(Node *root, int key) {
if (!root || root->data == key) return root;
if (key < root->data) return searchBST(root->left, key);
return searchBST(root->right, key);
}
int main() {
Node *root = NULL;
root = insertBST(root, 50);
insertBST(root, 30);
insertBST(root, 70);
Node *result = searchBST(root, 30);
printf(result ? "Found %d\n" : "Not found\n", 30);
return 0;
}
- What: BST organizes data for O(log n) average-case search.
- Why: Efficient for mobile apps searching device IDs.
- How: searchBST recursively navigates based on comparisons.
3.4 Binary HeapsWhat: A complete binary tree where parent nodes are smaller (min-heap) or larger (max-heap) than children.Why: Used in mobile software for priority queues (e.g., scheduling device tasks).How: Implement min-heap insertion.Code Example:
#include <stdio.h>
#define MAX_SIZE 100
typedef struct {
int items[MAX_SIZE];
int size;
} Heap;
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void heapifyUp(Heap *h, int index) {
int parent = (index - 1) / 2;
if (index > 0 && h->items[index] < h->items[parent]) {
swap(&h->items[index], &h->items[parent]);
heapifyUp(h, parent);
}
}
void insertHeap(Heap *h, int data) {
if (h->size < MAX_SIZE) {
h->items[h->size] = data;
heapifyUp(h, h->size++);
}
}
int main() {
Heap h = {{0}, 0};
insertHeap(&h, 10);
insertHeap(&h, 5);
printf("Min heap root: %d\n", h.items[0]); // Output: 5
return 0;
}
- What: Min-heap ensures the smallest element is at the root.
- Why: Useful for prioritizing tasks in mobile apps (e.g., urgent device updates).
- How: insertHeap adds an element and restores heap property (O(log n)).
3.5 Graphs - Representation, DFS, BFSWhat: A graph is a set of nodes (vertices) connected by edges. Represented as adjacency lists or matrices. DFS (Depth-First Search) and BFS (Breadth-First Search) traverse graphs.Why: In mobile software, graphs model networks (e.g., device connections).How: Implement BFS using an adjacency list.Code Example:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NODES 5
typedef struct Node {
int vertex;
struct Node *next;
} Node;
typedef struct {
Node *adjList[MAX_NODES];
} Graph;
void addEdge(Graph *g, int src, int dest) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->vertex = dest;
newNode->next = g->adjList[src];
g->adjList[src] = newNode;
}
void bfs(Graph *g, int start) {
int visited[MAX_NODES] = {0};
int queue[MAX_NODES], front = 0, rear = 0;
queue[rear++] = start;
visited[start] = 1;
while (front < rear) {
int v = queue[front++];
printf("%d ", v);
for (Node *current = g->adjList[v]; current; current = current->next) {
if (!visited[current->vertex]) {
queue[rear++] = current->vertex;
visited[current->vertex] = 1;
}
}
}
printf("\n");
}
int main() {
Graph g = {{NULL}};
addEdge(&g, 0, 1);
addEdge(&g, 0, 2);
bfs(&g, 0); // Output: 0 1 2
return 0;
}
- What: BFS explores nodes level by level using a queue.
- Why: Useful for finding shortest paths in device networks.
- How: Adjacency list stores edges; BFS uses a queue (O(V + E)).
4. Sorting and Searching4.1 Sorting TechniquesWhat: Sorting arranges data in order (e.g., ascending). Techniques include:
- Selection Sort: Repeatedly selects the smallest element (O(n²)).
- Insertion Sort: Builds sorted array incrementally (O(n²)).
- Quick Sort: Uses divide-and-conquer (O(n log n) average).
- Merge Sort: Divides and merges sorted halves (O(n log n)).
- Heap Sort: Uses a heap (O(n log n)).
- Radix Sort: Sorts by digits (O(dn)).
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return i + 1;
}
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
int main() {
int arr[] = {64, 34, 25, 12, 22};
int n = 5;
quickSort(arr, 0, n - 1);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // Output: 12 22 25 34 64
}
printf("\n");
return 0;
}
- What: Quick Sort selects a pivot and partitions the array.
- Why: Fast (O(n log n)) for sorting device data in mobile apps.
- How: Recursive partitioning sorts the array.
4.2 Searching TechniquesWhat: Searching finds an element in a dataset:
- Linear Search: Checks each element (O(n)).
- Binary Search: Works on sorted arrays, dividing search space (O(log n)).
- Hashing: Maps keys to indices for fast lookup (O(1) average).
#include <stdio.h>
int binarySearch(int arr[], int size, int key) {
int low = 0, high = size - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key) return mid;
if (arr[mid] < key) low = mid + 1;
else high = mid - 1;
}
return -1;
}
int main() {
int arr[] = {12, 22, 25, 34, 64};
int key = 25;
int result = binarySearch(arr, 5, key);
printf(result >= 0 ? "Found at index %d\n" : "Not found\n", result);
return 0;
}
- What: Binary Search divides the sorted array to find the key.
- Why: Efficient (O(log n)) for mobile apps searching sorted device IDs.
- How: Iterative search narrows the range.
4.3 HashingWhat: Hashing maps keys to indices using a hash function. Collisions (multiple keys mapping to the same index) are resolved using techniques like linear probing, quadratic probing, double hashing, or open hashing (chaining).Why: Provides O(1) average-case lookup for mobile apps (e.g., device ID lookups).How: Implement hashing with linear probing.Code Example:
#include <stdio.h>
#define TABLE_SIZE 10
typedef struct {
int data;
int occupied;
} HashTable;
void initTable(HashTable table[]) {
for (int i = 0; i < TABLE_SIZE; i++) {
table[i].occupied = 0;
}
}
int hashFunction(int key) {
return key % TABLE_SIZE; // Division method
}
void insert(HashTable table[], int key) {
int index = hashFunction(key);
int i = 0;
while (table[(index + i) % TABLE_SIZE].occupied && i < TABLE_SIZE) {
i++; // Linear probing
}
if (i < TABLE_SIZE) {
table[(index + i) % TABLE_SIZE].data = key;
table[(index + i) % TABLE_SIZE].occupied = 1;
}
}
int search(HashTable table[], int key) {
int index = hashFunction(key);
int i = 0;
while (table[(index + i) % TABLE_SIZE].occupied && i < TABLE_SIZE) {
if (table[(index + i) % TABLE_SIZE].data == key) {
return (index + i) % TABLE_SIZE;
}
i++;
}
return -1;
}
int main() {
HashTable table[TABLE_SIZE];
initTable(table);
insert(table, 15);
insert(table, 25); // Collision with 15
int result = search(table, 25);
printf(result >= 0 ? "Found at index %d\n" : "Not found\n", result);
return 0;
}
- What: Hash table uses division method; linear probing resolves collisions.
- Why: Fast lookups for mobile apps managing device IDs.
- How: insert and search use linear probing to handle collisions (O(1) average).
Teaching Session Plan (90–120 Minutes)
- Introduction (10–15 minutes):
- Context: “Data structures are critical for mobile software to manage devices, optimize performance, and save resources.”
- Objective: “Learn basic data structures, linked lists, trees, graphs, sorting, and searching with C examples.”
- Relevance: “Used in device management systems for tasks like searching devices or scheduling updates.”
- Explain Concepts (30–40 minutes):
- Basic Concepts: Define data structures, abstraction, complexity, and notations with array/stack examples.
- Linked Lists: Show singly, doubly, circular lists, and memory allocation.
- Trees/Graphs: Explain binary trees, BST, heaps, and BFS with examples.
- Sorting/Searching: Cover quick sort, binary search, and hashing.
- Use diagrams (e.g., tree structure, hash table) to visualize.
- Live Coding (30–40 minutes):
- Use a C compiler (e.g., GCC, Code::Blocks).
- Demonstrate:
- Stack push/pop (Basic Concepts).
- Linked list insertion (Linked Lists).
- BST search (Trees).
- Quick sort and hashing (Sorting/Searching).
- Show outputs and explain time complexities.
- Hands-On Practice (15–20 minutes):
- Tasks:
- Implement a queue using a linked list.
- Add deletion to the BST code.
- Modify the hash table to use quadratic probing.
- Tasks:
.