重建二叉树 前序遍历用来确定根节点,中序遍历用来确定左右子树以及大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class ReconsBinTree { public static void main (String[] args) { int [] pre={1 ,2 ,4 ,7 ,3 ,5 ,6 ,8 }; int [] in = {4 ,7 ,2 ,1 ,5 ,3 ,6 ,8 }; TreeNode root = reConstructBinaryTree(pre,in); lastOrder(root); } public static TreeNode reConstructBinaryTree (int [] pre, int [] in) { TreeNode root = reconstruct(pre[0 ],pre,0 ,pre.length,in,0 ,in.length); return root; } public static TreeNode reconstruct (int rootVal,int [] pre,int preStart, int preEnd, int [] in, int inStart, int inEnd) { TreeNode root = new TreeNode (rootVal); int rootIdxAtPre = getIndex(pre,preStart,preEnd,root.val) ; int rootIdxAtIn = getIndex(in,inStart,inEnd,root.val); if (rootIdxAtIn>0 ){ int lidx = rootIdxAtPre+1 ; root.left = reconstruct(pre[preStart+lidx],pre,preStart+1 ,preStart+1 +rootIdxAtIn,in,inStart,inStart+rootIdxAtIn); } else root.left=null ; if (inStart+rootIdxAtIn<inEnd-1 ){ int ridx = rootIdxAtIn+1 ; root.right = reconstruct(pre[preStart+ridx], pre,preStart+rootIdxAtIn+1 ,preEnd,in,inStart+rootIdxAtIn+1 ,inEnd); } else root.right=null ; return root; } public static int getIndex (int [] arr, int start, int end, int val) { for (int i=start; i<end; i++){ if (arr[i]==val) return i-start; } return -1 ; } public static void lastOrder (TreeNode root) { if (root!=null ) { if (root.left != null ) lastOrder(root.left); if (root.right != null ) lastOrder(root.right); } System.out.println(root.val); } }
用两个栈实现队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import java.util.Stack;public class Stack2Q { static Stack<Integer> stack1 = new Stack <Integer>(); static Stack<Integer> stack2 = new Stack <Integer>(); public static void push (int node) { stack1.push(node) ; } public static int pop () { int node ; if (!stack2.isEmpty()){ node = stack2.pop(); } else { while (!stack1.isEmpty()){ int tnode = stack1.pop(); stack2.push(tnode); } node = stack2.pop(); } return node ; } public static void main (String[] args) { push(1 ); push(2 ); push(3 ); push(4 ); System.out.println(pop()); System.out.println(pop()); } }
旋转数组的最小数字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class MinNumInRotateArray { public static int minNumberInRotateArray (int [] array) { int min = binSearch(array,0 ,array.length-1 ); return min; } static int binSearch (int [] arr, int start, int end) { int res; if (end-start<=0 ){ res = arr[start]; return res; } int mid = (start+end)/2 ; if (arr[mid]>=arr[start] && arr[mid] >=arr[end] ){ res = binSearch(arr,mid+1 ,end); } else { res = binSearch(arr,start,mid); } return res; } public static void main (String[] args) { int [] array={3 ,4 ,5 ,1 ,2 }; System.out.println(minNumberInRotateArray(array)); } }
跳台阶 一道典型的动态规划问题,对于跳上第n个台阶,只有两种跳法:
从第n-1个台阶跳一步上去
从第n-2个台阶跳两步上去
所以我们有
$$ f(n)=\left{ \begin{aligned} &1 & n=1 \ &2 & n=2 \ &f(n-1)+f(n-2) & n \ge 3 \end{aligned} \right.$$
其实就是个Fibonaci
数列,可以用递归求解,但是太麻烦,我们用DP来求解
数组版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class JumpFloor {public static int JumpFloor (int target) { if (target==1 ){ return 1 ; } if (target==2 ){ return 2 ; } int [] dp = new int [target+1 ]; dp[1 ]=1 ; dp[2 ]=2 ; for (int i=3 ; i<=target; i++){ dp[i]=dp[i-1 ]+dp[i-2 ]; } return dp[target]; } public static void main (String[] args) { System.out.println(JumpFloor(3 )); System.out.println(JumpFloor(4 )); } }
精简版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class JumpFloor { public static int JumpFloor (int target) { if (target==1 ){ return 1 ; } if (target==2 ){ return 2 ; } int pre=1 ,cur=2 ,res=0 ; for (int i=3 ; i<=target; i++){ res = pre+cur; pre=cur; cur=res; } return res; } public static void main (String[] args) { System.out.println(JumpFloor(3 )); System.out.println(JumpFloor(4 )); } }
变态跳台阶 题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
解析:
同样的,先写出递归方程,然后用递归或者DP来求解。
相比于上面那一道普通跳台阶,这里只不过是能跳的台阶多了一些。
矩形覆盖 题目描述 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
解析 同样的,先写状态转移方程:
$$ f(n)=\left{ \begin{aligned} &1 & n=1 \ &2 & n=2 \ &f(n-1)+f(n-2) & n \ge 3 \end{aligned} \right.$$
依然是个斐波那契数列问题
可以用递归和DP解决
其实讲道理,我觉得这道题也可以用排列组合来求解的
代码 1 2 3 4 5 6 7 public int RectCover (int target) { if (target == 1 ) return 1 ; if (target == 2 ) return 2 ; return RectCover(target - 1 ) + RectCover(target - 2 ); }
DP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int RectCover (int target) { int sum,p1,p2 ; if (target <=0 ) return 0 ; if (target == 1 ) return 1 ; if (target == 2 ) return 2 ; p1=1 ; p2=2 ; sum=0 ; for (int i = 3 ; i<=target; i++){ sum = p1 +p2 ; p1 = p2 ; p2 = sum ; } return sum; }
二进制中1的个数 题目描述 输入一个整数n,输出该数二进制表示中1的个数。其中负数用补码表示。
解析 方法一: 无符号右移输入的数字,然后和1逻辑与
方法二:保持输入不动,每次右移数字1然后逻辑与
方法三:将n和n-1逻辑与,消除一个n最右边的数字1
代码 方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class NumberOf1 { public int NumberOf1 (int n) { int count = 0 ; while (n!=0 ){ count += (n&1 ) ; n = n>>>1 ; } return count; } public static void main (String[] args) { System.out.println(new NumberOf1 ().NumberOf1(0x80000000 )); } }
数值的整数次方 题目描述 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
解析
方法一:for循环暴力输出,需要考虑到指数为负数等边界情况
方法二:使用递归将指数拆解进行计算。
调整数组顺序使奇数位于偶数前面 题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解析 这个问题使用冒泡排序的思想也可以解决,相邻元素比较并交换,问题在于这样做的话无谓的比较和交换比较多。比如说有一长串都是奇数或者偶数,这种情况我们就可以跳过了,其实是有点类似与KMP算法的。
所以我们需要两个指针,一个指向当前出现的第一个偶数,一个指向第一个偶数出现后的第一个奇数,然后奇数放在偶数位,偶数们依次后移。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public void reOrderArray (int [] array) { int feven=0 ; int fodd=1 ; int i=0 ; if (array==null || array.length==1 ) return ; while (fodd<=array.length){ for (; feven<array.length; feven++){ if (isEven(array[feven])){ break ; } } for (fodd=feven+1 ; fodd<array.length; fodd++){ if (!isEven(array[fodd])){ break ; } } if (fodd>=array.length) break ; int t = array[fodd] ; for (int j = fodd-1 ; j>=feven; j--){ array[j+1 ] = array[j]; } array[feven] = t ; } } public boolean isEven (int e) { return e%2 ==0 ; }
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Solution { public void reOrderArray (int [] array) { for (int i= 0 ;i<array.length-1 ;i++){ for (int j=0 ;j<array.length-1 -i;j++){ if (array[j]%2 ==0 &&array[j+1 ]%2 ==1 ){ int t = array[j]; array[j]=array[j+1 ]; array[j+1 ]=t; } } } } }
很类似于冒泡排序,每一轮冒泡保证至少有一个元素归位。
其实就是个冒泡排序,只不过我们常见的冒泡排序里面比较的是元素大小,这里比较的是元素奇偶,还有很重要的一点就是冒泡排序是稳定的,对应到题目中就是保证奇数和奇数,偶数和偶数之间的相对位置不变 。
这里相当于是一个通用性的冒泡排序,至于按照什么条件进行相邻元素比较是可插拔的,实在是妙啊。
所以理论上讲,这道题只要是稳定的排序算法应该是都可以用的。
那哪些排序稳定哪些排序不稳定呢?
一言以蔽之:涉及到非相邻交换的排序都是不稳定的,比如选择排序,比如快速排序,希尔排序,比如堆排序。
链表中倒数第k个结点 题目描述 输入一个链表,输出该链表中倒数第k个结点。
分析 用标尺法就可以解决。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Solution { public ListNode FindKthToTail (ListNode head,int k) { ListNode cur = head ; if (head == null ){ return null ; } if (k==0 ){ return null ; } ListNode post = cur.next ; int i = 1 ; while (post != null && i<k){ post = post.next ; i++; } if (i<k){ return null ; } while (post !=null ){ cur = cur.next; post = post.next ; } return cur ; } }
树的子结构 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
问题分析 学会把问题分解。对于这个问题,分成两个子问题:1. 遍历A树,看当前节点和B树的根节点是否相等,如果相等,继续看A当前节点的左右子树和B的左右子树结构是否一样 ,注意是结构是否一样而不是判断左右子树是否相等,因为题目问的是子结构,而不是子树。
二叉树镜像 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述: 1 2 3 4 5 6 7 8 9 10 11 12 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5
问题分析 二叉树是一种递归结构,关于它的问题一般都是有框架可循的,无论是这道翻转二叉树还是上面的子结构。其框架大概是:
当前节点做好当前节点的事,其他的交给递归。
那么这道题也是一样,对于当前节点,交换一下左右指针就行,其他的交给递归。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Solution { public void Mirror (TreeNode root) { if (root==null ) return ; if (root!=null ){ TreeNode temp = root.left ; root.left= root.right; root.right = temp ; } Mirror(root.left) ; Mirror(root.right) ; } }
顺时针打印矩阵 题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
问题分析 整个过程分为四步,从左到右,从上到下,从右到左,从下到上。
顺序是不变的,变的是上下左右的边界,比如第一次从左到右遍历完后,那么上界就增加了一,从上到下遍历完后,后界就缩小了一,以此类推。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.util.ArrayList;public class Solution { public ArrayList<Integer> printMatrix (int [][] matrix) { ArrayList res = new ArrayList () ; int up = 0 ; int down = matrix.length-1 ; int left = 0 ; int right = matrix[0 ].length-1 ; while (true ){ for (int col=left; col<=right; col++){ res.add(matrix[up][col]) ; } up++ ; if (up>down) break ; for (int row=up; row<=down; row++){ res.add(matrix[row][right]); } right--; if (right<left) break ; for (int col=right; col>=left; col--){ res.add(matrix[down][col]); } down-- ; if (down<up) break ; for (int row=down; row>=up; row--){ res.add(matrix[row][left]) ; } left++ ; if (left>right) break ; } return res; } }
字符串的排列
题解停更了很久,最近重新刷的时候感觉有些东西还是要总结,所以继续开始。
题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
问题分析 这道题说到底,还是个回溯的问题,回溯解法之前也写到过,最需要注意一个问题,就是回溯完之后记得回退!这点很重要,具体回退的地方就在我们递归函数结束的下一行,这其实就是labuladong
老哥讲的框架!框架!框架!八皇后问题也是回溯,都是有框架的。说到这里,安利一波公众号 labuladong
, 以及这篇 讲回溯的文章 ,其中还涉及到了八皇后问题,非常棒!
然后目前下面的解法并不是最优解,因为有重复字符,所以我们可以考虑先把字符放在set里再操作,这样就可以减少重复执行的次数了。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import java.util.ArrayList;import java.util.TreeSet;public class Solution { TreeSet<String> res = new TreeSet (); private StringBuilder ele = new StringBuilder (); public ArrayList<String> Permutation (String str) { StringBuilder sb = new StringBuilder (str); for (int i=0 ; i<sb.length(); i++){ traverse(sb,i); ele.deleteCharAt(ele.length()-1 ); } return new ArrayList <>(res); } public void traverse (StringBuilder sbd, int loc) { char prefix = sbd.charAt(loc); StringBuilder sb = new StringBuilder (sbd).deleteCharAt(loc); if (sb.length()==0 ){ ele.append(prefix); res.add(new String (ele)); return ; } ele.append(prefix); for (int i=0 ; i<sb.length(); i++){ traverse(sb,i); ele.deleteCharAt(ele.length()-1 ); } } public static void main (String[] args) { ArrayList<String> res =new Solution ().Permutation("aac" ); for (String str: res) { System.out.println(str); } } }