排序题目:第三大的数

题目

标题和出处

标题:第三大的数

出处:414. 第三大的数

难度

2 级

题目描述

要求

给定整数数组 nums \texttt{nums} nums,返回数组中第三大的不同数。如果不存在,则返回数组中最大的数。

示例

示例 1:

输入: num   =   [3,   2,   1] \texttt{num = [3, 2, 1]} num = [3, 2, 1]
输出: 1 \texttt{1} 1
解释:
第一大的不同数是 3 \texttt{3} 3
第二大的不同数是 2 \texttt{2} 2
第三大的不同数是 1 \texttt{1} 1

示例 2:

输入: num   =   [1,   2] \texttt{num = [1, 2]} num = [1, 2]
输出: 2 \texttt{2} 2
解释:
第一大的不同数是 2 \texttt{2} 2
第二大的不同数是 1 \texttt{1} 1
第三大的不同数不存在,所以返回最大的数 2 \texttt{2} 2

示例 3:

输入: num   =   [2,   2,   3,   1] \texttt{num = [2, 2, 3, 1]} num = [2, 2, 3, 1]
输出: 1 \texttt{1} 1
解释:
第一大的不同数是 3 \texttt{3} 3
第二大的不同数是 2 \texttt{2} 2(两个 2 \texttt{2} 2 都是第二大的不同数,因为值相同)。
第三大的不同数是 1 \texttt{1} 1

数据范围

  • 1 ≤ nums.length ≤ 10 4 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{4} 1nums.length104
  • -2 31 ≤ nums[i] ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{nums[i]} \le \texttt{2}^\texttt{31} - \texttt{1} -231nums[i]2311

进阶

你能设计一个时间复杂度 O(n) \texttt{O(n)} O(n) 的解决方案吗?

解法一

思路和算法

这道题目要求返回数组 nums \textit{nums} nums 中第三大的不同数,即排序时相同的元素只计算一次。为了确保相同的元素只计算一次,可以使用哈希集合存储数组中的元素。

将数组中的元素全部加入哈希集合之后,新建一个数组存储哈希集合中的全部元素,然后对新数组排序,新数组的长度即为数组 nums \textit{nums} nums 中的不同元素个数。

当新数组的长度至少为 3 3 3 时,存在第三大的不同数,返回新数组中的第三大元素。当新数组的长度小于 3 3 3 时,不存在第三大的不同数,返回新数组中的最大元素。

代码

class Solution {
    public int thirdMax(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        for (int num : nums) {
            set.add(num);
        }
        int size = set.size();
        int[] distinct = new int[size];
        int index = 0;
        for (int num : set) {
            distinct[index++] = num;
        }
        Arrays.sort(distinct);
        return size >= 3 ? distinct[size - 3] : distinct[size - 1];
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 nums \textit{nums} nums 的长度。使用哈希集合与新数组存储数组 nums \textit{nums} nums 中的元素需要 O ( n ) O(n) O(n) 的时间,对新数组排序需要 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间,因此时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。哈希集合与新数组需要 O ( n ) O(n) O(n) 的空间。

解法二

思路和算法

解法一使用哈希集合确保相同的元素只计算一次,也可以直接对原数组排序和遍历,达到确保相同的元素只计算一次的目的。

对数组 nums \textit{nums} nums 排序,由于 Java 自带的 Arrays.sort \texttt{Arrays.sort} Arrays.sort 对于基本数据类型排序都是升序排序,因此排序后的数组中,末尾元素是最大元素。

排序之后,从后往前遍历数组 nums \textit{nums} nums,遍历过程中维护遍历到的不同元素个数。由于末尾元素是最大元素,因此遍历时跳过末尾元素,将不同元素个数初始化为 1 1 1。对于每个遍历到的元素,执行如下操作。

  • 如果当前元素等于上一个遍历到的元素(即当前元素下标加 1 1 1 位置的元素),则没有遇到新的不同元素,跳过当前元素。

  • 如果当前元素不等于上一个遍历到的元素,则当前元素一定小于上一个遍历到的元素(由于数组已经有序),将不同元素个数加 1 1 1。如果不同元素个数等于 3 3 3,则当前元素为第三大的不同数,返回当前元素。

如果遍历结束之后,不同元素个数小于 3 3 3,则第三大的不同数不存在,返回排序后的数组的末尾元素。

代码

class Solution {
    public int thirdMax(int[] nums) {
        Arrays.sort(nums);
        int length = nums.length;
        int count = 1;
        for (int i = length - 2; i >= 0; i--) {
            if (nums[i] != nums[i + 1]) {
                count++;
                if (count == 3) {
                    return nums[i];
                }
            }
        }
        return nums[length - 1];
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 nums \textit{nums} nums 的长度。排序需要 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间,反向遍历数组需要 O ( n ) O(n) O(n) 的时间,因此时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是数组 nums \textit{nums} nums 的长度。排序需要 O ( log ⁡ n ) O(\log n) O(logn) 的递归调用栈空间。

解法三

思路和算法

上述两种解法都需要排序,时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)。如果要将时间复杂度降低到 O ( n ) O(n) O(n),则需要直接遍历数组找到第三大的不同数。

使用三个变量 first \textit{first} first second \textit{second} second third \textit{third} third 分别表示第一大的不同数、第二大的不同数和第三大的不同数。由于数组 nums \textit{nums} nums 中的元素范围是整个 int \texttt{int} int 类型的取值范围,为了避免初始化的值与数组中的元素重复,将 first \textit{first} first second \textit{second} second third \textit{third} third 都声明成 long \texttt{long} long 类型,并初始化为 long \texttt{long} long 类型的最小值,即 − 2 63 -2^{63} 263

遍历数组 nums \textit{nums} nums,遍历过程中维护遍历到的不同元素个数。对于每个元素 num \textit{num} num,如果 num \textit{num} num first \textit{first} first second \textit{second} second third \textit{third} third 相等,则跳过,当 num \textit{num} num first \textit{first} first second \textit{second} second third \textit{third} third 都不相等时,执行如下操作。

  • 如果 num > first \textit{num} > \textit{first} num>first,则将 first \textit{first} first second \textit{second} second third \textit{third} third 的值分别更新为 num \textit{num} num first \textit{first} first second \textit{second} second,将不同元素个数加 1 1 1

  • 如果 second < num < first \textit{second} < \textit{num} < \textit{first} second<num<first,则将 second \textit{second} second third \textit{third} third 的值分别更新为 num \textit{num} num second \textit{second} second first \textit{first} first 的值不变,将不同元素个数加 1 1 1

  • 如果 third < num < second \textit{third} < \textit{num} < \textit{second} third<num<second,则将 third \textit{third} third 的值更新为 num \textit{num} num first \textit{first} first second \textit{second} second 的值不变,将不同元素个数加 1 1 1

遍历结束之后,如果不同元素个数大于等于 3 3 3,则第三大的不同数存在,返回 third \textit{third} third,否则第三大的不同数不存在,返回 first \textit{first} first

代码

class Solution {
    public int thirdMax(int[] nums) {
        long first = Long.MIN_VALUE;
        long second = Long.MIN_VALUE;
        long third = Long.MIN_VALUE;
        int count = 0;
        for (long num : nums) {
            if (num == first || num == second || num == third) {
                continue;
            }
            if (num > first) {
                third = second;
                second = first;
                first = num;
                count++;
            } else if (num > second) {
                third = second;
                second = num;
                count++;
            } else if (num > third) {
                third = num;
                count++;
            }
        }
        return count >= 3 ? (int) third : (int) first;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要遍历数组一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值