Task03查找1_while i+1 and n not in seen-程序员宅基地

技术标签: LeetCode刷题  

搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5
输出: 2
示例 2:

输入: [1,3,5,6], 2
输出: 1
示例 3:

输入: [1,3,5,6], 7
输出: 4
示例 4:

输入: [1,3,5,6], 0
输出: 0

解题思路:二分查找
定义左侧下标left,右侧下标right,计算中间下标mid
每次根据nums[mid]和target之间的大小进行判断,相等则直接返回下标,小于则left右移,大于则right左移
查找结束如果没有相等则返回left作为插入位置

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
    
        while left <= right :
            mid = (left + right) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        
        return left

二分查找模板

class Solution:
    def firstBadVersion(self, arr):
        # 第一点
        lo, hi = 0, len(arr)-1
        while lo < hi:
            # 第二点
            mid = (lo+hi) // 2
            # 第三点
            if f(x):
                lo = mid + 1
            else:
                hi = mid
        return lo

快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。

如果 n 是快乐数就返回 True ;不是,则返回 False 。

示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

解题思路:没啥思路呢,感觉比较需要数学思维。
可能需要一个循环求解,那什么时候循环结束呢,想到三种情况:
1.过程转换后一个数变为1,这个数是快乐数,返回True
2.过程转换后又出现相同的数,说明进入了死循环,返回False;至于检查是否有相同的数可以想到使用集合set()
3.一直运行,直到无穷大;这一步比较麻烦,好像没什么方法可以知道这个数最终是快乐数,还是会一直计算下去,查阅题解后发现不用考虑这种情况,理由如下:
我们思考:每一位数的最大数字的下一个数字是多少?比如说,9的下一个数为81(9的平方),99的下一个数位162(9的平方+9的平方),999的下一个数为243(9的平方+9的平方+9的平方),也就是说,对于一个三位数的数字,它不可能大于243,就算是最坏的情况,也是在小于243的所有数字上循环,最终重复到一个数字本身,并不会无限期进行下去,因此可以排除第三种情况。
下面思考转换过程,题目要求将每一位数字平方后相加,也就是数位分离后求平方和,这里可以直接使用divmod(a,b),返回的两个数值分别为a//b,a%b;然后再使用一个哈希集合判断一个数字是否出现过;最后过程转换结束时判断结果是否等于1。

class Solution:
    def isHappy(self, n: int) -> bool:
        #获取下一个数字
        def get_next(n):
            next_num = 0
            while n > 0:
                n, mod = divmod(n, 10)  # 数位分离
                next_num += mod ** 2
            return next_num

        #定义一个哈希集
        seen = set()
        while n != 1 and n not in seen:
            seen.add(n)
            n = get_next(n)
        
        return n == 1

同构字符串

给定两个字符串 s 和 t,判断它们是否是同构的。

如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。

所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。

示例 1:
输入: s = “egg”, t = “add”
输出: true
示例
输入: s = “foo”, t = “bar”
输出: false
示例 3:
输入: s = “paper”, t = “title”
输出: true

解题思路:需要保证两个字符串中,字符重复出现的次数相同,可以使用索引,判断两者索引是否相同
s.index(str)方法,检测字符串s中是否包含子字符串str,返回第一次出现的位置,我们将其作为索引
map()方法,根据函数对指定序列做映射,可以将字符映射为索引,map是通过hash存储的,不能直接进行比较,需要转换为list比较

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        return list(map(s.index, s)) == list(map(t.index,t))

单词规律

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。

这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
示例 2:
输入:pattern = “abba”, str = “dog cat cat fish”
输出: false
示例 3:
输入: pattern = “aaaa”, str = “dog cat cat dog”
输出: false
示例 4:
输入: pattern = “abba”, str = “dog dog dog dog”
输出: false
说明:
你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。

解题思路:和上一题一模一样。需要将单词字符串拆分为字符列表。

class Solution:
    def wordPattern(self, pattern: str, str: str) -> bool:
        str = str.split()
        return list(map(pattern.index, pattern)) == list(map(str.index,str))

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:
你可以假设字符串只包含小写字母。

