📌  相关文章
📜  范围查询以找到具有最大数字总和的元素

📅  最后修改于: 2021-04-29 05:46:59             🧑  作者: Mango

给定一个由N个整数组成的数组ArrQ个查询,每个查询的范围从LR。查找范围从L到R的具有最大位数和的元素,如果一个以上的元素具有最大位数的和,则从中找出最大元素。
例子:

Input: Arr[] = { 16, 12, 43, 55} 
       Q = 2
       L = 0, R = 3
       L = 0, R = 2

Output: 55
        43

Explanation:
 The range (0, 3) in the 1st query has
 [16, 12, 43, 55]. Here, the digit sums are:
 for 16, 1 + 6 = 7
 for 12, 1 + 2 = 3
 for 43, 4 + 3 = 7
 for 55, 5 + 5 = 10
 Hence, the max digit sum is 10 and 
 max digit sum value is 55.

 The range (0, 2) in the 1st query has
 [16, 12, 43]. Here, the digit sums are:
 for 16, 1 + 6 = 7
 for 12, 1 + 2 = 3
 for 43, 4 + 3 = 7
 Hence, the max digit sum is 7 and 
 max digit sum value is max( 16, 43) = 43. 


天真的方法:
一个简单的解决方案是运行一个从L到R的循环,计算每个元素的数字总和,并为每个查询找到从L到R的最大数字总和元素。

时间复杂度:O(Q * N),
辅助空间:O(1)

高效方法:

  • 一种有效的方法是构建一个“分段树”,其中每个节点都存储两个值( valuemax_digit_sum ),并对其进行范围查询以找到最大位数和和相应的元素。但是对于构建段树,我们必须考虑在树的节点上存储什么。
  • 为了找出最大数字总和值,我们需要做两件事,一个是数值,另一个是数字总和。合并将返回两件事,数字总和将在段树中存储max(max_digit_sum.left,max_digit_sum.right),而value将包含相应的元素值。
  • 如果我们深入研究,则任意两个范围组合的最大位数总和要么是左侧的最大位数总和,要么是右侧的最大位数总和,以最大值为准。
  • 段树的表示形式:
    1. 叶节点是给定数组的元素。
    2. 每个内部节点代表叶节点的一些合并。对于不同的问题,合并可能会有所不同。对于此问题,合并是节点下叶子的max_digit_sum的最大值。
    3. 树的数组表示形式用于表示段树。对于索引i处的每个节点,左子节点在索引2 * i + 1处,右子节点在索引2 * i + 2处,父节点在(i-1)/ 2处
  • 从给定数组构造细分树:
    1. 我们从一个段arr [0开始。 。 。 n-1]。每次我们将当前段分为两半(如果尚未将其变成长度为1的段),然后在这两个半段上调用相同的过程,则对于每个这样的段,我们将max_digit_sum和值存储在对应的节点。
    2. 然后,我们在细分树上进行范围查询,以找出给定范围的max_digit_sum并输出相应的值。

下面是上述方法的实现。

C++
// C++ program to find
// maximum digit sum value
#include 
using namespace std;
 
// Struct two store two values in one node
struct Node {
    int value;
    int max_digit_sum;
};
 
Node tree[4 * 10000];
 
// Function to find the digit sum
// for a number
int digitSum(int x)
{
    int sum = 0;
    while (x) {
        sum += (x % 10);
        x /= 10;
    }
}
 
// Function to build the segment tree
void build(int a[], int index, int beg, int end)
{
    if (beg == end) {
 
        // If there is one element in array,
        tree[index].value = a[beg];
        tree[index].max_digit_sum = digitSum(a[beg]);
    }
    else {
 
        int mid = (beg + end) / 2;
 
        // If there are more than one elements,
        // then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid);
        build(a, 2 * index + 2, mid + 1, end);
 
        if (tree[2 * index + 1].max_digit_sum >
            tree[2 * index + 2].max_digit_sum)
        {
            tree[index].max_digit_sum =
                tree[2 * index + 1].max_digit_sum;
            tree[index].value =
                tree[2 * index + 1].value;
        }
        else if (tree[2 * index + 2].max_digit_sum >
                 tree[2 * index + 1].max_digit_sum)
        {
            tree[index].max_digit_sum =
                tree[2 * index + 2].max_digit_sum;
            tree[index].value =
                tree[2 * index + 2].value;
        }
        else
        {
            tree[index].max_digit_sum =
                tree[2 * index + 2].max_digit_sum;
            tree[index].value =
                max(tree[2 * index + 2].value,
                    tree[2 * index + 1].value);
        }
    }
}
 
// Function to do the range query in the segment
// tree for the maximum digit sum
Node query(int index, int beg, int end,
           int l, int r)
{
    Node result;
    result.value = result.max_digit_sum = -1;
 
    // If segment of this node is outside the given
    // range, then return the minimum valueue.
    if (beg > r || end < l)
        return result;
 
    // If segment of this node is a part of given
    // range, then return the node of the segment
    if (beg >= l && end <= r)
        return tree[index];
 
    int mid = (beg + end) / 2;
 
    // If left segment of this node falls out of
    // range, then recur in the right side of
    // the tree
    if (l > mid)
        return query(2 * index + 2, mid + 1,
                     end, l, r);
 
    // If right segment of this node falls out of
    // range, then recur in the left side of
    // the tree
    if (r <= mid)
        return query(2 * index + 1, beg,
                     mid, l, r);
 
    // If a part of this segment overlaps with
    // the given range
    Node left = query(2 * index + 1, beg,
                      mid, l, r);
    Node right = query(2 * index + 2, mid + 1,
                       end, l, r);
 
    if (left.max_digit_sum > right.max_digit_sum)
    {
        result.max_digit_sum = left.max_digit_sum;
        result.value = left.value;
    }
    else if (right.max_digit_sum > left.max_digit_sum)
    {
        result.max_digit_sum = right.max_digit_sum;
        result.value = right.value;
    }
    else
    {
        result.max_digit_sum = left.max_digit_sum;
        result.value = max(right.value, left.value);
    }
 
    // Returns the value
    return result;
}
 
// Driver code
int main()
{
 
    int a[] = {16, 12, 43, 55};
 
    // Calculates the length of array
    int N = sizeof(a) / sizeof(a[0]);
 
    // Calls the build function to build
    // the segment tree
    build(a, 0, 0, N - 1);
 
    // Find the max digit-sum valueue between
    // 0th and 3rd index of array
    cout << query(0, 0, N - 1, 0, 3).value
         << endl;
 
    // Find the max digit-sum value between
    // 0th and 2nd index of array
    cout << query(0, 0, N - 1, 0, 2).value
         << endl;
 
    return 0;
}


Java
// Java program to find
// maximum digit sum value
import java.util.*;
 
class GFG{
 
// Struct two store two values
// in one node
static class Node
{
    int value;
    int max_digit_sum;
};
 
static Node []tree = new Node[4 * 10000];
 
// Function to find the digit sum
// for a number
static int digitSum(int x)
{
    int sum = 0;
     
    while (x > 0)
    {
        sum += (x % 10);
        x /= 10;
    }
    return sum;
}
 
// Function to build the segment tree
static void build(int a[], int index,
                  int beg, int end)
{
    if (beg == end)
    {
         
        // If there is one element in array,
        tree[index].value = a[beg];
        tree[index].max_digit_sum = digitSum(a[beg]);
    }
    else
    {
        int mid = (beg + end) / 2;
 
        // If there are more than one elements,
        // then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid);
        build(a, 2 * index + 2, mid + 1, end);
 
        if (tree[2 * index + 1].max_digit_sum >
            tree[2 * index + 2].max_digit_sum)
        {
            tree[index].max_digit_sum =
                tree[2 * index + 1].max_digit_sum;
            tree[index].value =
                tree[2 * index + 1].value;
        }
        else if (tree[2 * index + 2].max_digit_sum >
                 tree[2 * index + 1].max_digit_sum)
        {
            tree[index].max_digit_sum =
                tree[2 * index + 2].max_digit_sum;
            tree[index].value =
                tree[2 * index + 2].value;
        }
        else
        {
            tree[index].max_digit_sum =
                tree[2 * index + 2].max_digit_sum;
            tree[index].value =
                Math.max(tree[2 * index + 2].value,
                         tree[2 * index + 1].value);
        }
    }
}
 
// Function to do the range query in the segment
// tree for the maximum digit sum
static Node query(int index, int beg, int end,
                  int l, int r)
{
    Node result = new Node();
    result.value = result.max_digit_sum = -1;
 
    // If segment of this node is outside the given
    // range, then return the minimum valueue.
    if (beg > r || end < l)
        return result;
 
    // If segment of this node is a part of given
    // range, then return the node of the segment
    if (beg >= l && end <= r)
        return tree[index];
 
    int mid = (beg + end) / 2;
 
    // If left segment of this node falls out of
    // range, then recur in the right side of
    // the tree
    if (l > mid)
        return query(2 * index + 2, mid + 1,
                     end, l, r);
 
    // If right segment of this node falls out of
    // range, then recur in the left side of
    // the tree
    if (r <= mid)
        return query(2 * index + 1, beg,
                     mid, l, r);
 
    // If a part of this segment overlaps with
    // the given range
    Node left = query(2 * index + 1, beg,
                      mid, l, r);
    Node right = query(2 * index + 2, mid + 1,
                       end, l, r);
 
    if (left.max_digit_sum > right.max_digit_sum)
    {
        result.max_digit_sum = left.max_digit_sum;
        result.value = left.value;
    }
    else if (right.max_digit_sum > left.max_digit_sum)
    {
        result.max_digit_sum = right.max_digit_sum;
        result.value = right.value;
    }
    else
    {
        result.max_digit_sum = left.max_digit_sum;
        result.value = Math.max(right.value,
                                 left.value);
    }
 
    // Returns the value
    return result;
}
 
// Driver code
public static void main(String[] args)
{
    int a[] = { 16, 12, 43, 55 };
 
    // Calculates the length of array
    int N = a.length;
     
    for(int i = 0; i < tree.length; i++)
        tree[i] = new Node();
         
    // Calls the build function to build
    // the segment tree
    build(a, 0, 0, N - 1);
 
    // Find the max digit-sum valueue between
    // 0th and 3rd index of array
    System.out.print(
        query(0, 0, N - 1, 0, 3).value + "\n");
 
    // Find the max digit-sum value between
    // 0th and 2nd index of array
    System.out.print(
        query(0, 0, N - 1, 0, 2).value + "\n");
}
}
 
// This code is contributed by Amit Katiyar


Python3
# Python3 program to find
# maximum digit sum value
  
# Struct two store two values in one node
class Node:
     
    def __init__(self):
         
        self.value = 0
        self.max_digit_sum = 0
     
tree = [Node() for i in range(4 * 10000)]
 
# Function to find the digit sum
# for a number
def digitSum(x):
     
    sum = 0;
    while(x != 0):
        sum += (x % 10)
        x //= 10
         
    return sum
     
# Function to build the segment tree
def build(a, index, beg, end):
     
    if (beg == end):
  
        # If there is one element in array,
        tree[index].value = a[beg]
        tree[index].max_digit_sum = digitSum(a[beg])
     
    else:
        mid = (beg + end) // 2
  
        # If there are more than one elements,
        # then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid)
        build(a, 2 * index + 2, mid + 1, end)
         
        if (tree[2 * index + 1].max_digit_sum >
            tree[2 * index + 2].max_digit_sum):
         
            tree[index].max_digit_sum = tree[2 * index + 1].max_digit_sum
            tree[index].value = tree[2 * index + 1].value
         
        elif (tree[2 * index + 2].max_digit_sum >
              tree[2 * index + 1].max_digit_sum):
         
            tree[index].max_digit_sum = tree[2 * index + 2].max_digit_sum
            tree[index].value = tree[2 * index + 2].value
         
        else:
            tree[index].max_digit_sum = tree[2 * index + 2].max_digit_sum
            tree[index].value = max(tree[2 * index + 2].value,
                                    tree[2 * index + 1].value)
         
# Function to do the range query in the segment
# tree for the maximum digit sum
def query(index, beg, end, l, r):
 
    result = Node()
    result.value = result.max_digit_sum = -1
  
    # If segment of this node is outside the given
    # range, then return the minimum valueue.
    if (beg > r or end < l):
        return result
  
    # If segment of this node is a part of given
    # range, then return the node of the segment
    if (beg >= l and end <= r):
        return tree[index]
  
    mid = (beg + end) // 2
  
    # If left segment of this node falls out of
    # range, then recur in the right side of
    # the tree
    if (l > mid):
        return query(2 * index + 2, mid + 1, end, l, r)
  
    # If right segment of this node falls out of
    # range, then recur in the left side of
    # the tree
    if (r <= mid):
        return query(2 * index + 1, beg, mid, l, r)
  
    # If a part of this segment overlaps with
    # the given range
    left = query(2 * index + 1, beg, mid, l, r)
    right = query(2 * index + 2, mid + 1, end, l, r)
  
    if (left.max_digit_sum > right.max_digit_sum):
        result.max_digit_sum = left.max_digit_sum
        result.value = left.value
     
    elif (right.max_digit_sum > left.max_digit_sum):
        result.max_digit_sum = right.max_digit_sum
        result.value = right.value
     
    else:
        result.max_digit_sum = left.max_digit_sum
        result.value = max(right.value, left.value)
  
    # Returns the value
    return result
     
# Driver code
if __name__=="__main__":
     
    a = [ 16, 12, 43, 55 ]
  
    # Calculates the length of array
    N = len(a)
  
    # Calls the build function to build
    # the segment tree
    build(a, 0, 0, N - 1)
  
    # Find the max digit-sum valueue between
    # 0th and 3rd index of array
    print(query(0, 0, N - 1, 0, 3).value)
  
    # Find the max digit-sum value between
    # 0th and 2nd index of array
    print(query(0, 0, N - 1, 0, 2).value)
    
