《算法通关之路》-chapter16一些有趣的题目

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

多数元素 II

力扣第229题
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

'''
方法一:摩尔投票法

时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def majorityElement(self, nums: list[int]) -> list[int]:
        n = len(nums)
        res = []
        cnt1 = 0
        cnt2 = 0
        n1 = None
        n2 = None

        # 筛选出出现次数最多的前两个元素
        for num in nums:
            if num == n1:
                cnt1 += 1
            elif num == n2:
                cnt2 += 1
            elif cnt1 == 0:
                n1 = num
                cnt1 += 1
            elif cnt2 == 0:
                n2 = num
                cnt2 += 1
            else:
                cnt1 -= 1
                cnt2 -= 1
        
        # 筛选出出现次数超过1/3的元素
        cnt1 = 0
        cnt2 = 0
        for num in nums:
            if num == n1:
                cnt1 += 1
            if num == n2:
                cnt2 += 1
        if cnt1 > n // 3:
            res += [n1]
        if cnt2 > n // 3:
            res += [n2]
        return res

nums = [3,2,3]
solu = Solution()
solu.majorityElement(nums)
[3]

柱状图中最大的矩形

力扣第84题
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

'''
方法一:双层循环(超时)

时间复杂度:O(n2)
空间复杂度:O(1)
'''

class Solution:
    def largestRectangleArea(self, heights: list[int]) -> int:
        n, ans = len(heights), 0
        if n != 0:
            ans = heights[0]
        for i in range(n):
            height = heights[i]
            for j in range(i, n):
                height = min(height, heights[j]) # 最小高度
                ans = max(ans, (j-i+1)*height) # 最大面积
        return ans

heights = [2,1,5,6,2,3]
solu = Solution()
solu.largestRectangleArea(heights)
10
'''
方法二:中心扩展法(超时)

时间复杂度:O(n2)
空间复杂度:O(1)
'''

class Solution:
    def largestRectangleArea(self, heights: list[int]) -> int:
        n = len(heights)
        ans = 0

        # 计算左边第一个高度小于heights[i]的索引和右边第一个小于heights[i]的索引
        for i in range(0, n):
            j = i - 1
            while j >= 0 and heights[j] >= heights[i]:
                j -= 1
            k = i + 1
            while k < n and heights[k] >= heights[i]:
                k += 1
            
            # 以i为最低点情况形成的最大矩阵面积
            ans = max(ans, heights[i] * (k - j - 1))
        return ans

heights = [2,1,5,6,2,3]
solu = Solution()
solu.largestRectangleArea(heights)
10
'''
方法三:中心扩展法(优化)

时间复杂度:O(n)
空间复杂度:O(n)
'''

class Solution:
    def largestRectangleArea(self, heights: list[int]) -> int:
        n = len(heights)
        l, r, ans = [-1]*n, [n]*n, 0

        for i in range(0, n):
            j = i - 1
            while j >= 0 and heights[j] >= heights[i]:
                j = l[j] # 优化
            l[i] = j

        for i in range(n-2, -1, -1):
            k = i + 1
            while k < n and heights[k] >= heights[i]:
                k = r[k] # 优化
            r[i] = k
        
        for i in range(n):
            ans = max(ans, heights[i] * (r[i] - l[i] - 1))
        return ans

heights = [2,1,5,6,2,3]
solu = Solution()
solu.largestRectangleArea(heights)
10
'''
方法四:单调栈

时间复杂度:O(n)
空间复杂度:O(n)
'''

class Solution:
    def largestRectangleArea(self, heights: list[int]) -> int:
        n, heights, st, ans = len(heights), [0]+heights+[0], [], 0
        for i in range(n+2):
            while st and heights[st[-1]] > heights[i]:
                ans = max(ans, heights[st.pop(-1)]*(i-st[-1]-1))
            st.append(i)
        return ans

heights = [2,1,5,6,2,3]
solu = Solution()
solu.largestRectangleArea(heights)
10

一周中的第几天

力扣第1185题
给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。

'''
时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def dayOfTheWeek(self, day: int, month: int, year: int) -> str:
        months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        leap_months = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        
        # 1971-01-01为基准日期,是周五
        days = [
            'Friday',
            'Saturday',
            'Sunday',
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday'
        ]

        diff = 0
        
        # 处理年
        for i in range(1971, year):
            if i % 400 == 0 or (i % 4 == 0 and i % 100 != 0):
                diff += 1
        diff += (year - 1971) * 365

        # 处理月
        for m in range(month - 1):
            if year % 400 == 0 or (year % 4 == 0 and year % 100 != 0):
                diff += leap_months[m]
            else:
                diff += months[m]
        
        # 处理日
        diff += day - 1
        return days[diff % 7]

day, month, year = 31, 8, 2019
solu = Solution()
solu.dayOfTheWeek(day, month, year)
'Saturday'

