首先重点在于理解丑数的概念:就是说只能被2或3或5整除,直到整除到结果为1,就是丑数。
接下来,来分析获取第N个丑数的方法。简单的思路就是从1开始,往后每次检查该数是不是丑数,但是这样时间效率很差,考虑因子只能为2/3/5,这样从反向考虑问题,只要每次将因子乘以2/3/5,取其中的最小值,该数一定为丑数,代码如下:
public class Solution { public int GetUglyNumber_Solution(int index) { if(index <= 0) return 0; int[] uglyNumber = new int[index]; uglyNumber[0] = 1; int index2 = 0; int index3 = 0; int index5 = 0; int curr = 1; while(curr < index) { int min = getMin(uglyNumber[index2] * 2, uglyNumber[index3] * 3, uglyNumber[index5] * 5); uglyNumber[curr] = min; while(uglyNumber[index2]*2 <= uglyNumber[curr]) index2++; while(uglyNumber[index3]*3 <= uglyNumber[curr]) index3++; while(uglyNumber[index5]*5 <= uglyNumber[curr]) index5++; curr++; } int ugly = uglyNumber[curr - 1]; return ugly; } public int getMin(int i, int j, int k) { int min = (i < j) ? i : j; min = (min < k) ? min : k; return min; } }
这道题的最初思路是对于从头开始的每个字符,遍历后面的字符看是否出现次数为1.但是这种思路的算法复杂度为 O(n) ,这当然是不可接受的啦。
接下来,考虑利用hashmap搞定这个问题,先遍历一次所有字符,出现的字符为 key ,出现的次数为 value 。在第二次遍历的时候,每个查一下 hashmap 中的出现次数是否为1,是就返回。代码如下:
import java.util.*; public class Solution { public int FirstNotRepeatingChar(String str) { if(str.length() == 0) return -1; Map<Integer, Integer> charMap = new HashMap<Integer, Integer>(); int i = 0; for(i = 0; i < str.length(); i++) { int num = str.charAt(i) - 'a'; if(charMap.containsKey(num)) charMap.put(num, charMap.get(num) + 1); else charMap.put(num, 1); } for(i = 0; i < str.length(); i++) { int num = str.charAt(i) - 'a'; if(charMap.get(num) == 1) return i; } return -1; } }
题目暴力解法是:对于从头往后的每个数字对在其后的每个数组成员遍历,数到比该数小的就加1,算法复杂度 O(n^2);
接下来考虑快速一点的解法:参考归并排序的思想,将数组拆分成单个数,在进行合并,不同的是合并时的规则不尽相同。这里在合并的时候,两个指针分别指向量数组的尾部,对其进行比较,若前面数组指针指向元素大于后面数组指针指向元素,那么就有逆序对了,个数就是后面指针的序号+1(因为后面数组也是由小到大排好序的),;若前面数组指针指向元素不大于后面数组指针指向元素,那么没有逆序对。下面是代码实现,在eclipse中调试成功,但是在牛客网上的OJ里怎么也调不通,好方。。。
import java.util.*; public class Solution { public static int count = 0; public int InversePairs(int [] array) { if(array.length == 1) return 0; int length = array.length; int[] firstHalf = new int[length / 2]; System.arraycopy(array, 0, firstHalf, 0, firstHalf.length); InversePairs(firstHalf); int[] secondHalf = new int[length - length / 2]; System.arraycopy(array, length / 2, secondHalf, 0, secondHalf.length); InversePairs(secondHalf); int[] tmp = InversePairs(firstHalf, secondHalf); System.arraycopy(tmp, 0, array, 0, array.length); return count; } public int[] InversePairs(int [] array1, int [] array2) { int[] result = new int[array1.length + array2.length]; int index1 = array1.length - 1; int index2 = array2.length - 1; int index = result.length - 1; while(index1 >= 0 && index2 >= 0) { if(array1[index1] > array2[index2]){ count += (index2 + 1); result[index] = array1[index1]; index1--; }else{ result[index] = array2[index2]; index2--; } index--; } while(index1 >= 0) result[index--] = array1[index1--]; while(index2 >= 0) result[index--] = array2[index2--]; return result; } }
注意这个题目的意思:从两个链表的第一个公共结点开始,后面的链表都相同了,根据这个特点,假设两个链表的长度分别为 m,n 。那么第一次遍历的时候得到 m & n ,第二次遍历的时候取 m , n 中较大的那个链表,将其前进 |m-n| 步,剩下的就是简单的同时比较两个链表的指针是否相同了,代码如下:
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { ListNode p1 = pHead1; ListNode p2 = pHead2; int m = 0; int n = 0; while(p1 != null) { m++; p1 = p1.next; } while(p2 != null) { n++; p2 = p2.next; } p1 = pHead1; p2 = pHead2; if(m > n) { for(int i = m - n; i > 0; i--) p1 = p1.next; }else{ for(int i = n - m; i > 0; i--) p2 = p2.next; } while(p1 != null && p2 != null) { if(p1 == p2) return p1; p1 = p1.next; p2 = p2.next; } return null; } }
考虑到已经是个排好序的数组,那么可以利用二分查找方法,但是对于二分查找来说,只能找到出现的位置,对于多个相同的数,需要对二分查找进行一定的改造来分别找到第一个和最后一个出现的待查找数,代码如下:
public class Solution { public int GetNumberOfK(int [] array , int k) { if(array.length == 0 || array == null) return 0; int start = GetFirstK(array, k, 0, array.length - 1); int end = GetLastK(array, k, 0, array.length - 1); if(start > -1 && end > -1){ return end - start + 1; } return 0; } public int GetFirstK(int[] array, int k, int start, int end) { if(start > end) return -1; int mid = (start + end) / 2; int data = array[mid]; if(data == k){ if((mid > 0 && array[mid - 1] != k) || mid == 0) return mid; end = mid - 1; }else if(data > k) { end = mid - 1; }else{ start = mid + 1; } return GetFirstK(array, k, start, end); } public int GetLastK(int[] array, int k, int start, int end) { if(start > end) return -1; int mid = (start + end) / 2; int data = array[mid]; if(data == k) { if((mid < array.length - 1 && array[mid + 1] != k) || mid == array.length - 1) { return mid; } start = mid + 1; }else if(data > k) { end = mid - 1; }else { start = mid + 1; } return GetLastK(array, k, start, end); } }
思路是:遍历树的过程中当子树没有左子树和右子树的时候,计算出这时的深度,与max depth比较,若比max depth大则更新max depth,代码如下:
/* public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } };*/ public class Solution { public int maxDepth = 0; public int TreeDepth(TreeNode pRoot) { if(pRoot == null) return 0; findDepth(pRoot, 0); return maxDepth; } public void findDepth(TreeNode node, int depth) { if(node == null) return; depth++; if(node.left == null && node.right == null) { if(maxDepth < depth) maxDepth = depth; return; } findDepth(node.left, depth); findDepth(node.right, depth); } }
public class Solution { public boolean IsBalanced_Solution(TreeNode root) { AuxClass aux = new AuxClass(); aux.depth = 0; return IsBalanced(root, aux); } public boolean IsBalanced(TreeNode node,AuxClass aux){ if(node==null){ aux.depth=0; return true; } AuxClass left=new AuxClass(); AuxClass right=new AuxClass(); //get leftTreeDepth and rightTreeDepth of a node.If the 'diff' is bigger than 1,return false;true otherwise if(IsBalanced(node.left,left)&&IsBalanced(node.right,right)){ int leftDepth=left.depth; int rightDepth=right.depth; int diff=leftDepth-rightDepth; if(diff==1||diff==-1||diff==0){ aux.depth=leftDepth>rightDepth?leftDepth+1:rightDepth+1; return true; } } return false; } class AuxClass{ public int depth; } }
ok,这道题目用到了位操作。具体的解释在书中有详细的说明,但是书中写的是C++操作,在java中,位操作转换为二进制数时,在获取二进制数位数的时候会有些问题。目前我还没有想出解决方法。
看到本题目最直观的想法就是从1到50,遍历一遍,但是光是想想就觉得时间复杂度过大。嗯,看了书后发现还是用两个指针指向开始和结束,用动态规划的解法来解答。
设头指针small初始化为1,尾指针big初始化为2,sum为从small到big的和.
1. sum < target, big++
2. sum > target, small++
3. sum == target, 序列找到
代码如下:
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); int small = 1; int big = 2; while(small < (sum + 1) / 2) { while(sumOf(small, big) < sum) big++; while(sumOf(small, big) > sum) small++; if(sumOf(small, big) == sum) { ArrayList<Integer> array = new ArrayList<Integer>(); for(int i = small; i <= big; i++) array.add(i); result.add(array); } small++; } return result; } public int sumOf(int start, int end) { for(int i = start + 1; i <= end; i++) start += i; return start; } }
这个题目和上个题目思路都是类似的,具体操作些许不同,但是思路都是用两个指针分别指向头和尾。代码如下:
import java.util.ArrayList; public class Solution { public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) { int small = 0; int big = array.length - 1; ArrayList<Integer> result = new ArrayList<Integer>(); while(small < array.length) { if(array[small] + array[big] > sum) big--; if(array[small] + array[big] < sum) small++; if(array[small] + array[big] == sum){ result.add(array[small]); result.add(array[big]); break; } small++; } return result; } }