【leetcode】Weekly Contest 91

  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)。很妙!

猜你喜欢

转载自www.cnblogs.com/zzzdp/p/9251907.html