leetcode刷題:1-20


leetcode刷題:1-20

1 Two Sum

在列表中找到兩個數,使得它們的和等於某一給定值,返回這兩個數的位置.時間複雜度:O(n),python中的字典其實就是哈希表的應用,所以我們通過字典用哈希表來降低查找的時間複雜度

def twoSum(self, nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[int]
    """
    d = {}
    
    for i, n in enumerate(nums):
        m = target - n
        if m in d:
            return [d[m], i]
        else:
            d[n] = i
        

2 Add Two Numbers

將兩個倒序存放在單鏈表裏的數相加,將結果倒序存儲在單鏈表裏返回.思路非常簡單,先將兩個單鏈表中的數字分別提取出來求和,然後將=求得的和存入一個單鏈表,實際上相加這一步也可以直接在原鏈表中完成,只需要添加判斷條件while(l1 or l2 or carry)即可.

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

class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        
        node1 = l1
        node2 = l2
        l3 = ListNode(0)
        l3.next = ListNode(0)
        node3 = l3
        
        sum1 = 0
        coe = 1
        while not node1 is None:
            sum1 += node1.val * coe
            coe *= 10
            node1 = node1.next
            
        sum2 = 0
        coe = 1
        while not node2 is None:
            sum2 += node2.val * coe
            coe *= 10
            node2 = node2.next
            
        sum = sum1+sum2
        
        while sum > 0:
            node3.next = ListNode(sum % 10)
            node3 = node3.next
            sum //= 10
            
        return l3.next

3 Longest Substring Without Repeating Characters

找到字符串中沒有重複字符的最大子串.一開始沒有想到用字典,而是直接用str來存儲字串,時間複雜度是O(n^2),後來用字典將時間複雜度降到了O(n).注意到僅當字典中出現重複值且該重複值在strat區段裏時才移動start.另外用了Sliding Window的思想,每次將strat移動到重複值的下一位置

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        start = 0
        max_length = 0
        substring = {}
        
        for i, c in enumerate(s):
            if c in substring and start <= sunstring[c]:
                start = substring[c] + 1
            else:
                max_length = max(max_length, i-start+1)
            substring[c] = i
            
        return max_length

4 尋找兩個有序數組的中位數

給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。

請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。

你可以假設 nums1 和 nums2 不會同時爲空。

def findMedianSortedArrays( nums1, nums2):
    """
    :type nums1: List[int]
    :type nums2: List[int]
    :rtype: float
    """

    m = len(nums1)
    n = len(nums2)

    if m > n:
        nums1,nums2,m,n = nums2, nums1,n,m
    if n== 0:
        raise ValueError

    imin = 0
    imax = m
    half_len = (m+n+1) // 2

    while imin <= imax:
        i = (imin + imax) // 2
        j = half_len - i
        
        print(i,j)

        if i < m and nums2[j-1] > nums1[i]:
            imin = i + 1
            print("(imin)")
            print( imin)
            
        elif i > 0 and nums1[i-1] > nums2[j]:
            imax = i-1
            print("(imax)")
            print(imax)
        else:
            print("(else)")

            if i == 0:
                max_of_left = nums2[j-1]
            elif j == 0:
                max_of_left = nums1[i-1]
            else:
                max_of_left = max(nums1[i-1], nums2[j-1])
                
            print('max_of_left:')
            print(max_of_left)

            if (m+n) % 2 == 1:
                return max_of_left

            if i == m:
                min_of_right = nums2[j]
            elif j == n:
                min_of_right = nums1[i]
            else:
                min_of_right = min(nums1[i], nums2[j])
                
            print('max_of_left,min_of_right:')
            print(max_of_left,min_of_right)

            return (max_of_left + min_of_right) / 2.0
        

    
                

                
nums1 = [1, 3]
nums2 = [2]

#nums1 = [1, 2]
#nums2 = [3,4]

print(findMedianSortedArrays(nums1,nums2))

5 Longest Palindromic Substring

最長迴文子串問題,一開始我的思路如下:迴文子串的特點是首尾字母相同,所以我對每一個字母都找到位於它後面的相同字母,利用切片判斷這一段是否爲迴文子串(str[i:j]==str[i:j][::-1]).雖然AC了但是時間複雜度很高,主要是因爲str.find操作非常耗時.

後來看了Solution發現這是一道可以用動態規劃解決的問題,思路是若s是迴文字串,令s’=s加上s左右兩側的兩個字母,如果這兩個字母相同則s’也是迴文字串.重寫代碼如下:

class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        
        max = 0
        palindromic = '' if len(s) == 0 else s[0]
        for i in range(len(s)):
            length = 1
            while i - length >= 0 and i+length < len(s) and s[i-length] == s[i+length]:
                
                tmp = s[i-length : i+length+1]
                print(i, tmp)
                if len(tmp) > max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
            length = 1
            
            while i-length+1 >=0 and i+length <len(s) and s[i-length+1] == s[i+length]:
                tmp = s[i-length+1:i+length+1]
                print(i, tmp)
                if len(tmp) >max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
        return palindromic
      
solu = Solution()
str1 = "babad"
print(solu.longestPalindrome(str1))

6 ZigZag Conversion

一道將字符串做之字形排列的題目.我們用n表示行數,將排列後得到的字符串分爲完整豎列和折線兩部分.每個完整豎列有n個數,每兩個完整豎列之間的折線有n-2列,每列一個數,因此每兩個完整豎列中同一行的數的間隔是n+n-2=2n-2.同時我們發現,除了第一行和最後一行之外的第i行都有折線,第i行的第一個折線是第2n-i個數.於是可以遍歷輸出每一行,判定條件是這一行我們要輸出的數字是否超出了字符串的長度

class Solution:
    def convert(self, s, numRows):
        """
        :type s: str
        :type numRows: int
        :rtype: str
        """
        zigzag = ''
        
        if numRows == 1 or numRows == 0 or numRows >=len(s):
            return s
        
        space = 2*numRows-2
        for i in range(1, numRows+1):
            n=0
            if i==1 or i==numRows:
                while i+n*space <= len(s):
                    zigzag += s[i+n*space-1]
                    n+=1
            else:
                while i+n*space <= len(s):
                    zigzag += s[i+n*space-1]
                    if (2*numRows-i) + (n*space) <= len(s):
                        zigzag += s[(2*numRows-i)+(n*space)-1]
                    n+=1
                    
        return  zigzag

7 Reverse Integer 將給定的數字倒序輸出.

class Solution(object):
    def reverse(self, x):
        """
        :type x: int
        :rtype: int
        """
        tmp = abs(x)
        
        sum = 0
        while tmp > 0:
            sum = sum*10 + tmp%10
            tmp = tmp // 10
            
        sum = sum if x >=0 else -sum
        
        return sum if sum<2**31 and sum>-2**31 else 0

8 String to Integer(atoi)

將給定字符串中符合條件的一串數字字符轉化爲int類型返回.我的思路是設定標誌位start=0和符號位sign,遍歷字符串,當start=0時遇到空格則continue,遇到+則記錄sign=1,遇到-則記錄sign=-1,遇到數字則記錄數字;當strat=1時代表已經找到了第一個數字或符號位,此時遇到除數字之外的字符都break,遇到數字則繼續記錄數字.注意我們得到的整數值不能超過INT_MAX和INT_MIN.後來發現其實用str.strip()函數來去除字符串頭尾的空格會更方便

class Solution(object):
    def myAtoi(self, str):
        """
        :type str: str
        :rtype: int
        """
        
        ans = 0
        start = 0
        sign = 0
        if str.isspace() is True:
            print(0)
        for i in str:
            if start == 0:
                if i.isspace() is True:
                    continue
                if i == '+':
                    sign = 1
                elif i == '-':
                    sign = -1
                elif i.isdigit() is True:
                    sign = 1
                    ans = ans*10 + int(i)
                else:
                    break
                start = 1
            else:
                if i.isdigit() is True:
                    ans = ans*10+int(i)
                else:
                    break
                    
        ans = sign*ans
        if ans >= 2147483647:
            return 2147483647
        elif ans <= -2147483648:
            return -2147483648
        
        return ans
                    

9 Palindrome Number

判斷一個數字是否是迴文數.題目要求不能用額外的空間,否則可以利用python的字符串切片輕鬆解決.我的思路是求出該整數的位數,判斷第一位數和最後一位數是否相同,如果相同則將位數/100,然後將原數字的首尾兩個數刪除,最後如果位數<1說明是迴文數.

def isPalindrome( x):
    """
    :type x: int
    :rtype: bool
    """

    if x<0:
        return False

    high = 1
    while x/high >= 10:
        high *= 10
    print(high)    
    
    while x // high == x%10:
        x = x%high//10
        print(x)
        high /= 100
        print(high)
        if high < 1:
            return True

    return False

10 正則表達式匹配

給定一個字符串 (s) 和一個字符模式 §。實現支持 ‘.’ 和 ‘*’ 的正則表達式匹配。

‘.’ 匹配任意單個字符。 ‘*’ 匹配零個或多個前面的元素。 匹配應該覆蓋整個字符串 (s) ,而不是部分字符串。

說明:

s 可能爲空,且只包含從 a-z 的小寫字母。 p 可能爲空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。 示例 1:

輸入: s = “aa” p = “a” 輸出: false 解釋: “a” 無法匹配 “aa” 整個字符串。 示例 2:

輸入: s = “aa” p = “a” 輸出: true 解釋: ‘’ 代表可匹配零個或多個前面的元素, 即可以匹配 ‘a’ 。因此, 重複 ‘a’ 一次, 字符串可變爲 “aa”。

if len(p) == 0: return len(s) == 0
        
        if len(p) == 1:
            return len(s) == 1 and (s[0] == p[0] or p[0] == '.')
        
        if p[1] != '*':
            if len(s) == 0: return False
            return (s[0] == p[0] or p[0] == '.') and self.isMatch(s[1:], p[1:])
        
        while (len(s) != 0 and (s[0] == p[0] or p[0] == '.')):
            if self.isMatch(s,p[2:]): 
                return True
            s=s[1:]
            
        return self.isMatch(s,p[2:])

執行用時 :2056 ms, 在所有Python提交中擊敗了7.75%的用戶
內存消耗 :11.6 MB, 在所有Python提交中擊敗了41.60%的用戶

11.求其中兩條直線與x軸圍成的容器的最大容量.

Container With Most Water 給定許多條與y軸平行的直線,求其中兩條直線與x軸圍成的容器的最大容量.

這道題用到了雙指針的思想.我們在數軸的兩端分別放置一個left指針和right指針,因爲容器容量=較短邊*兩邊位置之差,所以如果移動較大的那個指針,那麼容量必定在減小.因此我們不斷往中間移動較小的指針纔有可能使容量變大,直到兩指針相遇爲止.

對於算法合理性的邏輯推理:我們假設在best_left和best_right位置取到最大容量,那麼left指針到達best_left位置或right指針到達best_right位置至少有一種會發生.不妨令left指針到達best_left位置,此時right指針的位置有三種可能:

位於best_right位置左側.這說明best_right位置已經被計算過,成立. 位於best_right位置,同上. 位於best_right位置右側.因爲left指針移動的條件是right指針所在邊大於left指針所在邊,如果符合此條件,且right指針在best_right右側,那麼當前容量一定大於假設中的最大容量,與假設矛盾.所以left指針必定會一路移動至best_right位置.

class Solution(object):
    def maxArea(self, height):
        """
            :type height: List[int]
            :rtype: int
            """
        left = 0
        right = len(height) - 1

        maxArea = 0
        while left != right:
            h = min(height[left], height[right])
            maxArea = max(maxArea, h * (right-left))

            if height[left] < height[right]:
                left += 1
            else:
                right -= 1

        return maxArea

執行用時 :144 ms, 在所有Python提交中擊敗了55.27%的用戶
內存消耗 :13.2 MB, 在所有Python提交中擊敗了8.60%的用戶

12.Integer to Roman

將十進制數字轉化爲羅馬數字.比較簡單的一道題.我的思路是判斷當前位數,改變代表1/5/10的字符然後逐位輸出.也可以直接將每位上的各種字符表示存在列表裏,然後直接取出.

def intToRoman(num):
    """
    :type num: int
    :rtype: str
    """
    
    carry = 1
    roman = ''
    
    while num!= 0:
        n = num % 10
        num //= 10
        if carry == 1:
            numeral_1 = 'I'
            numeral_5 = 'V'
            numeral_10 = 'X'
        elif carry == 10:
            numeral_1 = 'X'
            numeral_5 = 'L'
            numeral_10 = 'C'
        elif carry == 100:
            numeral_1 = 'C'
            numeral_5 = 'D'
            numeral_10 = 'M'
        else:
            numeral_1 = 'M'
            numeral_5 = ''
            numeral_10 = ''
            
        if 1<= n <=3:
            roman = numeral_1*n + roman
        elif n==4:
            roman = numeral_1+numeral_5 + roman
        elif 5<=n<=8:
            roman = numeral_5 + numeral_1*(n-5)+roman
        elif n==9:
            roman = numeral_1+numeral_10+roman
            
        carry *= 10
        
    return roman
            
intToRoman(48)        

執行用時 :32 ms, 在所有Python提交中擊敗了97.90%的用戶
內存消耗 :11.7 MB, 在所有Python提交中擊敗了33.80%的用戶

13.Roman to Integer

將羅馬數字轉化爲十進制數字.非常無聊的一道題.比較簡單的方法是寫非常多的if語句來判斷,或者將羅馬數字與對應的十進制數字存入字典來轉換.下面是我在discuss裏看到的一個方案,巧妙利用了羅馬數字"大數前面的小數用來減,大數後面的小數用來加"這個特點.

def romanToInt( s):
    """
    :type s: str
    :rtype: int
    """
    roman_map = {
        "I": 1,
        "V": 5,
        "X": 10,
        "L": 50,
        "C": 100,
        "D": 500,
        "M": 1000,
    }
    
    
    result = 0
    last_num = None
    for char in s:
        current_num = roman_map[char]
        if last_num is None or last_num >= current_num:
            result += current_num
        elif last_num < current_num:
            result += current_num - 2 * last_num
        last_num = current_num
    return result


romanToInt('XLVIII')

執行用時 :52 ms, 在所有Python提交中擊敗了85.13%的用戶
內存消耗 :11.6 MB, 在所有Python提交中擊敗了32.99%的用戶

14.Longest Common Prefix

找最長公共前綴字符串.我的思路是找出列表中最短的字符串,然後對最短字符串的每個字符都在列表中遍歷,直到出現不同或者遍歷結束爲止.在discuss裏看到很多方法利用了python中的sort(),min(),max()這些內置方法對字符串排序,會使時間快很多.

def longestCommonPrefix( strs):
    """
    :type strs: List[str]
    :rtype: str
    """
    
    prefix = ''
    
    if strs == []:
        return prefix
    
    mininum = float("inf")
    for s in strs:
        mininum = min(len(s), mininum)
        
    print(mininum)
        
    i=0
    for j in range(mininum):
        for i in range(len(strs)):
            while strs[i][j] != strs[0][j]:
                print(prefix)
                return prefix
        prefix = prefix + strs[0][j]
        
    return prefix

T = ['abc','abcd','abfg','abopfge']
longestCommonPrefix( T)

執行用時 :40 ms, 在所有Python提交中擊敗了25.81%的用戶
內存消耗 :11.9 MB, 在所有Python提交中擊敗了23.86%的用戶

15.3Sum

給定一個數組,找到其中三個數的和爲零的所有可能,以列表形式返回.這道題的基本思路是先將數組排序,從左往右遍歷一次.在遍歷每個數的過程中設立兩個指針,如果三個數的和大於零則左移右指針,如果三個數的和小於零則右移左指針,直到兩個指針相遇.注意我們用的是set()來存儲找到的結果,可以避免list中出現重複.在此基礎上,我增加了一個對排序過的數組的操作,即當最左邊兩個數與最右邊一個數的和大於零時刪去最右邊的數,當最左邊一個數與最右邊兩個數的和小於零時刪去最左邊的數.這個操作大大提升了運行速度.

def threeSum(self, nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    
    zeros = set()
    nums.sort()
    
    if len(nums) < 3:
        return []
    
    if nums.count(0) > len(nums) -2:
        return [[0,0,0]]
    
    while len(nums) > 3 and (nums[0] + nums[1] + nums[-1] > 0 or nums[-1]+nums[-2]+nums[0] < 0):
        if nums[0] + nums[1] + nums[-1] > 0 :
            nums.remove(nums[-1])
        else:
            nums.remove(nums[0])
            
    for i in range(len(nums)-2):
        if nums[i] > 0:
            break
        j = i+1
        k = len(nums) -1
        while j < k:
            sum = nums[i] + nums[j] + nums[k]
            if sum == 0:
                zeros.add((nums[i], nums[j], nums[k]))
                j += 1
                continue
            elif sum < 0:
                j += 1
            else:
                k -= 1
                
    return list(map(list,zeros))

執行用時 :1044 ms, 在所有Python提交中擊敗了21.21%的用戶
內存消耗 :16.9 MB, 在所有Python提交中擊敗了5.55%的用戶

16.3Sum Closest

給定一個數組和一個目標值,找到數組中的三個數,使得這三個數之和與目標值之間的差距最小,返回它們的和.

這題的思路與15題類似,也是利用雙指針,只不過判定條件從三個數之和是否爲零改成了三個數之和是

否比目前已有的closest值更接近目標

def threeSum(self, nums, target):
    """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
    
    closest = nums[0] + nums[1] + nums[2]
    nums.sort()
    length = len(nums)
    for i in range(length):
        l = i+1
        r = length - 1
        while l < r:
            tmp = nums[i] + nums[l] + nums[r]
            if tmp == target:
                closest = target
                break
            elif tmp < target:
                if target - tmp < abs(target - closest):
                    closest = tmp
                l += 1
            elif tmp > target:
                if tmp - target < abs(target - closest):
                    closest = tmp;
                r -= 1
    return closest

17 電話號碼的字母組合

給定一個僅包含數字 2-9 的字符串,返回所有它能表示的字母組合。

給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。

[外鏈圖片轉存失敗(img-SdYneUnS-1566218841880)(attachment:image.png)]

示例:

輸入:“23”
輸出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

class Solution(object):
    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
        
        if not digits:
            return []
        
        digit2char = {
            '2':'abc',
            '3':'def',
            '4':'ghi',
            '5':'jkl',
            '6':'mno',
            '7':'pqrs',
            '8':'tuv',
            '9':'wxyz',
        }
        
        res = [ i for i in digit2char[digits[0]] ]
        
        result = []
        for i in digits[1:]:
            for m in res:
                for n in digit2char[i]:
                    res = m+n
                    result.append(res)
                    #print(res)
                    
        return result
        
        
solu =  Solution()
tmp = '23'
print(solu.letterCombinations(tmp))
        

18.4Sum 找出list中所有相加等於target的4個數的list.

一開始我的思路是令new_target=target-num1,然後轉換爲一個3Sum問題,但這種做法的時間複雜度太高了.查看Solution後發現這道題要使用hash的思想,在python中對應的實現就是使用先dict存儲list中的兩數之和和它們在list中的位置,然後對於這個dict中的value,尋找一個key=target-value,然後將他們對應的數字存入list即可.需要注意的是python中的list,set,dict是不可哈希的,int,float,str,tuple是可哈希的.

def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        d = dict()
        
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                sum2 = nums[i] + nums[j]
                if sum2 in d:
                    d[sum2].append((i,j))
                else:
                    d[sum2] = [(i,j)]
                    
        result = []
        for key in d:
            value = target - key
            if value in d:
                list1 = d[key]
                list2 = d[value]
                for(i,j) in list1:
                    for(m,n) in list2:
                        if i != m and i != n and j != m and j != n:
                            flist = [nums[i], nums[j],nums[m],nums[n]]
                            flist.sort()
                            if flist not in result:
                                result.append(flist)
        return result
            

19 刪除鏈表的倒數第N個節點

給定一個鏈表,刪除鏈表的倒數第 n 個節點,並且返回鏈表的頭結點。

示例:

給定一個鏈表: 1->2->3->4->5, 和 n = 2.

當刪除了倒數第二個節點後,鏈表變爲 1->2->3->5. 說明:

給定的 n 保證是有效的。

進階:

你能嘗試使用一趟掃描實現嗎?

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

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        res = ListNode(0)
        res.next = head
        list1 = res
        list2 = res
        for i in range(n):
            list1 = list1.next
        while list1.next:
            list1 = list1.next
            list2 = list2.next
        list2.next = list2.next.next
        return res.next

20 有效的括號

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

有效字符串需滿足:

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

示例 1:

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

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

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        result = []
        
        samples = ['[]','{}','()']
        
        for i in range(len(s)):
            result.append(s[i])
            
            if len(result) >= 2 and result[-2] + result[-1] in samples:
                result.pop()
                result.pop()
                
                
        return len(result) == 0
        
solu = Solution()
ss = "{{[}]}"

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