Leetcode Top 100 思路與程式碼(Java)
第01-100題
【Leetcode-easy-1】 Two Sum 兩數之和
平生不識TwoSum,刷盡LeetCode也枉然
思路:
為了提高時間的複雜度,需要用空間來換,那麼就是說只能遍歷一個數字,那麼另一個數字呢,我們可以事先將其儲存起來,使用一個HashMap,來建立數字和其座標位置之間的對映,我們都知道HashMap是常數級的查詢效率,這樣,我們在遍歷陣列的時候,用target減去遍歷到的數字,就是另一個需要的數字了,直接在HashMap中查詢其是否存在即可,注意要判斷查詢到的數字不是第一個數字,比如target是4,遍歷到了一個2,那麼另外一個2不能是之前那個2,整個實現步驟為:先遍歷一遍陣列,建立HashMap對映,然後再遍歷一遍,開始查詢,找到則記錄index。
import java.util.*;
public class Solution {
/**
* You may assume that each input would have exactly one solution
*
* use Map
*/
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i ++){
int remainder = target - nums[i];
if(map.containsKey(nums[i]))
return new int[]{map.get(nums[i]), i};
map.put(remainder, i);
}
throw new IllegalArgumentException("no solution.");
}
}
【Leetcode-medium-2】 Add Two Numbers 兩個數字相加
思路:建立一個新連結串列,然後把輸入的兩個連結串列從頭往後擼,每兩個相加,新增一個新節點到新連結串列後面,就是要處理下進位問題。還有就是最高位的進位問題要最後特殊處理一下。
/**
* Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
* Output: 7 -> 0 -> 8
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
* 使用dummyHead避免寫重複的程式碼,非常巧妙
*/
public class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0); // 第二個結點是連結串列的頭結點
int increment = 0;
ListNode currNode = dummyHead;
for (ListNode n1 = l1, n2 = l2; l1 != null || l2 != null;){
int x = l1 != null ? l1.val : 0;
int y = l2 != null ? l2.val : 0;
int result = x + y + increment;
currNode.next = new ListNode(result % 10);
increment = result / 10;
currNode = currNode.next;
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;
}
if (increment == 1){
currNode.next = new ListNode(1);
}
return dummyHead.next;
}
}
【Leetcode-medium-3】 Longest Substring Without Repeating Characters 最長無重複字元的子串
/**
* Given “abcabcbb”, the answer is “abc”, which the length is 3.
* Given “bbbbb”, the answer is “b”, with the length of 1.
*
*/
import java.util.*;
public class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLen = 0;
StringBuilder sub = new StringBuilder(s.length());
int fromIndex = 0;
for (int i = 0; i < s.length(); i ++){
char ch = s.charAt(i);
int index = sub.indexOf(ch+"", fromIndex); // 重複“字元”(字串)的位置
if (index != -1) fromIndex = index+1; // 不斷調整起始下標
sub.append(ch);
int len = sub.length() - fromIndex; // 總長度 - 起始下標 = 當前子字串的長度
if (maxLen < len) maxLen = len;
}
return maxLen;
}
}
【Leetcode-hard-4】
Median of Two Sorted Arrays 兩個有序陣列的中位數
思路:限制了時間複雜度為O(log (m+n)),應該使用二分查詢法來求解。難點在於要在兩個未合併的有序陣列之間使用二分法,這裡我們需要定義一個函式來找到第K個元素,由於兩個陣列長度之和的奇偶不確定,因此需要分情況來討論,對於奇數的情況,直接找到最中間的數即可,偶數的話需要求最中間兩個數的平均值。下面重點來看如何實現找到第K個元素,首先我們需要讓陣列1的長度小於或等於陣列2的長度,那麼我們只需判斷如果陣列1的長度大於陣列2的長度的話,交換兩個陣列即可,然後我們要判斷小的陣列是否為空,為空的話,直接在另一個數組找第K個即可。還有一種情況是當K = 1時,表示我們要找第一個元素,只要比較兩個陣列的第一個元素,返回較小的那個即可。
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
if (m < n) return findMedianSortedArrays(nums2, nums1);
if (n == 0) return (nums1[(m - 1) / 2] + nums1[m / 2]) / 2.0;
int left = 0, right = 2 * n;
while (left <= right) {
int mid2 = (left + right) / 2;
int mid1 = m + n - mid2;
double L1 = mid1 == 0 ? Double.MIN_VALUE : nums1[(mid1 - 1) / 2];
double L2 = mid2 == 0 ? Double.MIN_VALUE : nums2[(mid2 - 1) / 2];
double R1 = mid1 == m * 2 ? Double.MAX_VALUE : nums1[mid1 / 2];
double R2 = mid2 == n * 2 ? Double.MAX_VALUE : nums2[mid2 / 2];
if (L1 > R2) left = mid2 + 1;
else if (L2 > R1) right = mid2 - 1;
else return (Math.max(L1, L2) + Math.min(R1, R2)) / 2;
}
return -1;
}
}
【Leetcode-medium-5】 Longest Palindromic Substring 最長迴文串
/**
* Input: “babad” Output: “bab”
* Input: “cbbd” Output: “bb”
*/
class Solution {
public String longestPalindrome(String s) {
int start = 0, end = 0;
for (int i = 0; i < s.length()-1; i ++){
int len1 = expandAroundCenter(s, i, i); // 假設迴文字串的長度是奇數
int len2 = expandAroundCenter(s, i, i+1); // 假設迴文字串的長度是偶數
int len = Math.max(len1, len2);
// 邊界判斷
if (len > end-start){
start = i - (len-1)/2; // 計算新的邊界
end = i + len/2;
}
}
return s.substring(start, end+1);
}
// 從left,right向左右擴充套件
// 雙引數真是很巧妙,一直卡在這裡了
private int expandAroundCenter(String s, int left, int right){
int L = left, R = right;
for (; L >= 0 && R < s.length(); L --, R ++){
if (s.charAt(L) != s.charAt(R))
break;
}
return R-L-1; // 根據example判斷是否減去1
}
}
【Leetcode-hard-10】 Regular Expression Matching 正則表示式匹配
Some examples:
isMatch(“aa”,”a”) → false
isMatch(“aa”,”aa”) → true
isMatch(“aaa”,”aa”) → false
isMatch(“aa”, “a*”) → true
isMatch(“aa”, “.*”) → true
isMatch(“ab”, “.*”) → true
isMatch(“aab”, “c*a*b”) → true思路:用遞迴Recursion來解,大概思路如下
若p為空,若s也為空,返回true,反之返回false
若p的長度為1,若s長度也為1,且相同或是p為’.’則返回true,反之返回false
若p的第二個字元不為*,若此時s為空返回false,否則判斷首字元是否匹配,且從各自的第二個字元開始呼叫遞迴函式匹配
若p的第二個字元為*,若s不為空且字元匹配,呼叫遞迴函式匹配s和去掉前兩個字元的p,若匹配返回true,否則s去掉首字母
返回呼叫遞迴函式匹配s和去掉前兩個字元的p的結果
public class Solution {
public boolean isMatch(String s, String p) {
if(p.length() == 0)
return s.length() == 0;
//p's length 1 is special case
if(p.length() == 1 || p.charAt(1) != '*'){
if(s.length() < 1 || (p.charAt(0) != '.' && s.charAt(0) != p.charAt(0)))
return false;
return isMatch(s.substring(1), p.substring(1));
}else{
int len = s.length();
int i = -1;
while(i<len && (i < 0 || p.charAt(0) == '.' || p.charAt(0) == s.charAt(i))){
if(isMatch(s.substring(i+1), p.substring(2)))
return true;
i++;
}
return false;
}
}
}
【Leetcode-medium-11】 Container With Most Water 裝最多水的容器
思路:
我們認為長度越長且高度越大,則面積越大。
現在,使用兩個指標分別指向首、尾,這時它的寬度是最大的。
可能還會出現面積更大的情況,只有當高度變大的時候,所以可以移動兩個指標中的較小者,這樣可以能會使高度增加以彌補長度變短造成面積減少的損失。
一直移動兩者中較小者,直到兩者相遇,取各種情況的最大值即是最後的結果。
public int maxArea(int[] height){
int maxarea = 0;
int l = 0, r = height.length-1;
while(l < r){
int area = calArea(l, height[l], r, height[r]);
if (area > maxarea) maxarea = area;
if (height[l] <= height[r]) l ++;
else r --;
}
return maxarea;
}
private int calArea(int i, int h1, int j, int h2){
return Math.min(h1, h2) * (j-i);
}
【Leetcode-medium-15】 3Sum 三數之和
思路:
我們對原陣列進行排序,然後開始遍歷排序後的陣列,這裡注意不是遍歷到最後一個停止,而是到倒數第三個就可以了。這裡我們可以先做個剪枝優化,就是當遍歷到正數的時候就break,為啥呢,因為我們的陣列現在是有序的了,如果第一個要fix的數就是正數了,那麼後面的數字就都是正數,就永遠不會出現和為0的情況了。然後我們還要加上重複就跳過的處理,處理方法是從第二個數開始,如果和前面的數字相等,就跳過,因為我們不想把相同的數字fix兩次。對於遍歷到的數,用0減去這個fix的數得到一個target,然後只需要再之後找到兩個數之和等於target即可。我們用兩個指標分別指向fix數字之後開始的陣列首尾兩個數,如果兩個數和正好為target,則將這兩個數和fix的數一起存入結果中。然後就是跳過重複數字的步驟了,兩個指標都需要檢測重複數字。如果兩數之和小於target,則我們將左邊那個指標i右移一位,使得指向的數字增大一些。同理,如果兩數之和大於target,則我們將右邊那個指標j左移一位,使得指向的數字減小一些。
public List<List<Integer>> threeSum(int[] nums) {
ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
if (nums.length < 3) return result;
Arrays.sort(nums);
ArrayList<Integer> temp = null;
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; //選定nums[i]為第一個數,並去重
int left = i + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
temp = new ArrayList<Integer>();
temp.add(nums[i]);
temp.add(nums[left]);
temp.add(nums[right]);
result.add(temp);
while (left < right && nums[left] == nums[left + 1]) left++; //去重
while (left + 1 < right && nums[right] == nums[right - 1]) right--;
}
if (sum <= 0) left++;
else if (sum >= 0) right--;
}
}
return result;
}
【Leetcode-Easy-20】 Valid Parentheses 驗證括號
思路:需要用一個棧,我們開始遍歷輸入字串,如果當前字元為左半邊括號時,則將其壓入棧中,如果遇到右半邊括號時,若此時棧為空,則直接返回false,如不為空,則取出棧頂元素,若為對應的左半邊括號,則繼續迴圈,反之返回false
class Solution {
public boolean isValid(String s) {
LinkedList<String> stack = new LinkedList<>();
HashSet<String> set = new HashSet<>();
set.add("(");
set.add("[");
set.add("{");
for (int i = 0; i < s.length(); i ++){
String part = s.charAt(i) + "";
if (set.contains(part)) stack.push(part);
else{
if (stack.isEmpty()) return false;
String prepart = stack.pop();
if ("}".equals(part) && !"{".equals(prepart) ||
")".equals(part) && !"(".equals(prepart) ||
"]".equals(part) && !"[".equals(prepart)){
return false;
}
}
}
return stack.isEmpty();
}
}
//更為巧妙的解法
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.isEmpty() || stack.pop() != c)
return false;
}
return stack.isEmpty();
}
【Leetcode-Easy-21】 Merge Two Sorted Lists
思路:巧用頭結點(啞結點)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode currNode = dummyHead;
while (l1 != null && l2 != null){
if (l1.val < l2.val){
currNode.next = l1;
l1 = l1.next;
}else{
currNode.next = l2;
l2 = l2.next;
}
currNode = currNode.next;
}
if (l1 != null) currNode.next = l1;
if (l2 != null) currNode.next = l2;
return dummyHead.next;
}
}
【Leetcode-Medium-22】 Generate Parentheses
思路:
回溯,這裡的想法是隻新增‘(’和‘)’,我們知道這將保證我們的解決方案(而不是新增1太多關閉)。
一旦我們添加了一個‘(’然後我們將放棄它並嘗試一個‘)’,它只能關閉一個有效的’(‘。這些步驟中的每一個都被遞迴地呼叫。
public List<String> generateParenthesis(int n) {
List<String> list = new ArrayList<>();
backtrack(list, "", 0, 0, n);
return list;
}
private void backtrack(List<String> list, String str, int open, int close, int max){
if (str.length() == 2*max){
list.add(str);
return;
}
if (open < max)
backtrack(list, str+"(", open+1, close, max);
if (close < open)
backtrack(list, str+")", open, close+1, max);
}
【Leetcode-hard-23】 Merge k Sorted Lists 合併k個有序連結串列
思路:
這裡需要用到分治法 Divide and Conquer Approach。簡單來說就是不停的對半劃分,比如k個連結串列先劃分為合併兩個k/2個連結串列的任務,再不停的往下劃分,直到劃分成只有一個或兩個連結串列的任務,開始合併。舉個例子來說比如合併6個連結串列,那麼按照分治法,我們首先分別合併1和4,2和5,3和6。這樣下一次只需合併3個連結串列,我們再合併1和3,最後和2合併就可以了。
public ListNode mergeKLists(ArrayList<ListNode> lists) {
if(lists==null || lists.size()==0)
return null;
return helper(lists,0,lists.size()-1);
}
private ListNode helper(ArrayList<ListNode> lists, int l, int r)
{
if(l<r)
{
int m = (l+r)/2;
return merge(helper(lists,l,m),helper(lists,m+1,r));
}
return lists.get(l);
}
private ListNode merge(ListNode l1, ListNode l2)
{
ListNode dummy = new ListNode(0);
dummy.next = l1;
ListNode cur = dummy;
while(l1!=null && l2!=null)
{
if(l1.val<l2.val)
{
l1 = l1.next;
}
else
{
ListNode next = l2.next;
cur.next = l2;
l2.next = l1;
l2 = next;
}
cur = cur.next;
}
if(l2!=null)
cur.next = l2;
return dummy.next;
}
【Leetcode-hard-32】 Longest Valid Parentheses 最長有效括號
思路:
藉助棧來求解,需要定義個start變數來記錄合法括號串的起始位置,我們遍歷字串,如果遇到左括號,則將當前下標壓入棧,如果遇到右括號,如果當前棧為空,則將下一個座標位置記錄到start,如果棧不為空,則將棧頂元素取出,此時若棧為空,則更新結果和i - start + 1中的較大值,否則更新結果和i - 棧頂元素中的較大值
public class Demo2 {
public int longestValidParentheses(String s) {
if (s == null || s.length() < 1)
return 0;
Stack<Integer> stack = new Stack<Integer>();
int max = 0, left = -1;
for (int i = 0; i < s.length(); i++) {
//如果遍歷到左括號,壓入堆疊
if (s.charAt(i) == '(')
stack.push(i);
else {
if (!stack.isEmpty()) {
stack.pop();
if (!stack.isEmpty())
max = Math.max(max, i - stack.peek());
else
max = Math.max(max, i - left);
} else
//如果堆疊為空,說明當前的有括號無法配對,需要重新設定left的值
left = i;
}
}
return max;
}
}
【Leetcode-medium-33】 Search in Rotated Sorted Array 在旋轉有序陣列中搜索
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.思路:
這道題讓在旋轉陣列中搜索一個給定值,若存在返回座標,若不存在返回-1。我們還是考慮二分搜尋法,但是這道題的難點在於我們不知道原陣列在哪旋轉了,我們還是用題目中給的例子來分析,對於陣列[0 1 2 4 5 6 7] 共有下列七種旋轉方法:
0 1 2 4 5 6 7
7 0 1 2 4 5 6
6 7 0 1 2 4 5
5 6 7 0 1 2 4
4 5 6 7 0 1 2
2 4 5 6 7 0 1
1 2 4 5 6 7 0
二分搜尋法的關鍵在於獲得了中間數後,判斷下面要搜尋左半段還是右半段,我們觀察上面紅色的數字都是升序的,由此我們可以觀察出規律,如果中間的數小於最右邊的數,則右半段是有序的,若中間數大於最右邊數,則左半段是有序的,我們只要在有序的半段裡用首尾兩個陣列來判斷目標值是否在這一區域內,這樣就可以確定保留哪半邊了
public int search(int[] A, int target) {
if(A==null || A.length==0)
return -1;
int l = 0;
int r = A.length-1;
while(l<=r)
{
int m = (l+r)/2;
if(target == A[m])
return m;
if(A[m]<A[r])
{
if(target>A[m] && target<=A[r])
l = m+1;
else
r = m-1;
}
else
{
if(target>=A[l] && target<A[m])
r = m-1;
else
l = m+1;
}
}
return -1;
}
【Leetcode-medium-34】 Search for a Range 搜尋一個範圍
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].思路:
只用兩次二分查詢。 如果我們不尋找那個元素先,而是直接相等的時候也向一個方向繼續夾逼,如果向右夾逼,最後就會停在右邊界,而向左夾逼則會停在左邊界,如此用停下來的兩個邊界就可以知道結果了,只需要兩次二分查詢。
public int[] searchRange(int[] A, int target) {
int[] res = {-1,-1};
if(A==null || A.length==0)
{
return res;
}
int ll = 0;
int lr = A.length-1;
while(ll<=lr)
{
int m = (ll+lr)/2;
if(A[m]<target)
{
ll = m+1;
}
else
{
lr = m-1;
}
}
int rl = 0;
int rr = A.length-1;
while(rl<=rr)
{
int m = (rl+rr)/2;
if(A[m]<=target)
{
rl = m+1;
}
else
{
rr = m-1;
}
}
if(ll<=rr)
{
res[0] = ll;
res[1] = rr;
}
return res;
}
【Leetcode-easy-35】 Search Insert Position 搜尋插入位置
思路:
二分查詢。每次取中間,如果等於目標即返回,否則根據大小關係切去一半。因此演算法複雜度是O(logn),空間複雜度O(1)
public int searchInsert(int[] A, int target) {
if(A == null || A.length == 0)
{
return 0;
}
int l = 0;
int r = A.length-1;
while(l<=r)
{
int mid = (l+r)/2;
if(A[mid]==target)
return mid;
if(A[mid]<target)
l = mid+1;
else
r = mid-1;
}
return l;
}
【Leetcode-easy-39】 Combination Sum 組合之和
For example, given candidate set 2,3,6,7 and target 7,
A solution set is:
[7]
[2, 2, 3]思路:
NP問題,先排好序,然後每次遞迴中把剩下的元素一一加到結果集合中,並且把目標減去加入的元素,然後把剩下元素(包括當前加入的元素)放到下一層遞迴中解決子問題。演算法複雜度因為是NP問題,所以自然是指數量級的。
public ArrayList<ArrayList<Integer>> combinationSum(int[] candidates, int target) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(candidates == null || candidates.length==0)
return res;
Arrays.sort(candidates);
helper(candidates,0,target,new ArrayList<Integer>(),res);
return res;
}
private void helper(int[] candidates, int start, int target, ArrayList<Integer> item,
ArrayList<ArrayList<Integer>> res)
{
if(target<0)
return;
if(target==0)
{
res.add(new ArrayList<Integer>(item));
return;
}
for(int i=start;i<candidates.length;i++)
{
if(i>0 && candidates[i]==candidates[i-1])
continue;
item.add(candidates[i]);
helper(candidates,i,target-candidates[i],item,res);
item.remove(item.size()-1);
}
}
*注意在實現中for迴圈中第一步有一個判斷,那個是為了去除重複元素產生重複結果的影響,
因為在這裡每個數可以重複使用,所以重複的元素也就沒有作用了,所以應該跳過那層遞迴。
【Leetcode-Medium-46】 Permutations 全排列
置換實際上是給出所有的排列方式,同樣是用深度優先搜尋,不過為了避免重複選擇的情況,我們要保證兩點:第一,所有數必須是陣列中的,第二,陣列中每個數只能用不多於也不少於一次。如果我們要單獨寫一個函式,來判斷下一輪搜尋該選擇哪一個數就很麻煩了。這裡有一個技巧,我們可以只將數兩兩交換,不過交換時只能跟自己後面的交換。
public class Solution {
List<List<Integer>> res;
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
res = new LinkedList<List<Integer>>();
used = new boolean[nums.length];
List<Integer> tmp = new LinkedList<Integer>();
helper(nums, tmp);
return res;
}
private void helper(int[] nums, List<Integer> tmp){
if(tmp.size() == nums.length){
List<Integer> list = new LinkedList<Integer>(tmp);
res.add(list);
} else {
for(int idx = 0; idx < nums.length; idx++){
// 遇到已經加過的元素就跳過
if(used[idx]){
continue;
}
// 加入該元素後繼續搜尋
used[idx] = true;
tmp.add(nums[idx]);
helper(nums, tmp);
tmp.remove(tmp.size()-1);
used[idx] = false;
}
}
}
}
【Leetcode-Easy-53】 Maximum Subarray 最大連續子陣列的和
思路:定義兩個變數res和curSum,其中res儲存最終要返回的結果,即最大的子陣列之和.
curSum初始值為0,每遍歷一個數字num,比較curSum + num和num中的較大值存入curSum,
然後再把res和curSum中的較大值存入res,以此類推直到遍歷完整個陣列,可得到最大子陣列的值存在res
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
int curSum = 0;
for (int num : nums){
curSum = Math.max(num, num + curSum);
res = Math.max(res, curSum);
}
return res;
}
}
【Leetcode-Easy-70】 Climbing Stairs 爬樓梯,n階1步2步
思路:斐波拉契,第n階只與第 n - 1 階和第 n - 2 階有關,關係為ways[n] = ways[n - 1] + ways[n - 2]
// one
public int climbStairs(int n) {
if (n <= 2) return n;
int result = 0;
int first = 1;
int second = 2;
for (int i = 3; i <= n; i ++) {
result = first + second;
int temp = first;
first = second;
second = second + temp;
}
return result;
}
// two
public int climbStairs(int n) {
if (n <= 1) return n;
int[] dp = new int[n];
dp[0] = 1;
dp[1] = 2;
for (int i = 2; i < n; ++i) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n - 1];
}
// three
public int climbStairs(int n) {
if (n <= 1) return n;
int oneStep = 1; // 走一步的可能
int twoStep = 1; // 走兩步的可能
int res = 0;
for (int i = 2; i <= n; i ++) {
res = oneStep + twoStep;
twoStep = oneStep;
oneStep = res;
}
return res;
}
【Leetcode-Medium-94】 Binary Tree Inorder Traversal 二叉樹的中序遍歷
思路:
- 遞迴方式:對左子結點呼叫遞迴函式,根節點訪問值,右子節點再呼叫遞迴函式
- 迭代方式:使用棧的解法,也是符合本題要求使用的解法之一,需要用棧來做,思路是從根節點開始,先將根節點壓入棧,然後再將其所有左子結點壓入棧,然後取出棧頂節點,儲存節點值,再將當前指標移到其右子節點上,若存在右子節點,則在下次迴圈時又可將其所有左子結點壓入棧中。這樣就保證了訪問順序為左-根-右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
// ----------------------------------------
// 迭代方式
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> res = new ArrayList<>();
if (root == null) return res;
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode currNode = root;
while (currNode != null || !stack.isEmpty()){
while (currNode != null){
stack.push(currNode);
currNode = currNode.left;
}
if (!stack.isEmpty()){
currNode = stack.pop();
res.add(currNode.val);
currNode = currNode.right;
}
}
return res;
}
// --------------------------------------
// 遞迴方式
public List<Integer> inorderTraversal0(TreeNode root) {
List<Integer> res = new ArrayList<>();
helper(root, res);
return res;
}
private void helper2(TreeNode root, List<Integer> res){
if (root == null) return;
helper(root.left, res);
res.add(root.val);
helper2(root.right, res);
}
}
第101-200題
【Leetcode-Easy-101】 Symmetric Tree 判斷對稱樹
思路:判斷二叉樹是否是對稱樹,比如有兩個節點n1, n2,我們需要比較n1的左子節點的值和n2的右子節點的值是否相等,同時還要比較n1的右子節點的值和n2的左子結點的值是否相等,
以此類推比較完所有的左右兩個節點。我們可以用遞迴和迭代兩種方法來實現,寫法不同,但是演算法核心都一樣。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
// 遞迴
public boolean isSymmetric(TreeNode root) {
if (root == null) return true;
return isSymmetric(root.left, root.right);
}
private boolean isSymmetric(TreeNode root1, TreeNode root2){
if (root1 == null && root2 == null) return true;
if (root1 == null || root2 == null) return false; // 兩者只有一者為null,則返回false
if (root1.val != root2.val) return false; // 兩者均不為null,但兩者的值不相等
return isSymmetric(root1.left, root2.right) && isSymmetric(root1.right, root2.left);
}
}
【Leetcode-easy-104】 Maximum Depth of Binary Tree 二叉樹的最大深度
思路:
- 遞迴:深度優先搜尋DFS,遞迴的完美應用,跟求二叉樹的最小深度問題原理相同
層次遍歷
/**
* Definition