LeetCode習題整理(中等)II

煎餅排序

給定數組 A,我們可以對其進行煎餅翻轉:我們選擇一些正整數 k <= A.length,然後反轉 A 的前 k 個元素的順序。我們要執行零次或多次煎餅翻轉(按順序一次接一次地進行)以完成對數組 A 的排序。

返回能使 A 排序的煎餅翻轉操作所對應的 k 值序列。任何將數組排序且翻轉次數在 10 * A.length 範圍內的有效答案都將被判斷爲正確。

示例 1:

輸入:[3,2,4,1]
輸出:[4,2,4,3]
解釋:
我們執行 4 次煎餅翻轉,k 值分別爲 4,2,4,和 3。
初始狀態 A = [3, 2, 4, 1]
第一次翻轉後 (k=4): A = [1, 4, 2, 3]
第二次翻轉後 (k=2): A = [4, 1, 2, 3]
第三次翻轉後 (k=4): A = [3, 2, 1, 4]
第四次翻轉後 (k=3): A = [1, 2, 3, 4],此時已完成排序。
示例 2:

輸入:[1,2,3]
輸出:[]
解釋:
輸入已經排序,因此不需要翻轉任何內容。
請注意,其他可能的答案,如[3,3],也將被接受。

提示:

1 <= A.length <= 100
A[i] 是 [1, 2, …, A.length] 的排列

class Solution(object):
    def pancakeSort(self, A):
        """
        :type A: List[int]
        :rtype: List[int]
        """
        def tranv(A,index):#反轉前k個數
            k=[]
            for i in range(index+1):
                k.append(A[i])
            for i in range(index+1):
                A[i]=k.pop()
            return None
        n=len(A)
        ans=[]
        for i in range(n):
            if n>1:
                index,_=max(enumerate(A[:n]),key=lambda x:x[1])#找到前n個數的最大的索引
                if index >0:
                    tranv(A,index)#前index+1個數反轉
                    ans.append(index+1)
                tranv(A,n-1)
                ans.append(n)
                n-=1
            else:break
        return ans

複製帶隨機指針的鏈表

給定一個鏈表,每個節點包含一個額外增加的隨機指針,該指針可以指向鏈表中的任何節點或空節點。

要求返回這個鏈表的 深拷貝。

我們用一個由 n 個節點組成的鏈表來表示輸入/輸出中的鏈表。每個節點用一個 [val, random_index] 表示:

val:一個表示 Node.val 的整數。
random_index:隨機指針指向的節點索引(範圍從 0 到 n-1);如果不指向任何節點,則爲 null 。

示例 1:
在這裏插入圖片描述

輸入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
輸出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

在這裏插入圖片描述

輸入:head = [[1,1],[2,1]]
輸出:[[1,1],[2,1]]
示例 3:
在這裏插入圖片描述

輸入:head = [[3,null],[3,0],[3,null]]
輸出:[[3,null],[3,0],[3,null]]
示例 4:

輸入:head = []
輸出:[]
解釋:給定的鏈表爲空(空指針),因此返回 null。

提示:

-10000 <= Node.val <= 10000
Node.random 爲空(null)或指向鏈表中的節點。
節點數目不超過 1000 。

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        p = head
        result = None
        pre = None
        D = {}
        while p != None:
            node = Node(p.val, random=p.random)
            D[p] = node
            if pre != None:
                pre.next = node
            else:
                result = node
            pre = node
            p = p.next
        
        q = result
        while q != None:
            if q.random != None:
                q.random = D[q.random]
            q = q.next
        
        #print(result.val)
        return result

最長重複子數組

給兩個整數數組 A 和 B ,返回兩個數組中公共的、長度最長的子數組的長度。

示例 1:

輸入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
輸出: 3
解釋:
長度最長的公共子數組是 [3, 2, 1]。
說明:

1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100

class Solution(object):
    def findLength(self, A, B):
        """
        :type A: List[int]
        :type B: List[int]
        :rtype: int
        """
        
        m = len(A)
        n = len(B)

        if m>n:
            m, n, A, B = n, m, B, A

        tmp_list = []
        result = 0
        # 利用','.join([str(i) for i in B])將B轉化爲字符串且任意兩個元素之間用逗號間隔
        str_B = ',' + ','.join([str(i) for i in B]) + ','
        #print(type(str_B))

        for r in A:
            tmp_list.append(str(r))

            if ',' + ','.join(tmp_list) + ',' in str_B:
                result = max(result, len(tmp_list))

            else:
                tmp_list = tmp_list[1:]

        return result

數組中的第k個最大元素

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

示例 1:

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

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

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

import heapq
class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        result = heapq.nlargest(k,nums)
        return result[-1]

無重複字符的最長子串

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

示例 1:

輸入: “abcabcbb”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “abc”,所以其長度爲 3。
示例 2:

輸入: “bbbbb”
輸出: 1
解釋: 因爲無重複字符的最長子串是 “b”,所以其長度爲 1。
示例 3:

