文章目录
Leetcode-2021.04-part2
4.17-220.存在重复元素Ⅲ
题目描述
方法一:暴力破解,索引区间搜索
分析
分析题意:
- 数组nums 和 参数k,t,要求:
- 在数组中存在两个数 nums[i], nums[j]
- 满足 nums[i], nums[j]差的绝对值 <= t
- 且 i 与 j 的差值的绝对值 <=k
- 不妨设 i < j
- 遍历数组nums
- 以i为起点, k为区间长度,搜索 [i, i+k] 区间是否有符合值
- 如果有,返回true
代码
func containsNearbyAlmostDuplicate(nums []int, k int, t int) bool {
// abs的设计 go语言的int 是 int32往上升级的,所以不会越界
// 如果是java语言,v建议用long接收,返回long 这样不会有越界问题
abs := func(v int) int {
if v < 0 {
return -v
}
return v
}
// k 区间跨度, t 值跨度
// 遍历每一个元素
for i := 0; i < len(nums); i++ {
// 以当前元素为起点,k为跨度搜索
for j := i+1; j < len(nums) && j <= i+k; j++ {
if abs(nums[j] - nums[i]) <= t {
return true
}
}
}
return false
}
方法二:值域分桶+滑动窗口
分析
维护一个桶,桶的设计是这样的:
- a - b <= t 得到a,b的最大区间长度t+1
- 假设值为v, 那么以v/(t+1)作为区间映射
- 比如t为8 v为1, v就落在0号区间
- v为-1, 那么v应该落在-1区间 桶作映射判断
窗口滑动,每k个元素为一个窗口
- 由于此时维护了桶,当窗口达到k长度时,下一个元素开始作滑动维护(详情见代码)
判断依据:
- 如果当前值已经有桶存在,一定为true
- 如果当前值桶不存在,判断相邻桶是否存在,存在则比较桶内值是否满足要求
代码
func containsNearbyAlmostDuplicate(nums []int, k int, t int) bool {
// abs的设计 go语言的int 是 int32往上升级的,所以不会越界
// 如果是java语言,v建议用long接收,返回long 这样不会有越界问题
// 绝对值计算
abs := func(num int) int {
if num < 0 {
return -num
}
return num
}
// 桶计算
getBucket := func(num int) int {
if num >= 0 {
return num/(t+1)
}
// 负数解释 因为-1 -> -t+1 如果直接除 会落在0号位上和-1号位上,所以作处理
// 0 -> -(t) 落在0号位上 -1 落在-1号位上
return (num+1)/(t+1)-1
}
// 维护桶和桶内最近值
buckets := make(map[int]int)
for i, v := range nums {
bucket := getBucket(v)
// 如果存在桶,true
if _, exist := buckets[bucket]; exist {
return true
}
// 不存在桶,那么判断相邻桶
// 如果左边桶存在,判断桶值val和当前值v 是否满足条件
if val, exist := buckets[bucket-1]; exist && abs(v-val) <= t {
return true
}
// 如果右边桶存在,判断桶值val和当前值 是否满足条件
if val, exist := buckets[bucket+1]; exist && abs(v-val) <= t {
return true
}
// 将当前桶维护到buckets里
buckets[bucket] = v
// 最关键的点:滑动窗口 k是区间长度 i 从0,k-1的长度已经达到k
// 所以窗口滑动开始维护的点是在等于k的时候
if i >= k {
// 将k个之前的桶删掉,保证当前的桶量符合窗口区间
delete(buckets, getBucket(nums[i-k]))
}
}
// 遍历结束都不满足,返回false
return false
}
4.18-26.删除有序数组中的重复项
题目描述
分析
简单题:双指针即可
代码
// bug题,即使不做严谨性判断,代码也能通过 而此时返回的至少是1
func removeDuplicates(nums []int) int {
// 严谨性判断
if len(nums) == 0 {
return 0
}
// 维护一个假想栈
top := 0
for i := 1; i < len(nums); i++ {
// 栈顶元素等于当前元素则继续
if nums[top] == nums[i] {
continue
}
// 否则当前元素加入栈顶
top++
nums[top] = nums[i]
}
// 返回栈长
return top+1
}
4.19-27.移除元素
题目描述
分析
简单题,双指针即可
代码
func removeElement(nums []int, val int) int {
top := 0
for i := 0; i < len(nums); i++ {
if nums[i] != val {
nums[top] = nums[i]
top++
}
}
return top
}
4.20-28.实现strStr()
题目描述
分析
根据题意,
- 如果needle=="",直接return 0
- 遍历haystack,找到第一个与needle[0]相等的字符量,记录当前的索引,就遍历往下继续比;
- 如果needle被遍历结束,说明找到相同的了,返回之前记录的索引;
- 如果中途出现一个不和needle的字符一致的,那么重置needle,找下一个位置和needle[0]相同,重复2.1 ,2.2
- 临界条件:
- 当haystack遍历完了还没结果,那么返回-1;
- 遍历匹配needle元素的时候,别忘记判断haystack是否越界
时间复杂度:O(nm) n为haystack长度,m为needle长度
代码
func strStr(haystack string, needle string) int {
// 根据题意,如果为空串,那么返回0
if len(needle) == 0 {
return 0
}
flag: // 外循环标志
for i := range haystack {
// 如果当前值和needle[0]匹配不上,继续遍历下一个值
if haystack[i] != needle[0] {
continue
}
// 匹配上之后,先记录当前索引,以便可以返回
index := i
// 从当前位置开始,和needle一一比较
for j := 0; j < len(needle); j++ {
// 存在某一个字符不匹配,退出比较循环,继续外层循环
if haystack[i] != needle[j] {
continue flag
}
i++
// 循环比较的时候,haystack指针i同时变动,所以需要判断指针越界情况,如果i已经越界了,而j还未遍历完,那么 说明匹配不上
if i == len(haystack) && j+1 != len(needle) {
return -1
}
}
// 如果比较循环能够走完,说明index可以返回
return index
}
return -1
}