数组序号转换

标签: 数组 哈希表 排序

难度: Easy

给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。

序号代表了一个元素有多大。序号编号的规则如下:

  • 序号从 1 开始编号。
  • 一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
  • 每个数字的序号都应该尽可能地小。

示例 1:

输入:arr = [40,10,20,30]
输出:[4,1,2,3]
解释:40 是最大的元素。 10 是最小的元素。 20 是第二小的数字。 30 是第三小的数字。

示例 2:

输入:arr = [100,100,100]
输出:[1,1,1]
解释:所有元素有相同的序号。

示例 3:

输入:arr = [37,12,28,9,100,56,80,5,12]
输出:[5,3,4,2,8,6,7,1,3]

提示:

  • 0 <= arr.length <= 105
  • -109 <= arr[i] <= 109

Submission

运行时间: 56 ms

内存: 34.6 MB

class Solution:
    def arrayRankTransform(self, arr: List[int]) -> List[int]:
        ranks = {n:i for i, n in enumerate(sorted(set(arr)), 1)}
        return [ranks[n] for n in arr]

Explain

这个解法首先使用了集合来去除数组中的重复元素,然后对这个集合进行排序,以保证元素的序号是按照从小到大的顺序来分配的。接着,利用枚举函数enumerate,从1开始为排序后的元素生成一个序号映射(存储在字典ranks中)。最后,遍历原始数组arr,根据字典ranks里的映射关系,将arr中的每个元素替换为其对应的序号。

时间复杂度: O(n log n)

空间复杂度: O(n)

class Solution:
    def arrayRankTransform(self, arr: List[int]) -> List[int]:
        # 创建一个字典,键为arr中的元素,值为其排名
        ranks = {n:i for i, n in enumerate(sorted(set(arr)), 1)}
        # 遍历原数组,替换每个元素为其对应的排名
        return [ranks[n] for n in arr]

Explore

在Python中,集合(set)是一个基于哈希表的数据结构,它可以高效地处理元素的唯一性,即自动去除重复元素,且插入和查找操作的平均时间复杂度为O(1)。相比之下,如果使用列表去重,需要O(n^2)的时间复杂度来检查是否存在重复项。虽然哈希表(例如字典)也可以用来去重,集合提供了更为直接和简洁的方法来处理这个问题,因为它本质上是一个无序且不重复的元素集,更适合本题的需求。

在Python中,`enumerate` 函数默认从0开始生成索引。然而,在调用`enumerate`时,可以指定一个起始索引,通过传递第二个参数(start=1)来实现从1开始编号。这样,即使排序后的集合从0开始索引,使用`enumerate(sorted_set, 1)`确保了序号从1开始计数,这对应到数组元素的实际排名。

当数组长度接近0时,该算法效率很高,因为操作的数据量极小。而在数组长度接近10^5时,虽然算法仍可以工作,但需要进行大量的排序和查找操作,时间复杂度为O(n log n)。为了优化处理大规模数据的能力,可以考虑使用并行处理或优化排序算法,如使用多线程进行排序和映射生成,或者采用更高效的排序算法(如基数排序或计数排序),尤其是当数据范围有限时。

如果原数组非常大,确实可能导致内存消耗增加,尤其是在创建排序数组和字典时。一种可能的优化策略是使用原地算法(in-place)来减少额外的内存使用,例如直接在原数组上修改值而不是创建一个新的数组副本。此外,可以考虑使用更为紧凑的数据结构,或者在处理过程中清理不再需要的数据结构,例如在创建完映射字典后清除排序数组。