前言
如何判定一个数组是否为等差数列,可以直接排序O(nlogn),再不断求相邻数之间的差值,通过前后差值一致+传递性,完成等差数列的判定。但这没有充分利用等差数列的特性,强行将数排序,不如把每个数在等差数列的位置记下来,实现空间换时间。
一、等差子数组
二、空间换时间
排序,将凌乱的数按递增排序回来,时间复杂度不会小于O(nlogn),而通过公差 + 首项,充分利用等差数列的特性,得到每个数在等差数列中的位置,只要位置不重复,那么这个数组一定能排序成等差数列。
- 先取a = min,b = max,公差d = (b - a) / (nums.length - 1),不能完全除尽,说明不能排序成等差数列。
- 遍历nums,记录index = (x - a) / d,同样不能除尽,说明不能排除等差数列。
- 记录这些index是否出现过,重复出现说明不能排序成等差数列。
- 如果没有重复index,说明nums能排序成等差数列。
func checkArithmeticSubarrays(nums []int, l []int, r []int) []bool {
// 主方法,遍历查询数组,得到答案并返回。
ans := make([]bool,len(l))
for i := 0 ;i < len(l);i++ {
left,right := l[i],r[i]
// 取最大和最小值
b,a := getM(nums,left,right,1),getM(nums,left,right,-1)
// 计算公差
g1,g2 := b - a,right - left
d := g1 / g2
ans[i] = g1 % g2 == 0 && (d == 0 || isVaild(nums,left,right,a,d))
}
return ans
}
func isVaild(nums []int,left,right,a,d int) bool {
set := map[int]interface{}{}
for i := left;i <= right;i++ {
t := (nums[i] - a) / d
if _,ok := set[t];ok || (nums[i] - a) % d != 0 {
return false
}
set[t] = struct{}{}
}
return true
}
func getM(nums []int,left,right,r int) int {
m := nums[left]
for i := left;i <= right;i++ {
// 一正 一负 取min
if m * r < nums[i] * r {
m = nums[i]
}
}
return m
}
// 等差数列:s[i + 1] - s[i] = s[1] - s[0]
// 根据左右下标,查找该区间内重排序后是否为等差数列。
// 这里是寻找子数组,而不是子序列。
总结
1)充分利用bg中的特点,挖掘潜在规律,将问题转换,让时间复杂度降到最低。利用等差数列中的公差+首项+index,完成O(n)排定数组是否能排序成等差数列。
参考资料
[1] LeetCode 等差子数组