Leetcode--堆棧、隊列(python)

最小棧

設計一個支持 push,pop,top 操作,並能在常數時間內檢索到最小元素的棧。

push(x) – 將元素 x 推入棧中。
pop() – 刪除棧頂的元素。
top() – 獲取棧頂元素。
getMin() – 檢索棧中的最小元素。
示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

解法:
最簡單的就可以想到使用列表。
但是要實現檢索最小值的功能,可以想到維護一個最小值,就像前綴樹中維護一個word的布爾類型一樣。
注意隨時更新min,可以加快運行速度

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.min = None
        

    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self.stack.append(x)
        if self.min == None or self.min > x:
            self.min = x
        

    def pop(self):
        """
        :rtype: None
        """
        tmp = self.stack.pop()
        if len(self.stack) == 0:
            self.min = None
        
        if self.min == tmp:
            self.min = self.stack[0]
            for x in self.stack:
                if self.min > x:
                    self.min = x

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1]
        

    def getMin(self):
        """
        :rtype: int
        """
        return self.min
        

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

數據流的中位數

中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。

例如,

[2,3,4] 的中位數是 3

[2,3] 的中位數是 (2 + 3) / 2 = 2.5

設計一個支持以下兩種操作的數據結構:

void addNum(int num) - 從數據流中添加一個整數到數據結構中。
double findMedian() - 返回目前所有元素的中位數。
示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
進階:

如果數據流中所有整數都在 0 到 100 範圍內,你將如何優化你的算法?
如果數據流中 99% 的整數都在 0 到 100 範圍內,你將如何優化你的算法?

解法
對於有序數組求中位數很簡單了,但是題目給的是數據流,也就是我們必須做到可以時刻求出中位數的程度。

第一種方法:我們可以給每個從數據流出來的數進行排序,然後數組長度除以2就能得到中位數,但顯然這樣的做法是非常不好的,如果新進來的數是比當前排好序的所有元素都小的數,那麼意味着數組中的所有數都必須向後移動一位,也就是O(n)的複雜度,假設每進來一個數都來一遍O(n),性能還是很低的。

第二種方法:採用優先隊列,也就是大根堆跟小根堆,我們將較小的n/2個數放到大根堆中,將較大的n/2個數放到小根堆中,顯然,如果n是偶數,那麼大根堆的堆頂跟小根堆的堆頂就是我們要找的兩個中位數,將其相加除以2作爲結果返回即可。如果n是奇數,那麼就看大根堆跟小根堆誰的節點個數比另一個堆多一個,節點數量多的那個堆的堆頂就是我們要找的中位數,此時我們直接返回結果即可。

需要注意的是:

1.對於取堆頂元素的操作的時間複雜度是常數級別的。

2.插入新節點時我們需要判斷節點的值是否小於大根堆堆頂的值或者大於小根堆堆頂的值,

如果小於大根堆堆頂的值,那麼節點應該插入大根堆,反過來應該插入小根堆。

3.每次插入新節點我們還需要判斷兩個堆之間的元素個數是否平衡。插入新節點後,我們判斷兩個堆的元素個數,如果相差爲2那麼我們就要對堆進行調整。比如新插入一個節點到小根堆中,而此時大根堆的個數+1小於小根堆的節點個數,這個時候只需要將小根堆的堆頂元素彈出,然後將這個彈出的元素插入大根堆即可。反過來也是一樣的操作。爲什麼可以這樣做呢?這是因爲我們說了小根堆保存的是較大的n/2個數,而小根堆的堆頂是小根堆中最小的元素,同時也是大根堆中最大的元素,因此我們將這個堆頂元素彈出並插入大根堆的操作並不會破壞“小根堆保存較大的n/2個數,大根堆保存較小的n/2”這樣的前提。

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.max_heap = []
        self.min_heap = []

    def addNum(self, num):
        if not self.max_heap:
            heapq.heappush(self.max_heap, -num)  # python的heaqp模塊是小根堆,因此要保存大根堆的話需要加上一個負號
            return

        if num > -self.max_heap[0]:
            heapq.heappush(self.min_heap, num)
            if len(self.max_heap) + 1 < len(self.min_heap):
                heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))  # 插入大根堆需要給值加負號
        else:
            heapq.heappush(self.max_heap, -num)
            if len(self.max_heap) > len(self.min_heap) + 1:
                heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))  # 彈出大根堆堆頂的時候需要加負號變爲正數

    def findMedian(self):
        if len(self.max_heap) > len(self.min_heap):   # 大根堆元素多一個,中位數是大根堆堆頂
            return -self.max_heap[0]
        if len(self.max_heap) < len(self.min_heap):   # 小根堆元素多一個,中位數是小根堆堆頂
            return self.min_heap[0]
        else:
            return (-self.max_heap[0] + self.min_heap[0])/2
        


# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

數組中的第K個最大元素

在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。

示例 1:

輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5
示例 2:

輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4
說明:

你可以假設 k 總是有效的,且 1 ≤ k ≤ 數組的長度。

解法

  • 最簡單的就是給數組排序,求k這個Index位置上的數
  • 使用快速排序思想,就是找第k個pointer。注意是從左到右從大到小排序,使用partition比較簡單些。
  • 遞歸,就是麻煩一點,和解法二差不多
class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        return self.quick(nums, 0, len(nums)-1, k-1)
    
    def quick(self, nums, l, r, k):
        if l == r:
            return nums[l]
        
        p = self.partition(nums, l, r)
        
        if p == k:
            return nums[p]
        elif k < p:
            return self.quick(nums, l, p-1, k)
        else:
            return self.quick(nums, p+1, r, k)
        
    def partition(self, nums, l, r):
        j = l
        for i in range(l+1, r+1):
            if nums[i] > nums[l]:
                j+=1
                nums[i], nums[j] = nums[j], nums[i]
                
        nums[j], nums[l] = nums[l], nums[j]
        
        return j
        
            

解法3:

class Solution:
    def _partition(self, nums, l, r):
        j = l
        for i in range(l + 1, r + 1):
            if nums[i] > nums[l]:
                j += 1
                nums[i], nums[j] = nums[j], nums[i]
        nums[l], nums[j] = nums[j], nums[l]
        return j

    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        l, r, k = 0, len(nums) - 1, k - 1
        while 1:
            p = self._partition(nums, l, r)

            if k == p:
                return nums[p]
            elif k < p:
                r = p - 1
            else:
                l = p + 1

有序矩陣中第K小的元素

給定一個 n x n 矩陣,其中每行和每列元素均按升序排序,找到矩陣中第k小的元素。
請注意,它是排序後的第k小元素,而不是第k個元素。

示例:

matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,

返回 13。
說明:
你可以假設 k 的值永遠是有效的, 1 ≤ k ≤ n2 。

解法:

  • 利用最大堆,建一個大小爲k的最大堆,遍歷矩陣,將矩陣元素一一插入到堆中,堆的大小超過k後要彈出堆頂元素。這樣做時間複雜度爲O(N2logk)。但是這種解法沒有利用到矩陣行和列分別有序的信息。
  • 可以用二分查找法來做,我們由於是有序矩陣,那麼左上角的數字一定是最小的,而右下角的數字一定是最大的,所以這個是我們搜索的範圍。然後我們算出中間數字mid,由於矩陣中不同行之間的元素並不是嚴格有序的,所以我們要在每一行都查找一下mid。
    1.首先設置mid的初值爲矩陣matrix,最後一個數和第一個數的平均值。
    2.統計矩陣中所有行小於mid值的個數之和。若cnt小於8,L = mid + 1,並更新mid;若該值大於8,R = mid。
    3.重複上述搜索,直至L和R不滿足L < R,此時的L或者R值即爲所求。
class Solution(object):
    def kthSmallest(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        a, b = len(matrix), len(matrix[0])
        left = matrix[0][0]
        right = matrix[a-1][b-1]
        
        while left < right:
            mid = left + (right - left) / 2
            cnt = 0
            for i in range(a):
                j = b-1
                while j >=0 and matrix[i][j] > mid:
                    j -= 1
                cnt += j+1
                
            if cnt < k:
                left = mid + 1
            else:
                right = mid
                
        return left

滑動窗口最大值

給定一個數組 nums,有一個大小爲 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。

返回滑動窗口中的最大值。
示例:

輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7] 
解釋: 

  滑動窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

