Open In App

Proto Van Emde Boas Tree | Set 5 | Queries: Minimum, Maximum

Improve
Improve
Like Article
Like
Save
Share
Report

Please check previous sets of Proto Van Emde Boas Tree article first. It is highly recommended.
 

Procedure for finding minimum:  

  1. Base Case: If the size of Proto-VEB is 2 then we will return smallest key present in the cluster if no keys present then we will return -1 as the symbol that no keys are present.
  2. Recursion: 
    • We will start recursion over summary until we reach the first true value(In the code, first not nullptr in the summary cluster) which shows that there is a key present in that cluster.
    • Now We will find the position of the key in that cluster using again recursively call over a cluster to find the first true value (In the code, first not nullptr in the cluster) in a cluster as we have done above. 
       
    • Finally, we will return the index of that key according to cluster number we get from procedure over summary and position we get from the procedure over the cluster in the last step.


See the image below for the basic understanding of the operation:
Observe the light green circles from top to bottom:
 

VEB minimum query


See the image below for real Proto – VEB minimum operation:
Follow the instructions in order of numberings. 
 

Minimum Query : Proto-VEB


You can easily get the idea of Maximum from the minimum procedure. See the image below:
Observe the light green circles from top to bottom:
 

Maximum Query in Proto-VEB


Implementation of the above algorithm:
 

C++

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
 
class Proto_Van_Emde_Boas {
public:
    // Total number of keys
    int universe_size;
 
    // Summary
    Proto_Van_Emde_Boas* summary;
 
    // Clusters array of Proto-VEB pointers
    vector<Proto_Van_Emde_Boas*> clusters;
 
    int root(int u)
    {
        return (int)sqrt(u);
    }
 
    // Function to return cluster numbers
    // in which key is present
    int high(int x)
    {
        return x / root(universe_size);
    }
 
    // Function to return position of x in cluster
    int low(int x)
    {
        return x % root(universe_size);
    }
 
    // Function to return the index from
    // cluster number and position
    int generate_index(int cluster, int position)
    {
        return cluster * root(universe_size) + position;
    }
 
    // Constructor
    Proto_Van_Emde_Boas(int size)
    {
        universe_size = size;
 
        // Base case
        if (size <= 2) {
 
            // Set summary to nullptr as there is no
            // more summary for size 2
            summary = nullptr;
 
            // Vector of two pointers
            // nullptr in starting
            clusters = vector<Proto_Van_Emde_Boas*>(size, nullptr);
        }
        else {
 
            // Assigning Proto-VEB(sqrt(u)) to summary
            summary = new Proto_Van_Emde_Boas(root(size));
 
            // Creating array of Proto-VEB Tree pointers of size sqrt(u)
            // first all nullptrs are going to assign
            clusters = vector<Proto_Van_Emde_Boas*>(root(size), nullptr);
 
            // Assigning Proto-VEB(sqrt(u)) to all its clusters
            for (int i = 0; i < root(size); i++) {
                clusters[i] = new Proto_Van_Emde_Boas(root(size));
            }
        }
    }
};
 
// Function that returns true if the
// key is present in the tree
bool isMember(Proto_Van_Emde_Boas* helper, int key)
{
 
    // If key is greater then universe_size then
    // returns false
    if (key >= helper->universe_size)
        return false;
 
    // If we reach at base case
    // the just return whether
    // pointer is nullptr then false
    // else return true
    if (helper->universe_size == 2) {
        return helper->clusters[key];
    }
    else {
 
        // Recursively go deep into the
        // level of Proto-VEB tree using its
        // cluster index and its position
        return isMember(helper->clusters[helper->high(key)],
                        helper->low(key));
    }
}
 
