leetcode第145场周赛

比赛感想

        这次比赛血崩。。。第一题一直没通过心态爆炸,然后到最后就通过第四题,分数又降了不少。。赛后回顾了一下,第一题是因为没有初始化,提交解答后的测试应该是创建了一个实例,然后测试多个样例。第二题是个树的题目,不是很难,刚开始时没看懂题目,看了函数的返回值才知道要做什么,第三题想了很久都没想出来,看了别人的代码,别人是真的厉害,写的代码真的很巧妙,第四题是比较基础的状压dp。

第一题 数组的相对排序

题目大意

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

解题思路

   因为0 <= arr1[i], arr2[i] <= 1000,对arr1进行排序,重载compare函数即可。

代码如下

class Solution:
    dic_ys = {}
    maxx = 0

    #自定义key
    def customKey(self, x):
        if x in self.dic_ys:
            return self.dic_ys[x]
        else:
            return x + self.maxx
    
    #初始化
    def init(self):
        self.dic_ys = {}
        self.maxx = 0
    
    def relativeSortArray(self, arr1, arr2):
        self.init()
        for i in range(0, len(arr2)):
            self.dic_ys[arr2[i]] = i
        for each in arr1:
            self.maxx = max(self.maxx, each)
        return sorted(arr1, key=self.customKey)

第二题 最深叶节点的最近公共祖先

题目大意

        给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。

解题思路

        先确定树的最大深度是多少,然后再从根节点遍历这棵树,返回左子树中达到最大深度的叶子数量+右子树中达到最大深度的叶子数量,第一个到达树的最大深度的叶子数量总量的节点即为最近公共祖先,详细见代码。

代码如下

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def __init__(self):
        self.depth = 0
        self.ans = None
        self.max_sum = 0
    
    #确定最大深度
    def findDepth(self, root: TreeNode) -> int:
        if root == None:
            return 0
        return max(self.findDepth(root.left), self.findDepth(root.right)) + 1

    #遍历树
    def travelTree(self, root: TreeNode, now_depth: int) -> int:
        if root == None:
            return 0
        
        now_sum = 0
        if now_depth == self.depth:
            now_sum = 1
        else:
            now_sum = self.travelTree(root.left, now_depth + 1) + self.travelTree(root.right, now_depth + 1)
        #如果数量比记录的最深叶节点数量大则更新,遍历完树后的self.max_sum即为总最深叶节点数量
        if now_sum > self.max_sum:
            self.max_sum = now_sum
            self.ans = root
        return now_sum

    #初始化
    def initValue(self):
        self.depth = 0
        self.ans = None
        self.max_sum = 0

    def lcaDeepestLeaves(self, root: TreeNode) -> TreeNode:
        self.initValue()
        self.depth = self.findDepth(root)
        self.travelTree(root, 1)
        return self.ans

第三题 表现良好的最长时间段

题目大意

        给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。

        工作小时数大于 8 小时的时候是「劳累的一天」。

        所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。

        返回「表现良好时间段」的最大长度。

解题思路

       劳累的一天为1,不劳累的天数为-1,sum_day[i]即为1~i的劳累天数减去不劳累天数,要求i~j天的劳累天数减去不劳累天数即为sum_day[j]-sum_day[i-1]。现要求「劳累的天数」是严格 大于「不劳累的天数」,即sum_day[j]-sum_day[i-1]>0,即sum_day[j]>sum_day[i-1].确定左端节点为i,现需确定右端节点j,如果遍历,则时间复杂度仍为O(n^2);现在我们是需要越多越好,故生成一个max_day数组表示在1~i天之后的最大值。

        用一张图表示一下

在确定左端节点为i后,然后就可以对j使用二分了,j从i+1到length,如果max_day[j]>sum_day[i-1],则在j至length中找,否则在i+1至j-1中找,具体实现看代码,时间复杂度O(nlogn)。

代码如下

class Solution:
    def longestWPI(self, hours) -> int:
        #sum_day[i]表示劳累的天数大于不劳累天数的数量
        sum_day = [0]
        for i in range(0, len(hours)):
            if hours[i] > 8:
                sum_day.append(1)
            else:
                sum_day.append(-1)
            sum_day[i + 1] += sum_day[i]
        sum_day.append(-99999)
        #max_day[i]表示在1~(i天后(包括第i天))的最大值
        max_day = [-99999 for i in range(0, len(hours) + 2)]
        for i in range(len(hours), -1, -1):
            max_day[i] = max(sum_day[i], max_day[i + 1])

        ans = 0
        for i in range(0, len(hours) + 1):
            #用二分处理满足条件的最大值
            l, r = i + 1, len(hours) + 1
            while l < r:
                mid = (l + r) // 2 + 1
                #天数为mid-i,表示在从第i+1天到mid天的劳累天数大于不劳累天数的数量
                if max_day[mid] > sum_day[i]:
                    l = mid
                else:
                    r = mid - 1
            if max_day[l] > sum_day[i]:
                ans = max(ans, l - i)
        return ans

第四题 最小的必要团队

题目大意

       每个人都有一些技能,团队需要某部分技能,现挑选一些人满足团队的技能需求,要求人数最少。

解题思路

       因为req_skills.length <= 16,所以这个敏感的数字就想到了状态压缩dp,2^16 = 65536。把对应的技能转换成二进制位上的数字。状态转移方程f[j | people_skill[i]] = min(f[j] + 1,f[j | people_skill[i]])表示j的状态上加上第i个人的技能可以从j状态转化过来。

代码如下

class Solution:
    def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
        people_skill = [0 for i in range(len(people))]
        #计算每个人技能对应的二进制值
        for i in range(len(people)):
            for each_skill in people[i]:
                for j in range(len(req_skills)):
                    if each_skill == req_skills[j]:
                        people_skill[i] += 1 << j

        f = [999 for i in range(2**len(req_skills))]
        last = [-1 for i in range(2**len(req_skills))]
        getpeople = [-1 for i in range(2**len(req_skills))]
        f[0] = 0
        for i in range(len(people)):
            for j in range(2**len(req_skills)):
                #状态转移
                if  f[j] + 1 < f[j | people_skill[i]]:
                    f[j | people_skill[i]] = f[j] + 1
                    last[j | people_skill[i]] = j
                    getpeople[j | people_skill[i]] = i
        #求序列
        now = 2**len(req_skills) - 1
        ans = []
        while last[now] != -1:
            ans.append(getpeople[now])
            now = last[now]
        return ans

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值