您现在的位置是:首页 >技术教程 >(数组) 1365. 有多少小于当前数字的数字 ——【Leetcode每日一题】网站首页技术教程

(数组) 1365. 有多少小于当前数字的数字 ——【Leetcode每日一题】

酷酷的懒虫 2024-09-14 00:01:02
简介(数组) 1365. 有多少小于当前数字的数字 ——【Leetcode每日一题】

❓1365. 有多少小于当前数字的数字

难度:简单

给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。

换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != inums[j] < nums[i]

以数组形式返回答案。

示例 1:

输入:nums = [8,1,2,2,3]
输出:[4,0,1,1,3]
解释:
对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。
对于 nums[3]=2 存在一个比它小的数字:(1)。
对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。

示例 2:

输入:nums = [6,5,4,8]
输出:[2,1,0,3]

示例 3:

输入:nums = [7,7,7,7]
输出:[0,0,0,0]

提示

  • 2 < = n u m s . l e n g t h < = 500 2 <= nums.length <= 500 2<=nums.length<=500
  • 0 < = n u m s [ i ] < = 100 0 <= nums[i] <= 100 0<=nums[i]<=100

?思路:

两层 for 循环暴力查找,时间复杂度明显为 O ( n 2 ) O(n^2) O(n2)。这里不做介绍,我们来看一下如何优化。

法一:排序

首先要找小于当前数字的数字,那么从小到大排序之后,该数字之前的数字就都是比它小的了。

  • 所以可以定义一个新数组 temp ,将数组排个序,排序之后,其实每一个数值的下标就代表这前面有几个比它小的了;
  • 然后在遍历原数组 nums 是,对每一个 num[i]temp 使用二分查找,查到每个元素第一个位置的下标,即为 比它小的所有数字的数目

法二: 计数排序

注意到数组元素的值域为 [0,100],所以可以考虑建立一个频次数组 cntcnt[i] 表示数字 i 出现的次数。

那么对于数字 i 而言,小于它的数目就为 cnt[0...i−1] 的总和。

?代码:(Java、C++)

法一:排序
Java

class Solution {
    public int[] smallerNumbersThanCurrent(int[] nums) {
        int n = nums.length;
        int[] temp = Arrays.copyOf(nums, nums.length);
        Arrays.sort(temp);
        int[] ans = new int[nums.length];
        for(int i = 0; i < n; i++){
            int left = 0, right = n - 1;
            while(left <= right){
                int mid = left + (right - left) / 2;
                if(temp[mid] >= nums[i]) right--;
                else left++;
            }
            ans[i] = right + 1;
        }
        return ans;
    }
}

C++

class Solution {
public:
    vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        int n = nums.size();
        vector<int> temp(nums), ans(n);
        sort(temp.begin(), temp.end());
        for(int i = 0; i < n; i++){
            int left = 0, right = n - 1;
            while(left <= right){
                int mid = left + (right - left) / 2;
                if(temp[mid] >= nums[i]) right--;
                else left++;
            }
            ans[i] = right + 1;
        }
        return ans;
    }
};

法二: 计数排序
Java

class Solution {
    public int[] smallerNumbersThanCurrent(int[] nums) {
        int n = nums.length;
        int[] cnt = new int[101];
        int[] ans = new int[n];
        for(int num : nums){
            cnt[num]++;
        }
        int sum = 0;
        for(int i = 0; i <= 100; i++){
            if(cnt[i] != 0){
                sum += cnt[i];
                cnt[i] = sum - cnt[i];
            }
        }
        for(int i = 0; i < n; i++){
            ans[i] = cnt[nums[i]];
        }
        return ans;
    }
}

C++

class Solution {
public:
    vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        int n = nums.size();
        vector<int> cnt(101, 0), ans(n);
        for(int num : nums){
            cnt[num]++;
        }
        int sum = 0;
        for(int i = 0; i <= 100; i++){
            if(cnt[i] != 0){
                sum += cnt[i];
                cnt[i] = sum - cnt[i];
            }
        }
        for(int i = 0; i < n; i++){
            ans[i] = cnt[nums[i]];
        }
        return ans;
    }
};

? 运行结果:

在这里插入图片描述

? 复杂度分析:

法一:排序

  • 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),其中 n 为数组的长度,排序需要 O ( n l o g n ) O(nlogn) O(nlogn) 的时间,随后需要 O ( n ) O(n) O(n) 时间来遍历 乘上 二分查找的时间 O ( l o g n ) O(logn) O(logn) ,所以总时间为 O ( n l o g n ) O(nlogn) O(nlogn)
  • 空间复杂度 O ( n ) O(n) O(n),因为要额外开辟一个数组。

法二: 计数排序

  • 时间复杂度 O ( n + k ) O(n + k) O(n+k),其中 k 为值域大小。需要遍历两次原数组,同时遍历一次频次数组 cnt 找出前缀和。。
  • 空间复杂度 O ( k ) O(k) O(k),因为要额外开辟一个值域大小的数组。

题目来源:力扣。

放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我LeetCode主页 / CSDN—力扣专栏,每日更新!

注: 如有不足,欢迎指正!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。