【LeetCode】哈希表

目录

1. 有效的字母异位词(242)

2. 两个数组的交集(349)

3. 两数之和(1)

4. 四数相加II(454)

 5. 三数之和(15)

6. 四数之和(18)


1. 有效的字母异位词(242)

【题目描述】

        判断字符串 s 中的字符是否可以通过改变顺序的方式变成字符串 t(如果字符串 s 与 字符串 t 相同,那么也是可以的)。字符串中只包含小写字母。

【示例1】

Input: s = "anagram", t = "nagaram"
Output: true

【示例2】

Input: s = "rat", t = "car"
Output: false

【思路】

  1. 定义一个长度为 26 的数组 record,初始化为 0 ;
  2. 遍历字符串 s ,对数组下标为 s[i]-'a' 的元素做 +1 操作,统计出 s 中字符出现的次数;
  3. 遍历字符串 t ,对 t 中出现的字符映射到哈希表索引上的数值做 -1 操作;
  4. 如果数组 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

【思路】

  1. 定义一个 map ,key 为 a 和 b 两数之和,value 为 a 和 b 两数之和出现的次数;
  2. 遍历 A, B 数组,统计两个数组的元素之和及出现的次数,并放到 map 中;
  3. 定义 int 型变量 count ,用来统计 a+b+c+d=0 出现的次数;
  4. 在遍历 C, D 数组时,如果 0-(c+d) 在 map 中出现,就使用 count 统计 map 中 key 对应的 value ,即两数之和出现的次数;
  5. 返回统计值 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(n^{2})

        空间复杂度:O(n^{2})

 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(n^{2})

        空间复杂度: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(n^{2}) ,四数之和的时间复杂度是 O(n^{3}) 。

        同理,五数之和、六数之和等都采用这种解法。

        对于三数之和,双值针法就是将原本时间复杂度为 O(n^{3}) 的解法降为时间复杂度为 O(n^{2}) 的解法,四数之和的双值针法就是将原本时间复杂度为 O(n^{4}) 的解法降为时间复杂度为 O(n^{3}) 的解法。 

【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(n^{3}

        空间复杂度:O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值