LEETCODE-刷題個人筆記 Python(1-400)-TAG標籤版本

1. Array

(1) 27. Remove Element(Easy)

給定數組nums和值val,在適當位置刪除該值的所有實例並返回新長度。

思路:
不需要使用排序,如果等於該值,則將n-1的值賦給i,然後n = n - 1

    def removeElement(self, nums, val):
        """ 
        :type nums: List[int]
        :type val: int
        :rtype: int
        """
        i = 0
        n = len(nums)
        while(i<n):
            if (nums[i] ==val):
                nums[i]= nums[n-1]
                n -=1
            else:
                i+=1
        return n

(2) 26. Remove Duplicates from Sorted Array(Easy)

給定排序的數組nums,就地刪除重複項,使每個元素只出現一次並返回新的長度。
不要爲另一個數組分配額外的空間,必須通過使用O(1)額外內存修改輸入數組來實現此目的。

思路:
使用del

    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        i = 0
        while i < len(nums)-1:
            if nums[i+1] == nums[i]:
                del nums[i+1]
            else:
                i += 1
        return len(nums)

(3) 80. Remove Duplicates from Sorted Array II(Medium)

給定排序的數組nums,就地刪除重複項,使重複項最多出現兩次並返回新的長度。
在這裏插入圖片描述

思路:
使用單層循環,使用nums[j]>nums[i-2]來判斷

    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        i = 0
        for n in range(len(nums)):
            if i<2 or nums[n]>nums[i-2]:
                nums[i] = nums[n]
                i +=1
        return i

(4) 277. Find the Celebrity

如果一個人A是名人,那麼其他的n-1個人都認識A,而且A不認識其他的n-1個人。這題的目標是要從n個人中找出其中的名人,如果沒有,則告知其中沒有名人。我們可以調用knows(a,b)函數來詢問a是否認識b,而我們要儘量少調用這個函數。

思路:
如果我們從n個人中任意挑兩個人a,b出來,詢問啊是否認識b,那麼只有兩種情況:

(1)a認識b:那麼a肯定不是名人。

(2)b認識a:那麼b肯定不是名人。
所以任何一種情況,我們都可以排除掉2個人中的1一個人。如果我們不斷地重複的這個過程,直到只剩下一個人,那麼我們會做n-1次對比。而剩下這個人是唯一可能成爲名人的人,那麼我們需要詢問剩下的n-1個人是否認識他,也需要詢問他是否認識剩下的n-1個人。

因此我們一共需要詢問3(n-1)次——時間複雜度爲O(n)。(其中n個人中不可能存在兩個名人,可證)

def findCelebrity(self,n):
	if n ==0:
		return -1
	curr_stay = 0
	for i in range(1,n):
		if know(curr_stay,i):
			curr_stay = i
	for i in range(0,n):
		if curr_stay == i:
			cotinue
		if know(curr_stay,i):
			return -1
		if not know(i,curr_stay)
			return -1
	return curr_stay

(5) 189. Rotate Array

給定一個數組,將數組向右旋轉k步,其中k爲非負數,使用O(1)的空間,無需返回,直接數組上改動
在這裏插入圖片描述

思路:
Original List : 1 2 3 4 5 6 7
After reversing all numbers: 7 6 5 4 3 2 1
After reversing first k numbers : 5 6 7 4 3 2 1
After revering last n-k numbers : 5 6 7 1 2 3 4 --> Result
先全部reverse,然後前K個reverse,後面n-k個reverse

def rotate(self, nums, k):
	        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        def reverse(start,end,nums):
        	while(start<end):
        		temp = nums[end]
        		nums[end] = nums[start]
        		nums[start] = temp
        		start +=1
        		end -=1
        	return nums
        k = k %len(nums)
        nums = reverse(0,len(nums)-1,nums)
        nums = reverse(0,k-1,nums)
        nums = reverse(k,len(nums)-1,nums)     		

(6) 41. First Missing Positive(Hard)

給定未排序的整數數組,找到最小的缺失正整數。您的算法應該在O(n)時間運行並使用恆定的額外空間。
在這裏插入圖片描述

思路:
1、需要時間複雜度爲n
2、並且是尋找的最小的缺失正整數
3、首先將num[i] 放置在 num[num[i]-1]上,首要要保證num[i]-1<len(num)
4、使用while知道num[i]放到適當的位置,因爲for循環,i會直接跳過,如果不用while
5、找到第一個 nums[i] 不等於i+1,如果沒有,返回 len +1

def firstMissingPositive(self,nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        for i in range(len(nums)):
        	while 0<=nums[i]-1<len(nums) and nums[nums[i]-1] !=nums[i]:
        		temp = nums[i]-1
        		nums[temp],nums[i] = nums[i],nums[temp]
        for i in range(len(nums)):
        	if nums[i]!=i+1:
        		return i+1
        return len(nums)+1

(7) 299. Bulls and Cows(Medium)

給定兩個等長的字符串,字符串中只包含數字,求兩個字符串中的bulls和cows。其中,bulls表示在字符串同一位置的數值相同的數字的個數。cows表示,guess中的數字在secret中出現了,但是處在不正確的位置上的數字的個數。
1,每次給出的數字可能是重複的(跟一般猜數字不同);2,題目沒說是每次四個數字,只說secret和guess一樣長。

def getHint(self, secret, guess):
	bulls = sum(i==j for i,j in zip(secret,guess))
	bows = sum(min(secret.count(x),guess.count(x)) for x in set(guess)) - bulls
	return '%dA%dB' %(bulls,bows)

(8) 134 .Gas Station(Medium)

在一個圈子路線裏面有N個汽油站,i站的汽油有gas[i]汽油。現在有一輛無限容量的車,它從i站開到(i+1)需要耗費cost[i]汽油。如果這輛車可以走完這個圈,那麼返回這個車的起點,否者返回-1.
在這裏插入圖片描述

思路:
1、如果gas的總和大於或等於cost的總和,必然存在一種路線使得走完整個圈子。
2、假設當前剩餘的油量爲diff,若達到站點i的的剩餘油量小於0,則將設置起始站點設爲i;另外設置所有站點的剩餘油量爲total,當total小於0,則不能環繞,否則可以

def canCompleteCircuit(self,gas,cost):
	        """
        :type gas: List[int]
        :type cost: List[int]
        :rtype: int
        """
        if sum(gas)<sum(cost):
        	return -1
        index = 0
        diff = 0
        for i in range(len(gas)):
        	if gas[i]+dif <cost[i]:
        		index = i +1
        		diff = 0
        	else:
        		diff += gas[i]-cost[i]
        return index

(9) 118. Pascal’s Triangle(Easy)(很少考)

給定非負整數numRows,生成Pascal三角形的第一個numRows。
Input: 5
Output:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]

    def generate(self, numRows):
        """
        :type numRows: int
        :rtype: List[List[int]]
        """
        total = []
        if numRows == 0:
            return total
        total =[[1]]
        if numRows==1:
            return total
        total = [[1],[1,1]]
        if numRows==2:
            return total
        for i in range(3,numRows+1):
            total.append([])
            for j in range(i):
                if j ==0 or j == i-1:
                    total[-1].append(1)
                else:
                    total[-1].append(total[i-2][j-1]+total[i-2][j])
        return total

(10) 119. Pascal’s Triangle II(Easy)(很少考)

給定非負索引k,其中k≤33,返回Pascal三角形的第k個索引行。

思路:
1、使用遞歸,直到遞歸到那一層

    def getRow(self, rowIndex):
        """
        :type rowIndex: int
        :rtype: List[int]
        """
        if rowIndex == 0:
            return [1]
        if rowIndex == 1:
            return [1,1]
        prerow = self.getRow(rowIndex-1)
        return [1]+[prerow[i]+prerow[i+1] for i in range(len(prerow)-1)]+[1]

(11) 274. H-Index(Medium)

給定研究者的一系列引用(每個引用是一個非負整數),寫一個函數來計算研究者的h指數。

H指數(H index)是一個混合量化指標,可用於評估研究人員的學術產出數量與學術產出水平
可以按照如下方法確定某人的H指數:
將其發表的所有SCI論文按被引次數從高到低排序;
從前往後查找排序後的列表,直到某篇論文的序號大於該論文被引次數。所得序號減一即爲H指數。
h指數是指他至多有h篇論文分別被引用了至少h次
在這裏插入圖片描述

思路:
解法1: 先將數組排序,T:O(nlogn), S:O(1)。然後對於每個引用次數,比較大於該引用次數的文章,取引用次數和文章數的最小值,即 Math.min(citations.length-i, citations[i]),並更新 level,取最大值。排好序之後可以用二分查找進行遍歷,這樣速度會更快。

h指數是指他至多有h篇論文分別被引用了至少h次
解法2: Counting sort,T:O(n), S:O(n)。使用一個大小爲 n+1 的數組count統計引用數,對於count[i]表示的是引用數爲 i 的文章數量。從後往前遍歷數組,當滿足 count[i] >= i 時,i 就是 h 因子,返回即可,否則返回0。

爲什麼要從後面開始遍歷? 爲什麼 count[i] >= i 時就返回?

一方面引用數引用數大於 i-1 的數量是i-1及之後的累加,必須從後往前遍歷。另一方面,h 因子要求儘可能取最大值,而 h 因子最可能出現最大值的地方在後面,往前值只會越來越小,能儘快返回就儘快返回,所以一遇到 count[i] >= i 就返回。(count[i-1]代表大於i-1的個數有多少)

    def hIndex(self, citations):
        """
        :type citations: List[int]
        :rtype: int
        """
        n = len(citations)
        count  = [0]*(n+1)
        for i in range(n):
            if citations[i]>=n:
                count[n]+=1
            else:
                count[citations[i]] +=1
        
        for i in range(n,0,-1):
            if count[i]>=i:
                return i
            count[i-1] +=count[i]#count[i-1]代表大於i-1的個數有多少
        return 0

(12) 275. H-Index II(Medium)(Binary Search)

給定一個按升序排序的引文數組(每個引用是非負整數),編寫一個函數來計算研究者的h指數。
在這裏插入圖片描述

思路:
274題的拓展。輸入的數組是有序的,讓我們優化算法。提示(現在題目中沒有提示了):O(logn)。顯然使用二分法。

    def hIndex(self, citations):
        """
        :type citations: List[int]
        :rtype: int
        """
        n = len(citations)
        left = 0
        right = n-1
        while left<=right:
            middle = (left+right)//2
            if citations[middle] >=n-middle:
                right = middle-1
            else:
                left = middle+1
        return n-left

(13) 243. Shortest Word Distance(Easy)

給一個string的數組,裏面有word1和word2兩個詞,要求返回這兩個詞之間的最短距離。
在這裏插入圖片描述

思路:
給定兩個指針,如果這兩個都不爲空,求最小值。

def shortestDistance(self, words, word1,word2):
	    """
        :type words: List[str]
        :type word1: str
        :type word2: str
        :rtype: int
        """
        p1 = -1
        p2 = -1
        result = len(words)
        for i in range(len(words)):
        	if words[i] == word1:
        		p1 = i
        	elif word[i] == word2:
        		p2 = i
        	if p1!=-1 and p2!=-1:
        		result = min(result,abs(p2-p1))
        return result

(14) 217. Contains Duplicate

給定一個整數數組,查找數組是否包含任何重複項。 如果數組中任何值至少出現兩次,則函數應返回true,如果每個元素都不相同,則返回false。
在這裏插入圖片描述

思路:
可以巧妙的利用len 和 set
set底層實現也是用了hash和eq函數

def containsDuplicate(self,nums):
	        """
        :type nums: List[int]
        :rtype: bool
        """
        return len(nums)!=len(set(nums))

(15) 219. Contains Duplicate II(Easy)(很少考)

給定一個整數數組和一個整數k,找出數組中是否存在兩個不同的索引i和j,使得nums [i] = nums [j]並且i和j之間的絕對差值最多爲k
在這裏插入圖片描述