解法
Sliding Window法。
維護window這個數組,使得在任意時刻,window【0】都是當前Window內最大值的下標。
並且window從左到右恰恰是第一大、第二大、。。。。。第K大的元素(也就是C++中的雙端隊列,在隊列中存儲元素在數組中的位置, 並且維持隊列的嚴格遞減)
維護過程爲:

  1. 如果window不爲空,並且 window[0] 比 i - k 小 (這就說明window[0]在當前Window的左界的左側,應該被踢出去), 把window【0】弄出去
  2. 把從隊尾倒着數的每個比item 大的元素都弄出去
  3. 把item 弄進Window
  4. 如果index > = k - 1, 就說明Window size 已經有k了,可以輸出答案了。因爲如果 index < k - 1, 說明Window size還沒到k。
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        if not nums:
            return []
        
        res, window = [], []
        
        for index, item in enumerate(nums):
            if window and window[0] <= index - k:
                window.pop(0)
                
            while window and nums[window[-1]] <= item:
                window.pop()
                
            window.append(index)
            
            if index >= k-1:
                res.append(nums[window[0]])
                
        return res

基本計算器 II

實現一個基本的計算器來計算一個簡單的字符串表達式的值。

字符串表達式僅包含非負整數,+, - ,*,/ 四種運算符和空格 。 整數除法僅保留整數部分。

示例 1:

輸入: “3+2*2”
輸出: 7
示例 2:

輸入: " 3/2 "
輸出: 1
示例 3:

輸入: " 3+5 / 2 "
輸出: 5
說明:

你可以假設所給定的表達式都是有效的。
請不要使用內置的庫函數 eval。

解法
計算器的實現一般是使用壓棧的方式,但此處有幾個注意事項:

  1. 因爲包含加減乘除,一般是對乘法和除法特殊處理(括號也是這樣先處理)壓入棧,然後再全體加起來。
  2. 一般都是使用整數棧,不要再把符號壓入棧了,所以對於減法只需把數字加個負號。
  3. 注意題目中並沒有說是十以內加減法,所以有可能出現多位數,也就是要處理數字,用num作爲當前的數字,直到遇見符號的時候這個數字就結束了。
  4. 由於需要用下一個符號來判斷前一個數字是否結束,所以用sign來代表前一個符號。
  5. 兩個整數直接相除得到的結果是自動向上取整的,比如3/2=2,所以可以先轉換成浮點數再取整。
class Solution(object):
    def calculate(self, s):
        """
        :type s: str
        :rtype: int
        """
        s = s.replace(' ', '')
        res = []
        num = 0
        sign = '+'
        for idx, x in enumerate(s):
            if x.isdigit():
                num = num * 10 + int(x)
            if x.isdigit() == False or idx == len(s) - 1:
                if sign == '+':
                    res.append(num)
                elif sign == '-':
                    res.append(-num)
                elif sign == '*':
                    res.append(res.pop() * num)
                elif sign == '/':
                    res.append(int(float(res.pop())/float(num)))

                num = 0
                sign = x

        out = 0
        for idx, x in enumerate(res):
            out += x

        return out
            
                

逆波蘭表達式求值

根據逆波蘭表示法,求表達式的值。

有效的運算符包括 +, -, *, / 。每個運算對象可以是整數,也可以是另一個逆波蘭表達式。

說明:

整數除法只保留整數部分。
給定逆波蘭表達式總是有效的。換句話說,表達式總會得出有效數值且不存在除數爲 0 的情況。
示例 1:

輸入: [“2”, “1”, “+”, “3”, “*”]
輸出: 9
解釋: ((2 + 1) * 3) = 9
示例 2:

輸入: [“4”, “13”, “5”, “/”, “+”]
輸出: 6
解釋: (4 + (13 / 5)) = 6

示例 3:

輸入: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”, “+”]
輸出: 22
解釋:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

解法
和計算器的方法相同,都是壓棧,這個更簡單,因爲是後綴表達式的形式,而且不需要判斷當前數字是否已經結束,因爲用列表給出了。
但是要注意不能用.isdigit去判斷是否是數字,因爲這隻能判斷0-9的數字,當遇到”-11“這種就錯了。

