📌  相关文章
📜  数组范围查询以计数斐波纳契数与更新的数目

📅  最后修改于: 2021-05-17 05:50:06             🧑  作者: Mango

给定N个整数的数组arr [] ,任务是执行以下两个查询:

  • query(start,end) :从头到尾打印子数组中的斐波那契数
  • update(i,x) :将x添加到数组索引i引用的数组元素中,即:arr [i] = x

例子:

方法:要同时处理点更新和范围查询,为此目的,使用段树是最佳的。
为了检查斐波那契数,我们可以使用包含所有小于或等于最大值arr i的斐波那契数的动态编程来构建哈希表。我们可以采用MAX来测试O(1)时间复杂度的数字。
构建段树:

  • 现在,使用分段树问题将问题简化为子数组总和。
  • 现在,我们可以构建分段树,其中叶节点表示为0(如果不是素数)或1(如果是斐波那契数)。
  • 段树的内部节点等于其子节点的总和,因此,一个节点代表从L到R的总斐波那契数,范围[L,R]落在该节点下,并在其下子树。

处理查询和积分更新:

  • 每当我们收到一个从头到尾的查询时,我们都可以在段树中查询从头到尾的范围内的节点总数,这些节点的总和又代表从头到尾的范围内的斐波那契数。
  • 为了执行点更新并将索引i的值更新为x,我们检查以下情况:
    令arr i的旧值为y,新值为x。
    1. 情况1:斐波那契:如果x和y都是斐波那契数
      子数组中斐波那契数的计数不会改变,因此我们只更新数组而不修改段树
    2. 情况2:如果x和y都不是斐波那契数
      子数组中斐波那契数的计数不会改变,因此我们只更新数组而不修改段树
    3. 情况3:如果y是斐波那契数,而x不是
      子数组中斐波那契数的计数减少,因此我们更新数组并将-1添加到每个范围。要更新的索引i是分段树的一部分
    4. 情况4:如果y不是斐波那契数,而x是斐波那契数
      子数组中斐波那契数的计数增加,因此我们更新数组并向每个范围加1。要更新的索引i是分段树的一部分

下面是上述方法的实现:

CPP
// C++ program to find number of fibonacci numbers
// in a subarray and performing updates
 
#include 
using namespace std;
 
#define MAX 1000
 
// Function to create hash table
// to check Fibonacci numbers
void createHash(set& hash,
                int maxElement)
{
    int prev = 0, curr = 1;
    hash.insert(prev);
    hash.insert(curr);
 
    while (curr <= maxElement) {
        int temp = curr + prev;
        hash.insert(temp);
        prev = curr;
        curr = temp;
    }
}
 
// A utility function to get the middle
// index from corner indexes.
int getMid(int s, int e)
{
    return s + (e - s) / 2;
}
 
// Recursive function to get the number
// of Fibonacci numbers in a given range
/* where
    st    --> Pointer to segment tree
    index --> Index of current node in the
              segment tree. Initially 0 is passed
              as root is always at index 0
    ss & se  --> Starting and ending indexes of
              the segment represented by current
              node, i.e., st[index]
    qs & qe  --> Starting and ending indexes
              of query range  
    */
int queryFibonacciUtil(int* st, int ss,
                       int se, int qs,
                       int qe, int index)
{
    // If segment of this node is a part
    // of given range, then return
    // the number of Fibonacci numbers
    // in the segment
    if (qs <= ss && qe >= se)
        return st[index];
 
    // If segment of this node
    // is outside the given range
    if (se < qs || ss > qe)
        return 0;
 
    // If a part of this segment
    // overlaps with the given range
    int mid = getMid(ss, se);
    return queryFibonacciUtil(st, ss, mid, qs,
                              qe, 2 * index + 1)
           + queryFibonacciUtil(st, mid + 1, se,
                                qs, qe, 2 * index + 2);
}
 