輸入: “pwwkew”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “wke”,所以其長度爲 3。
請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        '''
        if s == '':
            return 0
        s_list = list(s)
        Subs = []
        for j in range(1, len(s_list)+1):
            for i in range(j-1):
                Subs.append(s_list[i:j])
        
        #print(Subs)
        Lengest = 1
        for i in range(len(Subs)):
            L = len(set(Subs[i]))
            #print(L)
            if L == len(Subs[i]) and L > Lengest:
                #print(Subs[i])
                Lengest = L
        
        return Lengest
        '''
        # 哈希集合,記錄每個字符是否出現過
        occ = set()
        n = len(s)
        # 右指針,初始值爲 -1,相當於我們在字符串的左邊界的左側,還沒有開始移動
        rk, ans = 0, 0
        for i in range(n):
            if i != 0:
                # 左指針向右移動一格,移除一個字符
                occ.remove(s[i - 1])
            while rk < n and s[rk] not in occ:
                # 不斷地移動右指針
                occ.add(s[rk])
                rk += 1
            # 第 i 到 rk 個字符是一個極長的無重複字符子串
            ans = max(ans, rk - i)
        return ans


兩個字符串的刪除操作

給定兩個單詞 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步數,每步可以刪除任意一個字符串中的一個字符。

示例:

輸入: “sea”, “eat”
輸出: 2
解釋: 第一步將"sea"變爲"ea",第二步將"eat"變爲"ea"

提示:

給定單詞的長度不超過500。
給定單詞中的字符只含有小寫字母。

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        n, m = len(word1),len(word2)
        dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
        for i in range(1,n+1):
            for j in range(1,m+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1])# 最終回溯到dp[k][k],故跳過k到i和k到j之間的不等部分。
        return n+m-2*dp[n][m]

破壞迴文字符串

給你一個迴文字符串 palindrome ,請你將其中 一個 字符用任意小寫英文字母替換,使得結果字符串的字典序最小,且 不是 迴文串。

請你返回結果字符串。如果無法做到,則返回一個空串。

示例 1:

輸入:palindrome = “abccba”
輸出:“aaccba”
示例 2:

輸入:palindrome = “a”
輸出:""

提示:

1 <= palindrome.length <= 1000
palindrome 只包含小寫英文字母。

class Solution(object):
    def symmetry(self, L, Len):
        if Len == 0:
            return False

        i = 0
        j = Len-1

        if Len % 2 == 0:
            while i < j:
                if L[i] != L[j]:
                    break
                i += 1
                j -= 1

            
        else:    
            while i <= j:
                if L[i] != L[j]:
                    break
                i += 1
                j -= 1

        if i > j:
            return True
        else: return False



    def breakPalindrome(self, palindrome):
        """
        :type palindrome: str
        :rtype: str
        """
        n = len(palindrome)
        if n == 1:
            return ''
        for i in range(n):
            if palindrome[i] > 'a':
                sub = palindrome[:i]+'a'+palindrome[i+1:]
                if not self.symmetry(sub, n):
                    return sub
        if self.symmetry(palindrome, n):
            palindrome = palindrome[:-1]+'b'
        return palindrome

有效的括號字符串

給定一個只包含三種字符的字符串:( ,) 和 *,寫一個函數來檢驗這個字符串是否爲有效字符串。有效字符串具有如下規則:

任何左括號 ( 必須有相應的右括號 )。
任何右括號 ) 必須有相應的左括號 ( 。
左括號 ( 必須在對應的右括號之前 )。

  • 可以被視爲單個右括號 ) ,或單個左括號 ( ,或一個空字符串。
    一個空字符串也被視爲有效字符串。
    示例 1:

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

輸入: “(*)”
輸出: True
示例 3:

輸入: “(*))”
輸出: True
注意:

字符串大小將在 [1,100] 範圍內。

class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack = []                         #第一個棧用來記錄所有(的index
        star_stack = []                    #第二個棧用來記錄所有*的index
        for i in range(len(s)):
            if s[i] == "(":
                stack.append(i)
            elif s[i] == "*":
                star_stack.append(i)
            else:
                if not stack and not star_stack:
                    return False                        #如果直接讀到),直接return False
                if stack:
                    stack.pop()
                elif star_stack:
                    star_stack.pop()                    #先用(來消),再用*來消)
        while stack and star_stack:
            if stack.pop() > star_stack.pop():          #在所有)都處理了之後,只需要考慮(和*的index,此時的*全部當作)來考慮,比較index即可
                return False
        return not stack                   #判斷是否有多餘的(

最大加號標誌

(第一次寫思路,有錯誤望指正)
動態規劃:
因爲判斷是否形成加法標誌需要上下左右四個臂的長度,所以不能單一的變量表示不了這個狀態;
我這邊是用四個變量表示狀態,分別爲上下左右的連續臂展長度;
上左狀態變量在正序遍歷數組的時候確定,下右狀態變量用逆序遍歷數組的時候確定;
狀態轉移方程爲 dp[i+1][j+1][0]=dp[i][j+1][0]+1 (上狀態);
最後加法的階數爲四個狀態中最下的那個確定;