思路:
使用字典存儲最大的index,判斷是否有重複的元素時,需要判斷兩者之間是否不大於k

    def containsNearbyDuplicate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: bool
        """
        dict = {}
        for i,v in enumerate(nums):
            if v in dict and i - dict[v] <=k:
                return True
            else:
                dict[v] = i
        return False

(16) 220. Contains Duplicate III(很少考)

給定一個整數數組,找出數組中是否有兩個不同的索引i和j,使得nums [i]和nums [j]之間的絕對差值最大爲t,i和j之間的絕對差值最大ķ。

思路:
如果: | nums[i] - nums[j] | <= t 式a

等價: | nums[i] / t - nums[j] / t | <= 1 式b

推出: | floor(nums[i] / t) - floor(nums[j] / t) | <= 1 式c

​等價: floor(nums[j] / t) ∈ {floor(nums[i] / t) - 1, floor(nums[i] / t), floor(nums[i] / t) + 1} 式d
其中式b是式c的充分非必要條件,因爲逆否命題與原命題等價,所以:

如果: floor(nums[j] / t) ∉ {floor(nums[i] / t) - 1, floor(nums[i] / t), floor(nums[i] / t) + 1} 非d
floor 指上界

    def containsNearbyAlmostDuplicate(self, nums, k, t):
        """
        :type nums: List[int]
        :type k: int
        :type t: int
        :rtype: bool
        """
        # 檢驗數據合法性
        if k < 1 or t < 0:
            return False
        # 這裏採用有序字典,它是dict的一個繼承子類,按照元素輸入順序進行排序
        dic = collections.OrderedDict()
        for n in nums:
            # 注意判斷t是否爲0
            key = n if not t else n // t
            for m in (dic.get(key - 1), dic.get(key), dic.get(key + 1)):
                # 如果找到一個數滿足條件一,返回
                if m is not None and abs(n - m) <= t:
                    return True
            if len(dic) == k:
                # 維持字典大小爲k,如果超過,刪除first;函數原型:dict.popitem(last=False),不加參數表示隨機從頭尾刪除一個
                dic.popitem(False)
            # 加入新數
            dic[key] = n
        return False

(17) 55. Jump Game(Medium)

跳棋,給一個list,查看能否到達最後一個
給定一個非負整數數組,您最初定位在數組的第一個索引處。 數組中的每個元素表示該位置的最大跳轉長度。 確定您是否能夠到達最後一個索引。
在這裏插入圖片描述

思路:
給個判斷條件i<cur

    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        cur = 0
        for i in range(len(nums)):
            if i>cur:
                return False
            cur = max(i+nums[i],cur)
        return True

(18) 45. Jump Game II(Hard)

每次可以向後面跳躍的格子數等於當前的點數。求最少需要多少步就能調大最後的格子。
Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
Jump 1 step from index 0 to 1, then 3 steps to the last index.

思路:
要記錄當前一跳所能到達的最遠距離cur、上一跳所能到達的最遠距離last,和當前所使用跳數
如果i<last,則表示從last這個位置可以直接到達i的位置,res就不需要加1;如果i>last,則表示從last到不了該位置,這一步就是必須的,res就要加1。同時要更新last的值。而cur記錄當前位置可以到達的最大位置,用以更新last的值。

    def jump(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # cur表示當前步能到達的最遠位置
        cur=0
        # last表示上一次能到達的最遠位置
        last = 0
        # 記錄總共跳了多少步
        res = 0
        for i in range(len(nums)):
            if i>last:
                last = cur
                res +=1
            if cur<i+nums[i]:
                cur = i+nums[i]
        return res if cur>=len(nums)-1 else 0

(19) 121. Best Time to Buy and Sell Stock(Easy)

假設您有一個數組,其中第i個元素是第i天給定股票的價格。 如果您只被允許完成最多一筆交易(即買入並賣出一股股票),請設計一個算法以找到最大利潤。

思路:
定義一個i之前的最小值,定義一個到i最大的sum

    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        min_p = float('inf')
        sum_o = 0
        for i in range(len(prices)):
            min_p = min(min_p,prices[i])
            p = prices[i]-min_p
            sum_o = max(p,sum_o)
        return sum_o

(20) 122. Best Time to Buy and Sell Stock II(Easy)

設計算法以找到最大利潤。您可以根據需要完成儘可能多的交易(即,多次買入並賣出一股股票)。
您不得同時進行多筆交易(即,您必須在再次購買之前賣出股票)。

思路:
1、可以找到谷峯和低窪,然後相減
2、完全可以不用理谷峯和低窪,如果值遞增的只要前後相減就可以了,避免了找谷峯和低窪

    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        total = 0;
        for i in range(len(prices)-1):
            if prices[i+1]>prices[i]:
                total += prices[i+1]-prices[i]
        return total

(21) 123. Best Time to Buy and Sell Stock III(Hard)

假設您有一個數組,其中第i個元素是第i天給定股票的價格。 設計算法以找到最大利潤。您最多可以完成兩筆交易。

思路:
使用DP
計算某一天極其之前所有時間內的最大利益
計算某一天極其之後所有時間內價格的最利益

    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        #使用DP好了,另一個實在看不懂
        n = len(prices)
        if n ==0:
            return 0
        p1 = [0]*n
        p2 = [0]*n
        
        ##計算某一天極其之前所有時間內的最大利益
        min_price = prices[0]
        for i in range(1,n):
            min_price = min(prices[i],min_price)
            p1[i] = max(p1[i-1],prices[i]-min_price)
        
         ##計算某一天極其之後所有時間內價格的最利益
        max_price = prices[-1]
        for i in range(n-2,-1,-1):
            max_price = max(prices[i],max_price)
            p2[i] = max(p2[i+1],max_price-prices[i])
        ans = 0
        ## 計算最大收益
        for i in range(n):
            ans = max(ans,p1[i]+p2[i])
        return ans

(22) 188. Best Time to Buy and Sell Stock IV(Hard)

假設您有一個數組,其中第i個元素是第i天給定股票的價格。
設計算法以找到最大利潤。您最多可以完成k筆交易。
您不得同時進行多筆交易(即,您必須在再次購買之前賣出股票)。
在這裏插入圖片描述

思路:
DP解決
使用兩個list,一個global,一個local
1、一個是當前到達第i天可以最多進行j次交易,最好的利潤是多少(global[i][j])
2、另一個是當前到達第i天,最多可進行j次交易,並且最後一次交易在當天賣出的最好的利潤是多少(local[j])(看代碼中的英文解釋會更好一點)
#判斷是否在邊界
#如果k>=n/2足夠大,那麼這個問題就會變成問題Ⅱ了

    def maxProfit(self, k, prices):
        """
        :type k: int
        :type prices: List[int]
        :rtype: int
        """
        ##判斷是否在邊界
        n = len(prices)
        if k<=0 or n ==0:
            return 0
        ##如果k足夠大,那麼這個問題就會變成問題Ⅱ了
        if k>=n/2:
            result = 0
            for i in range(1,n):
                if prices[i]-prices[i-1]>0:
                    result +=prices[i] - prices[i-1]
            return result
        
        global_profit = [[0]*n for _ in range(k+1)]
        
        for i in range(1,k+1):
            local_profit = [0]*n
            for j in range(1,n):
                profit = prices[j] - prices[j-1]
                #We have made max profit with (i - 1) transations in (j - 1) days. 
                #For the last transation, we buy stock on day (j - 1) and sell it on day j.
                
                # We have made profit in (j - 1) days.
                # We want to cancel the day (j - 1) sale and sell it on day j.
                local_profit[j] = max(global_profit[i-1][j-1]+max(profit,0),local_profit[j-1]+profit)
                global_profit[i][j] = max(local_profit[j],global_profit[i][j-1])
        return global_profit[k][n-1]

(23) 309. Best Time to Buy and Sell Stock with Cooldown(Medium)

設計算法以找到最大利潤。您可以通過以下限制完成任意數量的交易(即,多次買入並賣出一股股票):
您不得同時進行多筆交易(即,您必須在再次購買之前賣出股票)。
在您出售股票後,您無法在第二天購買股票。 (即冷卻1天)
在這裏插入圖片描述

思路:
可以使用狀態機輕鬆求解
在這裏插入圖片描述
s0[i] = max(s0[i-1], s2[i-1])
s1[i] = max(s1[i-1], s0[i-1] - price[i])
s2[i] = s1[i-1] + price[i]

    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        #記得時‘-inf’ 和sell是0,另外兩個都未知
        sell,sell_cooldown,hold = 0,float('-inf'),float('-inf')
        for p in prices:
            hold,sell,sell_cooldown = max(hold,sell-p),max(sell,sell_cooldown),hold+p
        return max(sell,sell_cooldown)

(24) 11. Container With Most Water (Medium)

Given n non-negative integers a1, a2, …, an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.在這裏插入圖片描述

思路:首先先從兩邊開始,確定最大的寬,然後再慢慢縮小

    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        i= 0
        j = len(height)-1
        max = 0
        while(i!=j):
            if height[i]<height[j]:
                max = height[i]*(j-i) if height[i]*(j-i)>max else max
                i+=1
            else:
                max = height[j]*(j-i) if height[j]*(j-i)>max else max
                j-=1
        return max

(25) 42. Trapping Rain Water(Hard)

給定n個非負整數表示每個柱的寬度爲1的高程圖,計算下雨後能夠捕獲的水量。
在這裏插入圖片描述

思路:
(1)從左向右進行掃描,獲取每個位置的左邊最高的邊。
(2)從右向左進行掃描,獲取每個位置的右邊最高的邊。
(3)再遍歷一邊,找出left[i]和right[i]最小的一個,減去當前值,累加

    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        if len(height)==0:return 0
        left = [0]*len(height)
        left[0] = height[0]
        for i in range(1,len(height)):
            left[i] = max(left[i-1],height[i])
        right = [0]*len(height)
        right[-1] = height[-1]
        for i in range(len(height)-2,-1,-1):
            right[i] = max(right[i+1],height[i])
        res = 0
        for i in range(len(height)):
            res += min(left[i],right[i])-height[i]
        return res

(26) 334. Increasing Triplet Subsequence(Medium)

給定未排序的數組返回是否在數組中存在增加的長度爲3的子序列。 正式的功能應該
Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
O(n) time complexity and O(1) space complexity
在這裏插入圖片描述

    def increasingTriplet(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        first = second = float('inf')
        for n in nums:
            if n<=first:
                first = n
            elif n<=second:
                second = n
            else:
                return True
        return False

(27) 128. Longest Consecutive Sequence(Hard)

給定未排序的整數數組,找到最長連續元素序列的長度。
在這裏插入圖片描述

思路:
在for循環中加入一些判斷達到O(n)的複雜度

首先判斷元素是不是最小的,然後再開始+1操作,計算長度

    def longestConsecutive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        longest = 0
        
        nums = set(nums)
        for i in nums:
            if i-1 not in nums:
                current_num = i
                current_long = 1
                while current_num+1 in nums:
                    current_num +=1
                    current_long +=1
                
                longest = max(longest,current_long)
        return longest

(28) 164. Maximum Gap(Hard)(桶排序)

給定未排序的數組,找到其排序形式中的連續元素之間的最大差異。 如果數組包含少於2個元素,則返回0。(嘗試在線性時間/空間中解決它。)
在這裏插入圖片描述

思路:
解題思路
由於需要線性,並且假定是整數,使用桶排序
1、如果數組中數字小於2或者最大最小值相等,return 0
2、向上取整(math.ceil()),得到桶的大小(最大值-最小值)/(數組長度-1)
(差值不小於(最大值-最小值)/(數組長度-1))
3、得到多少個桶(最大值-最小值)/(桶的大小+1)
4、循環將元素放入桶中(i = bucket[(n-a)//size]),只需記錄最大最小值
5、不同桶之間的最大最小值,就是候選值

    def maximumGap(self, num):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(num) < 2 or min(num) == max(num):
            return 0
        a, b = min(num), max(num)
        size = (b-a)//(len(num)-1) or 1
        bucket = [[None, None] for _ in range((b-a)//size+1)]
        for n in num:
            b = bucket[(n-a)//size]
            b[0] = n if b[0] is None else min(b[0], n)
            b[1] = n if b[1] is None else max(b[1], n)
        bucket = [b for b in bucket if b[0] is not None]
        return max(bucket[i][0]-bucket[i-1][1] for i in range(1, len(bucket)))

(29) 287. Find the Duplicate Number(Medium)

給定包含n + 1個整數的數組nums,其中每個整數在1和n之間(包括1和n),證明必須存在至少一個重複的數字。假設只有一個重複的數字,找到重複的數字.(可能數組中含有多個重複數字)
1、您不能修改數組(假設該數組是隻讀的)。
2、您必須僅使用常量O(1)額外空間。
3、您的運行時複雜度應小於O(n2)。
4、 數組中只有一個重複的數字,但它可以重複多次。
在這裏插入圖片描述

思路:
方法一:sorted之後,用前後是否一樣 時間複雜度:O(nlogn) 空間複雜度:O(1)
方法二:使用set() 時間複雜度:O(n) 空間複雜度:O(n)
方法三:映射找環法
假設數組中沒有重複,那我們可以做到這麼一點,就是將數組的下標和1到n每一個數一對一的映射起來。比如數組是213,則映射關係爲0->2, 1->1, 2->3。假設這個一對一映射關係是一個函數f(n),其中n是下標,f(n)是映射到的數。如果我們從下標爲0出發,根據這個函數計算出一個值,以這個值爲新的下標,再用這個函數計算,以此類推,直到下標超界。(由於我們這題是一定存在環,所以不用這個判斷)實際上可以產生一個類似鏈表一樣的序列。比如在這個例子中有兩個下標的序列,0->2->3。

但如果有重複的話,這中間就會產生多對一的映射,比如數組2131,則映射關係爲0->2, {1,3}->1, 2->3。這樣,我們推演的序列就一定會有環路了,這裏下標的序列是0->2->3->1->1->1->1->…,而環的起點就是重複的數。

所以該題實際上就是找環路起點的題,和142. Linked List Cycle II一樣。
在這裏插入圖片描述
第一次相遇時slow走過的距離:a+b,fast走過的距離:a+b+c+b。
因爲fast的速度是slow的兩倍,所以fast走的距離是slow的兩倍,有 2(a+b) = a+b+c+b,
可以得到a=c(這個結論很重要!)。

    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        slow = nums[0]
        fast = nums[0]
        while True:
            slow = nums[slow]
            fast = nums[nums[fast]]
            if slow == fast:
                slow = nums[0]
                while slow !=fast:
                    slow = nums[slow]
                    fast = nums[fast]
                return slow

(30) 135. Candy(Hard)(很少考)

幾個小孩站一排,每個小孩有個等級值,現在給小孩發糖,發的時候要遵守2個規則:(1)每個小孩至少一顆糖(2)兩個相鄰的小孩中,等級大的小孩一定比等級小的小孩糖多,求發糖的數目的最小值。對於等級序列[1, 2, 2]來說,發糖數目最小值爲4,發糖序列爲[1, 2, 1]。

思路:
1、初始化所有小孩糖數目爲1
2、從前往後掃描,如果第i個小孩等級比第i-1個高,那麼i的糖數目等於i-1的糖數目+1
3、從後往前掃描,如果第i個的小孩的等級比i+1個小孩高,但是糖的數目卻小或者相等,那麼i的糖數目等於i+1的糖數目
(第一遍,保證了每一點比他左邊candy更多(如果得分更高的話)。第二遍,保證每一點比他右邊candy更多(如果得分更高的話),同時也會保證比他左邊的candy更多,因爲當前位置的candy只增不減。)

    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        candy = [1 for i in range(len(ratings))]
        for i in range(1,len(ratings)):
            if ratings[i]>ratings[i-1]:
                candy[i] =candy[i-1]+1
        for i in range(len(ratings)-2,-1,-1):
            if ratings[i]>ratings[i+1]and candy[i]<=candy[i+1]:
                candy[i] =candy[i+1]+1
        return sum(candy)

提高

(31)4. Median of Two Sorted Arrays(Hard)

有兩個排序的數組nums1和nums2分別爲m和n。 找到兩個排序數組的中位數。總運行時間複雜度應爲O(log(m + n))。
您可以假設nums1和nums2不能都爲空。
在這裏插入圖片描述

思路:
在這裏插入圖片描述
我們可以假設:

所以我們就可以確定:
在這裏插入圖片描述
(n>m? 因爲0≤i≤m並且j= (m+n+1)/2 -i 如果n<m,j就會是負數)
對於邊界的 i=0,i=m,j=0,j=n where A[i-1],B[j-1],A[i],B[j] 不存在,如果處理?
本來需要對比兩個不等式,如果其中一個不存在,那麼那個不等式就不用對比了,就只用對比另一個就好了,例如i = 0 ,則A[i-1]不存在,所以我們就不需要對比A[i-1]<=B[j]
在這裏插入圖片描述
在這裏插入圖片描述

    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        m,n = len(nums1),len(nums2)
        if n<m:
            m,n,nums1,nums2 = n,m,nums2,nums1
        imin,imax,middle = 0,m,int((n+m+1)/2)
        while imin<=imax:
            i = int((imin+imax)/2)
            j = middle - i
            if i < m and nums1[i]<nums2[j-1]:
                # i is too small, must increase it
                imin = i + 1
            elif i>0 and nums1[i-1]>nums2[j]:
                # i is too big, must decrease it
                imax = i - 1
            else:
                if j == 0:
                    max_of_left = nums1[i-1]
                elif i == 0:
                    max_of_left = nums2[j-1]
                else:
                    max_of_left = max(nums1[i-1],nums2[j-1])
                
                if (n+m)%2 == 1:
                    return max_of_left
                
                if j ==n:
                    min_of_right = nums1[i]
                elif i == m:
                    min_of_right = nums2[j]
                else:
                    min_of_right = min(nums1[i],nums2[j])
                return (max_of_left+min_of_right)/2.0

(32)289. Game of Life(Medium)

給定具有m×n個單元的板,每個單元具有初始狀態live(1)或dead(0)。每個單元格使用以下四個規則(取自上述維基百科文章)與其八個鄰居(水平,垂直,對角線)進行交互
1任何活的鄰居少於兩個的活細胞都會死亡,好像是由人口不足造成的。
2任何有兩三個活鄰居的活細胞都會留在下一代。
3任何有三個以上活着的鄰居的活細胞都會死亡,好像人口過多一樣
4具有正好三個活鄰居的任何死細胞變成活細胞,就好像通過繁殖一樣
編寫一個函數來計算給定其當前狀態的電路板的下一個狀態(在一次更新之後)。通過將上述規則同時應用於當前狀態中的每個細胞來創建下一個狀態,其中出生和死亡同時發生

你能在原地解決嗎?請記住,電路板需要同時更新:您不能先更新某些單元格,然後使用更新的值更新其他單元格

思路:
首先定義8個方向的向量
然後使用雙重循環遍歷,統計某個點周圍活細胞
最後利用規則進行更新
爲了能夠原地更新,先把更新的用-1和2來代替,再用一個雙循環進行更新

    def gameOfLife(self, board):
        """
        :type board: List[List[int]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        # Neighbors array to find 8 neighboring cells for a given cell
        neighbors = [(1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1), (0,1), (1,1)]

        rows = len(board)
        cols = len(board[0])

        # Iterate through board cell by cell.
        for row in range(rows):
            for col in range(cols):

                # For each cell count the number of live neighbors.
                live_neighbors = 0
                for neighbor in neighbors:

                    # row and column of the neighboring cell
                    r = (row + neighbor[0])
                    c = (col + neighbor[1])

                    # Check the validity of the neighboring cell and if it was originally a live cell.
                    if (r < rows and r >= 0) and (c < cols and c >= 0) and abs(board[r][c]) == 1:
                        live_neighbors += 1

                # Rule 1 or Rule 3
                if board[row][col] == 1 and (live_neighbors < 2 or live_neighbors > 3):
                    # -1 signifies the cell is now dead but originally was live.
                    board[row][col] = -1
                # Rule 4
                if board[row][col] == 0 and live_neighbors == 3:
                    # 2 signifies the cell is now live but was originally dead.
                    board[row][col] = 2

        # Get the final representation for the newly updated board.
        for row in range(rows):
            for col in range(cols):
                if board[row][col] > 0:
                    board[row][col] = 1
                else:
                    board[row][col] = 0

Interval

(33)56. Merge Intervals(Medium)

給定間隔的集合,合併所有重疊的間隔。
在這裏插入圖片描述

思路:
要使用sorted(key = lambda)函數

for k in sorted(intervals,key = lambda i:i.start):
    def merge(self, intervals):
        """
        :type intervals: List[Interval]
        :rtype: List[Interval]
        """
        out = []
        for k in sorted(intervals,key = lambda i:i.start):
            if out and k.start<=out[-1].end:
                out[-1].end = max(out[-1].end,k.end)
            else:
                out.append(k)
        return out

(34)57. Insert Interval(Hard)

給定一組非重疊間隔,在間隔中插入新間隔(必要時合併)。 您可以假設間隔最初是根據其開始時間排序的。

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

思路:主要是想清楚判斷條件,要新建一個數組用來存儲
如果插入的數組在兩個數組之間,則要判斷誰的start、end小和大

    def insert(self, intervals, newInterval):
        """
        :type intervals: List[Interval]
        :type newInterval: Interval
        :rtype: List[Interval]
        """
        i =0
        all =[]
        start = newInterval.start
        end = newInterval.end
        while i<len(intervals):
            if intervals[i].end>=start:
                if intervals[i].start>end:
                    break
                start = min(start,intervals[i].start)
                end = max(end,intervals[i].end)
            else:
                all.append(intervals[i])
            i+=1
        all.append(Interval(start,end))
        all += intervals[i:]
        return all

(35)LeetCode 252. Meeting Rooms(Easy)

給定一系列會議時間間隔,包括開始和結束時間[[s1,e1],[s2,e2],…](si <ei),確定一個人是否可以參加所有會議.
For example,
Given [[0, 30],[5, 10],[15, 20]],
return false.

思路:
對array進行排序,從i =1 開始判斷start是否在前一個end之前。

intervals.sort(key = lambda i :i.start)
for i in range(1,len(intervals)):
	if intervals[i].start>intervals.end:
		return False
return True
	

(36)253 Meeting Rooms II

給定由開始和結束時間[[s1,e1],[s2,e2],…](si <ei)組成的會議時間間隔數組,找到所需的最小會議室數。

思路:
1 第一步是按照起始時間對intervals進行排序
2 使用堆來處理,初始化堆和初始化一個list是一樣的
3 掃描intervals裏的所有interval,如果interval.start>=heap[0],則不需要新建一個meeting room,所以就用interval.end替換掉heap[0],用heapq.heapreplace,也就是先pop再push。
4 如果interval.start<=heap[0],則需要新建一個room,使用heapq.heappush將interval.end push進去
5 需要注意的是,當heap爲空的時候,是需要先把第一個interval.end push到堆裏去的,所以在第一個if的時候,要判斷heap是否爲空
6 這裏用的是heap而不是stack,這是因爲heap是priority queue,最小的元素在最頂端,而在這題中,我們heap裏面放的是interval.end,interval.end越小的越提前結束,所以下一個interval需要和提前結束的對比。但stack是先進先出,就沒有這個優勢
Time: O(n)
Space: O(1)

    def minMeetingRooms(self, intervals):
        """
        :type intervals: List[Interval]
        :rtype: int
        """
        # sort the intervals by start time
        intervals.sort(key = lambda x:x.start)
        heap = []
        for interval in intervals:
        	if heap and interval.start >= heap[0]:
        		#heapq 是python的標準庫
        		heapq.heapreplace(heap,interval.end)
        	else:
        		heapq.heappush(heap,interval.end)
        return len(heap)

Counter

(37)239. Sliding Window Maximum(Hard)

給定一個數組nums,有一個大小爲k的滑動窗口,它從數組的最左邊移動到最右邊。您只能在窗口中看到k編號,每次滑動窗口向右移動一個位置。返回最大滑動窗口中的數字
使用線性解決在這裏插入圖片描述

思路:
使用雙端隊列(存儲index),隊列元素降序排序,隊首元素爲所求的最大值。滑動窗口右滑。若出現的元素比隊首大,則清空隊列(使用while循環)。並將最大值插入隊列中。
若比當前值小,則插入尾部。每次窗口右移的時候判斷當前的最大值是否在有效範圍內,不在則剔除。

#雙向列表
from collections import deque
class Solution:
    def maxSlidingWindow(self, nums, k):
        res = []
        bigger = deque()
        for i, n in enumerate(nums):
            # make sure the rightmost one is the smallest
            while bigger and nums[bigger[-1]] <= n:
                bigger.pop()

            # add in
            bigger += [i]

            # make sure the leftmost one is in-bound
            if i - bigger[0] >= k:
                bigger.popleft()

            # if i + 1 < k, then we are initializing the bigger array
            if i + 1 >= k:
                res.append(nums[bigger[0]])
        return res

(38)295. Find Median from Data Stream(Hard)

中位數是有序整數列表中的中間值。如果列表的大小是偶數,則沒有中間值。所以中位數是兩個中間值的平均值。
設計支持以下兩個操作的數據結構:
void addNum(int num) - 將數據流中的整數添加到數據結構中。
double findMedian() - 返回到目前爲止所有元素的中位數。
在這裏插入圖片描述

思路:
使用二分插入元素
也還可以用一個最大堆 和一個最小堆,保持兩個堆的大小平衡.讓大頂堆保存小的一半的數,小頂堆保存較大的一半數O(log n) + O(1)

from heapq import *


class MedianFinder:
    def __init__(self):
        self.small = []  # the smaller half of the list, max heap (invert min-heap)
        self.large = []  # the larger half of the list, min heap

    def addNum(self, num):
        if len(self.small) == len(self.large):
            #heappushpop向 heap 中加入 item 元素,並返回 heap 中最小元素(之所以是負的因爲要拿出來最大的元素)
            heappush(self.large, -heappushpop(self.small, -num))
        else:
            heappush(self.small, -heappushpop(self.large, num))

    def findMedian(self):
        if len(self.small) == len(self.large):
            return float(self.large[0] - self.small[0]) / 2.0
        else:
            return float(self.large[0])

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

(39)53. Maximum Subarray(Easy)

給定整數數組nums,找到具有最大總和並返回其總和的連續子數組(包含至少一個數字)。
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

思路:
通過後一個加上前一個的和,如果前一個是正的話。

    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        for i in range(1,len(nums)):
            if nums[i-1]>0:
                nums[i] +=nums[i-1]
        return max(nums)

(40)325. Maximum Size Subarray Sum Equals k(Medium)

給定數組nums和目標值k,找到總和爲k的子數組的最大長度。如果沒有,則返回0。
Can you do it in O(n) time?在這裏插入圖片描述

思路:
累積sum,存入字典,已有的話不更新(爲保證最長),並在hashmap中找是否有另一半作差能得到k(subarray和爲k)
Time Complexity: O(N) Space Complexity: O(N)

sum = 0
max_len = 0
dict = {}
dict[0] = -1 #邊界情況,整個數組就是結果
for i in range(len(nums)):
	sum = sum+nums[i]
	if sum - k in dict:
		max_len = max(max_len,i - dict.get(sum-k))
	if sum not in dict:
		dict[sum] = i
return max_len

(41)209. Minimum Size Subarray Sum(Medium)

給定n個正整數和正整數s的數組,找到連續子陣列的最小長度,其總和≥s。如果沒有,則返回0。
在這裏插入圖片描述

思路:
1、使用左右指針
2、如果sum>=s的時候,使用while循環,左邊不斷的加,直到不滿足條件爲止
3、最後如果找不到返回0

    def minSubArrayLen(self, s, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        left = right = 0
        min_length = len(nums)+1
        sum1 = 0
        for right in range(len(nums)):
            sum1 +=nums[right]
            #當加上一個數字後,尋找最小的長度,左邊不斷加,直到sum<s爲止
            while(sum1>=s):
                min_length = min(min_length,right+1-left)
                sum1 -=nums[left]
                left +=1
        return min_length if min_length!=len(nums)+1 else 0

(42)238. Product of Array Except Self(Medium)

給定n個整數的數組n其中n> 1,返回一個數組輸出,使得output [i]等於nums [i]以外的所有nums元素的乘積
Example:
Input: [1,2,3,4]
Output: [24,12,8,6]
注意:請在沒有除法的情況下解決,在O(n)中

思路:
爲了只用到O(n)的時間,O(1)的空間(除結果外),採用先算除了這個數的左邊,再算除了這個樹的右邊

    def productExceptSelf(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        n = len(nums)
        answer = [0]*n
        #left
        answer[0] = 1
        for i in range(1,n):
            answer[i] = nums[i-1]*answer[i-1]
        #right
        R = 1
        for i in range(n-1,-1,-1):
            answer[i] = answer[i]*R
            R *= nums[i]
        return answer

(43)152. Maximum Product Subarray(Medium)

給定整數數組nums,找到具有最大乘積的數組(包含至少一個數字)內的連續子數組
在這裏插入圖片描述

思路:
1、兩邊掃各掃一次

    def maxProduct(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        
        max1 = -2**31
        #正向
        product = 1
        for i in range(len(nums)):
                product *= nums[i]
                max1 = max(max1,product)
                if product==0:
                    product = 1
                    
        #反向            
        product = 1
        for j in range(len(nums)-1,-1,-1):
            product *= nums[j]
            
            max1 = max(max1,product)
            if product == 0:
                product = 1

        return max1

(44)228. Summary Ranges(Medium)

給定一個沒有重複的排序整數數組,返回其範圍的摘要
在這裏插入圖片描述

思路:
常規for循環,注意逗號的用法
ranges += [], 等同於 ranges += [[]]
r[1:] = n, 等同於 r[1:] = [n]

    def summaryRanges(self, nums):
        """
        :type nums: List[int]
        :rtype: List[str]
        """
        ranges = []
        for n in nums:
            if not ranges or n > ranges[-1][-1] + 1:
                ranges += [],
            ranges[-1][1:] = n,
            print(ranges)

        return ['->'.join(map(str, r)) for r in ranges]

(45)163. Missing Ranges(Medium)

給定一個排序的整數數組,其中元素的範圍是[lower,upper]包含,返回其缺失的範圍。 例如,給定[0,1,3,50,75],lower = 0和upper = 99,返回[“2”,“4-> 49”,“51-> 74”,“76-> 99” ]

思路:
1 首先判斷邊界條件nums is None
2 判斷nums[0]是否大於low,大於,則加上區間
3 循環數組,如果nums[i]+1<nums[i+1] 則加上區間
4 判斷nums[-1]是否小於upper,小於,則加上區間

ranges = []
if nums is None:
	ranges +=[low,upper]
else:
	if nums[0]>low:
		ranges +=[low,nums[0]-1]
	for i in range(len(nums)):
		if nums[i]+1<nums[i+1]:
			ranges +=[nums[i]+1,nums[i+1]-1]
	if nums[-1]<upper:
		ranges+=[nums[-1]+1,upper]
return ['->'.join(map(str,r)) if r[0]!=r[1] else str(r[0]) for r in ranges]

sort

(46)88. Merge Sorted Array(Easy)

Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.
在nums1和nums2中初始化的元素數分別爲m和n。 您可以假設nums1有足夠的空間(大小大於或等於m + n)來保存nums2中的其他元素。

思路:
1、用nums2來循環,當num1的值大於i時,插入
2、使用一個m變量來存儲當前數組有多大

    def merge(self, nums1, m, nums2, n):
        """
        :type nums1: List[int]
        :type m: int
        :type nums2: List[int]
        :type n: int
        :rtype: void Do not return anything, modify nums1 in-place instead.
        """
        j = 0
        for i in range(n):
            while j<m and nums1[j]<=nums2[i]:
                j+=1
            nums1.insert(j,nums2[i])
            m+=1
            j+=1
        del nums1[m:]

(47)75. Sort Colors(Medium)

給定一個具有紅色,白色或藍色的n個對象的數組,對它們進行就地排序,使相同顏色的對象相鄰,顏色順序爲紅色,白色和藍色。

def sortColors(self, nums):
    """
    :type nums: List[int]
    :rtype: None Do not return anything, modify nums in-place instead.
    """
    # 使用快排.  [0,i) [i, j) [j, k) are 0s, 1s and 2s
    i =0
    j=0
    for k in range(len(nums)):
        l = nums[k]
        nums[k] =2
        if l<2:
            nums[j] = 1
            j +=1
        if l ==0:
            nums[i] = 0
            i +=1

(48)283. Move Zeroes(Easy)

給定一個數組nums,寫一個函數將所有0移動到它的末尾,同時保持非零元素的相對順序。
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

思路:
1、使用zero變量記錄第一個0,然後使用for循環,當i不是0的時候,和zero互換,然後zero+1

# in-place
def moveZeroes(self, nums):
    zero = 0  # records the position of "0"
    for i in xrange(len(nums)):
        if nums[i] != 0:
            nums[i], nums[zero] = nums[zero], nums[i]
            zero += 1

(49)376. Wiggle Subsequence(Medium)

如果連續數字之間的差異在正數和負數之間嚴格交替,則數字序列稱爲擺動序列。第一個差異(如果存在)可以是正面的也可以是負面的。具有少於兩個元素的序列通常是擺動序列。
例如,[1,7,4,9,2,5]是一個擺動序列,因爲差異(6,-3,5,-7,3)交替爲正和負. 相比之下,[1,4,7,2,5]和[1,7,4,5,5]不是擺動序列,第一個因爲它的前兩個差異是正的而第二個是因爲它的最後差異是零。

給定一個整數序列,返回作爲擺動序列的最長子序列的長度。通過從原始序列中刪除一些元素(最終也爲零)來獲得子序列,其餘元素保持原始順序。
在這裏插入圖片描述
Can you do it in O(n) time?

思路:
由於要尋找擺動序列,尋找最長序列,其實就是尋找拐點,因爲當出現連續正或者負的時候,其實是遞增和遞減的過程,前面的元素對結果沒有影響。需要注意的是相等的連續元素,在拐點判斷中這些元素可以視爲一個元素,遇到相等的連續元素跳過即可。

    def wiggleMaxLength(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        #邊緣情況
        if len(nums)<2:
            return len(nums)
        #建立不同數組之間的差異
        diff  = []
        ans = 1
        for i in range(1,len(nums)):
            x = nums[i] - nums[i-1]
            if x!=0:
                diff.append(x)
        if not diff:
            return 1
        
        for i in range(1,len(diff)):
            if diff[i]*diff[i-1]<0:
                ans +=1
        return ans +1

(50)280 Wiggle Sort(Medium)

Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3]…
For example, given nums = [3, 5, 2, 1, 6, 4], one possible answer is [1, 6, 2, 5, 3, 4].
就是給定一個數組,排序這個數組,使得奇數位大於左邊和右邊,偶數位小於左邊和右邊

思路:
可以分爲偶數和奇數位進行討論,不滿足情況可以直接交換兩個數字

def swapsort(self,nums):
	if len(nums)<2:
		return
	for i in range(len(nums)-1):
		if i%2 ==0:
			if nums[i]>nums[i+1]:
				nums[i],nums[i+1] = nums[i+1],nums[i]
		else:
			if nums[i]<nums[i+1]:
				nums[i],nums[i+1] = nums[i+1],nums[i]

(51)324. Wiggle Sort II(Medium)

給定一個未排序的數組nums,重新排序,使得nums [0] <nums [1]> nums [2] <nums [3] …
Example 1:
Input: nums = [1, 5, 1, 1, 6, 4]
Output: One possible answer is [1, 4, 1, 5, 1, 6]

思路:
方法一: O(nlogn)時間排序+O(n)空間輔助數組解法
給數組進行排序,然後將前面的數插入奇數組中,後面的數插入偶數組中O(nlogn)O(n)

.        arr = sorted(nums)
        for i in range(1, len(nums), 2): nums[i] = arr.pop() 
        for i in range(0, len(nums), 2): nums[i] = arr.pop() 

方法二: O(n)時間複雜度+O(1)空間複雜度解法:(遞歸+快排切分法 和 非遞歸+快排切分法)
這個方法是比較重要的,在求第K小的元素的時候,求中位數的時候都是可以用到的。
啓發來源與快速排序的切分法,比如我們需要找到數組第K小的元素,首先將數組a[low,high]切分返回j,使得a[low,j-1]都小於等於j,而a[j+1,high]都大於等於a[j]。如果j==k,那麼j位置的元素就是我們要找到的第K小的元素。如果j>k,就要切分左子數組。如果j<k就要切分右子數組。不斷地縮小選定的子數組的規模直到剩下一個元素,則它就是最終我們需要找的第k小的元素。
經過數學推導,這種快速切分法尋找中位數僅僅位線性量級。是尋找中位數最爲快速的方法。
有遞歸和非遞歸兩種方式(即遞歸+快排切分法 和 非遞歸+快排切分法)

求中值:
Pos = (len(nums)+1)//2
求最小K值:
Pos = k
求最大K值
Pos = len(nums)+2-k

class Solution(object):
    def wiggleSort(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        #遞歸方案
        self.recursive(nums,0,len(nums)-1,(len(nums)+1)//2)
        #非遞歸方案
        #self.non_recursive(nums,0,len(nums)-1,(len(nums)+1)//2)
        middle = (len(nums)+1)//2
        nums[::2],nums[1::2] = nums[:middle][::-1],nums[middle:][::-1]

        

    # 切分法接口(選定一個數,然後把大於這個數放左邊,小於這個數放右邊,i代表小於這個數的最大index,j代表大於這個數的最小index)    
    def partition(self,nums,start,end):
        mid = (start+end)//2
        pivot = nums[mid]
        i = start -1
        j = end + 1
        k = start
        while k<j:
            if nums[k]<pivot:
                i+=1
                nums[i],nums[k] = nums[k],nums[i]
            elif nums[k]>pivot:
                j-=1
                nums[j],nums[k] = nums[k],nums[j]
                k-=1
            k+=1
        return i,j
    def recursive(self,nums,start,end,pos):
        i,j = self.partition(nums,start,end)
        #i + 1-start 代表左邊的元素共有多少個,如果左邊的元素個數大於pos,那麼中位數在左邊,+1是因爲index
        if i + 1 -start >= pos:
            return self.recursive(nums,start,i,pos)
        #左邊元素小於pos,j-start代表j距離start的個數,如果大於pos,說明既不滿足左邊元素太多,右邊元素太
        #多,所以就是中值
        elif j-start>=pos:
            return nums[j-1]
        #左邊元素小於pos,右邊元素大於pos,中位數在右邊
        else:
            return self.recursive(nums,j,end,pos-(j-start))
        
    
    def non_recursive(self,nums,start,end,pos):
        while True:
            i,j = self.partition(nums,start,end)
        #i + 1-start 代表左邊的元素共有多少個,如果左邊的元素個數大於pos,那麼中位數在左邊,+1是因爲index
            if i + 1 -start >= pos:
                end = i
        #左邊元素小於pos,j-start代表j距離start的個數,如果大於pos,說明既不滿足左邊元素太多,右邊元素太
        #多,所以就是中值
            elif j-start>=pos:
                return nums[j-1]
            #左邊元素小於pos,右邊元素大於pos,中位數在右邊
            else:
                pos -= (j-start)
                start = j
                

String

(52)28. Implement strStr() (Easy)

返回haystack中第一次出現針的索引,如果針不是haystack的一部分,則返回-1。
在這裏插入圖片描述

思路:
思路一:
使用循環

    def strStr(self, haystack, needle):
    """
    :type haystack: str
    :type needle: str
    :rtype: int
    """
    for i in range(len(haystack) - len(needle)+1):
        if haystack[i:i+len(needle)] == needle:
            return i
    return -1

思路二:
經典的KMP算法 時間複雜度O(m+n)
假設現在文本串S匹配到 i 位置,模式串P匹配到 j 位置

  • 如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符;
  • 如果j != -1,且當前字符匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]。此舉意味着失配時,模式串P相對於文本串S向右移動了j - next [j] 位。
  • next 代表當前字符串之前有多大長度的相同前綴後綴,next就是找最大對稱長度的前綴後綴,然後整體右移一位,初值賦爲-1

已知next [0, …, j],如何求出next [j + 1]

  • 若p[k] == p[j],則next[j + 1 ] = next [j] + 1 = k + 1;
  • 若p[k ] ≠ p[j],如果此時p[ next[k] ] == p[j ],則next[ j + 1 ] = next[k] + 1,否則繼續遞歸前綴索引k = next[k],而後重複此過程。
  • 此過程可以看july寫的例子

改進next

  • 當p[j] != s[i] 時,下次匹配必然是p[ next [j]] 跟s[i]匹配,如果p[j] = p[ next[j] ],必然導致後一步匹配失敗(因爲p[j]已經跟s[i]失配,然後你還用跟p[j]等同的值p[next[j]]去跟s[i]匹配,很顯然,必然失配),所以不能允許p[j] = p[ next[j ]]。如果出現了p[j] = p[ next[j] ]咋辦呢?如果出現了,則需要再次遞歸,即令next[j] = next[ next[j] ]。

尋找next的代碼

    def find_next(self,patten):
        next = [0]*len(patten)
        next[0] = -1
        j = 0
        k = -1
        while j<len(patten) - 1:
            # p[k]表示前綴,p[j]表示後綴
            #如果前綴和後綴的字符相同,則next[j] = k
            if (k ==-1 or patten[j] == patten[k]):
                j+=1
                k+=1
                #爲了防止patten[j+1] == patten[k+1],這樣會重複驗證,必定失敗
                if patten[j]!=patten[k]:
                    next[j] = k #未改進之前只需要這一步
                else:
                    next[j] = next[k]
            
            else:
                k = next[k]
        return next

查找P在S中的位置

    def strStr(self, haystack, needle):
        """
        :type haystack: str
        :type needle: str
        :rtype: int
        """
        # 考慮邊界問題
        if len(needle)==0:
            return 0
        #計算next數組
        next = self.find_next(needle)
        i = 0
        j = 0
        while(i<len(haystack) and j<len(needle)):
            #如果j=-1 或者當前字符匹配成功(s[i] == p[j]),則i++,j++
            if (j ==-1 or haystack[i] == needle[j]):
                i +=1
                j +=1
            #如果匹配失敗,則令i不變,j = next[j]
            else:
                j = next[j]
        if j == len(needle):
            return i-j
        else:
            return -1

還有BM算法和Sunday算法,是比KMP算法更快的算法

(53)14. Longest Common Prefix(Easy)

編寫一個函數來查找字符串數組中最長的公共前綴字符串。 如果沒有公共前綴,則返回空字符串“”。

思路:學會使用zip函數

    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        prefix = ""        
        for i in zip(*strs):
            if i.count(i[0])==len(i):
                prefix +=i[0]
            else:
                return prefix
        return prefix

(54)58. Length of Last Word(Easy)

給定字符串s由大寫/小寫字母和空格字符’‘組成,返回字符串中最後一個單詞的長度。
rstrip(’ ') 是指去除末尾的空格
在這裏插入圖片描述

        s = s.rstrip(' ').split(' ')
        if len(s) ==0:
            return 0
        else:
            return len(s[-1])

(55)387. First Unique Character in a String(Easy)

給定一個字符串,找到它中的第一個非重複字符並返回它的索引。如果它不存在,則返回-1。
Examples:
s = “leetcode”
return 0.
s = “loveleetcode”,
return 2.

思路:
使用字典存儲,然後使用s循環,找出字典中爲1的

    def firstUniqChar(self, s):
        """
        :type s: str
        :rtype: int
        """
        dict = {}
        for i in s:
            if i not in dict:
                dict[i] =1
            else:
                dict[i]+=1
        for i,c in enumerate(s):
            if dict[c] ==1:
                return i
        return -1
        # return min([s.index(char) for char in set(s) if s.count(char) == 1] or [-1])

(56)383. Ransom Note

給定一個任意贖金票據字符串和另一個包含所有雜誌字母的字符串,寫一個函數,如果贖金票據可以從雜誌上構建,它將返回true;否則,它將返回false。
雜誌字符串中的每個字母只能在贖金票據中使用一次
在這裏插入圖片描述

    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        for i in set(ransomNote):
            if magazine.count(i)< ransomNote.count(i):
                return False
        return True

(57)344. Reverse String

編寫一個反轉字符串的函數。輸入字符串以char []字符數組的形式給出。 不要爲另一個數組分配額外的空間,必須通過使用O(1)額外內存修改輸入數組來實現此目的
You may assume all the characters consist of printable ascii characters.
在這裏插入圖片描述

思路:
分一半

    def reverseString(self, s):
        """
        :type s: List[str]
        :rtype: None Do not return anything, modify s in-place instead.
        """
        n = len(s)
        for i in range(n//2):
            s[i],s[n-i-1] = s[n-i-1],s[i]

(58)151. Reverse Words in a String(Medium)

給定輸入字符串,逐字反轉字符串。

思路:
方法一:
使用python 的split函數

    def reverseWords(self, s):
        """
        :type s: str
        :rtype: str
        """
        l = [i for i in s.split() if i!=''][::-1]
        return ' '.join(l)

方法二:(爲了不使用[::-1],split())
主要是最前面要判斷一下是空格結尾還是非空格結尾

    def reverseWords(self, s):
        word = ""
        words = ""
        for i in range(len(s)-1,-1,-1):
            if s[i] ==' ' and word!='':
                words += (word+' ')
                word = ''
            elif s[i]!=' ':
                word = s[i]+word
        if  word == '' and len(words)>0 and words[-1]==' ':
            return words[:-1]
        else:
            return words+word

(59)186. Reverse Words in a String II(Medium)

給定輸入字符串,逐字反轉字符串。一個單詞被定義爲一系列非空格字符。 輸入字符串不包含前導或尾隨空格,並且單詞始終由單個空格分隔。
你可以在沒有分配額外空間的情況下就地進行嗎?
For example,
Given s = “the sky is blue”,
return “blue is sky the”.

思路:
由於不存在前面後面的空格,所以無需考慮
可以先反轉整個數組,然後再翻轉每個單詞,寫一個翻轉接口,就不需要用到空間了

class Solution:
    def reverseWords(self,s):
        def reverse(s, start, end):
            while start < end:
                s[start], s[end] = s[end], s[start]
                start += 1
                end -= 1
        if len(s)==0:
            return s
        s = list(s)
        reverse(s,0,len(s)-1)
        start = 0
        end = 0
        while start<len(s):
            end = start
            while end < len(s)-1 and s[end+1]!=' ':
                end +=1
            reverse(s,start,end)
            start = end + 2
        return s
y = Solution()
s = "the sky is blue"
s = y.reverseWords(s)
print(''.join(s))

(60)345. Reverse Vowels of a String(Easy)

編寫一個函數,該函數將字符串作爲輸入,並僅反轉字符串的元音
Example 2:
Input: “leetcode”
Output: “leotcede”

思路:
所有string類型都要轉換爲list
1、給定兩個指針,分別前後判別是否由元音

    def reverseVowels(self, s: 'str') -> 'str':
        vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}
        L = list(s)
        i = 0
        j = len(L)-1
        while i<j:
            while i<j and L[i] not in vowels:
                i +=1
            while j>i and L[j] not in vowels:
                j -=1
            L[i],L[j] = L[j],L[i]
            i +=1
            j -=1
        return ''.join(L)

(61)205. Isomorphic Strings(Easy)

給定兩個字符串s和t,確定它們是否是同構的
如果s中的字符可以替換爲t,則兩個字符串是同構的
在保留字符順序的同時,必須用另一個字符替換所有出現的字符。沒有兩個字符可以映射到相同的字符,但字符可以映射到自身
在這裏插入圖片描述

思路:
1、設立兩個字典分別對應兩個字符串的映射(正向、反向)

    def isIsomorphic(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        d1,d2 = {},{}
        for w1,w2 in zip(s,t):
            if ((w1 in d1) and d1[w1]!=w2) or ((w2 in d2) and d2[w2]!=w1):
                return False
            else:
                d1[w1] = w2
                d2[w2] = w1
        return True

(62)293. Flip Game(Easy)

You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip two consecutive “++” into “–”. The game ends when a person can no longer make a move and therefore the other person will be the winner.Write a function to compute all possible states of the string after one valid move.
在這裏插入圖片描述

思路:
就是把所有反轉一次的可能寫出來

def generatePossibleNextMoves(self,s):
	res = []
	if s:
		for i in range(1,len(s)):
			if s[i-1:i+1] =='++':
				res.append(s[:i-1]+'--'+s[i+1:])
	return res

(63)294.Flip Game II(Medium)

You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip two consecutive “++” into “–”. The game ends when a person can no longer make a move and therefore the other person will be the winner.
Write a function to determine if the starting player can guarantee a win.

input: a string with only + and - two kinds of characters
output: for a given input, based on the game rule, return whether there is one strategy that can make the first player win, if yes, return true, if there is none, return false
corner: when the string if null, return false

思路:
交替True,如果一個人是True,另一個人就是False
使用遞歸搞定

def canWin(self,s):
	for i in range(1,len(s)):
		if s[i-1:i+1] =='++':
			if (!canWin(s[:i-1]+'--'+s[i+1:])):
				return True
	return False

(64)290. Word Pattern(Easy)

Given a pattern and a string str, find if str follows the same pattern.
Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str.
在這裏插入圖片描述

思路:
尋找str和patten相同的格式的
使用字典,記得判斷邊界(兩個數組長度是否一樣)

    def wordPattern(self, pattern: 'str', str: 'str') -> 'bool':
        str = str.split()
        dict1 = {}
        if len(str)!=len(pattern):
            return False
        for i,v in enumerate(pattern):
            if v not in dict1:
                if str[i]  in dict1.values():
                    return False
                dict1[v] = str[i]
            elif dict1[v]!=str[i]:
                return False
        return True

(65)242. Valid Anagram(Easy)

Given two strings s and t , write a function to determine if t is an anagram of s.
您可以假設該字符串僅包含小寫字母。
如果輸入包含unicode字符怎麼辦?您如何使您的解決方案適應這種情況?
在這裏插入圖片描述

思路:
用字典,怎麼用get,看代碼

    def isAnagram(self, s: 'str', t: 'str') -> 'bool':
        map_s = {}
        map_t = {}
        for i in s:
            map_s[i] = map_s.get(i,0)+1
        for i in t:
            map_t[i] = map_t.get(i,0)+1
        return map_s == map_t

(66)49. Group Anagrams(Medium)

給定一個字符串數組,將字謎組合在一起。
Input: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
Output:[[“ate”,“eat”,“tea”],[“nat”,“tan”],[“bat”]]

思路:
1.將字符排序,然後存到字典裏O(NKlogK) (因爲涉及排序)
2.算字符的個數,然後存到字典 O(NK)
字典a.values()

    def groupAnagrams(self, strs):
        """
        :type strs: List[str]
        :rtype: List[List[str]]
        """
        a = collections.defaultdict(list)
        for s in strs:
            count = [0]*26
            for c in s:
                count[ord(c)-ord('a')] +=1            
            a[tuple(count)].append(s)
        return a.values()

(67)249. Group Shifted Strings(Medium)

給定一個字符串,我們可以將其每個字母“移位”到其後續字母,例如:“abc” - >“bcd”。我們可以保持“轉移”形成序列:“abc” -> “bcd” -> … -> “xyz”
給定僅包含小寫字母的字符串列表,將屬於相同移位序列的所有字符串分組。
Input: [“abc”, “bcd”, “acef”, “xyz”, “az”, “ba”, “a”, “z”],
Output:
[
[“abc”,“bcd”,“xyz”],
[“az”,“ba”],
[“acef”],
[“a”,“z”]
]

思路:
1、首先要定義一個key函數,abc的key就是(‘a’ - ‘b’), (‘b’ - ‘c’)
2、然後需要對每一行的結果進行排序

(68) 87. Scramble String(Hard)

判斷一個字符串是否爲另一個字符串“亂序”得到,使用樹結構,通過葉子對調的形式
在這裏插入圖片描述

思路:使用dfs,自然要循環所有從i位置切斷的樹。
一步步縮小,(左邊和左邊相同,右邊和右邊相同) 或者(左邊和右邊相同,右邊和左邊相同)
s[:i]==s[-i:] and s[i:]==s[:-i]

    def isScramble(self, s1, s2):
        """
        :type s1: str
        :type s2: str
        :rtype: bool
        """
        if len(s1)!=len(s2) or sorted(s1)!=sorted(s2):
            return False
        if len(s1)<4 or s1==s2:
            return True
        
        for i in range(1,len(s1)):
            if (self.isScramble(s1[:i],s2[:i]) and self.isScramble(s1[i:],s2[i:])) or (self.isScramble(s1[:i],s2[-i:]) and self.isScramble(s1[i:],s2[:-i])):
                return True
        return False

(69)179. Largest Number(Medium)

Given a list of non negative integers, arrange them such that they form the largest number.
在這裏插入圖片描述

思路:
1、自己定義一個比較函數(因爲需要返回倒序,當a大於b,返回負數),然後用sorted()函數
2、把後面的0給去除掉,lstrip(‘0’) 同時要注意數組元素都爲0的情況

    def largestNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        def compare(a,b):
            return int(b+a) - int(a+b)
        
        nums = sorted([str(i) for i in nums],cmp = compare)
        
        return ''.join(nums).lstrip('0') or '0'

(70)6. ZigZag Conversion (Medium)

The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
在這裏插入圖片描述
And then read line by line: “PAHNAPLSIIGYIR”

思路:
1、記得判斷是上升還是下降

    def convert(self, s, numRows):
        """
        :type s: str
        :type numRows: int
        :rtype: str
        """
        if numRows ==1 or len(s)<=numRows:
            return s
        step =1
        index = 0
        L = [""]*numRows
        for i in s:
            L[index] +=i
            if index ==0:
                step = 1
            elif index==numRows-1:
                step = -1
            index +=step
        return ''.join(L)

(71)161. One Edit Distance

給定兩個字符串s和t,確定它們是否相隔一個編輯距離。(增,刪,替換)

思路:
1、首先判斷兩個長度的差絕對值是不是小於1
2、分三種情況進行討論s>t、s==t、s<t

def isOneEditDistance(self,s,t):
	len_s,len_t = len(s),len(t)
	if abs(len_s-len_t)>1:
		return False
	s,t = list(s),list(t)
	if len_s<len_t:
		for i ,c in enumerate(s):
			if s[i]!=t[i]:
				s.insert(i,t[i])
				return s==t
		return True
	elif len_s == len_t:
		for i ,c in enumerate(s):
			if s[i] !=t[i]:
				s[i] = t[i]
				return s==t
		return True
	else:
		for i ,c in enumerate(t):
			if s[i]!=t[i]:
				t.insert(i,s[i])
				return s==t
		return True

(72)38. Count and Say(Easy)

The count-and-say sequence is the sequence of integers with the first five terms as following:
1
11
21
1211
111221
1 is read off as “one 1” or 11.
11 is read off as “two 1s” or 21.
21 is read off as “one 2, then one 1” or 1211.
Given an integer n where 1 ≤ n ≤ 30, generate the nth term of the count-and-say sequence.

def countStr(self,s):
    count = 0;ans = "";tmp = s[0]
    for i in range(len(s)):
        if s[i] == tmp:
            count += 1
        else:
            ans += str(count) + tmp
            tmp = s[i];count = 1
    ans += str(count) + tmp
    return ans
def countAndSay(self, n):
    """
    :type n: int
    :rtype: str
    """
    ans = '1'
    while n > 1:
        ans = self.countStr(ans)
        n -= 1
    return ans

(73)358 Rearrange String k Distance Apart(Hard)

給定非空字符串s和整數k,重新排列字符串,使得相同的字符至少彼此距離爲k。
所有輸入字符串都以小寫字母給出。如果無法重新排列字符串,則返回空字符串“”。
在這裏插入圖片描述

思路:
1、使用counter統計每個字符出現的次數,然後使用最小堆函數(使用-val來實現最大堆),每次彈出出現次數最多的字符
2、如果剩餘的字符個數不夠K,那麼不滿足,return ‘’
3、每次彈出之後不能直接放入堆中,先放入一個臨時數組,單次操作完畢後再插入堆中
時間複雜度是O(N),空間複雜度是O(N)

#類似於字典的存儲方式
words_count = collections.Counter(words)
#創建堆
que = []
heapq.heapify(que)
#把元素加入堆中
for w,v in words_count.items():
	heapq.heappush(que,(-v,w))
import collections
import heapq
def rearrangeString(words,k):
	n = len(words)
	words_count = collections.Counter(words)
	que = []
	heapq.heapify(que)
	for w,v in words_count.items():
		heapq.heappush(que,(-v,w))
	res = ''
	while que:
		cnt = min(n,k)
		used = []
		for i in range(cnt):
			if not que:
				return ''
			v,w = heapq.heappop(que)
			res +=w
			if v+1!=0:
				used.append((v+1,w))
			n -=1
		for use in used:
			heapq.heappush(que,use)
	return res

(74)316. Remove Duplicate Letters(Hard)

給定一個只包含小寫字母的字符串,刪除重複的字母,這樣每個字母只出現一次。您必須確保在所有可能的結果中,您的結果在字典順序中是最小的。

思路:
1、先把每個字符最大的index給找出來
2、然後循環判斷,如果不在result中,則加入,如果加入的元素字符較小,並且index也較最後一個元素小,就刪除最後一個元素

    def removeDuplicateLetters(self, s):
        """
        :type s: str
        :rtype: str
        """
        # 相當於把每個字符最大的index存儲起來
        rindex = {c: i for i, c in enumerate(s)}
        print(rindex)
        result = ''
        #循環判斷,如果不在result中,則加入,如果加入的元素字符較小,並且index也較小,就刪除最後一個元素
        for i, c in enumerate(s):
            if c not in result:
                print(c,result[-1:],result)#  [-1:] -1後面不加:會溢出
                while c < result[-1:] and i < rindex[result[-1]]:
                    result = result[:-1]
                result += c
        return result

(75)271 Encode and Decode Strings(Medium)(未寫)

(76)168. Excel Sheet Column Title(Easy)

給定正整數,返回Excel工作表中顯示的相應列標題
1 -> A
2 -> B
3 -> C

26 -> Z
27 -> AA
28 -> AB

    def convertToTitle(self, n):
        """
        :type n: int
        :rtype: str
        """
        r = ''
        while(n>0):
            n -=1
            r = chr(n%26+65)+r
            n /=26
        return r

(77)171. Excel Sheet Column Number(Easy)

給定Excel工作表中顯示的列標題,返回其相應的列號。
A -> 1
B -> 2
C -> 3

Z -> 26
AA -> 27
AB -> 28

    def titleToNumber(self, s):
        """
        :type s: str
        :rtype: int
        """
        s = s[::-1]
        sum_s = 0
        for exp,char in enumerate(s):
            sum_s +=(ord(char)-65+1)*(26**exp)
        return sum_s

(78)12. Integer to Roman(Medium)

數字轉羅馬

    def intToRoman(self, num):
        """
        :type num: int
        :rtype: str
        """
        #1~9
        I = ['','I','II','III','IV','V','VI','VII','VIII','IX']
        #10-90
        X = ['','X','XX','XXX','XL','L','LX','LXX','LXXX','XC']
        #100-900
        C = ['','C','CC','CCC','CD','D','DC','DCC','DCCC','CM']
        #1000-3000
        M = ['','M','MM','MMM']
        return M[num/1000]+C[(num%1000)/100]+X[(num%100)/10]+I[(num%10)]

(79)13. Roman to Integer(Easy)

羅馬到數字

思路:
1、先統計出每個羅馬數字的個數
2、然後減去前面放置的個數

    def romanToInt(self, s):
        """
        :type s: str
        :rtype: int
        """
        r = 0
        num_I = s.count('I')
        num_V = s.count('V')
        num_X = s.count('X')
        num_L = s.count('L')
        num_C = s.count('C')
        num_D = s.count('D')
        num_M = s.count('M')
        

        r +=num_I*1
        r +=num_V*5
        r +=num_X*10
        r +=num_L*50
        r +=num_C*100
        r +=num_D*500
        r +=num_M*1000
        if num_I>0:
            r += (s.count('IV')+s.count('IX'))*(-2)
        if num_X>0:
            r += (s.count('XL')+s.count('XC'))*(-20)
        if num_C>0:
            r += (s.count('CD')+s.count('CM'))*(-200)
        
        return r

(80)273. Integer to English Words(Hard)

將非負整數轉換爲其英語單詞表示。鑑於輸入保證小於231 - 1。
Input: 1234567891
Output: “One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One”

思路:
1、定義小於20,十位數,千位數的字符
2、循環判斷,每次除以1000,用於千位數
3、然後定義一個helper

    def __init__(self):
        self.lessThan20 = ["","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen","Sixteen","Seventeen","Eighteen","Nineteen"]
        self.tens = ["","Ten","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"]
        self.thousands = ["","Thousand","Million","Billion"]

    def numberToWords(self, num):
        if num == 0:
            return "Zero"
        res = ""
        for i in range(len(self.thousands)):
            if num % 1000 != 0:
                res = self.helper(num%1000) + self.thousands[i] + " " + res
            num /= 1000
        return res.strip()

    def helper(self, num):
        if num == 0:
            return ""
        elif num < 20:
            return self.lessThan20[num] + " "
        elif num < 100:
            return self.tens[num/10] + " " + self.helper(num%10)
        else:
            return self.lessThan20[num/100] + " Hundred " + self.helper(num%100)
        

(81)246. Strobogrammatic Number(Easy)

頻閃編號是旋轉180度時看起來相同的數字(看着倒置)。 寫一個函數來確定一個數字是否是strobogrammatic。數字表示爲字符串。

思路:
1、建立字典
2、然後倒置,判斷是否相同

d = {'0':'0','1':'1','6':'9','8':'8','9':'6'}
ans = ''
for n in num:
	if n not in d:
		return False
	ans +=d[n]
return ans[::-1] ==num

(82)247. Strobogrammatic Number II(Medium)

頻閃編號是旋轉180度時看起來相同的數字(看着倒置)。 找到所有長度爲n的頻閃編號。
Given n = 2, return [“11”,“69”,“88”,“96”].

思路:
可以像是一層層的給字符串從裏向外穿衣服一樣DFS生成所有的解.
其中翻轉之後和自身相等有0, 1, 8, 在n爲奇數的情況下最裏面的一個數可以爲這三個數的任意一個. 再外邊就一次給兩端添加一個對稱的字符. 如果是最外層的話需要注意不能是爲0.

def __init__(self):
	result =[]
	odd = [‘0’,'1','8']
	even = ['00','11','69','88','96']
def findStrobogrammatic(n):
	if n==0:
		return ['']
	if n==1:
		return self.find(1)
	return [l for l in self.find(n) if l[0]!='0' and n>1]

def find(self,n):
	if n==1:
		return odd
	if n==2:
		return even
	mid = self.find(n-2)
	res = [s[0]+m+s[1] for s in even for m in mid]
	return res

(83)248 Strobogrammatic Number III(Hard)

給定一個範圍,求這個範圍內的頻閃編號
思路:和上一題差不多,只不過多了判斷在不在範圍內

(84)68. Text Justification(Hard)

給定一個單詞數組和一個寬度maxWidth,格式化文本,使每行具有正確的maxWidth字符,並且完全(左和右)對齊。
在這裏插入圖片描述

    #分爲兩種情況,末行和非末行
    #末行,所有單詞中間只有一個空格,後面全部補空格
    #非末行,只有一個單詞,靠左放,其它補空格,
    #多個單詞,計算幾個num和幾個多餘的空格前
    #每個間隔再多方extra/num個,前extra%num個間隔再多放個空格。

n = len(words)
i = 0
res = []
while(i<n):
    j = i+1
    len_words = len(words[i])
    while j<n and len_words+1+len(words[j])<=maxWidth:
        len_words +=1+len(words[j])
        j +=1
    line = words[i]
    if j==n:
        for k in range(i+1,n):
            line +=' ' +words[k]
        while(len(line)<maxWidth):
            line +=' '
    else:
        nums = j-i-1
        if j == i+1:
            while(len(line)<maxWidth):
                line += ' '
        else:
            extraspace = maxWidth -len_words
            for k in range(i+1,j):
                line += ' '
                for l in range((extraspace/nums)):
                    line +=' '
                if (k-i<=extraspace%nums):
                    line +=' '
                line +=words[k]
    res.append(line)
    i =j
for k in res:
    print(len(k))
return res

(85)65.Valid Number(Hard)

判斷是不是合法的數字

使用狀態機來做(num,dot,exp)分別用來表示前面有沒有數字,點,科學記數法e

    def isNumber(self, s):
        """
        :type s: str
        :rtype: bool
        """
        #可以使用狀態機來做 https://blog.csdn.net/weixin_38314447/article/details/79075851
        begin, last = 0,len(s)-1
        #將字符串前後的空格去掉
        while begin<=last and s[begin] == ' ':
            begin +=1
        while begin<=last and s[last] == " ":
            last -=1
            
        #數字前爲正號或者負號的情況,首位後移
        if begin< last and (s[begin]=='+' or s[begin] == '-'):
            begin +=1
        num,dot,exp =False,False,False
        
        while begin<=last:
            # 該字符爲數字
            if s[begin]>='0' and s[begin]<='9':
                num = True
            #若首位爲'.'則返回False, 否則標記爲小數
            elif s[begin]=='.':
                if dot or exp:
                    return False
                dot = True
            #若首位爲'e',則返回False,否則記作科學計數
            elif s[begin] =='e' or s[begin] == 'E':
                if exp or not num:
                    return False
                exp,num = True,False#後面必須要有數字纔行
            #若遇到正負號,則判斷前一位是否爲‘e'
            elif s[begin]=='+' or s[begin]=='-':
                if s[begin-1] !='e':
                    return False
            else:
                return False
            begin +=1
        return num

Sliding Window

(86)76. Minimum Window Substring(Hard)

給定一個字符串S和一個字符串T,找到S中的最小窗口,它將包含複雜度爲O(n)的T中的所有字符。
在這裏插入圖片描述

  1. 先統計t中字符串的數量
  2. 定義left、right、min_len指針,left用來收縮,right用來擴張,min_len用來存儲最小字符串的位置
  3. 定義set(len_t),當dict[s[right]]==0時,set(len_t)-=1
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        len_s = len(s)
        len_t = len(t)

        if len_t>len_s or len_s==0 or len_t ==0:
            return ''

        countT1 = {}
        for i in t:
            if i in countT1:
                countT1[i] +=1
            else:
                countT1[i] =1
        left =0
        min_len = (0,100000)
        len_t = len(set(t))
        for right in range(len(s)):
            if s[right] in countT1:
                countT1[s[right]] -=1
                if countT1[s[right]] ==0:
                    len_t -=1
                if len_t ==0:
                    while True:
                        if s[left] not in countT1:
                            left+=1
                        else:
                            #如果最左邊是t的值的話,則值加一,然後左移一位,退出循環繼續找
                            countT1[s[left]] +=1
                            ##很有可能出現某個單詞次數小於0的情況,直到其中一個大於0時,統計
                            if countT1[s[left]]>0:
                                len_t +=1
                                if (min_len[1]-min_len[0])>right -left:
                                    min_len = (left,right)
                                left +=1
                                break
                            left +=1
        if min_len[1]==100000:
            return ''
        return s[min_len[0]:min_len[1]+1]

(87)30. Substring with Concatenation of All Words(Hard)

您將獲得一個字符串s , 以及一個長度相同的單詞words。在s中查找substring(s)的所有起始索引,它們是單詞中每個單詞的串聯,只有一次,沒有任何插入字符。(單詞可以不按順序)
Input:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are “barfoor” and “foobar” respectively.
The output order does not matter, returning [9,0] is fine too.

思路:
1、先將words存入字典,統計每個單詞的個數
2、for循環,while尋找
3、還需要一個字典存儲已經有的詞

    def findSubstring(self, s, words):
        """
        :type s: str
        :type words: List[str]
        :rtype: List[int]
        """
        numword = len(words)
        if numword ==0 or len(s)==0:
            return []
        dict ={}
        for i in words:
            if i not in dict:
                dict[i] =1
            else:
                dict[i] +=1
        
        j = 0
        lw = len(words[0])
        res =[]
        for i in range(len(s)-lw*numword+1):
            count_word ={}
            j=0
            while (j<numword):
                temp = s[i+j*lw:i+(j+1)*lw]
                if temp not in dict:
                    break
                if temp not in count_word:
                    count_word[temp] = 1
                else:
                    count_word[temp] +=1
                if count_word[temp]>dict[temp]:
                    break
                j+=1
            if j == numword:
                res.append(i)
        return res

(88)3.Longest Substring Without Repeating Characters(Medium)

給定一個字符串,找出其沒有重複字符的最大子序列的長度。

思路:定義max_len、start、substring
循環s,當s[end]不在substring->substring+s[end]
否則:while s[start]!=s[end]: start++

    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        max_len = 0
        start = 0
        substring = ''
        for end in range(len(s)):
            if s[end] in substring:
                max_len = max(max_len,len(substring))
                while s[start] !=s[end]:
                    start +=1
                start +=1
                substring = s[start:end+1]
            else:
                substring +=s[end]
        return max(max_len,len(substring))

(89)340. Longest Substring with At Most K Distinct Characters(Hard)

給定一個字符串,找到包含最多k個不同字符的最長子字符串T的長度。
Example 1:
Input: s = “eceba”, k = 2
Output: 3
Explanation: T is “ece” which its length is 3.
Example 2:
Input: s = “aa”, k = 1
Output: 2
Explanation: T is “aa” which its length is 2.

思路:
使用滑動窗口,然後使用字典進行存儲

def lengthOfLongestSubstringKDistinct(self,s,k):
	if k==0:
		return 0
	result = 0
	dict = {}
	left = 0
	for right in range(len(s)):
		if s[right] not in dict:
			dict[s[right]] = 1
		else:
			dict[s[right]] +=1
		while(len(dict)>k):
			if dict[s[left]] ==1:
				dict.pop(s[left])
			else:
				dict[s[left]] -=1
			left +=1
		result = max(result,right-left+1)
	return result

(90)395. Longest Substring with At Least K Repeating Characters(Medium)

查找給定字符串的最長子字符串T的長度(僅由小寫字母組成),以便T中的每個字符顯示不少於k次。
在這裏插入圖片描述

思路:
如果s中某個字符的數量小於k,那麼這個字符一定不能在字串中。
所以使用遞歸

    def longestSubstring(self, s, k):
        """
        :type s: str
        :type k: int
        :rtype: int
        """
        for c in set(s):
            if s.count(c)<k:
                return max(self.longestSubstring(t,k) for t in s.split(c))
        return len(s)

(91)159 Longest Substring with At Most Two Distinct Characters(Hard)

給定一個字符串,找到包含最多2個不同字符的最長子字符串T的長度。

思路:
就是把340題中的K = 2,其它一樣

String(Palindrome)

(92)125. Valid Palindrome(Easy)

給定一個字符串,確定它是否是迴文,只考慮字母數字字符並忽略大小寫。 注意:出於此問題的目的,我們將空字符串定義爲有效的迴文

思路:
1、要使用字符串的isalnum()判斷是否是字母or數字
2、要將所有字母轉化爲小寫

    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        s = [i.lower() for i in s if i.isalnum()]
        return s ==s[::-1]

(93)266. Palindrome Permutation(Easy)

給定一個字符串,確定字符串的排列是否可以形成迴文
Input: “code”
Output: false
Example 2:
Input: “aab”
Output: true
Example 3:
Input: “carerac”
Output: true

思路:
字符串最多隻有一個奇數的字母放在中間,剩下的必須都是偶數,因此定義一個變量用來判斷有沒有超過兩個奇數的變量

    def canPermutePalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        count = collections.Counter(s)
        c = 0
        for i in count:
        	if count[i]%2 !=0:
        		c +=1
        		if c>1:
        			return False
        return True

(94)5. Longest Palindromic Substring(Medium)

給定一個字符串s,找到s中最長的迴文子字符串。您可以假設s的最大長度爲1000。

思路:
分爲兩種情況1、迴文串是奇數2、迴文串是偶數
跳入一個函數判斷當前 i 前後最大回文

    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        substring = ''
        for i in range(len(s)):
                res = self.findsub(s,i,i)
                if len(res)>len(substring):
                    substring = res
                res = self.findsub(s,i,i+1)
                if len(res)>len(substring):
                    substring = res
        return substring
    
    
    def findsub(self,s,j,k):
        while j>=0 and k<len(s) and s[j] == s[k]:
            j-=1
            k+=1
        return s[j+1:k]

(95)9. Palindrome Number(Easy)

判斷該數字是不是迴文

思路:(兩個特點,x>=0 and (x%10!=0 or x==0))
可以用暴力破解(從左從右判斷),更好的方法是截取一半


           if (x<0 or (x %10 ==0 and x !=0)):
            return False
        
        revertedNumber = 0
        while (x > revertedNumber):
            revertedNumber = revertedNumber * 10 + x%10
            x= int(x/10)
        return x ==revertedNumber or x == int(revertedNumber/10)

(96)214. Shortest Palindrome(Hard)

給定一個字符串s,您可以通過在其前面添加字符將其轉換爲迴文結構。通過執行此轉換,找到並返回您可以找到的最短迴文。
在這裏插入圖片描述

思路:
先搞清楚KMP算法

1、藉助KMP的算法,查看最長的公共長度(第一個值仍然爲0,並且我們只需要最後一個值nex[-1],因爲它表明了rev_s與s相互匹配的最大前綴長度。)
2、首先判斷本身是不是迴文
3、然後將s和r連接生成新的字符串l
4、循環判斷l中最大前綴和後綴
5、最後只需要將rev_s的前k個字符與原始串s拼接即可,其中k是s的長度len(s)與p[-1]之差。
http://bookshadow.com/weblog/2015/05/22/leetcode-shortest-palindrome/

    def shortestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        r = s[::-1]
        if r == s:
            return s
        l = s+'#'+r
        j = 0
        nex = [0]*len(l)
        for i in range(1,len(l)):
            j = nex[i-1]
            while j>0 and l[i]!=l[j]:
                j = nex[j-1]
            nex[i] = j + (l[i]==l[j])
        return r[:len(r)-nex[-1]]+s

(97)336 Palindrome Pairs(Hard)

給定一個唯一單詞列表,找到給定列表中的所有不同索引(i,j)對,以便兩個單詞的串聯 i.e. words[i] + words[j]是一個迴文

思路:
基本思想是檢查每個單詞的前綴(和後綴)本身是迴文。如果您找到一個有效迴文的前綴,那麼反轉的後綴可以與該詞配對以形成迴文。用一個例子可以更好地解釋。

words = [“bot”, “t”, “to”]
從字符串“bot”開始。我們開始檢查所有前綴。如果"", “b”, “bo”, "bot"他們自己是迴文。空字符串和“b”是迴文。我們使用相應的後綴(“bot”,“ot”)並檢查它們的反轉(“tob”,“to”)是否出現在我們的初始單詞列表中。如果是的話(如字“to”),我們已經找到了有效的配對,其中逆轉後綴可以預先考慮到當前單詞,以便形成“to” +“BOT” =“tobot”。
https://leetcode.com/problems/palindrome-pairs/discuss/79209/Accepted-Python-Solution-With-Explanation

    def palindromePairs(self, words):
        """
        :type words: List[str]
        :rtype: List[List[int]]
        """
        word_dict = {}
        res = []
        for i in range(len(words)):
            word_dict[words[i]] = i
        for i in range(len(words)):
            for j in range(len(words[i])+1):
                temp1 = words[i][:j]
                temp2 = words[i][j:]
                if temp1[::-1] in word_dict and word_dict[temp1[::-1]]!=i and temp2[::-1] == temp2:
                    res.append([i,word_dict[temp1[::-1]]])
                if j>0 and temp2[::-1] in word_dict and word_dict[temp2[::-1]]!=i and temp1[::-1]==temp1:
                    res.append([word_dict[temp2[::-1]],i])
        return res

(98)131. Palindrome Partitioning(Medium)

給定字符串s,分區s使得分區的每個子字符串都是迴文。 返回s的所有可能的迴文分區。
在這裏插入圖片描述

思路:
1、使用遞歸
2、每次遞歸前先判斷前面那個字符串是不是迴文

    def dfs(s,path):
        if s=='':
            self.res.append(path)
        for i in range(1,len(s)+1):
            if s[:i]==s[:i][::-1]:
                dfs(s[i:],path + [s[:i]])
    self.res = []
    dfs(s,[])
    return self.res

(99)132. Palindrome Partitioning II(Hard)

給定字符串s,分區s使得分區的每個子字符串都是迴文。 返回s的迴文分區所需的最小割數

思路:
如果使用遞歸,會超時,考慮使用DP算法
用數組dp[i]記錄從第0位到i位最小割數
使用i-1對第i個位置進行初始化,如果子串s[j:i]是迴文串,則dp[i] = min(dp[i],dp[j]+1)

    def minCut(self, s):
        """
        :type s: str
        :rtype: int
        """
        n = len(s)
        dp = range(-1,n)
        for i in range(1, n + 1):
            for j in range(i):
                tmp = s[j:i]
                if tmp == tmp[::-1]:
                    dp[i] = min(dp[i], dp[j] + 1)
        return dp[n]

(100)267. Palindrome Permutation II(Medium)

給定一個字符串s,返回它的所有迴文排列(沒有重複)。如果沒有形成迴文排列,則返回一個空列表。 例如:
Given s = “aabb”, return [“abba”, “baab”].
Given s = “abc”, return [].

https://www.cnblogs.com/grandyang/p/5315227.html
思路:
使用字典存儲每個字符的個數,如果字符個數是奇數超過兩個,直接return []找出奇數個數的字符加入到mid
我們只需生成迴文的前半段就好了,所以每個字符的個數都除以2,獲得t,t就是前半段字符,只需要對其做全排列
還有一種情況需要注意,如果t中有重複字符,需要去重。

import collections
class Solution:
    def generatePalindromes(s):
        #邊界條件
        if len(s) ==0:
            return []
        #res結果
        # counter所有字符統計量,
        # p代表除以2之後的結果,
        # odd存儲奇數的字符
        res = []
        counter = collections.Counter(s)
        p = []
        odd = []
        for i,v in counter.items():
            if v%2:
                odd +=[i]
            p += [i]*(v//2)
        if len(odd)>1:
            return res
        #有重複字符的全排列
        def permutation(nums,path):
            if len(nums) == 0:
                res.append(''.join(path))
                return
            for i in range(len(nums)):
                if i>0 and nums[i-1] == nums[i]:
                    continue
                permutation(nums[:i]+nums[i+1:],path+[nums[i]])
        #使用全排列遞歸,然後判斷是否由奇數字符放在中間
        permutation(p,[])
        if odd:
            res = [i+odd[0]+i[::-1] for i in res]
        else:
            res = [i + i[::-1] for i in res]
        return res

print(Solution.generatePalindromes(''))

string(Parentheses)

(101)20. Valid Parentheses有效括號(Easy)

Given a string containing just the characters ‘(’, ‘)’, ‘{’, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.
An input string is valid if:
Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.
Note that an empty string is also considered valid.
在這裏插入圖片描述

思路:
1、使用壓棧出棧的方法

    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        dict = {')':'(','}':'{',']':'['}
        parentheses = ""
        for i in s:
            if dict.get(i) ==None:
                parentheses +=i
            elif (len(parentheses)!=0) and (parentheses[-1] == dict.get(i)):
                parentheses = parentheses[:-1]    
            else:
                return False
        if parentheses =='':
            return True
        else:
            return False

(102)22. Generate Parentheses(Medium)

給定n對括號,編寫一個函數來生成格式正確的括號的所有組合。

思路:
1、設定兩個變量,代表左邊和右邊的括號數量
2、使用遞歸

    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        def calculate(s,l,r):
            if l==0 and r==0:
                all_s.append(s)
            if l>0:
                calculate(s+'(',l-1,r)
            if l<r:
                calculate(s+')',l,r-1)

        all_s =[]
        if n>0:
            calculate('',n,n)

        return all_s

(103)32. Longest Valid Parentheses(Hard)

給定一個只包含字符’(‘和’)'的字符串,找到最長的有效(格式良好)括號子字符串的長度。

思路:
1、使用DP算法
2、遇到 ‘(’ 不一定合法
3、當i = ’)’ 時:分兩種情況
當 i-1 = ‘(‘時,dp[i] = dp[i-2] +2
當 i-1 = ‘)’ 時,要找到 s[i - 1 - dp[i - 1]] 這個字符,判斷它是否==’(’ 。且要加上dp[i-dp[i-1]-2]上的值

    def longestValidParentheses(self, s):
        """
        :type s: str
        :rtype: int
        """
        if len(s)==0:
            return 0
        dp = [0]*len(s)
        for i in range(len(s)):
            if i-1>=0 and s[i]==')':
                if s[i-1]=='(':
                    if i-2>0:
                        dp[i] = dp[i-2]+2
                    else:
                        dp[i] = 2
                elif s[i-1]==')':
                    if i-dp[i-1]-1>=0 and s[i-dp[i-1]-1]=='(':
                        if i-dp[i-1]-2>=0:
                            dp[i] = dp[i-dp[i-1]-2]+dp[i-1]+2
                        else:
                            dp[i] = dp[i-1]+2
        return max(dp)

(104)241. Different Ways to Add Parentheses(Medium)

給定一系列數字和運算符,通過計算所有不同的組編號和運算符的方式返回所有可能的結果。有效的運算符是+, - 和*。
在這裏插入圖片描述

思路:
使用分治算法
並且使用memory記錄已經算過的了。這樣速度會快很多

    def __init__(self):
        self.memo = {}
    def diffWaysToCompute(self, input):
        """
        :type input: str
        :rtype: List[int]
        """
        if input.isdigit():
            return [int(input)]
        if input in self.memo:
            return self.memo[input]
        res = []
        for i in range(len(input)):
            if input[i] in '-+*':
                r = self.diffWaysToCompute(input[:i])
                l = self.diffWaysToCompute(input[i+1:])
                res.extend([self.helper(j,k,input[i]) for j in r for k in l])
        self.memo[input] = res
        return res
    def helper(self,m,n,op):
        if op == '+':return m+n
        if op == '-':return m-n
        if op == '*':return m*n

(105)301. Remove Invalid Parentheses(Hard)

刪除最小數量的無效括號,以使輸入字符串有效。返回所有可能的結果。 注意:輸入字符串可能包含括號(和)以外的字母。
在這裏插入圖片描述

思路:
從左到右判斷,確保count[’(’]>=count[’)’]

  • ## 如果count[")"]大於count[’(’] 那麼就要循環result中所有元素,並且刪除掉可能的’(’
    ## removed代表已經去掉的元素
    ## 使用循環每次彈出result中的一個結果,並將這個結果中刪掉有可能的‘(’,將結果加入new_result
    ## 再將新的new_resulted賦予給resulted 且 remove +=1

然後從右到左判斷,確保count[’(’]<=count[’)’]
*- ##也是使用同樣的方法,只不過要注意末尾和開頭

     def removeInvalidParentheses(self, s):
        """
        :type s: str
        :rtype: List[str]
        """
        removed = 0
        results = {s}
        count = {"(": 0, ")": 0}
        #從左到右判斷
        for i, c in enumerate(s):
            ## 如果count[")"]大於count['('] 那麼就要循環result中所有元素,並且刪除掉可能的'('
            ## removed代表已經去掉的元素
            ## 使用循環每次彈出result中的一個結果,並將這個結果中刪掉有可能的‘(',將結果加入new_result
            ## 再將新的new_resulted賦予給resulted 且 remove +=1
            if c == ")" and count["("] == count[")"]:
                new_results = set()
                while results:
                    result = results.pop()
                    for j in range(i - removed + 1):
                        if result[j] == ")":
                            new_results.add(result[:j] + result[j + 1:])
                results = new_results
                removed += 1
            else:
                if c in count:
                    count[c] += 1
        count = {"(": 0, ")": 0}
        i = len(s)
        ll = len(s) - removed 
        #removed表明之前已經去掉了的括號,所以現在results中最長是len(s)-removed
        ## ii代表當前運行到哪一步了,i代表S的當前
        for ii in range(ll - 1, -1, -1):
            i-=1
            c = s[i]
            if c == "(" and count["("] == count[")"]:
                new_results = set()
                while results:
                   
                    result = results.pop()
                    for j in range(ii, ll):
                        if result[j] == "(":
                            new_results.add(result[:j] + result[j + 1:])
                results = new_results
                ll -= 1 #因爲又去除了一個,所以再減去一
            else:
                if c in count:
                    count[c] += 1
        return list(results)

(106)392. Is Subsequence(Medium)

給定字符串s和字符串t,檢查s是否是t的子序列。 您可以假設s和t中只有小寫英文字母。 t可能是一個非常長(長度〜= 500,000)的字符串,s是一個短字符串(<= 100)。字符串的子序列是一個新字符串,它是通過刪除一些(可以是無)字符而不干擾其餘字符的相對位置而由原始字符串形成的。
如果有很多傳入的S,比如S1,S2,…,Sk,其中k> = 1B,並且你想逐個檢查以查看T是否有其子序列。在這種情況下,您將如何更改代碼?
在這裏插入圖片描述

思路:
就是S中相對位置不變,是否再t中存在,可以通過
很簡單,遍歷就好了

    def isSubsequence(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if s == '':
            return True
        if t =='':
            return False
        i = 0
        j = 0
        while i<len(s) and j<len(t):
            if s[i]==t[j]:
                i+=1
            j+=1
        return i ==len(s)

(107)115. Distinct Subsequences(Hard)

在S中,能找到幾個T
給定字符串S和字符串T,計算S的不同子序列的數量,其等於T.
Input: S = “rabbbit”, T = “rabbit”
Output: 3
在這裏插入圖片描述

思路:使用動態規劃DP,左邊+(斜角(如果相等的話))res[i][j] = res[i][j-1] + (res[i-1][j-1] if s[j-1]==t[i-1] else 0)

    def numDistinct(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: int
        """
        res = [[0 for _ in range(len(s)+1)] for _ in range(len(t)+1)]
        res[0][0]=1
        for j in range(1,len(s)+1):
            res[0][j] = 1
        
        for i in range(1,len(t)+1):
            for j in range(1,len(s)+1):
                res[i][j] = res[i][j-1] + (res[i-1][j-1] if s[j-1]==t[i-1] else 0)
        print(res)
        return res[len(t)][len(s)]

Math(基礎)

(108)7. Reverse Integer(Easy)

給定32位有符號整數,整數的反向數字。

思路:
1、記得判斷是正的還是負的
2、判斷x是否越界

    def reverse(self, x):
        """
        :type x: int
        :rtype: int
        """
        sign = [1,-1][x<0]
        x = sign*int(str(abs(x))[::-1])
        if x<=(2**31)-1 and x>=-(2**31):
            return x
        else:
            return 0

(109)165. Compare Version Numbers(Medium)

比較兩個版本號version1和version2。 如果version1> version2返回1;如果version1 <version2返回-1;否則返回0。
在這裏插入圖片描述

思路:
1、使用split將數字分開
2、for循環最大的那個,小的用0補
3、比較兩個數字的大小

    def compareVersion(self, version1, version2):
        """
        :type version1: str
        :type version2: str
        :rtype: int
        """
        version1 = version1.split('.')
        version2 = version2.split('.')
        for i in range(max(len(version1),len(version2))):
            ##加int是因爲可能會出現輸入是01和1的情況
            v1 = int(version1[i]) if i<len(version1) else 0
            v2 = int(version2[i]) if i<len(version2) else 0
            if v1<v2:
                return -1
            if v1>v2:
                return 1
        return 0

(110)66. Plus One(Easy)

給定表示非負整數的非空數字數組,加上整數的1。
存儲數字使得最高有效數字位於列表的開頭,並且數組中的每個元素包含單個數字。
您可以假設整數不包含任何前導零,除了數字0本身。
在這裏插入圖片描述

思路:
1、首先判斷末尾是不是9,如果是,則變爲0,i+1
2、如果不是9,則加一,返回
3、如果最後都是9,那就插入1在最前面

    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        i = 1
        while i<=len(digits):
            if digits[-i] ==9:
                digits[-i] =0
                i+=1
            else:
                digits[-i] +=1
                return digits
        digits.insert(0,1)
        return digits

(111)8. String to Integer (atoi) (Medium)

string 到 int
1、前面有n個空格 2、除空格外第一個必須爲數字或者 - 或者 + 3、只輸出數字 4、滿足- 231~ 231-1

思路:
按照規格做就好,沒什麼難度

    def myAtoi(self, str):
        """
        :type str: str
        :rtype: int
        """
        ls = list(str.strip())
        if len(ls) == 0 : return 0
        
        sign = -1 if ls[0] == '-' else 1
        if ls[0] in ['-','+'] : del ls[0]
        ret, i = 0, 0
        while i < len(ls) and ls[i].isdigit() :
            ret = ret*10 + ord(ls[i]) - ord('0')
            i += 1
        return max(-2**31, min(sign * ret,2**31-1))

(112)258. Add Digits(Easy)

給定非負整數num,重複添加其所有數字,直到結果只有一位數。
你可以在O(1)運行時沒有任何循環/遞歸嗎?
在這裏插入圖片描述

思路:
思路一:
可以使用迭代+循環的方法
思路二:
N=(a[0] * 1 + a[1] * 10 + …a[n] * 10 n),令M = a[0] + a[1] + …a[n]
因爲
1 % 9 = 1

10 % 9 = 1

100 % 9 = 1
所以 N % 9 = (a[0] + a[1] + …a[n])%9 = M%9 = (b[0] + b[1] + …b[n])%9 = O%9
又因爲
9%9 = 0 爲了防止這個問題的出現,我們可以(n-1)%9 +1

    def addDigits(self, num: 'int') -> 'int':
        if num == 0:
            return 0
        else:
            return (num-1)%9+1

(113)67. Add Binary(Easy)

給定兩個二進制字符串,返回它們的總和(也是二進制字符串)。
在這裏插入圖片描述
思路:
1、先將a,b加在一起轉化爲str
2、倒過來,判斷是不是大於2,如果大於,則carry =1
3、循環完,記得判斷carry是不是大於0,大於,則要在最前面加1

    def addBinary(self, a, b):
        """
        :type a: str
        :type b: str
        :rtype: str
        """
        a = int(a)
        b = int(b)
        c = str(a+b)
        d = ''
        carry = 0
        print(c)
        for i in reversed(c):
            num = int(i)+carry
            print(num)
            if num>=2:
                # print(num)
                num -=2
                carry =1
            else:
                carry = 0
            d = str(num)+d
        if carry ==1:
            d = '1'+d
            
        return d

(114)43. Multiply Strings(Medium)

兩個用字符串表示的非負數字,用字符串的方式返回他們的乘積。
Input: num1 = “123”, num2 = “456”
Output: “56088”

思路:
1、定義一個長度爲len(num1)+len(num2),值爲0的list
2、用嵌套for循環,注意進位可能大於10(進位包括前面運算出來的),要再用% and /
3、把s前面的0都去掉,最後把s join一下

    def multiply(self, num1, num2):
        """
        :type num1: str
        :type num2: str
        :rtype: str
        """
        if num1=='0' or num2=='0':
            return '0'
        s =[0]*(len(num1)+len(num2))
        for i in range(len(num2)-1,-1,-1):
            add = 0
            for j in range(len(num1)-1,-1,-1):
                a = int(num2[i])*int(num1[j])+add
                add = a/10 + (a%10+s[i+j+1])/10
                s[i+j+1] = (a%10 +s[i+j+1])%10
            s[i] = add
        for i in range(len(s)):
            if s[i] !=0:
                s = s[i:]
                break
        return ''.join(str(i) for i in s)

(115)29. Divide Two Integers(Medium)

給定兩個整數除數和被除數,除去兩個整數而不使用乘法,除法和mod運算符。 將除數除以除數後返回商。 整數除法應截斷爲零。

思路:
1、先判斷除數和被除數時候有其中一個爲負(((dividend^divisor)>>31)
2、利用左移操作來實現出發過程。將一個數左移等於將一個數×2,取一個tmp = divisor,所以將除數tmp不斷左移,直到其大於被除數dividend,然後得到dividend - tmp,重複這個過程。
3、返回 return max(-res,-2147483648) if ((dividend^divisor)>>31) else min(res,2147483647)

        if abs(dividend) < abs(divisor):
            return 0

        ans = 0
        while dividend >= divisor:
            cnt = 1
            tmp = divisor

            while tmp << 2 < dividend:
                cnt <<= 2 
                tmp <<= 2
            ans += cnt
            dividend -= tmp
        return max(-res,-2147483648) if ((dividend^divisor)>>31) else min(res,2147483647)

(116)69. Sqrt(x)(Easy)

計算並返回x的平方根,其中x保證爲非負整數。 由於返回類型是整數,因此將截斷十進制數字,並僅返回結果的整數部分。

思路:
使用二分法來找,使用middle的平方來找

 (x/2+1)^2>x
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        #because (x/2+1)^2>x
        min_x =0
        max_x = int((x/2) + 1)
        while (max_x-min_x>=0):
            middle  = int((min_x+max_x)/2)
            if middle**2 == x:
                return middle
            elif middle**2 <x:
                min_x = middle+1
            else:
                max_x = middle-1
        return max_x

(117)50. Pow(x, n)(Medium)

自己實現一個pow

思路:
使用連乘的方式會超時,所以使用右移的方式

        m = abs(n)
        ans = 1.0
        while m:
            if m &1:
                ans *=x
            x *=x
            m >>=1
        return ans if n>0 else 1/ans

(118)367. Valid Perfect Square(Easy)

給定正整數num,寫一個函數,如果num是一個完美的正方形,則返回True,否則返回False。 注意:不要使用任何內置庫函數,例如sqrt。

思路一:
使用二分法查找

    def isPerfectSquare(self, num):
        """
        :type num: int
        :rtype: bool
        """
        if num<1:
            return False
        if num ==1:
            return True
        start = 0
        end = num
        while end>=start:
            mid = int((start+end)/2)
            if mid*mid>num:
                end = mid-1
            elif mid*mid<num:
                start = mid+1
            else:
                return True
        return False

思路二:
使用牛頓法
牛頓法本身是一階算法,本質是求根算法。但如果用來求最優解(極值點),這時就要求導
數爲 0 的跟,就需要求二階導數,就變成了二階算法。
在這裏插入圖片描述

class Solution(object):
	def mySqrt(self,x):
		result = 1.0
		while abs(result*result - x)>0.1:
			result = (result+x/result)/2
		return int(result)

(119)365. Water and Jug Problem(Medium)

你有兩個容量爲x和y升的水壺。有無限量的供水可供選擇。您需要確定是否可以使用這兩個水罐精確測量z升。
如果z升水是可測量的,那麼在一端或兩個桶中必須包含z升水
允許的操作:
用水完全填充任何水壺。
清空任何水壺。
將水從一個水壺倒入另一個水壺,直到另一個水壺完全充滿或第一個水壺本身爲空。

思路:
我們需要找到(m,n)使得 z = m*x + n*y
那麼這兩個(m,n)存在嗎?
根據貝祖定理我們總是可以找到mx +ny = b,b = gcd(x,y) gcd means 最大公因數
如果z%b ==0,那麼 m 和n就存在

    def canMeasureWater(self, x, y, z):
        """
        :type x: int
        :type y: int
        :type z: int
        :rtype: bool
        """
        return z ==0 or (x+y>=z and z%self.gcd(x,y)==0)
    
    def gcd(self,x,y):
        return x if y==0 else self.gcd(y,x%y)

(120)204. Count Primes(Easy)

計算小於非負數n的質數的數量。
Input: 10
Output: 4
Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.

思路:
使用循環判斷
1、如果s[i]是質數,那麼從i*i之後每間隔i都不是質數,因爲可以該數可以整除質數i
2、範圍就是0~n0.5

    def countPrimes(self, n):
        """
        :type n: int
        :rtype: int
        """
        s = [1]*n
        if n<3:
            return 0
        s[0] = s[1] = 0
        for i in range(2,int(n**0.5)+1):
            if s[i]==1:
                s[i*i:n:i] = [0]*len(s[i*i:n:i])
        return sum(s)

Math(SUM)

(121)1.Two Sum(easy)

給定一個整型數組,找出能相加起來等於一個特定目標數字的兩個數。
給定一個整數數組,返回兩個數字的索引,使它們相加到特定目標。 您可以假設每個輸入只有一個解決方案,並且您可能不會兩次使用相同的元素。

思路:建立一個字典,存儲結果,然後在字典裏面找

    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        dict = {}
        for i in range(len(nums)):
            complement = target - nums[i]
            if (dict.get(complement)!=None):
                return [dict.get(complement),i]
            else:
                dict[nums[i]] = i
        return None

(122)167. Two Sum II - Input array is sorted(Easy)

給定已按升序排序的整數數組,找到兩個數字,使它們相加到特定的目標數。 函數twoSum應返回兩個數字的索引,以便它們相加到目標,其中index1必須小於index2
您返回的答案(index1和index2)不是從零開始的。 您可以假設每個輸入只有一個解決方案,並且您可能不會兩次使用相同的元素
在這裏插入圖片描述

思路:
使用兩個指針判斷就好了

    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        l = 0
        r= len(numbers)-1
        while(l<r):
            if numbers[l]+numbers[r]==target:
                return l+1,r+1
            elif numbers[l]+numbers[r]>target:
                r -=1
            else:
                l +=1
        return None

(123)15. 3Sum(Medium)

給定n個整數的數組nums,是否有元素a,b,c在nums中,a + b + c = 0?找到數組中所有唯一的三元組,它們的總和爲零。

思路:
1、首先給list排序(重點!!)
2、按順序,如果list[i]>0,直接退出(因爲排過序了)
3、如果list[i]==list[i-1],continue
4、定義b =i+1 ;e=len(list)。然後b++,e- -
5、b 和 e 也不能相同,如果用過了(因爲題目要求唯一三元組)

    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        all = []
        for i in range(len(nums)-2):
            if nums[i]>0:
                break
            if i !=0 and nums[i]==nums[i-1]:
                continue
            b = i +1
            e = len(nums)-1
            while b<e:
                sum = nums[b]+nums[e]+nums[i]
                if sum>0:
                    e -=1
                elif sum<0:
                    b +=1
                else:
                    all.append([nums[i],nums[b],nums[e]])
                    while b<e and nums[b]==nums[b+1]:
                        b +=1
                    while b<e and nums[e]==nums[e-1]:
                        e -=1
                    b +=1
                    e -=1
        return all

(124)16. 3Sum Closest

給定n個整數和整數目標的數組nums,在nums中找到三個整數,使得總和最接近目標。返回三個整數的總和。您可以假設每個輸入都只有一個解決方案。

思路:
1、和15題解題方案有點像,只不過只需要一個輸出

    def threeSumClosest(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        out = nums[0]+nums[1]+nums[2]
        nums.sort()
        for i in range(len(nums)-2):
            b = i+1
            e = len(nums)-1
            while b<e:
                sum = nums[i]+nums[b]+nums[e]
                if abs(sum-target)<abs(out-target):
                    out = sum
                if sum-target>0:
                    e -=1
                elif sum-target<0:
                    b +=1
                else:
                    return out
        return out

(125)18. 4Sum(Medium)

給定n個整數和整數目標的數組nums,是否有元素a,b,c和d在nums中,a + b + c + d = target?找到數組中所有唯一的四元組,它們給出了目標的總和。

思路:
和3sum差不多,只不過多了一層循環

    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        nums.sort()
        all =[]
        for i in range(len(nums)-3):
            if i>0 and nums[i-1]==nums[i]:
                continue
            for j in range(i+1,len(nums)-2):
                if j!=i+1 and nums[j-1]==nums[j]:
                    continue
                b =j+1
                e = len(nums)-1
                while b<e:
                    sum = nums[i]+nums[j]+nums[e]+nums[b]
                    if sum>target:
                        e -=1
                    elif sum<target:
                        b +=1
                    else:
                        all.append([nums[i],nums[j],nums[b],nums[e]])
                        while b<e and nums[b]==nums[b+1]:
                            
                            b +=1
                        while b<e  and nums[e]==nums[e-1]:
                            e -=1
                        b+=1
                        e-=1
        return all

Tree(基礎)

(126)144 and 145. Binary Tree Postorder Traversal(Hard)

給定二叉樹,返回其節點值的前、後序遍歷。(使用迭代的方法)
在這裏插入圖片描述

思路:
前序遍歷:1.根節點 2.左子樹 3.右子樹
中序遍歷:1.左子樹 2.根節點 3.右子樹
後序遍歷:1.左子樹 2.右子樹 3.根節點
有個前序、中序、後序的迭代操作(只是加入的順序不一樣,後序就差不多是前序的反向操作)
1、定義兩個list,一個存儲結果,一個存儲訪問過的結點
2、後序,在最前面插入結點(中間、右邊、左邊)(相當於前序反過來操作,因爲在最前面插入)

    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        result = []
        stack = []
        p = root
        while(p!=None or stack!=[]):
            if p!=None:
                result.insert(0,p.val)
                stack.append(p)
                p = p.right
            else:
                p = stack.pop()
                p = p.left
        return result
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        cur = root
        result = []
        stack = []
        while cur !=None or stack!=[]:
            if cur!=None:
                result.append(cur.val)
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                cur = cur.right
        return result

(127)94. Binary Tree Inorder Traversal(Medium)

樹的中序遍歷

思路:
遞歸很容易實現
使用遍歷的方法左-》中-》右

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

(128)102. Binary Tree Level Order Traversal(Medium)

給定二叉樹,返回其節點值的級別順序遍歷。 (即,從左到右,逐級)。
在這裏插入圖片描述

思路:使用兩個數組,其中一個數組存儲當前層次的節點,另一個數組存儲下一個層次的節點

    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if root ==None:
            return []
        res = [root]
        ans = []
        while res:
            temp = []
            ans.append([i.val for i in res])
            for node in res:            
                temp.append(node.left)
                temp.append(node.right)
            res = [i for i in temp if i]
        return ans

Tree(Preorder)

(129)100. Same Tree(Easy)

給定兩個二叉樹,編寫一個函數來檢查它們是否相同。 如果兩個二叉樹在結構上相同並且節點具有相同的值,則認爲它們是相同的。

思路:
均使用遞歸來判斷

    def isSameTree(self, p, q):
        """
        :type p: TreeNode
        :type q: TreeNode
        :rtype: bool
        """
        if not p and not q:
            return True
        if not p or not q:
            return False
        if p.val !=q.val:
            return False
        return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

(130)101. Symmetric Tree(Easy)

給定二叉樹,檢查它是否是自身的鏡像(即,圍繞其中心對稱)。
在這裏插入圖片描述

思路:
1、使用遞歸
2、每次判斷左子樹和右子樹是不是對稱的,再判斷左子樹和右子樹的值是否相同

    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        def check(r1,r2):
            if not r1 and not r2:
                return True
            if not r1 or not r2:
                return False
            a1= check(r1.left,r2.right)
            a2 = check(r1.right,r2.left)
            
            return a1 and a2 and (r1.val==r2.val)
        
        
        return check(root.left,root.right)

(131)226. Invert Binary Tree(Easy)

反轉二叉樹

思路:
使用遞歸,分別反轉左節點和右節點的子集,然後左節點和右節點交換

    def invertTree(self, root):
        """
        :type root: TreeNode
        :rtype: TreeNode
        """
        if root ==None:
            return root
        left = right = None
        if root.left !=None:
            left = self.invertTree(root.left)
        if root.right != None:
            right = self.invertTree(root.right)
        root.left = right
        root.right = left
        return root

(132)257. Binary Tree Paths(Easy)

Given a binary tree, return all root-to-leaf paths.
Note: A leaf is a node with no children.
在這裏插入圖片描述

思路:
使用遞歸,分別訪問左節點和右節點,如果遞歸之後是葉子結點,就加入路徑中

    def dfs(self,l,root,s):
        if root.left ==None and root.right==None:
            l.append(s)
            return;
        if root.left !=None:
            self.dfs(l,root.left,s+'->'+str(root.left.val))
        if root.right !=None:
            self.dfs(l,root.right,s+'->'+str(root.right.val))
    def binaryTreePaths(self, root: 'TreeNode') -> 'List[str]':
        l =[]
        if root==None:
            return l
        self.dfs(l,root,str(root.val))
        return l

(133)112. Path Sum(Easy)

給定二叉樹和求和,確定樹是否具有根到葉路徑,使得沿路徑的所有值相加等於給定的總和。

思路:
1、使用遞歸

    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if root ==None:
            return False
        if (root.left==root.right==None):
            return sum == root.val
        else:
            return self.hasPathSum(root.left,sum-root.val) or self.hasPathSum(root.right,sum-root.val)

(134)113. Path Sum II(Medium)

給定一個二叉樹的總和,發現其中每個路徑的總和等於給定的款項的所有根到葉的路徑。

思路:使用遞歸,難度不大,就是陷阱比較多,注意幾個point

  1. 一定要葉節點爲結尾
  2. 節點的值可能有負數,不能碰到和爲0時,退出
    def pathSum(self, root, sum):
    	res = []
        def dfs(sum,root,path):
            if not root:
                return
            elif sum - root.val ==0:
                if not root.left and not root.right:
                    res.append(path+[root.val])
                    return
            dfs(sum-root.val,root.left,path+[root.val])
            dfs(sum-root.val,root.right,path+[root.val])
        dfs(sum,root,[])
        return res

(135)129. Sum Root to Leaf Numbers(Medium)

Input: [1,2,3]
1
/
2 3
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.
找到所有根到葉數的總和。

使用遞歸

    def sumNumbers(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        def dfs(root,sum_n):
            if root:
                dfs(root.left,sum_n*10+root.val)
                dfs(root.right,sum_n*10+root.val)
                if not root.left and not root.right:
                    self.res += sum_n*10+root.val
        self.res = 0
        dfs(root,0)
        return self.res

(136)298 Binary Tree Longest Consecutive Sequence(Medium)

給定二叉樹,找到最長連續序列路徑的長度。
該路徑指的是沿着父子連接從樹中的某個起始節點到任何節點的任何節點序列。最長的連續路徑需要從父到孩子(不能反過來)

思路:
最長連續子序列必須從root到leaf的方向,比如 1->2,返回長度2, 比如1->3->4->5,返回3->4->5這個子序列的長度3。
使用遞歸,遞歸的時候傳入父親節點的值,還要傳入一個變量用於記錄當前長度

self.max_len = 0
def longestConsecutive(self,root):
	self.dfs(root,0,0)
	return self.max_len
def dfs(self,root,last_value,cur_len):
	if root:
		if root.val == last_value+1:
			cur_len +=1
		else:
			cur_len =1
		max_len = max(max_len,cur_len)
		self.dfs(root.left,root.val,cur_len)
		self.dfs(root.right,root.val,cur_len)

(137)111. Minimum Depth of Binary Tree(Easy)

給定二叉樹,找到它的最小深度。
在這裏插入圖片描述

思路:
1、使用遞歸
2、左邊和右邊比最小

    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        

        def D(root,minmum):
            if not root:
                return 2**31 -1
            if root.left ==root.right==None:
                return minmum
            lm = D(root.left,minmum+1)
            rm = D(root.right,minmum+1)

            return min(lm,rm)
        if root ==None:
            return 0
        return D(root,1)
        

Tree(Postorder)

(138)104. Maximum Depth of Binary Tree(Easy)

給定二叉樹,找到它的最大深度。
在這裏插入圖片描述

思路:
使用遞歸方法

def maxDepth(self, root):
    """
    :type root: TreeNode
    :rtype: int
    """
    if root is None:
        return 0
    left = self.maxDepth(root.left)
    right = self.maxDepth(root.right)
    return max(left,right)+1

(139)110. Balanced Binary Tree(Easy)

給定二叉樹,確定它是否是高度平衡的。

思路:
1、使用DFS,在傳遞的時候,要加上height
2、如果right、left其中一個是False,就返回False。
3、如果right、left相差大於1,返回False

    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """   
        if root == None:
            return True
        
        def B(root,height):
            if not root:
                return height-1
            if root.left == root.right ==None:
                return height
            
            lheight = B(root.left,height+1) 
            rheight = B(root.right,height+1)
            if rheight==False or lheight==False:
                return False
            if abs(lheight-rheight)>1:
                return False
            return max(lheight,rheight)
        return True if B(root,1) else False

(140)124. Binary Tree Maximum Path Sum(Hard)

給定非空二叉樹,找到最大路徑總和。
對於此問題,路徑定義爲沿着父子連接從樹中的某個起始節點到任何節點的任何節點序列。該路徑必須至少包含一個節點,並且不需要通過根節點。在這裏插入圖片描述

思路:
1、使用遞歸,需要設置一個全局變量,用來隨時更新最大值(這個最大值是根節點加上左右節點)
2、返回當前節點加上左右其中一個最大值。(這個最大值是根節點,加上左右其中一個)
3、注意:全局變量剛開始不能設爲0,因爲可能出現所有節點都爲負的情況,然後題目要求至少要輸出一個節點

    def maxPathSum(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        def dfs(root):
            if not root:
                return 0
            left = dfs(root.left)
            right = dfs(root.right)
            self.max = max(self.max,root.val+left+right)
            return max(0,root.val+max(left,right))
        ##self.max 不能設爲0,因爲可能只有root.val = -3的情況
        self.max = None
        dfs(root)
        return self.max

(141)250 Count Univalue Subtrees(Medium)

給定二叉樹,計算唯一子樹的數量
唯一子樹:子樹裏面每個值都一樣

思路:
是一個自下往上的遞歸的過程。之所以是自下往上,是因爲只要一顆子樹不是univalue的,那麼任何包含這顆子樹的樹都不是univalue的。同樣的,uni value本身也是具備傳遞性的,也就是說如果某一個子樹是univalue的,那麼它的父親節點只需要和這個子樹的根節點進行比較就可以了,因爲整個樹的值都是一樣的。

所以整個算法基本就是,先往左右要結果,滿足以下條件的爲univalue subtree

  1. 左子樹爲空或者左子樹爲univalue subtree並且當前節點和左子樹根節點值相同
  2. 右子樹爲空或者右子樹爲univalue subtree並且當前節點和左子樹根節點值相同
    空節點我們也可以認爲是一個univalue subtree。
    當一個子樹被判定爲univalue subtree的時候就在計數器加一即可。
def countUnivalSubtrees(self,root):
	self.result = 0
	self.dfs(root)
	return self.result
def dfs(self,root):
	if not root:
		return True
	else:
		left = self.dfs(root.left)
		right = self.dfs(root.right)
		if (left and (root.left == None or root.left.val == root.val)) and right and (root.right==None or root.right.val==root.val)):
			self.result +=1

(142)366. Find Leaves of Binary Tree(Medium)

給定一個二叉樹,收集樹的節點,就好像你這樣做:收集並刪除所有葉子,重複直到樹爲空
在這裏插入圖片描述

思路:
每一個節點從左子節點和右子節點分開走可以得到兩個深度,由於成爲葉節點的條件是左右子節點都爲空,所以我們取左右子節點中較大值加1爲當前節點的深度值,知道了深度值就可以將節點值加入到結果res中的正確位置了

時間複雜度分析:每個結點只訪問一次 O(n)

def findLeaves(self,root):
	self.list = []
	self.dfs(root)
def dfs(self,root):
	if root:
		left = self.dfs(root.left)
		right = self.dfs(root.right)
		k = max(left,right)+1
		if len(self.list) <=k:
			self.list.append([])
		self.list[k].append(root)
		return k
	return -1

(143)337 House Robber III(Medium)

小偷又發現了自己盜竊的新地方。這個區域只有一個入口,稱爲“根”。除了根,每個房子只有一個或一個父母的房子。巡迴演出後,聰明的小偷意識到“這個地方的所有房屋都形成了一棵二叉樹”。如果兩個直接連接的房屋在同一個晚上被闖入,它將自動聯繫警方。
確定小偷今晚可以搶劫的最大金額而不警告警察。

思路:
返回現在最大的,和上一次最大的
now:如果當前結點被盜,掙到的最大money
later:如果當前結點沒有被盜,掙到的最大money
now = node.val + left[1] +right [1]
later = max(left)+max(right)

    def rob(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        return max(self.rob_dfs(root))
    def rob_dfs(self,root):
        if not root:
            return [0,0]
        left = self.rob_dfs(root.left)
        right = self.rob_dfs(root.right)
        now = root.val + left[1] + right[1]
        later = max(left)+max(right)
        return [now,later]

Tree(BFS)

(144)107. Binary Tree Level Order Traversal II(Easy)

給定二叉樹,返回其節點值的自下而上級別順序遍歷。 (即,從左到右,逐層逐層)。

思路:
和102一樣,只不過最後把數組倒過來就好了

    def levelOrderBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if root ==None:
            return []
        res = [root]
        ans = []
        while res:
            temp = []
            ans.append([i.val for i in res])
            for node in res:            
                temp.append(node.left)
                temp.append(node.right)
            res = [i for i in temp if i]
        return ans[::-1]

(145)103. Binary Tree Zigzag Level Order Traversal(Medium)

給定二叉樹,返回其節點值的Z字形級別遍歷。 (即,從左到右,然後從右到左進行下一級別並在之間交替)。
在這裏插入圖片描述

思路:102題的思想,然後增加一個變量flag=1 每次循環一遍flag*=-1

    def zigzagLevelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
        res = [root]
        ans = []
        flag = 1
        while res:
            ans.append([i.val for i in res][::flag])
            temp = []
            
            for node in res:
                temp.append(node.left)
                temp.append(node.right)
            res = [i for i in temp if i]
            flag *=-1
        return ans

(146)199. Binary Tree Right Side View(Medium)

給定一個二叉樹,想象自己站在它的右側,返回從上到下排序的節點的值。
在這裏插入圖片描述

思路:
使用循環操作,選取最後一個

    def rightSideView(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        res = [root]
        ans = []
        while(res):
            ans.append(res[-1].val)
            res = [k for node in res for k in (node.left,node.right) if k ]
        return ans

Tree(BST)

(147)98. Validate Binary Search Tree(Medium)

Given a binary tree, determine if it is a valid binary search tree (BST).

思路:
使用遞歸,怎麼設定這個子樹的最大最小值是關鍵(要均在最大最小值之間)。對於沒有最大最小值用None來表示

    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """  
        if not root:
            return True

        def dfs(root,lower,upper):
            if lower !=None and root.val<= lower:
                return False
            if upper !=None and root.val>=upper:
                return False
            
            left = dfs(root.left,lower,root.val) if root.left else True
            if (left):
                right = dfs(root.right,root.val,upper) if root.right else True
                return right
            else:
                return False
        return dfs(root,None,None)

(148)235. Lowest Common Ancestor of a Binary Search Tree(Easy)

給定二叉搜索樹(BST),找到BST中兩個給定節點的最低共同祖先(LCA)。
根據維基百科上LCA的定義:“最低共同祖先在兩個節點p和q之間定義爲T中的最低節點,其中p和q都是後代(我們允許節點成爲其自身的後代)。 ”
在這裏插入圖片描述

思路:
可以使用遞歸和迭代兩種方式
1、如果兩個結點都小於當前結點,走左邊
2、如果兩個結點都大於當前結點,走右邊
3、否則輸出當前結點

        #Recursive Approach space O(n) Time O(n) 
        # if p.val > root.val and q.val>root.val:
        #     return self.lowestCommonAncestor(root.right,p,q)
        # elif p.val<root.val and q.val<root.val:
        #     return self.lowestCommonAncestor(root.left,p,q)
        # else:
        #     return root
        
        #iterative
        while root:
            if p.val>root.val and q.val>root.val:
                root = root.right
            if p.val<root.val and q.val<root.val:
                root = root.left
            else :
                return root
            

(149)236. Lowest Common Ancestor of a Binary Tree(Medium)

給定二叉樹,找到樹中兩個給定節點的最低共同祖先(LCA)。
根據維基百科上LCA的定義:“最低共同祖先在兩個節點p和q之間定義爲T中的最低節點,其中p和q都是後代(我們允許節點成爲其自身的後代)。 ”
沒有做小右大得規矩了

思路:
使用遞歸,如果左右兩個結點+中間節點有大於等於兩個,那就是該節點

class Solution:

    def __init__(self):
        # Variable to store LCA node.
        self.ans = None

    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        def recurse_tree(current_node):

            # If reached the end of a branch, return False.
            if not current_node:
                return False

            # Left Recursion
            left = recurse_tree(current_node.left)

            # Right Recursion
            right = recurse_tree(current_node.right)

            # If the current node is one of p or q
            mid = current_node == p or current_node == q

            # If any two of the three flags left, right or mid become True.
            if mid + left + right >= 2:
                self.ans = current_node

            # Return True if either of the three bool values is True.
            return mid or left or right

        # Traverse the tree
        recurse_tree(root)
        return self.ans

(150)108. Convert Sorted Array to Binary Search Tree(Easy)

給定一個數組,其中元素按升序排序,將其轉換爲高度平衡的BST。

思路:
1、當len(nums)<=2時,賦值返回
2、使用二分法來建立樹

    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        if len(nums)==0:
            return None
        elif len(nums)==1:
            return TreeNode(nums[0])
        elif len(nums)==2:
            root = TreeNode(nums[0])
            root.right = TreeNode(nums[1])
        else:
            middle = int((len(nums)-1)/2)
            root = TreeNode(nums[middle])
            root.left = self.sortedArrayToBST(nums[:middle])
            root.right = self.sortedArrayToBST(nums[middle+1:])
        return root

(151)109. Convert Sorted List to Binary Search Tree(Medium)

給定單個鏈接列表,其中元素按升序排序,將其轉換爲高度平衡的BST。

思路:
1、先將鏈表轉化爲數組
2、然後採用150題的思路

    def sortedListToBST(self, head):
        """
        :type head: ListNode
        :rtype: TreeNode
        """
        array = []
        while head:
            array.append(head.val)
            head = head.next

        def dfs(nums):
            n = len(nums)
            if n ==0:
                return None
            if n ==1:
                return TreeNode(nums[0])
            if n ==2:
                root =  TreeNode(nums[0])
                root.right = TreeNode(nums[1])
                return root
            middle = (n-1)//2
            root = TreeNode(nums[middle])
            root.left = dfs(nums[:middle])
            root.right = dfs(nums[middle+1:])
            return root
        return dfs(array)

(152)173. Binary Search Tree Iterator(Medium)

在二叉搜索樹(BST)上實現迭代器。您的迭代器將使用BST的根節點進行初始化。 調用next()將返回BST中的下一個最小數字。
在這裏插入圖片描述

思路:
1、要加一個函數讀取當前最小
2、使用stack進行存儲
3、先把左邊的加入,然後左邊沒有了再加入右邊

class BSTIterator(object):

    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.stack = []
        self.addmin(root)
        

    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        tempNode = self.stack.pop()
        self.addmin(tempNode.right)
        return tempNode.val

    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return self.stack
    
    def addmin(self,node):
        while node is not None:
            self.stack.append(node)
            node = node.left

(153)230. Kth Smallest Element in a BST(Medium)

給定二叉搜索樹,編寫函數kthSmallest以找到其中的第k個最小元素。 注意: 您可以假設k始終有效,1≤k≤BST的總元素。
在這裏插入圖片描述

思路:
可是使用遞歸,也可以使用迭代

    def kthSmallest(self, root, k):
        """
        :type root: TreeNode
        :type k: int
        :rtype: int
        """
        stack = []
        while True:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            k -=1
            if not k:
                return root.val
            root = root.right

(154)297. Serialize and Deserialize Binary Tree(Hard)

序列化是將數據結構或對象轉換爲位序列的過程,以便將其存儲在文件或內存緩衝區中或者通過網絡連接鏈路傳輸,以便稍後在相同或另一個計算機環境中重建。
設計一種算法來序列化和反序列化二叉樹。序列化/反序列化算法的工作方式沒有限制,您只需要確保二進制樹可以序列化爲字符串,並且可以將此字符串反序列化爲原始樹結構
在這裏插入圖片描述
澄清:以上格式與LeetCode序列化二叉樹的方式相同。您不一定需要遵循這種格式,因此請發揮創意並自己提出不同的方法。
注意:不要使用類成員/全局/靜態變量來存儲狀態。您的序列化和反序列化算法應該是無狀態的。

思路:
就是個解碼編碼得問題
使用遞歸

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        def re_se(node):
            if node:
                res.append(node.val)
                re_se(node.left)
                re_se(node.right)
            else:
                res.append('#')
        res = []
        re_se(root)
        return res

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        def re_de():
            r = next(res)
            if r =='#':
                return None
            node = TreeNode(r)
            node.left = re_de()
            node.right = re_de()
            return node
        res = iter(data)
        return re_de()
        

(155)285. Inorder Successor in BST(Medium)

給定二叉搜索樹及其中的節點,在BST中找到該節點的有序後繼。 節點p的後繼者是具有大於p.val的最小密鑰的節點。
在這裏插入圖片描述

思路:
返回某個節點中序的下一個節點

def inorderSuccessor(self,root,p):
	if root and p:
		ans = None
		while root:
			if root.val>p.val:
				ans = root
				root = root.left
			else:
				root = root.right
		return ans
	return None

(156)270: Closest Binary Search Tree Value(Easy)

給定非空二進制搜索樹和目標值,找到BST中最接近目標的值。
注意: 給定目標值是浮點數。 保證BST中只有一個最接近目標的唯一值。

思路:
先定義一個最小值和靠近目標的點
然後使用循環和BST的特性尋找該最小值

def closestValue(self,root,target):
	if not root:
		return None
	ans = abs(root.val - target)
	ans_value = root.val
	while True:
		if abs(target - root.val)<ans:
			ans = abs(target - root.val)
			ans_value = root.val
		if target<root.val and root.left:
			root = root.left
		elif target>root.val and root.right:
			root = root.right
		else:
			break
	return ans_value

(157)272. Closest Binary Search Tree Value II(Medium)

給定非空二進制搜索樹和目標值,在BST中找到最接近目標的k值。
注意: 給定目標值是浮點數。 您可以假設k始終有效,即:k≤總節點數。 保證BST中只有一組唯一的k值最接近目標。
跟進: 假設BST是平衡的,你能在低於O(n)運行時間(其中n =總節點)解決它嗎?
在這裏插入圖片描述

思路:
中序遍歷,如果數組不到K個,直接加入res,中序遍歷代表數據是從小到大的
如果節點與目標值的差值絕對值小於結果res首元素與目標值之差的絕對值,則刪除首元素,然後加入末位
否則直接退出

def closestKValues(root,target,k):
	res = []
	s = []
	while(root or s):
		while root:
			s.insert(0,root)
			root = root.left
		root = s.pop()
		if len(res)<k:
			res.append(root.val)
		elif abs(root.val-target) <abs(res[0]-target):
			res = res[1:]
			res.append(root.val)
		else:
			break
		root = root.right
	return res

(158)99. Recover Binary Search Tree(Hard)

Two elements of a binary search tree (BST) are swapped by mistake.Recover the tree without changing its structure.

思路:
使用中序遍歷遞歸(是遞增序列),用pre來存儲中序遍歷的前一個變量(全局的)總共有兩種情況:
1,相鄰的換(只出現一個逆序對)只需將前後兩個值記下,然後進行交換
2,不相鄰的換(出現兩個逆序對)需要記下第一次的第一個,和第二次的第二個值,然後交換

    def recoverTree(self, root):
        """
        :type root: TreeNode
        :rtype: None Do not return anything, modify root in-place instead.
        """
        self.first=None
        self.second = None
        self.pre = None
        
        def dfs(root):
            if root ==None:
                return
            dfs(root.left)
            if self.pre and self.pre.val>root.val:
                if self.first ==None:
                    self.first,self.second = self.pre,root
                else:
                    self.second = root
            self.pre = root
            dfs(root.right)
        dfs(root)
        self.first.val,self.second.val = self.second.val,self.first.val

Tree(重要)

(159)116. Populating Next Right Pointers in Each Node(Medium)

您將獲得一個完美的二叉樹,其中所有葉子都在同一級別,每個父級都有兩個孩子。填充每個下一個指針以指向其下一個右側節點。如果沒有下一個右節點,則下一個指針應設置爲NULL。 最初,所有下一個指針都設置爲NULL
在這裏插入圖片描述

思路:

    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root:
            return root
        pre = root
        cur = None
        while(pre.left):
            cur = pre
            while(cur):
                cur.left.next = cur.right
                if cur.next:
                    cur.right.next = cur.next.left
                cur = cur.next
            pre = pre.left
        return root

(160)117.Populating Next Right Pointers in Each Node II(Medium)

在這裏插入圖片描述

思路:
和116一樣,只不過要多加一個判斷,下一行的最左邊的元素

    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        pre = root
        cur = []
        while(pre):
            #加一個判斷,判斷最左邊的元素
            cur = pre
            now = None
            while not now and cur:
                if cur.left !=None:
                    now = cur.left
                elif cur.right !=None:
                    now = cur.right
                else:
                    cur = cur.next
            pre = now
            while cur:
                if cur.left and cur.left !=now:
                    now.next = cur.left
                    now = cur.left
                if cur.right and cur.right !=now:
                    now.next = cur.right
                    now = cur.right
                cur = cur.next
        return root

(161)314. Binary Tree Vertical Order Traversal(Medium)

給定二叉樹,返回其節點值的垂直順序遍歷。 (即,從上到下,逐列)。 如果兩個節點位於同一行和列中,則順序應爲從左到右。
在這裏插入圖片描述

思路:
使用BFS+字典,設置根節點爲0,左節點列爲-1,右節點爲1,然後使用廣度優先搜索。

def vertivalOrder(self,root):
	if not root:
		return []
	cols = collections.defaultdict(list)
	q = [(root,0)]
	while q:
		new_q = []
		for node,col in q:
			cols[col].append(node.val)
			if node.left:
				new_q.append((node.left,col-1))
			if node.right:
				new_q.append((node.right,col+1))
		q = new_q
	return [cols[col] for col in sorted(cols.keys())]
	

(162)96. Unique Binary Search Trees(Medium)

Given n, how many structurally unique BST’s (binary search trees) that store values 1 … n?
在這裏插入圖片描述

思路:
使用dp算法

//由1,2,3,...,n構建的二叉查找樹,以i爲根節點,左子樹由[1,i-1]構成,其右子樹由[i+1,n]構成。
//定義f(i)爲以[1,i]能產生的Unique Binary Search Tree的數目
//若數組爲空,則只有一種BST,即空樹,f(0)=1;
//若數組僅有一個元素1,則只有一種BST,單個節點,f(1)=1;
//若數組有兩個元素1,2,則有兩種可能,f(2)=f(0)*f(1)+f(1)*f(0);
//若數組有三個元素1,2,3,則有f(3)=f(0)*f(2)+f(1)*f(1)+f(2)*f(0)
//由此可以得到遞推公式:f(i)=f(0)*f(i-1)+...+f(k-1)*f(i-k)+...+f(i-1)*f(0)

代碼:

    def numTrees(self, n):
        """
        :type n: int
        :rtype: int
        """
        res = [0 for _ in range(n+1)]
        res[0] =1
        res[1] =1
        for i in range(2,n+1):
            for j in range(0,i):
                res[i] += res[j]*res[i-j-1]
        return res[n]

Backtracking(基礎)

(163)78. Subsets(Medium)

給定一組不同的整數,nums,返回所有可能的子集(冪集)。

使用遞歸,還無需判斷條件

    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def dfs(nums,path,begin):
            res.append(path)
            for i in range(begin,len(nums)):
                dfs(nums,path+[nums[i]],i+1)
        res = []
        dfs(nums,[],0)
        return res

(164)90.Subsets II(Medium)

給定可能包含重複項,nums的整數集合,返回所有可能的子集(冪集)。

思路:
1、使用遞歸,當出現有重複元素,使用判斷if k>i and nums[k]==nums[k-1]
2、記得要對元素進行排序

    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []
        nums.sort()
        def dfs(i, path):
            res.append(path)
            for k in range(i,len(nums)):
                if k>i and nums[k-1]==nums[k]:
                    continue
                dfs(k+1,path +[nums[k]])
                
        dfs(0,[])
        return res

(165)77. Combinations(Medium)

Given two integers n and k, return all possible combinations of k numbers out of 1 … n.

利用遞歸

    def combine(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: List[List[int]]
        """
        res = []
        def dfs(begin,end,k,path):
            if len(path)==k:
                res.append(path)
                return
            for i in range(begin,end):
                dfs(i+1,end,k,path+[i])
        dfs(1,n+1,k,[])
        return res

(166)39. Combination Sum(Medium)

給定一組候選數字(候選者)(沒有重複)和目標數量(目標),找到候選人數總和目標的候選人中的所有獨特組合。
可以從候選者無限次數中選擇相同的重複數字。
注意: 所有數字(包括目標)都是正整數。 解決方案集不得包含重複的組合。
在這裏插入圖片描述

思路:
1、使用DFS
2、記得排序

def combinationSum(self, candidates, target):
    """
    :type candidates: List[int]
    :type target: int
    :rtype: List[List[int]]
    """
    def dfs(target,path,k):
        if target<0:
            return
        if target==0:
            res.append(path)
            return
        for i in range(k,len(candidates)):
            dfs(target-candidates[i],path+[candidates[i]],i)
            if target-candidates[i]<=0:
                break
    res = []
    candidates.sort()
    dfs(target,[],0)
    return res

(167)40. Combination Sum II(Medium)

給定候選數字(候選者)和目標數量(目標)的集合,找到候選人數量總和爲目標的候選人中的所有獨特組合。
候選人中的每個號碼只能在組合中使用一次。 注意: 所有數字(包括目標)都是正整數。 解決方案集不得包含重複的組合。
在這裏插入圖片描述

思路:要判斷當i>k時 並且candidates[i-1] == candidates[i],要退出
其它和39題類似

    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        def dfs(path,target,k):
            if target<0:
                return
            if target==0:
                # if path not in res:
                res.append(path)
                return
            for i in range(k,len(candidates)):
                if i>k and candidates[i-1] == candidates[i]:
                    continue
                dfs(path+[candidates[i]],target-candidates[i],i+1)
                if target-candidates[i]<=0:
                    break
        
        res = []
        candidates.sort()
        dfs([],target,0)
        
        return res

(168)216. Combination Sum III(Medium)

找到所有可能的k個數字組合,它們加起來爲n,假設只能使用1到9的數字,並且每個組合應該是一組唯一的數字。
注意: 所有數字都是正整數。 解決方案集不得包含重複的組合。
在這裏插入圖片描述

思路:
使用遞歸,使用減法操作,就無需用sum了

    def combinationSum3(self, k, n):
        """
        :type k: int
        :type n: int
        :rtype: List[List[int]]
        """
        def dfs(begin,end,k,n,path):
                if k == 0:
                    if n ==0:
                        res.append(path)
                    else:
                        return
                for i in range(begin,end):
                    dfs(i+1,end,k-1,n-i,path+[i])
        res = []
        dfs(1,min(n+1,10),k,n,[])
        return res              

(169)377. Combination Sum IV(Medium)

給定具有所有正數且沒有重複的整數數組,找到加到正整數目標的可能組合的數量。
在這裏插入圖片描述

思路:
dp[i]表示目標數爲i的解的個數,從1遍歷到target,對於每一個數i,遍歷nums數組,如果i>=x, dp[i] += dp[i - x]。比如說對於[1,2,3] 4,這個例子,當我們在計算dp[3]的時候,3可以拆分爲1+x,而x即爲dp[2],3也可以拆分爲2+x,此時x爲dp[1],3同樣可以拆爲3+x,此時x爲dp[0],我們把所有的情況加起來就是組成3的所有情況了。

    def combinationSum4(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        dp = [0]*(target+1)
        dp[0] = 1
        for i in range(1,target+1):
            for a in nums:
                if i>=a:
                    dp[i] += dp[i-a]
        return dp[target]

(170)254. Factor Combinations(Medium)

數字可以被視爲factor的產物。例如,8 = 2 x 2 x 2 = 2 x 4.
編寫一個取整數n的函數並返回其factor的所有可能組合。
每個組合的因子必須按升序排序,例如:2和6的因子是[2,6],而不是[6,2]。 你可以假設n總是正的。 因素應大於1且小於n。
在這裏插入圖片描述

思路:
使用遞歸,如果n==1,並且len(path)>1,則加入
如果n%i ==0 則輸入i,int(n/i),path+[i]

class Solution:
    def getfactor(self,n):
        self.res = []
        self.helper(2,n,[])
        return self.res
    def helper(self,start,n,path):
        if n ==1:
            if len(path)>1:
                self.res.append(path)
                return
        for i in range(start,n+1):
            if n%i == 0:
                self.helper(i,int(n/i),path+[i])

(171)46. Permutations(Medium)

全排列問題
Given a collection of distinct integers, return all possible permutations.
在這裏插入圖片描述

思路:使用遞歸dfs,沒啥難度

    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def dfs(nums,path):
            if len(nums)==0:
                res.append(path)
            for i in range(len(nums)):
                dfs(nums[:i]+nums[i+1:],path+[nums[i]])
        res = []
        dfs(nums,[])
        return res

(172)47. Permutations II(Medium)

全排列問題,加上了有重複數字

思路:首先先排序,使用遞歸之後,加上判斷,如果後一個與前一個相同,則跳過

    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def dfs(nums,path):
            if len(nums)==0:
                res.append(path)
            for i in range(len(nums)):
                if i>0 and nums[i]==nums[i-1]:
                    continue
                dfs(nums[:i]+nums[i+1:],path+[nums[i]])
        nums.sort()
        res = []
        dfs(nums,[])
        return res

(173)31. Next Permutation(Medium)

產生下一個序列,對給定序列進行重排,生成一個字母順序比它更大的下一個序列。
如果給定的序列已經是按字母順序排列中最大的一個了,則進行逆序排列。
算法在數組內進行,不要使用額外空間。

思路:
i = len -2 j = len-1
1、從後往前遍歷,i 找到第一個不滿足升序的元素;如果都是升序,則i爲-1(從後往前看的升序)
2、當 j>i 時,找到一個 j 的值大於 i 的,然後和 i 交換
3、將 i 之後的元素進行排序操作

    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        if not nums: return nums
        l = len(nums)
        i, j = l - 2, l - 1
        while i >= 0 and nums[i] >= nums[i+1]:
            i -= 1
        while j > i and nums[j] <= nums[i]:
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
        nums[i+1:] = sorted(nums[i+1:])

(174)60. Permutation Sequence(Medium)

Given n and k, return the kth permutation sequence.

思路:
在這裏插入圖片描述
使用了除法,還是需要使用int,不然就是float類型。//也不行

    def getPermutation(self, n, k):
        nums = [str(i) for i in range(1, n + 1)]
        k -= 1
        factor = 1
        res = []
        # 計算排列
        for i in range(1, n):
            factor *= i
        for i in reversed(range(n)):
            res.append(nums[int(k / factor)])
            nums.remove(nums[int(k / factor)])
            if i != 0:
                k %= factor
                factor /= i
        return ''.join(res)

(175)291. Word Pattern II 詞語模式 II(Medium)

Given a pattern and a string str, find if str follows the same pattern.
Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty substring in str.
Examples:
pattern = “abab”, str = “redblueredblue” should return true.
pattern = “aaaa”, str = “asdasdasdasd” should return true.
pattern = “aabb”, str = “xyzabcxzyabc” should return false.

思路:
可以用回溯法來判斷每一種情況,用哈希表建立模式字符和單詞之間的映射,還需要用變量p和r來記錄當前遞歸到的模式字符和單詞串的位置,在遞歸函數中,如果p和r分別等於模式字符串和單詞字符串的長度,說明此時匹配成功結束了,返回ture,反之如果一個達到了而另一個沒有,說明匹配失敗了,返回false。
1、判斷時候都達到了末尾
2、如果沒有達到末尾,判斷patten[i]有沒有在字典中,如果有,則判斷詞與str中下一個詞是否匹配
3、如果patten[i]不在字典中,則循環str

def wordpatternMatch(self,pattern,str):
	w2p,p2w = {},{}
	return self.match(pattern,str,0,0,w2p,p2w)

def match(self,pattern,str,i,j,w2p,p2w):
	is_match = False
	if i==len(pattern) and j==len(str)
		is_match = True
	elif i<len(pattern) and j<len(str):
		p = pattern[i]
		if p in p2w:
			w = p2w[p]
			if w == str[j:j+len(w)]:
				is match = self.match(pattern,str,i+1,j+len(w),w2p,p2w)
		else:
			for k in range(j,len(str)):
				w = str[j:k+1]
				if w not in w2p:
					w2p[w],p2w[p] = p,w
					is_match = self.match(pattern,str,i+1,k+1,w2p,p2w)
					w2p.pop(w),p2w.pop(p)
				if is_match:
					break
	return is_match

(176)17. Letter Combinations of a Phone Number(Medium)

給定包含2-9(含)的數字的字符串,返回該數字可能表示的所有可能的字母組合。(不包含1)
在這裏插入圖片描述

思路:
1、使用遞歸
2、將當前的和之後生成的進行組合

    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
        mapping = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', 
           '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
        if len(digits)==0:
            return []
        if len(digits) ==1:
            return list(mapping[digits[0]])
        l = self.letterCombinations(digits[:-1])
        
        return [a+c for a in l for c in mapping[digits[-1]]]

(177)320. Generalized Abbreviation(Medium)

Write a function to generate the generalized abbreviations of a word.
編寫一個函數來生成一個單詞的通用縮寫。
Example:
Given word = “word”, return the following list (order does not matter):
[“word”, “1ord”, “w1rd”, “wo1d”, “wor1”, “2rd”, “w2d”, “wo2”, “1o1d”, “1or1”, “w1r1”, “1o2”, “2r1”, “3d”, “w3”, “4”]

思路:
使用遞歸
分兩種情況,一種數字,一種字符,數字需要判斷,前面是不是數字

def GenerateAbbreviations(word):
	def backtracking(word,cur_num,cur_string,is_numberbefore):
		if cur_num == len(word):
			if is_numberbefore ==0:
				res.append(cur_string)
			else:
				res.append(cur_string+str(is_numberbefore))
		else:
			if is_numberbefore>0:
				backtracking(word,cur_num+1,cur_string+str(is_numberbefore)+word[cur_num],0)
			else:
				backtracking(word,cur_num+1,cur_string+word[cur_num],0)
			backtracking(word,cur_num+1,cur_string,is_numberbefore+1)
	res = []
	backtracking(word,0,'',0)
	return res
	

(178)93. Restore IP Addresses(Medium)(很少考)

給定僅包含數字的字符串,通過返回所有可能的有效IP地址組合來恢復它。
在這裏插入圖片描述
思路:
1、使用遞歸,不斷的縮小範圍
2、當point*3<len(s) 不符合。當len(s)==0 and num_point !=0 不符合
3、當字符大於1的時候,要判斷首位不爲0,當字符大於2時,要判斷數值不可大於255

        def dfs(s,num_point,path):
            if len(s)==0:
                if num_point ==0:
                    res.append(path[:-1])
                return
            elif num_point*3 <len(s):
                return
            dfs(s[1:],num_point-1,path +s[0]+'.')
            if 1<len(s) and s[0]!='0':
                dfs(s[2:],num_point-1,path+s[:2]+'.')
            if 2<len(s) and s[0]!='0':
                if int(s[:3])<=255:
                    dfs(s[3:],num_point-1,path +s[:3]+'.')
                else:
                    return
        res =[]
        dfs(s,4,'')
        return res

(179)282. Expression Add Operators(Hard)

給定一個只包含數字0-9和目標值的字符串,返回所有可能性,在數字之間添加二元運算符(非一元)+, - 或*,以便它們計算目標值。
在這裏插入圖片描述

思路:
1、首先乘法運算符較高,所以需要知道上一個值是什麼,用last進行記錄
2、可以多個數字連續在一起,但是注意‘00’等不允許出現,因爲不是操作數的格式
3、遞歸的時候總共有4種情況,不添加操作符、+、-、*
在這裏插入圖片描述

    def addOperators(self, num, target):
        """
        :type num: str
        :type target: int
        :rtype: List[str]
        """
        res, self.target = [], target
        for i in range(1,len(num)+1):
            if i == 1 or (i > 1 and num[0] != "0"): # prevent "00*" as a number
                self.dfs(num[i:], num[:i], int(num[:i]), int(num[:i]), res) # this step put first number in the string
        return res

    def dfs(self, num, temp, cur, last, res):
        if not num:
            if cur == self.target:
                res.append(temp)
            return
        for i in range(1, len(num)+1):
            val = num[:i]
            if i == 1 or (i > 1 and num[0] != "0"): # prevent "00*" as a number
                self.dfs(num[i:], temp + "+" + val, cur+int(val), int(val), res)
                self.dfs(num[i:], temp + "-" + val, cur-int(val), -int(val), res)
                self.dfs(num[i:], temp + "*" + val, cur-last+last*int(val), last*int(val), res)

(180)140. Word Break II(Hard)

給了一個字典,問給定的字符串s能有多少種被字典構造出來的方式,返回每一種構造方式。
在這裏插入圖片描述

思路:
1、使用DFS + DP
2、使用一個字典,記錄已經切分過的數組,不必要再次重切
3、接下來使用wordDict來循環,判斷是不是

    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        #dfs + dp
        # 使用一個數組記住前面已經切分的
        res = []
        momery = {}
        
        def dfs(s):
            if s in momery:
                return momery[s]
            if s =='':
                return ['']
            res = []
            for word in wordDict:
                if s[:len(word)] == word:
                    for r in dfs(s[len(word):]):
                        res.append(word +('' if not r else ' '+r))
            momery[s] = res
            return res
        return dfs(s)

(181)351 Android Unlock Patterns

給定Android 3x3密鑰鎖定屏幕和兩個整數m和n,其中1≤m≤n≤9,計算Android鎖定屏幕的解鎖模式總數,其包括最少m個密鑰和最多n個密鑰。
Rules for a valid pattern:

  • Each pattern must connect at least m keys and at most n keys.
  • All the keys must be distinct.
  • If the line connecting two consecutive keys in the pattern passes through any other keys, the other keys must have previously selected in the pattern. No jumps through non selected key is allowed.
  • The order of keys used matters.
    在這裏插入圖片描述

思路:
1、首先記錄,哪些兩個數字之間有中間鍵的,建立一個數組
2、使用visited記錄已經訪問的數字
3、如果有中間鍵,並且中間鍵沒有被訪問,就不進入遞歸
4、1,3,7,9是對稱的,所以我們乘4即可,然後再對數字2調用遞歸函數,2,4,6,8也是對稱的,再乘4,最後單獨對5調用一次,然後把所有的加起來就是最終結果了

def numberOfPattern(m,n):
	def helper(num,len,ans,visited):
		if len >=m:
			ans+=1
		if len>n:
			return ans
		visited.append(num)
		for next in range(1,10):
			if next not in visited:
				if num*10+next not in dict or dict[num*10+next] in visited:
					ans = helper(next,len,ans,visited)
			visited = visited[:-1]
		return ans
	res = 0
	dict[13] = dict[31] = 2
	dict[17] = dict[71] = 4
	dict[79] = dict[97] = 8
	dict[39] = dict[93] = 6
	dict[28] = dict[82] = 5
	dict[46] = dict[64] = 5
	dict[19] = dict[91] = dict[37] = dict[73] = 5
	res +=helper(1,1,0,[])*4
	res +=helper(2,1,0,[])*4
	res +=helper(5,1,0,[])
	return res

Dynamic Programming(一維)

(182)70. Climbing Stairs(Easy)

你正在爬樓梯。它需要n步才能達到頂峯。 每次你可以爬1或2步。您可以通過多少不同的方式登頂?

思路:f(1)=1 f(2)=2
1、假如第一次跳一級臺階,剩下還有n-1級臺階,有f(n-1)種跳法
2、假如第一次條2級臺階,剩下n-2級臺階,有f(n-2)種跳法。
f(n)=f(n-1)+f(n-2)

    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n<=1:
            return 1
        res = [1,1]
        for i in range(n-1):
            sum = res[-1]+res[-2]
            res.append(sum)
        return res[-1]

(183)62. Unique Paths(Medium)

一個人從矩陣的左頂點走到右底點有幾種方式

思路:使用dfs方式會超時,使用DP

dp[i][j] = dp[i-1][j] +dp[i][j-1]
        dp = [[1 for _ in range(n)] for _ in range(m)]
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j] = dp[i-1][j]+dp[i][j-1]
        return dp[-1][-1]

(184)63. Unique Paths II(Medium)

62 題,然後再加上障礙物

思路:首先先用兩個循環,橫向和豎向走一遍,遇到1,則邊0。後一個根據前一個來
然後再用 dp[i][j] = dp[i-1][j] +dp[i][j-1]

        if obstacleGrid[0][0] ==1:
            return 0
        obstacleGrid[0][0] = 1
        for i in range(1,len(obstacleGrid)):
            if obstacleGrid[i][0]!=0:
                obstacleGrid[i][0] =0
            else:
                obstacleGrid[i][0] = obstacleGrid[i-1][0]
        for j in range(1,len(obstacleGrid[0])):
            if obstacleGrid[0][j]!=0:
                obstacleGrid[0][j] =0
            else:
                obstacleGrid[0][j] =obstacleGrid[0][j-1]
                
            
        for i in range(1,len(obstacleGrid)):
            for j in range(1,len(obstacleGrid[0])):
                if obstacleGrid[i][j] ==0:
                    obstacleGrid[i][j] = obstacleGrid[i-1][j]+obstacleGrid[i][j-1]
                else:
                    obstacleGrid[i][j] = 0
        return obstacleGrid[-1][-1]

(185)64. Minimum Path Sum(Medium)

與63題相似
給定m x n網格填充非負數,找到從左上到右下的路徑,最小化了沿其路徑的所有數字的總和。

思路相同

    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        for i in range(1,len(grid)):
            grid[i][0] += grid[i-1][0]
        for j in range(1,len(grid[0])):
            grid[0][j] +=grid[0][j-1]
        
        for i in range(1,len(grid)):
            for j in range(1,len(grid[0])):
                grid[i][j] +=min(grid[i-1][j],grid[i][j-1])
        return grid[-1][-1]

(186)120. Triangle(Medium)

給定一個三角形,找到從上到下的最小路徑總和。您可以移動到下面一行中相鄰數字的每一步。

思路:
如果從上到下,需要用到O(n2)的空間
從下到上,我們只需要覆蓋上一層就可以了,因爲上一層沒有用了

def minimumTotal(self, triangle):
    """
    :type triangle: List[List[int]]
    :rtype: int
    """
    min_path = triangle[-1]
    for layer in range(len(triangle)-2,-1,-1):
        for i in range(len(triangle[layer])):
            min_path[i] = min(min_path[i+1],min_path[i])+ triangle[layer][i]
    return min_path[0]

(187)279. Perfect Squares(Medium)

給定正整數n,找到總和爲n的最小正方數(例如,1,4,9,16 …)。
在這裏插入圖片描述

思路:
在這裏插入圖片描述

    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp = [i for i in range(n+1)]
        for i in range(n+1):
            j = 1
            while(j*j<=i):
                dp[i] = min(dp[i],dp[i -j*j]+1)
                j+=1
        return dp[n]

(188)139. Word Break(Medium)

給定一個目標字符串和一組字符串,判斷目標字符串能否拆分成數個字符串,這些字符串都在給定的那組字符串中。
在這裏插入圖片描述

思路:
1、使用動態規劃
2、dp[i] 代表前i個是否滿足條件
3、第二層循環是將問題逐步化小

    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: bool
        """
        n,dp = len(s),[True] + [False]*len(s)
        
        for i in range(n):
            for j in range(i+1):
                if dp[j] and s[j:i+1] in wordDict:
                    dp[i+1] = True
                    break
        return dp[n]

(189)375. Guess Number Higher or Lower II(Medium)

我們正在玩猜猜遊戲。遊戲如下: 我從1到n選擇一個數字。你必須猜測我選擇了哪個號碼。 每當你猜錯了,我都會告訴你我選的號碼是高還是低。但是,當你猜出一個特定的數字x,並且你猜錯了,你需支付$ x。當你猜到我選擇的數字時,你贏了比賽。
鑑於特定的n≥1,找出您需要多少錢才能保證獲勝。
在這裏插入圖片描述

思路:
在1-n個數裏面,我們任意猜一個數(設爲i),保證獲勝所花的錢應該爲 i + max(w(1 ,i-1), w(i+1 ,n)),這裏w(x,y))表示猜範圍在(x,y)的數保證能贏應花的錢,則我們依次遍歷 1-n作爲猜的數,求出其中的最小值即爲答案,即最小的最大值問題

    def getMoneyAmount(self, n):
        """
        :type n: int
        :rtype: int
        """
        need = [[0]*(n+1) for _ in range(n+1)]
        
        for low in range(n, 0, -1):
            for high in range(low+1, n+1):
                need[low][high] = min(x + max(need[low][x-1], need[x+1][high]) for x in range(low, high))
        return need[1][n]

(190)312. Burst Balloons(Hard)

給定n個氣球,索引從0到n-1。每個氣球都塗有一個由數組nums表示的數字。你被要求爆破所有的氣球。如果你爆裂氣球我會得到nums [left] * nums [i] * nums [right]硬幣。這裏左邊和右邊是i的相鄰指數。在爆發之後,左右之後變得相鄰。 通過明智地爆破氣球找到可以收集的最大硬幣。
您可以想象nums [-1] = nums [n] = 1.它們不是真實的,因此您不能破壞它們。 0≤n≤500,0≤nums[i]≤100
在這裏插入圖片描述

思路:
氣球被分爲兩個部分,當前氣球是最後一個爆裂的,所以可以使用DP進行遞歸
使用三重循環
第一層循環i,j中間的有多少個元素,從2-n,之所以要從2,是因爲i,j中間至少要有一個元素。
第二層循環就是起始點i,從0 到n-gap,此時j = i+gap
第三層循環k,就是哪一個是i到j裏面最晚爆炸的。
最後選取結果最大的。

    def maxCoins(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
    
        nums = [1] + nums +[1]
        n = len(nums)
        dp = [[0]*n for _ in xrange(n)]
        for gap in xrange(2,n):
            for i in range(n-gap):
                j = i+gap
                for k in range(i+1,j):
                    dp[i][j] = max(dp[i][j],nums[i]*nums[k]*nums[j] + dp[i][k] +dp[k][j])
        return dp[0][n-1]

(191)322. Coin Change(Medium)

您將獲得不同面額的硬幣和總金額。編寫一個函數來計算構成該數量所需的最少數量的硬幣。如果這筆錢不能由任何硬幣組合彌補,則返回-1。
在這裏插入圖片描述

思路:
會有兩個思考:1、coins並不是排好序的 2、有可能有兩個相差的金額就是你剩下的錢,所以不能單純的認爲先用最大的,再接着用小額度的。所以循環使用%和/方式不可取

使用dp算法
dp[i] 代表額度爲i需要用到的最少數量硬幣
所以dp[i] = min(dp[i],dp[i-coins[j]+1)

    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
    
        dp = [0] +[float('inf')]*amount
        for i in range(1,amount+1):
            for c in coins:
                if i-c>=0:
                    dp[i] = min(dp[i],dp[i-c]+1)
        return dp[amount] if dp[amount]<float('inf') else -1

Dynamic Programming(二維)

(192)256 Paint House(Easy)

有一排n個房子,每個房子都可以塗上三種顏色中的一種:紅色,藍色或綠色。 用一定顏色繪製每個房子的成本是不同的。 你必須爲所有的房屋塗漆,使兩個相鄰的房屋沒有相同的顏色。
用特定顏色繪製每個房屋的成本由n×3成本矩陣表示。 例如,成本[0] [0]是用紅色繪製房屋0的成本; 成本[1] [2]是用綠色塗漆房屋1的成本,依此類推…找出所有房屋的最低成本。
Example:
Input: [[17,2,17],[16,16,5],[14,3,19]]
Output: 10
Explanation: Paint house 0 into blue, paint house 1 into green, paint house 2 into blue.

思路:
使用動態規劃,使用red,blue,green表示當前位置的最小cost,遍歷costs

def minCost(self,costs):
	red,blue,green = 0,0,0
	for r,b,g in costs:
		red,blue,green = min(blue,green)+r,min(red,green)+b,min(red,blue)+green
	return min(red,blue,green)

(193)265. Paint House II(Hard)

用特定顏色繪製每個房屋的成本由n×k成本矩陣表示。 例如,成本[0] [0]是用顏色0繪製房屋0的成本; 成本[1] [2]是用顏色2繪製房屋1的成本,依此類推…找出所有房屋的最低成本。 注意:所有費用均爲正整數。
Example:
Input: [[1,5,3],[2,9,4]]
Output: 5
Explanation: Paint house 0 into color 0, paint house 1 into color 2. Minimum cost: 1 + 4 = 5;
Or paint house 0 into color 2, paint house 1 into color 0. Minimum cost: 3 + 2 = 5.
Follow up:
Could you solve it in O(nk) runtime?

思路:
正常的dynamic programming思路是填入n * k大小的matrix,而每一個dp[i][j]暴力解的話都需要遍歷之前一個房子的O(k)項,這就造成了O(nk^2)的複雜度。follow up中要求O(nk)要怎麼做到呢?
實際上我們只需要前一輪至多兩個最小值:如果當第 j 個房子和min1一樣,那隻能選擇min2,如果不一樣,可以直接選min1。這裏的min1和min2應該是index而不是cost,因爲我們需要同時知道取到最小cost的上一輪顏色是什麼才能選擇min1還是min2。

def minCostII(self.costs):
	if len(costs) ==0 or len(costs[0]) ==0:
		return 0
	min_1,min_2,index_1 = 0,0,-1
	for i in range(len(costs)):
		m1 = float('inf')
		m2 = m1
		id1 = -1
		for j in range(len(cost[i])):
			if j == index_1:
				cost = costs[i][j] +min_2
			else:
				cost = cost[i][j] + min_1
			if cost<m1:
				m2 = m1
				m1 = cost
				id1 = j
			elif cost<m2:
				m2 = cost
		min_1,min_2,index_1 = m1,m2,id1
	return min_1

(194)72. Edit Distance(Hard)

給定兩個單詞word1和word2,找到將word1轉換爲word2所需的最小操作數。 您對單詞允許以下3個操作:
1、插入一個角色 2、刪除一個字符 3、替換一個角色
在這裏插入圖片描述

思路:
1.如果str1的第i個,也就是str1[i-1]和str2的第j個也就是str2[j-1]相等的話,那麼 dis[i][j] = dis[i-1][j-1]

2.如果str[i-1] != str2[j-1]

2.1 通過替換操作把str[i-1]替換成str2[j-1],那麼

dis[i][j] = dis[i-1][j-1] + 1;

2.2 通過插入操作在str1後面插入str2[j-1], 那麼就相當於計算

dis[i][j] = dis[i][j-1] + 1;

2.3 通過插入操作在str2後面插入str1[i-1],那麼就是

dis[i][j] = dis[i-1][j] + 1;

在上述三個中選一個最小的。迭代更新。

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
        for i in range(len(word1)+1): dp[i][0] = i
        for j in range(len(word2)+1): dp[0][j] = j
        
        for i in range(1,len(word1)+1):
            for j in range(1,len(word2)+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1))
        return dp[-1][-1]

(195)97. Interleaving String(Hard)

給定s1,s2,s3,找出s3是否由s1和s2的交織形成。

思路:(用遞歸回超時)
字符串的子序列或是匹配問題直接就上動態規劃
還可以使用一維dp,不需要兩維,因爲i依賴於前面的i
1、首先判斷橫豎的邊界條件

        for i in range(1,len(s2)+1):
            dp[0][i] = True if s2[i-1]==s3[i-1] and dp[0][i-1] else False
        for i in range(1,len(s1)+1):
            dp[i][0] = True if s1[i-1] == s3[i-1] and dp[i-1][0] else False

2、其次判斷i,j

	(dp[i-1][j] and s1[i-1]==s3[i-1+j]) or (dp[i][j-1] and s2[j-1]==s3[j-1+i])
# ##上動態規劃
    def isInterleave(self, s1, s2, s3):
        if len(s1)+len(s2)!=len(s3):
            return False
        dp=[[False for _ in range(len(s2)+1)] for _ in range(len(s1)+1)]
        dp[0][0] = True
        for i in range(1,len(s2)+1):
            dp[0][i] = True if s2[i-1]==s3[i-1] and dp[0][i-1] else False
        for i in range(1,len(s1)+1):
            dp[i][0] = True if s1[i-1] == s3[i-1] and dp[i-1][0] else False
        
        for i in range(1,len(s1)+1):
            for j in range(1,len(s2)+1):
                dp[i][j] = True if (dp[i-1][j] and s1[i-1]==s3[i-1+j]) or (dp[i][j-1] and s2[j-1]==s3[j-1+i]) else False
        return dp[len(s1)][len(s2)]

(196)174. Dungeon Game(Hard)

皇后右下角,國王從左上角開始尋找皇后,求國王最少需要多少血?
在這裏插入圖片描述

思路:
1、使用dp操作
2、當走完最後一個房間,至少要剩下1格血,最後狀態也可以當作最初狀態,從最後一個房間往前走,每個房間的血量由下面或者右邊一個較小的一個決定:
dp[i][j] = min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]
還要考慮每個位置最少最低爲一格血

    def calculateMinimumHP(self, dungeon):
        """
        :type dungeon: List[List[int]]
        :rtype: int
        """
        m = len(dungeon)
        n = len(dungeon[0])
        dp = [[2**31-1 for i in range(n+1)] for j in range(m+1)]
        
        dp[m-1][n] = 1
        
        for i in range(m-1,-1,-1):
            for j in range(n-1,-1,-1):
                dp[i][j] = max(min(dp[i+1][j],dp[i][j+1]) - dungeon[i][j],1)
        
        return dp[0][0]

(197)221. Maximal Square(Medium)

給定填充0和1的2D二進制矩陣,找到僅包含1的最大正方形並返回其面積。
在這裏插入圖片描述

思路:
1、我們初始化dp所有的元素爲0
2、dp(i,j)=min(dp(i−1,j),dp(i−1,j−1),dp(i,j−1))+1. dp就是左邊、右邊、斜上角最小值+1
3、因爲只用到了i-1,所以不需要用到m*n的空間。使用變量存儲上一個i-1的值就可以了
在這裏插入圖片描述
在這裏插入圖片描述

    def maximalSquare(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if not matrix:
            return 0
        dp = [0]*(len(matrix[0])+1)
        max_ans = 0 
        pre = 0
        for i in range(1,len(matrix)+1):
            for j in range(1,len(matrix[0])+1):
                temp = dp[j]
                if matrix[i-1][j-1] == '1':
                    dp[j] = min(dp[j],pre,dp[j-1])+1
                    max_ans = max(max_ans,dp[j])
                else:
                    dp[j] = 0
                pre = temp
        return max_ans**2

(198)84. Largest Rectangle in Histogram***(Hard)

給定n個非負整數表示直方圖的條形高度,其中每個條形的寬度爲1,找到直方圖中最大矩形的區域。

在這裏插入圖片描述
思路:題型和11題類似,但是解法不同。
此題用stack的思想O(n),重要的幾個trick
1、stack = [-1] height.append(0) #在最後面添加一個最小數
2、循環當矩陣不是遞增的時候,彈出末尾的元素,然後算面積。否則stack.append(i)(注:是加入索引)

用棧來模擬,遍歷heights數組,如果大於棧頂元素,就push進去;否則,持續彈棧來計算從棧頂點到降序點的矩陣大小。然後將這一部分全部替換爲降序點的值,即做到了整體依然是有序非降的。
整個過程中,即把所有的局部最大矩陣計算過了,又在寬度範圍內保留了全部的場景。
舉例,2,1,5,6,3的場景。
先加入一個0,方便最後可以全部彈棧出來。變成:2,1,5,6,3,0.
2進棧,1比棧頂小,對2進行出棧,area = 2;
5,6都是非降的,繼續進棧,棧爲1,5,6;
遇到3,是一個降序點;開始彈棧,6出棧,對應area=61; 5出棧對應area=52;下一個1比3小,不需要彈棧。然後將5、6的彈棧後的空位壓棧爲3,這是棧爲1,1,3,3,3;
下一步遇到0,開始依次出棧,得到area=31,32,33,14,1*5。
遍歷結束。整個過程中max的area爲10.

    def largestRectangleArea(self, height):
        #在最後面添加一個最小數
        height.append(0)
        stack = [-1]
        ans = 0
        for i in range(len(height)):
            while height[i]<height[stack[-1]]:
                h = height[stack.pop()]
                w = i - stack[-1] -1
                ans = max(ans,h*w)
                print(ans)
            stack.append(i)
        return ans

(199)85. Maximal Rectangle(Hard)

給定填充0和1的2D二進制矩陣,找到僅包含1的最大矩形並返回其區域。

Input:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
Output: 6

思路:橫着看,根據每一行的計算,然後使用84題的思路

    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if matrix==[] or matrix[0]==[]:
            return 0
        matrix1 = [0 for _ in range(len(matrix[0])+1)]
        max_sum = 0
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if i ==0:
                    matrix1[j] = int(matrix[i][j])
                else:
                    if matrix[i][j]!='0':
                        matrix1[j] +=  1
                    else:
                        matrix1[j] =0
            stack = [-1]
            for i in range(len(matrix1)):
                while matrix1[i]<matrix1[stack[-1]]:
                    h = matrix1[stack.pop()]
                    w = i-stack[-1] -1
                    max_sum = max(max_sum,h*w)
                stack.append(i)
        return max_sum

(200)363. Max Sum of Rectangle No Larger Than K(Hard)(不會)

給定非空的2D矩陣矩陣和整數k,找到矩陣中矩形的最大和,使得其總和不大於k。
在這裏插入圖片描述
注意: 矩陣內的矩形必須具有> 0的區域。 如果行數遠大於列數怎麼辦?

class Solution(object):
    def maxSubArraylessK(self,nums,k):
        """
        we need to find the sum[right]-sum[left]<=k
        since the bitsect return the index of the sorted value
        we can't directly pop the nums[idx] 
        we should use insort from the bisect
        """
        # python set() doesn't support indexing, using list instead
        # similar as the c++ or java set()
        
        cumset=[]
        cumset.append(0)
        maxsum=-1<<32
        cursum=0
        for i in xrange(len(nums)):
            cursum+=nums[i]
            # find the lower bound of the index
            idx=bisect.bisect_left(cumset,cursum-k)
            # find max in sum[right]-sum[left]<=k
            if 0<=idx<len(cumset):
                maxsum=max(maxsum,cursum-cumset[idx])
            # using insort instead of append since bisect_left reason
            bisect.insort(cumset,cursum)
        return maxsum
    
    
    def maxSumSubmatrix(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        """
        The python solution hasn't a good time performance,
        since lack some of the datatype to do 
        I am trying to imitate the way solved in c++ or Java
        Ther related quesiton might be:
        1. #53. Maximum Subarray 
        2. Maximum Subarray sum less or equal than K
        3. maximun sum of rectangle 
        """
        if not matrix or not matrix[0]:
            return 0
        row,col=len(matrix),len(matrix[0])
        res=-(1<<32)
        # using two pointer to record the scan position
        for left in xrange(col):
            # reset mem to store the row data
            cursums=[0 for _ in xrange(row)]
            # since the rectange has area>0 
            right=left
            while right<col:
                # count one row
                for i in xrange(row):
                    cursums[i]+=matrix[i][right]
                # find the max in this row
                curarrmax=self.maxSubArraylessK(cursums,k)
                res=max(res,curarrmax)
                right+=1
                
        return res

Dynamic Programming(化簡)

(201)198. House Robber(Easy)

你是一個專業的強盜,計劃在街上搶劫房屋。每個房子都有一定數量的錢存在,阻止你搶劫他們的唯一限制是相鄰的房屋有連接的安全系統,如果兩個相鄰的房子在同一個晚上被打破,它將自動聯繫警察。
給出一個代表每個房子的金額的非負整數列表,確定今晚可以搶劫的最大金額而不警告警察。
在這裏插入圖片描述

    def rob(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        last, now = 0, 0
        
        for i in nums: 
            last, now = now, max(last + i, now)                
        return now

(202)213. House Robber II(Medium)

你是一個專業的強盜,計劃在街上搶劫房屋。每個房子都藏着一定數量的錢。這個地方的所有房屋都排成一個圓圈。這意味着第一棟房屋是最後一棟房屋的鄰居。與此同時,相鄰的房屋連接了安全系統,如果兩個相鄰的房屋在同一個晚上被闖入,它將自動聯繫警方。

思路:
1、使用DP
2、因爲首尾是鄰居,所以使用去首,去尾。當作兩個數組,然後找出兩個數組的最大值
3、比較res[i-1] 和res[i-2]+nums[i]的最大值
4、因爲動態數組是k循環的,所以採用k大小的數組,使得空間使用率降爲O(1)

def rob(self, nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    if len(nums) ==0:
        return 0
    if len(nums)<=3:
        return max(nums)
    return max(self.rob_row(nums[1:]),self.rob_row(nums[:-1]))
    
def rob_row(self,nums):
    res = [0]*2
    res[0] = nums[0]
    res[1] = max(nums[0],nums[1])
    for i in range(2,len(nums)):
        res[i%2] = max(res[(i-1)%2],res[(i-2)%2]+nums[i])
    return max(res)

(203)276. Paint Fence

有一個有n個帖子的圍欄,每個帖子都可以塗上一種k顏色。 您必須繪製所有帖子,使得不超過兩個相鄰的柵欄柱具有相同的顏色。 返回可以繪製柵欄的總方式。

思路:
當n=1時, 結果爲k
當n = 2時, 分爲兩種情況, 顏色與上一步相同same, 顏色與上一步不同diff, 此時, same爲k, diff爲k*(k-1)
當n > 3時, 根據題意, 不能有超過兩個顏色相同的籬笆, 那麼顏色與上一步相同時的情況只能用上一步的diff推導, 個數爲diff. 顏色與上一步不同時, 可以用所有之前的情況, 個數爲(same+diff)*(k-1).

def numWays(self, n, k):
	if n ==0:
		return 0
	if n ==k:
		return k
	same,different = k,k*(k-1)
	for i in range(3,n+1):
		same,different = different,(same+different)*(k-1)
	return same+different

(204)91. Decode Ways(Medium)

Input: “12”
Output: 2
Explanation: It could be decoded as “AB” (1 2) or “L” (12).

思路:
因爲dp[i] = dp[i-1] +(dp[i-2] if 0<s[i-1:i+1]<=26)所以只需使用兩個變量存儲(可覆蓋)
一定要注意0不與任何數組成數字的時候,return 0

    def numDecodings(self, s):
        """
        :type s: str
        :rtype: int
        """
        #使用dp操作
        if len(s)==0 or s[0] =='0':
            return 0
        dp1 = 1
        dp2 = 1
        for i in range(1,len(s)):
            cur = dp2
            #當前面不是0,並且小於26
            if int(s[i-1])!=0 and int(s[i-1:i+1])<=26:
                cur +=dp1
            #如果當前i=0,並且不滿足上面的條件。不是合法的
            if int(s[i])==0 and cur ==dp2:
                return 0
            #如果當前i=0,則只能當成一個元素看
            elif int(s[i]) ==0:
                cur -=dp2
            dp1,dp2 = dp2,cur
        return dp2

(205)10. Regular Expression Matching(Hard)

給定輸入字符串和模式(p),實現與支持 ‘.’ and ‘*’.
‘.’ Matches any single character.
’ * ’ Matches zero or more of the preceding element.
匹配應覆蓋整個輸入字符串(不是部分)。
Note:
s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
在這裏插入圖片描述

思路:
1, If p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1];
2, If p.charAt(j) == ‘.’ : dp[i][j] = dp[i-1][j-1];
3, If p.charAt(j) == ’ * ':
here are two sub conditions:
1 if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //in this case, a* only counts as empty
2 if p.charAt(j-1) == s.charAt(i) or p.charAt(j-1) == ‘.’:
dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a
or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a
or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty

    def isMatch(self, text, pattern):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        dp = [[False]*(len(pattern)+1) for _ in range(len(text)+1)]
        dp[0][0] = True
        for i in range(len(pattern)):
            if pattern[i] == '*' and dp[0][i-1]:
                dp[0][i+1] = True
        
        for i in range(len(text)):
            for j in range(len(pattern)):
                if pattern[j] == text[i] or pattern[j] == '.':
                    dp[i+1][j+1] = dp[i][j]
                if pattern[j] == '*':
                    # in this case, a* counts as empty
                    if pattern[j-1] != text[i] and pattern[j-1]!='.':
                        dp[i+1][j+1] = dp[i+1][j-1]
                    #dp[i+1][j] in this case, a* counts as single a
                    #dp[i][j+1] in this case, a* counts as multiple a 
                    #dp[i+1][j-1] in this case, a* counts as empty
                    else:
                        dp[i+1][j+1] = (dp[i+1][j] or dp[i][j+1] or dp[i+1][j-1])
        return dp[-1][-1]

(206)44. Wildcard Matching(Hard)

Given an input string (s) and a pattern §, implement wildcard pattern matching with support for ‘?’ and ‘’.
‘?’ Matches any single character.
'
’ Matches any sequence of characters (including the empty sequence).

思路:使用迭代
如果遇到了多個*,只用返回最後一個星。還要記錄star的位置和s當前位置
1、使用兩個變量分別記錄最後一個星的位置和當時s的位置
2、四個條件:如果一對一匹配,都加一;如果p爲*,記錄;如果無法匹配且前面有*,P重來,S加一;如果都不匹配,False
3、如果j結束了,P後面應該都要*

    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        i =0
        j =0
        jIndex = -1
        starIndex = -1
        while(j<len(s)):
            ##一對一匹配,都加一
            if (i<len(p) and (s[j]==p[i] or p[i]=='?')):
                i+=1
                j+=1
            ## p爲*,記錄下p和s的索引。P到下一個位置
            elif (i<len(p) and (p[i]=='*')):
                jIndex = j
                starIndex = i
                i+=1
            ## 如果不匹配,則判斷前面是不是有*,同時jIndex前進一步,P則重新來
            elif (starIndex!=-1):
                j = jIndex+1
                i = starIndex+1
                jIndex +=1
            ##如果都不對,返回False
            else:
                return False
        ## j如果結束了,p沒結束,p必須後面都是*
        while(i<len(p)and p[i]=='*'):
            i+=1
        return i==len(p)

LinkedList(基礎)

(207)206. Reverse Linked List(Easy)

反轉單鏈表
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL

    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        prev = None
        curr = head
        while curr:
            next = curr.next
            curr.next = prev
            prev = curr
            curr = next
        return prev

(208)141. Linked List Cycle(Easy)

給定一個鏈表,確定它是否有一個循環。
爲了表示給定鏈表中的循環,我們使用整數pos來表示tail連接到的鏈表中的位置(0索引)。如果pos爲-1,則鏈表中沒有循環。
在這裏插入圖片描述

思路:
使用快慢指針

def hasCycle(self,head):
	    """
        :type head: ListNode
        :rtype: bool
        """
        if head ==None or head.next ==None:
        	return False
        slow = head
        fast = head.next
        while slow!=fast:
        	if fast ==None or fast.next ==None:
        		return False
        	slow = slow.next
        	fast = fast.next.next
        return True

(209)142. Linked List Cycle II(Medium)

給定鏈表,返回循環開始的節點。如果沒有循環,則返回null。
在這裏插入圖片描述

思路:
在這裏插入圖片描述
第一次相遇時slow走過的距離:a+b,fast走過的距離:a+b+c+b。
因爲fast的速度是slow的兩倍,所以fast走的距離是slow的兩倍,有 2(a+b) = a+b+c+b,
可以得到a=c(這個結論很重要!)。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        first = head
        second = head
        while first and second:
            first = first.next
            if second.next:
                second =second.next.next
            else:
                return None
            if first ==second:
                first = head
                while first != second:
                    first = first.next
                    second = second.next
                return first
        return None

(210)143. Reorder List(Medium)

在這裏插入圖片描述

思路:
1、先找出中間的node
2、將中間後面的給反轉
3、然後兩個鏈表相連

    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: None Do not return anything, modify head in-place instead.
        """
        if head ==None:
            return head
        
        first,second = head,head
        
        ##找出中間的
        while second.next and second.next.next:
            first = first.next
            second = second.next.next
        
        cur = first.next
        node = first.next = None
        
        ## 反轉後面的
        while cur:
            next = cur.next
            cur.next = node
            node = cur
            cur = next
            
        ##交替相連
        cur1 = head
        cur2 = node
        while cur2:
            next = cur1.next
            cur1.next = cur2
            next2 = cur2.next
            cur2.next = next
            cur2 = next2
            cur1 = next

(211)24. Swap Nodes in Pairs(Medium)

給定鏈表,交換每兩個相鄰節點並返回其頭部。 您可能無法修改列表節點中的值,只能更改節點本身。

思路:
主要是搞清楚交換的點

    def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        pre,pre.next = self,head
        cur = pre
        while cur.next!=None and cur.next.next !=None:
            a = cur.next
            b = a.next
            cur.next, b.next, a.next = b, a, b.next
            cur = a
        return pre.next

(212)328. Odd Even Linked List(Medium)

給定單鏈表,將所有奇數節點組合在一起,然後是偶數節點。請注意,這裏我們討論的是節點編號,而不是節點中的值。
你應該嘗試到位。該程序應該以O(1)空間複雜度和O(節點)時間複雜度運行。
在這裏插入圖片描述
注意: 偶數組和奇數組內的相對順序應保持與輸入中的相對順序。 第一個節點被認爲是奇數,第二個節點被認爲是偶數等等…

思路:
使用oddhead,和evenhead,最後將兩個相連。要記錄evenhead的第一個元素

    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None:
            return None
        odd,even,evenhead = head,head.next,head.next
        while even and even.next:
            odd.next = even.next
            even.next = even.next.next
            even = even.next
            odd = odd.next
        odd.next = evenhead
        return head

(213)92. Reverse Linked List II(Medium)

Reverse a linked list from position m to n. Do it in one-pass.
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL

思路:先把m給找到,然後從m開始到n先調轉,然後再頭尾對應的連上

    def reverseBetween(self, head, m, n):
        """
        :type head: ListNode
        :type m: int
        :type n: int
        :rtype: ListNode
        """
        if m ==n:
            return head
        pre = ListNode(0)
        pre.next = head
        cur = pre
        i = 1
        while i<m:
            cur = cur.next
            i+=1
        #使用m_cur記住reversed的前面一個
        m_cur = cur
        cur = cur.next
        Reversed = None
        #要reverse的自己內部先轉,然後再與外部鏈接
        while i<=n:
            next = cur.next
            cur.next = Reversed
            Reversed = cur
            cur = next
            i+=1
        m_cur.next.next = cur
        m_cur.next = Reversed
        return pre.next
        

(214)237. Delete Node in a Linked List(Easy)

編寫一個函數來刪除單鏈表中的節點(尾部除外),只允許訪問該節點。
在這裏插入圖片描述
The linked list will have at least two elements.
All of the nodes’ values will be unique.
The given node will not be the tail and it will always be a valid node of the linked list.
Do not return anything from your function.

思路:
給的輸入是當前這個節點,不是頭結點
將下一個節點的值賦給當前節點
next指向next.next

    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

(215)19. Remove Nth Node From End of List

給定鏈接列表,從列表末尾刪除第n個節點並返回其頭部。

思路:
1、定義兩個指針,相隔n個

    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        pre = head
        cur = head
        for i in range(n):
            cur = cur.next
        if cur==None:
            return head.next
        while cur.next!=None:
            cur = cur.next
            pre = pre.next
        pre.next = pre.next.next
        return head

(216)83. Remove Duplicates from Sorted List(Easy)

Given a sorted linked list, delete all duplicates such that each element appear only once.

def deleteDuplicates(self, head):
    """
    :type head: ListNode
    :rtype: ListNode
    """
    if head ==None:
        return head
    cur = head
    while cur.next !=None:
        if cur.val == cur.next.val:
            cur.next = cur.next.next
        else:
            cur = cur.next
            
    return head

(217)203. Remove Linked List Elements(Easy)

從具有值val的整數的鏈接列表中刪除所有元素。
Input: 1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5

思路:
使用迭代,遇到相同的就跳過,記得很多坑

    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        pre,pre.next = self,head
        cur = pre
        while cur:      
            if cur.next and cur.next.val ==val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return pre.next

(218)82. Remove Duplicates from Sorted List II(Medium)

給定已排序的鏈接列表,刪除所有具有重複數字的節點,只留下原始列表中的不同數字。
在這裏插入圖片描述

    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        pre,pre.next = self,head
        cur = pre
        while cur.next and cur.next.next:
            if cur.next.val == cur.next.next.val:
                #防止出現奇數個相同的數字,並且確保next.next不爲空。
                #最後要刪掉cur.next
                while cur.next.next and cur.next.val == cur.next.next.val:
                    cur.next.next = cur.next.next.next
                cur.next = cur.next.next
            else:
                cur = cur.next
        return pre.next

(219)369 Plus One Linked List(Medium)

給定一個非負數表示爲單個鏈接的數字列表,加上一個數字。 存儲數字使得最高有效數字位於列表的開頭。
Example:
Input:
1->2->3
Output:
1->2->4

思路:
首先先要把鏈表進行翻轉,然後進行加一操作,記得要進位,最後要判斷要不要加節點。最後再把鏈表翻轉

def plusOne(self,head):
	

(220)2. Add Two Numbers(Medium)

給你兩個表示兩個非負數字的鏈表。數字以相反的順序存儲,其節點包含單個數字。將這兩個數字相加並將其作爲一個鏈表返回。
輸入: (2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出: 7 -> 0 -> 8

思路:
1.進位問題
2. l1 和 l2 長度不同
3. .當 l1 + l2 大於1000 進位問題.

    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        add = 0
        head = ListNode(0)
        point = head
        while l1 !=None and l2!=None:
            point.next = ListNode((l1.val+l2.val+add)%10)
            add = (l1.val+l2.val+add)/10
            l1 = l1.next
            l2 = l2.next
            point = point.next
        while l1!=None:
            point.next = ListNode((l1.val+add)%10)
            add = (l1.val+add)/10
            l1 = l1.next
            point = point.next
        while l2!=None:
            point.next = ListNode((l2.val+add)%10)
            add = (l2.val+add)/10
            l2 = l2.next
            point = point.next
        if add>0:
            point.next = ListNode(add)
        return head.next

(221)160

(222)21. Merge Two Sorted Lists(Easy)

合併兩個已排序的鏈接列表並將其作爲新列表返回。新列表應該通過拼接前兩個列表的節點來完成。

思路:
1、就是合併排序的merge的實現

    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        head = ListNode(0)
        cur = head
        while l1!=None and l2!=None:
            if l1.val>l2.val:
                cur.next = l2
                l2 = l2.next
                
            else:
                cur.next = l1
                l1 = l1.next
            cur = cur.next
        while l1!=None:
            cur.next = l1
            l1 = l1.next
            cur = cur.next
        while l2!=None:
            cur.next = l2
            l2 = l2.next
            cur= cur.next
        return head.next

LinkedList(提高)

(223)234. Palindrome Linked List(Easy)

給出一個單鏈表,確定它是否是迴文。
你能在O(n)時間和O(1)空間做嗎?

思路:
先使用快慢指針,找到中間的節點(奇數,偶數沒關係)只要後面的長度比較短就可以了
然後反轉後面的鏈表
最後比較兩個鏈表
由於有三個指針交換,把next放在前面就沒問題

    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        reverse = None
        while slow:
            slow.next,slow,reverse = reverse,slow.next,slow
        while reverse:
            if reverse.val!=head.val:
                return False
            reverse = reverse.next
            head = head.next
        return True

(224)148. Sort List(Medium)

使用常量空間複雜度在O(n log n)時間內對鏈表進行排序。

思路:
滿足這樣要求的排序算法,我們首先想到快排,合併排序和堆排序。
快排的最壞的時間複雜度是O(n^2),平均複雜度爲O(nlgn)
堆排序實現上過於繁瑣,我們不做考慮

使用歸併
1、首先要將鏈表一步一步縮小(使用遞歸)定義三個變量,pre、slow、fast
2、其次使用歸併操作

    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head==None or head.next==None:
            return head
        
        pre = slow = fast = head
        
        while fast!=None and fast.next!=None:
            pre = slow
            slow = slow.next
            fast = fast.next.next
        pre.next = None
        i  = self.sortList(head)
        j  = self.sortList(slow)
        return self.merge(i,j)
    
    def merge(self,i,j):
        cur = tempNode = ListNode(0)
        while i!=None or j!=None:
            if i == None:
                cur.next = j
                j = j.next
            elif j ==None:
                cur.next = i
                i = i.next
            elif i.val>j.val:
                cur.next = j
                j = j.next
            else:
                cur.next = i
                i = i.next
            cur = cur.next
        return tempNode.next

(225)25. Reverse Nodes in k-Group(Hard)

給定鏈表,一次反轉鏈表k的節點並返回其修改後的列表。
k是正整數,並且小於或等於鏈表的長度。如果節點數不是k的倍數,那麼最後的剩餘節點應該保持不變。
Example:
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5

思路:
1、使用左右指針,然後對結果進行交換
2、還需要一個指針用來記錄當前節點的前一個
2、下次要注意,如果是三個交換,記得把next寫在前面

    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        pre,pre.next = ListNode(0),head
        l = r = head
        next_head = pre
        while True:
            count = 0
            while r and count<k:
                r = r.next
                count +=1
            if count == k:
                temp,cur = r,l
                for _ in range(k):
                    #這裏是三個元素,所以要有次序,在這裏搞了很久,次序影響結果(仍然沒搞明白,最好使用賦值的方法),下次把next放在前面,並且數組a,b反過來也不行
                    cur.next,temp,cur = temp,cur,cur.next
                #這裏也一樣
                next_head.next,next_head,l = temp,l,r
            else:
                return pre.next

(226)61. Rotate List(Medium)

給定鏈表,將列表向右旋轉k個位置,其中k爲非負數。
Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL 右移k位

思路:先找出有多少個,趁前面不注意,然後一次將後面幾個移到前面。

    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        start = head
        end = head
        if end ==None or end.next ==None:
            return head
        i=1
        while(end.next!=None):
            i+=1
            end = end.next
        ##免得走多了一個輪迴
        k%=i
        for j in range(i-k-1):
            start = start.next        
        end.next = head
        head = start.next
        start.next = None
        return head

(227)86. Partition List(Medium)

給一個鏈表,將小於值x的結點放到所有大於等於值x的結點的前面,不要改變結點之間的順序(例如1,4,3,2,5,2 將2結點提到至4的前面,但4,3,5的順序不變);
在這裏插入圖片描述
思路:設置一個變量,記錄下鏈表中第一次出現大於等於值x結點的位置insertPos。之後遍歷鏈表,將所有小於值x的結點提到這個位置上
(好像還有更好的解法)

    def partition(self, head, x):
        """
        :type head: ListNode
        :type x: int
        :rtype: ListNode
        """
        pre = ListNode(0)
        pre.next = head
        start = pre
        while start.next != None:
            if start.next.val >=x:
                break
            start = start.next
        cur = start
        while cur.next !=None:
            if cur.next.val < x:
                print(cur.val,cur.next.val,start.val,start.next.val)
                cur.next,start.next,start.next.next = cur.next.next,cur.next,start.next
                start = start.next
                # print(cur.val,cur.next.val,start.val,start.next.val)
            else:
                cur = cur.next
            if cur ==None:
                break
        return pre.next

(228)23. Merge k Sorted Lists(Hard)

合併k個已排序的鏈表並將其作爲一個排序列表返回。分析並描述其複雜性。

思路:
1、將list中的每個元素的值存入list中
2、將list進行排序,然後建立node,加入next


from Queue import PriorityQueue
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        queue = PriorityQueue()
        head = ListNode(0)
        cur = head
        for i in lists:
            if i:
                queue.put((i.val,i))
        while queue.qsize()>0:
            cur.next = queue.get()[1]
            cur = cur.next
            if cur.next:
                queue.put((cur.next.val,cur.next))
        return head.next

(229)147. Insertion Sort List(Medium)

使用插入排序對鏈接列表進行排序。

思路:
定義一個輔助鏈表頭
然後循環找到合適的位置插入

    def insertionSortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None:
            return None
        
        pre=temp= ListNode(0)
        cur = head
        next = None
        while cur:
            while (pre.next!=None and cur.val>pre.next.val):
                pre = pre.next
            next = cur.next
            cur.next = pre.next
            pre.next = cur
            cur = next
            pre = temp
        return pre.next

Binary Search(基礎)

(230)278. First Bad Version(Easy)

您是產品經理,目前領導團隊開發新產品。不幸的是,您產品的最新版本未通過質量檢查。
由於每個版本都是基於以前的版本開發的,因此糟糕版本之後的所有版本也都很糟糕。
假設您有n版本,[1, 2, …, n]並且您想找出第一個壞版本,這會導致以下所有版本都不好。
您將獲得一個API bool isBadVersion(version),它將返回是否version爲壞。實現一個函數來查找第一個壞版本。您應該最小化對API的調用次數。

在這裏插入圖片描述

思路:
使用二分法,考慮邊界條件 right不加1

    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """       
        left,right = 1,n
        while left<right:
            middle = (left+right)//2
            if isBadVersion(middle):
                right = middle
            else:
                left = middle+1
        return left

(231)35. Search Insert Position(Easy)

給定排序數組和目標值,如果找到目標,則返回索引。如果沒有,請返回索引按順序插入的索引。 您可以假設數組中沒有重複項。
在這裏插入圖片描述

思路:
1、使用二分法
2、當end-start <=1時退出
3、 當target<=start,return start
elif 當target>end, return end +1
else 當target<=end ,return end

    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        start = 0
        end = len(nums)-1
        
        while(end-start >1):
            middle = int((start+end)/2)
            if nums[middle]>target:
                end = middle
            elif nums[middle]==target:
                return middle
            else:
                start = middle
        
        if target<=nums[start]:
            return start
        elif target>nums[end]:
            return end+1
        else:
            return end

(232)33. Search in Rotated Sorted Array(Medium)

按升序排序的數組在事先未知的某個樞軸處旋轉。 (即[0,1,2,4,5,6,7]可能變爲[4,5,6,7,0,1,2])。
您將獲得要搜索的目標值。如果在數組中找到則返回其索引,否則返回-1。

思路:使用二分法,如果中間的點比右邊的點小,說明右邊時有序的,否則左邊時有序的
然後分右邊有序和左邊有序兩種情況進行討論。

    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        l = 0
        r = len(nums)-1
        while(l<=r):
            middle = int((l+r)/2)
            print(middle)
            if nums[middle]==target:
                return middle
            # mean right is in order
            if nums[middle]<nums[r]:
                if target>nums[middle] and target<=nums[r]:
                    l = middle+1
                else:
                    r = middle-1
                    print(l,r)
            else:
                if target<nums[middle] and target>=nums[l]:
                    r = middle -1
                else:
                    l = middle +1
        return -1

(233)81. Search in Rotated Sorted Array II

假設按升序排序的數組在事先未知的某個樞軸處旋轉。 (即[0,0,1,2,2,5,6]可能成爲[2,5,6,0,0,1,2])。
相較於Ⅰ,多了重複元素。

思路:程序差不多,只不過多了一個判斷,當l ==l+1,l +=1
當middle<right 說明右邊是有序的

    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: bool
        """
        l = 0 
        r = len(nums)-1
        while(l<=r):
            while(l<r and nums[l] == nums[l+1]):
                l +=1
            middle = (l+r)/2
            if nums[middle] == target:
                return True
            if nums[middle]<=nums[r]:
                if nums[middle]<target and target<=nums[r]:
                    l = middle +1
                else:
                    r = middle -1
            else:
                if nums[middle]>target and target>=nums[l]:
                    r = middle -1
                else:
                    l = middle +1
        return False

(234)153. Find Minimum in Rotated Sorted Array(Medium)

假設按升序排序的數組在事先未知的某個樞軸處旋轉。找出最小值

思路:
使用二分法查找

    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        start = 0
        end = len(nums)-1
        while end-start>1:
            middle = int((start+end)/2)
            if nums[middle]>nums[end]:
                start = middle
            else:
                end = middle
        return nums[start] if nums[start]<nums[end] else nums[end]

(235)154. Find Minimum in Rotated Sorted Array II(Hard)

假設按升序排序的數組在事先未知的某個樞軸處旋轉。找到最小元素。 該數組可能包含重複項。
在這裏插入圖片描述

思路:
遇到左邊重複的就加一

    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left,right = 0,len(nums)-1
        while right - left >1:
            if nums[left] ==nums[left+1]:
                left +=1
            else:
                middle = (left+right)//2
                if nums[middle]>nums[right]:
                    left = middle
                else:
                    right = middle
        return nums[left] if nums[left]<nums[right] else nums[right]

(236)162. Find Peak Element(Medium)

峯值元素是大於其鄰居的元素。在這裏插入圖片描述

思路:
思路一(O(n))
首先要麼單調遞增、要麼單調遞減、要麼上升之後減。
所以當出現開始下降的時候,該index就是了,不需要考慮之前的

    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        for i in range(len(nums)-1):
            if nums[i]>nums[i+1]:
                return i
        return len(nums)-1

思路二(O(logn))

    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left,right = 0,len(nums)-1
        while left<right:
            middle = (left+right)//2
            if nums[middle]<nums[middle+1]:
                left = middle+1
            else:
                right = middle
        return left

(237) 374 Guess Number Higher or Lower(EASY)

我們正在玩猜猜遊戲。遊戲如下:
我從1到n選擇一個數字。你必須猜測我選擇了哪個號碼。
每當你猜錯了,我都會告訴你這個數字是高還是低。
調用一個預先定義的API guess(int num)返回3個可能的結果(-1,1,或0):
-1:我的號碼較低
1:我的號碼更高
0:恭喜!你說對了!

    def guessNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        start = 1
        end = n
        if guess(n)==0:
            return n
        if guess(1)==0:
            return 1
        while end - start >1:
            middle = int((start+end)/2)
            if guess(middle)==-1:
                end = middle
            elif guess(middle)==1:
                start = middle
            else:
                return middle

(238)34. Find First and Last Position of Element in Sorted Array(Medium)

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
在這裏插入圖片描述
思路:
1、使用二分法
2、判斷邊界

    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        low = 0
        high = len(nums)-1
        if high <0 or target<nums[low] or target>nums[high]:
            return [-1,-1]
        while(low<=high):
            middle = int((low+high)/2)
            if nums[middle]>target:
                high = middle-1
            elif nums[middle]<target:
                low = middle+1
            else:
                low = high = middle
                while(low>0 and nums[low-1]==target):
                    low -=1
                while(high<len(nums)-1 and nums[high+1]==target):
                    high +=1
                break
        
        if nums[low]==target:
            return [low,high]
        else:
            return [-1,-1]

(239)349. Intersection of Two Arrays(Easy)

給定兩個數組,編寫一個函數來計算它們的交集。
在這裏插入圖片描述

    def intersection(self, nums1: 'List[int]', nums2: 'List[int]') -> 'List[int]':
        nums1=set(nums1)
        nums2=set(nums2)
        return list(nums1&nums2)

(240)349. Intersection of Two Arrays(Easy)

給定兩個數組,編寫一個函數來計算它們的交集。可以有重複
在這裏插入圖片描述
思路:
可以使用字典來做
也可以先排序,然後使用判斷來做

        i=0
        j=0
        nums1,nums2 = sorted(nums1),sorted(nums2)
        res = []
        while i<len(nums1) and j<len(nums2):
            if nums1[i]>nums2[j]:
                j+=1
            elif nums1[i]<nums2[j]:
                i+=1
            else:
                res.append(nums1[i])
                i +=1
                j +=1
        return res
    def intersect(self, nums1, nums2):

        counts = {}
        res = []

        for num in nums1:
            counts[num] = counts.get(num, 0) + 1

        for num in nums2:
            if num in counts and counts[num] > 0:
                res.append(num)
                counts[num] -= 1

        return res

(241)315. Count of Smaller Numbers After Self(Hard)

您將獲得一個整數數組nums,您必須返回一個新的計數數組。 counts數組具有其中count [i]是nums [i]右邊的較小元素的數量的屬性。
在這裏插入圖片描述

(242)315. Count of Smaller Numbers After Self(Hard)

您將獲得一個整數數組nums,您必須返回一個新的計數數組。 counts數組具有其中count [i]是nums [i]右邊的較小元素的數量的屬性。
在這裏插入圖片描述

思路:
使用歸併算法的思想(不太懂)

    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def sort(enum):
            half = len(enum) / 2
            if half:
                left, right = sort(enum[:half]), sort(enum[half:])
                for i in range(len(enum))[::-1]:
                    if not right or left and left[-1][1] > right[-1][1]:
                        smaller[left[-1][0]] += len(right)
                        enum[i] = left.pop()
                    else:
                        enum[i] = right.pop()
            return enum
        smaller = [0] * len(nums)
        sort(list(enumerate(nums))) #[(0, 5), (1, 2), (2, 6), (3, 1)])
        return smaller

(243)300. Longest Increasing Subsequence(Medium)

給定未排序的整數數組,找到最長的增加子序列的長度。
在這裏插入圖片描述
可能有多個LIS組合,只需要您返回長度。 您的算法應該以O(n2)複雜度運行。
你能把它提高到O(n log n)的時間複雜度嗎?

思路:
## 思路一O(n2)
##如果當前nums[i]>nums[j] 那麼dp[i]就等於dp[j]中最大的+1
## 思路二 O(nlogn)
##1. 將第1個數字加入解集
##2. 依次讀取後面的數字,如果此數字比解集中最後一個數字大,則將此數字追加到解集後
##3. 否則,用這個數字替換解集中第一個比此數字大的數,因爲解集是有序的,所以可以使用二分法

    ##例子:
    ##舉個栗子,輸入爲[1,4,6,2,3,5]:
    ##-解集初始化爲[1];
    ##-讀到4,將其追加到解集中,解集變爲[1,4];
    # -讀到6,將其追加到解集中,解集變爲[1,4,6];
    # -讀到2,用其替換解集中的4,解集變爲[1,2,6],注意此時解集不是一個合法解,因爲2是在6後出現的,但是解集的長度始終標識着當前最長序列的長度;
    # -讀到3,用其替換解集中的6,解集變爲[1,2,3];
    # -讀到5,將其追加到解集中,解集變爲[1,2,3,5],得到答案爲解集長度4。
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        ## 思路一O(n2)
        ##如果當前nums[i]>nums[j] 那麼dp[i]就等於dp[j]中最大的+1
        # n = len(nums)
        # if n ==0:
        #     return 0
        # dp = [0]*n
        # dp[0] =1
        # for i in range(n):
        #     cur_max = 0
        #     for j in range(0,i):
        #         if nums[i]>nums[j]:
        #             cur_max = max(cur_max,dp[j])
        #     dp[i] = cur_max + 1
        # return max(dp)
        
        ## 思路二 O(nlogn)
        ##1. 將第1個數字加入解集
        ##2. 依次讀取後面的數字,如果此數字比解集中最後一個數字大,則將此數字追加到解集後
        ##3. 否則,用這個數字替換解集中第一個比此數字大的數,因爲解集是有序的,所以可以使用二分法
        
        ##例子:
        ##舉個栗子,輸入爲[1,4,6,2,3,5]:
        ##-解集初始化爲[1];
        ##-讀到4,將其追加到解集中,解集變爲[1,4];
        # -讀到6,將其追加到解集中,解集變爲[1,4,6];
        # -讀到2,用其替換解集中的4,解集變爲[1,2,6],注意此時解集不是一個合法解,因爲2是在6後出現的,但是解集的長度始終標識着當前最長序列的長度;
        # -讀到3,用其替換解集中的6,解集變爲[1,2,3];
        # -讀到5,將其追加到解集中,解集變爲[1,2,3,5],得到答案爲解集長度4。
        n = len(nums)
        if n ==0:
            return 0
        res = [nums[0]]
        for i in range(1,n):
            if nums[i]>res[-1]:
                res.append(nums[i])
            else:
                index = self.midSearch(res,nums[i])
                res[index] = nums[i]
        return len(res)
    def midSearch(self,res,val):
        p = 0
        q = len(res)-1
        while(p<=q):
            m = (p+q)//2
            if res[m] ==val:
                return m
            if res[m] >val:
                q = m -1
            else:
                p = m + 1
        return p

(244)354. Russian Doll Envelopes(Hard)

你有許多寬度和高度的信封,它們是一對整數(w,h)。當且僅當一個信封的寬度和高度都大於另一個信封的寬度和高度時,一個信封可以裝入另一個信封。
俄羅斯娃娃的最大信封數量是多少? (把一個放在另一個裏面)
不允許輪換。
在這裏插入圖片描述

    def maxEnvelopes(self, envelopes):
        """
        :type envelopes: List[List[int]]
        :rtype: int
        """
        res = []
        def compare(x,y):
            if x[0]==y[0]:
                if x[1]>y[1]:
                    return -1
                else:
                    return 1
            else:
                if x[0]<y[0]:
                    return -1
                else:
                    return 1
        envelopes.sort(cmp = compare)
        for i in envelopes:
            if not res or i[1]>res[-1]:
                res.append(i[1])
            else:
                left =0
                right = len(res)-1
                while left<=right:
                    mid = (left+right)//2
                    if res[mid]<i[1]:
                        left = mid +1
                    else:
                        right = mid-1
                res[left] = i[1]
        return len(res)

Matrix

(245)48. Rotate Image(Medium)

旋轉 n*n 矩陣
Rotate the image by 90 degrees (clockwise).

思路:沿着副對角線對摺,然後沿着中線對摺 or 沿着中線對摺,然後沿着對角線對摺

matrix[i][j],matrix[n-j-1][n-i-1] = matrix[n-j-1][n-i-1],matrix[i][j]
        n = len(matrix[0])
        #沿着副對角線對摺
        for i in range(len(matrix)):
            for j in range(len(matrix)-i-1):
                matrix[i][j],matrix[n-j-1][n-i-1] = matrix[n-j-1][n-i-1],matrix[i][j]
        #沿着水平線對摺
        for i in range(len(matrix)/2):
            for j in range(len(matrix[0])):
                matrix[i][j],matrix[n-i-1][j] = matrix[n-i-1][j],matrix[i][j]

(246)54. Spiral Matrix(Medium)

給一個矩陣,輸出其螺旋結果

定義rowBegin,rowEnd ,colBegin,colEnd
然後循環.
注意: 當往上走的時候,和往左走的時候,記得判斷該行或該列符不符合要求(即判斷rowB<=rowE or colB<=colE)

    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if len(matrix)==0:
            return []
        rowB =0
        rowE = len(matrix)-1
        colB = 0
        colE = len(matrix[0])-1
        res =[]
        while(rowB<=rowE and colB<=colE):
            #right
            for i in range(colB,colE+1):
                res.append(matrix[rowB][i])
            rowB +=1
            
            #down
            for i in range(rowB,rowE+1):
                res.append(matrix[i][colE])
            colE -=1
            
            if rowB<=rowE:
                #left
                for i in range(colE,colB-1,-1):
                    res.append(matrix[rowE][i])
            rowE -=1
            
            #up
            if colB<=colE:
                for i in range(rowE,rowB-1,-1):
                    res.append(matrix[i][colB])
            colB +=1
        return res

(247)59. Spiral Matrix II(Medium)

給定正整數n,以螺旋順序生成填充有從1到n2的元素的方陣。
Input: 3
Output: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

思路:和前面的題類似,用上下左右四個指針

    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        res = [[0 for _ in range(n)] for i in range(n)]
        left =0
        right=n-1
        down =n-1
        up =0
        #left
        j =1
        while(left<=right and up<=down):
            #left
            for i in range(left,right+1):
                res[up][i] = j
                j +=1
            up +=1

            #down
            for i in range(up,down+1):
                res[i][right] = j
                j+=1
            right -=1   
        #right
            for i in range(right,left-1,-1):
                res[down][i] = j
                j +=1
            down -=1

        #up
            for i in range(down,up-1,-1):
                res[i][left] = j
                j+=1
            left +=1
        
        return res

(248)73. Set Matrix Zeroes

給定m×n矩陣,如果元素爲0,則將其整個行和列設置爲0.就地執行。
在這裏插入圖片描述

思路:
#不需要空間的做法
#首先判斷第一行和第一列有沒有0,用bool保存
#利用第一行和第一列來存儲

def setZeroes(self, matrix):
    """
    :type matrix: List[List[int]]
    :rtype: None Do not return anything, modify matrix in-place instead.
    """
    #不需要空間的做法
    #首先判斷第一行和第一列有沒有0,用bool保存
    #利用第一行和第一列來存儲
    first_row = False
    first_col = False
    for i in range(len(matrix)):
        if matrix[i][0]==0:
            first_row = True
    for j in range(len(matrix[0])):
        if matrix[0][j]==0:
            first_col = True
    
    for i in range(1,len(matrix)):
        for j in range(1,len(matrix[0])):
            if matrix[i][j] ==0:
                matrix[0][j] = matrix[i][0] = 0 
    
    for i in range(1,len(matrix)):
        for j in range(1,len(matrix[0])):
            if matrix[0][j]==0 or matrix[i][0] ==0:
                matrix[i][j] =0
    
    if first_row:
        for i in range(len(matrix)):
            matrix[i][0] =0
    if first_col:
        for j in range(len(matrix[0])):
            matrix[0][j] =0

(249)311 Sparse Matrix Multiplication(Medium)

給定兩個稀疏矩陣A和B,返回AB的結果。 您可以假設A的列號等於B的行號。

思路:
一個 i x k 的矩陣A乘以一個 k x j 的矩陣B會得到一個 i x j 大小的矩陣C,矩陣中的某個元素
C[i][j]=A[i][0]*B[0][j] + A[i][1]*B[1][j] + … + A[i][k]*B[k][j],
爲了不重複計算0乘0,遍歷A, B數組, 如果A[i][k], B[K][J]不爲0,才繼續計算,累加結果
res[i][j] += A[i][k] * B[k][j]。

def multiply(self,A,B):
        """
        :type A: List[List[int]]
        :type B: List[List[int]]
        :rtype: List[List[int]]
        """
        res = [[0]*len(B[0]) for _ in range(len(A))]
        for i in range(len(A)):
        	for k in range(len(A[0])):
        		if A[i][k]:
        			for j in range(len(B[0])):
        				res[i][j] += A[i][k] *B[k][j]
        return res

(250)329. Longest Increasing Path in a Matrix(Hard)

給定整數矩陣,找到最長增加路徑的長度。 從每個單元格,您可以移動到四個方向:左,右,上或下。您可能不會沿對角線移動或移動到邊界之外(即不允許環繞)。
在這裏插入圖片描述

思路:
爲每個元素使用dfs,並且使用dp存儲最大路徑

    def longestIncreasingPath(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: int
        """
        
        def dfs(i,j):
            #判斷時候訪問過,訪問過就跳過
            if not dp[i][j]:
                val = matrix[i][j]
                dp[i][j] = 1 + max(
                    dfs(i-1,j) if i and val>matrix[i-1][j] else 0,
                    dfs(i,j-1) if j and val>matrix[i][j-1] else 0,
                    dfs(i+1,j) if i<row-1 and val>matrix[i+1][j] else 0,
                    dfs(i,j+1) if j<col-1 and val>matrix[i][j+1] else 0
                                  )
            return dp[i][j]
        if not matrix or not matrix[0]:
            return 0
        row,col = len(matrix),len(matrix[0])
        dp = [[0]*col for _ in range(row)]
        return max(dfs(x,y) for x in range(row) for y in range(col))

(251)378. Kth Smallest Element in a Sorted Matrix(Medium)

給定n×n矩陣,其中每個行和列按升序排序,找到矩陣中的第k個最小元素。 請注意,它是排序順序中的第k個最小元素,而不是第k個不同元素。
在這裏插入圖片描述

思路:
使用堆來操作

heapq.heappush()函數把值加入堆中
heapq.heappop() 函數彈出堆中最小值
    def kthSmallest(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        result, heap,n = None, [],len(matrix)
        heapq.heappush(heap, (matrix[0][0], 0, 0))
        while k > 0:
            result, i, j = heapq.heappop(heap)
            #除了第一行需要加入,其他行通過列也加入了。
            if i == 0 and j + 1 < n:
                heapq.heappush(heap, (matrix[i][j + 1], i, j + 1))
            if i + 1 < n:
                heapq.heappush(heap, (matrix[i + 1][j], i + 1, j))
            k -= 1
        return result

(252)74. Search a 2D Matrix(Medium)

編寫一個有效的算法,搜索sorted m×n矩陣中的值。
每行中的整數從左到右排序。 每行的第一個整數大於前一行的最後一個整數。
在這裏插入圖片描述

先找到值再哪一行,然後再用中值查詢

def searchMatrix(self, matrix, target):
    """
    :type matrix: List[List[int]]
    :type target: int
    :rtype: bool
    """
    i =0
    if matrix ==[] or matrix[0]==[]:
        return False
    while(i<len(matrix)):
        if matrix[i][-1]<target:
            i+=1
            continue
        elif matrix[i][-1]>target:
            left = 0
            right = len(matrix[i])-1
            while left<=right:
                middle = (left+right)/2
                if matrix[i][middle]<target:
                    left = middle+1
                elif matrix[i][middle]>target:
                    right = middle-1
                else:
                    return True
            return False
        else:
            return True
    return False

(253)240. Search a 2D Matrix II(Medium)

編寫一個有效的算法,搜索m×n矩陣中的值。該矩陣具有以下屬性:
每行中的整數按從左到右的升序排序。
每列中的整數按從上到下的順序排序。
在這裏插入圖片描述

思路:
首先判斷邊界條件
從右上角開始,這樣當target比當前元素小,就走左邊,如果target比當前元素大,就走右邊。

    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix or not matrix[0]:
            return False
        row = 0
        col = len(matrix[0])-1
        while row<len(matrix) and col >= 0:
            if matrix[row][col] <target:
                row +=1
            elif matrix[row][col]>target:
                col -=1
            elif matrix[row][col]==target:
                return True
        return False

(254)370. Range Addition(Medium)

ssume you have an array of length n initialized with all 0’s and are given k update operations.
Each operation is represented as a triplet: [startIndex, endIndex, inc] which increments each element of subarray A[startIndex … endIndex] (startIndex and endIndex inclusive) with inc.
Return the modified array after all k operations were executed.
在這裏插入圖片描述在這裏插入圖片描述
Hint:
Thinking of using advanced data structures? You are thinking it too complicated.
For each update operation, do you really need to update all elements between i and j?
Update only the first and end element is sufficient.
The optimal time complexity is O(k + n) and uses O(1) extra space.

思路:
1、根據提示,每次只需要修改startIndex的值,和endldx+1的值就可以。因爲通過利用prefix sum的累加性質,把+val的效果擴散到startindex以後,那麼我們就需要在endidx+1及其以後的位置抵消掉這個影響。

def getModifiedArray(self,length,list):
	result = [0]*(length+1)
	for a in list:
		result[a[0]] +=a[2]
		result[a[1]+1] -=a[2]
	for i in range(1,length):
		result[i] +=result[i-1]
	return result[::-1]

(255)79. Word Search(Medium)

Given a 2D board and a word, find if the word exists in the grid.
在這裏插入圖片描述

使用dfs,記得訪問過的要標註

    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        if not board:
            return False
        for i in xrange(len(board)):
            for j in xrange(len(board[0])):
                if self.dfs(board, i, j, word):
                    return True
        return False

    # check whether can find word, start at (i,j) position    
    def dfs(self, board, i, j, word):
        if len(word) == 0: # all the characters are checked
            return True
        if i<0 or i>=len(board) or j<0 or j>=len(board[0]) or word[0]!=board[i][j]:
            return False
        tmp = board[i][j]  # first character is found, check the remaining part
        board[i][j] = "#"  # avoid visit agian 
        # check whether can find "word" along one direction
        res = self.dfs(board, i+1, j, word[1:]) or self.dfs(board, i-1, j, word[1:]) \
        or self.dfs(board, i, j+1, word[1:]) or self.dfs(board, i, j-1, word[1:])
        board[i][j] = tmp
        return res

(256)296. Best Meeting Point(Hard)

A group of two or more people wants to meet and minimize the total travel distance. You are given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group. The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.
Example:
Input:
1 - 0 - 0 - 0 - 1
| | | | |
0 - 0 - 0 - 0 - 0
| | | | |
0 - 0 - 1 - 0 - 0
Output: 6
Explanation: Given three people living at (0,0), (0,4), and (2,2):
The point (0,2) is an ideal meeting point, as the total travel distance
of 2+2+2=6 is minimal. So return 6.
Hint:
Try to solve it in one dimension first. How can this solution apply to the two dimension case?

思路:
這道題的我們先從一維開始想
兩個點A和B,P在A,B區間之內距離都是AB,A,B區間之外距離都會大於AB
當我們在左右加上兩個點C和D,這樣當P在AB之間,四個點的距離之和都是AB+CD的距離,當P不在AB之間,距離一定會大於AB+CD的和。二維的情況就是兩個一維的相加。

def minTotalDistance(self,grid):
	rows,col = [],[]
	for i in range(len(grid)):
		for j in range(len(grid[0])):
			if grid[i][j] ==1:
				row.append(i)
				col.append(j)
	return self.TotalDistance(rows)+self.TotalDistance(cols)
def TotalDistances(self,v):
	res = 0
	v.sort()
	i,j = 0,len(v)-1
	while(i<j):
		 res+=v[j]-v[i]
		 j -=1
		 i+=1
	return res
	

(257)361 Bomb Enemy(Midium)

給定2D網格,每個單元格可以是牆“W”,敵人“E”或空“0”(數字0),使用一個炸彈返回可以殺死的最大敵人。炸彈從種植點殺死同一行和列中的所有敵人,直到它撞到牆壁,因爲牆壁太強而不能被摧毀。
請注意,您只能將炸彈放在空單元格中。
Example:
Input: [[“0”,“E”,“0”,“0”],[“E”,“0”,“W”,“E”],[“0”,“E”,“0”,“0”]]
Output: 3
Explanation: For the given grid,
0 E 0 0
E 0 W E
0 E 0 0
Placing a bomb at (1,1) kills 3 enemies.

思路:
最普通的做法就是,使用4個二維數組left、right、up、down分別表示上下左右分別能炸死多少人。然後使用兩重循環確定max(left[i][j] + right[i][j] + up[i][j] + down[i][j])。時間複雜度爲O(mn)空間複雜度爲O(4m*n)

第二種做法是用二重循環遍歷grid的時候,如果發現該格子處於第一行或者該格子的上面一個格子是牆,那麼就把從該位置到下方第一個牆(或者邊界)之間的敵人數目統計出來並且記錄下來;同理,如果發現該格子處於第一列或者該格子的左邊一個格子是牆,那麼就把從該位置到右方第一個牆(或者邊界)之間的敵人數目統計出來並且記錄下來。之後如果發現該位置是空位置,說明可以放炸彈,就更新最終結果。該方法的巧妙之處在於每個縱區間和每個橫區間內的敵人個數只會被統計一次。因此雖然在代碼中兩重循環內部還有一重循環,但是由於有if條件語句的存在,實際上總的時間複雜度仍然是O(m*n)。

def maxKilledEnemies(self,grid):
	if not grid or not grid[0]:
		return 0
	row = len(grid)
	col = len(grid[0])
	ans = 0
	for i in range(row):
		for j in range(col):
			colHits = 0
			if i==0 or grid[i-1][j] =='W':
				for k in range(i,row):
					if grid[k][j] =='W':
						berak
					else:
						colHits[j] +=(grid[k][j] == 'E')
			rowHits = 0
			if j==0 or grid[i][j-1]=='W':
				for k in range(j,col):
					if grid[i][k] =='W':
						break
					else:
						rowHits +=(grid[i][k] =='E')
			if grid[i][j]=='0':
				ans = max(ans,rowHit+colHit)
	return ans

(258)317. Shortest Distance from All Buildings(Hard)(未寫)

You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where:
Each 0 marks an empty land which you can pass by freely.
Each 1 marks a building which you cannot pass through.
Each 2 marks an obstacle which you cannot pass through.
Example:
在這裏插入圖片描述

(259)302 Smallest Rectangle Enclosing Black Pixels(Hard)(未寫)

(260)36.有效的數獨(Medium)

橫豎不能包含相同數字,每個小的九宮格里面不能包含相同數字
給定一個九宮格,判斷是不是合法的數獨

思路:
1、使用一個set來存儲已經存在的元素
2、使用(i/3,j/3)來表示小的九宮格

        big = set()
        for i in xrange(0,9):
            for j in xrange(0,9):
                if board[i][j]!='.':
                    #cur is string
                    cur = board[i][j]
                    if (i,cur) in big or (cur,j) in big or (i/3,j/3,cur) in big:
                        return False
                    big.add((i,cur))
                    big.add((cur,j))
                    big.add((i/3,j/3,cur))
        return True

(261)37.數獨解算器(Hard)

思路:
1、需要有個判別器,判別加入元素後會不會是合法的數獨,判斷小九宮格有點困難
2、接着使用遞歸,加入元素

def solveSudoku(self, board):
    """ 
    :type board: List[List[str]]
    :rtype: None Do not return anything, modify board in-place instead.
    """
    def isVaild(i,j,c):
        for k in range(9):
            if (board[i][k]==c): return  False
            if (board[k][j]==c): return False
            if (board[3 * (i / 3) + k / 3][3 * (j / 3) + k % 3]==c): return False
        return True
    def slove():
        for i in range(9):
            for j in range(9):
                if board[i][j] =='.':
                    for c in range(1,10):
                        if (isVaild(i,j,str(c))):
                            board[i][j] = str(c)
                        else:
                            continue
                        if (slove()==False):
                            board[i][j] = '.'
                        else:
                            return True
                    return False
        return True
                            
                
    slove()

未完,待續。。。
此篇由於篇幅限制,轉入第二篇,(包括有常見的面試題哦,第二篇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章