Leetcode编程训练3 查找1

小结

这次共有10道题,主要是学习map、set和dict等数据结构,以及二分查找。
查找的问题的关键是找对数据结构、准确的二分查找套路条件还有诸如持续输出个位数的小技巧。

学习目的

  • 完成查找相关经典题:35. 搜索插入位置202. 快乐数205. 同构字符串242. 有效的字母异位词290. 单词规律349. 两个数组的交集350. 两个数组的交集 II 410. 分割数组的最大值451. 根据字符出现频率排序540. 有序数组中的单一元素;
  • 学习灵活应用map、set和dict数据结构。
  • 活用二分法的套路解题。

数据结构总结

  1. map(func, struct):作用是对结构struct按顺序以func映射。
  2. set():无序但不重复的数据结构。
  3. dict():无序但计数,按键值对结构存储。

map的经典题

205 同构字符串

在这里插入图片描述
这道题不关注字母的一致性,偏重于重叠字母的位置要一致。
如何验证各位置是否是重叠字母,只需要找到他们在各自字库的索引(lista.index(str)即可得到str在列表lista中的索引)即可,词各字母的首次出现位置由map可快速映射得到。对比两个词的索引相对位置,则用list(map())结果比较即可。

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        _s = [i for i in s]
        _t = [i for i in t]
        return list(map(_s.index, _s)) == list(map(_t.index, _t))

290. 单词规律

在这里插入图片描述
跟205 同构字符串一样的思路,如果在各自词库中出现的序号一致的话就算相同模式了,所以选择将拆分后的各个字母/词映射为各自词库的索引,然后比较两个词的索引列表是否一致即可。

class Solution:
    def wordPattern(self, pattern: str, str: str) -> bool:
        pattern = [i for i in pattern]
        str = str.split(' ')
        return list(map(pattern.index, pattern)) == list(map(str.index, str))

set()的经典题

349. 两个数组的交集

在这里插入图片描述
这道题需要的是重复出现的数,不考虑次数,因此选择set数据结构,对比两个数组的去重unique集合值,若同时存在即为交集,也可以直接&求交集。

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return set(nums1) & set(nums2)

dict()的经典题

242. 有效的字母异位词

在这里插入图片描述
这道题不要求字母所在信息,只要相同字母出现相同次数,因此考虑以字典存储(key-value:出现的字母-出现的频次),然后比较两个字典是否一致即可。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        import collections
        obj1 = collections.Counter(s)
        obj2 = collections.Counter(t)
        return obj1 == obj2

350. 两个数组的交集 II

在这里插入图片描述
这道题与349. 两个数组的交集相比,多了一个出现次数的要求,因此考虑以字典存储(key-value:出现的数字-出现的频次),然后输出两个字典均包含的数字,并以较少的那个出现频次输出。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        import collections
        obj1 = collections.Counter(nums1) 
        obj2 = collections.Counter(nums2) 
        res = []
        for i in obj1:
            if i in obj2:
                n = min(obj1[i], obj2[i])
                res.extend([i]*n)
        return res

451. 根据字符出现频率排序I

在这里插入图片描述
这道题要求出现的字母+出现的频次并降序排列,因此考虑以出现的字母-出现的频次为键值对的字典保存其词库信息,然后想办法按出现频次降序排列,依次输出出现频次个字母。
注:这道题的tree输出老是错,不知道怎么办。

class Solution:
    def frequencySort(self, s: str) -> str:
        from collections import Counter
        obj = Counter([_ for _ in s])
        asd = sorted(obj.items(),key=lambda item:item[1],reverse=True)
        return ''.join([i*j for i, j in asd])

二分查找

二分查找的套路如下,关键在1)是否有必要可更改high的初值 2)找到保留右半部分的cond。

low,high = 0, len(nums)-1
while low < high:
	mid = (low + high) // 2
	if cond:  #保留右半部分
		low = mid + 1
	else:
		high = mid
return low

35. 搜索插入位置

