Leetcode1
思路:看似需要兩重循環計算和,實則可以在第一遍循環的同時建立哈希表(字典),key記錄每個位置的數所需要配對的數(記錄每個位置的數本身也行,那麼循環的時候要找的就是target去減),value記錄位置信息
發現:當列表計算複雜度過高時,可考慮用字典,一遍循環同時建立字典,但是空間複雜度高,當同一個key需要對應不同的value不能考慮
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
l = len(nums)
d = {}
for x in range(l):
a = target - nums[x]
if a in d:
return d[a], x
else:
d[nums[x]] = x
Leetcode2
思路:可以很複雜的倒回去讀出來再相加再倒回去存進去(適用範圍廣),也可以很直接(結合問題本身)直接第一對節點相加存進目標節點,記錄進位,計算下一對節點相加加進位。
發現:建立鏈表的過程就是爲每一個賦好值得節點.next指向下一個新建的節點,循環過程,返回鏈表就是返回鏈表頭節點
# 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
"""
t1 = ''
t2 = ''
if l1 == None:
return l2
if l2 == None:
return l1
while l1!=None:
t1=t1+str(l1.val)
l1 = l1.next
while l2!=None:
t2=t2+str(l2.val)
l2 = l2.next
t1 = t1[::-1]
t2 = t2[::-1]
t= str(int(t1) + int(t2))[::-1]
head = ListNode(t[0])
answer = head
for i in range(1,len(t)):
node = ListNode(t[i])
head.next = node
head = head.next
return answer
Leetcode 3
思路:逐個位置進行匹配
發現:用數組存儲最大子串就會超時,用字符串處理就不會
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
l = len(s)
longest = 0
for i in range(l):
res = ''
for j in range(i, l):
if s[j] in res:
break
else:
res = res + s[j]
num = len(res)
if num > longest:
longest = num
return longest
Leetcode 4
思路:賴皮,利用sort()直接排序
發現://整數除法,長爲n的列表,下標0-(n-1),中位數是2/n或(2/n和2/(n-1)的平均數)
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums = nums1 + nums2
nums.sort()
n = len(nums)//2
if len(nums)%2 == 1:
return nums[n]
else:
return (nums[n] + nums[n-1])/2
Leetcode 5
思路:最小回文子串,奇數迴文和偶數迴文算法不同,一遍循環後,奇數迴文是以每個位置向左右不斷延伸比較每次延伸的位置是否相同,如是就加上。偶數迴文要先比較該位置與下一位是否相同,如是就以這兩位爲基點向兩邊延展。
發現:注意初始值,輸入值也可以是空集,所以s[0]不一定存在,得在遍歷中賦初值;注意遍歷中每一次都要先初始化最小奇偶迴文的值。
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
st = ''
sw = ''
longest = ''
l = len(s)
for i in range(l):
#先算奇迴文
st = s[i]
if len(st)>len(longest):
longest = st
t = 1#記錄每次往兩邊的偏移
while i-t >=0 and i+t <= l-1:
if s[i-t] == s[i+t]:
st = s[i-t] + st + s[i+t]
if len(st)>len(longest):
longest = st
t = t + 1
else:
break
#再算偶迴文
t = 0
sw = ''
while i-t >=0 and i+1+t <= l-1:
if s[i-t] == s[i+1+t]:
sw = s[i-t] + sw + s[i+1+t]
if len(sw)>len(longest):
longest = sw
t = t + 1
else:
break
return longest
Leetcode 6
思路:主要是找規律,行數n,一個循環是gap=2n-2,第一行和最後一行都是隻有一個主規律:每個gap出現一次,其餘行有副規律:每個gap-所在行數也會出現一次。輸出就一行一行輸出,找到每一行上字符的下標即可。
第一行:s[0]、s[gap]、s[2*gap]….
第二行:s[1]、s[gap-1]、s[gap+1]…
第i行:s[i]、s[gap-i]、s[gap+i]…
第n行:s[n]、s[gap+n]、s[2*gap+n]..
發現:找到規律問題就解決了,注意使用多個臨時變量。
class Solution:
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if numRows == 1:
return s
resultlist = ''
gap = 2*numRows - 2
for i in range(numRows):
temp1 = i
temp2 = gap -i
if i == 0 or i == numRows-1:
while temp1<len(s):
resultlist = resultlist + s[temp1]
temp1 = temp1 +gap
else:
while temp1<len(s):
resultlist = resultlist + s[temp1]
temp1 = temp1 + gap
if temp2<len(s):
resultlist = resultlist + s[temp2]
temp2 = temp2 + gap
return resultlist
Leetcode 7
思路:反轉問題,python都可以賴皮,先把正負符號存起來,對絕對值做反轉,如果輸出大於上限,按要求返回,否則加上符號輸出
發現:有[::-1]反轉功能,這些問題都很賴
class Solution:
def reverse(self, x):
"""
:type x: int
:rtype: int
"""
#先判斷x的符號,存在id裏
if x < 0:
id = 0
else:
id =1
#取x絕對值,將絕對值轉爲y,y翻轉
x2 = abs(x)
y = str(x2)
y = y[::-1]
#判斷y,判斷符號加上
if int(y) > 2147483647:
return 0
if id == 1:
return int(y)
else:
return -int(y)
Leetcode 8
思路:先尋找第一個非空值,是“+”“-”就存爲符號然後數數,是“1”-“9”就直接開始數數,別的情況就返回0。開始數數即該位是“0”-“9”那麼就加到結果裏去即可
發現:可以用re函數,專門解決何種格式化字符串問題,回顧廖雪峯re函數講解https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193331387014ccd1040c814dee8b2164bb4f064cff000
class Solution:
def myAtoi(self, str):
"""
:type str: str
:rtype: int
"""
import re
res = re.findall(r"^[\+\-]?\d+",str.strip())
print(res)
if res !=[]:
if int(res[0]) > (2**31-1):
return (2**31-1)
if int(res[0]) < (-2**31):
return (-2**31)
return int(res[0])
else:
return 0
Leetcode9
思路:先判斷符號,小於0直接返回錯誤。然後就是簡單的直接反轉。
發現:簡單賴皮[::-1]
if x < 0:
return False
s1 = str(x)
s2 = s1[::-1]
if s1 == s2:
return True
else:
return False
Leetcode11
思路:可以用兩重循環把所有邊界都表示出來,也可以用雙指針做。最開始頂左頂右的值爲初始面積,逐漸向中間移,爲了得到更高的面積,需要更高的柱子,所以矮的柱子向中間靠,一樣高就都要移。
發現: if a> b: b = a可以寫成b = max(a,b)。很多數組上的動態問題都可以考慮雙指針,逐漸尋找最優結果。
class Solution:
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
l = len(height)
ans = 0
p1 = 0
p2 = l-1
while p1<p2:
ans = max(ans, min(height[p1],height[p2])*(p2 - p1))
if height[p1] < height[p2]:
p1 +=1
else:
p2 -=1
return ans
Leetcode12
思路:編碼問題如果額外規則太多,按照判斷語句寫太過於複雜,不如直接寫一個矩陣對應規則,一行一個規則,這樣轉化對應時可以通過矩陣第一個座標定位規則,第二個座標找到正確的編碼。
發現:將現有數字進行編碼都可以參考這種做法,先將各種可能出現的編碼情況羅列出來,一一對應轉化。要熟練提取數字個十百千萬位的操作。
class Solution:
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
roman = [["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"],
["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"],
["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"],
["", "M", "MM", "MMM"]]
res = ''
res = res + (roman[3][int(num / 1000 % 10)])
res = res + (roman[2][int(num / 100 % 10)])
res = res + (roman[1][int(num / 10 % 10)])
res = res + (roman[0][int(num % 10)])
return res
Leetcode13
思路:將一種編碼的數轉換爲實數都要建立轉化對照表,用哈希(字典)表示很合適。同時根據規則,逐位讀字符,如果該字符比右邊大就是“加”如果該字符比右邊“小”就是減。
發現:先根據規則找規律,這類轉換基本都要建表建哈希或建數組。
r = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
l = len(s)
res = 0
for i in range(l-1):
if (r[s[i]] >= r[s[i+1]]):
res = res + r[s[i]]
else:
res = res - r[s[i]]
res = res + r[s[l-1]]
return res
Leetcode 14
思路:以字符串組中第一個字符串作爲基準比對,逐位比,每一位逐個字符串和第一個字符串的該位比,如果發現不同就返回現有答案,都相同就加上該位進入答案。或者先找出最短的字符串,以該位作爲基準比。
發現:注意下標索引,每一次判斷該字符串該位是否和第一個字符串該位相等前先要判斷該字符串是否有該位,通過len判斷
class Solution:
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
l = len(strs)
if l <=0:
return ''
if l ==0:
return strs[0]
res = ''
for i in range(len(strs[0])):
for j in range(1, l):
if len(strs[j])<i+1 or strs[j][i] != strs[0][i]:
return res
res = res + strs[0][i]
return res
Leetcode15
思路:如利用兩數之和建哈希表方法,仍需要逐個作爲target,複雜度仍爲O(n2)。尋求雙指針法。用雙指針就要先排序,每個位置一遍循環,對每一位在它的右側範圍尋找兩數和它本身加起來等於零。小的往大加,大的往小減,看和大還是小,逐步變化直到雙指針相遇。
發現:碰到列表問題,可以先排個序,利用大小關係繼續解決。注意防重。
nums.sort()#排序
res =[]
i = 0
for i in range(len(nums)):
if i == 0 or nums[i]>nums[i-1]:
l = i+1
r = len(nums)-1
while l < r:
s = nums[i] + nums[l] +nums[r]
if s ==0:
res.append([nums[i],nums[l],nums[r]])
l +=1
r -=1
while l < r and nums[l] == nums[l-1]:#避免相同值
l += 1
while r > l and nums[r] == nums[r+1]:
r -= 1
elif s>0:
r -=1
else :
l +=1
return res
Leetcode16
思路:一個數組裏的多數之和問題都可以用雙指針想法解決,先排序。判斷差值的絕對值是否可以更新。
發現:
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums.sort()
length = len(nums)
min= 1000000000000000
for i in range(length):
l = i +1
r = length - 1
while l<r:
sum = nums[i] + nums[l] +nums[r]
if abs(target - sum) < abs(target - min):
min = sum
if target - sum ==0:
return sum
elif target - sum > 0:
l = l +1
else:
r = r - 1
return min
Leetcode17
思路:還不會遞歸,就按規則來,編碼問題,考慮字典,按位讀數,每一位數對應幾個字母,在這些字母的後面都可以加上下一位數對應的字母,那麼就需要一個變量臨時存放這些臨時生成的字母串。在已有的字符串後面,加上該位數代表的字母可能。
發現:結果一步一步生成,後面產生的結果需要利用前面產生的結果,就是遞歸雛形了。
class Solution:
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return []
#按照鍵盤分佈,下標0-9分別對應字符串
digitLis = ["0","1","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
res = [""]
for num in digits:
tempLis = []
for ch in digitLis[int(num)]:
for str in res:
tempLis.append(str + ch)
res = tempLis
return res
Leetcode 18
思路:四數之和沿用三數之和的思想,三數之和外面再套一層。
發現:會超時,需要剪枝,在外面大循環的部分做判斷,如果當前最小值的數量倍>target或是當前最大值的數量倍
class Solution:
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
lent = len(nums)
res = []
nums.sort()
for i in range(lent-3):
if target < nums[i]*4 or target > nums[-1]*4:
break
if i==0 or nums[i] != nums[i-1]:
for j in range(i+1,lent-2 ):
if target - nums[i] < nums[j]*3 or target -nums[i] > nums[-1]*3:
break
if j==i+1 or nums[j]!=nums[j-1]:
l = j + 1
r = lent - 1
while l < r:
if nums[i] + nums[j] + nums[l] + nums[r] == target:
res.append([nums[i],nums[j],nums[l],nums[r]])
l = l+1
r = r-1
while r > l and nums[r] == nums[r+1]:
r = r-1
while r > l and nums[l] == nums [l-1]:
l = l + 1
elif nums[i] + nums[j] + nums[l] + nums[r] > target:
r = r - 1
else:
l = l + 1
return res
Leetcode19
思路:使用快慢指針記錄抵達鏈表尾和倒數第n個節點信息。在鏈表頭再創建一個節點指向原表頭,這樣能應對錶頭被刪的情況。
發現:記錄鏈表倒數第幾個節點可以參照此法,雙指針,讓快的先跑,跑一次記一次數。
class Solution:
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
dummy = ListNode(-1)
dummy.next = head
slow = fast = dummy
while n and fast.next:
fast = fast.next
n = n - 1
while fast.next and slow.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
Leetcode21
思路:將l1和l2裏的值全部提取出來放進列表,對列表排序,逐個放進鏈表裏,全是基本操作。但是這樣寫效率低,沒有用到兩個鏈表都是有序這一點。
發現:.sort()函數和.append()函數都會在原處修改對象,不會有返回值,不要寫成temp = temp.append(temp2)
class Solution:
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
if l1 == None and l2 ==None:
return None
temp1 = []
temp = []
while l1!= None:
temp1.append(l1.val)
l1 = l1.next
while l2!= None:
temp1.append(l2.val)
l2 = l2.next
temp1.sort()
res = ListNode(temp1[0])
head = res
for i in range(1, len(temp1)):
node = ListNode(temp1[i])
head.next = node
head = head.next
return res
Leetcode22
思路:
發現:
class Solution:
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
if n==0:
return []
res = []
self.helpler(n,n,'',res)
return res
def helpler(self,l,r,item,res):
if l>r:
return
if l==0 and r==0:
res.append(item)
if l>0:
self.helpler(l-1, r, item+'(', res)
if r>0:
self.helpler(l, r-1, item+')', res)
Leetcode23
思路:暴力全部取出來放進一個列表裏,列表排序,然後將這個列表存入鏈表裏
發現:創建答案鏈表時,如果用ListNode(res[0])會報錯,需要自行創建一個賦值節點指向要返回的節點
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
if lists ==None:
return []
l = len(lists)
res = []
for i in range(l):
while lists[i]!=None:
res.append(lists[i].val)
lists[i] = lists[i].next
res.sort()
ans = ListNode(0)
head = ans
for i in range(0, len(res)):
node = ListNode(res[i])
head.next = node
head = head.next
return ans.next
Leetcode24
思路:鏈表轉換
發現:
class Solution:
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
temp = ListNode(0)
temp.next = head
ans = temp
while temp.next and temp.next.next:
n1 = temp.next
n2 = temp.next.next
n3 = temp.next.next.next
temp.next = n2
temp.next.next = n1
temp.next.next.next = n3
temp = n1
return ans.next
Leetcode26
思路:從前到後依次讀,因爲已排序,所以每一個讀進去的數和上一個讀的數做比較,相同一次,返回值就在原長度上減一,不同的話就要修改原序列的值,那麼除了循環裏的原數組下標變量外,還需要一個修改後數組的下標,以及一個變量記錄要比較的值。
發現: 在原數組上修改問題,一般都需要一個修改後數組的下標變量以及一個要比較的量。
class Solution:
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
res = length
if not nums:
return 0
temp = nums[0]
new = 1
for i in range(1, length):
if nums[i]!= temp:
nums[new] = nums[i]
new = new +1
temp = nums[i]
else:
res = res -1
return res
Leetcode27
思路:上一題剛總結好這類思路,這裏應用,在原數組上改,需要一個變量記錄改變後的下標,因爲已經給了要比較的值就不需要新變量存比較值了。從左往右讀,相同就返回長度減一,不同就在原數組上改。
發現:這類題要變成基本操作了哦
class Solution:
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
if not nums:
return 0
l = len(nums)
length = l
index = 0
for i in range(0, l):
if nums[i] != val:
nums[index] = nums[i]
index = index +1
else:
length -=1
return length
Leetcode28
思路:對haystack做逐位讀,讀到和needle第一位相同開始判斷剩下是否相同,一旦位數超了或者不同就break,這裏可以用一個小技巧,返回值在每次找到第一個相同元素時賦位該位置,隨後做剩下位置的比較,一旦有不同就把這個值改回去(原來是用計數器,如果相同元素等於needle長度那麼才改變返回值,這樣有點慢)。
發現:終極賴皮:return haystack.find(needle)。另,判斷語句if i+j>=len1:和if haystack[i+j] != needle[j]:如果合在一起寫並判斷就會超時。
class Solution:
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
if not needle and not haystack:
return 0
if not needle:
return 0
if not haystack and needle:
return -1
len1 = len(haystack)
len2 = len(needle)
if len2 > len1:
return -1
res = -1
for i in range(len1):
if haystack[i] == needle[0]:
res = i
for j in range(1, len2):
if i+j>=len1:
return -1
if haystack[i+j] != needle[j]:
res = -1
break
if res != -1:
return res
return res
Leetcode29
思路:移位當作除法
發現:
class Solution:
def divide(self, dividend, divisor):
"""
:type dividend: int
:type divisor: int
:rtype: int
"""
if dividend >= 0 and divisor >0:
flag = 1
if dividend < 0 and divisor >=0:
flag = -1
if dividend >= 0 and divisor <0:
flag = -1
if dividend < 0 and divisor <0:
flag = 1
res = 0
cnt = 1
dividend = abs(dividend)
divisor = abs(divisor)
subsum = divisor
while dividend >= divisor:
while (subsum << 1) <= dividend:
cnt = cnt << 1
subsum = subsum << 1
res = res + cnt
cnt =1
dividend -= subsum
subsum = divisor
return max(min(flag * res, 0x7fffffff), -2147483648)
Leetcode34
思路:logn直接想到二分法,二分找到該數後要向左向右匹配,同時更新下標。向左匹配和向右匹配要分開寫,下標在合理範圍內且等於目標值則存儲並更新下標。
發現:
left = 0
right = len(A) - 1
result = [-1, -1]
while left <= right:
mid = (left + right) / 2
if A[mid] > target:
right = mid - 1
elif A[mid] < target:
left = mid + 1
else:
result[0] = mid
result[1] = mid
i = mid - 1
while i >= 0 and A[i] == target:
result[0] = i
i -= 1
i = mid + 1
while i < len(A) and A[i] == target:
result[1] = i
i += 1
break
return result
Leetcode35
思路:二分定位,這題關鍵是如果沒有找到怎麼記錄它該插在哪裏。不用在while裏做,等全部while做完了,left和right相同時,對target進行比較,如果target比left(right)小就放左邊,大就放右邊。
發現:二分法的問題再細化
class Solution:
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
length = len(nums)
left = 0
right = length -1
res = 0
while left < right:
mid = int((left+right)/2)
if nums[mid] < target:
left = mid +1
elif nums[mid] > target:
right = mid -1
else:
return mid
if target>nums[left]:
return left+1
else:
return left
Leetcode36
思路:不能重複出現,可以用字典解決。想一想一共有多少組需要不能重複出現,9行,9列,9個宮格,所以需要27個字典。每一個數要判斷行上是否有重複的,列上是否有重複的,所在宮格是否有重複的,所以對這27個字典分個類。字典建好之後就好辦了,每個數挨個讀,所在行所在列容易得到,所在宮格怎麼得到呢?一行三個宮格,根據所在行列就可以計算出來在第幾個宮格,注意//取整的運用。然後就是挨個判斷,有一個重複就false,沒有重複的就把這個數加入到對應的字典中,整個遍歷完也沒重複的就true。
發現:感覺可以用集合做?判斷是否有重複
class Solution:
def isValidSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: bool
"""
raw = [{},{},{},{},{},{},{},{},{}]
col = [{},{},{},{},{},{},{},{},{}]
cell = [{},{},{},{},{},{},{},{},{}]
for i in range(9):
for j in range(9):
temp = board[i][j]
num = 3*(i//3) + (j//3)
if temp != '.':
if temp not in raw[i] and temp not in col[j] and temp not in cell[num]:
raw[i][temp] =1
col[j][temp] =1
cell[num][temp] =1
else:
return False
return True
Leetcode38
思路:一層循環是按項生成,對於第n個生成的項都要在第n-1個項上而來,需要二層循環讀取第n-1項的各位,從第一位起,統計該數字num連續出現次數m,輸出值里加上m+num。每一層都是單獨生成的,所以需要一個臨時變量temp2來存儲,每一層是基於上一層統計的,所以需要一個臨時變量res記錄上一層。每一個數,判斷它和左邊一個是否相等,如果相等就計數器m加1,一旦不等就要在返回值res裏添加了。
發現:和人腦一樣,按位讀,如果該位和前位不同,表明前面數字不再連續,那麼輸出,所以只有在不同情況才輸出。最後一位判斷的時候,如果和前一位不同,那麼把前一位的數量和數字輸出,然後還需要輸出1和自己,如果和前一位相同,那麼現在不能輸出,還需要輸出m和自己,那麼在每一個二層循環之後還需要額外輸出 一次,加上m和該位。
class Solution:
def countAndSay(self, n):
"""
:type n: int
:rtype: str
"""
if n ==1:
return '1'
if n ==2:
return '11'
res = '11'
for i in range(3, n+1):
temp1 = res
temp2 = ''
m = 1
for j in range(1, len(temp1)):
num = temp1[j]
if num == temp1[j-1]:
m = m + 1
else:
temp2 = temp2 + str(m) + str(temp1[j-1])
m = 1
temp2 = temp2 + str(m) + str(temp1[j])
res = temp2
return res
Leetcode41
思路:剛開始覺得挺簡單的,一看好多限制條件,要O(n)的時間複雜度,而且還要常數空間。
分析一下:因爲是最小的整數,
反正是從1,2,3。。。。開始的。
那就把每一個數,給到他們自己的位置,比如說【3,4,-1,1】
把所有的數都放到位置:變成【1,-1,3,4】 那就能找到 那個-1的位置了。
因爲要常數空間,那估計的言外之意就是,交換swap。
1.不過這裏面有坑,首先【2,3】這樣的,3就不能換,也就是說大過數組長度的不要動。
3.而且小於0的也不能換,
3.還有這個【3,4,-1,1】如果只換一次。。
先換3,變成【-1,4,3,1】
然後4 變成【-1,1,3,4】
哎到頭了。。。這個1怎麼辦。。。
所以要不停的換,換到不能換了爲止:
改成:
先換3,變成【-1,4,3,1】,第一個位置是-1,不能換了
再還4,變成【-1,1,3,4】,第二個位置是1,繼續換【1,-1,3,4】
後面就不換了。。。
4.還有一個坑:【1,2,3】。。。換到頭了,怎麼辦,返回4
發現:nums[i]和nums[nums[i]-1]交換的時候要注意如用a,b = b,中間的索引並不會變
注意用temp交換時,第三段nums[temp-1]而不是nums[nums[i]-1],因爲nums[i]已經變了
class Solution:
def firstMissingPositive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(len(nums)):
while (nums[i]>0 and nums[i] <= len(nums) and nums[i]!= nums[nums[i]-1]):
# nums[i],nums[nums[i]-1]=nums[nums[i]-1], nums[i]
temp = nums[i]
nums[i] = nums[nums[i]-1]
nums[temp-1] = temp
for i in range(len(nums)):
if (nums[i] != i+1):
return i+1
return len(nums)+1
Leetcode 46
思路:長度爲1,0直接返回。一個n個數全排列包括除了n個數在第一位的剩餘n-1個數全排列。
迭代要去除已有數據nums更新爲nums[0:i]+(nums[i+1:]
發現:經常寫成.append[],必須是.append()
Leetcode 47
思路:
發現:
第一段代碼正常思路,一個個迭代,不考慮重複的找,每找到一各長度ok的就判斷它是否在之前出現過,出現過就不加了。
self.helper(nums, res, [])
return res
def helper(self, nums, res, path):
if not nums and path not in res:
res.append(path)
else:
for i in range(len(nums)):
self.helper(nums[:i] + nums[i+1:], res, path + [nums[i]])
第二個方法:思路還是不斷加,DFS中加到長度和nums相等就添加到結果中,爲了避免重複,那麼每次加數的時候,要判斷這次加進去的數和之前一次加進去的數是否一致,如果一致就跳過這次。加進去一個數之後,剩下的數組合templist繼續做DFS,剩下數組合可以用(nums[0:1]+nums[i+1])表示(奇怪爲什麼不會超過長度)做完要pop()
self.lennum = len(nums)
nums.sort()
self.res = []
templist =[]
self.DFS(nums, [])
return self.res
def DFS(self, nums, templist):
if len(templist) == self.lennum:
self.res.append(templist[:])
return
for i in range(len(nums)):
if i!=0 and nums[i]==nums[i-1]:
continue
else:
templist.append(nums[i])
self.DFS(nums[0:i]+(nums[i+1:]), templist)
templist.pop()
Leetcode 48
思路:
發現:可以作爲基礎知識:如何將一個矩陣順時針旋轉90度,先轉置矩陣,然後每行反轉。
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
lennum = len(matrix)
for i in range(lennum):
for j in range(i+1,lennum):
temp = matrix[i][j]
matrix[i][j] = matrix[j][i]
matrix[j][i] = temp
for i in range(lennum):
matrix[i] = matrix[i][::-1]
return matrix
Leetcode 49
思路:列表每個字符串排序,存進字典,鍵是拍過序的字符串,值是字母相同、順序不同的字符串。
發現:字典的鍵不能爲列表,所以temp1 = str(sorted(nums))
class Solution:
def groupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
"""
res = {}
for nums in strs:
temp1 = str(sorted(nums))
if temp1 not in res:
res[temp1] = []
res[temp1].append(nums)
return [res[x] for x in res]
Leetcode 50
思路:可以直接return x**n,但是沒意思,可以遞歸,2的10次方等於2的5次方相乘,2的11次方等於2的5次方相乘再乘以2,遞歸下去。
發現:
class Solution(object):
def myPow(self, x, n):
"""
:type x: float
:type n: int
:rtype: float
"""
if n < 0:
return 1 / self.myPow(x,-1 * n)
if x == 0:
return 0.0
if n == 0:
return 1.0
if n == 1:
return x
tmp = self.myPow(x,n // 2)
if n % 2 == 0:
return tmp * tmp
else:
return tmp * tmp * x
Leetcode 53
思路:遍歷是肯定要遍歷的,每讀一位怎麼比較,記錄什麼?應該要先算以每一位結尾的最大子串是多少,下一位讀進來,以下一位結尾的最大子串要麼是它本身,要麼是它加上以前一位結尾的最大子串(都是局部最優)。
最後,將所有,以每一位結尾的最大子串值進行比較,就得到全局最優。
class Solution:
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
lennum = len(nums)
if lennum ==1:
return nums[0]
res = -99999999
temp = -9999999
for i in range(0, lennum):
temp = max(nums[i], nums[i]+temp)
res = max(res, temp)
return res
Leetcode 54
思路:
從左上開始,按照箭頭的順序,進行4個for循環,完成一圈,一共用count = min(row,col)/2 圈。
注意最後的紅色箭頭,如果行數和列數中較小的數是奇數,就會出現上述情況。
這個時候咱們在最後補上就好
發現:每一輪循環下標都要注意,第i輪循環,第一次從matrix[i][i] 循環到matrix [i] [col-1-i] (最後一個不取,少一個給第二次循環開頭),第二次從matrix[i] [col -1-i] 循環到matrix[row-1-i] col-1-i-1,第三次從matrix[row-1-i ][col-1-i]循環到matrix[row-1-i][i],第四輪次從matrix[row-i-1][i]循環到matrix[row-i-1][i]循環到matrix[i][i]。
class Solution:
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
if matrix == []:
return []
row = len(matrix)
column = len(matrix[0])
rou = min(row, column)//2
mod = min(row, column)%2
res = []
for i in range(rou):
for j in range(i, column-i-1):
res.append(matrix[i][j])
for k in range(i, row-i-1):
res.append(matrix[k][column-i-1])
for l in range(i, column-i-1):
res.append(matrix[row-1-i][column-l-1])
for m in range(i, row-i-1):
res.append(matrix[row-m-1][i])
if mod ==1 :
if row==column:
res.append(matrix[rou][rou])
elif row>column:
for j in range(row-column+1):
res.append(matrix[rou+j][rou])
else:
for j in range(column-row+1):
res.append(matrix[rou][rou+j])
return res
Leetcode 55
思路:記錄當前位置能走的最遠位置max(maxpos,i+nums[i]),如果最遠位置等於該位置,則返回False,如果一直到結尾則返回Ture
發現:必定要到一個位置,同時到這個位置會被卡主。記錄每個位置能到達的最遠距離,如果一路訪問到這個最遠距離的位置還出不去,那麼就返回false。
能不能到達終點問題,可以轉化成每個位置能到達的最遠點。
class Solution:
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
maxpoi = 0
for i in range(len(nums)-1):
maxpoi = max(maxpoi, i+nums[i])
if maxpoi == i:
return False
return True
Leetcode 56
思路:首先按照開始區間進行排序sorted,然後從第一個區間開始,如果相鄰的兩個區間,end小於start則合併區間,且生成新的區間,如果不小於,則放到返回區間
發現:(1)List長度爲0和1時,直接返回即可:Line10-11
(2)排序要以start爲參考:Line12
(3)迭代判斷:Line17-21
a)end小於start則合併區間,且生成新的區間,並且賦給現在位置的list Line17-19
b)不成立,則把前一個區間添加到re Line20-21
(4)要把最後一個區間添加:Line22
# Definition for an interval.
# class Interval:
# def __init__(self, s=0, e=0):
# self.start = s
# self.end = e
class Solution:
def merge(self, intervals):
"""
:type intervals: List[Interval]
:rtype: List[Interval]
"""
lenint = len(intervals)
if lenint<2:
return intervals
intervals = sorted(intervals, key =lambda x:x.start)
re = []
for i in range(1, lenint):
last = intervals[i-1]
now = intervals[i]
if now.start<=last.end:
now.end = max(intervals[i].end, last.end)
now.start = last.start
else:
re.append(intervals[i-1])
re.append(intervals[i])
return re
Leetcode 59
思路:與之前題一樣,四個方向去生成,因爲長寬相同,偶數的話正好,奇數的話最後要在中間生成一個點。
發現:這種題,首先算輪數n//2,生成一個初始爲0的n*n矩陣,res= [[0 for x in range(n)] for x in range(n)],還需要一個計數器計數。注意每個循環的取值,下標取值。
roun = n // 2
res = [[0 for x in range(n)] for x in range(n)]
cnt = 1
for i in range(roun):
for j in range(i, n - i - 1):
res[i][j] = cnt
cnt = cnt + 1
for k in range(i, n - i - 1):
res[k][n - 1 - i] = cnt
cnt = cnt + 1
for l in range(i, n - i - 1):
res[n - 1 - i][n - 1 - l] = cnt
cnt = cnt + 1
for m in range(i, n - i - 1):
res[n - 1 - m][i] = cnt
cnt = cnt + 1
if n % 2 == 1:
res[roun][roun] = cnt
return res
Leetcode60
思路:這種生成全排列第一反應是DFS,但是超時,只能想不用生成全部,單獨去找這第k個應該是什麼?
發現:
暴力DFS,把所有的都生成出來
class Solution:
def getPermutation(self, n, k):
"""
:type n: int
:type k: int
:rtype: str
"""
nums = [str(i) for i in range(1, n+1)]
res = []
self.DFS(res, [], nums)
return res [k-1]
def DFS(self, res, temp1, nums):
if len(nums) == 0:
res.append("".join(temp1))
for i in range(len(nums)):
temp1.append(nums[i])
self.DFS(res, temp1, nums[:i]+ nums[i+1:])
temp1.pop()
return res
根據一位有多少個全排列依次算
class Solution:
def getPermutation(self, n, k):
"""
:type n: int
:type k: int
:rtype: str
"""
res = ''
k = k-1
fac =1
for i in range(1,n): #第一位確定是的全排列數量
fac= fac*i
nums = [str(i) for i in range(1, n+1)]
for i in reversed(range(n)):
curr =nums[k//fac] #下標是k//fac的數字
res = res + str(curr)
nums.remove(curr) #使用過的數字移除
if i != 0:
k = k%fac
fac = fac //i #(再固定一位全排列)
return res
Leetcode 61
思路:
發現:鏈表問題就是要畫圖,找到變化前後區別,頭結點在哪,哪些節點下一個改變了
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
if head == None or k ==0:
return head
dummy = ListNode(0) #創建一個虛假的頭結點
dummy.next = head
#計算鏈表長度
cnt =0
temp = dummy
while temp.next != None:
temp = temp.next #最終p指向尾節點
cnt = cnt +1
temp.next = dummy.next #形成一個環
#找到最新的head
right = cnt - k%cnt
temp = dummy.next #指向head
for i in range(1, right): #去找最新的頭結點
temp = temp.next
head = temp.next #找到頭結點賦給head
temp.next = None
return head
Leetcode 62
思路:動態規劃初嘗試,因爲f(m,n) = f(m-1,n) + f(m,n-1)建立一個m*n的數組,每個格子裏存的是當前格子需要的步數。
發現:
class Solution:
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
res=[[1 for i in range(n)] for i in range(m)] #m與n的位置別弄反了,m是行,n是列,第一行,第一列初始值都賦值爲1
for i in range(1,m):
for j in range(1,n):
res[i][j]=res[i-1][j]+res[i][j-1]
return res[m-1][n-1]
Leetcode 63
思路:有障礙的地方路徑數爲0,還是建表,初始化的時候注意第一行和第一列
發現:
m = len(obstacleGrid)
n = len(obstacleGrid[0])
if obstacleGrid[0][0] == 1:
return 0
newGrid = [[0 for i in range(n)] for i in range(m)]
#初始化第一行第一列
for i in range(m):
if obstacleGrid[i][0] !=1:
newGrid[i][0] = 1
else:
break
for j in range(n):
if obstacleGrid[0][j] !=1:
newGrid[0][j] = 1
else:
break
#賦值[1,1]~[m-1][n-1]
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j]!=1:
newGrid[i][j]= newGrid[i-1][j]+ newGrid[i][j-1]
else:
newGrid[i][j]=0
return newGrid[m-1][n-1]
Leetcode64
思路:和前兩題一樣,newGrid[i][j] = min(newGrid[i-1][j], newGrid[i][j-1]) + grid[i][j]即可
發現:
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
newGrid = [[0 for i in range(n)] for i in range(m)]
#給第一行第一列賦值
newGrid[0][0] = grid[0][0]
for i in range(1, n):
newGrid[0][i] = newGrid[0][i-1] + grid[0][i]
for j in range(1, m ):
newGrid[j][0] = newGrid[j-1][0] + grid[j][0]
for i in range(1, m):
for j in range(1, n):
newGrid[i][j] = min(newGrid[i-1][j], newGrid[i][j-1]) + grid[i][j]
return newGrid[-1][-1]
Leetcode67
思路:先字符串轉列表進行計算,最後列表轉字符串。因爲a和b長度不同,下標計算方式不同,故分開討論,從後往前分開加,該位和加進位大於2就進位,否則就不進位。最後還有進位的話,就在該列表前插一個1。
發現:列表和字符串相互轉換注意,a =list(a)後,列表裏元素都是字符串,如需計算,還需挨個int()。列表轉字符串,如果兩個元素之間不需要空格 “”.join(列表名),如果需要空格”“.join(列表名)
Leetcode 69
思路:通過二分法去查找結果,注意,left<=right
發現:
class Solution:
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
if x < 2:
return x
left = 1
right = x / 2
while left <= right:
mid = int((left + right) / 2)
if(x > mid * mid):
left = mid + 1
lastMid = mid
elif(x < mid * mid):
right = mid - 1
else:
return int(mid)
return int(lastMid)
Leetcode70
思路:明顯遞歸問題f(n)=f(n-1) +f (n-2),但是直接寫超時,所以因爲問題固定,那麼就把數列建起來
發現:
超時算法
class Solution:
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
if n==1:
return 1
if n==2:
return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
不超時算法
class Solution:
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
count = [1,2]
for i in range(2, n):
count.append(count[i-2]+count[i-1])
return count[n-1]
Leetcode 71
思路:看到這種來來回回,增增刪刪的題,一般都想到用棧。
我們把字符串按照/分割之後就得到了每個文件的目錄,然後判斷路徑是添加還是向上層進行返回。這個題很簡單了。
有一個地方犯了小錯誤,不能寫成if route == ‘..’ and stack: stack.pop()。這樣的話如果棧是空的,就把..進棧了。
發現:
class Solution:
def simplifyPath(self, path):
"""
:type path: str
:rtype: str
"""
stack = list()
dirs = path.split('/')
for route in dirs:
if not route or route =='.':
continue
if route =='..':
if stack:
stack.pop()
else:
stack.append(route)
return '/' + '/'.join(stack)
Leetcode 73
思路:1、暴力法:設定一個同樣大小的矩陣,每次遇到0,然後將第二個矩陣的相應位置置0,空間複雜度爲O(mn)。
2、略微優化:每次遇到0的時候,記錄需0的行號和列號,空間複雜度爲O(m+n)。
3、空間複雜度爲1,只需要將2中0的行號列號的記錄在第一行和第一列就行了。
利用第一行和第一列的元素去標記該行或該列是否在更新時要全部變成0。但是這樣操作會使得第一行和第一列的原始狀態丟失。因此,我們需要額外一個變量hasZeros去保存第一列(或者第一行)在更新時是否要變成0,這樣就不會有問題了。
另外還有一個讓代碼更加精簡的小竅門,就是在更新的時候從下往上(Bottom-Up),這樣就不用擔心會破壞第一行或第一列的數據了。
發現:
class Solution:
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
if len(matrix)==0:
return matrix
m = len(matrix)
n = len(matrix[0])
newmatrix = [[0 for i in range(n)] for i in range(m)]
for i in range(m):
for j in range(n):
newmatrix[i][j] = matrix[i][j]
for i in range(m):
for j in range(n):
if matrix[i][j]==0:
for k in range(n):
newmatrix[i][k] = 0
for l in range(m):
newmatrix[l][j] = 0
for i in range(m):
for j in range(n):
matrix[i][j] = newmatrix[i][j]
m = len(matrix)
n = len(matrix[0])
if m == 0 or n == 0:
return
hasZeros = 0
for i in range(m):#第i行
if matrix[i][0] == 0:#第零列
hasZeros = 1
for j in range(1,n):#第i行的第j列
if matrix[i][j] == 0:
matrix[i][0] = 0
matrix[0][j] = 0
for i in range(m-1,-1,-1):#i = 2,1,0
for j in range(n-1,0,-1):#j = 2,1
if matrix[i][0] == 0 ormatrix[0][j] == 0:
#第零列的第i行,或第零行的第i列任一個爲零,則將第i行第j列的元素置爲零
matrix[i][j] = 0
if hasZeros == 1:
matrix[i][0] = 0
return
Leetcode74
思路:[]、 [[]]等返回false,排序最快還是快排,找準下標,第x個數排進m*n的矩陣,位置在[int(x/n)][x%m]
發現:
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) == 0 or len(matrix[0]) == 0:
return False
m = len(matrix)
n = len(matrix[0])
start, end = 0, m * n - 1
while start <= end:
mid = int(start + (end - start) / 2)
if matrix[int(mid/n)][mid%n] > target:
end = mid - 1
elif matrix[int(mid/n)][mid%n] < target:
start = mid + 1
else:
return True
return False
Leetcode75
思路:0移到最左邊,2移到最右邊,移動後需繼續判斷,遇到1則跳過。因爲從左邊開始循環,必須換到當前位置爲0,1才進入下一位,這裏的1會被後面的0替換掉
發現:
class Solution:
def sortColors(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
left = 0
right = len(nums) - 1
i = 0
while i <= right:
if nums[i] == 2:
nums[i], nums[right] = nums[right], nums[i]
right -= 1
if nums[i] == 0:
nums[i], nums[left] = nums[left], nums[i]
left += 1
i += 1
continue
if nums[i] == 1:
i += 1
Leetcode 77
思路:這類全排列問題,第一反應是dfs,參數需要記錄當前排到哪個數了、當前生成的矩陣、還差幾個數、剩下res和n,即[res, i, n, k,temp]。
發現:
class Solution:
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = []
self.dfs(res, 0, n, k, [])
return res
def dfs(self, res, i, n, k, temp):
if k ==0:
res.append(temp)
else:
for i in range(i+1, n+1):
self.dfs(res, i, n, k-1, temp + [i])
Leetcode 78
思路:① 最外層循環逐一從 nums 數組中取出每個元素 num
② 內層循環從原來的結果集中取出每個中間結果集,並向每個中間結果集中添加該 num 元素
③往每個中間結果集中加入 num
④將新的中間結果集加入結果集中
發現:建立過程[]——[],[1]——[],[1],[2]——[], [1],[2],[1,2]——[],[1],[2],[1,2],[3]——[],[1],[2],[1,2],[3][1,3]——[],[1],[2],[1,2],[3],[1,3],[2,3]——[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]
res = [[]]
for num in nums :
for temp in res[:] :
x = temp[:]
x.append(num)
res.append(x)
return res
Leetcode79
思路:對於任何board中的一個元素,如果它上下左右的元素存在並且沒有被訪問到,則存在着向上,向下,向左,向右的四條搜索路徑,那麼將這條搜索路徑定義成遞歸函數,這個遞歸函數需要知道搜索點的位置以及word中剩下的子字符串。遞歸的終止條件是word中剩下的子字符串爲空。但是超時。
發現:
class Solution:
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
def find(board, word, i, j, m, n):
if word == '':
return True
if i < 0 or i >= m or j < 0 or j >= n:
return False
elif word[0] == board[i][j]:
board[i][j] = None #標記
res = find(board, word[1:], i+1, j, m, n)or find(board, word[1:], i-1, j, m, n)or find(board, word[1:], i, j+1, m, n)or find(board, word[1:], i, j-1, m, n)
board[i][j] = word[0] #恢復原來的值
return res
if len(word) == 0:
return True
m = len(board)
if m == 0:
return False
n = len(board[0])
for i in range(m):
for j in range(n):
if find(board, word, i, j, m, n):
return True
else:
return False
Leetcode 80
思路:遍歷數組,用一個變量計數,該位如果和前位一樣就加1,統計該數出現次數,如果大於2,就把該位刪了,如果不同就重新計數。
發現:數組長度變化,所以用while
size = len(nums)
temp = 1
i = 1
if size == 0 or size == 1:
return size
while i < len(nums):
if nums[i] == nums[i-1]:
temp = temp + 1
if temp >2:
del nums[i]
else:
i = i+1
else:
temp = 1
i = i+1
return len(nums)
Leetcode
思路:賴皮,if target in nums:。或者先二分找極小值,這個就是分界點,比兩邊都大,
發現:
class Solution:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
if target in nums:
return True
else:
return False
Leetcode82
思路:遍歷節點,如果相鄰兩個節點值一樣就刪掉,問題是如果連續奇數個值以上相同,最後一個數會被錯誤留下,所以當有連續兩個數相同時,需要繼續判斷後面的數是否一樣,如果一樣一起刪除,所以需要多一個指針cur。
發現:
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dummy = ListNode(0)
dummy.next = head
temp = dummy
while temp.next and temp.next.next:
if temp.next.val == temp.next.next.val:
cur = temp.next
temp.next = temp.next.next.next
while cur and cur.next and cur.val == cur.next.val:
cur = cur.next
temp.next = cur.next
else:
temp = temp.next
return dummy.next
Leetcode 83
思路:既然是鏈表已經是有序的了,那麼只需遍歷一次即可。遍歷時需要考慮當前結點的值與下一結點的值,如果相等就移除下一結點,注意此時要保持當前結點不變,而下一結點變成原來的下下結點。如果值不同,那麼只需把兩個結點指針都後移即可。
發現:
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
temp = head
while temp and temp.next:
if temp.next.val == temp.val:
temp.next = temp.next.next
else:
temp =temp.next
return head
Leetcode 86
思路:給個list,給個值,把list分爲兩部分,小於x的放在前,大於等於x的放在後,兩部分各自保持原有順序
發現:
class Solution:
def partition(self, head, x):
"""
:type head: ListNode
:type x: int
:rtype: ListNode
"""
if head == None or head.next == None or x == None:
return head
p1=head1=ListNode(0)
p2=head2=ListNode(0)
p=head
while p:
if p.val<x:
p1.next=p
p1=p1.next
else:
p2.next=p
p2=p2.next
p=p.next
p1.next=head2.next
p2.next=None
return head1.next
Leetcode88
思路:先把nus1裏初始化的0清掉,再把nums2裏的元素挨個放進nums1裏,最後排序nums1
發現:
class Solution:
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.
"""
for i in range(len(nums1)-m):
nums1.pop()
for j in range(len(nums2)):
nums1.append(nums2[j])
nums1.sort()
Leetcode 89
思路:考慮到格雷碼相鄰兩個二進制數只能在一位上有差異,所以我們可以用一種從最低爲開始,逐位加一的方法去解決這個問題,直接看一個例子,我們以n=3爲例。
我們可以令一開始的數爲000,然後在最低位加一,此時所有已知格雷碼爲:
000
001
然後我們在上述已知的格雷碼中,從下往上在次低位加一:
000
001
011
010
這裏要注意加一的順序,必須要從下往上加,因爲新增加的數字如果是從上往下產生的,那麼就相當於是同時在多個位上發生改變,而從下往上加可以確保只有一位發生了改變,又因爲原先有的相鄰格雷碼之間只有一位有區別,如果在同一個位置上都加一,相鄰兩個之間的差異仍然只有一,因此性質還是成立的。
最後我們在最高位上加一,注意還是要從下往上:
000
001
011
010
110
111
101
100
發現:注意鏡像規律才能保證相鄰位只差一位,需要一個矩陣存儲res[::-1]。
class Solution:
def grayCode(self, n):
"""
:type n: int
:rtype: List[int]
"""
res = [0]
i = 0
while i < n:#從2的0次方開始,
res_inv = res[::-1]#求res的反向list
res_inv = [x + pow(2,i) for x in res_inv]
res = res + res_inv
i += 1
return res
Leetcode 90
思路:集合中加入了重複元素,這樣可以判斷集合中是否已有當前子集,若無則將該子集加入到res中,if x not in res:res.append(x),注意先拍個序,不然會有[1,4],[4,1] 兩種組合題都可以用這種添加法
發現:
沿用78題思路版本:
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = [[]]
nums.sort()
for num in nums :
for temp in res[:] :
x = temp[:] #想要判斷res裏的每一個數組temp加上num是否在res裏過,不能直接再temp上變,需要一個臨時變量x
x.append(num)
if x not in res:
res.append(x)
return res
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def dfs(depth, start, valuelist):
if valuelist not in res:
res.append(valuelist)
if depth == len(nums):
return
for i in range(start, len(nums)):
dfs(depth+1, i +1, valuelist+[nums[i]])
nums.sort()
res = []
dfs(0, 0, [])
return res
Leetcode 94
思路:遞歸解法,對於每一個點來說都有一箇中序遍歷,所以可以寫一個re_inorder函數,只要當前節點存在,就對其先求左節點的中序排序,記錄當前點,對右節點中序排序。
發現
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
res = []
self.re_inorder(root, res)
return res
def re_inorder(self, root, res):
if root:
self.re_inorder(root.left, res)
res.append(root.val)
self.re_inorder```````
root.right, res)
Leetcode95
思路:二叉查找樹,也稱爲二叉搜索樹,二叉排序樹。它可以是一棵空樹,也可以是具有下列性質的二叉樹:
若二叉查找樹的左子樹不空,則左子樹上的所有結點的值均小於它的根結點的值,若右子樹不爲空,則右子樹上所有結點的值均大於它的根結點的值;它的左右子樹也分別爲二叉排序樹。
這個題目難在構造出來。一般構造樹都需要遞歸。從1–n中任意選擇一個數當做根節點,所以其左邊的數字構成其左子樹,右邊的數字當做右子樹。因爲要求出所有的子樹,所以當左子樹固定的時候,把所有可能的右子樹都構成,然後再變換左子樹
發現:返回的類型是List[TreeNode]意味着函數是返回樹節點類型的結果。這個代碼難理解的地方在於left_nodes 和 right_nodes的求法,這個一定要結合遞歸的終止條件去看,當選擇的根節點的值i比left小的時候,那麼其實左子樹就是空了。如果把這個理解了,那麼下面的對左右子樹的遍歷應該也不難理解
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def generateTrees(self, n):
"""
:type n: int
:rtype: List[TreeNode]
"""
if n == 0: return []
return self.generateTreesDFS(1, n)
def generateTreesDFS(self, left, right):
if left > right:
return [None]
res = []
for i in range(left, right + 1):
left_nodes = self.generateTreesDFS(left, i - 1)
right_nodes = self.generateTreesDFS(i + 1, right)
for left_node in left_nodes:
for right_node in right_nodes:
root = TreeNode(i)
root.left = left_node
root.right = right_node
res.append(root)
return res
Leetcode 96
思路:這裏是指不同結構,結構相同的二叉搜索樹只有一種分佈,因爲每個數之間的大小關係都很清晰。我們發現,如果數組爲空,毫無疑問,只有一種 BST,即空樹,f (0) = 1。
如果數組僅有一個元素 1,只有一種 BST,單個節點,f (1) = 1。
如果數組有兩個元素 1,2,那麼有如下兩種可能
f (2) = f (0) * f (1) ,1 爲根的情況
+ f (1) * f (0) , 2 爲根的情況
再看一看 3 個元素的數組,可以發現 BST 的取值方式如下:
f (3) = f (0) * f (2) ,1 爲根的情況
+ f (1) * f (1) ,2 爲根的情況
+ f (2) * f (0) ,3 爲根的情
所以,由此觀察,可以得出 f 的遞推公式爲
發現:注意初始值,dp[0]=1,需要一個二重循環,一維動態規劃問題。
class Solution:
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0 for i in range(n+1)]
dp[0] = 1
dp[1] = 1
for i in range(2, n+1):
for j in range(0, i):
dp[i] = dp[i] + dp[j]*dp[i-1-j]
return dp[n]
Leetcode 98
思路:一開始想,遍歷每個節點,判斷它們是否滿足左節點比它小,右節點比它大即可,後來發現不對。所以迭代的時候需要時刻記錄當前的節點能取的最大值和最小值。
發現:
錯誤思路代碼如下,利用遍歷,對每個節點判斷,不對。
class Solution:
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
return self.valid(root)
def valid(self, root):
if root:
if (root.left != None and root.left.val >= root.val) or (root.right != None and root.right.val <= root.val):
return False
self.valid(root.left)
self.valid(root.right)
return True
Leetcode118
思路:
發現:初始化temp =[1]*(i+1)很重要,不然不好賦值。
class Solution:
def generate(self, numRows):
"""
:type numRows: int
:rtype: List[List[int]]
"""
res= []
for i in range(numRows):
temp = [1]*(i+1)
res.append(temp)
for j in range(1, i):
res[i][j] = res[i-1][j-1]+res[i-1][j]
return res
Leetcode120
思路:動態規劃,每個點記錄到達該點要走的最短路徑,從底向上計算。
發現:
class Solution:
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
n = len(triangle)
dp = triangle[n-1]
for i in range(0, n-1)[::-1]:
for j in range(i+1):
dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]
return dp[0]
Leetcode 121
思路:第一反應找最大值和最小值,最大的早於最小的,那麼就找次大和次小,這樣肯定很麻煩,從這個過程已經能感受到一點動態規劃的感覺。是的沒錯,這道題就是用動態規劃,我們維持兩個變量,最低買入價格和當前可達到的最高利潤,從第二天開始遍歷,小於最低價格那麼我們更新最低價格變量,然後以這一天的價格作爲賣出價格,那麼利潤就是賣出價格-最低價格,最次也就是0,也就是我更新了最低價格還以最低價格賣出去了,因爲不能用之前的價格賣,此時利潤也要相應的更新,大於保存的最大利潤我們就更新,遍歷完成後得到結果。
發現:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
if (len(prices) <=1):
return 0
buy_price = prices[0]
max_profit = 0
for i in range(1, len(prices)):
buy_price = min(buy_price, prices[i])
max_profit = max(max_profit, prices[i] - buy_price)
return max_profit
Leetcode122
思路:122題是說不限制買賣股票的次數,只要保證你賣的日期晚於買的日期即可。這個就適合用貪心算法,只要當前比前一天的大,那麼我們就賣了股票。
發現:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
n = len(prices)
max_profit = 0
for i in range(1, n):
if prices[i]>prices[i-1]:
max_profit = max_profit +(prices[i] - prices[i-1])
return max_profit
Leetcode123
思路:將天數分開,前i天調用一次121的算法,後面的天調用一次121的算法,但是要注意如果外層循環i,裏面再循環121的算法,會超時,這時我們考慮用兩個數組來存儲結果,pre_profit和pro_profit,其中pre_profit[i]表示i天之前的最大利潤,pro_profit[i]表示i天之後的最大利潤,前i天的很好理解和121一樣的寫法,後i天注意要從後往前動態規劃。
發現:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
n = len(prices)
if n == 0:
return 0
maxpre = [0 for i in range(n)]
maxpro = [0 for i in range(n)]
max_profit = 0
pre_min = prices[0]
for i in range(1, n):
pre_min = min(pre_min, prices[i])
maxpre[i] = max([maxpre[i-1], prices[i] - pre_min])
pro_max = prices[-1]
for i in range(1, n-1)[::-1]:
pro_max = max(pro_max, prices[i])
maxpro[i] = max(maxpro[i+1], pro_max - prices[i])
for i in range(n):
max_profit = max(max_profit, maxpre[i] + maxpro[i])
return max_profit
Leetcode 125
思路:主要是對字符串進行處理,保留數字字母並判斷是否迴文
發現:
class Solution:
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
string = []
for i in range(len(s)):
if s[i].isalnum():
string.append(s[i].lower())
if string == string[::-1]:
return True
else:
return False
超級簡潔版本:
cleanlist = [c for c in s.lower() if c.isalnum()]
return cleanlist == cleanlist[::-1]
Leetcode 128
思路:將這些數放進一個集合,對集合裏的數遍歷,每個數去向上向下找下一個數在不在,找到了就長度加1,集合裏去掉這個數,最後返回最長的子串
發現:
用集合:
class Solution:
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
s = set(nums)
for num in nums:
if num in s:
s.discard(num)
cnt = 1
right = num + 1
left = num - 1
while left in s:
s.discard(left)
cnt += 1
left -= 1
while right in s:
s.discard(right)
cnt += 1
right += 1
ans = max(ans, cnt)
return ans
用哈希表,鍵是數組值,值記錄是否用過:
class Solution:
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
d ={}
for num in nums:
d[num] = 0
for num in nums:
cnt = 1
d[num] = 1
left = num - 1
right = num + 1
while left in d and d[left] == 0:
d[left] = 1
left = left - 1
cnt = cnt + 1
while right in d and d[right] == 0:
d[right] = 1
right = right +1
cnt = cnt +1
ans = max(ans, cnt)
return ans
Leetcode135
思路:結果數組是res的話,初始全是1,我一開始想着遍歷一遍,兩個相鄰依次比,rating大的那個res裏對應改爲rating小的+1,正着一遍if rating[i
]>rating[i-1]:res[i] = res[i-1] +1 if rating[i]
class Solution(object):
def candy(self, ratings):
"""
:type ratings: List[int]
:rtype: int
"""
n = len(ratings)
left = [1] * n
ans = 0
for i in range(1, n):
if ratings[i] > ratings[i-1]:
left[i] = left[i-1] + 1
ans = left[-1]
for i in reversed(range(0, n - 1)):
if ratings[i] > ratings[i+1]:
left[i] = max(left[i], left[i+1] + 1)
ans += left[i]
return ans
Leetcode136
思路:想到用哈希表做
發現:取字典裏值最小對應的鍵值res = min(temp, key=temp.get)
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
temp = {}
for i in range(len(nums)):
if nums[i] not in temp:
temp[nums[i]] = 1
else:
temp[nums[i]] = 2
res = min(temp, key=temp.get)
return res
另一種字典操作方法:
dic = {}
for num in nums:
dic[num] = dic.get(num, 0)+1
for key, val in dic.items():
if val == 1:
return key
Leetcode136
思路:可用同樣方法
發現:
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
temp = {}
for i in range(len(nums)):
if nums[i] not in temp:
temp[nums[i]] = 1
else:
temp[nums[i]] = 3
return min(temp, key=temp.get)
Leetcode 139
思路:# dp[i] = true means that s[0, i -1] can be constructed by the words in wordDict. # So, dp[0] must be ture.
發現:dp解決此類問題,關鍵在於遍歷什麼,每個位置存什麼,此題中,每個位置應當存的是s[0, i-1]是否可以由wordDict裏的詞構成。所以需要一個二重循環判定從0到i進行分。
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
n = len(s)
dp = [True]+[False]*n
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]
Leetcode151
思路:
發現:
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
rs=s[::-1] #將整個字符串反轉
l=rs.split() #將反轉後的字符串通過split()函數進行分割,產生的單詞發在列表l中
ls=[word[::-1] for word in l] #使用列表解析,反轉列表l中的每一個單詞
return ' '.join(ls)
Leetcode 152
思路:本題要求連續子數組的最大乘積,思路與求連續子數組的最大和相似,都是採用動態規劃,maxvalue[i]表示以a[i]爲結尾的子數組中最大乘積,同時維護一個全局最大值globalmax,記錄maxvalue[i]中的最大值。與求子數組的最大和不同的是,還需要維記錄子數組最小乘積minvalue[i],因爲可能會出現 負 × 負 = 正的情況。並且最大最小乘積只可能出現在
(maxvalue[i−1]×a[i],minvalue[i−1]×a[i],a[i])三者之間
發現:
class Solution(object):
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
maxvalue = minvalue = nums[0]
globalmax = nums[0]
for i in range(1, n):
lastmax = maxvalue
maxvalue = max(minvalue*nums[i], lastmax*nums[i], nums[i])
minvalue = min(minvalue*nums[i], lastmax*nums[i], nums[i])
globalmax = max(globalmax, maxvalue)
return globalmax
Leetcode 153
思路:對於沒有重複數字的數組, 旋轉之後的數組可以看做兩個升序數組,而且前面數組的元素都比後面數組元素大。可以用二分查找
發現:
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
left, right = 0, len(nums)-1
while left < right:
mid = left + (right - left) // 2 # 地板除,捨去小數部分
if nums[mid] < nums[right]: # 移動右邊顯然是更安全的選擇
right = mid
else:
left = mid + 1
return nums[left]
Leetcode154
思路:
發現:
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = nums[0]
start, end = 0, len(nums) - 1
while start + 1 < end:
mid = ( start + end ) / 2
if nums[start] < nums[mid]:
start = mid
elif nums[start] > nums[mid]:
end = mid
else:
start += 1
ans = min(ans, nums[start])
return min(ans, nums[start], nums[end])
Leetcode 155
思路:簡單
發現:
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.l = []
def push(self, x):
"""
:type x: int
:rtype: void
"""
if x is None:
pass
else:
self.l.append(x)
def pop(self):
"""
:rtype: void
"""
if self.l is None:
return 'error'
else:
self.l.pop(-1)
def top(self):
"""
:rtype: int
"""
if self.l is None:
return 'error'
else:
return self.l[-1]
def getMin(self):
"""
:rtype: int
"""
if self.l is None:
return 'error'
else:
return min(self.l)
Leetcode 160
思路:
發現:集合裏也可以放數據結構如鏈表節點
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
A_set=set()
while headA:
A_set.add(headA)
headA = headA.next
while headB:
if headB in A_set:
return headB
headB = headB.next
return None
Leetcode 162
思路:easy,比較左右值即可,需要注意最後一個元素比倒數第二個大也算峯值
發現:
class Solution(object):
def findPeakElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(1, len(nums)-1):
if nums[i]> nums[i-1] and nums[i]>nums[i+1]:
return i
if nums[len(nums)-1] > nums[len(nums)-2]:
return len(nums)-1
return 0
Leetcode167
思路:用夾逼法和哈希表法都可以
發現:
class Solution(object):
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
left = 0
right = len(numbers) -1
while(left < right):
if(numbers[left] + numbers[right] == target):
return [left+1,right+1]
elif(numbers[left] + numbers[right] < target):
left += 1
else:
right -= 1
temp = dict()
result = [-1, -1]
for i in range(len(nums)):
if target-nums[i] in temp.keys():
result[1] = i
result[0] = temp.get(target - nums[i])
break
else:
temp[nums[i]] = i
return result
Leetcode 168
思路:相當於26進制
發現:
class Solution:
def convertToTitle(self, n):
"""
:type n: int
:rtype: str
"""
result=''
while n>0:
rest=(n-1)%26
result=chr(rest+65)+result
n=int((n-1)/26)
return result
Leetcode169
思路:排序後取中間那位即可
發現:
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return sorted(nums)[int(len(nums)/2)]
Leetcode 171
思路:26進制轉十進制
發現:
class Solution:
def titleToNumber(self, s):
"""
:type s: str
:rtype: int
"""
result=0
n=len(s)
for i in range(n):
result=result*26+ord(s[i])-64
return result
Leetcode172
思路:求其中2*5的個數,也就是5的個數,因爲每一個偶數都含2,只要有5肯定有2,使用n除以5即可
發現:
class Solution:
def trailingZeroes(self, n):
"""
:type n: int
:rtype: int
"""
r = 0
while n >= 5:
n = n // 5
r+=n
return r
Leetcode 189
思路:
發現:可以固定爲這種寫法了
k = k % len(nums)
nums[:k], nums[k:] = nums[len(nums)-k:], nums[:len(nums)-k]
Leetcode191
思路:bin(int) 轉化爲二進制數
發現:
class Solution(object):
def hammingWeight(self, n):
"""
:type n: int
:rtype: int
"""
return bin(n).count('1')
Leetcode 202
思路:按照“happy number”的定義,直接循環計算各位平方和,觀察是否收斂到1,若是則是 happy number。爲了判斷循環是否開始重複,要用一個字典(dict)或集合(set)來保存已經出現的數字,dict的效率更高
發現:可以對算法進行優化。比如利用10以內的happy number只有1和7,或者先求出100以內的所有happy number等。
超時算法:
num_dict = {}
while True:
num_dict[n] = True
sum = 0
while(n>0):
sum += (n%10)*(n%10)
n /= 10
if sum == 1:
return True
elif sum in num_dict:
return False
else:
n = sum
AC算法:
happySet = set([1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97])
while n>99:
n = sum([int(x) * int(x) for x in list(str(n))])
return n in happySet
Leetcode 203
思路:字面意思寫
發現:
if head == None:
return head
dummy = ListNode(0)
dummy.next = head
pre = dummy
while head:
if head.val == val:
pre.next = head.next
head = pre
pre = head
head = head.next
return dummy.next
Leetcode204
思路:開闢一個輔助數組,依次標記2−√n的所有倍數。最後遍歷該數組,計數素數。
發現:
if n is None or n <= 1:
return 0
tmp = [True] * n
tmp[0] = False
tmp[1] = False
i = 2
while i * i < n:
if tmp[i]:
j = i
while j * i < n:
tmp[i * j] = False
j += 1
i += 1
res = 0
for k in tmp:
if k:
res += 1
return res