进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

解题思路:如果只包含26个小写字符,可以开一个数组保存[a-z],每次遍历s和t,遍历s时数组对应字符的位置+1,表示该字符出现了一次;遍历t时数组对应字符的位置-1,最后判断结果数组是否都为0。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False   
        res = [0] * 26

        for i in range(len(s)):
            res[ord(s[i]) - 97] += 1
            res[ord(t[i]) - 97] -= 1

        for i in range(26):
            if res[i] != 0:
                return False
        
        return True

两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。

解题思路:两个set()搞定,如果在两个set()都出现,说明是交集,如果没保存过则保存

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res = []
        for i in set(nums1):
            if i in set(nums2) and i not in res:
                res.append(i)
        
        return res

两个数组的交集||

给定两个数组,编写一个函数来计算它们的交集。

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解题思路:和上一题不一样的是,上一题的答案唯一,而这道题有多个交集。
方法一:哈希表
同一个数字在两个数组中都可能出现多次,可以用哈希表存储每个数字出现的次数。对于一个数字,其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。
首先遍历第一个数组,并在哈希表中记录第一个数组中的每个数字以及对应出现的次数,然后遍历第二个数组,对于第二个数组中的每个数字,如果在哈希表中存在这个数字,则将该数字添加到答案,并减少哈希表中该数字出现的次数。这里第一个数组应为较短的数组。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        #将较短字符遍历后将数字和频率存入哈希表
        if len(nums1) > len(nums2):
            return self.intersect(nums2, nums1)

        m = collections.Counter()   # 计数器
        for num in nums1:
            m[num] += 1
        
        intersection = []
        for num in nums2:
            if (count := m.get(num, 0)) > 0:    #count要么为key是num的value,要么为0
                intersection.append(num)
                m[num] -= 1
                if m[num] == 0:
                    m.pop(num)
        
        return intersection

这里使用了计数器collections.Counter(),以及海象赋值法:=,在循环过程进行赋值。
复杂度分析
时间复杂度:O(m+n),其中 m 和 n 分别是两个数组的长度。需要遍历两个数组并对哈希表进行操作,哈希表操作的时间复杂度是 O(1),因此总时间复杂度与两个数组的长度和呈线性关系。
空间复杂度:O(min(m,n)),其中 m和 n 分别是两个数组的长度。对较短的数组进行哈希表的操作,哈希表的大小不会超过较短的数组的长度。为返回值创建一个数组 intersection,其长度为较短的数组的长度。

方法二:排序,如果两个数组是有序的,则可以便捷地计算两个数组的交集。
首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()

        res = []
        i, j = 0, 0
        while i < len(nums1) and j < len(nums2):
            if nums1[i] == nums2[j]:
                res.append(nums1[i])
                i += 1
                j += 1
            elif nums1[i] < nums2[j]:
                i += 1
            elif nums1[i] > nums2[j]:
                j += 1
            
        return res

复杂度分析
时间复杂度:O(m log m+n log n),其中 m 和 n 分别是两个数组的长度。对两个数组进行排序的时间复杂度是 O(m log m+n log n),遍历两个数组的时间复杂度是 O(m+n),因此总时间复杂度是 O(m log m+n log n)。
空间复杂度:O(min(m,n)),其中 m 和 n 分别是两个数组的长度。为返回值创建一个数组 intersection,其长度为较短的数组的长度。不过在 C++ 中,我们可以直接创建一个 vector,不需要把答案临时存放在一个额外的数组中,所以这种实现的空间复杂度为 O(1)。

结语
如果nums 2的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中。那么就无法高效地对nums 2进行排序,因此推荐使用方法一而不是方法二。在方法一中,nums2 只关系到查询操作,因此每次读取nums 2中的一部分数据,并进行处理即可。

分割数组的最大值

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

注意:
数组长度 n 满足以下条件:

1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

