问:
- 给定一个数组,求假如排序后,相邻两数的最大差值。要求时间复杂度O(N)。不能使用非基于比较的排序。
- 给一个长度为n的数字字符串,现在要划分区域,使得区域内异或和为0。最多能划分多少个。
解:
- 数组长度为N,创建N+1个容器。每个容器代表一个区间。区间为从数组最小值开始到最大值结束的均分N+1个。遍历数组,把每个元素根据值放入对应的区间容器里。给每个容器记录该容器内最大值和最小值。遍历容器,用当前容器最小值减去上个容器最大值的差值。其中最大的差值就是结果。解法核心就在于创建了N+1个容器。这意味着必定至少有一个容器是空的,所以最大差值一定是在不同容器之间发生的。这样就避免了同容器内的排序。
function getMaxReduce(arr) {
// 容器个数
const num = arr.length + 1
// 容器
const helpArr = []
// 每个容器的最小值和最大值
const helpMin = []
const helpMax = []
// 整个数组的最小值和最大值
let min = Infinity
let max = -Infinity
for (let i of arr) {
min = Math.min(min, i)
max = Math.max(max, i)
}
// 最大值和最小值的差值,有多少个数
const rde = max - min + 1
// 每个容器的大小
const section = rde / num
// 创建容器
for (let i = 0; i < num; i++) {
helpArr[i] = []
}
for (let i of arr ) {
// 当前数放在哪个容器
const idx = Math.floor((i - min) / section)
// 放入对应容器,并且计算容器里的最大值和最小值
helpArr[idx].push(i)
helpMin[idx] = helpMin[idx] ? Math.min(i, helpMin[idx]) : i
helpMax[idx] = helpMax[idx] ? Math.max(i, helpMax[idx]) : i
}
// 清除掉空的容器
let maxReduce = -Infinity
let preMax = helpMax[0]
for (let i = 1; i < helpArr.length; i++) {
// 空的容器直接跳过
if (!helpMin[i]) continue
// 当前容器的最小值,减去上一个最大值就是本次的差值
const tempRde = helpMin[i] - preMax
preMax = helpMax[i]
maxReduce = Math.max(maxReduce, tempRde)
}
return maxReduce
}
function getMaxSplit(str) {
const dp = []
dp[0] = str[0] === '0' ? 1 : 0
// 当前异或和
let curEor = str[0]
// 记录上一次异或和出现的位置表
const hashMap = new Map()
// 对于 i 来说,两种情况: ①加上第i个元素后,以i为结尾能划分出一个异或和为0的区域 ② 划分不出
// 第二种情况意味着i可有可无,所以dp[i]直接复用dp[i-1]
// 第一种情况。找到以i为结尾区域的开头位置。结果就是dp[开头位置] + 1。但它不一定能比dp[i-1]更好,所以取较大的
for (let i = 1; i < str.length; i++) {
// 计算当前异或和
curEor ^= str[i]
// 如果表中存在当前异或和
if (hashMap.has(curEor)) {
// 拿到当前异或和上次出现的位置idx,这意味着[idx+1,i]区间的异或和为0
const idx = hashMap.get(curEor)
dp[i] = Math.max(dp[idx] + 1, dp[i - 1])
} else {
// 如果不为0,那代表跟第i项无关
dp[i] = dp[i - 1]
}
// 记录位置信息
hashMap.set(curEor, i)
}
}