# This code is contributed by rutvik_56


C#
// C# program to find
// maximum digit sum value
using System;
class GFG{
 
// Struct two store
// two values in one node
class Node
{
  public int value;
  public int max_digit_sum;
};
 
static Node []tree = new Node[4 * 10000];
 
// Function to find the digit sum
// for a number
static int digitSum(int x)
{
  int sum = 0;
 
  while (x > 0)
  {
    sum += (x % 10);
    x /= 10;
  }
  return sum;
}
 
// Function to build the segment tree
static void build(int []a, int index,
                  int beg, int end)
{
  if (beg == end)
  {
    // If there is one element in array,
    tree[index].value = a[beg];
    tree[index].max_digit_sum =
                digitSum(a[beg]);
  }
  else
  {
    int mid = (beg + end) / 2;
 
    // If there are more than one elements,
    // then recur for left and right subtrees
    build(a, 2 * index + 1, beg, mid);
    build(a, 2 * index + 2, mid + 1, end);
 
    if (tree[2 * index + 1].max_digit_sum >
        tree[2 * index + 2].max_digit_sum)
    {
      tree[index].max_digit_sum =
           tree[2 * index + 1].max_digit_sum;
      tree[index].value =
           tree[2 * index + 1].value;
    }
    else if (tree[2 * index + 2].max_digit_sum >
             tree[2 * index + 1].max_digit_sum)
    {
      tree[index].max_digit_sum =
           tree[2 * index + 2].max_digit_sum;
      tree[index].value =
           tree[2 * index + 2].value;
    }
    else
    {
      tree[index].max_digit_sum =
           tree[2 * index + 2].max_digit_sum;
      tree[index].value =
           Math.Max(tree[2 * index + 2].value,
                tree[2 * index + 1].value);
    }
  }
}
 
// Function to do the range query
// in the segment tree for the
// maximum digit sum
static Node query(int index, int beg,
                  int end, int l, int r)
{
  Node result = new Node();
  result.value = result.max_digit_sum = -1;
 
  // If segment of this node is
  // outside the given range,
  // then return the minimum valueue.
  if (beg > r || end < l)
    return result;
 
  // If segment of this node
  // is a part of given range,
  // then return the node of the segment
  if (beg >= l && end <= r)
    return tree[index];
 
  int mid = (beg + end) / 2;
 
  // If left segment of this
  // node falls out of range,
  // then recur in the right
  // side of the tree
  if (l > mid)
    return query(2 * index + 2, mid + 1,
                 end, l, r);
 
  // If right segment of this
  // node falls out of range,
  // then recur in the left side of
  // the tree
  if (r <= mid)
    return query(2 * index + 1, beg,
                 mid, l, r);
 
  // If a part of this segment
  // overlaps with the given range
  Node left = query(2 * index + 1, beg,
                    mid, l, r);
  Node right = query(2 * index + 2, mid + 1,
                     end, l, r);
 
  if (left.max_digit_sum > right.max_digit_sum)
  {
    result.max_digit_sum = left.max_digit_sum;
    result.value = left.value;
  }
  else if (right.max_digit_sum > left.max_digit_sum)
  {
    result.max_digit_sum = right.max_digit_sum;
    result.value = right.value;
  }
  else
  {
    result.max_digit_sum = left.max_digit_sum;
    result.value = Math.Max(right.value,
                            left.value);
  }
 
  // Returns the value
  return result;
}
 
// Driver code
public static void Main(String[] args)
{
  int []a = {16, 12, 43, 55};
 
  // Calculates the length
  // of array
  int N = a.Length;
 
  for(int i = 0; i < tree.Length; i++)
    tree[i] = new Node();
 
  // Calls the build function
  // to build the segment tree
  build(a, 0, 0, N - 1);
 
  // Find the max digit-sum value between
  // 0th and 3rd index of array
  Console.Write(query(0, 0,
                      N - 1,
                      0, 3).value + "\n");
 
  // Find the max digit-sum value between
  // 0th and 2nd index of array
  Console.Write(query(0, 0,
                      N - 1,
                      0, 2).value + "\n");
}
}
 
// This code is contributed by Rajput-Ji


输出:
55
43







复杂度分析:
树木构建的时间复杂度为O(N)。总共有2n-1个节点,并且在树结构中每个节点的值仅计算一次。
每个查询的时间复杂度为O(log N)。
该问题的时间复杂度为O(Q * log N)