生活随笔
收集整理的這篇文章主要介紹了
力扣(简单+中等)50题整理总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄 前言 一、簡單題 1. 兩數之和 7. 整數反轉 9. 回文數 13. 羅馬數字轉整數 14. 最長公共前綴 20. 有效的括號 21. 合并兩個有序鏈表 26. 刪除有序數組中的重復項 27. 移除元素 28. 實現 strStr() 35. 搜索插入位置 38. 外觀數列 53. 最大子序和 58. 最后一個單詞的長度 66. 加一 70. 爬樓梯 101. 對稱二叉樹 110. 平衡二叉樹 118. 楊輝三角 217. 存在重復元素 219. 存在重復元素 II 283. 移動零 509. 斐波那契數 643. 子數組最大平均數 I 976. 三角形的最大周長 1137. 第 N 個泰波那契數 二、中等題 2. 兩數相加 3. 無重復字符的最長子串 5. 最長回文子串 6. Z 字形變換 8. 字符串轉換整數 (atoi) 17. 電話號碼的字母組合 46. 全排列 62. 不同路徑 73. 矩陣置零 94. 二叉樹的中序遍歷 130. 被圍繞的區域 144. 二叉樹的前序遍歷 145. 二叉樹的后序遍歷 198. 打家劫舍 200. 島嶼數量 445. 兩數相加 II 486. 預測贏家 503. 下一個更大元素 II 567. 字符串的排列 877. 石子游戲 總結
前言
???????從第一次進入力扣,和朋友們琢磨著第一題的兩數之和,到現在玩力扣有一個多學期了。雖然沒有系統的訓練,但還是見識到了一些基礎的算法。先把做過的題總結了,然后接下來就撿一些重點算法去打。
一、簡單題
1. 兩數之和
方法:暴力,快慢指針
class Solution { public int [ ] twoSum ( int [ ] nums
, int target
) { int [ ] ans
= new int [ 2 ] ; for ( int i
= 0 ; i
< nums
. length
; ++ i
) { for ( int j
= i
+ 1 ; j
< nums
. length
; ++ j
) { if ( nums
[ j
] == target
- nums
[ i
] ) { ans
[ 0 ] = i
; ans
[ 1 ] = j
; return ans
; } } } return ans
; }
}
7. 整數反轉
方法:一位一位反轉(注意:判斷溢出)
class Solution { public int reverse ( int x
) { int ans
= 0 ; while ( x
!= 0 ) { if ( ( ans
* 10 ) / 10 != ans
) { ans
= 0 ; break ; } ans
= ans
* 10 + x
% 10 ; x
= x
/ 10 ; } return ans
; }
}
9. 回文數
方法:反轉此數,判斷反轉前后是否相等
class Solution { public boolean isPalindrome ( int x
) { if ( x
< 0 ) { return false ; } int ans
= 0 , m
= x
; while ( m
!= 0 ) { ans
= ans
* 10 + m
% 10 ; m
= m
/ 10 ; } if ( x
== ans
) { return true ; } else { return false ; } }
}
13. 羅馬數字轉整數
方法:循環嵌套switch開關,選擇對應的轉換值
class Solution {
public : int romanToInt ( string s
) { int ans
= 0 ; for ( int i
= 0 ; i
< s
. size ( ) ; i
++ ) { switch ( s
[ i
] ) { case 'I' : { if ( s
[ i
+ 1 ] == 'V' ) { ans
+ = 4 ; i
++ ; break ; } if ( s
[ i
+ 1 ] == 'X' ) { ans
+ = 9 ; i
++ ; break ; } ans
+ = 1 ; } break ; case 'V' : ans
+ = 5 ; break ; case 'X' : { if ( s
[ i
+ 1 ] == 'L' ) { ans
+ = 40 ; i
++ ; break ; } if ( s
[ i
+ 1 ] == 'C' ) { ans
+ = 90 ; i
++ ; break ; } ans
+ = 10 ; } break ; case 'L' : ans
+ = 50 ; break ; case 'C' : { if ( s
[ i
+ 1 ] == 'D' ) { ans
+ = 400 ; i
++ ; break ; } if ( s
[ i
+ 1 ] == 'M' ) { ans
+ = 900 ; i
++ ; break ; } ans
+ = 100 ; } break ; case 'D' : ans
+ = 500 ; break ; case 'M' : ans
+ = 1000 ; break ; } } return ans
; }
} ;
14. 最長公共前綴
方法:按住第一個串,將其逐個字符與后面的串中對應下標字符比較 注意:當首串當前字符下標與后面某個串的串長相等時,即可不再比較
class Solution { public String
longestCommonPrefix ( String
[ ] strs
) { if ( strs
== null
|| strs
. length
== 0 ) { return "" ; } for ( int i
= 0 ; i
< strs
[ 0 ] . length ( ) ; ++ i
) { char c
= strs
[ 0 ] . charAt ( i
) ; for ( int j
= 1 ; j
< strs
. length
; ++ j
) { if ( i
== strs
[ j
] . length ( ) || strs
[ j
] . charAt ( i
) != c
) { return strs
[ 0 ] . substring ( 0 , i
) ; } } } return strs
[ 0 ] ; }
}
20. 有效的括號
方法:左括號逐個進棧,右括號逐個與棧頂左括號匹配
關鍵點:
以棧的形式使用Deque(java) 當左括號比右括號多時,匹配結束棧不為空
class Solution { public boolean isValid ( String s
) { Deque
< Character> stack
= new LinkedList < > ( ) ; for ( int i
= 0 ; i
< s
. length ( ) ; ++ i
) { if ( s
. charAt ( i
) == '(' || s
. charAt ( i
) == '[' || s
. charAt ( i
) == '{' ) { stack
. push ( s
. charAt ( i
) ) ; } if ( s
. charAt ( i
) == ')' ) { if ( ! stack
. isEmpty ( ) && stack
. peek ( ) == '(' ) { stack
. pop ( ) ; } else { return false ; } } else if ( s
. charAt ( i
) == ']' ) { if ( ! stack
. isEmpty ( ) && stack
. peek ( ) == '[' ) { stack
. pop ( ) ; } else { return false ; } } else if ( s
. charAt ( i
) == '}' ) { if ( ! stack
. isEmpty ( ) && stack
. peek ( ) == '{' ) { stack
. pop ( ) ; } else { return false ; } } } return stack
. isEmpty ( ) ; }
}
21. 合并兩個有序鏈表
方法:雙指針+啞元
class Solution {
public : ListNode
* mergeTwoLists ( ListNode
* l1
, ListNode
* l2
) { ListNode
* ehead
= new ListNode
; ListNode
* e
= ehead
; while ( l1
&& l2
) { if ( l1
- > val
<= l2
- > val
) { e
- > next
= l1
; l1
= l1
- > next
; } else { e
- > next
= l2
; l2
= l2
- > next
; } e
= e
- > next
; } e
- > next
= l1
== nullptr ? l2
: l1
; ehead
= ehead
- > next
; return ehead
; }
} ;
26. 刪除有序數組中的重復項
方法:快慢指針
關鍵點:
快指針負責跳過重復元素 慢指針負責把不重復的元素記錄在數組前端
class Solution {
public : int removeDuplicates ( vector
< int > & nums
) { if ( nums
. size ( ) == 0 ) return 0 ; int i
= 0 , j
, flag
= 1 ; for ( j
= i
+ 1 ; j
<= nums
. size ( ) - 1 ; ++ j
) { if ( nums
[ j
] <= nums
[ i
] ) continue ; else { nums
[ ++ i
] = nums
[ j
] ; flag
++ ; } } return flag
; }
} ;
27. 移除元素
方法:快慢指針
關鍵點:
慢指針負責找目標val值 快指針負責將val值移動到數組后端
class Solution { public int removeElement ( int [ ] nums
, int val
) { int i
= 0 , flag
; for ( ; i
< nums
. length
; ++ i
) { if ( nums
[ i
] == val
) { flag
= 1 ; for ( int j
= i
; flag
== 1 && j
< nums
. length
; ++ j
) if ( nums
[ j
] != val
) { int temp
= nums
[ j
] ; nums
[ j
] = nums
[ i
] ; nums
[ i
] = temp
; flag
= 0 ; } if ( flag
== 1 ) break ; } } return i
; }
}
28. 實現 strStr()
關鍵點(滑動窗口):
維護窗口起始位start 使用substring截斷進行比較
public class Solution { public int strStr ( String haystack
, String needle
) { int n
= needle
. length ( ) ; int h
= haystack
. length ( ) ; for ( int start
= 0 ; start
< h
- n
+ 1 ; start
++ ) { if ( haystack
. substring ( start
, start
+ n
) . equals ( needle
) ) { return start
; } } return - 1 ; }
}
35. 搜索插入位置
方法:暴力
class Solution { public int searchInsert ( int [ ] nums
, int target
) { for ( int i
= 0 ; i
< nums
. length
; ++ i
) { if ( nums
[ i
] < target
) { continue ; } else { return i
; } } return nums
. length
; }
}
38. 外觀數列
class Solution { public String
countAndSay ( int n
) { if ( n
== 1 ) { return "1" ; } String s
= countAndSay ( n
- 1 ) ; StringBuilder result
= new StringBuilder ( ) ; int start
= 0 ; for ( int i
= 1 ; i
< s
. length ( ) ; i
++ ) { if ( s
. charAt ( i
) != s
. charAt ( start
) ) { result
. append ( i
- start
) . append ( s
. charAt ( start
) ) ; start
= i
; } } result
. append ( s
. length ( ) - start
) . append ( s
. charAt ( start
) ) ; return result
. toString ( ) ; }
}
53. 最大子序和
關鍵點(動態規劃):
pre維護的窗口的含義
class Solution { public int maxSubArray ( int [ ] nums
) { int pre
= 0 , maxAns
= nums
[ 0 ] ; for ( int x
: nums
) { pre
= Math
. max ( pre
+ x
, x
) ; maxAns
= Math
. max ( maxAns
, pre
) ; } return maxAns
; }
}
58. 最后一個單詞的長度
方法:暴力
class Solution { public int lengthOfLastWord ( String s
) { int count
= 0 ; s
= s
. toLowerCase ( ) ; for ( int i
= s
. length ( ) - 1 ; i
>= 0 ; -- i
) { if ( s
. charAt ( i
) >= 'a' && s
. charAt ( i
) <= 'z' ) { count
++ ; } else if ( count
> 0 ) { break ; } } return count
; }
}
66. 加一
關鍵點(暴力):
因為是加1,固不進位即可返回 溢出需構建長度為原數組長度+1的新數組,首元素初始化為1即可
class Solution { public int [ ] plusOne ( int [ ] digits
) { for ( int i
= digits
. length
- 1 ; i
>= 0 ; i
-- ) { digits
[ i
] ++ ; digits
[ i
] = digits
[ i
] % 10 ; if ( digits
[ i
] != 0 ) return digits
; } digits
= new int [ digits
. length
+ 1 ] ; digits
[ 0 ] = 1 ; return digits
; }
}
70. 爬樓梯
關鍵點(動態規劃):方程 F(n)= F(n - 1)- F(n - 2)
class Solution { public int climbStairs ( int n
) { int p
= 0 , q
= 0 , r
= 1 ; for ( int i
= 0 ; i
< n
; ++ i
) { p
= q
; q
= r
; r
= p
+ q
; } return r
; }
}
101. 對稱二叉樹
關鍵點(雙指針):
遞歸的傳參方式:check(p.left, q.right) && check(p.right, q.left) 遞歸的出口判斷:&& 要在 || 前面,幫 || 過濾兩個節點都是空的情況
class Solution { public boolean isSymmetric ( TreeNode root
) { return check ( root
, root
) ; } public boolean check ( TreeNode p
, TreeNode q
) { if ( p
== null
&& q
== null
) { return true ; } if ( p
== null
|| q
== null
) { return false ; } return p
. val
== q
. val
&& check ( p
. left
, q
. right
) && check ( p
. right
, q
. left
) ; }
}
110. 平衡二叉樹
關鍵點: 在計算二叉樹的算法基礎上加判斷:兩個子樹的高度絕對值是否滿足題目定義。
class Solution { public boolean isBalanced ( TreeNode root
) { return hightree ( root
) != - 1 ; } public static int hightree ( TreeNode root
) { int H
, H1
, H2
; if ( root
== null
) { H
= 0 ; } else { H1
= hightree ( root
. left
) ; H2
= hightree ( root
. right
) ; if ( H1
== - 1 || H2
== - 1 ) { return - 1 ; } if ( Math
. abs ( H1
- H2
) > 1 ) { return - 1 ; } H
= ( H1
> H2
? H1
: H2
) + 1 ; } return H
; }
}
118. 楊輝三角
關鍵點:
找規律 每次構建answer時需重新分配其內存 沒有搞懂第二點為什么不能用List.clear()代替
class Solution { public List
< List
< Integer> > generate ( int numRows
) { List
< List
< Integer> > answers
= new ArrayList < List
< Integer> > ( ) ; for ( int i
= 0 ; i
< numRows
; ++ i
) { List
< Integer> answer
= new ArrayList < > ( ) ; for ( int j
= 0 ; j
<= i
; ++ j
) { if ( j
== 0 || j
== i
) { answer
. add ( 1 ) ; } else { answer
. add ( answers
. get ( i
- 1 ) . get ( j
- 1 ) + answers
. get ( i
- 1 ) . get ( j
) ) ; } } answers
. add ( answer
) ; } return answers
; }
}
217. 存在重復元素
關鍵點:Set中不能添加重復元素
class Solution { public boolean containsDuplicate ( int [ ] nums
) { Set
< Integer> set
= new HashSet < Integer> ( ) ; for ( int x
: nums
) { if ( ! set
. add ( x
) ) { return true ; } } return false ; }
}
219. 存在重復元素 II
關鍵點(滑動窗口):
用Set作為滑動窗口,若窗口內存在重復元素,則true 前進右窗:add(nums【i】),前進左窗:remove(nums【i - k】)
class Solution { public boolean containsNearbyDuplicate ( int [ ] nums
, int k
) { Set
< Integer> set
= new HashSet < Integer> ( ) ; for ( int i
= 0 ; i
< nums
. length
; ++ i
) { if ( set
. contains ( nums
[ i
] ) ) { return true ; } set
. add ( nums
[ i
] ) ; if ( set
. size ( ) > k
) { set
. remove ( nums
[ i
- k
] ) ; } } return false ; }
}
283. 移動零
關鍵點(雙指針):
一次循環代替兩次,將復雜度O(n^2)降O(n) 快指針前進的條件:遇到0或完成一次賦值
class Solution { public void moveZeroes ( int [ ] nums
) { int a
= 0 , b
= 0 ; while ( b
< nums
. length
) { if ( nums
[ b
] == 0 ) { b
++ ; continue ; } nums
[ a
++ ] = nums
[ b
++ ] ; } while ( a
< b
) { nums
[ a
++ ] = 0 ; } }
}
509. 斐波那契數
(略)
int fib ( int N
) { if ( N
== 0 ) return 0 ; if ( N
== 1 ) return 1 ; return fib ( N
- 1 ) + fib ( N
- 2 ) ;
}
643. 子數組最大平均數 I
關鍵點(滑動窗口):
維護一個長度為k的窗口,在每次移動此窗口前,求窗口內元素平均值,取Max
class Solution { public double findMaxAverage ( int [ ] nums
, int k
) { double sum
= 0 , maxAvg
= 0 ; for ( int i
= 0 ; i
< nums
. length
; ++ i
) { sum
+= nums
[ i
] ; if ( i
== k
- 1 ) { maxAvg
= sum
/ k
; } if ( i
>= k
) { sum
-= nums
[ i
- k
] ; maxAvg
= Math
. max ( sum
/ k
, maxAvg
) ; } } return maxAvg
; }
}
976. 三角形的最大周長
方法:排序、按順序找符合條件的三角形,其周長即為數組內最大
class Solution { public int largestPerimeter ( int [ ] A
) { Arrays
. sort ( A
) ; for ( int i
= A
. length
- 1 ; i
>= 2 ; -- i
) { if ( A
[ i
- 2 ] + A
[ i
- 1 ] > A
[ i
] ) { return A
[ i
- 2 ] + A
[ i
- 1 ] + A
[ i
] ; } } return 0 ; }
}
1137. 第 N 個泰波那契數
方法:動態規劃
int tribonacci ( int n
) { if ( n
< 3 ) return n
== 0 ? 0 : 1 ; int x
= 0 , y
= 1 , z
= 1 , tep
; for ( int i
= 0 ; i
< n
- 2 ; ++ i
) { tep
= x
+ y
+ z
; x
= y
; y
= z
; z
= tep
; } return z
;
}
二、中等題
2. 兩數相加
關鍵點:雙指針,尾插,補零相加
class Solution { public ListNode
addTwoNumbers ( ListNode l1
, ListNode l2
) { ListNode head
= null
, tail
= null
; int carry
= 0 ; while ( l1
!= null
|| l2
!= null
) { int s1
= l1
== null
? 0 : l1
. val
; int s2
= l2
== null
? 0 : l2
. val
; int sum
= s1
+ s2
+ carry
; if ( head
== null
) { head
= tail
= new ListNode ( sum
% 10 ) ; } else { tail
. next
= new ListNode ( sum
% 10 ) ; tail
= tail
. next
; } carry
= sum
/ 10 ; if ( l1
!= null
) { l1
= l1
. next
; } if ( l2
!= null
) { l2
= l2
. next
; } } if ( carry
> 0 ) { tail
. next
= new ListNode ( 1 ) ; } return head
; }
}
3. 無重復字符的最長子串
關鍵點(滑動窗口):
右窗口臨近元素的加入條件:!occ.contains(s.charAt(rk + 1)) 左窗口移除元素的邏輯:剛好不包含rk+1處的重復元素
class Solution { public int lengthOfLongestSubstring ( String s
) { Set
< Character> occ
= new HashSet < Character> ( ) ; int n
= s
. length ( ) ; int rk
= - 1 , ans
= 0 ; for ( int i
= 0 ; i
< n
; ++ i
) { if ( i
!= 0 ) { occ
. remove ( s
. charAt ( i
- 1 ) ) ; } while ( rk
+ 1 < n
&& ! occ
. contains ( s
. charAt ( rk
+ 1 ) ) ) { occ
. add ( s
. charAt ( rk
+ 1 ) ) ; ++ rk
; } ans
= Math
. max ( ans
, rk
- i
+ 1 ) ; } return ans
; }
}
5. 最長回文子串
關鍵點(動態規劃):
dp的填表方向:列自左往右,行自上往下 獲取更大的回文子串判斷條件:j - i + 1 > ans.length()
class Solution { public static String
longestPalindrome ( String s
) { if ( s
. length ( ) <= 1 ) return s
; boolean [ ] [ ] dp
= new boolean [ s
. length ( ) ] [ s
. length ( ) ] ; String ans
= "" ; for ( int i
= 0 ; i
< s
. length ( ) ; ++ i
) { dp
[ i
] [ i
] = true ; } ans
= s
. substring ( 1 , 2 ) ; for ( int j
= 1 ; j
< s
. length ( ) ; ++ j
) { for ( int i
= 0 ; i
< j
; ++ i
) { if ( j
== i
+ 1 ) { dp
[ i
] [ j
] = s
. charAt ( i
) == s
. charAt ( j
) ; } else { dp
[ i
] [ j
] = ( s
. charAt ( i
) == s
. charAt ( j
) && dp
[ i
+ 1 ] [ j
- 1 ] ) ; } if ( dp
[ i
] [ j
] && j
- i
+ 1 > ans
. length ( ) ) { ans
= s
. substring ( i
, j
+ 1 ) ; } } } return ans
; }
}
6. Z 字形變換
關鍵點:找規律( 詳情見 Z字形變換(LeetCode第6題))
class Solution {
public : string
convert ( string s
, int numRows
) { if ( numRows
== 1 ) return s
; string
ans ( "" ) ; int down
= 2 * numRows
- 4 , up
= 2 ; for ( int i
= 0 ; i
< numRows
; i
++ ) { int k
= i
, flag
= 1 ; if ( i
== 0 || i
== numRows
- 1 ) { while ( k
< s
. size ( ) ) { ans
+ = s
[ k
] ; k
= k
+ 2 * numRows
- 2 ; } continue ; } else while ( k
< s
. size ( ) ) { ans
+ = s
[ k
] ; flag
++ % 2 == 1 ? k
= k
+ down
: k
= k
+ up
; } down
- = 2 ; up
+ = 2 ; } return ans
; }
} ;
8. 字符串轉換整數 (atoi)
方法:有限狀態機(不是很會,抄的題解,知道有這回事)
class Solution { public int myAtoi ( String str
) { Automaton automaton
= new Automaton ( ) ; int length
= str
. length ( ) ; for ( int i
= 0 ; i
< length
; ++ i
) { automaton
. get ( str
. charAt ( i
) ) ; } return ( int ) ( automaton
. sign
* automaton
. ans
) ; }
} class Automaton { public int sign
= 1 ; public long ans
= 0 ; private String state
= "start" ; private Map
< String
, String
[ ] > table
= new HashMap < String
, String
[ ] > ( ) { { put ( "start" , new String [ ] { "start" , "signed" , "in_number" , "end" } ) ; put ( "signed" , new String [ ] { "end" , "end" , "in_number" , "end" } ) ; put ( "in_number" , new String [ ] { "end" , "end" , "in_number" , "end" } ) ; put ( "end" , new String [ ] { "end" , "end" , "end" , "end" } ) ; } } ; public void get ( char c
) { state
= table
. get ( state
) [ get_col ( c
) ] ; if ( "in_number" . equals ( state
) ) { ans
= ans
* 10 + c
- '0' ; ans
= sign
== 1 ? Math
. min ( ans
, ( long ) Integer
. MAX_VALUE
) : Math
. min ( ans
, - ( long ) Integer
. MIN_VALUE
) ; } else if ( "signed" . equals ( state
) ) { sign
= c
== '+' ? 1 : - 1 ; } } private int get_col ( char c
) { if ( c
== ' ' ) { return 0 ; } if ( c
== '+' || c
== '-' ) { return 1 ; } if ( Character
. isDigit ( c
) ) { return 2 ; } return 3 ; }
}
17. 電話號碼的字母組合
關鍵點(dfs+狀態重置):
Map、StringBuffer類型的使用 當dfs層數到達號碼串長時,剪枝
class Solution { public List
< String> letterCombinations ( String digits
) { List
< String> answers
= new ArrayList < String> ( ) ; if ( digits
. length ( ) == 0 ) { return answers
; } Map
< Character, String> phoneMap
= new HashMap < Character, String> ( ) { { put ( '2' , "abc" ) ; put ( '3' , "def" ) ; put ( '4' , "ghi" ) ; put ( '5' , "jkl" ) ; put ( '6' , "mno" ) ; put ( '7' , "pqrs" ) ; put ( '8' , "tuv" ) ; put ( '9' , "wxyz" ) ; } } ; dfs ( answers
, phoneMap
, digits
, 0 , new StringBuffer ( ) ) ; return answers
; } public void dfs ( List
< String> answers
, Map
< Character, String> phoneMap
, String digits
, int deepth
, StringBuffer path
) { if ( deepth
== digits
. length ( ) ) { answers
. add ( path
. toString ( ) ) ; } else { char digit
= digits
. charAt ( deepth
) ; String letters
= phoneMap
. get ( digit
) ; for ( int i
= 0 ; i
< letters
. length ( ) ; i
++ ) { path
. append ( letters
. charAt ( i
) ) ; dfs ( answers
, phoneMap
, digits
, deepth
+ 1 , path
) ; path
. deleteCharAt ( deepth
) ; } } }
}
46. 全排列
關鍵點(dfs):狀態變量,狀態重置,以棧的形式使用Deque(java)
class Solution { public List
< List
< Integer> > permute ( int [ ] nums
) { List
< List
< Integer> > res
= new ArrayList < > ( ) ; if ( nums
. length
== 0 ) { return res
; } Deque
< Integer> path
= new ArrayDeque < > ( ) ; boolean [ ] used
= new boolean [ nums
. length
] ; int deepth
= 0 ; dfs ( nums
, res
, path
, used
, deepth
) ; return res
; } private void dfs ( int [ ] nums
, List
< List
< Integer> > res
, Deque
< Integer> path
, boolean [ ] used
, int deepth
) { if ( deepth
== nums
. length
) { res
. add ( new ArrayList < > ( path
) ) ; return ; } for ( int i
= 0 ; i
< nums
. length
; ++ i
) { if ( used
[ i
] == true ) { continue ; } path
. addLast ( nums
[ i
] ) ; used
[ i
] = true ; dfs ( nums
, res
, path
, used
, deepth
+ 1 ) ; path
. removeLast ( ) ; used
[ i
] = false ; } }
}
62. 不同路徑
關鍵點(動態規劃):
先填首行與首列 再依次列從左往右,行從上往下填表
class Solution { public int uniquePaths ( int m
, int n
) { int [ ] [ ] dp
= new int [ m
] [ n
] ; for ( int i
= 0 ; i
< m
; ++ i
) { dp
[ i
] [ 0 ] = 1 ; } for ( int i
= 0 ; i
< n
; ++ i
) { dp
[ 0 ] [ i
] = 1 ; } for ( int i
= 1 ; i
< m
; ++ i
) { for ( int j
= 1 ; j
< n
; ++ j
) { dp
[ i
] [ j
] = dp
[ i
- 1 ] [ j
] + dp
[ i
] [ j
- 1 ] ; } } return dp
[ m
- 1 ] [ n
- 1 ] ; }
}
73. 矩陣置零
關鍵點:行列標記數組,先標記再替換
class Solution { public void setZeroes ( int [ ] [ ] matrix
) { int m
= matrix
. length
, n
= matrix
[ 0 ] . length
; boolean [ ] row
= new boolean [ m
] ; boolean [ ] col
= new boolean [ n
] ; for ( int i
= 0 ; i
< m
; i
++ ) { for ( int j
= 0 ; j
< n
; j
++ ) { if ( matrix
[ i
] [ j
] == 0 ) { row
[ i
] = col
[ j
] = true ; } } } for ( int i
= 0 ; i
< m
; i
++ ) { for ( int j
= 0 ; j
< n
; j
++ ) { if ( row
[ i
] || col
[ j
] ) { matrix
[ i
] [ j
] = 0 ; } } } }
}
94. 二叉樹的中序遍歷
(數據結構基礎)
class Solution { public List
< Integer> inorderTraversal ( TreeNode root
) { List
< Integer> answer
= new ArrayList ( ) ; if ( root
== null
) return answer
; PT ( answer
, root
) ; return answer
; } public void PT ( List
< Integer> answer
, TreeNode root
) { if ( root
. left
!= null
) PT ( answer
, root
. left
) ; answer
. add ( root
. val
) ; if ( root
. right
!= null
) PT ( answer
, root
. right
) ;
}
}
130. 被圍繞的區域
關鍵點(dfs):
從上下左右四個方向深度優先搜索邊界的O以及與其相連的O,并替換成‘#’ 填充所有未被搜索到的O(被圍繞的O),再將#還原為O(邊界O)
class Solution { public void solve ( char [ ] [ ] board
) { if ( board
== null
|| board
. length
== 0 ) return ; int m
= board
. length
; int n
= board
[ 0 ] . length
; for ( int i
= 0 ; i
< m
; i
++ ) { for ( int j
= 0 ; j
< n
; j
++ ) { boolean isEdge
= i
== 0 || j
== 0 || i
== m
- 1 || j
== n
- 1 ; if ( isEdge
&& board
[ i
] [ j
] == 'O' ) { dfs ( board
, i
, j
) ; } } } for ( int i
= 0 ; i
< m
; i
++ ) { for ( int j
= 0 ; j
< n
; j
++ ) { if ( board
[ i
] [ j
] == 'O' ) { board
[ i
] [ j
] = 'X' ; } if ( board
[ i
] [ j
] == '#' ) { board
[ i
] [ j
] = 'O' ; } } } } public void dfs ( char [ ] [ ] board
, int i
, int j
) { if ( i
< 0 || j
< 0 || i
>= board
. length
|| j
>= board
[ 0 ] . length
|| board
[ i
] [ j
] == 'X' || board
[ i
] [ j
] == '#' ) { return ; } board
[ i
] [ j
] = '#' ; dfs ( board
, i
- 1 , j
) ; dfs ( board
, i
+ 1 , j
) ; dfs ( board
, i
, j
- 1 ) ; dfs ( board
, i
, j
+ 1 ) ; }
}
144. 二叉樹的前序遍歷
(數據結構基礎)
class Solution { public List
< Integer> preorderTraversal ( TreeNode root
) { List
< Integer> answer
= new ArrayList ( ) ; if ( root
== null
) return answer
; PT ( answer
, root
) ; return answer
; } public void PT ( List
< Integer> answer
, TreeNode root
) { answer
. add ( root
. val
) ; if ( root
. left
!= null
) PT ( answer
, root
. left
) ; if ( root
. right
!= null
) PT ( answer
, root
. right
) ; }
}
145. 二叉樹的后序遍歷
(數據結構基礎)
class Solution { public List
< Integer> postorderTraversal ( TreeNode root
) { List
< Integer> answer
= new ArrayList ( ) ; if ( root
== null
) return answer
; PT ( answer
, root
) ; return answer
; } public void PT ( List
< Integer> answer
, TreeNode root
) { if ( root
. left
!= null
) PT ( answer
, root
. left
) ; if ( root
. right
!= null
) PT ( answer
, root
. right
) ; answer
. add ( root
. val
) ; }
}
198. 打家劫舍
關鍵點(動態規劃):
狀態轉移方程:dp[i]=max(dp[i?2]+nums[i],dp[i?1]) 邊界條件:dp[0]=nums[0],dp[1]=max(nums[0],nums[1]) ?
class Solution { public int rob ( int [ ] nums
) { if ( nums
. length
== 0 ) return 0 ; if ( nums
. length
== 1 ) return nums
[ 0 ] ; if ( nums
. length
== 2 ) return Math
. max ( nums
[ 0 ] , nums
[ 1 ] ) ; int [ ] dp
= new int [ nums
. length
] ; dp
[ 0 ] = nums
[ 0 ] ; dp
[ 1 ] = Math
. max ( nums
[ 0 ] , nums
[ 1 ] ) ; for ( int i
= 2 ; i
< nums
. length
; ++ i
) { dp
[ i
] = Math
. max ( dp
[ i
- 2 ] + nums
[ i
] , dp
[ i
- 1 ] ) ; } return dp
[ nums
. length
- 1 ] ; }
}
?
200. 島嶼數量
思路:
遍歷二維數組,遇到1,島嶼數量+1 DFS將與其連起來的1替換成0,退出DFS繼續遍歷二維數組
class Solution { public int numIslands ( char [ ] [ ] grid
) { int num_Islands
= 0 ; for ( int i
= 0 ; i
< grid
. length
; ++ i
) { for ( int j
= 0 ; j
< grid
[ 0 ] . length
; ++ j
) { if ( grid
[ i
] [ j
] == '1' ) { ++ num_Islands
; dfs ( grid
, i
, j
) ; } } } return num_Islands
; } public void dfs ( char [ ] [ ] grid
, int i
, int j
) { if ( i
< 0 || j
< 0 || i
> grid
. length
- 1 || j
> grid
[ 0 ] . length
- 1 || grid
[ i
] [ j
] == '0' ) { return ; } grid
[ i
] [ j
] = '0' ; dfs ( grid
, i
- 1 , j
) ; dfs ( grid
, i
+ 1 , j
) ; dfs ( grid
, i
, j
- 1 ) ; dfs ( grid
, i
, j
+ 1 ) ; }
}
445. 兩數相加 II
關鍵點(棧):
保證位數對齊:將兩個數的每個位數都分別入棧,然后從兩個棧頂開始相加
class Solution { public ListNode
addTwoNumbers ( ListNode l1
, ListNode l2
) { Deque
< Integer> s1
= new ArrayDeque < > ( ) ; while ( l1
!= null
) { s1
. addLast ( l1
. val
) ; l1
= l1
. next
; } Deque
< Integer> s2
= new ArrayDeque < > ( ) ; while ( l2
!= null
) { s2
. addLast ( l2
. val
) ; l2
= l2
. next
; } ListNode answer
= new ListNode ( 0 ) ; int carry
= 0 ; while ( ! s1
. isEmpty ( ) || ! s2
. isEmpty ( ) ) { int sum
= ( s1
. isEmpty ( ) ? 0 : s1
. removeLast ( ) ) + ( s2
. isEmpty ( ) ? 0 : s2
. removeLast ( ) ) + carry
; ListNode newnode
= new ListNode ( sum
% 10 ) ; newnode
. next
= answer
. next
; answer
. next
= newnode
; if ( sum
>= 10 ) { carry
= sum
/ 10 ; } else { carry
= 0 ; } } if ( carry
!= 0 ) { ListNode newnode
= new ListNode ( 1 ) ; newnode
. next
= answer
. next
; answer
. next
= newnode
; } return answer
. next
; }
}
486. 預測贏家
關鍵點(動態規劃):
先設計暴力解法,再用dp數組記錄重復運算進行優化
class Solution { public boolean PredictTheWinner ( int [ ] nums
) { int [ ] [ ] dp
= new int [ nums
. length
] [ nums
. length
] ; for ( int i
= 0 ; i
< nums
. length
; ++ i
) { dp
[ i
] [ i
] = nums
[ i
] ; } for ( int i
= nums
. length
- 2 ; i
>= 0 ; -- i
) { for ( int j
= i
+ 1 ; j
<= nums
. length
- 1 ; ++ j
) { dp
[ i
] [ j
] = Math
. max ( nums
[ i
] - dp
[ i
+ 1 ] [ j
] , nums
[ j
] - dp
[ i
] [ j
- 1 ] ) ; } } return dp
[ 0 ] [ nums
. length
- 1 ] >= 0 ; }
}
503. 下一個更大元素 II
(暴力,單調棧為更優解)
class Solution { public int [ ] nextGreaterElements ( int [ ] nums
) { int [ ] ans
= new int [ nums
. length
] ; for ( int n
= 0 ; n
< nums
. length
; ++ n
) { int count
= 0 ; for ( int i
= ( n
+ 1 ) % nums
. length
; i
< nums
. length
; i
= ( i
+ 1 ) % nums
. length
) { if ( ++ count
>= nums
. length
) { ans
[ n
] = - 1 ; break ; } if ( nums
[ n
] < nums
[ i
] ) { ans
[ n
] = nums
[ i
] ; break ; } } } return ans
; }
}
567. 字符串的排列
關鍵點(滑動窗口):
用長度為26的一維數組維護一個滑動窗口 窗口在l2滑動的過程,只要滿足窗口內各字母數量與l1內各字母數相符,即匹配成功
class Solution { public boolean checkInclusion ( String s1
, String s2
) { if ( s1
. length ( ) > s2
. length ( ) ) return false ; int [ ] s1map
= new int [ 26 ] ; int [ ] s2map
= new int [ 26 ] ; for ( int i
= 0 ; i
< s1
. length ( ) ; i
++ ) { s1map
[ s1
. charAt ( i
) - 'a' ] ++ ; s2map
[ s2
. charAt ( i
) - 'a' ] ++ ; } for ( int i
= 0 ; i
< s2
. length ( ) - s1
. length ( ) ; i
++ ) { if ( isMatch ( s1map
, s2map
) ) { return true ; } s2map
[ s2
. charAt ( i
+ s1
. length ( ) ) - 'a' ] ++ ; s2map
[ s2
. charAt ( i
) - 'a' ] -- ; } return isMatch ( s1map
, s2map
) ; } private static boolean isMatch ( int [ ] charArray1
, int [ ] charArray2
) { for ( int i
= 0 ; i
< charArray1
. length
; i
++ ) { if ( charArray1
[ i
] != charArray2
[ i
] ) { return false ; } } return true ; }
}
877. 石子游戲
(預測贏家的同類題,此處可用數學解,略)
class Solution { public boolean stoneGame ( int [ ] piles
) { return true ; }
}
總結
???????認識了很多算法,像動態規劃、滑動窗口、Dfs、回溯、雙指針…認識了很多數據結構,像Java的Set、Deque、ArrayList、LinkedList…
???????我身邊有很多人,參加過很多競賽,獲得過很多榮譽。有人問過我說:他們各方面都比你優秀,努力程度也不比你差,為啥還要努力?我說,有意義的是過程,結果是大家都看得到的,只有過程是真正屬于自己的,即使未來不青睞我,我也要繼續,因為:
???????“強大者應持謙卑之心不可因力量而跋扈, ???????????????弱小者應持上進之心不可因不敵而麻木。”
繼續打題、繼續前進… (未完待續)
總結
以上是生活随笔 為你收集整理的力扣(简单+中等)50题整理总结 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。