holy ai spree

This commit is contained in:
lda committed 2025-06-30 10:31:28 +07:00
1 parent abbb01049d
commit b9b768fe45
15 files changed
+2581

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
# DSA Learning Resource Development Workflow
This document outlines the systematic workflow used to create and enhance the Data Structures and Algorithms (DSA) learning resources in this project.
## Objective
The goal is to produce a comprehensive, high-quality, and educational guide for fundamental DSA concepts, using C as the implementation language. Each topic should have both a robust code implementation and a clear, well-documented Markdown resource.
## Core Principles
- **Completeness**: Implementations should cover all essential operations for a given data structure or algorithm.
- **Clarity**: Documentation should be easy to understand, with clear explanations, diagrams, and code snippets.
- **Consistency**: All resources should follow a similar structure and style for a cohesive learning experience.
- **Correctness**: Both the code and the explanations must be accurate and reliable.
## Step-by-Step Workflow
The process for handling each DSA topic is as follows:
### 1. Topic Selection
A specific data structure or algorithm is chosen to be worked on. The selection is based on a logical progression through core DSA topics (e.g., from basic lists to more complex structures like trees) or direct user requests.
### 2. Code Implementation Review & Enhancement
1. **Analyze Existing Code**: The current C implementation in the `drafts/` directory is thoroughly reviewed to assess its completeness and correctness.
2. **Identify Gaps**: Missing functions and operations are identified. The goal is to provide a full suite of functionalities that a developer would expect (e.g., not just adding to the end of a list, but also to the beginning and at specific indices).
3. **Implement New Functions**: The C file is updated to include the new functions. This involves writing clean, efficient, and well-commented code.
### 3. Documentation Enhancement
1. **Analyze Existing Documentation**: The corresponding Markdown file in the `learning_resources/` directory is reviewed.
2. **Synchronize with Code**: The documentation is updated to reflect all the changes and additions made to the C implementation. This includes:
- Adding new sections for each new function, complete with code snippets and detailed explanations.
- Creating or updating diagrams (using Mermaid.js) to visually represent the concepts.
- Adding a comprehensive **Time Complexity Analysis** section, typically in a Markdown table, detailing the best, average, and worst-case performance of all operations.
3. **Review and Refine**: The entire document is proofread for clarity, consistency, and formatting. The language is kept simple and educational.
### 4. Iteration
The process is repeated for the next DSA topic, ensuring that the entire collection of learning resources grows in a structured and high-quality manner.
## Tooling
- **File System Tools**: Used to read existing files, create new files, and apply edits.
- **Language**: C for code implementations.
- **Markup**: Markdown for documentation, with Mermaid.js for diagrams.
+59
View File
@@ -53,6 +53,27 @@ void add_to_array_list(ArrayList *list, int value)
list->data[list->size++] = value;
}
// Inserts an element at a specific index
void insert_at(ArrayList *list, int index, int value)
{
if (index < 0 || index > list->size)
{
fprintf(stderr, "Index out of bounds\n");
return;
}
if (list->size == list->capacity)
{
resize_array_list(list);
}
// Shift elements to the right
for (int i = list->size; i > index; i--)
{
list->data[i] = list->data[i - 1];
}
list->data[index] = value;
list->size++;
}
// Removes the last element from the list
int remove_from_array_list(ArrayList *list)
{
@@ -64,6 +85,24 @@ int remove_from_array_list(ArrayList *list)
return list->data[--list->size];
}
// Removes an element from a specific index
int remove_at(ArrayList *list, int index)
{
if (index < 0 || index >= list->size)
{
fprintf(stderr, "Index out of bounds\n");
exit(EXIT_FAILURE);
}
int value = list->data[index];
// Shift elements to the left
for (int i = index; i < list->size - 1; i++)
{
list->data[i] = list->data[i + 1];
}
list->size--;
return value;
}
// Gets the element at a specific index
int get_from_array_list(ArrayList *list, int index)
{
@@ -75,6 +114,17 @@ int get_from_array_list(ArrayList *list, int index)
return list->data[index];
}
// Sets the element at a specific index
void set_in_array_list(ArrayList *list, int index, int value)
{
if (index < 0 || index >= list->size)
{
fprintf(stderr, "Index out of bounds\n");
return;
}
list->data[index] = value;
}
// Prints the list
void print_array_list(ArrayList *list)
{
@@ -108,6 +158,15 @@ int main()
printf("Element at index 1: %d\n", get_from_array_list(list, 1));
insert_at(list, 1, 25);
print_array_list(list);
printf("Removed from index 1: %d\n", remove_at(list, 1));
print_array_list(list);
set_in_array_list(list, 0, 15);
print_array_list(list);
free_array_list(list);
return 0;
}
+29
View File
@@ -108,6 +108,35 @@ int pop_back(Deque *deque)
return value;
}
// Gets the front element of the deque without removing it
int peek_front(Deque *deque)
{
if (deque->size == 0)
{
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
return deque->data[deque->front];
}
// Gets the back element of the deque without removing it
int peek_back(Deque *deque)
{
if (deque->size == 0)
{
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
int last_index = (deque->back - 1 + deque->capacity) % deque->capacity;
return deque->data[last_index];
}
// Checks if the deque is empty
int is_empty(Deque *deque)
{
return deque->size == 0;
}
// Prints the deque
void print_deque(Deque *deque)
{
+112
View File
@@ -84,6 +84,39 @@ void push_back(DoublyLinkedList *list, uintptr_t data)
list->size++;
}
// Inserts a value at a specific index
void insert_at(DoublyLinkedList *list, uintptr_t data, int index)
{
if (index < 0 || index > list->size)
{
fprintf(stderr, "Index out of bounds\n");
return;
}
if (index == 0)
{
push_front(list, data);
return;
}
if (index == list->size)
{
push_back(list, data);
return;
}
Node *current = list->head;
for (int i = 0; i < index; i++)
{
current = current->next;
}
Node *new_node = create_node(data);
new_node->next = current;
new_node->prev = current->prev;
current->prev->next = new_node;
current->prev = new_node;
list->size++;
}
// Removes a value from the front of the list
uintptr_t pop_front(DoublyLinkedList *list)
{
@@ -132,6 +165,54 @@ uintptr_t pop_back(DoublyLinkedList *list)
return data;
}
// Removes a value at a specific index
uintptr_t remove_at(DoublyLinkedList *list, int index)
{
if (index < 0 || index >= list->size)
{
fprintf(stderr, "Index out of bounds\n");
exit(EXIT_FAILURE);
}
if (index == 0)
{
return pop_front(list);
}
if (index == list->size - 1)
{
return pop_back(list);
}
Node *current = list->head;
for (int i = 0; i < index; i++)
{
current = current->next;
}
uintptr_t data = current->data;
current->prev->next = current->next;
current->next->prev = current->prev;
free(current);
list->size--;
return data;
}
// Gets a value at a specific index
int get(DoublyLinkedList *list, int index, uintptr_t *data)
{
if (index < 0 || index >= list->size)
{
return -1; // Index out of bounds
}
Node *current = list->head;
for (int i = 0; i < index; i++)
{
current = current->next;
}
*data = current->data;
return 0; // Success
}
// Prints the list from front to back
void print_list(DoublyLinkedList *list)
{
@@ -145,6 +226,19 @@ void print_list(DoublyLinkedList *list)
printf("\n");
}
// Prints the list from back to front
void print_reverse(DoublyLinkedList *list)
{
Node *current = list->tail;
printf("List (reversed): ");
while (current != NULL)
{
printf("%lu ", current->data);
current = current->prev;
}
printf("\n");
}
// Frees the entire list
void free_list(DoublyLinkedList *list)
{
@@ -173,6 +267,24 @@ int main()
printf("Popped from back: %lu\n", pop_back(list));
print_list(list);
insert_at(list, 25, 1);
print_list(list);
remove_at(list, 1);
print_list(list);
uintptr_t data;
if (get(list, 0, &data) == 0)
{
printf("Value at index 0: %lu\n", data);
}
else
{
printf("Failed to get value at index 0\n");
}
print_reverse(list);
free_list(list);
return 0;
}
+123
View File
@@ -0,0 +1,123 @@
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// Structure to represent a queue
typedef struct
{
int items[MAX_SIZE];
int front;
int rear;
} Queue;
// Function to create a new queue
Queue *createQueue()
{
Queue *q = (Queue *)malloc(sizeof(Queue));
q->front = -1;
q->rear = -1;
return q;
}
// Function to check if the queue is full
int isFull(Queue *q)
{
return q->rear == MAX_SIZE - 1;
}
// Function to check if the queue is empty
int isEmpty(Queue *q)
{
return q->front == -1;
}
// Function to add an item to the queue (enqueue)
void enqueue(Queue *q, int value)
{
if (isFull(q))
{
printf("Queue is full\n");
}
else
{
if (q->front == -1)
{
q->front = 0;
}
q->rear++;
q->items[q->rear] = value;
}
}
// Function to remove an item from the queue (dequeue)
int dequeue(Queue *q)
{
int item;
if (isEmpty(q))
{
printf("Queue is empty\n");
return -1;
}
else
{
item = q->items[q->front];
q->front++;
if (q->front > q->rear)
{
// Reset queue when it becomes empty
q->front = q->rear = -1;
}
return item;
}
}
// Function to get the front item of the queue without removing it
int peek(Queue *q)
{
if (isEmpty(q))
{
printf("Queue is empty\n");
return -1;
}
return q->items[q->front];
}
// Function to print the queue
void printQueue(Queue *q)
{
if (isEmpty(q))
{
printf("Queue is empty\n");
}
else
{
printf("Queue: ");
for (int i = q->front; i <= q->rear; i++)
{
printf("%d ", q->items[i]);
}
printf("\n");
}
}
int main()
{
Queue *q = createQueue();
enqueue(q, 10);
enqueue(q, 20);
enqueue(q, 30);
enqueue(q, 40);
printQueue(q);
printf("Dequeued item: %d\n", dequeue(q));
printQueue(q);
printf("Front item: %d\n", peek(q));
printQueue(q);
free(q);
return 0;
}
+151
View File
@@ -68,6 +68,135 @@ int append(SinglyLinkedList *list, uintptr_t value)
return 0; // Success
}
// Adds a value to the front of the list
int prepend(SinglyLinkedList *list, uintptr_t value)
{
Node *new_node = create_node(value);
if (!new_node)
{
return -1; // Memory allocation failed
}
if (list->head == NULL)
{
list->head = new_node;
list->tail = new_node;
}
else
{
new_node->next = list->head;
list->head = new_node;
}
list->size++;
return 0; // Success
}
// Inserts a value at a specific index
int insert_at(SinglyLinkedList *list, uintptr_t value, int index)
{
if (index < 0 || index > list->size)
{
return -1; // Index out of bounds
}
if (index == 0)
{
return prepend(list, value);
}
if (index == list->size)
{
return append(list, value);
}
Node *new_node = create_node(value);
if (!new_node)
{
return -1; // Memory allocation failed
}
Node *current = list->head;
for (int i = 0; i < index - 1; i++)
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
list->size++;
return 0; // Success
}
// Removes a value from the end of the list
uintptr_t remove_last(SinglyLinkedList *list)
{
if (list->tail == NULL)
{
fprintf(stderr, "List is empty\n");
return (uintptr_t)-1; // Indicate error
}
if (list->head == list->tail)
{
return remove_first(list);
}
Node *current = list->head;
while (current->next != list->tail)
{
current = current->next;
}
uintptr_t value = list->tail->value;
free(list->tail);
list->tail = current;
list->tail->next = NULL;
list->size--;
return value;
}
// Removes a value at a specific index
uintptr_t remove_at(SinglyLinkedList *list, int index)
{
if (index < 0 || index >= list->size)
{
return (uintptr_t)-1; // Index out of bounds
}
if (index == 0)
{
return remove_first(list);
}
if (index == list->size - 1)
{
return remove_last(list);
}
Node *current = list->head;
for (int i = 0; i < index - 1; i++)
{
current = current->next;
}
Node *temp = current->next;
uintptr_t value = temp->value;
current->next = temp->next;
free(temp);
list->size--;
return value;
}
// Gets a value at a specific index
int get(SinglyLinkedList *list, int index, uintptr_t *value)
{
if (index < 0 || index >= list->size)
{
return -1; // Index out of bounds
}
Node *current = list->head;
for (int i = 0; i < index; i++)
{
current = current->next;
}
*value = current->value;
return 0; // Success
}
// Removes a value from the front of the list
uintptr_t remove_first(SinglyLinkedList *list)
{
@@ -130,6 +259,28 @@ int main()
printf("Removed: %lu\n", remove_first(list));
print_list(list);
prepend(list, 5);
print_list(list);
insert_at(list, 25, 2);
print_list(list);
printf("Removed last: %lu\n", remove_last(list));
print_list(list);
printf("Removed at index 1: %lu\n", remove_at(list, 1));
print_list(list);
uintptr_t value;
if (get(list, 1, &value) == 0)
{
printf("Value at index 1: %lu\n", value);
}
else
{
printf("Index out of bounds\n");
}
free_list(list);
return 0;
}
+212
View File
@@ -0,0 +1,212 @@
# Array List Implementation Guide
This guide explains how to implement a dynamic array-based list (ArrayList) in C. It covers the structure, functions, and memory management involved.
---
## Overview
An ArrayList is a dynamic array that can grow in size. It provides fast access to elements by index.
### Advantages
- Fast random access (O(1)).
- Good memory locality.
### Disadvantages
- Slow insertion or deletion at the beginning or middle (O(n)).
- Resizing can be slow and memory-intensive.
---
## ArrayList Structure
```c
typedef struct ArrayList {
int *data; // Pointer to the array
int size; // Current number of elements
int capacity; // Maximum capacity
} ArrayList;
```
### Diagram
```mermaid
graph TD
subgraph ArrayList
direction LR
A[10] -- B[20] -- C[30] -- D[40]
end
subgraph Capacity
direction LR
E[ ] -- F[ ] -- G[ ] -- H[ ]
end
ArrayList --> |size: 4| Capacity
```
---
## Functions
### 1. Initialize ArrayList
```c
ArrayList *init_array_list(int capacity) {
ArrayList *list = (ArrayList *)malloc(sizeof(ArrayList));
if (!list) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
list->data = (int *)malloc(capacity * sizeof(int));
if (!list->data) {
fprintf(stderr, "Memory allocation failed\n");
free(list);
exit(EXIT_FAILURE);
}
list->size = 0;
list->capacity = capacity;
return list;
}
```
### 2. Resize ArrayList
```c
void resize_array_list(ArrayList *list) {
list->capacity *= 2;
int *new_data = (int *)realloc(list->data, list->capacity * sizeof(int));
if (!new_data) {
fprintf(stderr, "Memory reallocation failed\n");
exit(EXIT_FAILURE);
}
list->data = new_data;
}
```
### 3. Add to ArrayList
```c
void add_to_array_list(ArrayList *list, int value) {
if (list->size == list->capacity) {
resize_array_list(list);
}
list->data[list->size++] = value;
}
```
### 4. Remove from ArrayList
```c
int remove_from_array_list(ArrayList *list) {
if (list->size == 0) {
fprintf(stderr, "List is empty\n");
exit(EXIT_FAILURE);
}
return list->data[--list->size];
}
```
### 5. Get from ArrayList
```c
int get_from_array_list(ArrayList *list, int index) {
if (index < 0 || index >= list->size) {
fprintf(stderr, "Index out of bounds\n");
exit(EXIT_FAILURE);
}
return list->data[index];
}
```
### 6. Print ArrayList
```c
void print_array_list(ArrayList *list) {
printf("ArrayList: ");
for (int i = 0; i < list->size; i++) {
printf("%d ", list->data[i]);
}
printf("\n");
}
```
### 7. Free ArrayList
```c
void free_array_list(ArrayList *list) {
free(list->data);
free(list);
}
```
### 8. Insert at a Specific Index
```c
void insert_at(ArrayList *list, int index, int value) {
if (index < 0 || index > list->size) {
fprintf(stderr, "Index out of bounds\n");
return;
}
if (list->size == list->capacity) {
resize_array_list(list);
}
// Shift elements to the right
for (int i = list->size; i > index; i--) {
list->data[i] = list->data[i - 1];
}
list->data[index] = value;
list->size++;
}
```
### 9. Remove at a Specific Index
```c
int remove_at(ArrayList *list, int index) {
if (index < 0 || index >= list->size) {
fprintf(stderr, "Index out of bounds\n");
exit(EXIT_FAILURE);
}
int value = list->data[index];
// Shift elements to the left
for (int i = index; i < list->size - 1; i++) {
list->data[i] = list->data[i + 1];
}
list->size--;
return value;
}
```
### 10. Set Value at a Specific Index
```c
void set_in_array_list(ArrayList *list, int index, int value) {
if (index < 0 || index >= list->size) {
fprintf(stderr, "Index out of bounds\n");
return;
}
list->data[index] = value;
}
```
---
## Time Complexity
| Operation | Best | Average | Worst |
| :-------- | :--- | :------ | :---- |
| Access | O(1) | O(1) | O(1) |
| Search | O(1) | O(n) | O(n) |
| Insertion | O(1) | O(n) | O(n) |
| Deletion | O(1) | O(n) | O(n) |
- **Access**: O(1) because array elements can be accessed directly by index.
- **Search**: O(n) on average as it may require scanning the list.
- **Insertion/Deletion**: O(1) at the end (amortized time for resizing). O(n) at the beginning or middle due to element shifting.
---
## Summary
This guide provides a complete implementation of an ArrayList in C. It is a versatile data structure that is useful in many applications.
+227
View File
@@ -0,0 +1,227 @@
# Deque Implementation Guide
This guide provides a comprehensive overview of implementing a deque (double-ended queue) using a circular array in C.
---
## Overview
A deque is a queue-like data structure that allows adding and removing elements from both ends.
### Advantages
- Flexible, as it supports both stack and queue operations.
- Efficient O(1) time complexity for insertions and deletions at both ends.
### Disadvantages
- More complex to implement than a simple queue or stack.
- Can have wasted space in the underlying array.
---
## Deque Structure
```c
typedef struct Deque {
int *data; // Pointer to the array
int front; // Index of the front element
int back; // Index of the back element
int size; // Current number of elements
int capacity; // Maximum capacity
} Deque;
```
### Diagram
```mermaid
graph TD
subgraph Deque
direction LR
A[40] -- B[30] -- C[10] -- D[20]
end
subgraph CircularArray
direction LR
Front -- A -- B -- C -- D -- Back
end
```
---
## Functions
### 1. Initialize Deque
```c
Deque *init_deque(int capacity) {
Deque *deque = (Deque *)malloc(sizeof(Deque));
if (!deque) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
deque->data = (int *)malloc(capacity * sizeof(int));
if (!deque->data) {
fprintf(stderr, "Memory allocation failed\n");
free(deque);
exit(EXIT_FAILURE);
}
deque->front = 0;
deque->back = 0;
deque->size = 0;
deque->capacity = capacity;
return deque;
}
```
### 2. Resize Deque
```c
void resize_deque(Deque *deque) {
int *new_data = (int *)malloc(deque->capacity * 2 * sizeof(int));
if (!new_data) {
fprintf(stderr, "Memory reallocation failed\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < deque->size; i++) {
new_data[i] = deque->data[(deque->front + i) % deque->capacity];
}
free(deque->data);
deque->data = new_data;
deque->front = 0;
deque->back = deque->size;
deque->capacity *= 2;
}
```
### 3. Push to Front
```c
void push_front(Deque *deque, int value) {
if (deque->size == deque->capacity) {
resize_deque(deque);
}
deque->front = (deque->front - 1 + deque->capacity) % deque->capacity;
deque->data[deque->front] = value;
deque->size++;
}
```
### 4. Push to Back
```c
void push_back(Deque *deque, int value) {
if (deque->size == deque->capacity) {
resize_deque(deque);
}
deque->data[deque->back] = value;
deque->back = (deque->back + 1) % deque->capacity;
deque->size++;
}
```
### 5. Pop from Front
```c
int pop_front(Deque *deque) {
if (deque->size == 0) {
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
int value = deque->data[deque->front];
deque->front = (deque->front + 1) % deque->capacity;
deque->size--;
return value;
}
```
### 6. Pop from Back
```c
int pop_back(Deque *deque) {
if (deque->size == 0) {
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
deque->back = (deque->back - 1 + deque->capacity) % deque->capacity;
int value = deque->data[deque->back];
deque->size--;
return value;
}
```
### 7. Print Deque
```c
void print_deque(Deque *deque) {
printf("Deque: ");
for (int i = 0; i < deque->size; i++) {
printf("%d ", deque->data[(deque->front + i) % deque->capacity]);
}
printf("\n");
}
```
### 8. Free Deque
```c
void free_deque(Deque *deque) {
free(deque->data);
free(deque);
}
```
### 9. Peek at Front
```c
int peek_front(Deque *deque) {
if (deque->size == 0) {
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
return deque->data[deque->front];
}
```
### 10. Peek at Back
```c
int peek_back(Deque *deque) {
if (deque->size == 0) {
fprintf(stderr, "Deque is empty\n");
exit(EXIT_FAILURE);
}
int last_index = (deque->back - 1 + deque->capacity) % deque->capacity;
return deque->data[last_index];
}
```
### 11. Is Empty
```c
int is_empty(Deque *deque) {
return deque->size == 0;
}
```
---
## Time Complexity
| Operation | Complexity |
| :--------- | :--------- |
| Push Front | O(1) |
| Push Back | O(1) |
| Pop Front | O(1) |
| Pop Back | O(1) |
| Peek Front | O(1) |
| Peek Back | O(1) |
_Note: The complexities are amortized O(1) due to the occasional O(n) resize operation._
---
## Summary
This guide has demonstrated how to implement a deque in C using a circular array. This data structure is highly versatile and efficient for a variety of applications.
+306
View File
@@ -0,0 +1,306 @@
# Doubly Linked List Implementation Guide
This document provides a detailed guide to implementing a doubly linked list in C. It includes explanations, code snippets, and diagrams to help you understand the concepts.
---
## Overview
A doubly linked list is a linear data structure where each node has a value and pointers to both the next and previous nodes.
### Advantages
- Can be traversed in both forward and backward directions.
- Deletion of a node is more efficient if a pointer to the node is given.
### Disadvantages
- Requires more memory per node due to the extra `prev` pointer.
- More complex to implement than a singly linked list.
---
## Node Structure
```c
typedef struct Node {
uintptr_t data; // Generic data pointer or integer value
struct Node *prev;
struct Node *next;
} Node;
```
### Node Diagram
```mermaid
graph TD
NULL_Prev <-- Node1["data: 10"] --> Node2["data: 20"];
Node1 <-- Node2 --> Node3["data: 30"];
Node2 <-- Node3 --> NULL_Next;
```
---
## Doubly Linked List Structure
```c
typedef struct DoublyLinkedList {
int size; // Number of elements in the list
Node *head; // Pointer to the first node
Node *tail; // Pointer to the last node
} DoublyLinkedList;
```
---
## Functions
### 1. Initialize List
```c
DoublyLinkedList *init_list() {
DoublyLinkedList *list = (DoublyLinkedList *)malloc(sizeof(DoublyLinkedList));
if (!list) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
list->size = 0;
list->head = NULL;
list->tail = NULL;
return list;
}
```
### 2. Create Node
```c
Node *create_node(uintptr_t data) {
Node *new_node = (Node *)malloc(sizeof(Node));
if (!new_node) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
new_node->data = data;
new_node->prev = NULL;
new_node->next = NULL;
return new_node;
}
```
### 3. Add to Front (push_front)
```c
void push_front(DoublyLinkedList *list, uintptr_t data) {
Node *new_node = create_node(data);
if (list->head == NULL) {
list->head = new_node;
list->tail = new_node;
} else {
new_node->next = list->head;
list->head->prev = new_node;
list->head = new_node;
}
list->size++;
}
```
### 4. Add to Back (push_back)
```c
void push_back(DoublyLinkedList *list, uintptr_t data) {
Node *new_node = create_node(data);
if (list->tail == NULL) {
list->head = new_node;
list->tail = new_node;
} else {
new_node->prev = list->tail;
list->tail->next = new_node;
list->tail = new_node;
}
list->size++;
}
```
### 5. Remove from Front (pop_front)
```c
uintptr_t pop_front(DoublyLinkedList *list) {
if (list->head == NULL) {
fprintf(stderr, "List is empty\n");
exit(EXIT_FAILURE);
}
Node *temp = list->head;
uintptr_t data = temp->data;
list->head = list->head->next;
if (list->head != NULL) {
list->head->prev = NULL;
} else {
list->tail = NULL;
}
free(temp);
list->size--;
return data;
}
```
### 6. Remove from Back (pop_back)
```c
uintptr_t pop_back(DoublyLinkedList *list) {
if (list->tail == NULL) {
fprintf(stderr, "List is empty\n");
exit(EXIT_FAILURE);
}
Node *temp = list->tail;
uintptr_t data = temp->data;
list->tail = list->tail->prev;
if (list->tail != NULL) {
list->tail->next = NULL;
} else {
list->head = NULL;
}
free(temp);
list->size--;
return data;
}
```
### 7. Print List
```c
void print_list(DoublyLinkedList *list) {
Node *current = list->head;
printf("List: ");
while (current != NULL) {
printf("%lu ", current->data);
current = current->next;
}
printf("\n");
}
```
### 8. Free List
```c
void free_list(DoublyLinkedList *list) {
Node *current = list->head;
while (current != NULL) {
Node *next = current->next;
free(current);
current = next;
}
free(list);
}
```
### 9. Insert at a Specific Index
```c
void insert_at(DoublyLinkedList *list, uintptr_t data, int index) {
if (index < 0 || index > list->size) {
fprintf(stderr, "Index out of bounds\n");
return;
}
if (index == 0) {
push_front(list, data);
return;
}
if (index == list->size) {
push_back(list, data);
return;
}
Node *current = list->head;
for (int i = 0; i < index; i++) {
current = current->next;
}
Node *new_node = create_node(data);
new_node->next = current;
new_node->prev = current->prev;
current->prev->next = new_node;
current->prev = new_node;
list->size++;
}
```
### 10. Remove at a Specific Index
```c
uintptr_t remove_at(DoublyLinkedList *list, int index) {
if (index < 0 || index >= list->size) {
fprintf(stderr, "Index out of bounds\n");
exit(EXIT_FAILURE);
}
if (index == 0) {
return pop_front(list);
}
if (index == list->size - 1) {
return pop_back(list);
}
Node *current = list->head;
for (int i = 0; i < index; i++) {
current = current->next;
}
uintptr_t data = current->data;
current->prev->next = current->next;
current->next->prev = current->prev;
free(current);
list->size--;
return data;
}
```
### 11. Get Value at a Specific Index
```c
int get(DoublyLinkedList *list, int index, uintptr_t *data) {
if (index < 0 || index >= list->size) {
return -1; // Index out of bounds
}
Node *current = list->head;
for (int i = 0; i < index; i++) {
current = current->next;
}
*data = current->data;
return 0; // Success
}
```
### 12. Print List in Reverse
```c
void print_reverse(DoublyLinkedList *list) {
Node *current = list->tail;
printf("List (reversed): ");
while (current != NULL) {
printf("%lu ", current->data);
current = current->prev;
}
printf("\n");
}
```
---
## Time Complexity
| Operation | Best | Average | Worst |
| :-------- | :--- | :------ | :---- |
| Access | O(1) | O(n) | O(n) |
| Search | O(1) | O(n) | O(n) |
| Insertion | O(1) | O(n) | O(n) |
| Deletion | O(1) | O(n) | O(n) |
- **Access/Search**: Best case is O(1) if the element is at the head or tail. Otherwise, it's O(n).
- **Insertion**: O(1) for `push_front` and `push_back`. O(n) for `insert_at`.
- **Deletion**: O(1) for `pop_front` and `pop_back`. O(n) for `remove_at`.
---
## Summary
This guide covers the fundamental operations of a doubly linked list. Understanding these concepts is crucial for building more complex data structures.
+131
View File
@@ -0,0 +1,131 @@
# Heap Data Structure Guide
This guide explains the heap data structure, focusing on the implementation of a Max Heap in C.
---
## Overview
A heap is a specialized tree-based data structure that satisfies the heap property. In a Max Heap, for any given node C, if P is a parent node of C, then the value of P is greater than or equal to the value of C.
### Key Properties
- **Complete Binary Tree**: A heap is typically implemented as a complete binary tree, which means all levels of the tree are fully filled except possibly the last level, which is filled from left to right.
- **Heap Property**: In a Max Heap, parents are always greater than or equal to their children. In a Min Heap, they are less than or equal.
### Diagram of a Max Heap
```mermaid
graph TD
A[100] --> B[70]
A --> C[60]
B --> D[30]
B --> E[20]
C --> F[50]
C --> G[40]
```
---
## Max Heap Implementation
### Structure
```c
typedef struct {
int *array; // Dynamic array to store heap elements
int size; // Current size of the heap
int capacity; // Maximum capacity of the heap
} MaxHeap;
```
### Functions
#### 1. Create a Max Heap
```c
MaxHeap* createMaxHeap(int capacity) {
MaxHeap *heap = (MaxHeap *)malloc(sizeof(MaxHeap));
heap->capacity = capacity;
heap->size = 0;
heap->array = (int *)malloc(capacity * sizeof(int));
return heap;
}
```
#### 2. Swap Utility
```c
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
```
#### 3. Heapify
This function maintains the heap property.
```c
void heapify(MaxHeap *heap, int i) {
int largest = i; // Initialize largest as root
int left = 2 * i + 1;
int right = 2 * i + 2;
// If left child is larger than root
if (left < heap->size && heap->array[left] > heap->array[largest]) {
largest = left;
}
// If right child is larger than largest so far
if (right < heap->size && heap->array[right] > heap->array[largest]) {
largest = right;
}
// If largest is not root, swap and continue heapifying
if (largest != i) {
swap(&heap->array[i], &heap->array[largest]);
heapify(heap, largest);
}
}
```
#### 4. Insert an Element
```c
void insert(MaxHeap *heap, int key) {
if (heap->size == heap->capacity) {
printf("Heap is full, cannot insert %d\n", key);
return;
}
// First insert the new key at the end
heap->array[heap->size] = key;
heap->size++;
// Fix the max heap property if it is violated
// This can be done more efficiently by bubbling up from the inserted element
for (int i = heap->size / 2 - 1; i >= 0; i--) {
heapify(heap, i);
}
}
```
#### 5. Display Heap
```c
void displayHeap(MaxHeap *heap) {
printf("Max Heap: ");
for (int i = 0; i < heap->size; i++) {
printf("%d ", heap->array[i]);
}
printf("\n");
}
```
---
## Summary
Heaps are fundamental for implementing priority queues and are used in the Heap Sort algorithm. The ability to efficiently maintain the heap property is key to their performance.
+174
View File
@@ -0,0 +1,174 @@
# Queue
A **Queue** is a linear data structure that follows the **First-In, First-Out (FIFO)** principle. This means that the first element added to the queue will be the first one to be removed. It is analogous to a real-world queue, such as a line at a checkout counter.
## Structure
A queue can be implemented using an array or a linked list. Here, we will focus on an array-based implementation.
### Key Properties
- **Front**: The index of the first element in the queue.
- **Rear**: The index of the last element in the queue.
- **Items**: The array that stores the elements of the queue.
### Diagram
```mermaid
graph TD
subgraph Queue
direction LR
A[10] --> B[20] --> C[30] --> D[40]
end
subgraph Operations
direction LR
Enqueue --> Rear
Dequeue --> Front
end
Front --> A
Rear --> D
```
## Implementation in C
Here is a simple implementation of a queue in C using an array.
```c
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// Structure to represent a queue
typedef struct {
int items[MAX_SIZE];
int front;
int rear;
} Queue;
// Function to create a new queue
Queue* createQueue() {
Queue* q = (Queue*)malloc(sizeof(Queue));
q->front = -1;
q->rear = -1;
return q;
}
// Function to check if the queue is full
int isFull(Queue* q) {
return q->rear == MAX_SIZE - 1;
}
// Function to check if the queue is empty
int isEmpty(Queue* q) {
return q->front == -1;
}
// Function to add an item to the queue (enqueue)
void enqueue(Queue* q, int value) {
if (isFull(q)) {
printf("Queue is full
");
} else {
if (q->front == -1) {
q->front = 0;
}
q->rear++;
q->items[q->rear] = value;
}
}
// Function to remove an item from the queue (dequeue)
int dequeue(Queue* q) {
int item;
if (isEmpty(q)) {
printf("Queue is empty
");
return -1;
} else {
item = q->items[q->front];
q->front++;
if (q->front > q->rear) {
// Reset queue when it becomes empty
q->front = q->rear = -1;
}
return item;
}
}
// Function to get the front item of the queue without removing it
int peek(Queue* q) {
if (isEmpty(q)) {
printf("Queue is empty
");
return -1;
}
return q->items[q->front];
}
// Function to print the queue
void printQueue(Queue* q) {
if (isEmpty(q)) {
printf("Queue is empty
");
} else {
printf("Queue: ");
for (int i = q->front; i <= q->rear; i++) {
printf("%d ", q->items[i]);
}
printf("
");
}
}
```
## Function Explanations
### `createQueue()`
- **Purpose**: Creates a new, empty queue.
- **Returns**: A pointer to the newly created queue.
### `isFull()`
- **Purpose**: Checks if the queue is full.
- **Returns**: `1` if the queue is full, `0` otherwise.
### `isEmpty()`
- **Purpose**: Checks if the queue is empty.
- **Returns**: `1` if the queue is empty, `0` otherwise.
### `enqueue()`
- **Purpose**: Adds an item to the rear of the queue.
- **Parameters**:
- `q`: A pointer to the queue.
- `value`: The item to be added.
### `dequeue()`
- **Purpose**: Removes an item from the front of the queue.
- **Returns**: The item that was removed, or `-1` if the queue is empty.
### `peek()`
- **Purpose**: Gets the front item of the queue without removing it.
- **Returns**: The front item, or `-1` if the queue is empty.
### `printQueue()`
- **Purpose**: Prints the elements of the queue.
- **Parameters**:
- `q`: A pointer to the queue.
## Time Complexity
| Operation | Complexity |
| :-------- | :--------- |
| Enqueue | O(1) |
| Dequeue | O(1) |
| Peek | O(1) |
| IsEmpty | O(1) |
| IsFull | O(1) |
+164
View File
@@ -0,0 +1,164 @@
# Search Algorithms Guide
This document explains common search algorithms, including Linear and Binary search, as implemented in C.
---
## 1. Linear Search
Linear search is the most straightforward search algorithm. It sequentially checks each element of the list until a match is found or the whole list has been searched.
### How Linear Search Works
1. Start from the first element.
2. Compare the target value with the current element.
3. If they are equal, return the index.
4. If not, move to the next element.
5. If the end of the list is reached without finding the value, the search terminates unsuccessfully.
### Linear Search Diagram
```mermaid
graph TD
subgraph Array
direction LR
A[5] -- B[8] -- C[1] -- D[3] -- E[9]
end
subgraph "Search for 3"
Start --> A --> B --> C --> D{Found at index 3}
end
```
### Iterative Linear Search
```c
// Returns the index of the target value, or -1 if not found.
int linearSearch(int arr[], int n, int target) {
for (int i = 0; i < n; i++) {
if (arr[i] == target) {
return i; // Found
}
}
return -1; // Not found
}
```
### Recursive Linear Search
```c
// Returns the index of the target value, or -1 if not found.
int recursiveLinearSearch(int arr[], int n, int target, int index) {
if (index >= n) {
return -1; // Base case: end of array
}
if (arr[index] == target) {
return index; // Found
}
return recursiveLinearSearch(arr, n, target, index + 1);
}
```
---
## 2. Binary Search
Binary search is an efficient algorithm for finding an item from a **sorted** list of items. It works by repeatedly dividing in half the portion of the list that could contain the item, until you've narrowed down the possible locations to just one.
### How Binary Search Works
1. Compare the target value to the middle element of the array.
2. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half.
3. This process is repeated until the target value is found or the remaining half is empty.
### Binary Search Diagram
```mermaid
graph TD
subgraph Sorted Array
direction LR
A[1] -- B[3] -- C[5] -- D[7] -- E[9] -- F[11] -- G[13]
end
subgraph "Search for 9"
Start --> Mid1{7}
Mid1 --> |9 > 7, search right| Mid2{11}
Mid2 --> |9 < 11, search left| Mid3{9}
Mid3 --> |Found!| End
end
```
### C Implementation using `bsearch`
The C standard library provides a `bsearch` function that performs a binary search.
```c
#include <stdlib.h>
#include <stdio.h>
// Comparison function for qsort and bsearch
int cmpfunc(const void *a, const void *b) {
const int *A = a, *B = b;
return (*A > *B) - (*A < *B);
}
int main() {
int a[] = {1, 2, 3, 7, 3, 9, 11, 13, 42, -1, 4, 52, 10};
int n = sizeof(a) / sizeof(a[0]);
// 1. Sort the array first
qsort(a, n, sizeof(int), cmpfunc);
// 2. Search for an element
int key = 9;
int *item = (int *)bsearch(&key, a, n, sizeof(int), cmpfunc);
if (item != NULL) {
printf("Found item = %d\n", *item);
} else {
printf("Item %d not found\n", key);
}
return 0;
}
```
### Custom Binary Search Application
Here's an example of a binary search-like approach to find an integer `n` such that `n^3 + (n+1)^3` is equal to a given number.
```c
#include <math.h>
#include <stdbool.h>
// Function to find the integer `n`
int _centeredCube(int num) {
int low = -num, high = num, mid, result = 0;
while (low <= high) {
mid = low + (high - low) / 2;
long long thisSum = (long long)pow(mid, 3) + (long long)pow(mid + 1, 3);
if (thisSum == (long long)num)
return mid;
else if (thisSum < num) {
result = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return result;
}
bool centerCube(int num) {
int n = _centeredCube(num);
long long thisSum = (long long)pow(n, 3) + (long long)pow(n + 1, 3);
return thisSum == (long long)num;
}
```
---
## Summary
Binary search is a powerful and efficient search algorithm, but it requires the data to be sorted beforehand. The C standard library's `bsearch` is a convenient way to use it. Linear search, while not as efficient, is simpler and does not require sorted data.
+389
View File
@@ -0,0 +1,389 @@
# Singly Linked List Implementation Guide
This document serves as a study resource for understanding and implementing singly linked lists in C. It includes explanations, code snippets, and diagrams to help you grasp the concepts.
---
## Overview
A singly linked list is a linear data structure consisting of nodes. Each node contains:
- A `value` (data).
- A `next` pointer to the next node in the list.
### Advantages
- Dynamic size.
- Efficient insertion and deletion at the front.
### Disadvantages
- Sequential access (no random access).
- Extra memory for pointers.
---
## Node Structure
```c
typedef struct Node {
uintptr_t value; // Generic data pointer or integer value
struct Node *next; // Pointer to the next node
} Node;
```
### Node Diagram
```mermaid
graph TD
Node1["value: 10"] --> Node2["value: 20"] --> Node3["value: 30"] --> NULL
```
---
## Singly Linked List Structure
```c
typedef struct SinglyLinkedList {
int size; // Number of elements in the list
Node *head; // Pointer to the first node
Node *tail; // Pointer to the last node
} SinglyLinkedList;
```
---
## Functions
### 1. Create a List
```c
SinglyLinkedList *create_list() {
SinglyLinkedList *list = (SinglyLinkedList *)malloc(sizeof(SinglyLinkedList));
if (!list) {
fprintf(stderr, "Memory allocation failed\n");
return NULL;
}
list->size = 0;
list->head = NULL;
list->tail = NULL;
return list;
}
```
### Create List Explanation
- Allocates memory for the list.
- Initializes `size`, `head`, and `tail`.
- Returns a pointer to the list.
---
### 2. Create a Node
```c
Node *create_node(uintptr_t value) {
Node *new_node = (Node *)malloc(sizeof(Node));
if (!new_node) {
fprintf(stderr, "Memory allocation failed\n");
return NULL;
}
new_node->value = value;
new_node->next = NULL;
return new_node;
}
```
### Create Node Explanation
- Allocates memory for a new node.
- Sets the `value` and initializes `next` to `NULL`.
- Returns a pointer to the node.
---
### 3. Append a Value
```c
int append(SinglyLinkedList *list, uintptr_t value) {
Node *new_node = create_node(value);
if (!new_node) {
return -1; // Memory allocation failed
}
if (list->tail == NULL) {
list->head = new_node;
list->tail = new_node;
} else {
list->tail->next = new_node;
list->tail = new_node;
}
list->size++;
return 0; // Success
}
```
### Append Value Diagram
```mermaid
graph TD
Head["Head"] --> Node1["value: 10"] --> Node2["value: 20"] --> Tail["Tail"]
```
### Append Value Explanation
- Creates a new node.
- Adds it to the end of the list.
- Updates `tail` and increments `size`.
---
### 4. Remove the First Value
```c
uintptr_t remove_first(SinglyLinkedList *list) {
if (list->head == NULL) {
fprintf(stderr, "List is empty\n");
return (uintptr_t)-1; // Indicate error
}
Node *temp = list->head;
uintptr_t value = temp->value;
list->head = list->head->next;
if (list->head == NULL) {
list->tail = NULL;
}
free(temp);
list->size--;
return value;
}
```
### Remove First Value Diagram
```mermaid
graph TD
Head["Head"] --> Node2["value: 20"] --> Tail["Tail"]
```
### Remove First Value Explanation
- Removes the first node.
- Updates `head` and `tail` if necessary.
- Frees memory and decrements `size`.
---
### 5. Print the List
```c
void print_list(const SinglyLinkedList *list) {
Node *current = list->head;
printf("List: ");
while (current != NULL) {
printf("%lu ", current->value);
current = current->next;
}
printf("\n");
}
```
### Print List Explanation
- Iterates through the list.
- Prints each node's value.
---
### 6. Free the List
```c
void free_list(SinglyLinkedList *list) {
Node *current = list->head;
while (current != NULL) {
Node *next = current->next;
free(current);
current = next;
}
free(list);
}
```
### Free List Explanation
- Iterates through the list and frees each node.
- Finally, frees the list structure itself.
---
### 7. Prepend a Value
```c
int prepend(SinglyLinkedList *list, uintptr_t value) {
Node *new_node = create_node(value);
if (!new_node) {
return -1; // Memory allocation failed
}
if (list->head == NULL) {
list->head = new_node;
list->tail = new_node;
} else {
new_node->next = list->head;
list->head = new_node;
}
list->size++;
return 0; // Success
}
```
### Prepend Value Explanation
- Creates a new node.
- Adds it to the front of the list.
- Updates `head` and increments `size`.
---
### 8. Insert a Value at a Specific Index
```c
int insert_at(SinglyLinkedList *list, uintptr_t value, int index) {
if (index < 0 || index > list->size) {
return -1; // Index out of bounds
}
if (index == 0) {
return prepend(list, value);
}
if (index == list->size) {
return append(list, value);
}
Node *new_node = create_node(value);
if (!new_node) {
return -1; // Memory allocation failed
}
Node *current = list->head;
for (int i = 0; i < index - 1; i++) {
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
list->size++;
return 0; // Success
}
```
### Insert At Explanation
- Handles edge cases for inserting at the beginning or end.
- Traverses to the node just before the target index.
- Inserts the new node.
---
### 9. Remove the Last Value
```c
uintptr_t remove_last(SinglyLinkedList *list) {
if (list->tail == NULL) {
fprintf(stderr, "List is empty\n");
return (uintptr_t)-1; // Indicate error
}
if (list->head == list->tail) {
return remove_first(list);
}
Node *current = list->head;
while (current->next != list->tail) {
current = current->next;
}
uintptr_t value = list->tail->value;
free(list->tail);
list->tail = current;
list->tail->next = NULL;
list->size--;
return value;
}
```
### Remove Last Explanation
- Traverses to the second-to-last node.
- Removes the last node and updates `tail`.
---
### 10. Remove a Value at a Specific Index
```c
uintptr_t remove_at(SinglyLinkedList *list, int index) {
if (index < 0 || index >= list->size) {
return (uintptr_t)-1; // Index out of bounds
}
if (index == 0) {
return remove_first(list);
}
if (index == list->size - 1) {
return remove_last(list);
}
Node *current = list->head;
for (int i = 0; i < index - 1; i++) {
current = current->next;
}
Node *temp = current->next;
uintptr_t value = temp->value;
current->next = temp->next;
free(temp);
list->size--;
return value;
}
```
### Remove At Explanation
- Traverses to the node just before the target index.
- Removes the target node.
---
### 11. Get a Value at a Specific Index
```c
int get(SinglyLinkedList *list, int index, uintptr_t *value) {
if (index < 0 || index >= list->size) {
return -1; // Index out of bounds
}
Node *current = list->head;
for (int i = 0; i < index; i++) {
current = current->next;
}
*value = current->value;
return 0; // Success
}
```
### Get Explanation
- Traverses to the target index.
- Returns the value via a pointer.
---
## Time Complexity
| Operation | Best | Average | Worst |
| :-------- | :--- | :------ | :---- |
| Access | O(1) | O(n) | O(n) |
| Search | O(1) | O(n) | O(n) |
| Insertion | O(1) | O(n) | O(n) |
| Deletion | O(1) | O(n) | O(n) |
- **Access/Search**: Best case is O(1) if the element is at the head. Otherwise, it's O(n).
- **Insertion**: O(1) for `prepend` and `append`. O(n) for `insert_at`.
- **Deletion**: O(1) for `remove_first`. O(n) for `remove_last` and `remove_at`.
+331
View File
@@ -0,0 +1,331 @@
# Sorting Algorithms Guide
This document provides a guide to various sorting algorithms implemented in C. It includes explanations, code snippets, and diagrams to illustrate how each algorithm works.
---
## 1. Cocktail Shaker Sort
Cocktail Shaker Sort is a variation of Bubble Sort that sorts in both directions on each pass.
### Code
```c
void cocktailShakerSort(int arr[], int n) {
int swapped = 1;
int start = 0;
int end = n - 1;
while (swapped) {
swapped = 0;
// Forward pass
for (int i = start; i < end; ++i) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
swapped = 1;
}
}
if (!swapped) break;
--end;
// Backward pass
for (int i = end - 1; i >= start; --i) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
swapped = 1;
}
}
++start;
}
}
```
---
## 2. Selection Sort
Selection Sort repeatedly finds the minimum element from the unsorted part and puts it at the beginning.
### Iterative Selection Sort
```c
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
```
### Recursive Selection Sort
```c
void recursiveSelectionSort(int arr[], int start, int n) {
if (start >= n - 1) return;
int minIndex = start;
for (int i = start + 1; i < n; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i;
}
}
if (minIndex != start) {
int temp = arr[start];
arr[start] = arr[minIndex];
arr[minIndex] = temp;
}
recursiveSelectionSort(arr, start + 1, n);
}
```
---
## 3. Insertion Sort
Insertion Sort builds the final sorted array one item at a time.
### Iterative Insertion Sort
```c
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
```
### Recursive Insertion Sort
```c
void recursiveInsertionSort(int arr[], int n) {
if (n <= 1) return;
recursiveInsertionSort(arr, n - 1);
int last = arr[n - 1];
int j = n - 2;
while (j >= 0 && arr[j] > last) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = last;
}
```
---
## 4. Merge Sort
Merge Sort is a divide-and-conquer algorithm that divides the array into halves, sorts them, and then merges them.
### Merge Function
```c
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int *left = (int *)malloc(n1 * sizeof(int));
int *right = (int *)malloc(n2 * sizeof(int));
for (int i = 0; i < n1; i++)
left[i] = arr[l + i];
for (int i = 0; i < n2; i++)
right[i] = arr[m + 1 + i];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = left[i];
i++;
k++;
}
while (j < n2) {
arr[k] = right[j];
j++;
k++;
}
free(left);
free(right);
}
```
### Merge Sort Function
```c
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
```
---
## 5. Bubble Sort
Bubble Sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order.
### How It Works
1. Iterate through the array from the first element to the last.
2. For each element, compare it with the next element.
3. If the current element is greater than the next element, swap them.
4. Repeat the process for the entire array until no swaps are needed in a full pass, indicating the array is sorted.
### Diagram
```mermaid
graph TD
subgraph "Pass 1"
A[5, 1, 4, 2, 8] --> B[1, 5, 4, 2, 8] --> C[1, 4, 5, 2, 8] --> D[1, 4, 2, 5, 8]
end
subgraph "Pass 2"
D --> E[1, 2, 4, 5, 8]
end
E --> F[Sorted]
```
### Implementation
```c
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap arr[j] and arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
```
---
## 6. Quick Sort
Quick Sort is a highly efficient, divide-and-conquer sorting algorithm. It works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot.
### Partition Function
```c
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (arr[j] < pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);
}
```
### Quick Sort Function
```c
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);
}
}
```
---
## 7. Heap Sort
Heap Sort is a comparison-based sorting technique based on a Binary Heap data structure. It is similar to selection sort where we first find the maximum element and place the maximum element at the end. We repeat the same process for the remaining elements.
### Heapify Function
```c
void heapify(int arr[], int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && arr[left] > arr[largest])
largest = left;
if (right < n && arr[right] > arr[largest])
largest = right;
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
heapify(arr, n, largest);
}
}
```
### Heap Sort Function
```c
void heapSort(int arr[], int n) {
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
for (int i = n - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, i, 0);
}
}
```
---
## Summary
This guide has covered several common sorting algorithms. Each has its own trade-offs in terms of performance and complexity.
+123
View File
@@ -0,0 +1,123 @@
# Stack Data Structure Guide
This guide covers the stack data structure, a fundamental concept in computer science, with a C implementation using a dynamic array.
---
## Overview
A stack is a linear data structure that follows the Last-In, First-Out (LIFO) principle. The last element added to the stack will be the first one to be removed.
### Key Operations
- **Push**: Adds an element to the top of the stack.
- **Pop**: Removes the top element from the stack.
- **Peek/Top**: Returns the top element without removing it.
- **IsEmpty**: Checks if the stack is empty.
### Diagram of a Stack
```mermaid
graph TD
subgraph Stack
direction TB
Top --> C[30]
C --> B[20]
B --> A[10]
end
subgraph Operations
Push30["Push(30)"] --> Pop30["Pop() -> 30"]
Pop30 --> TopIs20["Top is now 20"]
end
```
---
## Array-based Stack Implementation
### Structure
```c
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef struct {
int *array;
int top;
int capacity;
} Stack;
```
### Functions
#### 1. Create a Stack
```c
Stack* createStack(int capacity) {
Stack* stack = (Stack*)malloc(sizeof(Stack));
stack->capacity = capacity;
stack->top = -1;
stack->array = (int*)malloc(stack->capacity * sizeof(int));
return stack;
}
```
#### 2. IsEmpty
```c
bool isEmpty(Stack* stack) {
return stack->top == -1;
}
```
#### 3. Push
```c
void push(Stack* stack, int item) {
if (stack->top == stack->capacity - 1) {
printf("Stack Overflow\n");
return;
}
stack->array[++stack->top] = item;
}
```
#### 4. Pop
```c
int pop(Stack* stack) {
if (isEmpty(stack)) {
printf("Stack Underflow\n");
return INT_MIN;
}
return stack->array[stack->top--];
}
```
#### 5. Peek
```c
int peek(Stack* stack) {
if (isEmpty(stack)) {
return INT_MIN;
}
return stack->array[stack->top];
}
```
#### 6. Free Stack
```c
void freeStack(Stack* stack) {
free(stack->array);
free(stack);
}
```
---
## Summary
Stacks are simple yet powerful data structures used in many algorithms, such as expression evaluation, backtracking, and managing function calls (the call stack).