// Recursive function to update
// the nodes which have the given
// index in their range.
/* where
    st, si, ss and se are same as getSumUtil()
    i --> index of the element to be updated.
          This index is in input array.
   diff --> Value to be added to all nodes
          which have i in range
*/
void updateValueUtil(int* st, int ss,
                     int se, int i,
                     int diff, int si)
{
    // Base Case:
    // If the input index lies outside
    // the range of this segment
    if (i < ss || i > se)
        return;
 
    // If the input index is in range
    // of this node, then update the value
    // of the node and its children
    st[si] = st[si] + diff;
    if (se != ss) {
 
        int mid = getMid(ss, se);
        updateValueUtil(st, ss, mid, i,
                        diff, 2 * si + 1);
        updateValueUtil(st, mid + 1, se,
                        i, diff, 2 * si + 2);
    }
}
 
// Function to update a value in the
// input array and segment tree.
// It uses updateValueUtil() to update
// the value in segment tree
void updateValue(int arr[], int* st,
                 int n, int i,
                 int new_val,
                 set hash)
{
    // Check for erroneous input index
    if (i < 0 || i > n - 1) {
        printf("Invalid Input");
        return;
    }
 
    int diff, oldValue;
 
    oldValue = arr[i];
 
    // Update the value in array
    arr[i] = new_val;
 
    // Case 1: Old and new values
    // both are Fibonacci numbers
    if (hash.find(oldValue) != hash.end()
        && hash.find(new_val) != hash.end())
        return;
 
    // Case 2: Old and new values
    // both not Fibonacci numbers
    if (hash.find(oldValue) == hash.end()
        && hash.find(new_val) == hash.end())
        return;
 
    // Case 3: Old value was Fibonacci,
    // new value is non Fibonacci
    if (hash.find(oldValue) != hash.end()
        && hash.find(new_val) == hash.end()) {
        diff = -1;
    }
 
    // Case 4: Old value was non Fibonacci,
    // new_val is Fibonacci
    if (hash.find(oldValue) == hash.end()
        && hash.find(new_val) != hash.end()) {
        diff = 1;
    }
 
    // Update the values of nodes in segment tree
    updateValueUtil(st, 0, n - 1, i, diff, 0);
}
 
// Return number of Fibonacci numbers
// in range from index qs (query start)
// to qe (query end).
// It mainly uses queryFibonacciUtil()
void queryFibonacci(int* st, int n,
                    int qs, int qe)
{
    int FibonacciInRange
        = queryFibonacciUtil(st, 0, n - 1,
                             qs, qe, 0);
 
    cout << "Number of Fibonacci numbers "
         << "in subarray from "
         << qs << " to "
         << qe << " = "
         << FibonacciInRange << "\n";
}
 
// Recursive function that constructs
// Segment Tree for array[ss..se].
// si is index of current node
// in segment tree st
int constructSTUtil(int arr[], int ss,
                    int se, int* st,
                    int si, set hash)
{
    // If there is one element in array,
    // check if it is Fibonacci number
    // then store 1 in the segment tree
    // else store 0 and return
    if (ss == se) {
 
        // if arr[ss] is fibonacci number
        if (hash.find(arr[ss]) != hash.end())
            st[si] = 1;
        else
            st[si] = 0;
 
        return st[si];
    }
 
    // If there are more than one elements,
    // then recur for left and right subtrees
    // and store the sum of the
    // two values in this node
    int mid = getMid(ss, se);
    st[si] = constructSTUtil(arr, ss, mid, st,
                             si * 2 + 1, hash)
             + constructSTUtil(arr, mid + 1, se, st,
                               si * 2 + 2, hash);
    return st[si];
}
 
// Function to construct a segment tree from given array.
// This function allocates memory for segment tree and
// calls constructSTUtil() to fill the allocated memory
int* constructST(int arr[], int n, set hash)
{
    // Allocate memory for segment tree
 
    // Height of segment tree
    int x = (int)(ceil(log2(n)));
 
    // Maximum size of segment tree
    int max_size = 2 * (int)pow(2, x) - 1;
 
    int* st = new int[max_size];
 
    // Fill the allocated memory st
    constructSTUtil(arr, 0, n - 1, st, 0, hash);
 
    // Return the constructed segment tree
    return st;
}
 