// Function to insert a key in the tree
void insert(Proto_Van_Emde_Boas*& helper, int key)
{
    // If we reach at base case
    // then assign Proto-VEB(1) in place
    // of nullptr
    if (helper->universe_size == 2) {
        helper->clusters[key] = new Proto_Van_Emde_Boas(1);
    }
    else {
 
        // Recursively using index of cluster and its
        // position in cluster
        insert(helper->clusters[helper->high(key)],
               helper->low(key));
 
        // Also do the same recursion in summary VEB
        insert(helper->summary, helper->high(key));
    }
}
 
// Function to return the minimum key from the tree
int minimum(Proto_Van_Emde_Boas* helper)
{
    // Base case choses the least key
    // present in the cluster
    if (helper->universe_size == 2) {
        if (helper->clusters[0]) {
            return 0;
        }
        else if (helper->clusters[1]) {
            return 1;
        }
 
        // No keys present then return -1
        return -1;
    }
    else {
 
        // Recursively find in summary for
        // first 1 present in Proto-VEB
        int minimum_cluster = minimum(helper->summary);
        int offset;
 
        // If no key is present in
        // the cluster then return -1
        if (minimum_cluster == -1) {
            return -1;
        }
        else {
 
            // Recursively find the position of the key
            // in the minimum_cluster
            offset = minimum(helper->clusters[minimum_cluster]);
 
            // Returns overall index of minimum key
            return helper->generate_index(minimum_cluster, offset);
        }
    }
}
 
// Function to return the maximum key from the tree
int maximum(Proto_Van_Emde_Boas* helper)
{
 
    // Return the maximum key present in
    // the cluster
    if (helper->universe_size == 2) {
        if (helper->clusters[1]) {
            return 1;
        }
        else if (helper->clusters[0]) {
            return 0;
        }
 
        // Return -1 if no keys present in the
        // cluster
        return -1;
    }
    else {
 
        // Recursively find the last 1 present
        // in the summary
        int maximum_cluster = maximum(helper->summary);
        int offset;
 
        // If no key is present in
        // the cluster then return -1
        if (maximum_cluster == -1) {
            return -1;
        }
        else {
 
            // Recursively find the position of the key
            // in the maximum_cluster
            offset = maximum(helper->clusters[maximum_cluster]);
            return helper->generate_index(maximum_cluster, offset);
        }
    }
}
 
// Function to delete a key from the tree
void pveb_delete(Proto_Van_Emde_Boas*& helper, int key)
{
 
    // Base case: If the key is present
    // then make it nullptr
    if (helper->universe_size == 2) {
        if (helper->clusters[key]) {
            delete helper->clusters[key];
            helper->clusters[key] = nullptr;
        }
    }
    else {
 
        // Recursive delete to reach at the base case
        pveb_delete(helper->clusters[helper->high(key)], helper->low(key));
 
        bool isanyinCluster = false;
 
        // Iterate over the cluster of keys to check whether
        // any other key is present within that cluster
        // If yes then we should not update summary to 0
        // else update summary to 0
        for (int i = helper->high(key) * helper->root(helper->universe_size);
             i < (helper->high(key) + 1) * helper->root(helper->universe_size);
             i++) {
 
            // If member is present then break the loop
            if (isMember(helper->clusters[helper->high(key)], i)) {
                isanyinCluster = true;
                break;
            }
        }
 
        // If no member is present then
        // update summary to zero
        if (isanyinCluster == false) {
            pveb_delete(helper->summary, helper->high(key));
        }
    }
}
 
// Driver code
int main()
{
    Proto_Van_Emde_Boas* hello = new Proto_Van_Emde_Boas(4);
 
    cout << boolalpha;
 
    insert(hello, 2);
 
    insert(hello, 3);
 
    cout << minimum(hello) << endl;
 
    cout << maximum(hello) << endl;
}

                    

Java

// Java implementation
import java.util.ArrayList;
 
class Proto_Van_Emde_Boas {
    public int universe_size;
    public Proto_Van_Emde_Boas summary;
 
    // Clusters array of Proto-VEB pointers
    public ArrayList<Proto_Van_Emde_Boas> clusters;
 
    public Proto_Van_Emde_Boas(int size)
    {
        universe_size = size;
        if (size <= 2) {
            summary = null;
            clusters
                = new ArrayList<Proto_Van_Emde_Boas>(size);
            for (int i = 0; i < size; i++) {
                clusters.add(null);
            }
        }
        else {
            summary = new Proto_Van_Emde_Boas(root(size));
            clusters = new ArrayList<Proto_Van_Emde_Boas>(
                root(size));
            for (int i = 0; i < root(size); i++) {
                clusters.add(
                    new Proto_Van_Emde_Boas(root(size)));
            }
        }
    }
 
    public int root(int u) { return (int)Math.sqrt(u); }
    // Function to return cluster numbers
    // in which key is present
    public int high(int x)
    {
        return x / root(universe_size);
    }
 
    // Function to return position of x in cluster
    public int low(int x)
    {
        return x % root(universe_size);
    }
 
    // Function to return the index from
    // cluster number and position
    public int generate_index(int cluster, int position)
    {
        return cluster * root(universe_size) + position;
    }
}
 
class Main {
    // Function to insert a key in the tree
    public static void insert(Proto_Van_Emde_Boas helper,
                              int key)
    {
 
        // If we reach at base case
        // then assign Proto-VEB(1) in place
        // of nullptr
        if (helper.universe_size == 2) {
            helper.clusters.set(key,
                                new Proto_Van_Emde_Boas(1));
        }
        else {
            // Recursively using index of cluster and its
            // position in cluster
            insert(helper.clusters.get(helper.high(key)),
                   helper.low(key));
            insert(helper.summary, helper.high(key));
        }
    }
 
    // Function to return the minimum key from the tree
    public static int minimum(Proto_Van_Emde_Boas helper)
    {
 
        // Base case choses the least key
        // present in the cluster
        if (helper.universe_size == 2) {
            if (helper.clusters.get(0) != null) {
                return 0;
            }
            else if (helper.clusters.get(1) != null) {
                return 1;
            }
            return -1;
        }
        else {
 
            // Recursively find in summary for
            // first 1 present in Proto-VEB
            int minimum_cluster = minimum(helper.summary);
            int offset;
 
            if (minimum_cluster
                == -1) { // If no key then return -1
                return -1;
            }
            else {
 
                // Recursively find the position of the key
                // in the minimum_cluster
                offset = minimum(
                    helper.clusters.get(minimum_cluster));
                return helper.generate_index(
                    minimum_cluster, offset);
            }
        }
    }
 
    // Function to return the maximum key from the tree
    public static int maximum(Proto_Van_Emde_Boas helper)
    {
        if (helper.universe_size == 2) {
            if (helper.clusters.get(1) != null) {
                return 1;
            }
            else if (helper.clusters.get(0) != null) {
                return 0;
            }
            return -1; // If no key then return -1
        }
        else {
 
            // Recursively find in summary for
            // first 1 present in Proto-VEB
            int maximum_cluster = maximum(helper.summary);
            int offset;
 
            if (maximum_cluster == -1) {
                return -1; // If no key then return -1
            }
            else {
 
                // Recursively find the position of the key
                // in the minimum_cluster
                offset = maximum(
                    helper.clusters.get(maximum_cluster));
                return helper.generate_index(
                    maximum_cluster, offset);
            }
        }
    }
 
    // Driver code
    public static void main(String[] args)
    {
 
        Proto_Van_Emde_Boas hello
            = new Proto_Van_Emde_Boas(4);
        // inserting
        insert(hello, 2);
        insert(hello, 3);
       // finding minimun by minimum func.
        System.out.println(minimum(hello));
      // fiding maximum by maximum func.
        System.out.println(maximum(hello));
    }
}

                    

Python3

# Python implementation of the program
import math
 
