1.哈希表理论基础
首先什么是 哈希表,哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道都是指hash table就可以了)。
那么哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
2. 有效的字母异位词
// 判断字符串 t 是否是字符串 s 的字母异位词
func isAnagram(s string, t string) bool {
charCount := make(map[rune]int,0)
// 若字符串长度不同,直接返回 false
if len(s) != len(t) {
return false
}
// 将字符串转换为字符数组,并对字符数组进行排序
sChars := []rune(s)
tChars := []rune(t)
for i:=0;i<len(sChars);i++{
if _,ok:=charCount[sChars[i]];ok{
charCount[sChars[i]]++
}else{
charCount[sChars[i]]=1
}
}
for i:=0;i<len(tChars);i++{
char,ok := charCount[tChars[i]]
if !ok || char==0{
return false
}
charCount[tChars[i]]--
}
return true
}
这是官方的一种更简洁的写法:
func isAnagram(s string, t string) bool {
if len(s)!=len(t){
return false
}
s_map:=[26]int{}
t_map:=[26]int{}
for i:=0;i<len(s);i++{
s_map[s[i]-'a']++
t_map[t[i]-'a']++
}
return s_map==t_map
}
2.1 字母异位词分组
2.1.1 第一次的写法,有点慢
func groupAnagrams(strs []string) [][]string {
map_style := make([][26]int, 0)
str_list := make([][]string, 0)
map_index := 0
for _, str := range strs {
strMap := changeStrToMap(str)
index := findCorrectStyle(strMap, map_style, map_index)
if index == -1 {
//表明这是一个新的style
map_style = append(map_style, strMap)
temp := []string{str}
str_list = append(str_list, temp)
map_index++
} else {
str_list[index] = append(str_list[index], str)
}
}
return str_list[0:map_index]
}
func changeStrToMap(s string) [26]int {
s_map := [26]int{}
for i := 0; i < len(s); i++ {
s_map[s[i]-'a']++
}
return s_map
}
func findCorrectStyle(strMap [26]int, map_style [][26]int, map_index int) int {
for i := 0; i < map_index; i++ {
if strMap == map_style[i] {
return i
}
}
return -1
}
2.1.2 学习一下高手的写法
确实快,主体思路没什么问题,就是不知道数组也能当做map的key。
func groupAnagrams(strs []string) [][]string {
// 没想到数组也可以当做key
hmap := make(map[[26]int][]string)
for _, str := range strs {
// key 表示str 的字符最多26个,每个字符有多少个
key := [26]int{}
// 计数
for _, ch := range str {
// idx 代表字符距离小写字母的距离, b 的话是1
idx := ch - 'a'
key[idx] ++
}
hmap[key] = append(hmap[key], str)
}
result := make([][]string, 0, len(hmap))
for _, v := range hmap {
result = append(result, v)
}
return result
}
2.2 找到字符串中所有字母异位词
func findAnagrams(s string, p string) []int {
res := make([]int, 0)
if len(s) < len(p) {
return res
}
// 用来存储p中各个小写字母出现的次数
pattern := [26]int{}
temp := [26]int{}
for i := 0; i < len(p); i++ {
pattern[p[i]-'a']++
temp[s[i]-'a']++
}
if pattern == temp {
res = append(res, 0)
}
for i := 0; i < len(s)-len(p); i++ {
temp[s[i]-'a']--
temp[s[i+len(p)]-'a']++
if pattern == temp {
res = append(res, i+1)
}
}
return res
}
3. 两个数组的交集
func intersection(nums1 []int, nums2 []int) []int {
mp := make(map[int]struct{},0)
res := make([]int,0)
// 遍历nums1
for _,num := range(nums1){
mp[num] = struct{}{}
}
// 遍历nums2
for _,num := range(nums2){
_,ok := mp[num]
if ok{
// 查找到相同元素,加入答案。并从mp中删除
res = append(res,num)
delete(mp,num)
}
}
return res
}
3.1 两个数组的交集②
和上面的代码只有一点点区别,区别在于这道题目需要记录元素出现的个数。
func intersect(nums1 []int, nums2 []int) []int {
mp := make(map[int]int,0)
res := make([]int,0)
// 遍历nums1
for _,num := range(nums1){
_,ok := mp[num]
if !ok{
mp[num]=1
}else{
mp[num]++
}
}
// 遍历nums2
for _,num := range(nums2){
_,ok := mp[num]
if ok{
// 查找到相同元素,加入答案。并从mp中删除
res = append(res,num)
mp[num]--
if mp[num]==0{
delete(mp,num)
}
}
}
return res
}
4.快乐数
func isHappy(n int) bool {
mp := make(map[int]struct{},0)
for n != 1{
_,ok := mp[n]
if ok {
//这个数见过
return false
}
//没见过,就记录一下
mp[n] = struct{}{}
n = getSum(n)
}
return true
}
func getSum(n int)int{
res := 0
for n>0{
res = res + (n%10)*(n%10)
n = n/10
}
return res
}
5. 两数之和
func twoSum(nums []int, target int) []int {
m:=make(map[int]int)
for i,n:= range nums {
j,ok:= m[target-n]
if ok {
return []int{i,j}
}
m[n]=i
}
return []int{}
}
6. 四数相加②
思路:两两组合
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
res := 0
mp := make(map[int]int,len(nums1)*len(nums1))
//循环遍历nums1和nums2两个数组
for i:=0;i<len(nums1);i++{
for j:=0;j<len(nums2);j++{
num := nums1[i] + nums2[j]
if _,ok := mp[num];ok{
mp[num]++
}else{
mp[num]=1
}
}
}
//循环遍历nums3和nums4两个数组
for i:=0;i<len(nums3);i++{
for j:=0;j<len(nums4);j++{
num := nums3[i] + nums4[j]
if _,ok := mp[-num];ok{
res += mp[-num]
}
}
}
return res
}
7.赎金信
func canConstruct(ransomNote string, magazine string) bool {
pattern1 := [26]int{}
for i:=0;i<len(magazine);i++{
pattern1[magazine[i]-'a']++
}
for i:=0;i<len(ransomNote);i++{
pattern1[ransomNote[i]-'a']--
if pattern1[ransomNote[i]-'a']<0{
return false
}
}
return true
}
8.三数之和
方法一:哈希表
这题不太适合使用hash来做
方法二:双指针,需要先排序。感觉不够快
func threeSum(nums []int) [][]int {
var result [][]int
if nums == nil || len(nums) < 3 {
return result
}
slices.Sort(nums)
for i := 0; i < len(nums); i++ {
if nums[i] > 0 {
return result
}
if i > 0 && nums[i] == nums[i-1] {
continue
}
L := i + 1
R := len(nums) - 1
for L < R {
if nums[i]+nums[L]+nums[R] == 0 {
sub := []int{nums[i], nums[L], nums[R]}
result = append(result, sub)
for L < R && nums[L] == nums[L+1] {
L += 1
}
for L < R && nums[R] == nums[R-1] {
R -= 1
}
L += 1
R -= 1
}else if nums[i]+nums[L]+nums[R] > 0 {
R -= 1
} else {
L += 1
}
}
}
return result
}
9.四数之和
9.1 利用map的写法
我的方法无法通过测试用例,会超时
func fourSum(nums []int, target int) [][]int {
res := make(map[[4]int]struct{}, 0)
mp := make(map[int][]int, 0)
for i := 0; i < len(nums); i++ {
for j := i + 1; j < len(nums); j++ {
temp := nums[i] + nums[j]
arr, ok := mp[temp]
if !ok {
slice := make([]int, 2)
slice[0] = i
slice[1] = j
mp[temp] = slice
} else {
//已有
arr = append(arr, i)
arr = append(arr, j)
mp[temp] = arr
}
}
}
for key, arr1 := range mp {
toFind := target - key
arr2, ok := mp[toFind]
if !ok {
continue
}
for i := 0; i < len(arr1)/2; i++ {
for j := 0; j < len(arr2)/2; j++ {
index_a, index_b := arr1[i*2], arr1[i*2+1]
index_c, index_d := arr2[j*2], arr2[j*2+1]
if index_a == index_c || index_a == index_d || index_b == index_c || index_b == index_d {
continue
}
item := []int{nums[index_a], nums[index_b], nums[index_c], nums[index_d]}
sort.Ints(item)
res[[4]int{item[0], item[1], item[2], item[3]}] = struct{}{}
}
}
}
back := [][]int{}
for key, _ := range res {
back = append(back, key[:])
}
return back
}
9.2 使用双指针的解法
两层for循环,套用一个左右指针。可以通过剪枝提高算法的运行速度。
func fourSum(nums []int, target int) [][]int {
if len(nums) < 4 {
return nil
}
sort.Ints(nums)
var res [][]int
for i := 0; i < len(nums)-3; i++ {
n1 := nums[i]
// if n1 > target { // 不能这样写,因为可能是负数
// break
// }
if i > 0 && n1 == nums[i-1] { // 对nums[i]去重
continue
}
for j := i + 1; j < len(nums)-2; j++ {
n2 := nums[j]
if j > i+1 && n2 == nums[j-1] { // 对nums[j]去重
continue
}
l := j + 1
r := len(nums) - 1
for l < r {
n3 := nums[l]
n4 := nums[r]
sum := n1 + n2 + n3 + n4
if sum < target {
l++
} else if sum > target {
r--
} else {
res = append(res, []int{n1, n2, n3, n4})
for l < r && n3 == nums[l+1] { // 去重
l++
}
for l < r && n4 == nums[r-1] { // 去重
r--
}
// 找到答案时,双指针同时靠近
r--
l++
}
}
}
}
return res
}