LeetCodeCampsDay6哈希表part01
LeetCodeCampsDay6哈希表part01
以及几题目主要利用hash table的“唯一性”思想解决题目
关键词:哈希表;快慢指针;双指针;用set/dict/当成hash表;
242. 有效的字母异位词
https://leetcode.cn/problems/valid-anagram/
给定两个字符串
s和t,编写一个函数来判断t是否是s的 字母异位词。
示例 1:
1  | 输入: s = "anagram", t = "nagaram"  | 
示例 2:
1  | 输入: s = "rat", t = "car"  | 
提示:
1 <= s.length, t.length <= 5 * 104s和t仅包含小写字母
进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
思路
- 使用hash table可解决
 - 开辟一块26个int型的数组即可,比如对于字符串s,"a"出现一次,则’a’ - ‘a’的下标(应该是0)对应的数据加一;而对于字符串t,就将反着“检查”,比如’n’出现一次,就要将’n’ - 'a’的下标对应的数据减一;
 - 最后,判断是否有「非零」数,若存在则s与t不是字母异位词
 - 可以通过判断s与t长度提前判断;
 
如动画所示
代码
ord(.)用于获取字符的ascii码
- 时间复杂度O(n)
 - 空间复杂度O(1),指仅用了26个int型(因为length <= 5*10^4)
 
1  | class Solution:  | 
代码改进,可以使用zip减少一次遍历次数
1  | class Solution:  | 
349. 两个数组的交集
https://leetcode.cn/problems/intersection-of-two-arrays/
给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
1  | 输入:nums1 = [1,2,2,1], nums2 = [2,2]  | 
示例 2:
1  | 输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]  | 
提示:
1 <= nums1.length, nums2.length <= 10000 <= nums1[i], nums2[i] <= 1000
思路
- 
如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费,此时就要使用另一种结构体set
 - 
先将nums1转成unordered_set,再将nums2与这unordered_set比较,得到二次筛选后的unordered_set.
 - 
在python时使用dict字典即可,将nums1的数据添加到字典了(如果已经存在就不用添加了,也是变相地一种set)
 
如动画所示
代码
python提供了集合set函数,可以直接使用
1  | class Solution:  | 
手动实现,使用字典dict和列表list实现
1  | class Solution:  | 
当然也可以使用数组(list)实现,这题目限制范围是0<=num<=1000数据,可以开1001个位置的数据并按242. 有效的字母异位词的思路赋值再查询
1  | dictSet = [0] * 1001  | 
Pytorch版本
1  | import torch  | 
202. 快乐数
https://leetcode.cn/problems/happy-number/
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
 - 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
 - 如果这个过程 结果为 1,那么这个数就是快乐数。
 
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
1  | 输入:n = 19  | 
示例 2:
1  | 输入:n = 2  | 
提示:
1 <= n <= 231 - 1
思路一
- 题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!
 - 可以使用Hash Table,如果sum在表中出现了则说明有循环,可以直接退出;否则就一直找,直到sum=1
 
代码
1  | class Solution:  | 
相似的思路,如果不在list中就添加进来,一直计算sum直到等于1停止
1  | class Solution:  | 
思路二快慢指针
- 这题目可以用快慢指针解决,如果出现了"循环"(sum会重复出现),其实就是“链表有环”的问题
 - 慢指针每次走一步;快指针每次走两步。即慢指针每次计算sumN(slow),快指针每次计算sumN(sumN(fast))
 - 如果slow == fast则说明有环(有重复的sum)返回False
 - while跳出条件是sumN(fast)!=1,这和fast.next不为空即可[在有环链表题目中,while fast and fast.next则执行]
 
代码
1  | class Solution:  | 
1. 两数之和
https://leetcode.cn/problems/two-sum/
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:
1  | 输入:nums = [2,7,11,15], target = 9  | 
示例 2:
1  | 输入:nums = [3,2,4], target = 6  | 
示例 3:
1  | 输入:nums = [3,3], target = 6  | 
提示:
2 <= nums.length <= 104-109 <= nums[i] <= 109-109 <= target <= 109- 只会存在一个有效答案
 
**进阶:**你可以想出一个时间复杂度小于 O(n2) 的算法吗?
思路
- 使用hash map, 本题目可以使用dict,因为它已经假设每种输入只有一个答案
 - 记录table[target - nums[i_1]] = i_1,如果target - nums[i_1] == nums[i_2]则说明target == nums[i_1] + nums[i_2]
 - 如果nums[i_2]在table中,则找到了一个匹配的返回i_1和i_2
 
代码
如果使用双重循环的方式,时间复杂度O(N^2),但空间复杂度可以到O(1);所以本质还是空间换时间
- 时间复杂度O(N)
 - 空间复杂度O(N)
 
1  | class Solution:  | 
思路二双指针
- 可以使用双指针做,但前提是:数组是有序的(从小到大)
 - left指针从左向右遍历;right指针从右向左遍历
 - 每次计算left和right所指数字的和;并与target判断大小;如果current_sum大了,则right向左移动;小了则left向右移动;如果相等,有点儿的麻烦的是,需要将left和right在原nums的下标进行还原;
 - 初次的还原使用nums.index(nums_sorted[left]),但会出现left_index == right_index的情况,因为题目规定不能使用同一个下标,所以需要将right_index进行调整,调整到nums[left_index+1:].index(nums_sorted[right]) + left_index + 1,也就是强行在nums[left_index+1:]里找right数字对应的下标(返回的是nums[left_index+1:]的下标值,即
相对下标)所以需要再加上left_index+1成为绝对下标 
代码
- 时间复杂度O(N)
 - 空间复杂度O(N)—因为占用了个空间存放排序后的数据
 
1  | # method2 双指针  | 