// Driver Code
int main()
{
 
    int arr[] = { 1, 2, 3, 4, 8, 9 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // find the largest node value in the array
    int maxEle = *max_element(arr, arr + n);
 
    // Creating a set containing all Fibonacci numbers
    // upto the maximum data value in the array
    set hash;
    createHash(hash, maxEle);
 
    // Build segment tree from given array
    int* st = constructST(arr, n, hash);
 
    // Query 1: Query(start = 0, end = 4)
    int start = 0;
    int end = 4;
    queryFibonacci(st, n, start, end);
 
    // Query 2: Update(i = 3, x = 5),
    // i.e Update a[i] to x
    int i = 3;
    int x = 5;
    updateValue(arr, st, n, i, x, hash);
 
    // uncomment to see array after update
    // for(int i = 0; i < n; i++)
    // cout << arr[i] << " ";
 
    // Query 3: Query(start = 0, end = 4)
    start = 0;
    end = 4;
    queryFibonacci(st, n, start, end);
 
    return 0;
}


Java
// Java program to find number of fibonacci numbers
// in a subarray and performing updates
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
class GFG
{
  static final int MAX = 1000;
 
  // Function to create hash table
  // to check Fibonacci numbers
  static void createHash(Set hash, int maxElement)
  {
    int prev = 0, curr = 1;
    hash.add(prev);
    hash.add(curr);
    while (curr <= maxElement)
    {
      int temp = curr + prev;
      hash.add(temp);
      prev = curr;
      curr = temp;
    }
  }
 
  // A utility function to get the middle
  // index from corner indexes.
  static int getMid(int s, int e)
  {
    return s + (e - s) / 2;
  }
 
  // Recursive function to get the number
  // of Fibonacci numbers in a given range
  /*
     * where st --> Pointer to segment tree index --> Index of current node in the
     * segment tree. Initially 0 is passed as root is always at index 0 ss & se -->
     * Starting and ending indexes of the segment represented by current node, i.e.,
     * st[index] qs & qe --> Starting and ending indexes of query range
     */
  static int queryFibonacciUtil(int[] st, int ss, int se,
                                int qs, int qe, int index)
  {
 
    // If segment of this node is a part
    // of given range, then return
    // the number of Fibonacci numbers
    // in the segment
    if (qs <= ss && qe >= se)
      return st[index];
 
    // If segment of this node
    // is outside the given range
    if (se < qs || ss > qe)
      return 0;
 
    // If a part of this segment
    // overlaps with the given range
    int mid = getMid(ss, se);
    return queryFibonacciUtil(st, ss, mid,
                              qs, qe, 2 * index + 1)
      + queryFibonacciUtil(st, mid + 1, se,
                           qs, qe, 2 * index + 2);
  }
 
  // Recursive function to update
  // the nodes which have the given
  // index in their range.
  /*
     * where st, si, ss and se are same as getSumUtil() i --> index of the element
     * to be updated. This index is in input array. diff --> Value to be added to
     * all nodes which have i in range
     */
  static void updateValueUtil(int[] st, int ss,
                              int se, int i,
                              int diff, int si)
  {
 
    // Base Case:
    // If the input index lies outside
    // the range of this segment
    if (i < ss || i > se)
      return;
 
    // If the input index is in range
    // of this node, then update the value
    // of the node and its children
    st[si] = st[si] + diff;
    if (se != ss)
    {
 
      int mid = getMid(ss, se);
      updateValueUtil(st, ss, mid, i,
                      diff, 2 * si + 1);
      updateValueUtil(st, mid + 1, se, i,
                      diff, 2 * si + 2);
    }
  }
 
  // Function to update a value in the
  // input array and segment tree.
  // It uses updateValueUtil() to update
  // the value in segment tree
  static void updateValue(int arr[], int[] st, int n,
                          int i, int new_val, Set hash)
  {
 
    // Check for erroneous input index
    if (i < 0 || i > n - 1)
    {
      System.out.printf("Invalid Input");
      return;
    }
 
    int diff = 0, oldValue;
    oldValue = arr[i];
 
    // Update the value in array
    arr[i] = new_val;
 
    // Case 1: Old and new values
    // both are Fibonacci numbers
    if (hash.contains(oldValue) &&
        hash.contains(new_val))
      return;
 
    // Case 2: Old and new values
    // both not Fibonacci numbers
    if (!hash.contains(oldValue) &&
        !hash.contains(new_val))
      return;
 
    // Case 3: Old value was Fibonacci,
    // new value is non Fibonacci
    if (hash.contains(oldValue) &&
        !hash.contains(new_val))
    {
      diff = -1;
    }
 
    // Case 4: Old value was non Fibonacci,
    // new_val is Fibonacci
    if (!hash.contains(oldValue) &&
        hash.contains(new_val))
    {
      diff = 1;
    }
 
    // Update the values of nodes in segment tree
    updateValueUtil(st, 0, n - 1, i, diff, 0);
  }
 
  // Return number of Fibonacci numbers
  // in range from index qs (query start)
  // to qe (query end).
  // It mainly uses queryFibonacciUtil()
  static void queryFibonacci(int[] st, int n, int qs, int qe)
  {
    int FibonacciInRange = queryFibonacciUtil(st, 0,
                                              n - 1, qs, qe, 0);
    System.out.printf("Number of Fibonacci numbers in subarray from %d to %d = %d\n", qs, qe, FibonacciInRange);
  }
 
  // Recursive function that constructs
  // Segment Tree for array[ss..se].
  // si is index of current node
  // in segment tree st
  static int constructSTUtil(int arr[], int ss, int se,
                             int[] st, int si, Set hash)
  {
 
    // If there is one element in array,
    // check if it is Fibonacci number
    // then store 1 in the segment tree
    // else store 0 and return
    if (ss == se)
    {
 
      // if arr[ss] is fibonacci number
      if (hash.contains(arr[ss]))
        st[si] = 1;
      else
        st[si] = 0;         
      return st[si];
    }
 
    // If there are more than one elements,
    // then recur for left and right subtrees
    // and store the sum of the
    // two values in this node
    int mid = getMid(ss, se);
    st[si] = constructSTUtil(arr, ss, mid, st,
                             si * 2 + 1, hash)
      + constructSTUtil(arr, mid + 1, se,
                        st, si * 2 + 2, hash);
    return st[si];
  }
 
  // Function to construct a segment tree from given array.
  // This function allocates memory for segment tree and
  // calls constructSTUtil() to fill the allocated memory
  static int[] constructST(int arr[], int n, Set hash)
  {
 
    // Allocate memory for segment tree
 
    // Height of segment tree
    int x = (int) (Math.ceil(Math.log(n) / Math.log(2)));
 
    // Maximum size of segment tree
    int max_size = 2 * (int) Math.pow(2, x) - 1;
    int[] st = new int[max_size];
 
    // Fill the allocated memory st
    constructSTUtil(arr, 0, n - 1, st, 0, hash);
 
    // Return the constructed segment tree
    return st;
  }
 
  // Driver Code
  public static void main(String[] args)
  {
 
    int arr[] = { 1, 2, 3, 4, 8, 9 };
    int n = arr.length;
 
    // find the largest node value in the array
    int maxEle = Arrays.stream(arr).max().getAsInt();
 
    // Creating a set containing all Fibonacci numbers
    // upto the maximum data value in the array
    Set hash = new HashSet<>();
    createHash(hash, maxEle);
 
    // Build segment tree from given array
    int[] st = constructST(arr, n, hash);
 
    // Query 1: Query(start = 0, end = 4)
    int start = 0;
    int end = 4;
    queryFibonacci(st, n, start, end);
 
    // Query 2: Update(i = 3, x = 5),
    // i.e Update a[i] to x
    int i = 3;
    int x = 5;
    updateValue(arr, st, n, i, x, hash);
 
    // uncomment to see array after update
    // for(int i = 0; i < n; i++)
    // cout << arr[i] << " ";
 
    // Query 3: Query(start = 0, end = 4)
    start = 0;
    end = 4;
    queryFibonacci(st, n, start, end);
  }
}
 
// This code is contributed by sanjeev2552


输出:
Number of Fibonacci numbers in subarray from 0 to 4 = 4
Number of Fibonacci numbers in subarray from 0 to 4 = 5

时间复杂度:每个查询和更新的时间复杂度为O(log n) ,构建段树的时间复杂度为O(n)