目录
1. 有效的字母异位词(242)
【题目描述】
判断字符串 s 中的字符是否可以通过改变顺序的方式变成字符串 t(如果字符串 s 与 字符串 t 相同,那么也是可以的)。字符串中只包含小写字母。
【示例1】
Input: s = "anagram", t = "nagaram"
Output: true
【示例2】
Input: s = "rat", t = "car"
Output: false
【思路】
- 定义一个长度为 26 的数组 record,初始化为 0 ;
- 遍历字符串 s ,对数组下标为 s[i]-'a' 的元素做 +1 操作,统计出 s 中字符出现的次数;
- 遍历字符串 t ,对 t 中出现的字符映射到哈希表索引上的数值做 -1 操作;
- 如果数组 record 中所有元素都为 0 ,返回 true ,否则返回 false 。
【Go代码】
func isAnagram(s string, t string) bool {
record := [26]int{}
for _, v := range s {
record[v - rune('a')]++
}
for _, v := range t {
record[v - rune('a')]--
}
return record == [26]int{}
}
【性能分析】
时间复杂度:O( len(s)+len(t) )
空间复杂度:O(1)
2. 两个数组的交集(349)
【题目描述】
计算两个数组的交集,交集里的元素都是唯一的。
【示例1】
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]
【示例2】
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [9,4]
Explanation: [4,9] is also accepted.
【思路】
遍历数组 nums1 ,用 map 记录 nums1 中出现的元素(不用记录同一个元素出现的次数);遍历数组 nums2 ,若 map 存在当前元素,则该元素属于两个数组的交集。
【Go代码】
func intersection(nums1 []int, nums2 []int) []int {
m := make(map[int]int)
for _, v := range nums1 {
m[v] = 1
}
var res []int
for _, v := range nums2 {
if count, ok := m[v]; ok && count > 0 {
res = append(res, v)
m[v]--
}
}
return res
}
【性能分析】
时间复杂度:O( len(nums1)+len(nums2) )
空间复杂度:O( max{len(nums1), len(nums2)} )
3. 两数之和(1)
【题目描述】
在数组中找到两个元素,这两个元素的数值之和等于所给目标值,返回这两个元素的下标。
【示例1】
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
【示例2】
Input: nums = [3,2,4], target = 6
Output: [1,2]
【思路】
使用 map ,key 保存数值,value 保存数值所在的下标。
【Go代码】
func twoSum(nums []int, target int) []int {
m := make(map[int]int)
for index, val := range nums {
if preIndex, ok := m[target - val]; ok {
return []int{preIndex, index}
} else {
m[val] = index
}
}
return []int{}
}
【性能分析】
时间复杂度:O(n)
空间复杂度:O(n)
4. 四数相加II(454)
【题目描述】
给出四个长度相同的数组,找出有几种元组可以使 A[i]+B[j]+C[k]+D[l]=0 。
【示例】
Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
Output: 2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
【思路】
- 定义一个 map ,key 为 a 和 b 两数之和,value 为 a 和 b 两数之和出现的次数;
- 遍历 A, B 数组,统计两个数组的元素之和及出现的次数,并放到 map 中;
- 定义 int 型变量 count ,用来统计 a+b+c+d=0 出现的次数;
- 在遍历 C, D 数组时,如果 0-(c+d) 在 map 中出现,就使用 count 统计 map 中 key 对应的 value ,即两数之和出现的次数;
- 返回统计值 count 。
【Go代码】
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
m := make(map[int]int)
for _, a := range nums1 {
for _, b := range nums2 {
m[a + b]++
}
}
count := 0
for _, c := range nums3 {
for _, d := range nums4 {
if v, ok := m[0 - (c + d)]; ok {
count += v
}
}
}
return count
}
【性能分析】
时间复杂度:O()
空间复杂度:O()
5. 三数之和(15)
【题目描述】
在一个数组中,找到和为 0 的三个元素组成一个三元组,用数组返回符合条件的所有三元组(三元组不可重复)。注意 [0, 0, 0, 0] 这组数据。
【示例】
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
【思路】
双值针法:
首先将数组排序,然后有一层 for 循环,i 从下标 0 开始,同时将一个下标 left 定义在 i+1 的位置上,将下标 right 定义在数组结尾的位置上。在数组中找到 a, b, c ,使得 a+b+c=0 ,这里 a=nums[i], b=nums[left], c=nums[right] 。接下来移动 left 和 right :
- 如果 nums[i]+nums[left]+nums[right]<0 ,说明此时三数之和小了,因为数组已排好序,所以 left 应该向右移动,这样才能让三数之和大一些;
- 如果 nums[i]+nums[left]+nums[right]>0 ,说明此时三数之和大了,right 向左移动,才能让三数之和小一些,直到 left 与 right 相遇为止。
【Go代码】
func threeSum(nums []int) [][]int {
sort.Ints(nums)
res:=[][]int{}
// 找出 a+b+c=0
// a=nums[i], b=nums[left], c=nums[right]
for i := 0; i < len(nums) - 2; i++ {
if nums[i] > 0 { // 排序之后如果a已经大于0,那么之后就不再有符合条件的三元组了
return res
}
if i > 0 && nums[i] == nums[i - 1] { // a去重
continue
}
left := i + 1
right := len(nums) - 1
for left < right {
if nums[i] + nums[left] + nums[right] < 0 {
left++
} else if nums[i] + nums[left] + nums[right] > 0 {
right--
} else {
res = append(res, []int{nums[i], nums[left], nums[right]})
for left < right && nums[left] == nums[left + 1] { // b去重
left++
}
for left < right && nums[right] == nums[right - 1] { // c去重
right--
}
// 找到答案时,双指针同时收缩
left++
right--
}
}
}
return res
}
【性能分析】
时间复杂度:O()
空间复杂度:O(1)
6. 四数之和(18)
【题目描述】
在一个数组中,找到和为 target 的四个元素组成一个四元组,用数组返回符合条件的所有四元组(四元组不可重复)。
【示例】
Input: nums = [1,0,-1,0,-2,2], target = 0
Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
【思路】
与上一题同个思路,都是使用双指针法,基本解法就是在三数之和解法的基础上再套一层 for 循环。
但有一些细节需要注意,例如,在剪枝的时候不需要判断 nums[i]>target 就返回,在上一题中,当 nums[i]>0 时就返回,因为 0 已经是确定的数了,四数之和这道题的 target 是任意值。
三数之和的双指针解法是一层 for 循环,得到的 nums[i] 为确定值,然后循环内有 left 和 right 下标作为双指针,找到 nums[i]+nums[left]+nums[right]==0 。
四数之和的双指针解法是两层 for 循环遍历,得到的 nums[i]+nums[j] 为确定值,依然是循环内有 left 和 right 下标作为双指针,找出 nums[i]+nums[j]+nums[left]+nums[right]==target 的情况,三数之和的时间复杂度是 O() ,四数之和的时间复杂度是 O(
) 。
同理,五数之和、六数之和等都采用这种解法。
对于三数之和,双值针法就是将原本时间复杂度为 O() 的解法降为时间复杂度为 O(
) 的解法,四数之和的双值针法就是将原本时间复杂度为 O(
) 的解法降为时间复杂度为 O(
) 的解法。
【Go代码】
func fourSum(nums []int, target int) [][]int {
sort.Ints(nums)
res := [][]int{}
for i := 0; i < len(nums) - 3; i++ {
if i > 0 && nums[i] == nums[i - 1] {
continue
}
for j := i + 1; j < len(nums) - 2; j++ {
if j > i + 1 && nums[j] == nums[j - 1] {
continue
}
left, right := j + 1, len(nums) - 1
for left < right {
if nums[i] + nums[j] + nums[left] + nums[right] < target {
left++
} else if nums[i] + nums[j] + nums[left] + nums[right] > target {
right--
} else {
res = append(res, []int{nums[i], nums[j], nums[left], nums[right]})
for left < right && nums[left] == nums[left + 1] {
left++
}
for left < right && nums[right] == nums[right - 1] {
right--
}
left++
right--
}
}
}
}
return res
}
【性能分析】
时间复杂度:O()
空间复杂度:O(1)