class Solution:
    def orderOfLargestPlusSign(self, N: int, mines: List[List[int]]) -> int:
        l=[[1 for _ in range(N)] for _ in range(N)]
        for i in mines:
            l[i[0]][i[1]]=0
        # 四個位置分別記錄上左下右四個方向上的1的個數,且dp[i+1][j+1]記錄的是以[i,j]爲中心點。
        dp = [[[0,0,0,0] for _ in range(N+2)] for _ in range(N+2)]
        for i in range(N): #先正向遍歷
            for j in range(N):
                if l[i][j]==0:
                    continue
                else:
                    dp[i+1][j+1][0]=dp[i][j+1][0]+1
                    dp[i+1][j+1][1]=dp[i+1][j][1]+1
        for i in range(N-1,-1,-1):
            for j in range(N-1,-1,-1): #逆向遍歷
                if l[i][j]==0:
                    continue
                else:
                    dp[i + 1][j + 1][2] = dp[i+2][j + 1][2]+1
                    dp[i + 1][j + 1][3] = dp[i + 1][j+2][3]+1
        m=max([max([min(i) for i in j]) for j in dp])
        return m

三數之和

給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。

注意:答案中不可以包含重複的三元組。

示例:

給定數組 nums = [-1, 0, 1, 2, -1, -4],

滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        n=len(nums)
        res=[]
        if(not nums or n<3):
            return []
        nums.sort()
        res=[]
        for i in range(n):
            if(nums[i]>0):
                return res
            if(i>0 and nums[i]==nums[i-1]):
                continue
            L=i+1
            R=n-1
            while(L<R):
                if(nums[i]+nums[L]+nums[R]==0):
                    res.append([nums[i],nums[L],nums[R]])
                    while(L<R and nums[L]==nums[L+1]):
                        L=L+1
                    while(L<R and nums[R]==nums[R-1]):
                        R=R-1
                    L=L+1
                    R=R-1
                elif(nums[i]+nums[L]+nums[R]>0):
                    R=R-1
                else:
                    L=L+1
        return res

分數排名

編寫一個 SQL 查詢來實現分數排名。

如果兩個分數相同,則兩個分數排名(Rank)相同。請注意,平分後的下一個名次應該是下一個連續的整數值。換句話說,名次之間不應該有“間隔”。

±—±------+
| Id | Score |
±—±------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
±—±------+
例如,根據上述給定的 Scores 表,你的查詢應該返回(按分數從高到低排列):

±------±-----+
| Score | Rank |
±------±-----+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 2 |
| 3.65 | 3 |
| 3.65 | 3 |
| 3.50 | 4 |
±------±-----+

select Score, dense_rank() over (order by Score desc)  as `Rank`
from Scores;

字母位移

有一個由小寫字母組成的字符串 S,和一個整數數組 shifts。

我們將字母表中的下一個字母稱爲原字母的 移位(由於字母表是環繞的, ‘z’ 將會變成 ‘a’)。

例如·,shift(‘a’) = ‘b’, shift(‘t’) = ‘u’,, 以及 shift(‘z’) = ‘a’。

對於每個 shifts[i] = x , 我們會將 S 中的前 i+1 個字母移位 x 次。

返回將所有這些移位都應用到 S 後最終得到的字符串。

示例:

輸入:S = “abc”, shifts = [3,5,9]
輸出:“rpl”
解釋:
我們以 “abc” 開始。
將 S 中的第 1 個字母移位 3 次後,我們得到 “dbc”。
再將 S 中的前 2 個字母移位 5 次後,我們得到 “igc”。
最後將 S 中的這 3 個字母移位 9 次後,我們得到答案 “rpl”。
提示:

1 <= S.length = shifts.length <= 20000
0 <= shifts[i] <= 10 ^ 9

import numpy as np
class Solution(object):
    def shiftingLetters(self, S, shifts):
        """
        :type S: str
        :type shifts: List[int]
        :rtype: str
        """
        '''動態規劃法
        m = len(S)
        n = len(shifts)
        if n > m:
            return

        S = list(S)
        
        # dp[j][i] 表示第j次位移後S[i]的值
        dp = [[S[i] for i in range(n)] for j in range(n)]
        
        for j in range(1, n+1):
            for i in range(j):
                if j == 1:
                    dp[j-1][i] = chr((ord(dp[j-1][i]) - 97 + shifts[j-1]) % 26 + 97)
                    #print(dp[0])
                else:
                    dp[j-1][i] = chr((ord(dp[j-2][i]) - 97 + shifts[j-1]) % 26 + 97)
        
        if m == n:
            return ''.join(dp[-1][:])
        
        return ''.join(dp[-1][:]) + ''.join(S[n+1])
        '''
        res=[]
        total=sum(shifts)
        for i in range(len(S)):
            x=(ord(S[i])+total-97)%26+97
            res.append(chr(x))   
            total-=shifts[i]
        return ''.join(res)

索引處的解碼字符串

給定一個編碼字符串 S。爲了找出解碼字符串並將其寫入磁帶,從編碼字符串中每次讀取一個字符,並採取以下步驟:

如果所讀的字符是字母,則將該字母寫在磁帶上。
如果所讀的字符是數字(例如 d),則整個當前磁帶總共會被重複寫 d-1 次。
現在,對於給定的編碼字符串 S 和索引 K,查找並返回解碼字符串中的第 K 個字母。

示例 1:

