leetcode刷題-- 4. 貪心


貪心

455分發餅干

假設你是一位很棒的家長,想要給你的孩子們一些小餅干。但是,每個孩子最多只能給一塊餅干。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅干的最小尺寸;並且每塊餅干 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅干 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是盡可能滿足越多數量的孩子,並輸出這個最大數值。

注意:

你可以假設胃口值為正。
一個小朋友最多只能擁有一塊餅干。

示例 1:

輸入: [1,2,3], [1,1]

輸出: 1

解釋:

你有三個孩子和兩塊小餅干,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅干,由於他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。

示例 2:

輸入: [1,2], [1,2,3]

輸出: 2

解釋:

你有兩個孩子和三塊小餅干,2個孩子的胃口值分別是1,2。
你擁有的餅干數量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2.

題解

為了滿足最多的孩子,應該盡量讓胃口小的吃小餅干, 把胃口小的先滿足。將孩子胃口,和餅干都升序排列,這里還用了兩個指針。

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        i,j = 0,0
        count = 0
        while True:
            if i>=len(g) or j>=len(s):
                break
            if g[i]<=s[j]:
                count += 1
                i += 1
            j += 1
        return count

435無重疊區間

給定一個區間的集合,找到需要移除區間的最小數量,使剩余區間互不重疊。

注意:

可以認為區間的終點總是大於它的起點。
區間 [1,2] 和 [2,3] 的邊界相互“接觸”,但沒有相互重疊。

示例 1:

輸入: [ [1,2], [2,3], [3,4], [1,3] ]

輸出: 1

解釋: 移除 [1,3] 后,剩下的區間沒有重疊。

示例 2:

輸入: [ [1,2], [1,2], [1,2] ]

輸出: 2

解釋: 你需要移除兩個 [1,2] 來使剩下的區間沒有重疊。

示例 3:

輸入: [ [1,2], [2,3] ]

輸出: 0

解釋: 你不需要移除任何區間,因為它們已經是無重疊的了。

題解

在每次選擇中,區間的結尾最為重要,選擇的區間結尾越小,盡可能留給后面的區間的空間越大,那么后面能夠選擇的區間個數也就越大。

按區間的結尾進行排序,每次選擇結尾最小,並且和前一個區間不重疊的區間。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        intervals.sort(key=lambda x: x[1]) # 注意這里是按照第二個元素來排序
        count = 0
        end = intervals[0][1]              # 初始化end
        for i in range(1,len(intervals)):
            if intervals[i][0] < end:
                count += 1
            else:
                end = intervals[i][1]      # 更新end
        return count

402移掉K位數字

給定一個以字符串表示的非負整數 num,移除這個數中的 k 位數字,使得剩下的數字最小。

注意:

num 的長度小於 10002 且 ≥ k。
num 不會包含任何前導零。

示例 1 :

輸入: num = "1432219", k = 3
輸出: "1219"

解釋: 移除掉三個數字 4, 3, 和 2 形成一個新的最小的數字 1219。

示例 2 :

輸入: num = "10200", k = 1
輸出: "200"

解釋: 移掉首位的 1 剩下的數字為 200. 注意輸出不能有任何前導零。

示例 3 :

輸入: num = "10", k = 2
輸出: "0"

解釋: 從原數字移除所有的數字,剩余為空就是0。

這道題主要思路:當指針指向的數比左邊的數小,那么舍棄左邊的數,在刪除數字時應該從左向右迭代,這樣的數整體最小。這里用到棧的思想,也就是一個列表。

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        numStack = []
        
        for i in num:
            while k and numStack and numStack[-1]>i: #這里循環是為了將i與棧內的所有數進行比較,保證整體最小。
                numStack.pop()
                k -= 1
            numStack.append(i)
        
        numStack = numStack[:-k] if k else numStack
        return ''.join(numStack).lstrip('0') or '0'   

452用最少數量的箭引爆氣球

在二維空間中有許多球形的氣球。對於每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束坐標。由於它是水平的,所以y坐標並不重要,因此只要知道開始和結束的x坐標就足夠了。開始坐標總是小於結束坐標。平面內最多存在104個氣球。

一支弓箭可以沿着x軸從不同點完全垂直地射出。在坐標x處射出一支箭,若有一個氣球的直徑的開始和結束坐標為 xstart,xend, 且滿足  xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之后,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。

Example:

輸入:
[[10,16], [2,8], [1,6], [7,12]]

輸出:
2

解釋:
對於該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11(射爆另外兩個氣球)。

這道題也是計算區間重疊的個數,和上面那道差不多。按每個區間end升序排列。

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        if len(points)==0:
            return 0

        points.sort(key=lambda x:x[1])
        end = points[0][1]
        count = 1

        for i in range(1,len(points)):
            if points[i][0]<=end:
                continue
            else:
                end = points[i][1]
                count+=1
        return count

