[Week 2] LeetCode 335. Count of Smaller Numbers After Self

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/A_bigUncle/article/details/82714483

LeetCode 335. Count of Smaller Numbers After Self

问题描述

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Input: [5,2,6,1]
Output: [2,1,1,0] 
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

题解:

我们可以采用类似 MergeSort 的算法来解决这个问题,按照 Example 里给的数据 [5, 2, 6, 1] 来一步步分析:

  1. [5, 2, 6, 1] = [5, 2] + [6, 1]

  2. [5, 2] = [5] + [2] | [6, 1] = [6] + [1]

  3. 将 [5], [2] 合并成长度为2的数组:

    • 2 < 5, 则新数组[2, *], 所有已经被推入新数组的数组元素[2]
    • 数组已空,则新数组[2, 5], 比该元素小的数组元素有[2],故有5(1),代表比5小的元素有1个
  4. 将 [6], [1] 合并成长度为2的数组:

    • 1 < 6, 则新数组[1, *], 所有已经被推入新数组的数组元素[1]

    • 数组已空,则新数组[1, 6], 比该元素小的数组元素有[1],故有6(1),代表比6小的元素有1个

  5. 将 [2, 5], [1, 6] 合并成长度为4的数组:

    • 1 < 2, 则新数组[1, *, *, *], 所有已经被推入新数组的数组元素[1]

    • 2 < 6, 则新数组[1, 2, *, *], 比该元素小的数组元素有[1],故有2(1),代表比2小的元素有1个

    • 5 < 6, 则新数组[1, 2, 5, *], 比该元素小的数组元素有[1],故有5(1 + 1),代表比5小的元素有2个

    • 数组已空,则新数组[1, 2, 5, 6]

Ok, 目前我们已经算出输入 [5, 2, 6, 1] 的输出 [2, 1, 1, 0]。总结一下就是,记录 MergeSort 每次合并过程中数组已被推入新数组的元素(但其实我们只需要维护一个 helpCount 变量来记录个数即可),当数组中有元素推入,则其所维护的统计量(比它小的元素个数)加上 helpCount。

Code:

class Solution {
public:
    vector<int> countSmaller(vector<int> &nums)
    {
      vector<int> indices(nums.size(), 0);
      vector<int> result(nums.size(), 0);
      for (int i = 0; i < nums.size(); ++i)
        indices[i] = i;
      helper(nums, indices, result, 0, nums.size() - 1);
      return result;
    }

private:
    void helper(vector<int> &nums, vector<int> &indices,
                vector<int> &result, int low, int high)
    {
      if (low >= high)
        return;

      int mid = (low + high) / 2;
      helper(nums, indices, result, low, mid);
      helper(nums, indices, result, mid + 1, high);

      int i = low, j = mid + 1, helpCount = 0, k = 0;
      vector<int> tmp_nums(high - low + 1), tmp_indices(indices.begin(), indices.end());
      while (i <= mid && j <= high)
      {
        if (nums[i] <= nums[j])
        {
          tmp_nums[k] = nums[i];
          tmp_indices[low + k] = indices[i];
          result[indices[i]] += helpCount;
          ++i;
        }
        else
        {
          tmp_nums[k] = nums[j];
          tmp_indices[low + k] = indices[j];
          ++helpCount;
          ++j;
        }
        ++k;
      }

      while (i <= mid)
      {
        tmp_nums[k] = nums[i];
        tmp_indices[low + k] = indices[i];
        result[indices[i]] += helpCount;
        ++i;
        ++k;
      }

      while (j <= high)
      {
        tmp_nums[k] = nums[j];
        tmp_indices[low + k] = indices[j];
        ++helpCount;
        ++j;
        ++k;
      }

      for (i = low, j = 0; i <= high; ++i, ++j)
        nums[i] = tmp_nums[j];
      indices.assign(tmp_indices.begin(), tmp_indices.end());
    }
};

复杂度分析:

将问题分解成求两个小数组(左半数组和右半数组,元素个数为 n/2 )的 Count of Smaller Numbers After Self 问题,求完后再在合并过程中求整数组的 Count of Smaller Numbers After Self 问题,而两个小问题又能各自分解成两个小问题,依此递归直至数组元素个数为1。

故我们能写出表达式:T(n) = 2T(n/2) + O(n)

根据大师定理得:a = 2, b = 2, d = 1 即 d = logab,所以表达式复杂度为 O(nlogn)

Ok,如果对大师定理不了解,我们可以这样分析,将一个元素个数为n的数组每次折半,需要几次能将数组大小折为1呢,答案是 log2n 对吧!每一层数组元素个数为上一层的一半(第1层除外)则每一层我们需要遍历数组几次呢,答案是一次,例如[5],[2],[6],[1],要将它们变为长度为2的两个数组,需要遍历这四个数组各一次,所以加起来就是原数组的长度。

其它解法:

  1. 维护二叉搜索树,将数组的元素从左到右依次插入树中,如果元素比root小,则root的统计量+1,然后进入左子树,不断递归到叶子节点…
  2. 使用STL的 upper_bound 和 lower_bound 函数…

猜你喜欢

转载自blog.csdn.net/A_bigUncle/article/details/82714483