在这里插入图片描述

  • 因为待插入数字可以随便出现在0~len(nums)位置,因此需要修改high的初值为len(nums)。
  • 怎么找cond使得保留右半部分?答:当nums[mid]<待插入数字时候,所以待插入数字必然出现在右半部分。
  • 然后,题目就写完了,万能的套路啊!
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        lo, hi = 0, len(nums)
        while hi > lo:
            m = (lo + hi) // 2
            if target > nums[m]:
                lo = m + 1
            else:
                hi = m
        return lo

540. 有序数组中的单一元素

在这里插入图片描述

  • 是否有必要可更改high的初值?因为落单的数字可以随便出现在0~len(nums)-1位置,因此不需要修改high的初值。
  • 怎么找cond使得保留右半部分?
    如果mid为奇数,那么左边0~mid共偶数个数,如果nums[mid-1]与nums[mid]相等,则说明左边都是成对的数,落单的数在右边,要保留右边!
    如果mid为偶数,那么左边0~mid共奇数个数,如果nums[mid]与nums[mid+1]相等,则说明左边与nums[mid+1]成双成对,落单的数在右边,要保留右边!
    另一个方法我想破脑壳也想不到,抑或使得偶数+=1,奇数-=1,于是乎第一种方法的那么长的废话变成了nums[mid] == nums[mid^1],真是简洁,666.
  • 然后,题目就写完了,万能的套路啊!
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
    	'''方法1——mid奇偶性+[mid-1][mid][mid+1]数是否相等'''
        lo, hi = 0, len(nums) - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if ((mid %2 ==0) and (nums[mid] == nums[mid+1])) or ((mid %2 !=0) and (nums[mid-1] == nums[mid])):
                lo = mid + 1
            else:
                hi = mid 
        return nums[lo]
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
    	'''方法2——mid奇偶性+[mid-1][mid][mid+1]数是否相等居然变成了神奇的抑或,666'''
        lo, hi = 0, len(nums) - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] == nums[mid^1]:
                lo = mid + 1
            else:
                hi = mid 
        return nums[lo]

410. 分割数组的最大值

在这里插入图片描述
按值二分?仓促间看不懂,这题延期…
20200826补充。
这道题是按值进行二分:对于给定的数组和最大值,在保证这个最大值的情况下至少要分多少组,如果需要分组数高于输入的m次,说明在分组m次条件下无法实现,需要提高给定的数组和最大值,即选取数组和右侧部分。

  • 是否有必要可更改low,high的初值?因为数组和最大值可以随便出现在 m i n ( n u m s )   s u m ( n u m s ) min(nums)~sum(nums) min(nums) sum(nums)之间,当然我也不知道为什么要用 m a x ( n u m s )   s u m ( n u m s ) max(nums)~sum(nums) max(nums) sum(nums)
  • 怎么找cond使得保留右半部分?分组数结果高于输入时候保留。
  • 套用套路,完美
class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        def helper(mid):
            _sum = 0
            n = 0
            for i in nums:
                if _sum + i <= mid:
                    _sum += i
                else:
                    _sum = i
                    n += 1
            return n+1

        lo, hi = max(nums), sum(nums)       
        while lo < hi:
            mid = (lo + hi) // 2
            if helper(mid) > m:
                lo = mid + 1
            else:
                hi = mid
        return lo

持续输出个位数+while循环练习+击破无限循环

202. 快乐数

在这里插入图片描述
这道题的精髓就是持续输出个位数+while循环练习+击破无限循环。

  • %得最后一位数是指n%10得到个位数,配合着n=n//10就能不停地得到个位数
  • while循环。
  • 击破无限循环的方式就是,是不是曾经出现过?如果出现那循环下去就无休止了,直接返回False。
class Solution:
    def isHappy(self, n: int) -> bool:
        already = set()
        while n != 1:
            res = 0
            while n > 0:
                last = n % 10 
                n = n // 10
                res += last ** 2 
            if res in already:
                return False
            else:
                already.add(res)
            n = res
        return True

参考:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值