406. 根據身高重建隊列

假設有打亂順序的一群人站成一個隊列。 每個人由一個整數對(h, k)表示,其中h是這個人的身高,k是排在這個人前面且身高大於或等於h的人數。 編寫一個算法來重建這個隊列。

注意:
總人數少於1100人。

示例

輸入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

輸出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

讓我們從最簡單的情況下思考,當隊列中所有人的 (h,k) 都是相同的高度 h,只有 k 不同時,解決方案很簡單:每個人在隊列的索引 index = k。

即使不是所有人都是同一高度,這個策略也是可行的。因為個子矮的人相對於個子高的人是 “看不見” 的,所以可以先安排個子高的人。


上圖中我們先安排身高為 7 的人,將它放置在與 k 值相等的索引上;再安排身高為 6 的人,同樣的將它放置在與 k 值相等的索引上。

該策略可以遞歸進行:

  • 將最高的人按照 k 值升序排序,然后將它們放置到輸出隊列中與 k 值相等的索引位置上。
  • 按降序取下一個高度,同樣按 k 值對該身高的人升序排序,然后逐個插入到輸出隊列中與 k 值相等的索引位置上。
  • 直到完成為止

在python里直接用list.insert(index, i)在Index處插入。最高的人中,肯定有一個人排他前面人數是0,所以我們這里插入第一個肯定是[最高的身高,0]這樣

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        if len(people)<2:
            return people
        result = []
        people.sort(key=lambda x:(-x[0],x[1]))

        for i in people:
            result.insert(i[1], i)

        return result 

121. 買賣股票的最佳時機

121
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多只允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。

注意你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
     注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。
示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

只要記錄前面的最小價格,將這個最小價格作為買入價格,然后將當前的價格作為售出價格,查看當前收益是不是最大收益。

查看評論后,把它看作DP問題理解起來簡單點。

假設當前在第 i 天,令 minPrice 表示前 i-1 天的最低價格;令 maxProfit 表示前 i-1 天的最大收益。那么考慮第 i 天的收益時,存在兩種情況:

  • 在第 i 天賣出。很顯然,想要獲得最大收益,應該在前 i-1 天中價格最低的時候買入,即此時的收益為:prices[i] - minPrice。(可能會出現負數,但是沒關系)
  • 不在第 i 天賣出。那么第 i 天的最大收益就等於前 i -1 天中的最大收益

狀態轉移方程為:第 i 天最大收益 = max( 在第 i 天賣出的所得收益 , 前 i-1 天的最大收益)

題解

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)==0:
            return 0
        Min = prices[0]
        Max = 0
        for i in range(1,len(prices)):
            temp = prices[i] - Min
            Max = max(Max, temp)
            Min = min(Min, prices[i])
        return Max

貪心做法

只允許一筆交易,在從左往右掃描數組的過程中,保存當前的最小值,結果就是ans = max(ans, price[i]-Min),保證ans時刻保存了當前最大收益。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)==0:
            return 0
        Min, ans = prices[0], 0

        for i in range(len(prices)):
            if Min>=prices[i]:
                Min = prices[i]
                continue
            else:
                ans = max(ans, prices[i] - Min)
        
        return ans

188. 買賣股票的最佳時機 IV(這類題通解)

參考LeetCode

763. 划分字母區間

763

字符串 S 由小寫字母組成。我們要把這個字符串划分為盡可能多的片段,同一個字母只會出現在其中的一個片段。返回一個表示每個字符串片段的長度的列表。

示例 1:

輸入: S = "ababcbacadefegdehijhklij"
輸出: [9,7,8]
解釋:
划分結果為 "ababcbaca", "defegde", "hijhklij"。
每個字母最多出現在一個片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是錯誤的,因為划分的片段數較少。

對於遇到的每一個字母,去找這個字母最后一次出現的位置,用來更新當前的最小區間。參考

定義數組 last[char] 來表示字符 char 最后一次出現的下標。定義 anchor 和 j 來表示當前區間的首尾。如果遇到的字符最后一次出現的位置下標大於 j, 就讓 j=last[c] 來拓展當前的區間。當遍歷到了當前區間的末尾時(即 i==j ),把當前區間加入答案,同時將 start 設為 i+1 去找下一個區間。

class Solution(object):
    def partitionLabels(self, S):
        last = {c: i for i, c in enumerate(S)} # 構建一個字典來保存每個字符最后一次出現位置,很巧妙。
        j = anchor = 0
        ans = []
        for i, c in enumerate(S):
            j = max(j, last[c])
            if i == j:
                ans.append(i - anchor + 1)
                anchor = i + 1
            
        return ans


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM