文章目錄
- 1 Two Sum
- 2 Add Two Numbers
- 3 Longest Substring Without Repeating Characters
- 4 尋找兩個有序數組的中位數
- 5 Longest Palindromic Substring
- 6 ZigZag Conversion
- 7 Reverse Integer 將給定的數字倒序輸出.
- 8 String to Integer(atoi)
- 9 Palindrome Number
- 10 正則表達式匹配
- 11.求其中兩條直線與x軸圍成的容器的最大容量.
- 12.Integer to Roman
- 13.Roman to Integer
- 14.Longest Common Prefix
- 15.3Sum
- 16.3Sum Closest
- 17 電話號碼的字母組合
- 18.4Sum 找出list中所有相加等於target的4個數的list.
- 19 刪除鏈表的倒數第N個節點
- 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))