class Proto_Van_Emde_Boas:
    def __init__(self, size):
        self.universe_size = size
        if size <= 2:
            self.summary = None
            self.clusters = [None] * size
        else:
            self.summary = Proto_Van_Emde_Boas(int(math.sqrt(size)))
            self.clusters = [Proto_Van_Emde_Boas(
                int(math.sqrt(size))) for _ in range(int(math.sqrt(size)))]
 
    def root(self, u):
        return int(math.sqrt(u))
    # Function to return cluster numbers
    # in which key is present
    def high(self, x):
        return x // self.root(self.universe_size)
 
    #  Function to return position of x in cluster
    def low(self, x):
        return x % self.root(self.universe_size)
 
#   Function to return the index from
#   cluster number and position
    def generate_index(self, cluster, position):
        return cluster * self.root(self.universe_size) + position
 
# Function to insert a key in the tree
def insert(helper, key):
    if helper.universe_size == 2:
        helper.clusters[key] = Proto_Van_Emde_Boas(1)
    else:
        insert(helper.clusters[helper.high(key)], helper.low(key))
        insert(helper.summary, helper.high(key))
# Function to find minimum
def minimum(helper):
    if helper.universe_size == 2:
        if helper.clusters[0] is not None:
            return 0
        elif helper.clusters[1] is not None:
            return 1
        return -1
    else:
        minimum_cluster = minimum(helper.summary)
        if minimum_cluster == -1:
            return -1
        else:
            offset = minimum(helper.clusters[minimum_cluster])
            return helper.generate_index(minimum_cluster, offset)
 
# Function to find maximum
def maximum(helper):
    if helper.universe_size == 2:
        if helper.clusters[1] is not None:
            return 1
        elif helper.clusters[0] is not None:
            return 0
        return -1
    else:
        maximum_cluster = maximum(helper.summary)
        if maximum_cluster == -1:
            return -1
        else:
            offset = maximum(helper.clusters[maximum_cluster])
            return helper.generate_index(maximum_cluster, offset)
 
# Driver code
hello = Proto_Van_Emde_Boas(4)
 
# inserting
insert(hello, 2);
insert(hello, 3);
 
print(minimum(hello))
print(maximum(hello))

                    

Javascript

// JavaScript implementation of the program
 
class Proto_Van_Emde_Boas {
  constructor(size) {
    this.universe_size = size;
    if (size <= 2) {
      this.summary = null;
      this.clusters = new Array(size).fill(null);
    } else {
      this.summary = new Proto_Van_Emde_Boas(Math.sqrt(size));
      this.clusters = Array(Math.sqrt(size)).fill().map(() => {
        return new Proto_Van_Emde_Boas(Math.sqrt(size));
      });
    }
  }
 
  root(u) {
    return Math.sqrt(u);
  }
 
  // Function to return cluster numbers
  // in which key is present
  high(x) {
    return Math.floor(x / this.root(this.universe_size));
  }
 
  //  Function to return position of x in cluster
  low(x) {
    return x % this.root(this.universe_size);
  }
 
  // Function to return the index from
  // cluster number and position
  generate_index(cluster, position) {
    return cluster * this.root(this.universe_size) + position;
  }
}
 
// Function to insert a key in the tree
function insert(helper, key) {
  if (helper.universe_size == 2) {
    helper.clusters[key] = new Proto_Van_Emde_Boas(1);
  } else {
    insert(helper.clusters[helper.high(key)], helper.low(key));
    insert(helper.summary, helper.high(key));
  }
}
 
// Function to find minimum
function minimum(helper) {
  if (helper.universe_size == 2) {
    if (helper.clusters[0] !== null) {
      return 0;
    } else if (helper.clusters[1] !== null) {
      return 1;
    }
    return -1;
  } else {
    let minimum_cluster = minimum(helper.summary);
    if (minimum_cluster == -1) {
      return -1;
    } else {
      let offset = minimum(helper.clusters[minimum_cluster]);
      return helper.generate_index(minimum_cluster, offset);
    }
  }
}
 
