leetcode周赛,早上起来发现没网,用热点意识模糊的a了三个水题。
1.Lemonade Change
简单模拟题,收到十元用五元钱找回,收到20元时优先用一张10一张5,如果10不够用3张5,如果没有就返回flase(贪心)。
1 public boolean lemonadeChange(int[] bills) { 2 int five = 0; 3 int ten = 0; 4 int twenty = 0; 5 for (int i = 0; i < bills.length; i++) { 6 if (bills[i] == 5) { 7 five++; 8 } else if (bills[i] == 10) { 9 if (five > 0) { 10 five--; 11 ten++; 12 } else { 13 return false; 14 } 15 } else if (bills[i] == 20) { 16 if (ten > 0 && five > 0) { 17 five--; 18 ten--; 19 twenty++; 20 } else if (five >= 3) { 21 five -= 3; 22 twenty++; 23 } else { 24 return false; 25 } 26 } 27 } 28 return true; 29 }
2.All Nodes Distance K in Binary Tree
找到target的位置,根据左(0)右(1)记录路径,再遍历(我用的是先根)一遍整颗树,每个点与target的距离是其“两点的深度和”减去“公共前缀的两倍”,判断是否与K相等即可。
1 public List<Integer> distanceK(TreeNode root, TreeNode target, int K) { 2 String t = findTarget(root,target,new String()); 3 return helper(root, K,t, new LinkedList<>(), new String()); 4 } 5 public List<Integer> helper(TreeNode root,int k,String target,List<Integer> res,String s){//先根遍历 6 if(root!=null){ 7 if(disK(target, s.toString(), k)){ 8 res.add(root.val); 9 } 10 helper(root.left, k, target, res, s+"0"); 11 helper(root.right, k, target, res, s+"1"); 12 } 13 return res; 14 } 15 public boolean disK(String s1,String s2,int k){//计算距离,距离=点1的深度+点2的深度-2倍的公共前缀深度 16 if(s1.length()>s2.length()){ 17 String s = s1; 18 s1 = s2; 19 s2 = s; 20 } 21 int i; 22 for(i = 0;i<s1.length();i++){ 23 if(s1.charAt(i) != s2.charAt(i)){ 24 break; 25 } 26 } 27 int dis = s1.length() + s2.length() - i*2; 28 return dis == k; 29 } 30 public String findTarget(TreeNode root, TreeNode target,String res){//查找target节点,返回路径 31 if(root == null){ 32 return "w"; 33 } 34 if(root == target){ 35 return res; 36 } 37 String s = findTarget(root.left, target, res+"0"); 38 if(s.equals("w")){ 39 return findTarget(root.right, target, res+"1"); 40 }else { 41 return s; 42 } 43 }
3.Score After Flipping Matrix
在一行中,每一列中的值比后面所有的列和都要大,因此把第一列的值置为1是第一目标,所以首先转换行用于把第一列的值全置为1,第二列到最后一列只需要根据每一列的1的个数是否大于行数的一半,如果大于就翻转列,反之则该列已经最大。
1 public int matrixScore(int[][] A) { 2 int score = 0; 3 for (int i = 0; i < A.length; i++) {//把第一列的所有值都用行转换变为1 4 if (A[i][0] == 0) { 5 swap(A, true, i); 6 } 7 score += Math.pow(2, A[i].length - 1); 8 } 9 for (int j = 1; j < A[0].length; j++) { 10 int count = 0;//每一列1的个数 11 for (int i = 0; i < A.length; i++) { 12 if (A[i][j] == 1) { 13 count++; 14 } 15 } 16 count = Math.max(count, A.length - count);//如果小于一半,就反转 17 score += Math.pow(2, A[0].length - 1 - j) * count; 18 } 19 return score; 20 } 21 22 public void swap(int[][] A, boolean row, int num) {//用于转换行、列(后来发现转换列其实不需要,只需要计算每列的个数,如果反转就把个数反转即可,因为该列是否反转与后序无关联) 23 if (row) { 24 for (int i = 0; i < A[0].length; i++) { 25 A[num][i] ^= 1; 26 } 27 } 28 }
4.Shortest Subarray with Sum at Least K
感觉A.length<=50000是最大的难点,当时没做出来,补题学到了一种很厉害的思路。首先用P[i]数组记录数组中第0个元素到第i个元素的和,因此P[y]-p[x]就是从位置x到y的和。
然后,用一个双端队列,扫描一遍p数组,使队列保持递增(因为对于i<j,p[i]>p[j],则最小的距离肯定不是后面某个位置的值到i的和,而是到j的和,因此需要保持队列递增)。
扫描每个点(比如i)的时候,用i与队列中首位的值配对,如果到队列首位位置的和比K大,则可以把队列首位的值remove掉,因为对于j>i来说,后面的所有符合条件的位置离队列首位记录的位置更远。
可能听描述有点抽象,看代码就好啦!
1 public int shortestSubarray(int[] A, int K) { 2 int[] P = new int[A.length + 1]; 3 int res = Integer.MAX_VALUE; 4 for (int i = 0; i < A.length; i++) { 5 P[i + 1] = P[i] + A[i]; 6 } 7 Deque<Integer> deque = new LinkedList<>();// 下面保证单调递增队列 8 for (int i = 0; i < A.length + 1; i++) { 9 while (!deque.isEmpty() && P[i] <= P[deque.getLast()]) {// 如果x1<y2 10 // 且p[y2]<p[x1]则把x1抛弃 11 deque.removeLast(); 12 } 13 while (!deque.isEmpty() && P[i] >= P[deque.getFirst()] + K) {// 从前找满足的位置 14 res = Math.min(res, i - deque.removeFirst());// 如果满足就把队列中的那个点去掉,因为i之后的点-队列中的点位置肯定比当前点大 15 } 16 deque.addLast(i); 17 } 18 return res == Integer.MAX_VALUE ? -1 : res; 19 }
oN的时间复杂度和空间复杂度,正好利用了区间和大于k这个条件(我才不会说我一开始看错题在思考怎么判断等于k)。很妙!