[路飞]数组的相对排序

73 阅读2分钟

记录 1 道算法题

数组的相对排序

leetcode-cn.com/problems/re…


要求:提供两个数组 arr1 和 arr2,将 arr1 和 arr2 合并,然后要求如果数是在 arr2 内的,那么按照 arr2 的相对顺序进行排列,如果不在 arr2 内,则按照升序,放在 arr2 的最后一个数字后面。

比如:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6],结果:[2,2,2,1,4,3,3,9,6,7,19]

下面会介绍两种解法,一种是利用哈希表记录 arr2 的数字的相对顺序,另一种是使用计数数组。

  1. 哈希表 + 排序

首先我们需要得到一个关于 arr2 数字的顺序,我们可以简单用 arr2 的下标作为他们的顺序,得到顺序之后,sort排序 arr1,这时候会分为 3 种情况,1、两个数都在 arr2 中,这时候比较哈希表上的顺序。2、只有其中一个数在哈希表中,希望在哈希表中的数排前面。3、两个都不在哈希表中,这时候按照升序排列。

    function relativeSortArray(arr1, arr2) {
        const map = arr2.reduce((a, b, i) => {
            a[b] = i
            return a
        }, {})
        
        arr1.sort((a, b) => {
            const i = map[a]
            const ii = map[b]
            // 两个都在, 按照哈希表是顺序
            if (i && ii) {
                return i - ii
            }
            
            // 只有一个在,哈希表上的排前面
            if (i || ii) {
                return 1
            }
            
            // 都不在,升序
            return a - b
        })
        
        return arr1
    }
  1. 计数数组

和名字一样,我们需要有一个计数的数组进行计数,在空间上的优化细节是,我们只需要 arr1 上最大的数字的长度。

如何进行计数?首先我们遍历计数 arr1,然后遍历 arr2,如果 arr1 和 arr2 共同的数,则收集 arr2 的数字,同时将计数变为 0,如果 arr1上没有的数字,那么直接收集即可。

遍历完 arr2 之后,计数数组上仍留有一部分计数大于 0 的数字,正好他们与下标一一对应,所以已经是升序的,这时候只要将遍历 arr1 收集计数大于 0 的数字即可。

为了原地改变 arr1 引入一个指针 k 记录收集到哪。

    function relativeSortArray(arr1, arr2) {
        // 先找到最大的数字,作为计数数组的长度
        const limit = Math.max(...arr1)
        // 预留一个空间 limit + 1
        // 计数
        const countMap = arr1.reduce((a, b) => {
            ++a[b]
            return a
        }, new Array(limit + 1).fill(0))
        
        let k = 0
        for(let i = 0; i < arr2.length; i++) {
            const n = arr2[i]
            const count = countMap[n]
            // 合并数组,无论如何要收集 arr2 的值
            arr1[k] = n
            if (count) {
                for(let j = 0; j < count; j++) {
                    // 收集 arr1 的数字
                    arr1[j + k] = n
                }
                k += count
                countMap[n] = 0
            }
        }
        
        // 处理剩下 arr1 的未收集数字, 因为 arr1 已经原地改变了,所以需要遍历 countMap
        for(let i = 0; i < countMap.length; i++) {
            const count = countMap[i]
            if (count) {
                for(let j = 0; j < count; j++) {
                    // 收集 arr1 的数字, 与下标对应
                    arr1[j + k] = i
                }
                k += count
            }
        }
        
        return arr1
    }