public long fibonacci(int n){ int[] res = {0, 1}; if(n < 2){ return res[n]; } long fibNMinusOne = 1; long fibNMinusTwo = 0; long fibN = 0; for(int i = 2; i < n; i++){ fibN = fibNMinusOne + fibNMinusTwo; fibNMinusTwo = fibNMinusOne; fibNMinusOne = fibN; } return fibN; }
public class Solution{ public int minDepth(TreeNode root) { if(root == null){ return 0; } return getMin(root); } public int getMin(TreeNode root){ if(root == null){ return Integer.MAX_VALUE; } if(root.left == null && root.right == null){ return 1; } return Math.min(getMin(root.left), getMin(root.right)) + 1; } }
public class Solution{ public int minDepth(TreeNode root){ if(root == null){ return 0; } Queue<TreeNode> queue = new LinkedList<>(); Queue<TreeNode> counts = new LinkedList<>(); queue.add(root); counts.add(1); while(!queue.isEmpty()){ TreeNode cur = queue.remove(); int count = counts.remove(); if(cur.left != null){ queue.add(cur.left); counts.add(count + 1); } if(cur.right != null){ queue.add(cur.right); counts.add(count + 1); } if(cur.left == null && cur.right == null){ return count; } } return 0; } }
簡單題須要注意的是細節和邊界條件,本題須要注意:html
Integer.MIN_VALUE比Integer.MAX_VALUE的絕對值大1git
符號問題數組
翻轉後整型越界問題dom
public int reverse(int x) { if(x == Integer.MIN_VALUE){ return 0; } int n = Math.abs(x); int res = 0; while(n != 0){ // 翻轉後整數越界 Integer.MAX_VALUE=2147483647 if(res > (Integer.MAX_VALUE - n % 10) / 10){ return 0; } res = res * 10 + n % 10; n /= 10; } return x > 0 ? res : -res; }
public boolean hasCycle(ListNode head) { if(head == null || head.next == null){ return false; } ListNode fast = head; ListNode slow = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(fast == slow){ return true; } } return false; }
slow到起點後,通過n-x步相遇spa
設相遇點到圈起點距離是b3d
slow走的距離是a + b指針
fast走的距離是a + b + k n = 2 (a + b),從而a + b = k * n,k爲常數code
如何找圈的起點?
把slow拉回起點,fast從相遇點繼續走,每次走一步。a步後,slow到圈起點,fast恰好也到圈起點!htm
如何找圈長?
相遇後,fast再走一圈,並統計長度就是圈長blog
public ListNode detectCycle(ListNode head) { if(head == null || head.next == null){ return null; } ListNode fast = head; ListNode slow = head; while(true){ if(fast == null || fast.next == null){ return null; } fast = fast.next.next; slow = slow.next; if(fast == slow){ break; } } fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; }
總結:當while(fast != null || fast.next != null)轉成本題的條件時,變成
while(true){ if(fast == null || fast.next == null){ //走你 } }
相交的兩鏈表第一個交點,若是有環,此法失敗。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) { int lengthA = 0; int lengthB = 0; ListNode p = headA; ListNode q = headB; while(p != null){ p = p.next; lengthA++; } while(q != null){ q = q.next; lengthB++; } if(q != p){ return null; } p = headA; q = headB; if(lengthA > lengthB){ int delta = lengthA - lengthB; while(delta > 0){ p = p.next; delta--; } while(q != p){ p = p.next; q = q.next; } return q; }else{ int delta = lengthB - lengthA; while(delta > 0){ q = q.next; delta--; } while(q != p){ p = p.next; q = q.next; } return q; } }
難點,並不知道新鏈表的random指針指向何處 把大象裝冰箱總共分幾步? 1. 在舊鏈表的每一個結點後面,複製一個結點複本 2. 複製random域,爲何要在每一個結點後面複製複本,就在於,
a.next.random = a.random.next
舊random指向的地址的next,指向的就是新random的地址! 3. 拆分兩個鏈表
private void copyNext(RandomListNode head){ while(head != null){ RandomListNode newNode = new RandomListNode(head.label); newNode.next = head.next; head.next = newNode; newNode.random = head.random; head = head.next.next; } } private void copyRandom(RandomListNode head){ while(head != null){ if(head.next.random != null){ head.next.random = head.random.next; } head = head.next.next; } } private RandomListNode splitList(RandomListNode head){ RandomListNode newHead = head.next; while(head != null){ RandomListNode temp = head.next; head.next = temp.next; if(temp.next != null){ temp.next = temp.next.next; } head = head.next; } return newHead; } public RandomListNode copyRandomList(RandomListNode head) { if(head == null){ return null; } copyNext(head); copyRandom(head); return splitList(head); }
總結:1. 新random地址如何經過複製結點傳遞 2. while()條件,搞清楚明白。
copyNext中while的循環條件
一次循環中:
newNode確定不爲null,由於是new出來的
head.next指向newNode,所以,head.next不爲null
newNode.random可能爲null,可是不影響循環
head = head.next.next可能爲null,所以,循環條件應該爲head != null
copyRandom中while的循環條件
一次循環中:
此時,copyNext已經完成,鏈表的結點數確定爲偶數,head.next不可能爲null
head = head.next.next可能爲null,所以循環條件應該爲head != null
splitList中while的循環條件
一次循環中:
RandomListNode temp = head.next此時不會爲null
temp.next 多是尾結點,因此可能爲null
若是不是尾結點,temp.next = temp.next.next必定不爲null
head = head.next,head可能已經指向了尾結點,因此可能爲null
循環條件應該爲head != null
鏈表的partition比數組更有優點,能夠直接新建鏈表
//x 爲pivot值 public ListNode partition(ListNode head, int x) { ListNode h1 = null; ListNode t1 = null; ListNode h2 = null; ListNode t2 = null; while(head != null){ if(head.val < x){ if(h1 == null){ h1 = head; t1 = head; }else{ t1.next = head; t1 = t1.next; } }else{ if(h2 == null){ h2 = head; t2 = head; }else{ t2.next = head; t2 = t2.next; } } head = head.next; } if(t1 != null){ t1.next = h2; } if(t2 != null){ t2.next = null; } return h1 != null ? h1 : h2; }
鏈表題總結
細緻---多寫多練習
哪些指針要修改
修改鏈表前保存(防止鏈表斷掉)
注意空指針
特色:能夠從新創建表頭
翻轉
Partition
注意:第一個元素,表尾
小窗移動的過程:1. 用i來控制小窗的左邊界,條件map[charAt[j]]==0控制右邊界 2. 若是發現map[charAt[j]] == 1,將左邊界右移,j先保持不動,走到條件map[charAt[j]]==0再次知足,j開始右移 3. answer經過 answer = Math.max(answer,j - i + 1)來控制,很是妙
public int lengthOfLongestSubstring(String s) { int answer = 0; int i = 0; int j = 0; int map = new int[256]; for(i = 0; i < s.length(); i++){ while(j < s.length && map[s.charAt(j)] == 0){ map[s.charAt(j)] = 1; answer = Math.max(answer, j - i + 1); j++; } map[s.charAt(i)] = 0; } return answer; }
本題參考解法 : https://siddontang.gitbooks.i...
這題要求在一個輪轉了的排序數組裏面找到最小值,咱們能夠用二分法來作。
首先咱們須要知道,對於一個區間A,若是A[start] < A[stop],那麼該區間必定是有序的了。
假設在一個輪轉的排序數組A,咱們首先獲取中間元素的值,A[mid],mid = (start + stop) / 2。由於數組沒有重複元素,那麼就有兩種狀況:
A[mid] > A[start],那麼最小值必定在右半區間,譬如[4,5,6,7,0,1,2],中間元素爲7,7 > 4,最小元素必定在[7,0,1,2]這邊,因而咱們繼續在這個區間查找。
A[mid] < A[start],那麼最小值必定在左半區間,譬如[7,0,1,2,4,5,6],這件元素爲2,2 < 7,咱們繼續在[7,0,1,2]這個區間查找。
須要注意給定的數組可能並無翻轉
public int findMin(int[] nums) { if(nums == null || nums.length == 0){ return Integer.MIN_VALUE; } int low = 0; int high = nums.length - 1; //nums[low] < nums[high]若是已經有序了,那麼退出循環,題目說明,數組中沒有重複數字 while(low < high && nums[low] > nums[high]){ int mid = (low + high) / 2; if(nums[mid] > nums[high]){ low = mid + 1; }else{ high = mid; } } return nums[low]; }
與上題的區別在於,數組中的無序可能重複
public int findMin(int[] nums) { if(nums == null || nums.length == 0){ return Integer.MIN_VALUE; } int low = 0; int high = nums.length - 1; while(low < high && nums[low] > nums[high]){ int mid = (low + high) / 2; if(nums[mid] > nums[high]){ low = mid + 1; }else if(nums[mid] < nums[high]){ high = mid; }else{ low++; } } return nums[low]; }
public int maxArea(int[] height) { int n = height.length; int best = 0; for(int i = 0, j = n - 1; i < j;){ best = Math.max(Math.min(height[i], height[j]) * (j - i), best); if(height[i] < height[j]){ i++; }else{ j--; } } return best; }
時間 O(n) 空間 O(n)
public boolean isPalindrome(ListNode head) { if(head == null || head.next == null){ return true; } ListNode cur = new ListNode(head.val); ListNode newHead = null; ListNode old = head; while(old != null){ ListNode preCur = new ListNode(old.val); old = old.next; preCur.next = newHead; newHead = preCur; } while(newHead != null){ if(newHead.val != head.val){ return false; } newHead = newHead.next; head = head.next; } return true; }