9_平方根分解和线段树(以LeetCode307为例)

本文介绍了解决区间查询问题的三种方法:朴素方法、平方根分解和线段树,并详细阐述了每种方法的实现原理及复杂度分析。
  • Problem

      /*
        Given an integer array nums, find the sum of the elements between indices i
        and j (i ≤ j), inclusive.
    
        The update(i, val) function modifies nums by updating the element at index i
        to val.
    
        Example:
    
            Given nums = [1, 3, 5]
    
            sumRange(0, 2) -> 9
            update(1, 2)
            sumRange(0, 2) -> 8
    
        Note:
            The array is only modifiable by the update function.
            You may assume the number of calls to update and sumRange function is
            distributed evenly.
      */    
    
  • Summary

    This article is for intermediate level readers. It introduces the following concepts: Range sum query, Sqrt decomposition, Segment tree.

  • Solution

    Approach #1 (Naive) [Time Limit Exceeded]

    Algorithm

    A trivial solution for Range Sum Query - RSQ(i, j) is to iterate the array from index i to j and sum each element.

      private int[] nums;
    
      public int sumRange(int i, int j) {
    
          int sum = 0;
          for (int l = i; l <= j; l++) {
              sum += data[l];
          }
          return sum;
      }
    
      public int update(int i, int val) {
    
          nums[i] = val;
      }
    

    Complexity Analysis

    Time complexity :

    O(n) - range sum query,

    O(1) - update query

    For range sum query we access each element from the array for constant time and in the worst case we access n elements. Therefore time complexity is O(n). Time complexity of update query is O(1).

    Space complexity : O(1).

    Approach #2 (Sqrt decomposition) [Accepted]

    Intuition

    The idea is to split the array in blocks with length of sqrt{n}. Then we calculate the sum of each block and store it in auxiliary memory b. To query RSQ(i, j), we will add the sums of all the blocks lying inside and those that partially overlap with range [i…j].

    Algorithm: Range sum query using SQRT decomposition

      private int[] b;
      private int len;
      private int[] nums;
    
      public NumArray(int[] nums) {
    
          this.nums = nums;
          double l = Math.sqrt(nums.length);
          len = (int) Math.ceil(nums.length/l);
          b = new int [len];
    
          for (int i = 0; i < nums.length; i++) {
              b[i / len] += nums[i];
          }
      }
    
      public int sumRange(int i, int j) {
    
          int sum = 0;
    
          int startBlock = i / len;
          int endBlock = j / len;
    
          if (startBlock == endBlock) {
    
              for (int k = i; k <= j; k++) {
                  sum += nums[k];
              }
    
          } else {
    
              // The first part
              for (int k = i; k <= (startBlock + 1) * len - 1; k++) {
                  sum += nums[k];
              }
    
              // The main part
              for (int k = startBlock + 1; k <= endBlock - 1; k++) {
                  sum += b[k];
              }
    
              // The last part
              for (int k = endBlock * len; k <= j; k++) {
                  sum += nums[k];
              }
          }
    
          return sum;
      }
    
      public void update(int i, int val) {
    
          int b_l = i / len;
          b[b_l] = b[b_l] - nums[i] + val;
          nums[i] = val;
      }
    

    Complexity Analysis

    Time complexity :

    O(n) - preprocessing,

    O(sqrt{n}) - range sum query,

    O(1) - update query

    For range sum query in the worst-case scenario we have to sum approximately 3 * sqrt{n} elements. In this case the range includes sqrt{n} - 2 blocks, which total sum costs sqrt{n} - 2 operations. In addition to this we have to add the sum of the two boundary blocks. This takes another 2 (sqrt{n} - 1) operations. The total amount of operations is around 3 sqrt{n}.

    Space complexity : O(sqrt{n})

    We need additional sqrt{n} memory to store all block sums.

    Approach #3 (Segment tree) [Accepted]

    Algorithm

    Segment tree is a very flexible data structure, because it is used to solve numerous range query problems like finding minimum, maximum, sum, greatest common divisor, least common denominator in array in logarithmic time.

    Illustration of Segment tree

      {2, 4, 5, 7, 8, 9}
    
                            35 (0-5)
                   6(0-1)              29(2-5)
              2(0)      4(1)       12(2-3)     17(4-5)
                                5(2)    7(3)  8(4)   9(5)
    

    Figure 2. Illustration of Segment tree.

    The segment tree for array a[0, 1, …, n−1] is a binary tree in which each node contains aggregate information (min, max, sum, etc.) for a subrange [i, j] of the array, as its left and right child hold information for range [i, (i+j)/2], [((i+j)/2)+1, j]

    Segment tree could be implemented using either an array or a tree. For an array implementation, if the element at index i is not a leaf, its left and right child are stored at index 2i and 2i+1 respectively.

    In the example above (Figure 2), every leaf node contains the initial array elements {2,4,5,7,8,9}. The internal nodes contain the sum of the corresponding elements in range - (11) for the elements from index 0 to index 2. The root (35) being the sum of its children (6);(29), holds the total sum of the entire array.

    Segment Tree can be broken down to the three following steps:

    1. Pre-processing step which builds the segment tree from a given array.

    2. Update the segment tree when an element is modified.

    3. Calculate the Range Sum Query using the segment tree.

    Build segment tree

    We will use a very effective bottom-up approach to build segment tree. We already know from the above that if some node p holds the sum of [i, j] range, its left and right children hold the sum for range [i, (i+j)/2], [((i+j)/2)+1, j] respectively.

    Therefore to find the sum of node p, we need to calculate the sum of its right and left child in advance.

    We begin from the leaves, initialize them with input array elements a[0, 1, …, n−1]. Then we move upward to the higher level to calculate the parents’ sum till we get to the root of the segment tree.

      int[] tree;
      int n;
    
      public NumArray(int[] nums) {
    
          if (nums.length > 0) {
              n = nums.length;
              tree = new int[n * 2];
      
              buildTree(nums);
          }
      }
    
      private void buildTree(int[] nums) {
    
          for (int i = n, j = 0;  i < 2 * n; i++,  j++)
              tree[i] = nums[j];
    
          for (int i = n - 1; i > 0; --i)
              tree[i] = tree[i * 2] + tree[i * 2 + 1];
          }
    

    Complexity Analysis

    Time complexity : O(n)

    Time complexity is O(n), because we calculate the sum of one node during each iteration of the for loop. There are approximately 2n nodes in a segment tree.

    This could be proved in the following way: Segmented tree for array with n elements has n leaves (the array elements itself). The number of nodes in each level is half the number in the level below.

    So if we sum the number by level we will get:

      n + n/2 + n/4 + n/8 + ... + 1 ≈ 2n
    

    Space complexity : O(n).

    We used 2n extra space to store the segment tree.

    Update segment tree

    When we update the array at some index i we need to rebuild the segment tree, because there are tree nodes which contain the sum of the modified element. Again we will use a bottom-up approach. We update the leaf node that stores a[i]. From there we will follow the path up to the root updating the value of each parent as a sum of its children values.

      void update(int pos, int val) {
    
          pos += n;
          tree[pos] = val;
    
          while (pos > 0) {
    
              int left = pos;
              int right = pos;
      
              if (pos % 2 == 0) {
                  right = pos + 1;
              } else {
                  left = pos - 1;
              }
    
              // parent is updated after child is updated
              tree[pos / 2] = tree[left] + tree[right];
              pos /= 2;
          }
      }
    

    Complexity Analysis

    Time complexity : O(logn).

    Algorithm has O(logn) time complexity, because there are a few tree nodes with range that include ith array element, one on each level. There are log(n) levels.

    Space complexity : O(1)O(1).

    Range Sum Query

    We can find range sum query [L, R] using segment tree in the following way:

    Algorithm hold loop invariant:

    l≤r and sum of [L…l] and [r…R] has been calculated, where l and r are the left and right boundary of calculated sum. Initially we set l with left leaf L and r with right leaf R. Range [l, r] shrinks on each iteration till range borders meets after approximately logn iterations of the algorithm

    Loop till l≤r

    Check if l is right child of its parent P

    l is right child of P. Then P contains sum of range of l and another child which is outside the range [l, r] and we don’t need parent P sum. Add l to sumsum without its parent PP and set l to point to the right of PP on the upper level.

    l is not right child of P. Then parent P contains sum of range which lies in [l, r]. Add P to sumsum and set l to point to the parent of P

    Check if r is left child of its parent P

    r is left child of P. Then P contains sum of range of r and another child which is outside the range [l, r] and we don’t need parent P sum. Add r to sum without its parent P and set r to point to the left of PP on the upper level.

    r is not left child of P. Then parent P contains sum of range which lies in [l, r]. Add P to sum and set r to point to the parent of P

      public int sumRange(int l, int r) {
    
          // get leaf with value 'l'
          l += n;
          // get leaf with value 'r'
          r += n;
    
          int sum = 0;
    
          while (l <= r) {
    
              if ((l % 2) == 1) {
                  sum += tree[l];
                  l++;
              }
    
              if ((r % 2) == 0) {
                  sum += tree[r];
                  r--;
              }
    
              l /= 2;
              r /= 2;
          }
    
          return sum;
      }
    

    Complexity Analysis

    Time complexity : O(logn)

    Time complexity is O(logn) because on each iteration of the algorithm we move one level up, either to the parent of the current node or to the next sibling of parent to the left or right direction till the two boundaries meet. In the worst-case scenario this happens at the root after logn iterations of the algorithm.

    Space complexity : O(1).

  • 使用线段树频繁求区间内最小值

      import java.util.Arrays;
      import java.util.List;
    
      class SegmentTree {
    
          public static void main(String[] args) {
    
              List<Integer> list = Arrays.asList(10, 3, 2, -1, -2, 9, 7, 1, 4, 8, 11, 6, 5, -4);
              //Collections.shuffle(list);
    
              int[] array = new int[list.size()];
              for (int i = 0; i < list.size(); i++) {
                  array[i] = list.get(i);
              }
    
              SegmentTree segmentTree = new SegmentTree(array);
    
              System.out.println("segmentTree.findMinElement(0, 14)  " + segmentTree.findMinElement(0, 14));
              System.out.println("segmentTree.findMinElement(0, 14)  " + segmentTree.findMinElement(0, 8));
          }
    
          private int[] segmentTree = null;
          private int originalNumsLength = 0;
    
          public SegmentTree(int[] nums) {
    
              if (nums == null || nums.length == 0) {
                  return;
              }
    
              this.segmentTree = new int[nums.length * 2];
              this.originalNumsLength = nums.length;
    
              for (int i = nums.length; i < segmentTree.length; i++) {
                  this.segmentTree[i] = nums[i - nums.length];
              }
    
              for (int i = nums.length - 1; i > 0; i--) {
    
                  segmentTree[i] = Math.min(segmentTree[2 * i], segmentTree[2 * i + 1]);
              }
          }
    
          /**
           * To find the minimum element within a range.
           *
           * @param startIndex the start index of the searching range (inclusive)
           * @param endIndex   the end index of the searching range (exclusive)
           * @return the minimum element with the range
           */
          public int findMinElement(int startIndex, int endIndex) {
    
              if (this.segmentTree == null) {
                  throw new UnsupportedOperationException();
              }
    
              startIndex += this.originalNumsLength;
              endIndex += this.originalNumsLength - 1;
    
              int result = Integer.MAX_VALUE;
    
              while (startIndex <= endIndex) {
    
                  if ((startIndex & 1) != 0) {
                      result = Math.min(result, segmentTree[startIndex]);
                      startIndex++;
                  }
    
                  if ((endIndex & 1) == 0) {
                      result = Math.min(result, segmentTree[endIndex]);
                      endIndex--;
                  }
    
                  startIndex /= 2;
                  endIndex /= 2;
              }
    
              return result;
          }
      }
    
