1. 程式人生 > >Leetcode OJ 刷題

Leetcode OJ 刷題

吐槽一下Leetcode上各種不定義標準的輸入輸出(不過面試時起碼可以問一下輸入輸出格式。。。),此篇文章不是具體的題解,是自己刷LeetCode的一個筆記吧,雖然沒有程式碼,但是稍微難一點的都會標出主要思路,便於以後複習

PS:題目中有“水題”兩字的都是一遍AC,沒有標註的都說明了問題,順序按照Leetcode上時間倒序排列,少量題目由於和之前的題目有相關性,因此將其放在一起,比如12題和13題,因此中間可能會“缺少”幾道題目,缺少的題目請自行ctrl + F

PSS:題目前加"*"都是有點意思的題目,根據*的多少說明題目的有(kun)趣(nan)程度

PSSS:雖然一直計劃刷Leetcode,但是各種懶得動筆,不過因為上週日(5.18號)聽了Facebook在北大的宣講會,聽後決定還是早準備好,於是便有此文。去年A了前面的150道,過了一年又多了70多道,於是決定花一週再A掉。

水題,排序,然後兩個邊界指標往中間查詢,一次AC。題目不難,但是由於是第一道,因此搞了半天才弄懂輸入輸出,而且在為sort過載輔助類的“<”時犯了錯誤,具體看過載運算子和友元函式,通過這個錯誤還認識到了CONST成員函式

(*) 2.Median of Two Sorted Arrays

類似於二分吧,研一暑假實習去面試阿里時遇到該題的簡單版本(兩個陣列大小相同),此題是在兩個有序陣列中找第k小的值的弱化版,不過寫的時候突然腦子短路了,本來只扔一個數組的一部分,我扔了兩個陣列(實習時的題目是兩個都扔),一直WA

3.Longest Substring Without Repeating Characters

水題,不過第一次提交時,我預設字元只有a~z,結果RE了一次。注意substring是子串:要求連續;而subsequence是子序列:不要求連續

4.Add Two Numbers 

主要是考指標操作。。因為沒給輸入輸出樣例,導致多種情況沒考慮到,本質上不難

第一反應是DP,結果TLE,然後去網上看了下有專門求最長迴文字串的中心法,雖然兩個理論上都是O(n^2)的演算法,但是DP由於不能剪枝導致一定是嚴格O(n^2)的,而中心法則是隻有O(nm)的時間,m為最長迴文字串的長度,而m在大多數情況都很小。

PS:後來又對DP進行了修改,使其變成了O(0.5 *n^2),AC,提醒了我在列狀態方程時,要多想是否能簡化一些狀態,比如將dp[start][end]改為dp[start][len].

最長迴文子串還有一個O(n)的演算法,即Manacher演算法 ,這個演算法有點類似DP的思想,利用之前找到的迴文串,擴大初始的搜尋範圍。

6.ZigZag Conversion 

水題,估計是考邊界條件吧

將整數翻轉輸出。其實題目要考慮的地方很多,例如:

  1. 正負號
  2. 翻轉後的前導0,以及恰好是0
  3. 翻轉後資料溢位

但是由於此題有提示故很容易過,如果是java的話可以直接用Long.parseLong(string),如果是C++可以用stringstream庫檔案,這是一個C++中專門用來對string和其他型別互相轉換的庫函式,一次AC

8.String to Integer (atoi) 

和上一道題類似,注意前導空白符號,可以用上一題的stringstream,也可以自己寫,由於編譯器不同CE了一次,然後改了下AC

9.Palindrome Number

判斷迴文數,和上面Reverse Integer類似,不過他要求負數不能是迴文數,WA了一次

10.Regular Expression Matching

匹配正則表示式。。暴力法,不過要考慮比較多情況,WA了2次 

(*)11. Container With Most Water

水題,一開始我以為是一道和棧有關的題(第84題),然後發現比那道題簡單,開始用了先排序處理再找左右邊界的演算法,該演算法AC後在Discuss裡面發現還有O(n)的演算法,稍微思考後寫的新演算法也AC了,而且O(n)演算法更看清本質。

水題,當做一個常識來做吧,沒啥技巧。

水題,注意輸入為空的情況

15.3Sum 

水題,題目不難,排序後利用2Sum,關鍵點是如何不重複,需要一點小預處理,定義next[i] = j表示: min{ j >i && num[i] != num[j] } ,注意next求法是從大往小求的,O(n)就可以求完,類似可以比較KMP演算法中的預處理

水題,和上題類似,這道題是找和目標最近的一個,因此關鍵點是"在算出三個整數之和後,如果不和目標相等該怎麼移動指標去找下一組",trick是找兩遍:第一遍找不小於target的三元和,第二遍找小於target的三元和,問題迎刃而解。後來在Discuss發現,其實直接套用2Sum移動指標就可以,簡單證明確實如此。。。不用這麼麻煩

(*) 17,4Sum

水題,2(3)Sum的拓展,排序後列舉前兩個,然後利用2Sum的的左右指標查詢,類似的注意要求求得的陣列不重複。PS:4Sum是O(n^3)的複雜度,而3Sum只有O(n^2),但兩個在OJ上跑的時間基本相同,可能資料量不一樣

沒啥意思。。不過WA了一次。。因為沒看清題目= =導致每個數字對應的字母有問題。。。

19.Remove Nth Node From End of List和60Rotate List

水題,但也是經典題目,兩個指標,一個先從頭走n步之後另一個才開始從頭走,當第一個走到末尾時,後一個指標對應的節點就是要刪除的節點,注意考慮邊界情況。

20.Valid Parentheses 

水題,同樣經典題目,用棧即可解決。

21.Generate Parentheses

水題,上一道題的對偶題

水題,經典題目,K路歸併的連結串列形式,可以用分治法做,也可以用堆,此外注意使用指標的邊界情況

水題,主要考如何交換相鄰兩個節點

水題,題目不難,但是連結串列題。。。特別噁心= =考慮了很久,雖然一次AC了,但是花費時間很多,要是面試遇到肯定掛了

水題

KMP演算法,水題

題目型別很經典,水題,但是一些邊界情況想了一點時間:用位運算實現一些特殊計算,本題是實現除法,要注意涉及到INT_MAX和INT_MIN的特殊情況,相關文章可見位運算之美

一上來被唬住了。。。以為有很複雜的演算法,其實就是暴力法= =。。。不過RE了一次,因為忘了STL.size()返回的是unsigned int,結果導致做減法後,出現了4294967295(0xffffffff)

此題分析起來還是很有意思的,水題,對於一個排列A,從後往前看A的每個數,則得到的字尾一定屬於下面兩種情況:

  1. 先升序(可相等),然後在嚴格降序,我們記為xab...d,其中x < a,但a >=b >= ... >=d,此時在a~d中尋找大於x的最小值(一定存在,因為a>x),記為c,則將xab...d換為cB,其中B是由x,a...d中除c以外的數字組成的最小數,A的其餘部分不變,該排列即為解。
  2. 一直升序(可相等),則這個排列是最大排列,只需要將其翻轉得到最小排列輸出即可。

