代码随想录第五天| 哈希表 242 有效的字母异位词 349 两个数组的交集 202 快乐数 1 两数之和

哈希表理论基础

先来学习一下哈希表的基础知识,这部分之前了解不多。
数组本质就是一个哈希表,只不过索引是数字
哈希表用于快速判断一个元素是否出现在集合里,查询复杂度为O(1)
原数据经过hashFunction得到index,再对哈希表size取模,来避免超出size
哈希碰撞:拉链法,线性探测法
三种常用数据结构:数组,set,map

242 有效的字母异位词

题目链接
首先想到了最笨的思路,遍历字符串找到所有字母的重复次数并记录,然后对比两个字符串的记录结果是否相同,可以用字典,不过我不确定字典是否是有序的,先尝试一下
经过在jupyter notebook测试发现字典是无序的,内容相同但顺序不同的字典判断为相同
此思路一次成功通过,但耗时略长,60ms

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        dic1 = self.findDup(s)
        dic2 = self.findDup(t)
        if dic1 == dic2:
            return True
        else:
            return False

    def findDup(self, s: str):
        dic = {}
        for i in s:
            if i not in dic:
                dic[i] = 0
            else:
                dic[i] += 1
        return dic

现在看一下解答,发现我的思路很接近python使用defaultdict函数的情况,不过结束的输出可以直接return dic1 == dic2,而无需判断true,false的
而且原来还有更方便的counter方法!尝试一下这个!耗时64ms
不过不管是defaultdict还是counter,都需要从collections包导入一下
再试下常规思路使用数组构建哈希表

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0]*26
        record2 = [0]*26
        for i in s:
            record[ord(i) - ord('a')] += 1
        for i in t:
            record[ord(i) - ord('a')] -= 1
        return record == record2

要记住ord函数用于取字符的ASCII码

349 两个数组的交集

题目链接
看到此题目,暴力解法就是逐个判断某元素是否在另一个数组里,然后去重

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        ans = set()
        for i in nums1:
            if i in nums2:
                ans.add(i)
        ans2 = list(ans)
        return ans2

其中还是有个地方不清楚,就是如何把集合转为列表,用到了list函数,此思路通过
再思考一下有没有其他途径,有个笨方法是根据元素小于等于1000创建一个1000的数组,然后遍历有这个数字就在相应下标下+1,以空间换时间,试一下

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        record = [0]*1000
        record2 = [0]*1000
        ans = []
        for i in nums1:
            record[i] += 1
        for i in nums2:
            if record[i] > 0:
                if i not in ans:
                    ans.append(i)
        return ans

耗时减少了一点点,但内存空间居然没怎么变化,有点离谱,看看解答
两种方法和python的前两个解答类似,不过第三种方法挺巧妙的,利用&取交集的方法一行代码就实现了功能,不过测试了一下耗时比第二种方法长一点

202 快乐数

数字链接
这题有点复杂,乍一看没啥思路,应该需要先把数字的各位拆出来,然后按照要求不断计算,直到遇到循环或结果为1跳出
首先思考如何取出各位数字,可以除以10的最高位次幂,得到最高位数字,然后减去,再循环得到结果,试一下
实现此功能后思考按要求计算,最先想到的是递归调用原函数直到结果为1,但这样无法处理循环的情况,需要单独加一个判断
不对,递归无法判断循环,只能在一个函数中用while

class Solution:
    def isHappy(self, n: int) -> bool:
        s = n
        record2 = []
        while 1:
            num = 1
            while num <= s:
                num *= 10
            num /= 10 #找到了最高位
            record = [] #把各位数字存到列表里
            while num != 0.1:
                a = int(s/num) #取出最高位数字
                record.append(a)
                s -= a*num
                num /= 10
            ans = 0
            for i in record:
                ans += i**2
            if ans == 1:
                return True
            elif ans in record2:
                return False 
            else:
                record2.append(ans)
                s = ans

判断逻辑是若结果为1则返回true,若结果在之前的记录中则证明出现了循环返回false,否则加入记录
看一下解答,python有很多种写法。。
首先学习python中单位操作的函数divmod(a,b),返回结果是a/b的商和余数,只需要b取10,再把商赋值给原数字,即可不断从小到大取得单位数字,比我自己写的从大到小取单位数字要快捷许多
还可以使用字符串!直接把数字转为字符串,然后就可以看作数组逐字符访问了!
判断是否重复可以用集合、数组、快慢指针相遇等多种方法!