下载前可以先看下教程 https://pan.quark.cn/s/a426667488ae 标题“仿淘宝jquery图片左右切换带数字”揭示了这是一个关于运用jQuery技术完成的图片轮播机制,其特色在于具备淘宝在线平台普遍存在的图片切换表现,并且在整个切换环节中会展示当前图片的序列号。 此类功能一般应用于电子商务平台的产品呈现环节,使用户可以便捷地查看多张商品的照片。 说明中的“NULL”表示未提供进一步的信息,但我们可以借助标题来揣摩若干核心的技术要点。 在构建此类功能时,开发者通常会借助以下技术手段:1. **jQuery库**:jQuery是一个应用广泛的JavaScript框架,它简化了HTML文档的遍历、事件管理、动画效果以及Ajax通信。 在此项目中,jQuery将负责处理用户的点击动作(实现左右切换),并且制造流畅的过渡效果。 2. **图片轮播扩展工具**:开发者或许会采用现成的jQuery扩展,如Slick、Bootstrap Carousel或个性化的轮播函数,以达成图片切换的功能。 这些扩展能够辅助迅速构建功能完善的轮播模块。 3. **即时数字呈现**:展示当前图片的序列号,这需要通过JavaScript或jQuery来追踪并调整。 每当图片切换时,相应的数字也会同步更新。 4. **CSS美化**:为了达成淘宝图片切换的视觉效果,可能需要设计特定的CSS样式,涵盖图片的排列方式、过渡效果、点状指示器等。 CSS3的动画过渡特性(如`transition``animation`)在此过程中扮演关键角色。 5. **事件监测**:运用jQuery的`.on()`方法来监测用户的操作,比如点击左右控制按钮或自动按时间间隔切换。 根据用户的交互,触发相应的函数来执行...
垃圾实分割数据集 一、基础信息 • 数据集名称:垃圾实分割数据集 • 图片数量: 训练集:7,000张图片 验证集:426张图片 测试集:644张图片 • 训练集:7,000张图片 • 验证集:426张图片 • 测试集:644张图片 • 分类类别: 垃圾(Sampah) • 垃圾(Sampah) • 标注格式:YOLO格式,包含实分割的多边形点坐标,适用于实分割任务。 • 数据格式:图片文件 二、适用场景 • 智能垃圾检测系统开发:数据集支持实分割任务,帮助构建能够自动识别分割图像中垃圾区域的AI模型,适用于智能清洁机器人、自动垃圾桶等应用。 • 环境监控与管理:集成到监控系统中,用于实时检测公共区域的垃圾堆积,辅助环境清洁治理决策。 • 计算机视觉研究:支持实分割算法的研究优化,特别是在垃圾识别领域,促进AI在环保方面的创新。 • 教育与实践:可用于高校或培训机构的AI课程,作为实分割技术的实践数据集,帮助学生理解计算机视觉应用。 三、数据集优势 • 精确的实分割标注:每个垃圾实都使用详细的多边形点进行标注,确保分割边界准确,提升模型训练效果。 • 数据多样性:包含多种垃圾物品实,覆盖不同场景,增强模型的泛化能力鲁棒性。 • 格式兼容性强:YOLO标注格式易于与主流深度学习框架集成,如YOLO系列、PyTorch等,方便研究人员开发者使用。 • 实际应用价值:直接针对现实世界的垃圾管理需求,为自动化环保解决方案提供可靠数据支持,具有重要的社会意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值