class Solution(object):
    def evalRPN(self, tokens):
        """
        :type tokens: List[str]
        :rtype: int
        """
        res = []
        for idx, x in enumerate(tokens):
            if x != '+' and x != '-' and x != "*" and x != "/":
                res.append(int(x))
            else:
                b=res.pop()
                a=res.pop()
                tmp = 0
                if x == '+':
                    tmp=a+b
                elif x == '-':
                    tmp = a - b
                elif x == '*':
                    tmp = a * b
                elif x == '/':
                    tmp = int(float(a) / float(b))
                
                res.append(tmp)
                
        return res[-1]

打開轉盤鎖

你有一個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每個撥輪可以自由旋轉:例如把 ‘9’ 變爲 ‘0’,‘0’ 變爲 ‘9’ 。每次旋轉都只能旋轉一個撥輪的一位數字。

鎖的初始數字爲 ‘0000’ ,一個代表四個撥輪的數字的字符串。

列表 deadends 包含了一組死亡數字,一旦撥輪的數字和列表裏的任何一個元素相同,這個鎖將會被永久鎖定,無法再被旋轉。

字符串 target 代表可以解鎖的數字,你需要給出最小的旋轉次數,如果無論如何不能解鎖,返回 -1。

示例 1:

輸入:deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
輸出:6
解釋:
可能的移動序列爲 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。
注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 這樣的序列是不能解鎖的,
因爲當撥動到 “0102” 時這個鎖就會被鎖定。
示例 2:

輸入: deadends = [“8888”], target = “0009”
輸出:1
解釋:
把最後一位反向旋轉一次即可 “0000” -> “0009”。
示例 3:

輸入: deadends = [“8887”,“8889”,“8878”,“8898”,“8788”,“8988”,“7888”,“9888”], target = “8888”
輸出:-1
解釋:
無法旋轉到目標數字且不被鎖定。
示例 4:

輸入: deadends = [“0000”], target = “8888”
輸出:-1

提示:

死亡列表 deadends 的長度範圍爲 [1, 500]。
目標數字 target 不會在 deadends 之中。
每個 deadends 和 target 中的字符串的數字會在 10,000 個可能的情況 ‘0000’ 到 ‘9999’ 中產生。

解法
首先是求“最小”次數,所以用BFS。本來用了vis另一個set保存遍歷過的元素,但是發現其實可以和deadends合併,省時間和空間。
注意每次加入“樹的子節點”的時候,都是加入8個(4位數,8種改變的可能)

class Solution(object):
    def openLock(self, deadends, target):
        """
        :type deadends: List[str]
        :type target: str
        :rtype: int
        """
        ori = "0000"
        if ori in deadends:
            return -1
        
        q = [(ori, 0)]
        deadends = set(deadends)
        
        while q:
            top, step = q.pop(0)
            if top == target:
                return step
            for i in range(4):
                x = [(int(top[i]) + 1) % 10, (int(top[i]) - 1) % 10]
                for j in range(2):
                    k = str(x[j])
                    cur = top[:i] + k + top[i+1:]
                    if cur not in deadends:
                        deadends.add(cur)
                        q.append((cur, step+1))
                
                        
        return -1

有效的括號

給定一個只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判斷字符串是否有效。

有效字符串需滿足:

左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。

示例 1:

輸入: “()”
輸出: true
示例 2:

輸入: “()[]{}”
輸出: true
示例 3:

輸入: “(]”
輸出: false
示例 4:

輸入: “([)]”
輸出: false
示例 5:

輸入: “{[]}”
輸出: true

解法
首先讀懂題目,判斷括號是否有效;然後找到有效的條件:

  • 左右括號匹配:存在右括號,就找左括號是否存在
  • 順序不能錯:存在右括號時,pop出來的第一個括號就應該是對應的左括號。
  • 空字符串
class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s:
            return True
        
        stack = []
        for x in s:
            #print(x)
            if x == "(" or x == "{" or x == "[":
                stack.append(x)
            if x == ')':
                if not stack or stack.pop() != '(':
                    return False
            if x == '}':
                if not stack or stack.pop() != '{':
                    return False
            if x == ']':
                if not stack or stack.pop() != '[':
                    return False
                
        if not stack:
            return True
        return False
    