解题思路:贪心+二分查找
根据题意,最大值一定在区间 [max(nums),sum(nums)] 之中。
7,2,5,10,8中max=10,sum=32
在…中找到最大/最小值可以使用二分,需要问题具有二段性
在范围10 11 12 …17 18…32中,可以通过二分查找找到答案18,因此上界为数组的sum,下界为数组的max
二分查找时,通过mid是否满足条件判断是在左区间还是右区间,这就需要一个额外的判断函数
这里使用了贪心的思想,模拟分割的过程,从前往后遍历,如果当前分割子数组的和sum加上当前值超过了用于判断的mid,则把当前值作为开头,新开一个分割子数组,并且将子数组数cnt+1,结束后验证cnt是否不超过n

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        def check(x):
            #total表示当前分割子数组之和,cnt表示已经分割出的子数组数量
            total, cnt = 0, 1
            for num in nums:
                #当total加上当前值超过x,把当前值作为新的分割子数组的开头,再一次分段,目的是要保证当前子数组的和不能超过假设的值x
                if total + num > x:
                    cnt += 1
                    total = num
                else:
                    total += num
            #最后验证cnt是否不超过m
            #如果cnt的值比m大,就表示分组的太多,也就是代表当初假设的太小了,需要在mid与数组元素和之间继续二分。
            #如果cnt的值比m小,就表示结果值不是最优的,因为还可以继续多分些组,找到那个最小值。因此需要在数组元素的最大值与mid之间继续二分。
            return cnt <= m
        #二分查找 上界为所有元素之和,下界为所有元素最大值,因为结果值一定出现在数组中的最大值与数组元素和之间。 
        left = max(nums)
        right = sum(nums)
        while left < right:
            mid = (left + right) // 2
            if check(mid):
                right = mid
            else:
                left = mid + 1
        return left

根据字符出现频率排序

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:
输入:
“tree”
输出:
“eert”
解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
“cccaaa”
输出:
“cccaaa”
解释:
'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
“Aabb”
输出:
“bbAa”
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。

解题思路:collections.Counter()计数器

class Solution:
    def frequencySort(self, s: str) -> str:
        return ''.join(i * j for i,j in collections.Counter(s).most_common())

collections.Counter(s).most_common()返回一个字典,key为字符,value为出现次数,从大到小排列
i为字符,j为该字符出现的次数,使用join()方法将字符拼接为字符串

Counter(计数器):用于追踪值的出现次数
Counter类继承dict类,所以它能使用dict类里面的方法
创建一个Counter类

import collections
obj = collections.Counter('aabbccc')
print(obj)

#输出:Counter({'c': 3, 'a': 2, 'b': 2})

elements()获取元素

import collections
obj = collections.Counter('aabbccc')
print(sorted(obj.elements()))
#输出:['a', 'a', 'b', 'b', 'c', 'c', 'c']

most_common(指定一个参数n,按出现次数从大到小列出前n个元素,不指定参数,则列出所有)

import collections
obj = collections.Counter('aabbbcccc')
print(obj.most_common(2))
#输出:[('c', 4), ('b', 3)]

有序数组中的单一元素

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例 1:

输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:

输入: [3,3,7,7,10,11,11]
输出: 10

解题思路:二分查找
由于每个元素会出现两次,只有一个数字出现一次,那么该单次数字之前要么没数字,要么是2n个数字,由于索引从0开始,所以单次数字的索引要么为0要么为2n,因此我们只需检索偶数索引即可
检查mid时,我们判断mid与其之后的数字是否相同
如果相同,由于mid是偶数索引,前面有偶数个数字(索引从0开始),所以mid之前不可能有单次数字;所以我们跳过mid和mid之后那个数字,将mid+2置为下界,left=mid+2
如果不同,说明单次数字可能是mid或者在mid之前的数字,我们将mid置为上界,rigth=mid
最后只能检测一个数字,即left=right时,该数即为单次数字

class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        while left < right:
            mid = (left + right) // 2
            #只检查偶数索引
            if mid % 2 == 1:
                mid -= 1
            if nums[mid] == nums[mid+1]:
                left = mid + 2
            else:
                right = mid
        return nums[left]
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33253721/article/details/108209498

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签