0% found this document useful (0 votes)
18 views

Chapter 4

Uploaded by

ndesta2016
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views

Chapter 4

Uploaded by

ndesta2016
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

Chapter- 4 Linked Lists

Singly linked lists


A singly linked list is a data structure where each element (called a node) contains:

1. Data: The actual value to be stored.


2. Pointer: A reference to the next node in the sequence.

In a singly linked list, each node points to the next node in the list, and the last node points to
NULL, indicating the end of the list.

Basic Operations of a Singly Linked List

1. Insertion: Adding a new node at the beginning, end, or at a specific position in the list.
2. Deletion: Removing a node from the beginning, end, or a specific position in the list.
3. Traversal: Accessing each node in the list to read or display data.
4. Search: Finding a node with a specific value.

Here’s a complete C++ implementation of a singly linked list:

#include <iostream>

using namespace std;

// Define the Node structure

struct Node {

int data; // The data the node holds

Node* next; // Pointer to the next node in the list

// Constructor to initialize a node

Node(int value) : data(value), next(NULL) {} // Use NULL

};
// Define the SinglyLinkedList class

class SinglyLinkedList {

private:

Node* head; // Pointer to the head of the list

public:

// Constructor to initialize an empty list

SinglyLinkedList() : head(NULL) {} // Use NULL

// Insert a new node at the beginning of the list

void insertAtBeginning(int value) {

Node* newNode = new Node(value); // Create a new node

newNode->next = head; // Make the new node point to the


current head

head = newNode; // Update the head to the new node

// Insert a new node at the end of the list

void insertAtEnd(int value) {

Node* newNode = new Node(value); // Create a new node

if (head == NULL) { // If the list is empty, new node


becomes head

head = newNode;

return;

Node* temp = head;


while (temp->next != NULL) { // Traverse to the last node

temp = temp->next;

temp->next = newNode; // Point the last node to the new


node

// Insert a new node after a specific position

void insertAfterPosition(int position, int value) {

Node* newNode = new Node(value);

Node* temp = head;

for (int i = 1; i < position && temp != NULL; ++i) {

temp = temp->next;

if (temp == NULL) {

cout << "Position out of bounds." << endl;

delete newNode;

return;

newNode->next = temp->next; // Link new node with next node

temp->next = newNode; // Link current node to new node

}
// Delete a node from the beginning

void deleteFromBeginning() {

if (head == NULL) {

cout << "List is empty." << endl;

return;

Node* temp = head;

head = head->next; // Move head to the next node

delete temp; // Free memory of the removed node

// Delete a node from the end

void deleteFromEnd() {

if (head == NULL) {

cout << "List is empty." << endl;

return;

if (head->next == NULL) {

delete head;

head = NULL;

return;

Node* temp = head;


while (temp->next->next != NULL) {

temp = temp->next;

delete temp->next;

temp->next = NULL;

// Delete a node by value

void deleteByValue(int value) {

if (head == NULL) {

cout << "List is empty." << endl;

return;

if (head->data == value) {

Node* temp = head;

head = head->next;

delete temp;

return;

Node* temp = head;

while (temp->next != NULL && temp->next->data != value) {

temp = temp->next;

}
if (temp->next == NULL) {

cout << "Value not found in the list." << endl;

return;

Node* nodeToDelete = temp->next;

temp->next = nodeToDelete->next;

delete nodeToDelete;

// Display the list

void display() {

Node* temp = head;

while (temp != NULL) {

cout << temp->data << " -> ";

temp = temp->next;

cout << "NULL" << endl;

// Destructor to free all nodes in the list

~SinglyLinkedList() {

Node* temp = head;

while (temp != NULL) {

Node* nextNode = temp->next;


delete temp;

temp = nextNode;

};

int main() {

SinglyLinkedList list;

// Insert nodes

list.insertAtBeginning(10);

list.insertAtEnd(20);

list.insertAtEnd(30);

list.insertAfterPosition(2, 25); // Insert 25 after the 2nd position

cout << "List after insertions: ";

list.display();

// Delete nodes

list.deleteFromBeginning();

cout << "List after deleting from beginning: ";

list.display();

list.deleteFromEnd();

cout << "List after deleting from end: ";

list.display();
list.deleteByValue(25);

cout << "List after deleting by value 25: ";

list.display();

return 0;

Doubly linked lists


A doubly linked list is a data structure where each element (called a node) contains:

1. Data: The actual value to be stored.


2. Pointer to the next node: A reference to the next node in the sequence.
3. Pointer to the previous node: A reference to the previous node in the sequence.

In a doubly linked list, each node points to both its next and previous nodes, allowing traversal in
both directions (forward and backward).

Basic Operations of a Doubly Linked List

1. Insertion: Adding a new node at the beginning, end, or a specific position.


2. Deletion: Removing a node from the beginning, end, or a specific position.
3. Traversal: Accessing each node to read or display data from the start or end.
4. Search: Finding a node with a specific value.

C++ Implementation of a Doubly Linked List

Here’s a complete C++ example that demonstrates the basic operations for a doubly linked list.

#include <iostream>
using namespace std;

// Define the Node structure


struct Node {
int data; // Data the node holds
Node* next; // Pointer to the next node
Node* prev; // Pointer to the previous node

// Constructor to initialize a node


Node(int value) : data(value), next(NULL), prev(NULL) {}
};
// Define the DoublyLinkedList class
class DoublyLinkedList {
private:
Node* head; // Pointer to the head of the list
Node* tail; // Pointer to the tail of the list

public:
// Constructor to initialize an empty list
DoublyLinkedList() : head(NULL), tail(NULL) {}

// Insert a new node at the beginning of the list


void insertAtBeginning(int value) {
Node* newNode = new Node(value); // Create a new node
if (head == NULL) { // If the list is empty
head = tail = newNode; // New node is both head and tail
} else {
newNode->next = head; // Link new node to the current
head
head->prev = newNode; // Link current head back to new
node
head = newNode; // Update head to new node
}
}

// Insert a new node at the end of the list


void insertAtEnd(int value) {
Node* newNode = new Node(value); // Create a new node
if (tail == NULL) { // If the list is empty
head = tail = newNode; // New node is both head and tail
} else {
newNode->prev = tail; // Link new node to current tail
tail->next = newNode; // Link current tail to new node
tail = newNode; // Update tail to new node
}
}

// Delete a node from the beginning


void deleteFromBeginning() {
if (head == NULL) { // List is empty
cout << "List is empty." << endl;
return;
}

Node* temp = head;


if (head == tail) { // Only one node in the list
head = tail = NULL;
} else {
head = head->next; // Move head to the next node
head->prev = NULL; // Remove link to the old head
}
delete temp; // Free memory of the removed node
}

// Delete a node from the end


void deleteFromEnd() {
if (tail == NULL) { // List is empty
cout << "List is empty." << endl;
return;
}

Node* temp = tail;


if (head == tail) { // Only one node in the list
head = tail = NULL;
} else {
tail = tail->prev; // Move tail to the previous node
tail->next = NULL; // Remove link to the old tail
}
delete temp; // Free memory of the removed node
}

// Display the list from head to tail


void displayForward() {
Node* temp = head;
while (temp != NULL) {
cout << temp->data << " <-> ";
temp = temp->next;
}
cout << "NULL" << endl;
}

// Display the list from tail to head


void displayBackward() {
Node* temp = tail;
while (temp != NULL) {
cout << temp->data << " <-> ";
temp = temp->prev;
}
cout << "NULL" << endl;
}

// Destructor to free all nodes in the list


~DoublyLinkedList() {
Node* temp = head;
while (temp != NULL) {
Node* nextNode = temp->next;
delete temp;
temp = nextNode;
}
}
};

int main() {
DoublyLinkedList list;

// Insert nodes
list.insertAtBeginning(10);
list.insertAtEnd(20);
list.insertAtEnd(30);
list.insertAtBeginning(5);

cout << "List displayed forward: ";


list.displayForward();
cout << "List displayed backward: ";
list.displayBackward();

// Delete nodes
list.deleteFromBeginning();
cout << "List after deleting from beginning: ";
list.displayForward();

list.deleteFromEnd();
cout << "List after deleting from end: ";
list.displayForward();

return 0;
}

Circular lists
A circular list is a variation of a linked list in which the last node points back to the first node,
creating a circular structure with no beginning or end. Circular linked lists can be either singly or
doubly linked, but here, we will focus on the singly linked circular list.

Characteristics of a Circular Linked List

1. The last node’s next pointer points back to the first node, creating a loop.
2. There is no NULL pointer in the list, unlike a traditional singly linked list.
3. Traversing a circular linked list requires special handling to avoid infinite loops, as there
is no natural endpoint (NULL).

Advantages of a Circular Linked List

 Efficient Circular Traversal: Useful when you need to loop through data continuously,
such as in round-robin scheduling.
 Constant Time Insertions: Adding or removing elements from both ends of the list can
be done in constant time, provided you maintain a pointer to the last node.

Example of a Circular Linked List in C++

Let’s create a simple circular linked list with the following operations:

1. Insert a node at the end of the list.


2. Display the entire list.

Code Implementation

Here's how you can implement a circular linked list in C++:

#include <iostream>
using namespace std;
class Node {
public:
int data; // Data part of the node
Node* next; // Pointer to the next node

Node(int value) {
data = value;
next = NULL;
}
};

// Define the circular linked list class


class CircularLinkedList {
private:
Node* last; // Pointer to the last node of the circular linked
list

public:
// Constructor to initialize an empty list
CircularLinkedList() {
last = NULL;
}

// Function to insert a new node at the end of the circular linked list
void insertAtEnd(int value) {
Node* newNode = new Node(value); // Create a new node

if (last == NULL) {
// If the list is empty, make the new node point to itself and
set it as last
last = newNode;
last->next = last;
} else {
// Insert the new node after the last node and update pointers
newNode->next = last->next; // New node points to the first
node
last->next = newNode; // Last node points to the new
node
last = newNode; // Update last to the new node
}
}

// Function to display the circular linked list


void display() {
if (last == NULL) {
cout << "List is empty." << endl;
return;
}

Node* temp = last->next; // Start from the first node


do {
cout << temp->data << " "; // Print the data
temp = temp->next; // Move to the next node
} while (temp != last->next); // Continue until we loop back to
the first node
cout << endl;
}

// Destructor to clean up memory


~CircularLinkedList() {
if (last != NULL) {
Node* current = last->next; // Start from the first node
Node* nextNode;

do {
nextNode = current->next; // Save the next node
delete current; // Delete the current node
current = nextNode; // Move to the next node
} while (current != last->next); // Stop when back to the first
node

last = NULL; // Set last to nullptr to mark the list as empty


}
}
};

int main() {
CircularLinkedList list;

// Insert elements in the circular linked list


list.insertAtEnd(10);
list.insertAtEnd(20);
list.insertAtEnd(30);
list.insertAtEnd(40);

// Display the circular linked list


cout << "Circular Linked List: ";
list.display();

return 0;
}

Skip Lists
A skip list is a probabilistic data structure that allows for fast search, insertion, and deletion
operations. It is an alternative to balanced trees, providing a simpler and often more efficient way
to maintain a sorted list of elements. Skip lists achieve logarithmic time complexity for these
operations through a layered structure that allows for "skipping" over elements.

Structure of Skip Lists

A skip list consists of multiple levels of linked lists:

 The bottom level contains all the elements sorted in increasing order.
 Each higher level acts as an "express lane" for the level below it, containing a subset of the
elements from the level directly below.
 Each element in a higher level has a pointer to a subsequent element in the same level and also
points to elements in the lower level.
How Skip Lists Work

1. Levels: A skip list has multiple levels (or layers). The lowest level contains all the
elements, while each subsequent level contains fewer elements. The probability of an
element being included in a higher level is typically set at 50%.
2. Insertion: When a new element is inserted:
o It is added to the bottom level first.
o Then, a random number of pointers are created for the new element, which may place it
into higher levels.
o The number of levels is determined randomly, often using a coin flip.
3. Search: Searching for an element starts at the highest level and moves horizontally
across pointers until the target value is greater than or less than the current element. If the
value is less than the current element, the search moves down to the next level. This
continues until the element is found or the search reaches the bottom level.
4. Deletion: Deletion works similarly to insertion. The element is located and removed from
all levels where it appears.

Advantages of Skip Lists

 Simplicity: Skip lists are simpler to implement than balanced trees.


 Probabilistic Balancing: They automatically balance themselves through randomization,
providing average-case logarithmic performance.
 Dynamic Size: Like linked lists, skip lists can grow and shrink dynamically without the need for
reallocation.

Disadvantages of Skip Lists

 Randomness: The performance is probabilistic, so the worst-case performance can degrade to


linear time, although this is rare.
 Memory Overhead: Because of the multiple levels and pointers, skip lists can consume more
memory than simpler structures like linked lists.

Example of a Skip List

Here's a simplified view of how a skip list might look:

rust
Copy code
Level 3: |----> 7 ----> 20 ----> 50 ---->
Level 2: |--> 3 --> 7 --> 15 --> 20 --> 30 --> 50 --> 60
Level 1: |--> 1 --> 2 --> 3 --> 4 --> 5 --> 7 --> 10 --> 15 --> 20 -->
30 --> 50 --> 60
Level 0: 1 2 3 4 5 6 7 8 9 10 15 20 30 50 60

Operations
Insertion

To insert a value:

1. Start from the highest level and move right until you find a value greater than the value to be
inserted.
2. Move down to the next level and repeat until you reach the bottom level.
3. Insert the new value at the bottom level.
4. Randomly determine how many levels the new value will occupy above the bottom level and
insert it into those levels.

Search

To search for a value:

1. Start at the highest level.


2. Move right until you find a value greater than the target value or reach the end of that level.
3. Move down to the next lower level and repeat.
4. If you reach the bottom level and find the value, it exists in the list.

Deletion

To delete a value:

1. Search for the value in the skip list.


2. If found, remove it from all levels where it exists.

Example Code for Skip List in C++

Here is a basic implementation of a skip list in C++

#include <iostream>

#include <cstdlib>

#include <vector>

#include <ctime>

using namespace std;

class Node {

public:

int value;

vector<Node*> forward; // Pointers to next nodes


Node(int level, int value) {

this->value = value;

forward.resize(level + 1, NULL); // Initialize forward pointers

};

class SkipList {

private:

int maxLevel; // Maximum level for the skip list

float p; // Probability for level increase

Node* header; // Header node

int currentLevel; // Current level of the skip list

public:

SkipList(int maxLevel, float p) : maxLevel(maxLevel), p(p),


currentLevel(0) {

header = new Node(maxLevel, -1); // Create header node with dummy


value

// Function to generate a random level

int randomLevel() {

int level = 0;

while (static_cast<float>(rand()) / RAND_MAX < p && level < maxLevel)


{

level++;
}

return level;

// Function to insert a new value

void insert(int value) {

vector<Node*> update(maxLevel + 1);

Node* current = header;

// Start from the highest level and find the position to insert

for (int i = currentLevel; i >= 0; i--) {

while (current->forward[i] != NULL && current->forward[i]->value


< value) {

current = current->forward[i];

update[i] = current; // Keep track of last node at each level

// Move to the next node at level 0

current = current->forward[0];

// If current is nullptr or current's value is not equal to value,


insert

if (current == NULL || current->value != value) {

int newLevel = randomLevel();

// If new level is greater than current level, update the list


if (newLevel > currentLevel) {

for (int i = currentLevel + 1; i <= newLevel; i++) {

update[i] = header; // Update the update vector

currentLevel = newLevel;

Node* newNode = new Node(newLevel, value);

// Update forward pointers for the new node

for (int i = 0; i <= newLevel; i++) {

newNode->forward[i] = update[i]->forward[i];

update[i]->forward[i] = newNode; // Insert new node

// Function to display the skip list

void display() {

cout << "Skip List:\n";

for (int i = currentLevel; i >= 0; i--) {

Node* node = header->forward[i];

cout << "Level " << i << ": ";

while (node != NULL) {

cout << node->value << " ";

node = node->forward[i];
}

cout << "\n";

// Destructor to free the memory

~SkipList() {

Node* current = header;

while (current != NULL) {

Node* temp = current;

current = current->forward[0]; // Move to next node

delete temp; // Delete the node

};

int main() {

srand(time(0)); // Seed for random number generation

SkipList skipList(3, 0.5); // Max level 3, probability 0.5

// Insert elements into the skip list

skipList.insert(3);

skipList.insert(6);

skipList.insert(7);

skipList.insert(9);

skipList.insert(12);
skipList.insert(19);

skipList.insert(17);

skipList.insert(26);

skipList.insert(21);

skipList.insert(25);

// Display the skip list

skipList.display();

return 0;

Explanation of the Code

1. Node Class: Represents a node in the skip list, containing the value and a vector of
forward pointers to support multiple levels.
2. SkipList Class:
o maxLevel: The maximum number of levels in the skip list.
o p: Probability factor for generating levels.
o header: The header node that acts as a starting point for all levels.
o currentLevel: Tracks the current highest level in the list.
o randomLevel(): Generates a random level for a new node based on the probability p.
o insert(int value): Inserts a new value into the skip list, adjusting pointers and levels as
necessary.
o display(): Outputs the elements in each level of the skip list.
o Destructor: Cleans up memory used by the skip list.
3. Main Function:
o Initializes the skip list and inserts several values.
o Displays the skip list.

Output

The output will show the elements at each level of the skip list. Due to the randomness of the
level generation, the exact output will vary each time you run the program.
Self-organizing lists
Self-organizing lists are dynamic data structures that adapt their ordering based on access
patterns, allowing frequently accessed elements to be moved closer to the front of the list. This
optimization helps reduce access times for commonly used elements. The basic idea is to
rearrange the elements of the list upon access (search), effectively making the most frequently
accessed items quicker to reach in subsequent operations.

Characteristics of Self-Organizing Lists

1. Dynamic Rearrangement: The list rearranges itself based on access frequency or


recency.
2. Improved Access Times: The average time for accessing elements in the list can be
reduced over time, especially for those that are accessed frequently.
3. Various Strategies: Common strategies include Move-To-Front and Frequency-Based
ordering.

Common Strategies

1. Move-To-Front: When an element is accessed, it is moved to the front of the list. This
strategy is effective if there are a few frequently accessed elements.
2. Frequency-Based: Elements are ordered based on how frequently they are accessed.
More frequently accessed elements are moved toward the front based on their access
count.

Example Implementation: Move-To-Front Strategy

Here’s a simple implementation of a self-organizing list using the Move-To-Front strategy in


C++:

#include <iostream>

#include <string>

using namespace std;

class SelfOrganizingList {

private:

std::string* elements; // Dynamic array to store elements

int size; // Current size of the list

int capacity; // Capacity of the dynamic array


// Function to resize the array when capacity is reached

void resize() {

capacity *= 2;

string* newElements = new string[capacity];

for (int i = 0; i < size; ++i) {

newElements[i] = elements[i];

delete[] elements;

elements = newElements;

public:

SelfOrganizingList() {

capacity = 2; // Initial capacity

elements = new string[capacity];

size = 0;

// Destructor to free memory

~SelfOrganizingList() {

delete[] elements;

// Function to access an element

void access(const string& element) {

// Search for the element


int index = -1;

for (int i = 0; i < size; ++i) {

if (elements[i] == element) {

index = i;

break;

if (index != -1) {

// Move the accessed element to the front

string temp = elements[index];

for (int i = index; i > 0; --i) {

elements[i] = elements[i - 1];

elements[0] = temp; // Move to front

} else {

// If the element does not exist, add it to the front

if (size >= capacity) {

resize(); // Resize if capacity is reached

// Shift elements to make space at the front

for (int i = size; i > 0; --i) {

elements[i] = elements[i - 1];

elements[0] = element; // Insert at front

size++;
}

// Function to display the current order of elements

void display() {

cout << "Current order of elements: ";

for (int i = 0; i < size; ++i) {

cout << elements[i] << " ";

cout << endl;

};

int main() {

SelfOrganizingList sol;

// Access elements

sol.access("grape");

sol.access("banana");// Access "banana" again, should move to front

sol.access("apple");

sol.access("banana");

sol.access("orange");

sol.access("apple"); // Access "apple" again, should move to front


// Display the current order of elements

sol.display();

return 0;

Explanation of the Code

1. Dynamic Array:
o We use a dynamic array (string* elements) to store the elements. This allows
us to manage memory manually, simulating the behavior of older C++ standards.
o size keeps track of the current number of elements, while capacity represents
the maximum number of elements the array can currently hold.
2. Resize Function:
o The resize function doubles the capacity of the array when needed. It creates a
new array, copies existing elements, and deletes the old array.
3. Access Function:
o The access function checks if the element exists in the array.
o If it does, it moves that element to the front by shifting the other elements down.
o If the element does not exist, it inserts it at the front, shifting the rest of the
elements accordingly.
4. Display Function: This function outputs the current order of elements in the list.
5. Destructor: Cleans up dynamically allocated memory when the SelfOrganizingList
object is destroyed.
6. Main Function: Demonstrates the usage of the SelfOrganizingList class by accessing
a series of elements and displaying the resulting order.

Conclusion

Self-organizing lists are useful data structures for optimizing access times based on usage
patterns. The Move-To-Front strategy is a simple and effective way to implement this concept.
By utilizing lists and hash maps, we can efficiently manage and access elements, adapting the
structure dynamically based on real-time usage. This makes self-organizing lists a powerful tool
in scenarios where certain elements are accessed more frequently than others.

Sparse Tables

Sparse Tables are a powerful data structure primarily used for answering range queries
efficiently, especially for idempotent operations, where applying the operation multiple
times doesn't change the result (e.g., minimum, maximum, greatest common divisor).
Sparse Tables are widely used for range minimum/maximum queries (RMQ) due to their
efficiency and ease of implementation.

Key Characteristics of Sparse Table:

Characteristics of Sparse Tables

1. Preprocessing: The Sparse Table is built in O(n log n) time. The preprocessing involves
filling a table that can answer queries about minimum (or maximum) values in
logarithmic range segments of the array.
2. Query Time: After preprocessing, the Sparse Table can answer range minimum queries
in O(1) time.
3. Space Complexity: The space complexity is O(n log n), as we store multiple segments of
the array.

Structure of the Sparse Table

Given an array arr, a Sparse Table st is created such that st[i][j] holds the minimum value in
the subarray starting at index i and covering the next 2^j elements.

Formula:
1. Base Case: st[i][j]=arr[i]
2. Recursive Case: For each cell, compute :st[i][j] = min(st[i][j−1], st[i+2(j−1)][j−1])

This means that to compute the minimum of length 2^j starting at i, we use two
overlapping subarrays of length 2^{(j - 1)}.

For a range query [L, R], find the largest power of two that fits into the range and use
it.

Example

Let's say we have an array arr = [1, 3, 2, 7, 9, 11, 3, 5].

1. Step 1: Build the Sparse Table

Construction

 Initialization:
o st[i][0] is initialized to arr[i] for all i.
 Filling the Sparse Table:
o For j>0 , compute the minimum for segments of length 2^j based on previously
computed values.

Explanation:

 st[0][1] = min of arr[0] to arr[1] (1, 3) = 1


 st[0][2] = min of arr[0] to arr[3] (1, 3, 2, 7) = 1
 st[1][1] = min of arr[1] to arr[2] (3, 2) = 2
 And so forth...

Step 2: Querying

Complete C++ Implementation

Here's a complete C++ implementation of a Sparse Table:

#include <iostream>

#include <cmath>

#include <vector>

using namespace std;

class SparseTable {

private:

vector<vector<int> > st; // Sparse Table

vector<int> arr; // Original array


int n; // Size of the array

public:

SparseTable(const vector<int>& input) {

arr = input;

n = arr.size();

int logn = log2(n) + 1;

st.resize(n, vector<int>(logn));

// Initialize st[i][0] for the intervals of length 1

for (int i = 0; i < n; i++) {

st[i][0] = arr[i];

// Fill the Sparse Table

for (int j = 1; (1 << j) <= n; j++) {

for (int i = 0; (i + (1 << j) - 1) < n; i++) {

st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);

// Query the minimum value in range [L, R]

int query(int L, int R) {

int j = log2(R - L + 1); // Find the largest power of 2

return min(st[L][j], st[R - (1 << j) + 1][j]);


}

};

int main() {

// Older C++ initialization without list initialization

vector<int> arr;

arr.push_back(1);

arr.push_back(3);

arr.push_back(2);

arr.push_back(7);

arr.push_back(9);

arr.push_back(11);

arr.push_back(3);

arr.push_back(5);

SparseTable st(arr);

cout << "Minimum value in range [1, 5]: " << st.query(1, 5) << endl; //
Output: 2

cout << "Minimum value in range [0, 3]: " << st.query(0, 3) << endl; //
Output: 1

cout << "Minimum value in range [4, 7]: " << st.query(4, 7) << endl; //
Output: 3

return 0;

} Explanation of the Code

1. Initialization:
o The constructor initializes the Sparse Table based on the input array and calculates the
minimum values for each segment of lengths 2j2^j2j.
2. Query Function:
o The query method takes two indices, LLL and RRR, and computes the minimum value in
the range by comparing the appropriate segments in the Sparse Table.

Conclusion

Sparse Tables are an efficient solution for static range query problems, particularly for range
minimum or maximum queries. They provide a way to preprocess the data for very fast queries,
making them suitable for situations where data does not change frequently.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy