1. 程式人生 > >Leetcode解題思路總結(Medium)

Leetcode解題思路總結(Medium)

338. Counting Bits

136. Single Number

思路:一個數組中,每個數字都出現了2次,只有一個數字出現了1次,要求找到那個數。我一開始想到的方法是用掃描陣列新增到set中,如果出現了2次就從set中erase掉,掃面完後set中唯一一個元素就是我們需要的返回值,寫完程式碼AC結果發現要84ms,超級慢。然後我在搜了搜,發現有種20ms就AC的方法,那就是利用異或操作^,a^a=0, a^0=a,根據交換律a^b=b^a,比如3^4^3=4,根據異或操作的這種性質,我們可以將所有元素依次做異或操作,相同元素異或為0,最終剩下的元素就為Single Number。時間複雜度O(n),空間複雜度O(1)。

程式碼見我

347. Top K Frequent Elements

思路:找出一個數組中出現次數最多的K個數。可以考慮用priority_queue,假如元素是<int, int>這種pair形式,那麼優先佇列就是按著key的降序進行的,所以隊首一定是key最大的元素。這樣從優先佇列中pop出前K個元素,就可以得到結果了。不過問題是怎樣把陣列中的元素加到優先佇列中。所以我們需要對陣列做一次預處理,即統計每個數字出現的次數。可以用map,但是我選擇用unordered_map,因為unordered_map是無序的,map是有序的,所以插入元素時unordered_map會快很多。最後程式碼執行

36ms 如果用map的話,那就是60ms了。

260. Single Number III

思路:前面做了只有一個出現一次的數,其他都是出現兩次,思路和那題一樣。只是這個先將所有數一起異或,然後得到的結果將是兩個只出現一次數的異或。也就是說最後得到的數字是a ^ b,而我們需要的是a和b而不是a和b的異或。那如何將a和b從(a^b)中分離呢?比如a是5(101),b是3(011),那麼(a^b)就是6(110),觀察一下6,6的第二位為1說明a和b在第二位肯定不同,然後就可以根據與這個位的數是否為0將陣列分為兩半,並且a和b肯定會被分別分開到這兩個陣列中。這樣問題就回到了上題的思路,只要讓子陣列內元素全部異或一下,得到的兩個數就是最終要的只出現一次的數。這段話可能有點繞,需要好好琢磨琢磨一下。判斷1在哪一位就用0x01迴圈的去做&運算就好。程式碼只需要

16ms

238. Product of Array Except Self

思路:解法比較巧妙。由於output[i] = (x0 * x1 * ... * xi-1) * (xi+1 * .... * xn-1)。因此執行兩趟迴圈:
第一趟正向遍歷陣列,計算x0 ~ xi-1的乘積
第二趟反向遍歷陣列,計算xi+1 ~ xn-1的乘積

執行時間64ms

122. Best Time to Buy and Sell Stock II

思路:解法比較巧妙。題目說明可以多次買賣,但是同一時間只能有一股在手裡。 這樣就可以在每次上升子序列之前買入,在上升子序列結束的時候賣出。相當於能夠獲得所有的上升子序列的收益。 而且,對於一個上升子序列,比如:5,1,2,3,4,0 中的1,2,3,4序列來說,

對於兩種操作方案: 

一,在1買入,4賣出; 

二,在1買入,2賣出同時買入,3賣出同時買入,4賣出; 

這兩種操作下,收益是一樣的。 所以演算法就是:從頭到尾掃描prices,如果i比i-1大,那麼price[i] – price[i-1]就可以計入最後的收益中。這樣掃描一次O(n)就可以獲得最大收益了。

執行時間為8ms

319. Bulb Switcher

思路:這是一道數學題。大概解釋一下,當一個燈泡被執行偶數次switch操作時它是關著的,當被執行奇數次switch操作時它是開著的,那麼這題就是要找出哪些編號的燈泡會被執行奇數次操作。將燈泡編號為1到n,第i個燈泡會在第d輪被switch,當且僅當d能整除i(i % d == 0)。所以第i個燈泡只有當switch了奇數次後,它才會亮著。

而一個數的質因數是成對存在的。比如12,它能被1、12、2、6、3、4整除,並且12被switch了偶數次,所以最後還是滅著的。所以問題就轉變成了求一個數的質因子數有多少個。再來看下36,36有質因數1、36、2、18、3、12、4、9.還有2個6.所以它的distinct的質因數是奇數個,它最後會亮著。

可以發現規律,只有當i它是一個平方數的時候,第i個燈泡最後會亮。所以問題就變成了求1到n範圍內的平方數有多少個。

所以直接用sqrt(n),就可以求出1到n範圍內的平方數的數目了。這道題真是考數學和智商啊23333

343. Integer Break

思路:一個數,把它分為至少2個數的和的形式,然後求分解的各個數的乘積的最大值。比如9=3+3+3,9=4+5.但是3*3*3=27是大於4+5的,所以最後返回27.這道題也是一道數學找規律題。可以嘗試著手寫2到12之間的數字。然後發現的規律就是把每個數分解為3+3+···+2+···+2的形式是最優的。並且3的數目要儘量的多,只有當不能取夠3的時候,再去取2.程式碼可見:0ms

268. Missing Number

思路:求一個數組中少了哪個數,比如[0,1,3]裡面少了2,數組裡的數都是distinct的,陣列的長度如果是3,那麼就從0到3裡挑3個數存入陣列,然後讓你找少了哪個數。有2種方法可以AC。

第一種是數學方法:可以先求0到n的和,然後求陣列的sum,然後把他們一相減,得到的數字就是答案。執行時間32ms

第二種是bit位操作:利用異或的性質。把數組裡的每個數和下標以及陣列長度連環做異或。那麼得到的就是答案。執行時間36ms

比如0^1^3 ^0^1^2^3 = 2,2就是缺少的那個數,也就是答案。

144. Binary Tree Preorder Traversal

思路:二叉樹先根遍歷。有遞迴和非遞迴2種方式,遞迴很簡單:0ms

非遞迴就是利用一個stack,不過記得每次是先把右節點push進來,然後再push左節點。時間也是0ms

318. Maximum Product of Word Lengths

思路:在一個字串組成的陣列words中,找出max{Length(words[i]) * Length(words[j]) },其中words[i]和words[j]中沒有相同的字母。

方法就是先把每一個單詞編碼為26位的二進位制數,比如abcd就是00000000000000000000001111,abce就是00000000000000000000010111。然後,兩兩之間互相比較,如果位與運算為0,則說明兩者沒有共同字母,則可以進行乘法。然後在這兩層迴圈的過程中不斷更新最大值,迴圈結束後就得到返回值了。時間複雜度為O(n*n)。執行時間為128ms

94. Binary Tree Inorder Traversal

思路:二叉樹中序遍歷。有遞迴和非遞迴2種方式,遞迴很簡單:3ms

非遞迴就是利用一個stack,對於任一結點P,

1)若其左孩子不為空,則將P入棧並將P的左孩子置為當前的P,然後對當前結點P再進行相同的處理; 

2)若其左孩子為空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的P置為棧頂結點的右孩子; 

3)直到P為NULL並且棧為空則遍歷結束

時間0ms

12. Integer to Roman

思路:整數轉換為羅馬數字。分析羅馬數字的規律。就把羅馬數字和對應的整數存入陣列,然後逐個迴圈遍歷。

我們現在拿到一個數N 我們就去表裡面找不超過它的最大的數x, 然後把它入我們的輸出字串中,然後將數N-=x, 繼續執行這個操作,直到N=0。執行時間32ms

230. Kth Smallest Element in a BST

思路:求二叉查詢樹的第k個最小元素。注意到二叉查詢樹的中序遍歷結果是有序的,可以利用這個性質來輸出結果。用遞迴的方法做24ms

137. Single Number II

思路:一個數組中,除了一個數之外,其他全部元素都出現了3次,找出那個數。我用的很簡單暴力的方法,就是先直接全部存入unordered_map中(因為不用排序,比map要快),key是每個元素的值,value是每個元素出現的次數。然後再遍歷整個map,找出value不為3的元素,返回就是答案了,執行28ms  還有一種是位運算,但是我沒搞懂,希望有大神能給我講解一下

96. Unique Binary Search Trees

思路:一個典型的動態規劃題,後面的計算依賴於前面產生的結果。假如當前根節點是A,則這棵樹的二叉樹的數目為#(左子樹)×#(右子樹),而根據二叉查詢樹的規律,左子樹跟右子樹是互相沒有交集的,並且左子樹的任意元素的值都要小於右子樹的任意元素的值。以一個count陣列來表示共有i個節點時,能產生的BST樹的個數。

n == 0 時,count(0) = 1,

n == 1 時,count(1) = 1,因為只有1這個根節點,數量也為1。

n == 2 時,分2種情況考慮,一種是根為1,另一種是根為2.

n = 2; 1__ __2  

\ /  

count[1] count[1]  

count(2) = count(0) × count(1) + count(1) × count(0) = 2

n == 3 時,分3種情況考慮,根分別為1、2、3,

count(3) = count(0) × count(2) + count(1) × count(1) + count(2) × count(0) = 5

同時,當根節點元素為 1, 2, 3, 4, 5, ..., i, ..., n時,基於以下原則的BST樹具有唯一性: 以i為根節點時,其左子樹構成為[0,...,i-1],其右子樹構成為[i+1,...,n]構成 

因此,count[i] = sigma(count[0...k] × count[k+1...i]) 0 <= k < i - 1

所以只要找到規律,就能0ms通過

35. Search Insert Position

思路:二分查詢,但是需要變下型,因為其實這個問題是等價於找lower bound,所以每次就把left+1,9ms

108. Convert Sorted Array to Binary Search Tree

思路:把一個有序陣列轉換為平衡二叉搜尋樹。還是用二分查詢遞迴構造,每次選擇中點作為root,然後左子樹就遞迴的用左半邊陣列,右子樹就遞迴的用右半邊陣列。執行時間20ms

337. House Robber III

22. Generate Parentheses

思路:見CC150的9.6

78. Subsets

90. Subsets II

思路:同上

46. Permutations

思路:最簡單的是用C++的next_permutation函式去實現,記得先排序一下。12ms過。此外,自己實現的話,有遞迴和迭代兩種方式,細細觀察,可以從頭開始找規律。

47. Permutations II

思路:受到這個解法的啟發,以及這個部落格的4種生成全排列的演算法。這道題也可以用C++的next_permutation函式去AC過。我還是選擇了用全排列樹去AC,不過注意的是,如果2個需要交換的數相等的話或者要交換的數已經在set中,就不用交換了。40ms過的

74. Search a 2D Matrix

思路:二維數組裡進行二分查詢,記得用下標把二維轉換為一維,此外注意以下幾點,否則會造成死迴圈:記得在外層迴圈的判斷條件裡寫left <= right,如果寫的是left < right的話,就不會到達最右邊的值。然後記得每次二分後left = mid +1 和right = mid - 1。1ms