作了又忘,忘了又作,怎么刷都是学不会啊啊啊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(); } }