每日溫度

根據每日 氣溫 列表,請重新生成一個列表,對應位置的輸入是你需要再等待多久溫度纔會升高超過該日的天數。如果之後都不會升高,請在該位置用 0 來代替。

例如,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的輸出應該是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:氣溫 列表長度的範圍是 [1, 30000]。每個氣溫的值的均爲華氏度,都是在 [30, 100] 範圍內的整數。

解法
題意:遍歷列表,得到最近的比當前值大的索引差值。分析可能的情況:

  • i+1位置就比i位置值大(比如[73, 74]),res[i]=1
  • i+1位置小於i位置,但是i+n位置大於i位置(比如[71, 69, 72]),res[i]=n
  • 後面沒有比i位置更大的數,res[i]=0

那麼就可以想到用棧保存前面的數的位置,遍歷當前數x的時候,就pop出最近的數比較一下是否比x小,如果是的話,取出來給res賦值(索引差);如果不是的話,stack前面不可能有比x更小的

class Solution(object):
    def dailyTemperatures(self, T):
        """
        :type T: List[int]
        :rtype: List[int]
        """
        res = [0] * len(T)
        
        stack = [0]
        for i in range(1, len(T)):
            while len(stack) > 0 and T[stack[-1]] < T[i]:
                idx = stack.pop()
                res[idx] = i - idx
            stack.append(i)
            
        return res

克隆圖

給你無向 連通 圖中一個節點的引用,請你返回該圖的 深拷貝(克隆)。

圖中的每個節點都包含它的值 val(int) 和其鄰居的列表(list[Node])。

class Node {
public int val;
public List neighbors;
}

測試用例格式:

簡單起見,每個節點的值都和它的索引相同。例如,第一個節點值爲 1,第二個節點值爲 2,以此類推。該圖在測試用例中使用鄰接列表表示。

鄰接列表是用於表示有限圖的無序列表的集合。每個列表都描述了圖中節點的鄰居集。

給定節點將始終是圖中的第一個節點(值爲 1)。你必須將 給定節點的拷貝 作爲對克隆圖的引用返回。

示例 1:
輸入:adjList = [[2,4],[1,3],[2,4],[1,3]]
輸出:[[2,4],[1,3],[2,4],[1,3]]
解釋:
圖中有 4 個節點。
節點 1 的值是 1,它有兩個鄰居:節點 2 和 4 。
節點 2 的值是 2,它有兩個鄰居:節點 1 和 3 。
節點 3 的值是 3,它有兩個鄰居:節點 2 和 4 。
節點 4 的值是 4,它有兩個鄰居:節點 1 和 3 。

示例 2:
輸入:adjList = [[]]
輸出:[[]]
解釋:輸入包含一個空列表。該圖僅僅只有一個值爲 1 的節點,它沒有任何鄰居。

示例 3:
輸入:adjList = []
輸出:[]
解釋:這個圖是空的,它不含任何節點。

示例 4:
輸入:adjList = [[2],[1]]
輸出:[[2],[1]]

提示:

節點數介於 1 到 100 之間。
每個節點值都是唯一的。
無向圖是一個簡單圖,這意味着圖中沒有重複的邊,也沒有自環。
由於圖是無向的,如果節點 p 是節點 q 的鄰居,那麼節點 q 也必須是節點 p 的鄰居。
圖是連通圖,你可以從給定節點訪問到所有節點。

解法
題意:使用圖的起始節點重新構建一個相同的圖。情況分析:

  • 圖是空的(沒有節點)
  • 圖上只有一個節點(注意要返回節點,而不是返回空)
  • 如果用隊列來做的話:也就是BFS遍歷,當遍歷到當前節點ori,需要找到對應的新建的圖上的該節點cur才能給cur賦值鄰居,因此需要一個字典存儲,該字典還可以用於判斷ori的鄰居是否在新圖上已經存在;爲了避免重複遍歷節點(因爲鄰居可能重複),需要一個visit存儲;爲了避免重複把同一個未遍歷過的鄰居節點加入隊列,需要判斷隊列中是否已經存在。