上來先寫了個O(n^2)的演算法,結果TLE(有個輸入1s才跑完),然後又去琢磨O(n)的演算法,結果想了半小時沒找到,然後就回寢室了。。。回去的路上考慮對原來演算法剪枝,發現了一個剪枝技巧:我們查詢最長串的本質是在已經找到一個合法串A的基礎上,在末尾繼續新增字元a,會出現3種情況:

  1. Aa中'('個數等於')',此時Aa為合法串,更新答案長度,繼續搜尋
  2. Aa中'('個數小於')',Aa不合法,此時終止這次搜尋
  3. Aa中'('個數大於'),TLE的程式碼中沒有剪枝。演算法的低效原因就在這:比如以某個位置為出發點時,發現前面搜尋的幾乎全是'(',這樣導致我們會一直搜尋到末尾,此時才發現')'個數太少以至於不能形成合法串,浪費了大量時間。所以我添加了新的剪枝,定義neg[j]:代表以j開頭的字串(可以為空)中,')'個數減去'('個數的最大值(如果為負數,則置為0),這樣當我們搜尋字串S[i,j]時,利用neg[j]發現目前已經累計的'('的個數不足以從j開始的')'抵消,則終止這次搜尋。經過這個剪枝92ms AC
32Search in Rotated Sorted Array和81Search in Rotated Sorted Array II

二分的變種,WA了一次

二分法變種,水題

34Search Insert Position

還是二分法。。。遞迴比迭代麻煩多了,不過還是全部用迭代寫的

= =純模擬題,水題,沒啥意思

水題,純模擬,題目第一次居然沒讀懂

38.Combination Sum 和39Combination Sum II

水題。。。還是遞迴模擬就行了

此題第一反應是排序後比較,但是不滿足題目要求的線性時間。仔細分析後我們只是要求找第一個沒出現的正整數,不需要全部排序,因此一個思路是,我們將其“部分排序”,使得從i = 0開始時,A[i] = i + 1,當第一次不滿足這個要求時得到的i即為所求,所以此題的本質類似於置換。RE了兩次(其實是一次。。。第一次RE後,修改BUG的時候大腦短路本來想打>結果打了<)

水題

42Multiply Strings 

大整數乘法,因為大一寫過大整數乘法,實在不想再費勁了(真面試遇到這種題,我就糊面試官的熊臉)。。。所以隨便粘了份程式碼交上去了。。。

(*)43Wildcard Matching 

基本類似第10題,TLE後把遞迴改成非遞迴就過了

(*)44.Jump Game II和54Jump Game
第一反應是DP,TLE。。。然後發現dp[i]在這道題裡面是遞增的,因此加了一個剪枝就過了,這道題還是提醒我們DP後發現TLE,多去找剪枝,一般能DP的題都是能過的,只不過寫的太麻煩,類似的還有第5題和31題。
45Permutations和46.Permutations II
水題,注意求給定數字的全部排列有個經典的遞迴演算法
47.Rotate Image
找到規律後很容易,水題
48.Anagrams 
題目叫做如何使用STL。。。。用multimap即可解決,不過對multimap不熟,WA了幾次
48.Pow(x, n) 
求冪,分治法經典題目,水題,注意INT_MIN和INT_MAX
50.N-Queens和51.N-Queens II
N皇后問題。。。回溯法,好久沒寫非遞迴了(面試官很愛考遞迴轉偽遞迴)。。。所以花了一個小時去琢磨偽遞迴。。。發現差不多忘完了。。。以後再練練
52.Maximum Subarray
以為有什麼好演算法。。其實就是區間排序後合併就行了,水題
57Length of Last Word
沒啥技術含量,就是根據測試樣例調BUG
67.Plus One 
水題
68Text Justification
模擬題。。。主要是邊界條件處理。。。然後理解錯題意了。。。WA了很久
69Sqrt(x) 
本來想直接迴圈找,結果TLE,然後改成牛頓法過了。。。看來以後得找個時間看看《演算法心得》
70.Climbing Stairs 
用棧完美解決,由於C++標準裡面沒有string.split,有幾個方法可以替代:1.用strtok 2.用sstream 和 getline()
72.Edit Distance 
因為寫完沒跑樣例就交上去了,RE了一次。。發現打錯了一個符號,修改AC。雖然要求常數空間,但是題目不難
74Search a 2D Matrix
二分搜尋的矩陣版本,水題
75.Sort Colors
題目提示可以在O(2n)內用計數排序排完,但該演算法還有O(1*n)的演算法:可以用類似40題置換置換的思想,將陣列分成4部分: 下標在[0,i-1]內的數全為0,下標在[i,i+j-1]內的數全為1,在[k,n-1]內全為2,而剩下的在[i+j,k-1]內的數待排序。水題
要求O(n)時間,只能用動態規劃了,dp[i]表示以s[i]開頭的子串中能包含T的最小長度,然後就很容易做了。(由於是一維的DP且是O(n)的,所以DP本質上是雙指標法,類似可對比題1)
77.Combinations 
經典問題,可以和下一題以及11題比較一下。  解決演算法是:從左往右遍歷Rectangle,用棧儲存之前遍歷的長方形的編號,棧中元素滿足:任何一個元素代表的長方形比它下面元素代表的長方形高。在遍歷時,如果當前長方形比棧頂的長方形的高度低時,更新面積: 即以棧頂代表的長方形高度為所求長方形高度,以棧頂下面一個元素代表的長方形為右邊界(如果不存在則以最左邊為左邊界),以當前遍歷到的長方形之前的一個長方形為右邊界,計算所得到的面積。然後彈出棧頂元素,重複這步直到棧空或者棧長方形高度不小於當前長方形高度。 PS:有更簡單的處理方法,即棧裡面儲存用一個二元組,二元組儲存長方形高度和它對應的左邊界,這樣的話可以簡化程式碼
經典問題,最大子段和的2D版本,WA了一次
86.Partition List
水題。。
上來就用遞迴演算法,TLE了一次,然後考慮了半天沒有找到好的解法(原解法是指數級的)。。。然後回來考慮剪枝,添加了一個預處理的剪枝後(即比較兩個串所含字元是否完全相同),52ms AC。。。還是和以前的觀點,TLE了多考慮剪枝。
88.Merge Sorted Array
水題=。=合併有序陣列B到A。。。倒著排序即可,即從大往小插入到A裡面,O(1)空間,O(n)時間
89.Gray Code
格雷碼。。生成格雷碼有特定方法,按照該方法即可。。。WA了一次。。。原因是題目認為n = 0 時應該輸出0.。。。
90.Decode Ways
遞迴後TLE。。。然後改成DP,各種WA,其實這道題的本質是判斷哪個輸入合法= =。。。
91.Subsets II 
2種解法:
  1. 78題的升級版,即先生產後面的集合,然後在把前面的資料加進去,此方法耗費空間很大,但時間快,每次呼叫輔助函式都能在ans裡面加入多個結果。
  2. DFS,此方法簡單粗暴,程式碼容易,空間小(實際上和上一個方法空間是可比的,沒有數量級的差距),時間慢,每次呼叫dfs只能加入一個結果。
此外還有一個技巧是利用set把找到的解存起來,然後再最後轉成vector輸出,這樣可以不用任何多於處理保證找到的集合不重複,但是這樣對思考沒有太大幫助,不建議使用。注意一下for_each的多種用法
92.Reverse Linked List II
二叉樹三種遍歷方法,必須掌握的內容,遞迴版本就不說了,2分鐘內應該能寫出來;非遞迴版本中前序和中序也應該5分鐘內寫出來,而非遞迴的後序遍歷稍微需要點技巧,但是思考一下10分鐘完全OK。
95.Unique Binary Search Trees  和96Unique Binary Search Trees II
水題,96題WA了一次。。=。=因為輸入是0的時候居然要求輸出根節點為空的二叉樹。。。
97.Interleaving String
本來以為是水題。。。結果程式碼中忘了把根節點和全部兒子節點作比較(不能簡單的和左子樹的最小節點或者右子樹的最大節點直接比較,因為輸入的子樹可能是非法的)。。。
99.Recover Binary Search Tree
注意交換節點有3種情況:1.某個節點的左後代和右後代交換 2.某個節點和左後代交換 3.某個節點和右後代交換 。 WA了兩次,第一次是隻考慮了第一種情況,第二次是忘了可以和任意後代交換,所以遍歷子樹時要完整。
100.Same Tree
水題。。。二叉樹如果可以用遞迴,很多題都可以秒破
101.Same Tree
水題,前兩題要求用陣列返回寬搜,每個陣列代表一層,那麼將每層按層數的奇偶區分,用兩個佇列即可分別儲存奇數層和偶數層即可。第三題特別SB=。=,只要把第一題的結果翻轉一遍輸出就行了
104.Maximum Depth of Binary Tree
兩道水題,理由同100
根據中序遍歷和前序(後序)遍歷還原二叉樹(已知無重複元素,如果有二叉樹不唯一)。。。基礎內容,不能有誤,要求一次AC,水題
108.Convert Sorted Array to Binary Search Tree 和 
109.Convert Sorted List to Binary Search Tree
水題,一開始沒反應過來Height Balanced BST是啥。。。孤溝了一下。。
110.Balanced Binary Tree
水題,理由同100,不過這幾道題都要求是父節點到葉子節點,因此要對遞迴稍微加點限制
112.Path Sum 和 113.Path Sum I
水題,理由同100
114.Flatten Binary Tree to Linked List
水題,注意在遞迴時,需要同時記錄生成連結串列的頭和尾
115.Distinct Subsequences
水題,同72的DP
116.Populating Next Right Pointers in Each Node
水的令人髮指,因為沒有規定輸入0時輸出什麼,所以兩道題各WA一次
120.Triangle
水題,經典DP題
121.Best Time to Buy and Sell Stock
122.Best Time to Buy and Sell Stock II 和
第一題很水,第二題DP可以AC但是有更好的演算法(弄懂問題的本質是求所有上升子段的高度和),第三題也很簡單,先DP,然後以i為分界點,分別算左右兩邊最大利潤的和即可,但是關鍵是如果第三題擴充套件要求最多買k次有沒有好的演算法
124Binary Tree Maximum Path Sum
弄懂本質是後序遍歷即可,WA了一次,提醒我以後設定最小值可能是INT_MIN,而不一定是0(根據問題解是否非負而定)
125.Valid Palindrome
水題
126.Word Ladder
127.Word Ladder II
一看題目就知道本質上是圖的最短路徑問題。。。然後覺得寫起來太麻煩,面試時真遇到很有可能20分鐘寫不出來。。。覺得沒太大意義,隨便貼了別人程式碼交上去了,以後有興趣再回來寫吧
128.Longest Consecutive Sequence
這道題想了很久沒想到好的方法,然後看了提示大家都是用HashSet過的。。。覺得沒啥意思。。。
129.Sum Root to Leaf Numbers
水題
130.Surrounded Regions
水題,寬搜
131.Palindrome Partitioning
132.Palindrome Partitioning II
水題
133.Clone Graph
題目沒啥意思,隨便貼份程式碼上去·
134.Gas Station
水題,本來還以為有多高深的演算法。。簡單剪枝的純模擬。。。一直TLE以為演算法錯了。。。發現題目雖然說保證解唯一但是沒有說明一定有解
135.Candy
是任何一個小孩都要比他旁邊小的人多一個糖果,所以從左往右,然後再從右往左各掃面一遍就可以了

經典題目了。。。用異或就可以解決,可以考慮一下如果有2(3)個數只出現一次時怎麼解決,詳情可以見這篇部落格
(*)137.Single Number II
上面一道題的另一種變種:重複的數字每次出現3次(上題是2次).注意到雖然上面一題用的異或,本質上是由於1^1 = 0^0 = 0,而如果我們在二元域上做(摸2)加法也有類似效果:0 + 0 = 1 + 1 =0,所以我們只需要類似在3元域上作類似的模3加法即可。

一個直接方法是記錄原來的節點和新建節點的對應關係。還有一種方法是將新建節點直接加入到原來節點的末尾。=.=最後拆開連結串列時犯二了,所以連結串列題還是要畫圖 。

139.Word Break和140.Word Break II
第一思路是遞迴,肯定超時,然後考慮使用DP(一維的問題可以多考慮DP)。然後C++要用unordered_set但是由於我WINDOWS系統下沒找到這個檔案,然後各種CE,所以只好用JAVA過的這兩道題。。。
經典題目了,Floyd判圈法。

水題。

146.https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/

連結串列題。。。理解錯題意,WA了好久。

147.Insertion Sort List

連結串列題,水題

(*)148.Sort List

要求用nlog n以及常數空間對連結串列排序,只能寫個Quicksort了,然後發現超時了。。。再思考發現應該使用MergeSort,雖然在陣列中用歸併排序不是常數空間,但是連結串列歸併確實是常數空間=。=說明還是沒考慮周全

149.Max Points on a Line

題目本身不難,對每一個點,求它和其餘點的斜率,然後統計同一個斜率出現次數的最大值,結果即為所求,如果精度要求不高的話,其實用double和map就行了。如果要求高精度,可以用一個Long long  int儲存dx和dy,具體來說key = (dx<<32) | dy ,這樣就可以了。

逆波蘭表示式求值,水題,用棧即可解決。

151.Reverse Words in a String

經典題目,先把整個string做倒置,然後再對每個詞做倒置。

152.Maximum Product Subarray

類似於最大子段和吧,DP,需要用兩個陣列,一個存最大的正數,一個存最小的負數

經典題目。。WA了一次,忘了考慮完全排好序的狀態了,本質就是二分。第二題稍微動一下腦筋也不難,最壞情況是O(n) (一個最小數隨機在某個位置,但其餘數字全部相等)

經典題目,用兩個棧,一個裝普通值,一個裝最小值。。。要注意判斷棧是否為空的情況。

經典題目,先判斷兩個連結串列長度,然後長的那一個先往後走幾步,在兩個連結串列一般長的情況下,逐個判斷。

水題,=。=沒啥技巧,唯一的看點就是用bool變數記錄之前的資訊可以使得相鄰元素只比較一次。

比較有技巧的一道題。由於要求線性所以沒法排序,開始考慮hash後二分,發現不滿足二分關係;又回到排序,只能選用基數排序或者桶排序之類的線性方法,後來發現如果用桶排序構造出nums.size()+1個桶,那麼肯定由抽屜原理,肯定有一個桶是空的,那麼相鄰兩個不空的桶之間的差值才有可能是最大值。。。顧得到解法。。。總體來說題目還是很值得思考的。

水題,純模擬

這是我刷到目前為止WA的最多的題目。題目是求小數的迴圈節表示,本質上就是用除法,然後看餘數(準確說是餘數的10倍)是否之前被除過,如果是那麼就發現了迴圈節,用一個map就可以搞定。本質上不難,但是遇到了諸如正負號,int和string的轉換,除數和被除數是否超過int範圍等,各種WA。總的來說是一道非常考驗細節的題目,推薦多思考一下。Rotate Array

本質上是一個進位制問題,稍微思考一下就出來了,有點小坑。

經典題目,配對比較就可以了,要注意區分奇偶情況以及眾數不存在的情況。

經典數論問題,本質上是求n!有多少個素因子p,數論上有公式,注意溢位即可。

本質上就是用棧實現中序遍歷

經典DP,水題。。。不知道為啥放在Hard裡面

水題。。用stringstream 排序就可以了。。。但是WA了一次,要注意輸入全為0的特殊情況。

水題

水題,迴圈移動陣列,要注意移動次數超過陣列大小的情況。

按位翻轉,一般有兩種方法:一種二分(10101011 -> 01010111 -> 01011101 -> 11010101);還有一種查表法。兩種各有優勢,具體可以參考《C語言實現bit-reverse》  另外這裡第一次編譯時出錯,忘了類中靜態成員的初始化規則,具體可見《C++中類靜態成員初始化詳解》