「アルゴリズムクリアランスへの道」 - 第 16 章のいくつかの興味深いトピック

「アルゴリズムクリアへの道」の学習ノート、問題を磨く自分のプロセスを記録します。詳細な内容については、著者の本を購入してください。

マジョリティ 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]

ヒストグラム内の最大の長方形

Lituo の質問 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

曜日

Lituo の質問 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'

やかんの問題

Leetok 質問 365
それぞれ jug1Capacity リットルと jug2Capacity リットルの容量を持つ 2 つの水差しがあります。水の供給は無制限です。両方のジャグを使用して targetCapacity リットルを正確に取得できるかどうかを決定します。

targetCapacity リットルの水が利用可能な場合は、最後に上記の水差しを 1 つまたは 2 つ使用して、得られた 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

かわいそうな豚

Lituo No. 458
液体の入ったバケツがあり、その 1 つは毒が入っており、残りは水で満たされています。外から見るとどれも同じに見えます。どのバケツに毒が入っているかを調べるには、数頭の豚に水を飲ませて、豚が死ぬかどうかを確認します。残念ながら、どのバケツの液体が有毒であるかを判断するには、テストまでの時間は数分しかありません。

豚に餌を与えるときのルールは次のとおりです。

餌を与えるために複数の生きた豚を選択すると、
子豚は同時に任意の数のバケツから水を飲むことができ、このプロセスには時間がかかりません。
子豚が水を飲んだ後は、数分間冷却する必要があります。この間は観察のみが可能であり、豚に餌を与え続けることはできません。
minutesToDie 分が経過すると、毒を飲んだすべての豚は死に、他のすべての豚は生き残ります。
時間がなくなるまでこのプロセスを繰り返します。
バケットの数、 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

おすすめ

転載: blog.csdn.net/cjw838982809/article/details/132527818