"""
# Definition for a Node.
class Node(object):
    def __init__(self, val = 0, neighbors = []):
        self.val = val
        self.neighbors = neighbors
"""
class Solution(object):
    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """
        if not node:
            return None
        if not node.neighbors:
            return Node(val=node.val)
        
        root = Node(val=node.val)
        dic = {1: root}
        q = [node]
        vis = set()
        
        while q:
            ori = q.pop(0)
            cur = dic[ori.val]
            #print(ori.val, cur.val)
            for x in ori.neighbors:
                #print(x.val, dic.keys(), vis)
                if x.val not in dic:
                    tmp = Node(x.val)
                    dic[x.val] = tmp
                cur.neighbors.append(dic[x.val])
                if x.val not in vis and x not in q:
                    q.append(x)
                    
            vis.add(ori.val)
            
        return root
            
        

目標和

給定一個非負整數數組,a1, a2, …, an, 和一個目標數,S。現在你有兩個符號 + 和 -。對於數組中的任意一個整數,你都可以從 + 或 -中選擇一個符號添加在前面。

返回可以使最終數組和爲目標數 S 的所有添加符號的方法數。

示例 1:

輸入: nums: [1, 1, 1, 1, 1], S: 3
輸出: 5
解釋:

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

一共有5種方法讓最終目標和爲3。
注意:

數組非空,且長度不會超過20。
初始的數組的和不會超過1000。
保證返回的最終結果能被32位整數存下。

解法
題意:返回加減數組中每個數字和爲目標數 S 的所有可能性。(如果用動態規劃的思路的話:在揹包問題中,我們要考慮物品放還是不放)
很快想到用DFS,定義函數f(i,target)表示i長度內目標爲target的方法數,那麼f(i,target)=f(i−1,target−nums[i])+f(i−1,target+nums[i])。直接這樣做容易超時,所以可以想到用一個字典保存下來已經遍歷過的方法數目。

class Solution(object):
    def findTargetSumWays(self, nums, S):
        """
        :type nums: List[int]
        :type S: int
        :rtype: int
        """
        
        vis = {(0, 0): 1}
        
        res = self.dfs(nums, S, vis)
        
        return res
    
    def dfs(self, nums, S, vis):
        if (len(nums), S) in vis:
            return vis[(len(nums), S)]
        elif len(nums) == 0:
            return 0
        
        vis[(len(nums), S)] = self.dfs(nums[1:], S - nums[0], vis) + self.dfs(nums[1:], S + nums[0], vis)
        
        return vis[(len(nums), S)]

二叉樹的中序遍歷

給定一個二叉樹,返回它的中序 遍歷。

示例:

輸入: [1,null,2,3]
   1
    \
     2
    /
   3

輸出: [1,3,2]

輸出: [1,3,2]
進階: 遞歸算法很簡單,你可以通過迭代算法完成嗎?

解法
使用迭代方法

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

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if root == None:
            return []
        
        res = []
        cur = root
        stack = []
        
        while stack or cur:
            if cur:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
                
        return res
        

字符串解碼

給定一個經過編碼的字符串,返回它解碼後的字符串。

編碼規則爲: k[encoded_string],表示其中方括號內部的 encoded_string 正好重複 k 次。注意 k 保證爲正整數。

你可以認爲輸入字符串總是有效的;輸入字符串中沒有額外的空格,且輸入的方括號總是符合格式要求的。

此外,你可以認爲原始數據不包含數字,所有的數字只表示重複的次數 k ,例如不會出現像 3a 或 2[4] 的輸入。

示例:

s = “3[a]2[bc]”, 返回 “aaabcbc”.
s = “3[a2[c]]”, 返回 “accaccacc”.
s = “2[abc]3[cd]ef”, 返回 “abcabccdcdcdef”.

解法
題意:類似於計算器,返回k*字符串內部子串的結果
思路:遇到不同類型的字符進行不同方式處理,幾種情況:

  • 數字:注意可能出現多位數
  • 字母:拼成tmp子串,用於後續的重複
  • 字符“]":將重複後的tmp子串再放入棧
  • 字符"[":重置數字、子串等

自己寫的,比較麻煩:

class Solution(object):
    def decodeString(self, s):
        """
        :type s: str
        :rtype: str
        """
        if not s:
            return ""
        stack = []
        for i, x in enumerate(s):
            if x != "]":
                stack.append(x)
            else:
                tmp = []
                while stack[-1] != "[":
                    tmp.append(stack.pop())
                tmp = tmp[::-1]
                stack.pop()
                
                k = ""
                while len(stack) > 0 and stack[-1].isdigit():
                    k = stack.pop() + k
                k = int(k)
                while k > 0:
                    stack.extend(tmp)
                    k -= 1
                    
        return "".join(stack)

網上看的比較清晰簡潔的方法:

class Solution(object):
    def decodeString(self, s):
        """
        :type s: str
        :rtype: str
        """
        stack = []
        curNum = 0
        curString = ''
        for c in s:
            if c == '[':
                stack.append(curString)
                stack.append(curNum)
                curString = ''
                curNum = 0
            elif c == ']':
                num = stack.pop()
                prevString = stack.pop()
                curString = prevString + num * curString
            elif c.isdigit():
                curNum = curNum * 10 + int(c)
            else:
                curString += c
        return curString

圖像渲染

有一幅以二維整數數組表示的圖畫,每一個整數表示該圖畫的像素值大小,數值在 0 到 65535 之間。

給你一個座標 (sr, sc) 表示圖像渲染開始的像素值(行 ,列)和一個新的顏色值 newColor,讓你重新上色這幅圖像。

爲了完成上色工作,從初始座標開始,記錄初始座標的上下左右四個方向上像素值與初始座標相同的相連像素點,接着再記錄這四個方向上符合條件的像素點與他們對應四個方向上像素值與初始座標相同的相連像素點,……,重複該過程。將所有有記錄的像素點的顏色值改爲新的顏色值。

最後返回經過上色渲染後的圖像。

示例 1:

輸入:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
輸出: [[2,2,2],[2,2,0],[2,0,1]]
解析:
在圖像的正中間,(座標(sr,sc)=(1,1)),
在路徑上所有符合條件的像素點的顏色都被更改成2。
注意,右下角的像素沒有更改爲2,
因爲它不是在上下左右四個方向上與初始點相連的像素點。

注意:
image 和 image[0] 的長度在範圍 [1, 50] 內。
給出的初始點將滿足 0 <= sr < image.length 和 0 <= sc <image[0].length。
image[i][j] 和 newColor 表示的顏色值在範圍 [0, 65535]內。

解法
很顯然就是”迷宮“類問題,用dfs即可。也可以用隊列解決

dfs方法:

class Solution(object):
    def floodFill(self, image, sr, sc, newColor):
        """
        :type image: List[List[int]]
        :type sr: int
        :type sc: int
        :type newColor: int
        :rtype: List[List[int]]
        """
        if not image:
            return image
        
        curC = image[sr][sc]
        self.vis = [[False for i in range(len(image[0]))] for j in range(len(image))]
        image = self.dfs(image, sr, sc, curC, newColor)
        
        return image
        
    def dfs(self, image, r, c, curC, newC):
        image[r][c] = newC
        self.vis[r][c] = True
            
        if r > 0 and image[r - 1][c] == curC and not self.vis[r - 1][c]:
            image = self.dfs(image, r - 1, c, curC, newC)
            
        if r < len(image) - 1 and image[r + 1][c] == curC and not self.vis[r + 1][c]:
            image = self.dfs(image, r + 1, c, curC, newC)
            
        if c > 0 and image[r][c - 1] == curC and not self.vis[r][c - 1]:
            image = self.dfs(image, r, c - 1, curC, newC)
            
        if c < len(image[0]) - 1 and image[r][c + 1] == curC and not self.vis[r][c + 1]:
            image = self.dfs(image, r, c + 1, curC, newC)
            
        return image
            

隊列:

class Solution(object):
    def floodFill(self, image, sr, sc, newColor):
        """
        :type image: List[List[int]]
        :type sr: int
        :type sc: int
        :type newColor: int
        :rtype: List[List[int]]
        """
        m, n = len(image), len(image[0])
        color = image[sr][sc]
        image[sr][sc] = newColor
        
        visited = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
        dx = [1, -1, 0, 0]
        dy = [0, 0, 1, -1]
        
        q = deque()
        q.append([sr,sc])
        while q:
            x0, y0 = q.popleft()
            for k in range(4):
                x = x0 + dx[k]
                y = y0 + dy[k]
 
                if 0 <= x < m and 0 <= y < n and image[x][y] == color and visited[x][y] == 0:
                    image[x][y] = newColor
                    visited[x][y] = 1
                    q.append([x, y])
                    
        return image

01 矩陣

給定一個由 0 和 1 組成的矩陣,找出每個元素到最近的 0 的距離。

兩個相鄰元素間的距離爲 1 。

示例 1:
輸入:

0 0 0
0 1 0
0 0 0
輸出:

0 0 0
0 1 0
0 0 0
示例 2:
輸入:

0 0 0
0 1 0
1 1 1
輸出:

0 0 0
0 1 0
1 2 1

注意:
給定矩陣的元素個數不超過 10000。
給定矩陣中至少有一個元素是 0。
矩陣中的元素只在四個方向上相鄰: 上、下、左、右。

解法
題意:找離每個1最近的0的距離
步驟:因爲是最近距離,顯然是BFS。開始想的方法是遍歷矩陣,找到每個1之後再用BFS,顯然複雜度非常高。後來看到網上的做法,其實這就相當於求每個0最近的1的距離,遍歷一遍矩陣就可以了。

class Solution(object):
    def updateMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[List[int]]
        """
        
        if not matrix:
            return matrix
        
        res = [[0 for i in range(len(matrix[0]))] for j in range(len(matrix))]
        vis = [[False for ii in range(len(matrix[0]))] for jj in range(len(matrix))]
        queue = []
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if matrix[i][j] == 0:
                    queue.append((i, j, 0))
                    vis[i][j] = True
        
        dx = [1, -1, 0, 0]
        dy = [0, 0, 1, -1]
        while queue:
            x0, y0, step = queue.pop(0)
            if matrix[x0][y0] == 1:
                res[x0][y0] = step
                
            step += 1
            for k in range(4):
                x = x0 + dx[k]
                y = y0 + dy[k]

                if x >= 0 and x <= len(matrix) - 1 and y >= 0 and y <= len(matrix[0]) - 1 and vis[x][y] == False:
                    queue.append((x, y, step))
                    vis[x][y] = True
                        
        return res                
                        

