leetcode第145场周赛总结

leetcode第145场周赛总结

第一题:5127. 数组的相对排序

给你两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

示例:

输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]

提示:

arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2 中的元素 arr2[i] 各不相同
arr2 中的每个元素 arr2[i] 都出现在 arr1 中

分析:简单题,按题意做即可,不过后续可优化,代码如下:

class Solution:
    def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
        dic = collections.Counter(arr1)
        ans = []
        for i in range(len(arr2)):
            ans.extend([arr2[i]]*dic[arr2[i]])
            dic.pop(arr2[i])
        temp = []
        for key, val in dic.items():
            temp.extend([key]*val)
        temp.sort()
        ans.extend(temp)
        return ans
第二题:5128. 最深叶节点的最近公共祖先

给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。
回想一下:
叶节点 是二叉树中没有子节点的节点
树的根节点的 深度为 0,如果某一节点的深度为 d,那它的子节点的深度就是 d+1
如果我们假定 A 是一组节点 S 的 最近公共祖先,S 中的每个节点都在以 A 为根节点的子树中,且 A 的深度达到此条件下可能的最大值。

示例1:

输入:root = [1,2,3]
输出:[1,2,3]

示例2:

输入:root = [1,2,3,4]
输出:[4]

示例3:

输入:root = [1,2,3,4,5]
输出:[2,4,5]

提示:

给你的树中将有 1 到 1000 个节点。
树中每个节点的值都在 1 到 1000 之间。

分析:既然这题要我们找到所有最深节点的最近公共祖先,言下之意就是我们必须得先完整的遍历完这棵树,找到所有的最深节点,而且要找的是这些所有最深节点的最近公共祖先,注意是最近,也就是说,这个祖先节点也要尽可能的深。这就自然让人联想到深度优先搜索!既能完整遍历完这棵树,又是以深度为优先来搜索的!所以我们对这棵树做深度优先搜索,计算每个节点的左右子树能达到的最大深度,如果左右子树能达到的最大深度都等于目前整棵树的最大深度,那么说明这个节点的左右子树中都包含有最深的节点,但是还不能确定这个节点的左右子树是否包含了所有的最深节点,因此暂时更新我们的答案为当前节点。如果在回溯过程中发现了符合条件的节点,则要继续更新,因为这说明刚才的更深的那个节点并不能覆盖到所有的最深节点。总之,通过回溯,我们能找到覆盖到所有最深节点的最近祖先节点。

class Solution:
    def lcaDeepestLeaves(self, root: TreeNode) -> TreeNode:
        self.deep = 0
        self.ans = None
        self.helper(root, 1)
        return self.ans
        
    def helper(self, root, deep):
        self.deep = max(deep, self.deep)
        l, r = deep, deep
        if root.left:
            l = self.helper(root.left, deep+1)
        if root.right:
            r = self.helper(root.right, deep+1)
        if l == self.deep and r == self.deep:
            self.ans = root
        return max(l, r)
第三题:5129. 表现良好的最长时间段

给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。

示例:

输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。

提示:

1 <= hours.length <= 10000
0 <= hours[i] <= 16

分析:哈哈,这个示例很有意思,是暗示了996才是表现良好吗→_→。这题咋看像是能做,实际我没做出来,代码是借鉴大神的。一开始我是想着用滑动窗口做的,但是发现这个想法不好处理一种情况,那就是前期几天都是不劳累的但是后面几天都是劳累的,这种情况下最长的表现良好的时间段是应该包括部分前面不劳累的天数的,但是用滑动窗口的话是一直往后考虑的,没法回头。所以难点在于如何处理刚才讨论的那种情况,解决办法是既然后面有可能要回头,那就先将它记录下来!将这个想法与滑动窗口结合起来就能解决这个问题了。具体思路是:(1)先处理一下这个列表,将小于等于8的数替换成-1,将大于8的数替换成1,方便后续操作。(2)定义s,用来存储hours从头到当前位置的和,即劳累天数与不劳累天数的差值,再定义两个列表,一个是negative_sum,用来存储小于0的s,一个是index,用来存储相应的下标。(3)遍历hours,求出当前s,即到目前为止,劳累天数与不劳累天数的差值,若s>0,表示到目前为止都是表现良好的天数,更新ans,若s <=0,表示并不是从头开始到目前都是表现良好的,那么考虑之前是否存在过比目前s还要小的s,假设为s1,如果我们把之前的那段s1剪掉,则新的这段的和=s-s1>0,并且我们要找尽量长的,那我们就只要减去尽量短的一段,即刚好令s-s1=1,可以知道如果存在比目前的s还要小的s,那这样的s1是肯定存在的,因为s是以1为单位增加或者减少的。接着,如果s<0并且目前这个s比negative_sum里存的s都要小或者negative_sum为空,则将它与相应的下标都存起来。这样做的目的有两个,一是将这些状态存起来备用,二是之所以往后只存更小的s而遇到已经存过的s不更新是因为我们要找尽量长的区间,所以只需要存最早的那个状态嘛,这样能使区间更长。
代码如下:

class Solution:
    def longestWPI(self, hours: List[int]) -> int:
        for i in range(len(hours)):
            hours[i] = -1 if hours[i] <= 8 else 1
        ans = 0
        s = 0
        negative_sum = []
        index = []
        for i in range(len(hours)):
            s += hours[i]
            if s > 0:
                ans = i + 1
            else:
                p = - s
                if p < len(index):
                    ans = max(ans, i - index[p])
                if s < 0 and (len(negative_sum) == 0 or negative_sum[-1] > s):
                    negative_sum.append(s)
                    index.append(i)
        return ans
第四题:5130. 最小的必要团队

作为项目经理,你规划了一份需求的技能清单 req_skills,并打算从备选人员名单 people 中选出些人组成一个「必要团队」( 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。
所谓「必要团队」,就是在这个团队中,对于所需求的技能列表 req_skills 中列出的每项技能,团队中至少有一名成员已经掌握。
我们可以用每个人的编号来表示团队中的成员:例如,团队 team = [0, 1, 3] 表示掌握技能分别为 people[0],people[1],和 people[3] 的备选人员。
请你返回任一规模最小的必要团队,团队成员用人员编号表示。你可以按任意顺序返回答案,本题保证答案存在。

示例1:

输入:req_skills = [“java”,“nodejs”,“reactjs”], people = [[“java”],[“nodejs”],[“nodejs”,“reactjs”]]
输出:[0,2]

示例2:

输入:req_skills = [“algorithms”,“math”,“java”,“reactjs”,“csharp”,“aws”], people = [[“algorithms”,“math”,“java”],[“algorithms”,“math”,“reactjs”],[“java”,“csharp”,“aws”],[“reactjs”,“csharp”],[“csharp”,“math”],[“aws”,“java”]]
输出:[1,2]

提示:

1 <= req_skills.length <= 16
1 <= people.length <= 60
1 <= people[i].length, req_skills[i].length, people[i][j].length <= 16
req_skills 和 people[i] 中的元素分别各不相同
req_skills[i][j], people[i][j][k] 都由小写英文字母组成
本题保证「必要团队」一定存在

分析:借鉴大神的位运算的思路。列表里每一项技能用相应的一位来表示。用字典存储各种技能的组合情况相对应的需要的最少人数的组合情况,即字典的键是技能的组合情况,字典的值是相应的需要的最少的那些人。用两重for循环,外循环遍历每个人,内循环遍历当前的字典,将现在遍历的人与字典里的每一项进行组合,如果产生新的技能组合情况,则存入字典,如果目前组合出的技能组合情况已在字典中,但是目前组合所需人数比字典中存的要更少的话,则更新字典中这种技能组合情况所需的人。循环结束后,其实就求出了所有技能组合情况所需的最少的人的组合。代码如下:

class Solution:
    def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
        dic = {}
        for skill in req_skills:
            dic[skill] = len(dic)
        combination = {0: []}
        for i in range(len(people)):
            p = people[i]
            s = 0
            for skill in p:
                s += (1 << dic[skill])
            for key, val in list(combination.items()):
                new_skill = s | key
                new_people = val + [i]
                if new_skill not in combination or len(combination[new_skill]) > len(new_people):
                    combination[new_skill] = new_people
        return combination[(1 << len(dic)) - 1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值