记录 1 道算法题
数组的相对排序
要求:提供两个数组 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 的数字的相对顺序,另一种是使用计数数组。
- 哈希表 + 排序
首先我们需要得到一个关于 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
}
- 计数数组
和名字一样,我们需要有一个计数的数组进行计数,在空间上的优化细节是,我们只需要 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
}