鑰匙和房間

有 N 個房間,開始時你位於 0 號房間。每個房間有不同的號碼:0,1,2,…,N-1,並且房間裏可能有一些鑰匙能使你進入下一個房間。

在形式上,對於每個房間 i 都有一個鑰匙列表 rooms[i],每個鑰匙 rooms[i][j] 由 [0,1,…,N-1] 中的一個整數表示,其中 N = rooms.length。 鑰匙 rooms[i][j] = v 可以打開編號爲 v 的房間。

最初,除 0 號房間外的其餘所有房間都被鎖住。

你可以自由地在房間之間來回走動。

如果能進入每個房間返回 true,否則返回 false。

示例 1:

輸入: [[1],[2],[3],[]]
輸出: true
解釋:
我們從 0 號房間開始,拿到鑰匙 1。
之後我們去 1 號房間,拿到鑰匙 2。
然後我們去 2 號房間,拿到鑰匙 3。
最後我們去了 3 號房間。
由於我們能夠進入每個房間,我們返回 true。
示例 2:

輸入:[[1,3],[3,0,1],[2],[0]]
輸出:false
解釋:我們不能進入 2 號房間。

提示:
1 <= rooms.length <= 1000
0 <= rooms[i].length <= 1000
所有房間中的鑰匙數量總計不超過 3000。

解法
題意:判斷是否每個房間都被visited過
方法:用隊列或者棧應該都可以。

class Solution(object):
    def canVisitAllRooms(self, rooms):
        """
        :type rooms: List[List[int]]
        :rtype: bool
        """
        if not rooms:
            return rooms
        
        queue = []
        vis = [False for i in range(len(rooms))]
        vis[0] = True
        for x in rooms[0]:
            queue.append(x)
            vis[x] = True
            
        while queue:
            top = queue.pop(0)
            for x in rooms[top]:
                if vis[x] == False:
                    queue.append(x)
                    vis[x] = True
                    
        for i in range(len(vis)):
            if vis[i] == False:
                return False
        
        return True
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章