輸入:S = “leet2code3”, K = 10
輸出:“o”
解釋:
解碼後的字符串爲 “leetleetcodeleetleetcodeleetleetcode”。
字符串中的第 10 個字母是 “o”。
示例 2:

輸入:S = “ha22”, K = 5
輸出:“h”
解釋:
解碼後的字符串爲 “hahahaha”。第 5 個字母是 “h”。
示例 3:

輸入:S = “a2345678999999999999999”, K = 1
輸出:“a”
解釋:
解碼後的字符串爲 “a” 重複 8301530446056247680 次。第 1 個字母是 “a”。

提示:

2 <= S.length <= 100
S 只包含小寫字母與數字 2 到 9 。
S 以字母開頭。
1 <= K <= 10^9
解碼後的字符串保證少於 2^63 個字母。

思路:
如果S=‘vk3u5xhq2v’, 這種時候需要找到每一個操作的原始和解碼字符索引, 記爲(x, y), x表示decode(S)中的位置(從1開始計算), y表示S中的位置(從0開始計算)
最終的字符爲’vkuxhqv’
索引記錄表對應的就是[v(1,0), k(2,1), 3(6,1), u(7,2), 5(35,2), x(36,3), h(37,4), q(38,5), 2(76,5), v(77,6)]
可以根據K值提前結束索引記錄過程, 比如假設K=50, 上面的S在2(76,5)的時候由於76>=50, 跳出循環
然後開始逆向找K對應的應該是原始S中的哪個字符, 類似於進制轉換, 把K與每個decode(S)取餘, 結果爲0的時候說明答案就是對應操作中的原始位置.

class Solution(object):
    def decodeAtIndex(self, S, K):
        """
        :type S: str
        :type K: int
        :rtype: str
        """
        i = cnt = 0
        c = ''
        x = []# x[i] = [解碼後的位置(從1開始),解碼前的位置(從0開始)]
        while i < len(S) and cnt < K:
            if S[i].isdigit():# S[i]是數字
                cnt *= int(S[i])
                if S[i-1].isdigit(): x[-1][0] = cnt# 若上一個也是數字,則修改上一次的解碼後位置。
                else: x.append([cnt, len(c) - 1])# 若上一個不是數字,則記錄這次的位置。即上一個字母經過複製在當下的位置。
            else:# S[i]是字母
                c += S[i]
                cnt += 1
                x.append([cnt, len(c) - 1])
            i += 1
        ret = ''
        while x:
            t = x.pop()
            K %= t[0]
            if K == 0:
                ret = c[t[1]]
                break
        return ret

模式匹配

你有兩個字符串,即pattern和value。 pattern字符串由字母"a"和"b"組成,用於描述字符串中的模式。例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat"是"a",“go"是"b”),該字符串也匹配像"a"、"ab"和"b"這樣的模式。但需注意"a"和"b"不能同時表示相同的字符串。編寫一個方法判斷value字符串是否匹配pattern字符串。

示例 1:

輸入: pattern = “abba”, value = “dogcatcatdog”
輸出: true
示例 2:

輸入: pattern = “abba”, value = “dogcatcatfish”
輸出: false
示例 3:

輸入: pattern = “aaaa”, value = “dogcatcatdog”
輸出: false
示例 4:

輸入: pattern = “abba”, value = “dogdogdogdog”
輸出: true
解釋: “a”=“dogdog”,b="",反之也符合規則
提示:

0 <= len(pattern) <= 1000
0 <= len(value) <= 1000
你可以假設pattern只包含字母"a"和"b",value僅包含小寫字母。

思路:
存在以下幾種情況:
1)pattern和value都爲空,返回True
2)僅pattern爲空: 返回False
3)僅value爲空,這時候分兩種情況:如果pattern中只存在a或者b一種,那麼令a或者b爲空,符合條件,返回True;否則,由於不同pattern不能表示同一字符串,返回False
4)都不爲空的情況下:如果pattern中只有a或者b一種,那意味這value全由一種字符串構成。它需滿足以下兩個條件:首先,value的長度需要被pattern的長度整除;其次,value中的每一個字符串需要都是a,根據value長度和pattern長度很容易計算出a的長度,然後順次取出看是否相同即可。
5)如果pattern中同時含有a和b,也有兩種情況。第一種,其中一個pattern只有一個,假設a只有一個,那麼只需要令a= value, b=“”,即可,所以直接返回True
6)另一種情況,同時含有a, b且個數都大於1。這時候,我們先統計pattern中a, b各有多少個,然後遍歷a的所有可能長度,對於某個確定的a的長度,結合a的個數和b的個數以及value的長度,我們即可以確定b的長度。這時候需要滿足以下條件:首先b的長度也必須是整數;其次,value只能由a, b結合而成。

class Solution(object):
    def patternMatching(self, pattern, value):
        """
        :type pattern: str
        :type value: str
        :rtype: bool
        """
        if not value and not pattern: return True
        if not pattern: return False
        if not value:
            if len(pattern)==1: return True
            else: return False
        if len(set(pattern))==1: # 如果只有一種pattern
            if len(value)%len(pattern)!=0:
                return False
            length = len(value) // len(pattern)
            return all([value[i:i+length]==value[0:length] for i in range(0, len(value),length)])

        if pattern.count('a')==1 or pattern.count('b')==1:# 兩種pattern但是其中一種只有一個,只需要另這個爲value,另外一個爲空即可
            return True
        
        cnt_a = pattern.count('a')
        cnt_b = pattern.count('b')
        for i in range(len(value)//cnt_a): # 遍歷a的所有可能長度
            remain = len(value) - i * cnt_a 
            if remain % cnt_b != 0:
                continue
            j = remain // cnt_b
            set_a = set()
            set_b = set()
            p = 0
            for s in pattern:
                if s=='a':
                    set_a.add(value[p:p+i])
                    p += i
                else:
                    set_b.add(value[p:p+j])
                    p += j
            if len(set_a)==len(set_b)==1:
                return True
        return False

二倍數對數組

給定一個長度爲偶數的整數數組 A,只有對 A 進行重組後可以滿足 “對於每個 0 <= i < len(A) / 2,都有 A[2 * i + 1] = 2 * A[2 * i]” 時,返回 true;否則,返回 false。

示例 1:

輸入:[3,1,3,6]
輸出:false
示例 2:

輸入:[2,1,2,6]
輸出:false
示例 3:

輸入:[4,-2,2,-4]
輸出:true
解釋:我們可以用 [-2,-4] 和 [2,4] 這兩組組成 [-2,-4,2,4] 或是 [2,4,-2,-4]
示例 4:

輸入:[1,2,4,16,8,4]
輸出:false

提示:

0 <= A.length <= 30000
A.length 爲偶數
-100000 <= A[i] <= 100000

from collections import Counter
class Solution(object):
    def canReorderDoubled(self, A):
        """
        :type A: List[int]
        :rtype: bool
        """
        count = Counter(A)#對於不存在的key,count[key]=0
        # 注意是對count進行排序
        # sorted(count, key=abs)等價於sorted(count.keys(),key = abs)
        for i in sorted(count, key=abs):
            if count[i] > count[i*2]:
                return False
            count[i*2] -= count[i]
        return True

鏈表隨機節點

給定一個單鏈表,隨機選擇鏈表的一個節點,並返回相應的節點值。保證每個節點被選的概率一樣。

進階:
如果鏈表十分大且長度未知,如何解決這個問題?你能否使用常數級空間複雜度實現?

示例:

// 初始化一個單鏈表 [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);

// getRandom()方法應隨機返回1,2,3中的一個,保證每個元素被返回的概率相等。
solution.getRandom();

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

    def __init__(self, head):
        """
        @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node.
        :type head: ListNode
        """
        assert head is not None
        self.head = head        
        self.length = self.Length()

    def Length(self):
        p = self.head
        L = 0
        while p != None:
            L += 1
            p = p.next
        return L

    def getRandom(self):
        """
        Returns a random node's value.
        :rtype: int
        """
        
        k = random.randint(0, self.length-1)
        #print(k)
        p = self.head
        L = 0
        while p != None:
            if L == k:
                break
            L += 1
            p = p.next
        if p == None:
            print(k, self.length)
        return p.val


# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()

有序數組中的單一元素

給定一個只包含整數的有序數組,每個元素都會出現兩次,唯有一個數只會出現一次,找出這個數。

示例 1:

輸入: [1,1,2,3,3,4,4,8,8]
輸出: 2
示例 2:

輸入: [3,3,7,7,10,11,11]
輸出: 10
注意: 您的方案應該在 O(log n)時間複雜度和 O(1)空間複雜度中運行。

import collections
class Solution(object):
    def singleNonDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        count = collections.Counter(nums)
        result = 0
        for key, value in count.items():
            if value == 1:
                result = key
        return result

等差數列劃分

如果一個數列至少有三個元素,並且任意兩個相鄰元素之差相同,則稱該數列爲等差數列。

例如,以下數列爲等差數列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下數列不是等差數列。

1, 1, 2, 5, 7

數組 A 包含 N 個數,且索引從0開始。數組 A 的一個子數組劃分爲數組 (P, Q),P 與 Q 是整數且滿足 0<=P<Q<N 。

如果滿足以下條件,則稱子數組(P, Q)爲等差數組:

元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。並且 P + 1 < Q 。

函數要返回數組 A 中所有爲等差數組的子數組個數。

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三個子等差數組: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

思路:
首先遍歷原數組 nums,用數組 diffs 存儲相鄰兩個元素之間的差值。

然後遍歷 diffs,用數組 cons 存儲其中連續相同的差值的數目,比如有 33 個 33 連在一起,意味着原數組中這個位置存在一個最大長度爲 44 的等差數列。

然後遍歷 cons,對於長度爲 n 的等差數列,其所有的長度大於等於 33 的子數列都是等差數列,則一共有 (n-2)(n-1)/2 個等差數列。
全部相加得到結果。

比如:

nums = [1,2,3,4,5,6,12,14,16]
diffs = [1,1,1,1,1,6,2,2]
cons = [5,1,2]
將 1 捨去,nums 中有長度爲 5+1 和 2+1 的等差數列
result = (6-2)(6-1)/2 + (3-2)(3-1)/2

class Solution(object):
    def numberOfArithmeticSlices(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
         # 第一次遍歷
        diffs = []
        for i in range(len(nums) - 1):
            diffs.append(nums[i + 1] - nums[i])
            
        # 第二次遍歷
        cons = []
        a = 1
        for i in range(1, len(diffs)):
            if diffs[i] == diffs[i - 1]:
                a += 1
            else:
                cons.append(a)
                a = 1
        cons.append(a)
        
        # 第三次遍歷
        res = 0
        for num in cons:
            res += int(self.calc(num))
        return res
        
    # 用於計算cons內每個數代表的等差數列個數
    def calc(self, n):
        if n == 1:
            return 0
        n += 1
        return (n-2)*(n-1)/2

最長迴文子串(連續子串)

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

示例 1:

輸入: “babad”
輸出: “bab”
注意: “aba” 也是一個有效答案。
示例 2:

輸入: “cbbd”
輸出: “bb”

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        
        n = len(s)
        dp = [[False for j in range(n)] for i in range(n)]

        begin_idx = 0
        MaxL = 0

        for j in range(n):
            for i in range(j+1):
                L = j - i + 1
                if s[i] == s[j]:
                    if L <= 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i+1][j-1]
                
                if dp[i][j] and L > MaxL:
                    #print(i, j)
                    begin_idx = i
                    MaxL = L
        #print(dp)

        return s[begin_idx:begin_idx+MaxL]

最長迴文子串(不連續)

給定一個字符串s,找到其中最長的迴文子序列,並返回該序列的長度。可以假設s的最大長度爲1000。

示例 1:
輸入:

“bbbab”
輸出:

4
一個可能的最長迴文子序列爲 “bbbb”。

示例 2:
輸入:

“cbbd”
輸出:

2
一個可能的最長迴文子序列爲 “bb”。

class Solution(object):
    def longestPalindromeSubseq(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 使用dp思想
        n = len(s)
        dp = [[0]*n for i in range(n)]
        # 注意basecase
        for i in range(n):
            dp[i][i] = 1
        # 倒着遍歷
        for i in range(n-2,-1,-1):
            for j in range(i+1,n):
                if s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1])
        return dp[0][n-1]