// Function to find maximum
function maximum(helper) {
  if (helper.universe_size == 2) {
    if (helper.clusters[1] !== null) {
      return 1;
    } else if (helper.clusters[0] !== null) {
      return 0;
    }
    return -1;
  } else {
    let maximum_cluster = maximum(helper.summary);
    if (maximum_cluster == -1) {
      return -1;
    } else {
      let offset = maximum(helper.clusters[maximum_cluster]);
      return helper.generate_index(maximum_cluster, offset);
    }
  }
}
 
// Driver code
let hello = new Proto_Van_Emde_Boas(4);
 
// inserting
insert(hello, 2);
insert(hello, 3);
 
console.log(minimum(hello));
console.log(maximum(hello));

                    

C#

using System;
using System.Collections.Generic;
 
public class Proto_Van_Emde_Boas {
    public int universe_size;
    public Proto_Van_Emde_Boas summary;
    public List<Proto_Van_Emde_Boas> clusters;
 
    public Proto_Van_Emde_Boas(int size)
    {
        universe_size = size;
        if (size <= 2) {
            summary = null;
            clusters = new List<Proto_Van_Emde_Boas>(size);
            for (int i = 0; i < size; i++) {
                clusters.Add(null);
            }
        }
        else {
            summary = new Proto_Van_Emde_Boas(root(size));
            clusters
                = new List<Proto_Van_Emde_Boas>(root(size));
            for (int i = 0; i < root(size); i++) {
                clusters.Add(
                    new Proto_Van_Emde_Boas(root(size)));
            }
        }
    }
 
    public int root(int u) { return (int)Math.Sqrt(u); }
 
    public int high(int x)
    {
        return x / root(universe_size);
    }
 
    public int low(int x)
    {
        return x % root(universe_size);
    }
 
    public int generate_index(int cluster, int position)
    {
        return cluster * root(universe_size) + position;
    }
}
 
public class GFG {
    public static void insert(Proto_Van_Emde_Boas helper,
                              int key)
    {
        if (helper.universe_size == 2) {
            helper.clusters[key]
                = new Proto_Van_Emde_Boas(1);
        }
        else {
            insert(helper.clusters[helper.high(key)],
                   helper.low(key));
            insert(helper.summary, helper.high(key));
        }
    }
 
    public static int minimum(Proto_Van_Emde_Boas helper)
    {
        if (helper.universe_size == 2) {
            if (helper.clusters[0] != null) {
                return 0;
            }
            else if (helper.clusters[1] != null) {
                return 1;
            }
            return -1;
        }
        else {
            int minimum_cluster = minimum(helper.summary);
            int offset;
 
            if (minimum_cluster == -1) {
                return -1;
            }
            else {
                offset = minimum(
                    helper.clusters[minimum_cluster]);
                return helper.generate_index(
                    minimum_cluster, offset);
            }
        }
    }
 
    public static int maximum(Proto_Van_Emde_Boas helper)
    {
        if (helper.universe_size == 2) {
            if (helper.clusters[1] != null) {
                return 1;
            }
            else if (helper.clusters[0] != null) {
                return 0;
            }
            return -1;
        }
        else {
            int maximum_cluster = maximum(helper.summary);
            int offset;
 
            if (maximum_cluster == -1) {
                return -1;
            }
            else {
                offset = maximum(
                    helper.clusters[maximum_cluster]);
                return helper.generate_index(
                    maximum_cluster, offset);
            }
        }
    }
 
    static void Main()
    {
        Proto_Van_Emde_Boas hello
            = new Proto_Van_Emde_Boas(4);
        insert(hello, 2);
        insert(hello, 3);
        Console.WriteLine(minimum(hello));
        // fiding maximum by maximum func.
        Console.WriteLine(maximum(hello));
    }
}
 
// The code is contributed by Nidhi goel.

                    

Output
2
3

Both Minimum and Maximum query runs in O(log2(u)) time complexity.
Recurrence Relation: 

T(u) = 2T(\sqrt{u}) + O(1)


Last Updated : 30 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads