* Always remember to check the quality/availability of the input array/number/anything.
1. Two sum
The complexity of my solution is O(n^2). It's very simple and a little stupid.
There are two others' solution:
which complexity is O(n)
以上這兩種方法用的都是hash或dictionary。還有一種方法是夾逼法,先對(duì)數(shù)組進(jìn)行排序,然后用兩個(gè)pointer分別指向數(shù)組的第一個(gè)和最后一個(gè)數(shù)字,檢查這兩個(gè)數(shù)字相加是否等于target,若等于,則返回這兩個(gè)數(shù)字。否則就比較這兩個(gè)數(shù)字相加后和target的大小,若大于target,便把right pointer向左移一個(gè)數(shù)字(right --), 若比target小,則 left++。但是這種方法最終返回的是數(shù)字,不是index。若需要返回index,則要另外構(gòu)造一個(gè)數(shù)字結(jié)構(gòu),存儲(chǔ)最初的數(shù)字和index,再依次進(jìn)行查找。
66. Plus one
Use a variable "carry" to denote whether to carry on this digit. It's very skillful, and need to be often reviewed.
88. Merged sorted array
num1 is long enough to contain both num1 and num2. So compare element in num1 and in num2 one by one, put the biggest in num1[m+n-1]. If the num1 is empty, just copy num2 into num1.
118. Pascal's Triangle
The main principle of Pascal Triangle is:
通過觀察楊輝三角可知,下一行的每一個(gè)元素都依次由本行中每?jī)蓚€(gè)相鄰元素之和得到,這個(gè)方法可以用一個(gè)技巧實(shí)現(xiàn),即:將本行l(wèi)ist拷貝出兩個(gè)副本,將兩個(gè)副本錯(cuò)1位,然后加在一起。由于錯(cuò)位后,前后各多了一個(gè)元素,所以要在錯(cuò)位后的兩個(gè)list的前后各加一個(gè)[0]來補(bǔ)齊(其實(shí),這個(gè)0是理所當(dāng)然的,是楊輝三角的一部分)。
同一行中前后相鄰兩個(gè)元素相加(這是楊輝三角的構(gòu)成規(guī)則),就相當(dāng)于兩個(gè)本行元素錯(cuò)位相加。而zip方法,就是從這兩行中分別取出第i個(gè)位置的元素組成元組(這也是添“0”的原因)。sum()函數(shù)正好求出它們的和,進(jìn)而求出了下一行。然后又yield函數(shù)把這一行“塞入”generator--也就是本例中的triangles()。
217. Contains Duplicate
Given an unsorted array, most times it's better to sort it first, then do what you want. In this question, if I didn't sort it first, I need to compare the 'temp' variable to every element in the array, which takes O(n^2). But after sorted the array, we only need to compare 'nums[i]' with 'nums[i-1]' which obviously takes O(n).
219. Contains Duplicates 2
模仿大牛的代碼還是挺有用的。
1. set(): 去除重復(fù)對(duì)象
2. zip的用法:zip可被用來計(jì)算相鄰兩元素兩兩相減---m=[j-i for i, j in zip(p[:-1], p[1:])]
3. enumerate()
4. 切片
切片操作符中的第一個(gè)數(shù)(冒號(hào)之前)表示切片開始的位置,第二個(gè)數(shù)(冒號(hào)之后)表示切片到哪里結(jié)束,第三個(gè)數(shù)(冒號(hào)之后)表示切片間隔數(shù)。如果不指定第一個(gè)數(shù),Python就從序列首開始。如果沒有指定第二個(gè)數(shù),則Python會(huì)停止在序列尾。注意,返回的序列從開始位置開始,剛好在結(jié)束位置之前結(jié)束。即開始位置是包含在序列切片中的,而結(jié)束位置被排斥在切片外。
這樣,shoplist[1:3]返回從位置1開始,包括位置2,但是停止在位置3的一個(gè)序列切片,因此返回一個(gè)含有兩個(gè)項(xiàng)目的切片。類似地,shoplist[:]返回整個(gè)序列的拷貝。shoplist[::3]返回位置3,位置6,位置9…的序列切片。
你可以用負(fù)數(shù)做切片。負(fù)數(shù)用在從序列尾開始計(jì)算的位置。例如,shoplist[:-1]會(huì)返回除了最后一個(gè)項(xiàng)目外包含所有項(xiàng)目的序列切片,shoplist[::-1]會(huì)返回倒序序列切片。
121. Best time to buy and sell stock 動(dòng)態(tài)規(guī)劃
this question can be seen as dynamic programming. ? Using one local max profit and one global max profit to find the best time to buy and sell stock. ?Compare the local max profit with 0, cause sometime the stock profit may be negative. then compare the local max profit with the global max profit, which aims to update the global max profit.
local = 0; global = 0;
local = max(local+ prices[i+1]-prices[i], 0)
global = max(local, global)
* do not forget to add the former local max profit
268. Missing Number
To find the element in list1 but not in list2:
list3 = list(set(list1) - set(list2))
list3 = [i for i in list1 if i not in list2]
list3 = list(set(list1).difference(set(list2)))
54. Spiral Matrix
When traverse left and up, we need to add the additional judgement condition, "if rowbegin <= rowend" and "if colbegin <= colend". Aims to avoid traverse back when there is only one row or one column in matrix.
55. Jump Game. 53 Maximum subarray動(dòng)態(tài)規(guī)劃
396. Rotate Function
變換公式,找出數(shù)學(xué)規(guī)律
238. Product of Array Except Self
Use a middle variable to compute the product.
First, initialise the list as: 1, n_0, n_0*n_1, n_0*n_1*n_2. Then start from the end of the list, times the middle variable P. ?P start from 1, to n_3, n_3*n_2, n_3*n_2*n_1
15. 3 Sum
在三個(gè)數(shù)中,固定一個(gè)數(shù)nums[i] ,將第二個(gè)數(shù)和第三個(gè)數(shù)分別從i+1和最后一個(gè)數(shù)開始。判斷三個(gè)數(shù)相加是否為0,若為0,則將這三個(gè)數(shù)添加到結(jié)果中,然后向右移動(dòng)left(需要保證left < right),若移動(dòng)后的數(shù)字和移動(dòng)前的數(shù)字一樣,則繼續(xù)移動(dòng)。 若三個(gè)數(shù)相加大于0, 則向左移動(dòng)right(需要保證left < right),若移動(dòng)后的數(shù)字和移動(dòng)前的數(shù)字一樣,則繼續(xù)移動(dòng)。若三個(gè)數(shù)相加小于0,繼續(xù)上邊的操作。
記住每次更換固定的數(shù)字時(shí),都要重新初始化left和right。并且要保證left < right。
時(shí)間復(fù)雜度為n^2
39. Combination Sum
運(yùn)用Backtracking和DFS的思想,因?yàn)橄嗉拥扔趖arget的組合有多個(gè),所以我們維護(hù)一個(gè)path list,每次向其中添加來自candidates中的一個(gè)元素,然后將target減去這個(gè)元素得到新的target,繼續(xù)調(diào)用backtracking函數(shù),此時(shí)傳進(jìn)去的參數(shù)是新的path和新的target。在每次backtracking執(zhí)行過程中,要注意檢查以下幾點(diǎn):
1. 如果target<0: 則返回。 因?yàn)閎acktracking 函數(shù)沒有返回值,所以返回的是上一次調(diào)用這個(gè)函數(shù)之前的地方。即for循環(huán)那里,此時(shí)for循環(huán)向后移動(dòng),程序繼續(xù)
2.如果target=0:則說明此時(shí)的path便是我們想要的結(jié)果之一,便將path append到result中,然后返回。
3.如果target>0:便開始for循環(huán),調(diào)用backtracking函數(shù),此時(shí)的調(diào)用應(yīng)該注意更新參數(shù)(path, target)。 ** 在調(diào)用函數(shù)之前,通過增加判斷 if target < c(break)來節(jié)省時(shí)間。并通過 if path and target<path[-1] ?(continue) 來去掉結(jié)果中的重復(fù)組合。這個(gè)判斷使得結(jié)果中的組合都是有序的,因此就去掉了重復(fù)項(xiàng)。
40. Combination Sum II
這道題跟第39題的區(qū)別就是,這道題不允許重復(fù)使用數(shù)字,比如candidates中如果有兩個(gè)1,那結(jié)果集中的每個(gè)path,也最多只能有兩個(gè)1。代碼需要改變的地方就是for循環(huán)中的判斷條件。上一題中,由于允許出現(xiàn)重復(fù)數(shù)字,因此可能會(huì)出現(xiàn)(2,2,3),(2,3,2),(3,2,2)這種情況,因此需要對(duì)每個(gè)path進(jìn)行排序,來消除重復(fù)的path。這一題中,由于每個(gè)數(shù)字只允許使用一次,所以 1.每次調(diào)用backtracking函數(shù),是從i+1開始的:
self.backtracking(path + [self.cand[i]], i+1, target - self.cand[i])
2. 不需要對(duì)path進(jìn)行排序,但是要跳過candidates中的重復(fù)項(xiàng),避免得到重復(fù)的path。
if i > index and self.cand[i] == self.cand[i-1]: (continue)
**注意這里是 i>index 而不是 i>0,因?yàn)閒or循環(huán)的起始點(diǎn)是 index, index每次都會(huì)進(jìn)行更新。
94. Binary Tree Inorder Traversal
二叉樹的中序遍歷,可背
中序 inorder:左節(jié)點(diǎn)->根節(jié)點(diǎn)->右節(jié)點(diǎn)
前序 pre-order:根節(jié)點(diǎn)-左節(jié)點(diǎn)-右節(jié)點(diǎn)
后序 post-order: 左節(jié)點(diǎn)-右節(jié)點(diǎn)-根節(jié)點(diǎn)
注意:因?yàn)槊看蝡op出來的是最后一個(gè)元素,所以push時(shí)應(yīng)該按相反的順序來push
inorder:?
nodelist.append((node.right, False))
nodelist.append((node, True))
nodelist.append((node.left, False))
pre-order:
nodelist.append((node.right, False))
nodelist.append((node.left, False))
nodelist.append((node, True))
post-order:
nodelist.append((node, True))
nodelist.append((node.right, False))
nodelist.append((node.left, False))
206. Reverse Linked List
維護(hù)三個(gè)pointer: ?pre, head, next。 pre永遠(yuǎn)是第一個(gè),初始值應(yīng)為none,因?yàn)榈谝粋€(gè)node反轉(zhuǎn)后指向的為空。head.next指向pre,pre后移到head, head后移到next. ?next指向的永遠(yuǎn)是head.next。
92. Reverse Linked List II
先找到m之前的一個(gè)node,標(biāo)記為mpre,然后m的位置就是mp = mpre.next. ?使用一個(gè)計(jì)數(shù)器從0開始,花費(fèi) m-1>count 步驟找到mpre。 此時(shí)count已經(jīng)有一定的值,再此count基礎(chǔ)上,再花費(fèi)n-1>count步驟進(jìn)行reverse。reverse的三個(gè)變量分別是: mpre, mp, temp(next). ?temp為mp的下一個(gè)node, 讓mp.next指向temp.next, 再將temp指向mp, 即temp.next = mpre.next, ?最后將mpre指向temp, 即mpre.next = temp.
61. Rotate List
given 1-2-3-4-5, k=2 ? ?return: 4-5-1-2-3
尋找尾節(jié)點(diǎn)的同時(shí)計(jì)算鏈表長(zhǎng)度。
記鏈表長(zhǎng)度為n,則實(shí)際移位次數(shù)為k=k%n。記len=n-k。
也就是說鏈表的后k個(gè)節(jié)點(diǎn)成為新鏈表的前半部分,鏈表的前l(fā)en個(gè)節(jié)點(diǎn)為新鏈表的后半部分。
因此head往右第len的節(jié)點(diǎn)為新鏈表的尾,第len+1為新鏈表的頭
兩端相連即可,尾部下一個(gè)設(shè)為NULL即可。
注意: 當(dāng) k % len == 0 時(shí),直接返回原list
(a+b)>>1 == (a+b)/2
147. Insertion Sort List
針對(duì)linkedlist的插入排序, 先生成虛擬節(jié)點(diǎn)指向head。將head.next 設(shè)為空,以head作為最初的sorted list, 逐個(gè)加入后邊list中的每個(gè)元素。因?yàn)椴迦肱判蚴菑暮笙蚯皰呙璧模杂肋h(yuǎn)維護(hù)sorted list中的最后一個(gè)元素作為pre, ? unsorted list中的第一個(gè)元素作為cur, 標(biāo)記cur.next為next, 當(dāng)當(dāng)前cur被加入到sorted list中時(shí),next 變成為新的cur。
每次比較pre和cur的值,如果pre的值小于等于cur的值,則直接將cur鏈接在pre之后即可。如果pre大于cur的值,則需要另一個(gè)變量prepre, ?使prepre從dummy開始,判斷prepre.next的值與cur值的大小關(guān)系,當(dāng)prepre.next.val>cur.val時(shí)便停止。這時(shí)prepre所處的位置的下一個(gè)元素應(yīng)指向cur。但在指向cur之前,應(yīng)先標(biāo)記prepre.next為prnext,因?yàn)椴迦隿ur之后,要將cur.next指向之前的prepre.next。
86. Partition List
昨天想了半天,今天看了一下discuss, 原來只要把original list拆成兩個(gè)list, 第一個(gè)list全是比x小的node, 第二個(gè)list全是大于等于x的node。再將第一個(gè)list指向第二個(gè)list,不要忘了將第二個(gè)list的最后一個(gè)node的next指向none。
148. Sort List
1. merge and sort。 ?
while fast and fast.next: ? slow = head, ? ? fast = head.next
循環(huán)停止時(shí),slow正好停在上半段最后一個(gè)元素,fast停在下半段最后一個(gè)元素。
half2 = slow.next 作為下半段的開頭。迭代對(duì)兩個(gè)list進(jìn)行sort,直到最后每一個(gè)node構(gòu)成一個(gè)list,再對(duì)所有的list進(jìn)行排序。
需要注意的就是,while停止時(shí),要將slow.next先賦給half2, 然后將它指向空。fast是從head.next開始的,不是從head開始。
當(dāng)fast從head開始時(shí),需要另一個(gè)指針pre,指向前半段的最后一個(gè)元素。while中加上pre = slow。 while停止時(shí),pre.next = none。 此時(shí)slow指向后半段第一個(gè)元素。
binary search
二分查找中mid=(l+r)/2 和 mid=l+(r-l)/2 是一樣的,二分查找復(fù)雜度為log(n)
34. Search for a Range
這題需要注意的問題不多,主要是更新l 和r時(shí)不能單純的將 l=mid, r=mid, 而應(yīng)該是l = mid+1, r=mid-1。 循環(huán)判斷條件也應(yīng)該是l<=r。 具體什么時(shí)候可以直接用mid,什么時(shí)候要mid+-1,我還沒有完全搞清楚
35. Search Insert Position
這個(gè)要注意的也不多, 找不到元素時(shí)返回r+1就好了
69. Sqrt(x)
用二分查找找出mid, 如果x介于mid*mid和(mid+1)*(mid+1)之間,那mid就是sqrt(x)。所以判斷條件就是:mid*mid <= x and x < (mid+1)*(mid+1) ?否則的話就相應(yīng)的更新r或者l
74. Search a 2D Matrix
這題的重點(diǎn)在于,可以把這個(gè)每行都有序,且行與行之間也都有序的matrix看為一個(gè)一維數(shù)組來處理,l=0, r=m*n-1.? mid = (l+r)/2。知道了總的index,怎樣確定它在哪一行哪一列呢?
num = matrix[mid/n][mid % n]。 行:mid/n, ?列:mid%n。 n 為列數(shù)。
240. Search a 2D Matrix II
與上一題不同的地方在于,這個(gè)矩陣不僅每行升序排列,每列也都升序排列。idea就在于:
這里更新的不是l和r, 而是row 和 col。判斷條件也變?yōu)?while row < m and col >= 0。
不再是二分查找,時(shí)間復(fù)雜度為o(n), 因?yàn)槊看味己妥钣疑戏降脑乇容^,一共有n個(gè)最右上方的元素,最多比較n次。
29. Divide Two Integers
for example, if we want to calc (17/2)
ret = 0;
17-2 ,ret+=1; left=15
15-4 ,ret+=2; left=11
11-8 ,ret+=4; left=3
3-2 ,ret+=1; left=1
ret=8;
程序如下:
正負(fù)號(hào)和數(shù)字越界這里處理的比較巧妙。
50. Pow(x, n)
有5種不同解法,至少應(yīng)該理解兩種,包括binary search的
https://discuss.leetcode.com/topic/21837/5-different-choices-when-talk-with-interviewers
81. Search in Rotated Sorted Array II
是33的follow up, 存在重復(fù)數(shù)字的情況,尚不明白
https://discuss.leetcode.com/topic/20593/python-easy-to-understand-solution-with-comments
209. Minimum Size Subarray Sum
https://discuss.leetcode.com/topic/13759/python-3-different-ac-solutions
不理解 binary search的
167. Two Sum II - Input array is sorted
15. 3Sum
16. 3Sum Closest
18. 4Sum
https://discuss.leetcode.com/topic/22705/python-140ms-beats-100-and-works-for-n-sum-n-2
python for N-sum
13. Roman to Integer
https://discuss.leetcode.com/topic/17110/my-straightforward-python-solution
http://blog.csdn.net/wzy_1988/article/details/17057929
43. Multiply Strings
150. Evaluate Reverse Polish Notation
1. 中綴轉(zhuǎn)換到reverse polish notation:(幫助理解解題思路)
? 第一步:按照運(yùn)算符的優(yōu)先級(jí)對(duì)所有的運(yùn)算單位加括號(hào)~
? 式子變成拉:((a+(b*c))-(d+e))
? 第二步:轉(zhuǎn)換中綴與后綴表達(dá)式
? 后綴:把運(yùn)算符號(hào)移動(dòng)到對(duì)應(yīng)的括號(hào)后面
? 則變成拉:((a(bc)*)+(de)+)-
? 把括號(hào)去掉:abc*+de+-后綴式子出現(xiàn)
2. ?需要注意的地方有:
a. 將數(shù)字append到stack中時(shí),應(yīng)該把數(shù)字轉(zhuǎn)換為int型
?b. ?注意計(jì)算順序。第一個(gè)pop出來的元素在運(yùn)算符右邊,第二個(gè)pop出來的元素在運(yùn)算符左邊!!!!
c. ?除法需注意: python中1/-22= -1,但leetcode要求 1/-22=0,所以除法運(yùn)算應(yīng)寫為:
stack.append(int(float(num2)/float(num1))) ? 或者
#stack.append(int(num2*1.0/num1))
d. 判斷運(yùn)算符時(shí)注意使用elif
139. Word Break ----- 動(dòng)態(tài)規(guī)劃
維護(hù)一個(gè)數(shù)組dp, dp[i]=true 意味著 s[:i+1]在worddict中, dp[0]應(yīng)該被初始為true?
dp初始化:dp = [False] * (len(s)+1) ? 注意長(zhǎng)度應(yīng)為s的長(zhǎng)度再加1
雙層for循環(huán),每次的判斷條件為dp[i]==true and s[i:j+1] in worddict, then dp[j+1]== true
最后返回結(jié)果為dp的最后一個(gè)元素dp[-1]
136.Single Number
利用每個(gè)元素出現(xiàn)兩次,以及位操作異或的性質(zhì)來解決這個(gè)問題。因?yàn)閮蓚€(gè)相同的元素異或結(jié)果是0,利用這個(gè)特點(diǎn)我們可以對(duì)所有數(shù)組元素進(jìn)行異或,如果出現(xiàn)兩次的元素就會(huì)自行抵消,而最終剩下的元素則是出現(xiàn)一次的元素。這個(gè)方法只需要一次掃描,即O(n)的時(shí)間復(fù)雜度,而空間上也不需要任何額外變量
137. Single Number II
I like to think about the number in 32 bits and just count how many 1s are there in each bit, and "sum %= 3" will clear it once it reaches 3. After running for all the numbers for each bit, if we have a 1, then that 1 belongs to the single number, we can simply move it back to its spot by doing "ans |= sum << i" ;
This has complexity of O(32n), which is essentially O(n) and very easy to think and implement. Plus, you get a general solution for any times of occurrence. Say all the numbers have 5 times, just do "sum %= 5".
** 當(dāng)把剩余的一個(gè)1 還原為十進(jìn)制整數(shù)時(shí),在python中需要注意負(fù)號(hào)的情況。
if i == 31: ? ?res -= 1<<31
For a 32 bit number, if i == 31, we are on the bit telling us whether the number is negative or not. if we are on the bit that tells us the sign, and the number that appears not three times has a 1 here", then take the largest 32 bit integer and subtract it from our res. If the number that we are looking for is negative, this will give the correct answer.
134. Gas Station
思路沒有問題,就是一直疊加balance += gas[i] - cost[i]。 需要注意的是,當(dāng)balance小于0時(shí),要對(duì)balance 和 start 進(jìn)行更新:balance = 0; ?start = i + 1。意思就是從下一個(gè)station繼續(xù)出發(fā)。這是題目沒有說清楚的地方,即油不夠時(shí),可以挪到下一個(gè)station重新開始。。
133. Clone Graph------圖問題
BFS用queue, DFS用stack。 用queue時(shí)是popleft(),從左邊取第一個(gè)。用stack是pop(),取最后一個(gè)。
125. Valid Palindrome
**注意python中的字符串可以直接以數(shù)組的形式訪問,無需再改寫成數(shù)組。
** s[l].isalnum() 檢測(cè)字符串/字符 是否由字母或數(shù)字組成。not s[l].isalnum() 意思就是不是字母或數(shù)字
** 當(dāng)字符串為空時(shí),也算回文序列
129. Sum Root to Leaf Numbers
1. 注意邊緣情況:當(dāng)樹為空時(shí),返回 0!! 跟257題思路相同,但邊緣情況返回值不同
120. Triangle---動(dòng)態(tài)規(guī)劃
給定一個(gè)三角形,輸出最短路徑和。每一個(gè)元素只能向下一層與它相鄰的兩個(gè)元素移動(dòng)。Each step you may move to adjacent numbers on the row below.
這道題比較適合從下往上遍歷,可以不用處理頭尾特殊情況。
先初始化一個(gè)數(shù)組用來存放當(dāng)前行和下一行相鄰元素相加的最小和,注意?? 數(shù)組大小應(yīng)該是triangle最后一行大小再加1. ?加1是因?yàn)橐紤]最后一行的最后一個(gè)數(shù)組,參考動(dòng)態(tài)規(guī)劃的遞歸式來理解:
rowmin[i] = row[i] + min(rowmin[i], rowmin[i+1])
這個(gè)遞歸式的含義是,對(duì)每一行的每一個(gè)元素,保存這個(gè)元素與下一行相鄰兩個(gè)元素中最小的那個(gè)的和。rowmin要初始化為0.
雙層循環(huán)結(jié)束后,rowmin的第一個(gè)元素就是minimum path,因?yàn)榈谝粚又挥幸粋€(gè)元素。
時(shí)間復(fù)雜度為:把每個(gè)元素遍歷一次,需要 n^2
空間復(fù)雜度為:O(n)
116. Populating Next Right Pointers in Each Node
又是樹的題目。理解題意時(shí)有很重要的一點(diǎn)就是,因?yàn)橐呀?jīng)初始化了所有的node的next指針為null,所以每一行最后邊那個(gè)node是不用管的,它的next已經(jīng)指向了null。
使用一個(gè)pre指針,讓它總是指向root
while循環(huán)的判斷條件為while root.left,因?yàn)槲覀冊(cè)诋?dāng)前行就可以執(zhí)行完下一行的任務(wù),所以無需將root循環(huán)到最后一行。
if 循環(huán)的判斷語句為if root.next,意思是檢查當(dāng)前root的next是指向空,還是已經(jīng)指向了某個(gè)node,如果它不指向空,我們就root.right.next = root.next.left, 并把root.next作為新的root
如果它指向空,root = pre.left ; ?pre = root; ?我們就把pre.left作為新的root, 即向右移動(dòng)了一個(gè)node。