水壶问题

力扣第365题
有两个水壶,容量分别为 jug1Capacity 和 jug2Capacity 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 targetCapacity 升。

如果可以得到 targetCapacity 升水,最后请用以上水壶中的一或两个来盛放取得的 targetCapacity 升水。

你可以:

装满任意一个水壶
清空任意一个水壶
从一个水壶向另外一个水壶倒水,直到装满或者倒空

'''
方法一:DFS(超时)
'''

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:

        def dfs(x: int, y: int, z: int, state: set) -> bool:

            a, b = state
            if a == z or b == z or a + b == z:
                return True

            seen.add((a, b))
            states = set()
            states.add((x, b)) # 将x的水盛满
            states.add((a, y)) # 将y的水盛满
            states.add((0, b)) # 将x的水倒空
            states.add((a, 0)) # 将y的水倒空
            states.add((min(x, b + a), 0 if b < x - a else b - (x-a))) # 将x的水全部倒给y
            states.add((0 if a < y - b else a - (y - b), min(b + a, y))) # 将y的水全部倒给x

            res = False
            for state in states:
                if state in seen:
                    res |= False
                else:
                    res |= dfs(x, y, z, state)
            return res

        if x + y < z:
            return False
        state = (0, 0)
        seen = set(state)
        return dfs(x, y, z, state)

x, y, z = 3, 5, 4
solu = Solution()
solu.canMeasureWater(x, y, z)
True
'''
方法二:BFS

时间复杂度:O(xy)
空间复杂度:O(xy)
'''

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        if x + y < z:
            return False
        
        queue = [(0, 0)]
        seen = set((0, 0))

        while len(queue) > 0:
            a, b = queue.pop(0)
            if a == z or b == z or a + b == z:
                return True

            states = set()

            states.add((x, b)) # 将x的水盛满
            states.add((a, y)) # 将y的水盛满
            states.add((0, b)) # 将x的水倒空
            states.add((a, 0)) # 将y的水倒空
            states.add((min(x, b + a), 0 if b < x - a else b - (x-a))) # 将x的水全部倒给y
            states.add((0 if a < y - b else a - (y - b), min(b + a, y))) # 将y的水全部倒给x

            for state in states:
                if state in seen:
                    continue
                queue.append(state)

                seen.add(state)
        
        return False

x, y, z = 3, 5, 4
solu = Solution()
solu.canMeasureWater(x, y, z)
True
'''
方法三:最大公约数

时间复杂度:O(log(max(a, b)))
空间复杂度:O(min(a, b))
'''

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:

        if x + y < z:
            return False
        
        if z == 0:
            return True
        
        if x == 0:
            return y == z
        
        if y == 0:
            return x == z

        def GCD(a: int, b:int) -> int:
            return a if b == 0 else GCD(b, a % b)
        
        return z % GCD(x, y) == 0

x, y, z = 3, 5, 4
solu = Solution()
solu.canMeasureWater(x, y, z)
True

可怜的小猪

力扣第458题
有 buckets 桶液体,其中 正好有一桶 含有毒药,其余装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药,你可以喂一些猪喝,通过观察猪是否会死进行判断。不幸的是,你只有 minutesToTest 分钟时间来确定哪桶液体是有毒的。

喂猪的规则如下:

选择若干活猪进行喂养
可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
小猪喝完水后,必须有 minutesToDie 分钟的冷却时间。在这段时间里,你只能观察,而不允许继续喂猪。
过了 minutesToDie 分钟后,所有喝到毒药的猪都会死去,其他所有猪都会活下来。
重复这一过程,直到时间用完。
给你桶的数目 buckets ,minutesToDie 和 minutesToTest ,返回 在规定时间内判断哪个桶有毒所需的 最小 猪数 。

'''
方法一:搜索法

时间复杂度:O(logn)
空间复杂度:O(1)
'''

class Solution:
    def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int:
        cnt = 0
        while(minutesToTest // minutesToDie + 1) ** cnt < buckets:
            cnt += 1
        return cnt

buckets, minutesToDie, minutesToTest = 1000, 15, 60
solu = Solution()
solu.poorPigs(buckets, minutesToDie, minutesToTest)
5
import math
'''
方法二:n分法

时间复杂度:O(logn)
空间复杂度:O(1)
'''

class Solution:
    def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int:
        states = minutesToTest // minutesToDie + 1
        return math.ceil(math.log(buckets) / math.log(states) - 1e-5)

buckets, minutesToDie, minutesToTest = 1000, 15, 60
solu = Solution()
solu.poorPigs(buckets, minutesToDie, minutesToTest)
5

笔记本-Github

你可能感兴趣的:(#,《算法通关之路》学习笔记,算法,python,leetcode,dfs,数据结构)