搜索旋轉排序數組

假設按照升序排序的數組在預先未知的某個點上進行了旋轉。

( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。

搜索一個給定的目標值,如果數組中存在這個目標值,則返回它的索引,否則返回 -1 。

你可以假設數組中不存在重複的元素。

你的算法時間複雜度必須是 O(log n) 級別。

示例 1:

輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4
示例 2:

輸入: nums = [4,5,6,7,0,1,2], target = 3
輸出: -1

思路:分成兩段,分別進行二分查找,平均時間複雜度O(logn)
各種查找和排序算法的時間和空間複雜度

class Solution(object):
    def BinarySearch(self, nums, start, end, target, n):
        if end < 0 or start > n-1 or (start == end and nums[start] != target) or end < start:
            return -1

        mid = (start + end) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] > target:
            return self.BinarySearch(nums, start, mid-1, target, n)
        else:
            return self.BinarySearch(nums, mid+1, end, target, n)

    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        boundary = 0
        n = len(nums)

        for i in range(n-1):
            if nums[i] > nums[i+1]:
                boundary = i+1
        
        answer1 = self.BinarySearch(nums, 0, boundary, target, n)
        answer2 = self.BinarySearch(nums, boundary, n, target, n)

        if answer1 == answer2 == -1:
            return -1
        elif answer1 != -1:
            return answer1
        else:
            return answer2

分隔數組以得到最大和

給出整數數組 A,將該數組分隔爲長度最多爲 K 的幾個(連續)子數組。分隔完成後,每個子數組的中的值都會變爲該子數組中的最大值。

返回給定數組完成分隔後的最大和。

示例:

輸入:A = [1,15,7,9,2,5,10], K = 3
輸出:84
解釋:A 變爲 [15,15,15,9,10,10,10]

提示:

1 <= K <= A.length <= 500
0 <= A[i] <= 10^6

思路:動態規劃
在這裏插入圖片描述

class Solution(object):
    def maxSumAfterPartitioning(self, A, K):
        """
        :type A: List[int]
        :type K: int
        :rtype: int
        """
        n=len(A)
        res=[0]*(n+1)
        for i in range(1,n+1) :
            j=i-1
            mx=float("-inf")
            while i-j <= K and j >= 0 :
                #由於不知道lastSubA的大小,因此對lastSubA的所有可能都進行計算,取最大的
                mx=max(mx,A[j])
                res[i]=max(res[i],res[j]+mx*(i-j)) 
                j-=1
        return res[n]

擺動排序

給你一個無序的數組 nums, 將該數字 原地 重排後使得 nums[0] <= nums[1] >= nums[2] <= nums[3]…。

示例:

輸入: nums = [3,5,2,1,6,4]
輸出: 一個可能的解答是 [3,5,1,6,2,4]

from heapq import *
class Solution(object):
    def wiggleSort(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        if n <= 1:
            return nums
        
        a = n // 2

        new_nums = zip(nums, [i for i in range(n)])
        lar = nlargest(a, new_nums)
        laridx = [t[1] for t in lar]

        #print(lar)
        smal = [nums[t] for t in range(n) if t not in laridx]
        
        lar = [nums[m] for m in laridx]

        nums[1:n:2] = lar
        nums[0:n:2] = smal
        return nums

排序鏈表

在 O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。

示例 1:

輸入: 4->2->1->3
輸出: 1->2->3->4
示例 2:

輸入: -1->5->3->4->0
輸出: -1->0->3->4->5

思路:
解答一:歸併排序(遞歸法)
題目要求時間空間複雜度分別爲O(nlogn)O(nlogn)和O(1)O(1),根據時間複雜度我們自然想到二分法,從而聯想到歸併排序;

對數組做歸併排序的空間複雜度爲 O(n)O(n),分別由新開闢數組O(n)O(n)和遞歸函數調用O(logn)O(logn)組成,而根據鏈表特性:

數組額外空間:鏈表可以通過修改引用來更改節點順序,無需像數組一樣開闢額外空間;
遞歸額外空間:遞歸調用函數將帶來O(logn)O(logn)的空間複雜度,因此若希望達到O(1)O(1)空間複雜度,則不能使用遞歸。
通過遞歸實現鏈表歸併排序,有以下兩個環節:

分割 cut 環節: 找到當前鏈表中點,並從中點將鏈表斷開(以便在下次遞歸 cut 時,鏈表片段擁有正確邊界);
我們使用 fast,slow 快慢雙指針法,奇數個節點找到中點,偶數個節點找到中心左邊的節點。
找到中點 slow 後,執行 slow.next = None 將鏈表切斷。
遞歸分割時,輸入當前鏈表左端點 head 和中心節點 slow 的下一個節點 tmp(因爲鏈表是從 slow 切斷的)。
cut 遞歸終止條件: 當head.next == None時,說明只有一個節點了,直接返回此節點。
合併 merge 環節: 將兩個排序鏈表合併,轉化爲一個排序鏈表。
雙指針法合併,建立輔助ListNode h 作爲頭部。
設置兩指針 left, right 分別指向兩鏈表頭部,比較兩指針處節點值大小,由小到大加入合併鏈表頭部,指針交替前進,直至添加完兩個鏈表。
返回輔助ListNode h 作爲頭部的下個節點 h.next。
時間複雜度 O(l + r),l, r 分別代表兩個鏈表長度。
當題目輸入的 head == None 時,直接返回None。

在這裏插入圖片描述

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

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next: return head # termination.
        # cut the LinkedList at the mid index.
        slow, fast = head, head.next
        while fast and fast.next:
            fast, slow = fast.next.next, slow.next
        mid, slow.next = slow.next, None # save and cut.
        # recursive for cutting.
        left, right = self.sortList(head), self.sortList(mid)
        # merge `left` and `right` linked list and return it.
        h = res = ListNode(0)
        while left and right:
            if left.val < right.val: h.next, left = left, left.next
            else: h.next, right = right, right.next
            h = h.next
        h.next = left if left else right
        return res.next

島嶼的最大面積

給定一個包含了一些 0 和 1 的非空二維數組 grid 。

一個 島嶼 是由一些相鄰的 1 (代表土地) 構成的組合,這裏的「相鄰」要求兩個 1 必須在水平或者豎直方向上相鄰。你可以假設 grid 的四個邊緣都被 0(代表水)包圍着。

找到給定的二維數組中最大的島嶼面積。(如果沒有島嶼,則返回面積爲 0 。)

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
對於上面這個給定矩陣應返回 6。注意答案不應該是 11 ,因爲島嶼只能包含水平或垂直的四個方向的 1 。

示例 2:

[[0,0,0,0,0,0,0,0]]
對於上面這個給定的矩陣, 返回 0。

注意: 給定的矩陣grid 的長度和寬度都不超過 50。

class Solution(object):
    def maxAreaOfIsland(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m = len(grid)
        if m == 0: return 0
        n = len(grid[0])
        ans = 0
        def dfs(i, j):
            if i < 0 or i >= m or j < 0 or j >= n: return 0
            if grid[i][j] == 0: return 0
            grid[i][j] = 0
            top = dfs(i + 1, j)
            bottom = dfs(i - 1, j)
            left = dfs(i, j - 1)
            right = dfs(i, j + 1)
            return 1 + sum([top, bottom, left, right])
        for i in range(m):
            for j in range(n):
                ans = max(ans, dfs(i, j))
        return ans

合併區間

給出一個區間的集合,請合併所有重疊的區間。

示例 1:

輸入: [[1,3],[2,6],[8,10],[15,18]]
輸出: [[1,6],[8,10],[15,18]]
解釋: 區間 [1,3] 和 [2,6] 重疊, 將它們合併爲 [1,6].
示例 2:

輸入: [[1,4],[4,5]]
輸出: [[1,5]]
解釋: 區間 [1,4] 和 [4,5] 可被視爲重疊區間。

class Solution(object):
    def merge(self, intervals):
        """
        :type intervals: List[List[int]]
        :rtype: List[List[int]]
        """
        intervals.sort()
        m = len(intervals)
        
        i = 0

        while i < m-1:
            if intervals[i][1] >= intervals[i+1][0]:
                # 合併區間
                intervals[i][1] = max(intervals[i][1], intervals[i+1][1])
                del intervals[i+1]
                m -= 1
            else:
                i += 1
        
        return intervals

整數替換

給定一個正整數 n,你可以做如下操作:

  1. 如果 n 是偶數,則用 n / 2替換 n。
  2. 如果 n 是奇數,則可以用 n + 1或n - 1替換 n。
    n 變爲 1 所需的最小替換次數是多少?

示例 1:

輸入:
8

輸出:
3

解釋:
8 -> 4 -> 2 -> 1
示例 2:

輸入:
7

輸出:
4

解釋:
7 -> 8 -> 4 -> 2 -> 1

7 -> 6 -> 3 -> 2 -> 1

思路:每遇偶數除以2,沒什麼好說。
遇到奇數,應變換使其成爲4的倍數,目的是使該數下一步變成能連續兩次除以2的偶數
因此mod41時減1,mod43時加1
有個例外是3, 3——2——1 比 3——4——2——1更快

class Solution(object):
    def integerReplacement(self, n):
        """
        :type n: int
        :rtype: int
        """
        count = 0
        while n>1:
            if n==3:
                n-=1
            elif n%4==1:
                n-=1
            elif n%4==3:
                n+=1
            else:
                n/=2
            count+=1
        return count

分割數組

給定一個數組 A,將其劃分爲兩個不相交(沒有公共元素)的連續子數組 left 和 right, 使得:

left 中的每個元素都小於或等於 right 中的每個元素。
left 和 right 都是非空的。
left 要儘可能小。
在完成這樣的分組後返回 left 的長度。可以保證存在這樣的劃分方法。

示例 1:

輸入:[5,0,3,8,6]
輸出:3
解釋:left = [5,0,3],right = [8,6]
示例 2:

輸入:[1,1,1,0,6,12]
輸出:4
解釋:left = [1,1,1,0],right = [6,12]

提示:

2 <= A.length <= 30000
0 <= A[i] <= 10^6
可以保證至少有一種方法能夠按題目所描述的那樣對 A 進行劃分。

class Solution(object):
    def partitionDisjoint(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        tmp,pos,m = A[0],0,A[0]
        for i in range(1,len(A)):
            m = max(m,A[i])
            if A[i]<tmp:
                pos = i
                tmp = m
        return pos+1

從英文中重建數字

給定一個非空字符串,其中包含字母順序打亂的英文單詞表示的數字0-9。按升序輸出原始的數字。

注意:

輸入只包含小寫英文字母。
輸入保證合法並可以轉換爲原始的數字,這意味着像 “abc” 或 “zerone” 的輸入是不允許的。
輸入字符串的長度小於 50,000。
示例 1:

輸入: “owoztneoer”

輸出: “012” (zeroonetwo)
示例 2:

輸入: “fviefuro”

輸出: “45” (fourfive)

class Solution(object):
    def originalDigits(self, s):
        """
        :type s: str
        :rtype: str
        """
        res = {}
        for i in s:
            if res.get(i):
                res[i] += 1
            else:
                res[i] = 1
        if res.get('z'):             #0 : z
            res['r'] -= res['z']
            res['o'] -= res['z']
        if res.get('w'):             #2 : w
            res['t'] -= res['w']
            res['o'] -= res['w']
        if res.get('u'):             #4 : u; 1 : o; 3 : r; 5 : f; 
            res['f'] -= res['u']
            res['o'] -= res['u']
            res['r'] -= res['u']
        if res.get('o') and res['o'] > 0:
            res['n'] -= res['o']
        if res.get('f') and res['f'] > 0:   #7 : v
            res['v'] -= res['f']
        if res.get('r') and res['r'] > 0:   #8 : t
            res['t'] -= res['r']
        if res.get('v') and res['v'] > 0:   #6 : s; 9 : n
            res['s'] -= res['v']
            res['n'] -= res['v']
        num = ''
        if res.get('z'):
            num += '0'*res['z']
        if res.get('o'):
            num += '1'*res['o']
        if res.get('w'):
            num += '2'*res['w']
        if res.get('r'):
            num += '3'*res['r']
        if res.get('u'):
            num += '4'*res['u']
        if res.get('f'):
            num += '5'*res['f']
        if res.get('s'):
            num += '6'*res['s']
        if res.get('v'):
            num += '7'*res['v']
        if res.get('t'):
            num += '8'*res['t']
        if res.get('n'):
            num += '9'*(res['n']//2)
        return num

克隆圖

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

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

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

測試用例格式:

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

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

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

示例 1:

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

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

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

輸入:adjList = [[2],[1]]
輸出:[[2],[1]]

提示:

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

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val = 0, neighbors = []):
        self.val = val
        self.neighbors = neighbors
"""

class Solution(object):
    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """
        lookup = {}

        def dfs(node):
            #print(node.val)
            if not node: return
            if node in lookup:
                return lookup[node]
            clone = Node(node.val, [])
            lookup[node] = clone
            for n in node.neighbors:
                clone.neighbors.append(dfs(n))
            
            return clone

        return dfs(node)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章