上岸 I LeetCode Weekly Contest 194解题报告 刷题解法

卖大米 2020-6-21 615


上岸算法

死磕100%上岸率的精品小班课

关注我们,第一时间获得大厂面试真题讲解

No.1 数组异或操作

★解题思路

比较简单, 一次循环就可以计算出结果.

代码展示


class Solution { 
   public int xorOperation(int n, int start) { 
       int res = 0; 
       for (int i = 0; i < n; i++) { 
           res ^= start + 2 * i; 
       } 
       return res; 
   } 
}

No.2 保证文件名唯一

解题思路

使用一个 Map记录某个文件名的编号即可. 不过需要注意 ["name", "name(1)", "name"] 这种手动创建了 "name(1)"的情况.

代码展示


class Solution { 
   public String[] getFolderNames(String[] names) { 
       Map<String, Integer> cnt = new HashMap<>(); 
       String[] res = new String[names.length]; 
       for (int i = 0; i < names.length; i++) { 
           res[i] = names[i]; 
           int j = 0; 
           if (cnt.containsKey(names[i])) { // 包含, 说明重名, 加编号 
               // 编号不断增加, 直到不重名 (针对上面所说的手动创建 name(1) 的情况) 
               for (j = cnt.get(names[i]); ;j++) { 
                   res[i] = names[i] + "(" + j + ")"; 
                   if (!cnt.containsKey(res[i])) break;  
               } 
           } 
           cnt.put(names[i], j + 1); // names[i] 的编号 
           cnt.put(res[i], 1); // res[i] 的编号 
       } 
       return res; 
   } 
}

No.3 避免洪水泛滥

★解题思路

贪心法, 在当前已经有水的湖泊中, 抽取距离下一次下雨时间最近的湖泊的水. 使用优先队列即可完成这个排序.

由于 LeetCode 不支持 AbstractMap.SimpleEntry, 所以需要自行实现 Map.Entry.

代码展示


class MyEntry<K, V> implements Map.Entry<K, V> { 

   K k; 
   V v; 

   public MyEntry(K rain, V integers) { 
       k = rain; 
       v = integers; 
   } 

   @Override 
   public K getKey() { 
       return k; 
   } 

   @Override 
   public V getValue() { 
       return v; 
   } 

   @Override 
   public V setValue(V value) { 
       v = value; 
       return v; 
   } 
} 

class Solution { 
   public int[] avoidFlood(int[] rains) { 
       // rainTimes[i] 表示湖泊 i 的下雨时间列表 
       Map<Integer,List<Integer>> rainTimes = new HashMap<>(); 
       for (int i = 0; i < rains.length; i++) { 
           if (!rainTimes.containsKey(rains[i])) { 
               rainTimes.put(rains[i], new LinkedList<>()); 
           } 
           rainTimes.get(rains[i]).add(i); 
       } 
       for (int k : rainTimes.keySet()) { 
           rainTimes.get(k).add(0x3f3f3f3f); // 避免优先队列中 get(0) 异常 
       } 
       // 优先队列, 保存当前已经有水的湖泊, 按照下一次下雨的时间排序 
       PriorityQueue<Map.Entry<Integer,List<Integer>>> heap = new PriorityQueue<>((a, b)->a.getValue().get(0) - b.getValue().get(0)); 
       // 集合, 保存当前已经有水的湖泊 
       Set<Integer> hasWater = new HashSet<>(); 
       int[] res = new int[rains.length]; 
       for (int i = 0; i < rains.length; i++) { 
           if (rains[i] > 0) { 
               if (hasWater.contains(rains[i])) { 
                   return new int[]{}; 
               } 
               res[i] = -1; 
               hasWater.add(rains[i]); 
               rainTimes.get(rains[i]).remove(0); // 下过一次雨 
               heap.add(new MyEntry<Integer,List<Integer>>(rains[i], rainTimes.get(rains[i]))); 
           } else { 
               var lake = heap.poll(); 
               if (lake != null) { 
                   res[i] = lake.getKey(); 
                   hasWater.remove(res[i]); 
               } else { 
                   res[i] = 1; // heap 为空说明所有湖泊都没有水, 随便抽一个 
               } 
           } 
       } 
       return res; 
   } 
}

No.4 找到最小生成树里的关键边和伪关键边

★解题思路

如果我们不使用一条边, 最小生成树变大, 那么它就是关键的.

如果我们使用了一条边, 最小生成树不变, 并且它不是关键的, 那么它就是伪关键的.

代码展示


class Edge { 
   int id, a, b, v; 

   public Edge(int id, int a, int b, int v) { 
       this.id = id; 
       this.a = a; 
       this.b = b; 
       this.v = v; 
   } 
} 

class UnionFind { 
   int[] fa; 

   public UnionFind(int n) { 
       fa = new int[n]; 
       Arrays.fill(fa, -1); 
   } 

   public int find(int x) { 
       if (fa[x] < 0) return x; 
       return fa[x] = find(fa[x]); 
   } 

   public void merge(int a, int b) { 
       int Fa = find(a); 
       int Fb = find(b); 
       fa[Fa] = Fb; 
   } 
} 

class Solution { 

   public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { 
       List<Edge> edgeList = new ArrayList<>(); 
       for (int i = 0; i < edges.length; i++) { 
           int[] e = edges[i]; 
           edgeList.add(new Edge(i, e[0], e[1], e[2])); 
       } 
       edgeList.sort((a, b)->a.v-b.v); 
       int mst = calcMST(n, edgeList, -1, -1); // 真正的最小生成树 
       List<List<Integer>> res = new ArrayList<>(); 
       res.add(new ArrayList<>()); 
       res.add(new ArrayList<>()); 
       for (int i = 0; i < edges.length; i++) { 
           int t1 = calcMST(n, edgeList, i, -1); // 不使用 i 构造最小生成树 
           int t2 = calcMST(n, edgeList, -1, i); // 必使用 i 构造最小生成树 
           if (t1 != mst) { // 不使用, MST变大, 关键边 
               res.get(0).add(i); 
           } else if (t2 == mst) { // 使用, MST不变, 且不是关键边, 则是伪关键边 
               res.get(1).add(i); 
           } 
       } 
       return res; 
   } 

   private int calcMST(int n, List<Edge> edgeList, int forbidden, int must) { 
       // 不使用 forbidden, 而必须使用 must 构造最小生成树 
       UnionFind uf = new UnionFind(n); 
       int res = 0; 
       int cnt = 0; 
       if (must >= 0) { 
           cnt++; 
           for (var e : edgeList) { 
               if (e.id == must) { 
                   res += e.v; 
                   uf.merge(e.a, e.b); 
                   break; 
               } 
           } 
       } 
       for (var e : edgeList) { 
           if (e.id == forbidden || e.id == must) { 
               continue; 
           } 
           int fa = uf.find(e.a); 
           int fb = uf.find(e.b); 
           if (fa != fb) { 
               uf.merge(fa, fb); 
               cnt++; 
               res += e.v; 

           } 
           if (cnt == n - 1) return res; 
       } 
       return 0x3f3f3f3f; 
   } 
}

杭州上岸算法网络科技有限公司

上岸科技,一家位于西雅图致力于帮助北美小伙伴们上岸的团体。

团队主要由一群怀揣情怀于美国高校毕业的一线IT公司工程师构成。我们坚持采用小班课形式,并结合大量”面试式“和”白板“的教学和交流形式,最终帮助大家提升求职成功概率同时大大减少刷题的痛苦。

正如我们的信仰:我们教的是如何上岸而不仅是算法。

最新回复 (0)
返回