1 两数之和

题目链接
此题比较直接的思路就是逐个访问任意两个元素的和,直到找到结果并返回
先对数组排序应该会提高效率,至少可以排除超过target的元素,然后把剩下的元素从两边取值来找到结果!但是要记得最后得返回index,还得再找一遍。。
排序函数不熟练啊,还得临时查,list.sort()无返回值,直接排序原列表,sorted(list),操作一个列表,返回一个排序后的新列表

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        nums2 = sorted(nums)
        index1 = 0
        index2 = len(nums2) - 1
        ans = []
        while 1:
            sum = nums2[index1] + nums2[index2]
            if sum == target:
                ans.append(nums2[index1])
                ans.append(nums2[index2])
                break
            elif sum > target:
                index2 -= 1
            elif sum < target:
                index1 += 1
        if ans[0] != ans[1]:
            return [nums.index(ans[0]), nums.index(ans[1])]
        else:
            ans2= [nums.index(ans[0])]
            temp = nums
            temp[nums.index(ans[0])] += 1
            ans2.append(nums.index(ans[1]))
            return ans2

提交错了一次,遇到两个不同位置的相同元素时操作有误,不能把重复元素删掉,因为会改变index,而应该改变它的值
看一下解答,解答的思路比我还要更快一些,直接在遍历每个数字时寻找剩下的元素中有没有与其相加等于target的,再试着写一下这个思路:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            temp = [j for j in nums]
            temp.remove(nums[i])
            nums2 = set(temp)
            if target - nums[i] in nums2:
                if target - nums[i] != nums[i]:
                    return [i, nums.index(target - nums[i])]
                else:
                    temp2 = [j for j in nums]
                    temp2[i] += 1
                    return [i, temp2.index(target - nums[i])]

遇到了深拷贝浅拷贝的问题! 列表名直接赋值给另一个变量只是浅拷贝,修改两个变量会导致一个列表改变!被迫重新创建了两次列表,这样导致此代码运行时间很差,甚至和暴力法差不多,看一下解答代码
解答代码如何处理重复元素问题:判断出现重复元素后不改变元素,而使用切片访问不包含该元素的列表,重新获得index后再加上切片少的部分
还是字典方法比较好用,enumerate函数很方便

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        seen = set()
        for index, value in enumerate(nums):
            if target - value in seen:
                return [index, nums.index(target-value)]
            else:
                seen.add(value)

甚至此代码无需考虑重复元素的问题,因为检查的都是之前见过的元素,重复元素只会在第二次遇到时发现,此时只需要返回当前的index和list.index()发现的即可

总结

熟悉了以下几种python方法:

  • defaultdict() 创建一个特殊的字典,key不存在则返回一个默认值,参数为list,str,int等
  • Counter() 函数,统计可迭代对象中各元素重复的次数,需要from collections import Counter,返回一个Counter对象,可以直接dict构建字典,也可以for k,v in Counter 来遍历访问
  • ord() 用于取字符的ASCII码
  • set创建的集合可以用&取交集,用|取并集,用-取差集运算
  • divmod(a,b) 函数返回结果是a/b的商和余数,只需要b取10,再把商赋值给原数字,即可不断从小到大取得单位数字,也即 n, v = divmod(n, 10), v即为从小到大取出来的单位数字
  • str(int) 可以直接把数字转换为字符串,如123转为’123’,如此便可以直接以列表形式访问单位数字
  • list.sort() 直接对原list排序,返回值为空,类似的还有remove()函数,要注意这类无返回值的函数
  • sorted(list)会返回一个新创建的列表
  • 深浅拷贝问题!!a = [1,2,3],若赋值b=a,则a,b指向的是同一个列表!改变b会把a改变,这是浅拷贝,如果b = [i for i in a],则可以深拷贝一层,也即列表的第一维深拷贝了,但是二维列表的大于等于第二维仍然浅拷贝,因为实际存的是一个列表地址(b = a.copy()也一样的)。真正深拷贝只有import copy的copy.deepcopy()函数
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值