作了又忘,忘了又作,怎麼刷都是學不會啊啊啊java
從每行每列都是遞增的二維數組中找是否存在某數node
public class Solution { public boolean Find(int target, int[][] array) { int rows = array.length; int cols = array[0].length; int i = rows - 1; int j = 0; // 從左下角,或右上角掃描 while(i >= 0 && j < cols){ if(target < array[i][j]){ i--; }else if(target > array[i][j]){ j++; }else{ return true; } } return false; } }
字符串替換算法
// 內部函數 public class Solution { public String replaceSpace(StringBuffer str) { return str.toString().replaceAll(" ", "%20"); } }
// 效率也沒差 public class Solution { public String replaceSpace(StringBuffer str) { StringBuffer sb = new StringBuffer(str.length()); // 避免屢次擴容 System.arraycopy for(int i = 0; i < str.length(); i++){ if(str.charAt(i) == ' '){ sb.append("%20"); }else{ sb.append(str.charAt(i)); } } return sb.toString(); } }
// 考察優化 // 從前向後計算空格數 // 從後向前替換,這樣移動的次數相對少了 public class Solution { public String replaceSpace(StringBuffer str) { int spaceNum = 0; // 算出空格總數 for(int i = 0; i < str.length(); i++){ if(str.charAt(i) == ' '){ spaceNum++; } } if(spaceNum == 0) return str.toString(); // 沒有空格直接返回 int oldLength = str.length(); int newLength = str.length() + spaceNum * 2; // 設置新長度 str.setLength(newLength); newLength--; // Java中沒有 "\0"結尾字符,因此實際大小減一 for(int i = oldLength-1; i >= 0; i--){ if(str.charAt(i) == ' '){ str.setCharAt(newLength--, '0'); str.setCharAt(newLength--, '2'); str.setCharAt(newLength--, '%'); }else{ str.setCharAt(newLength--, str.charAt(i)); } } return str.toString(); } }
從尾到頭遍歷鏈表數組
// 遞歸 public class Solution { ArrayList list = new ArrayList(); public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { if(listNode != null){ printListFromTailToHead(listNode.next); list.add(listNode.val); } return list; } }
// 非遞歸 // 模擬棧,ArrayList(index,val)也能夠模擬棧,remove(index),LinkedList?? // ArrayList的頭插法 public class Solution { public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { ArrayList list = new ArrayList(); while(listNode != null){ list.add(0,listNode.val); listNode = listNode.next; } return list; } }
重建二叉樹(前序,中序)數據結構
// 通常樹都是遞歸操做,用索引 public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { return reConBTree(pre,0,pre.length-1,in,0,in.length-1); // 傳入實際長度 } public TreeNode reConBTree(int[] pre,int pleft,int pright,int[] in,int inleft,int inright){ if(pleft > pright || inleft > inright) return null; // 遞歸出口 TreeNode root = new TreeNode(pre[pleft]); // 父節點能夠肯定的 for(int i = inleft; i <= inright; i++){ // 遍歷尋找根節點 if(pre[pleft] == in[i]){ // in的要排除根節點+-一、 root.left = reConBTree(pre,pleft+1,pleft+(i-inleft),in,inleft,i-1); root.right = reConBTree(pre,pleft+1+(i-inleft),pright,in,i+1,inright); break; } } return root; // 返回父節點 } }
// Arrays.copyOfRange(arr,from,to),注意包括上標,不包括下標 // Arrays.copyOf(arr,newLength),新長度大於就長度就填充默認值 import java.util.*; public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { if(pre.length == 0 || in.length == 0) return null; // 條件判斷 TreeNode root = new TreeNode(pre[0]); // 建立根節點 for(int i = 0; i < in.length; i++){ if(pre[0] == in[i]){ root.left = reConstructBinaryTree (Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(in, 0, i)); root.right = reConstructBinaryTree (Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(in, i+1,in.length)); } } return root; } }
兩個棧模擬隊列,先清空棧2才進入元素,先清空棧1才彈出元素app
import java.util.Stack; public class Solution { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); public void push(int node) { while(!stack2.isEmpty()){ stack1.push(stack2.pop()); } stack1.push(node); } public int pop() { while(!stack1.isEmpty()){ stack2.push(stack1.pop()); } return stack2.pop(); } }
旋轉數組的最小數字函數
// 非遞減,即遞增有重複 // 用二分法:最後low指向分界點前一個,high指向分界點後一個,兩者相鄰,返回low++ // 第二個指針將指向最小元素 import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if(array.length == 0) return 0; int low = 0; int high = array.length - 1; int mid = 0; while(low < high){ mid = low + (high-low)/2; if(array[low] < array[mid]){ low = mid; }else if(array[mid] < array[high]){ high = mid; }else { // 重複元素沒法判斷是分界先後,只能順序找 low++; // 且最後也使指針指向分界的後一個 } } return array[low]; } }
斐波那契數列優化
// 迭代法 public class Solution { public int Fibonacci(int n) { if(n == 0 || n == 1) return n; int a = 0; int b = 1; int c = 0; while(n > 1){ // 第一項1,上面已經給出了 c = a + b; a = b; b = c; n--; } return c; } }
// 遞歸 public class Solution { public int Fibonacci(int n) { if(n == 0 || n == 1) return n; return Fibonacci(n-1) + Fibonacci(n-2); } }
跳臺階:注重思想,就是斐波那契數列spa
迭代沒什麼好說的指針
遞歸:只剩最後一步時,要麼是跳1階,要麼是跳2階
// 設n個臺階有f(n)種走法 // 只剩最後一步時,要麼是跳1階,要麼是跳2階 // 最後一步跳1階,即以前有n-1個臺階,據前面的假設,即n-1個臺階有f(n-1)種走法 // 最後一步跳2階,即以前有n-2個臺階,據前面的假設,即n-2個臺階有f(n-2)種走法 // 總結規律:n個臺階的走法等於前兩種狀況的走法之和即 f(n) = f(n-1)+f(n-2) // 變相斐波那契數列 // 注意和上面那題起點不同 public class Solution { public int JumpFloor(int target) { if(target == 1 || target == 2) return target; return JumpFloor(target - 1) + JumpFloor(target - 2); } }
跳臺階II
// n級臺階,第一步有n種跳法:跳1級、跳2級、……、到跳n級 // 跳1級,剩下n-1級,則剩下跳法是f(n-1) // 跳2級,剩下n-2級,則剩下跳法是f(n-2) // 因此f(n) = f(n-1)+f(n-2)+...+f(1) // 由於f(n-1) = f(n-2)+f(n-3)+...+f(1) // 因此f(n) = 2*f(n-1) public class Solution { public int JumpFloorII(int target) { int temp = 1; while(target > 1){ // 第一個已給出{ temp *= 2; target--; } return temp; } }
矩形覆蓋
// 逆序思想:最後一步有兩種狀況 // OO OO // XX OO // XX XX // .. .. // XX XX // 第一種狀況:陰影部分的n-1塊矩形有多少種覆蓋方法,爲f(n-1); // 第二種狀況:陰影部分的n-2塊矩形有多少種覆蓋方法,爲f(n-2); // 故f(n) = f(n-1) + f(n-2),仍是一個斐波那契數列 // 斐波那契數列就是注意起點問題 public class Solution { public int RectCover(int target) { if(target == 0) return 0; if(target == 1) return 1; int a = 1,b = 1,c = 0; while(target > 1){ // 第一項1,上面已經給出了 c = a + b; a = b; b = c; target--; } return c; } }
求整數的二進制中1的個數
// 逐位比較 public class Solution { public int NumberOf1(int n) { int cnt = 0; while(n != 0){ // 不是大於0,由於有負數 cnt += n & 1; n >>>= 1; } return cnt; } }
// 最優解 // 一個整數減去1,再和原整數作與運算,會把該整數最右邊一個1變成0 // 那麼一個整數的二進制有多少個1,就能夠進行多少次這樣的操做 public class Solution { public int NumberOf1(int n) { int cnt = 0; while(n != 0){ n = n & (n-1); cnt++; } return cnt; } }
// 做弊解 // 計算該數的二進制,而後返回二進制中`1`的個數 public class Solution { public int NumberOf1(int n) { return Integer.bitCount(n); } }
數值的整數次方
// 連乘思路:O(n) public class Solution { public double Power(double base, int exponent) { double rs = 1; for(int i = 0; i < Math.abs(exponent); i++){ rs *= base; // 連乘 } if(exponent < 0){ rs = 1 / rs; // 負數次冪,直接倒數 } return rs; } }
// 快速冪:log(n) // 11可轉化爲二進制次冪:11 = 1011 = 2³×1 + 2²×0 + 2¹×1 + 2º×1 = 2³×1 + 2¹×1 + 2º×1 // base *= base 保持累乘的做用:base-->base2-->base4-->base8-->base16 // 那麼化簡後:a¹¹ = a^(2º+2¹+2³) = a^(1+2+8) // 那麼 a¹¹ = a¹ * a² * a^8 = base * base^2 * base^8 public class Solution { public double Power(double base, int exponent) { double rs = 1; // 保存結果 int power = Math.abs(exponent); // 冪的絕對值 // 快速冪核心 while(power != 0){ if( (power & 1) == 1 ){ // 冪的二進制當前位爲1即有效,結果相乘 rs *= base; } power >>>= 1; // 冪右移 base *= base; // 保持累乘,後面利用 } if(exponent < 0){ // 負次冪,結果取倒數 rs = 1 / rs; } return rs; } }
調整數組順序,奇數位於偶數前面,且相對順序不變(穩定性)
// 分治,思路明瞭 import java.util.ArrayList; public class Solution { public void reOrderArray(int [] array) { ArrayList<Integer> list = new ArrayList(); for(int i = 0; i < array.length; i++){ if(array[i] % 2 != 0){ list.add(array[i]); } } for(int i = 0; i < array.length; i++){ if(array[i] % 2 == 0){ list.add(array[i]); } } for(int i = 0; i < array.length; i++){ array[i] = (Integer)list.get(i); } } }
// 相對位置不變:保持穩定性便可,冒泡、直接插入等 // 相似冒泡算法,前偶後奇數就交換:從後往前 public class Solution { public void reOrderArray(int [] array) { for(int i = 0; i < array.length; i++){ for(int j = array.length-1; j > i; j--){ if(array[j] % 2 == 1 && array[j-1] % 2 == 0){ // 總體奇數前移 int temp = array[j]; array[j] = array[j-1]; array[j-1] = temp; } } } } }
// 就地算法,不借助輔助,原地修改數據結構 // i 前面的奇數都排好了 public class Solution { public void reOrderArray(int [] array) { int temp; int i = 0; for(int j = 0; j < array.length; j++){ if(array[j] % 2 != 0){ // j遇到奇數,非奇後移 temp = array[j]; // 保存移動覆蓋的奇數 for(int k = j - 1; k >= i; k--){ // i到j的偶數後移一位 array[k+1] = array[k]; } array[i] = temp; // 奇數往前跳動 i++; // i指向奇數排好的下一個 } } } }
返回鏈表倒數第K個節點
// 設置快慢指針 public class Solution { public ListNode FindKthToTail(ListNode head,int k) { ListNode node,pre; node = pre = head; for(int i = 1; i <= k; i++){ // 先驅走k步 if(pre == null) return null; // 鏈表長沒有k步長 pre = pre.next; } while(pre != null){ // 同步走 pre = pre.next; node = node.next; } return node; } }
反轉鏈表
// 須要三個指針 public class Solution { public ListNode ReverseList(ListNode head) { ListNode pre,next; // 若是head爲空,那麼next.next就空指針異常 pre = next = null; // 因此只能在head不爲空的循環內next了 while(head != null){ // head表示當前節點 next = head.next; // 上面說的next,這個要先寫 head.next = pre; pre = head; head = next; } return pre; // 這裏注意:上面的循環結束條件爲head爲空,那麼前驅節點纔是真正的頭節點 } }
合併兩個鏈表,而且按大小排序
public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { ListNode head,node; // 要有一個表頭head來返回,node保存節點,防止斷鏈 head = node = new ListNode(0); // 這裏竟然這樣 while(list1 != null && list2 != null){ if(list1.val < list2.val){ node.next = list1; node = node.next; // 指針移動 list1 = list1.next; }else{ node.next = list2; node = node.next; list2 = list2.next; } } if(list1 == null) node.next = list2; if(list2 == null) node.next = list1; return head.next; } }
判斷是否某樹的子結構(兩個遞歸,一個遍歷樹,一個匹配)
// 思路:先序遍歷父樹,每次遍歷都用來匹配 public class Solution { public boolean HasSubtree(TreeNode root1,TreeNode root2) { // 遞歸出口:包含了先序遍歷的 root != null if(root1 == null || root2 == null) return false; // 先序遍歷:當前節點,而後左子樹,最後右子樹 return recur(root1,root2) || HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2); } private boolean recur(TreeNode root1,TreeNode root2){ if(root2 == null) return true; // 可先判斷root2爲空,即完成遍歷匹配 if(root1 == null) return false; // 則root1先被擊穿 if(root1.val != root2.val) return false;// 當前節點不匹配 return recur(root1.left,root2.left) && recur(root1.right,root2.right); } }
轉變成二叉樹的鏡像
// 遞歸:也是前序遍歷 public class Solution { public void Mirror(TreeNode root) { if(root == null) return ; // 遞歸出口 swap(root); // 交換根節點的左右孩子 Mirror(root.left); // 孩子的孩子也交換 Mirror(root.right); // 孩子的孩子也交換 } private void swap(TreeNode root){ TreeNode temp = root.left; root.left = root.right; root.right = temp; } }
螺旋打印矩陣
// 上下左右,四個邊界分別爲l,r,t,b // 思路1:按題意打印 import java.util.ArrayList; public class Solution { public ArrayList<Integer> printMatrix(int [][] matrix) { ArrayList<Integer> list = new ArrayList(); int left = 0, right = matrix[0].length - 1; // 四個邊界,-1方便後面用等於判斷 int top = 0, bottom = matrix.length - 1; while(true){ // 無限循環遍歷,出口在內部的邊界判斷 for(int i = left; i <= right; i++) list.add(matrix[top][i]); top++; // 遍歷完一行,判斷上下是否遍歷完 if(top > bottom) break; for(int i = top; i <= bottom; i++) list.add(matrix[i][right]); right--; if(left > right) break; for(int i = right; i >= left; i--) list.add(matrix[bottom][i]); bottom--; if(top > bottom) break; for(int i = bottom; i >= top; i--) list.add(matrix[i][left]); left++; if(left > right) break; } return list; } }
用兩個棧求最小元素----O(1)
import java.util.Stack; public class Solution { Stack<Integer> data = new Stack(); Stack<Integer> min = new Stack(); public void push(int node) { data.push(node); if(min.isEmpty() || node < min.peek()){ min.push(node); } } public void pop() { if(data.peek() == min.peek()){ min.pop(); } data.pop(); } public int top() { return data.peek(); } public int min() { return min.peek(); } }