指针
6.三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
题解
该题需要先从小到大排序,后需要建立三个指针,从头到尾完成遍历,第一层遍历决定第一个元素(first),第二层遍历决定第二个元素(second = first + 1)跟第三个元素(third,初始化为最大的元素)。其中对于确认了first后,就要对second和third进行首尾遍历。原则为:三者相加,大于target则左移third,小于则右移second。另外需要注意,不能输出相同的元素,又因为该数组一开始就经过排序,则可以跳过相等的两个元素。
package main
import (
"fmt"
"sort"
)
func threeSum(nums []int) [][]int {
var res [][]int
sort.Ints(nums) // 使用sort包中的Ints函数对切片进行排序
for first := 0; first < len(nums); first++ {
if first > 0 && nums[first] == nums[first-1] {
continue // 跳过重复元素
}
target := -nums[first]
third := len(nums) - 1
for second := first + 1; second < len(nums)-1; second++ {
if second > first+1 && nums[second] == nums[second-1] {
continue // 跳过重复元素
}
for second < third && nums[second]+nums[third] > target {
third-- // 向头部收缩
}
if second == third {
break // 相等了,则直接跳出内层循环
}
if nums[second]+nums[third] == target {
res = append(res, []int{nums[first], nums[second], nums[third]})
}
}
}
return res
}
func main() {
arr := threeSum([]int{-1, 0, 1, 2, -1, -4})
fmt.Println(arr) // 输出: [[-1 -1 2] [-1 0 1]]
}
7.最接近的两数和 - 字节-上海
给出一个数组和一个目标值,找出两个和最接近目标值的子项
题解
数组从大到小排序,双指针首尾收缩遍历,当前两者相加大于目标则收缩尾部,小于则收缩头部,用res记录最接近目标值的值
package main
import (
"fmt"
"math"
"sort"
)
func testNear(arrNear []int, targetNear int) int {
// 排序
sort.Ints(arrNear)
left, right := 0, len(arrNear)-1
var res int
for left < right {
temp := arrNear[left] + arrNear[right]
if temp == targetNear {
return temp // 直接返回结果,因为找到了精确匹配
} else if temp > targetNear {
right--
} else {
left++
}
// 更新最接近的值
if math.Abs(float64(targetNear-res)) > math.Abs(float64(targetNear-temp)) {
res = temp
}
}
return res
}
func main() {
arrNear := []int{24, 69, 14, 37}
targetNear := 60
nearP := testNear(arrNear, targetNear)
fmt.Printf("最接近的值是: %d\n", nearP) // 61
}
8 接雨水 - 腾讯cdg
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
题解
一:动态规划
用dp数组记录下标i及其左(leftMax )右(rightMax)边的所有柱子的最大高度。dp数组初始化,leftMax[0] = height[0];和rightMax[len - 1] = height[len - 1];。dp数组遍历过程中,左右侧的值与当值的高度进行对比,更新dp数组
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
rightMax[j] = Math.max(rightMax[j + 1], height[j]);
左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
// 动态规划
package main
import (
"fmt"
)
func trapDp(height []int) int {
if len(height) == 0 {
return 0
}
len := len(height)
// 下标i及其左边的所有柱子的最大高度
leftMax := make([]int, len)
leftMax[0] = height[0]
// 下标i及其右边的所有柱子的最大高度
rightMax := make([]int, len)
rightMax[len-1] = height[len-1]
// 计算每个位置左侧的最大高度
for i := 1; i < len; i++ {
leftMax[i] = max(leftMax[i-1], height[i])
}
// 计算每个位置右侧的最大高度
for j := len - 2; j >= 0; j-- {
rightMax[j] = max(rightMax[j+1], height[j])
}
var ans int
// 计算总的水量
for k := 0; k < len; k++ {
// 左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
ans += min(leftMax[k], rightMax[k]) - height[k]
}
return ans
}
// 辅助函数:返回两个整数中的较大值
func max(a, b int) int {
if a > b {
return a
}
return b
}
// 辅助函数:返回两个整数中的较小值
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
height := []int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}
result := trapDp(height)
fmt.Println(result) // 输出: 6
}
二、双指针
头尾双指针收缩遍历,详细见代码解
// 双指针
package main
import (
"fmt"
)
func trap(height []int) int {
if len(height) == 0 {
return 0
}
left, right := 0, len(height)-1
leftMax, rightMax := height[left], height[right]
ans := 0
for left < right {
// 更新左右两侧的最大值
if height[left] > leftMax {
leftMax = height[left]
}
if height[right] > rightMax {
rightMax = height[right]
}
// 移动较矮的指针并累加可以积累的雨水
if height[left] < height[right] {
ans += leftMax - height[left]
left++
} else {
ans += rightMax - height[right]
right--
}
}
return ans
}
func main() {
height := []int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}
result := trap(height)
fmt.Println(result) // 输出: 6
}
9 无重复字符串的最长子串
给定一个字符串s ,请你找出其中不含有重复字符的 最长
子串的长度。
题解
双指针搭配set, 用set去重,左指针移动, set移除元素,右指针移动,且右指针元素不存在于set中,则加入set,最后根据左右指针位置更新最大长度
package main
import (
"fmt"
)
func lengthOfLongestSubstring(s string) int {
// 使用map作为set,用于存储当前考虑的子字符串中的字符
charSet := make(map[rune]bool)
left, right := 0, -1
ans := 0
for left < len(s) {
if left > 0 {
// 移除左指针指向的字符
delete(charSet, rune(s[left-1]))
}
// 尝试移动右指针
for right+1 < len(s) && !charSet[rune(s[right+1])] {
// 右指针指向的字符不在集合中,则加入集合并移动右指针
charSet[rune(s[right+1])] = true
right++
}
// 更新最长子字符串的长度
ans = max(ans, right-left+1)
// 移动左指针
left++
}
return ans
}
// 辅助函数:返回两个整数中的较大值
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
s := "abcabcbb"
result := lengthOfLongestSubstring(s)
fmt.Println(result) // 输出: 3
}
10 找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
题解
使用《位置数组》记录当前值在英文字母中的顺序。初始化遍历先把p字符串和s下标0到pLen - 1的子串遍历完。如这个时候如果位置数组相等,则把下标0推进数组。后将s剩下的数据遍历完成。
package main
import (
"fmt"
)
func findAnagrams(s, p string) []int {
sLen, pLen := len(s), len(p)
if sLen < pLen {
return []int{}
}
ans := []int{}
sCount := make([]int, 26)
pCount := make([]int, 26)
// 初始化pCount和sCount(s的前pLen个字符)
for i := 0; i < pLen; i++ {
sCount[s[i]-'a']++
pCount[p[i]-'a']++
}
// 检查初始窗口是否匹配
if arraysEqual(sCount, pCount) {
ans = append(ans, 0)
}
// 滑动窗口
for i := 0; i < sLen-pLen; i++ {
// 移除窗口左侧的字符
sCount[s[i]-'a']--
// 添加窗口右侧的字符
sCount[s[i+pLen]-'a']++
// 检查窗口是否匹配
if arraysEqual(sCount, pCount) {
ans = append(ans, i+1)
}
}
return ans
}
// 辅助函数:检查两个整数数组是否相等
func arraysEqual(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func main() {
s := "cbaebabacd"
p := "abc"
arr := findAnagrams(s, p)
fmt.Println(arr) // 输出: [0 6]
}
关注我的公众号,回复 100905A2 获取hot100算法在线链接