zoukankan      html  css  js  c++  java
  • 493. Reverse Pairs

    Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j].

    You need to return the number of important reverse pairs in the given array.

    Example1:

    Input: [1,3,2,3,1]
    Output: 2
    

    Example2:

    Input: [2,4,3,5,1]
    Output: 3
    

    Note:

    1. The length of the given array will not exceed 50,000.
    2. All the numbers in the input array are in the range of 32-bit integer.

    拿到题目后,最直观的解法就是遍历尝试所有的(i, j)组合,看看有多少种组合满足 i < j and nums[i] > 2*nums[j]这一条件。

    那么,写成代码就是一个嵌套的for循环:

    result := 0
    for
    i:= 0; i < len(nums); i++ { for j := i; j < len(nums); j++ {
        if nums[i] > nums[j]*2 {
          result += 1
        } } }

    可以想见,这样解法的时间复杂度为O(n²),空间复杂度为O(1)。

    那么,有么有什么办法,可以把时间复杂度降低一些呢?

    O(n*n)中,有个一个n是无法优化的,因为需要至少遍历每个元素一次,那么第二个n能否降低为<n的值呢?

    想象一下,当我们从第0个元素往右遍历的时候,随着index的增加,我们能知道哪些信息?

    (想象一个卷轴打开的样子,未打开的部分是未知的,已打开的部分就能看的内容)

    对于这类顺序展开的情况,我们可以将其抽象成 f(i,j) = f(i, j-1) + c的情况。其中,可以假定f(i, j-1) 已知,我们通过解决c来完成f(i, j)的求解。

    那么,在这道题目中,f(i, j-1) 和 c 分别是什么呢?

    假设:

    f(i, j-1) 代表 [i,j-1] 范围内,满足nums[i] > 2*nums[j] 这一条件的pair数。

    c 代表以 j 为end时,一共可以新增多少个pair。

    故,f(i,j) 就等于 f(i, j-1)已知的pair数 +  新增第j位后新出现的pair数

    那么,如何求 c 呢?

    以题目的第二个例子 [2,4,3,5,1] 举例:

    nums 2 4 3 5 1
    index 0 1 2 3
    c 0 0 0 0 3
    result 0 0 0 0 3


    只需要数一下,[0, j-1] 范围内,有多少个元素大于 2*nums[j] 即可得到c。或者换一个角度,即统计在[0, len(nums)-1] 内大于 2*nums[j] 的元素中有多少个已经出现了。

    我们可以将nums数组复制一个副本并排序,得到一个sorted[]数组,并用一个bit[]数组来标记哪些元素已经出现了(0表示元素还未出现,>0表示元素出现了若干次)。

    那么,问题转变成求一个范围内的累加值,即:

    idx := index(sorted, 2*nums[j]+1) //查找 2*nums[j]+1这数在sorted数组中的下标

    result := sum(idx, len(sorted)-1) //计算bit[]数组 [idx, n-1] 区间的累加值

    求指定范围内的累加值有几种方法:

    1、for 循环遍历累加,时间复杂度O(n)

    1 func rangeSum(nums []int, start, end int) int {
    2   result := 0
    3   for i := start; i <= end; i++ {
    4     result += nums[i]
    5   }
    6   return result
    7 }

    当数组中的值有变化时,时间复杂度仍为O(0)

    2、预先先算一个累计数组,即sum[];然后直接使用sum[j]-sum[i] 即可求得 [i, j]范围内的累加值。

     1 var sum []int
     2 func calcSums(nums []int)  {
     3   sum = make([]int, len(nums))
     4   lastSum := 0
     5   for i := 0; i < len(nums); i++ {
     6     sum[i] += nums[i] + lastSum
     7     lastSum = sum[i]
     8   }
     9 }
    10 
    11 func rangeSum(nums []int, start, end int) int {
    12   return sum[end] - sum[start]
    13 }

    当数组不变时,预处理时间为O(n),取值的时间为O(1)

    当数组元素变化时,需要O(n)时间来更新sum数组

    3、segment tree

     

     1 func rangeSum(nums []int, start, end int) int {
     2     root := addNode(nums, 0, len(nums)-1)
     3     getSum(root, start, end)
     4 }
     5 
     6 type SegmentTreeNode struct{
     7   sum int
     8   start int
     9   end int
    10   left *SegmentTreeNode
    11   right *SegmentTreeNode
    12 }
    13 
    14 func NewSegmentTreeNode(sum, start, end int, left, right *SegmentTreeNode) *SegmentTreeNode {
    15     return  &SegmentTreeNode{
    16         sum: sum,
    17         start: start,
    18         end: end,
    19         left: left,
    20         right: right,
    21     }
    22 }
    23 
    24 func addNode(nums []int, start, end int) *SegmentTreeNode {
    25     if start == end {
    26         return NewSegmentTreeNode(nums[start], start, end, nil, nil)
    27     }
    28     mid := (start+end)>>1
    29     left := addNode(nums, start, mid-1)
    30     right := addNode(nums, mid+1, end)
    31     return NewSegmentTreeNode(left.sum+right.sum, start, end, left, right)
    32 }
    33 
    34 func (node *SegmentTreeNode)update(idx, deltaVal int) {
    35     if node.start == idx && node.end == idx {
    36         node.sum += deltaValu
    37         return
    38     }
    39 
    40     mid := (node.start + node.end) >> 1
    41     sum := 0
    42     if idx >= mid {
    43         if node.right != nil {
    44             node.right.update(idx, deltaVal)
    45             sum += node.right.sum
    46         }
    47     }else{
    48         if node.left != nil {
    49             node.left.update(idx, deltaValu)
    50             sum += node.left.sum
    51         }
    52     }
    53     node.sum = sum
    54 }
    55 
    56 func getSum(root *SegmentTreeNode, start, end int) int {
    57     if root.start == start && root.end == end {
    58         return root.sum
    59     }
    60     //all in left subtree
    61     if end < root.start {
    62         if root.left == nil {
    63             return 0
    64         } 
    65         return getSum(root.left, start ,end)
    66     }
    67     //all in right subtree
    68     if start > root.end {
    69         if root.right == nil {
    70             return 0
    71         }
    72         return getSum(root.right, start ,end)
    73     }
    74     leftSum := getSum(root.left, start, root.start)
    75     rightSum := getSum(root.right, root.right, end)
    76     return leftSum+root.sum+rightSum
    77 }
    View Code

     segment tree的缺点是无法改变数组长度。

    因为,为了保证查询时间是logn,因此在构造时使用了二分法保证树尽量平衡。

     4、fenwick tree

    也称为 Binary Index Tree

     

     1 func reversePairs(nums []int) int {
     2     sorted := getSortedArray(nums)
     3     fenwickTree := NewFenwickTree(len(nums)+1)
     4     result := 0
     5     for i := 0; i < len(nums); i++{
     6         if 2*nums[i]+1 <= sorted[len(sorted)-1] {
     7             idx := index(sorted, 2*nums[i]+1)
     8             count := fenwickTree.getSum(len(fenwickTree.arr)-1) - fenwickTree.getSum(idx-1)
     9             result += count
    10             fmt.Println(i, nums[i], idx, count, result, fenwickTree.arr)
    11         }
    12         //将nums[i]加入到fenwick tree中,让后面的元素可以看到它
    13         fenwickTree.update(index(sorted, nums[i]), 1)
    14     }
    15     return result
    16 }
    17 
    18 type FenwickTree struct {
    19     arr []int
    20 }
    21 func NewFenwickTree(n int) *FenwickTree {
    22     return &FenwickTree{arr: make([]int, n)}
    23 }
    24 func (ft *FenwickTree)update(idx, delta int) {
    25     for ;idx < len(ft.arr); {
    26         ft.arr[idx] += delta
    27         idx += idx & -idx
    28     }
    29     //fmt.Println(idx, ft.arr)
    30 }
    31 func (ft *FenwickTree)getSum(idx int) int {
    32     sum := 0
    33     for ;idx > 0; {
    34         sum += ft.arr[idx]
    35         idx -= idx &-idx
    36     }
    37     return sum
    38 }
    39 
    40 func index(sorted []int, val int) int {
    41     l, r, m := 0, len(sorted)-1, 0
    42     for ; l < r; {
    43         m = (l+r) >> 1
    44         if sorted[m] == val {
    45             l = m
    46             break
    47         }else if sorted[m] > val{
    48             r = m-1
    49         }else{
    50             l = m+1
    51         }
    52     }
    53     //fmt.Println(val, l+1)
    54     return l+1
    55 }
    56 
    57 func getSortedArray(nums []int) []int {
    58     list := make([]int, len(nums))
    59     for i := 0; i < len(nums); i++ {
    60         list[i] = nums[i]
    61     }
    62    
    63     sort.Slice(list, func (i,j int) bool{
    64         return list[i]<list[j]
    65     })
    66     return list
    67 }
    View Code

    Reference:

    https://leetcode.com/problems/reverse-pairs/discuss/97268/General-principles-behind-problems-similar-to-%22Reverse-Pairs%22

    https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/

    https://zh.wikipedia.org/zh-hans/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84

    http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=3513BBA8CE0ADA3B5520B42FA344D045?doi=10.1.1.14.8917&rep=rep1&type=pdf

  • 相关阅读:
    meta属性
    博客
    概念术语
    装饰器与生成器
    Linux基础
    线程
    网络编程之socket
    网络编程之网络基础部分

    内置函数(max,min,zip)及文件处理
  • 原文地址:https://www.cnblogs.com/elaron/p/12215687.html
Copyright © 2011-2022 走看看