算法题解之math类题
Bulb Switcher
灯泡开关
思路:除了平方数以外,其他所有位置的灯泡最终都被开关了偶数次,因此最终都为0。问题等价于求1~n中平方数的个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int bulbSwitch(int n) { 3 return (int)Math.sqrt(n); 4 } 5 }
Count Primes
质数计数
思路1:暴力法。其中判断每一个数n是不是质数需要验证是否任何小于n的数都不能整除n,这一步是O(n)。因此整体复杂度是O(n^2)。
思路2:思路1的优化版。如果一个数n不是质数,则n = p * q。令 p <= q,则可以推出 p * p <= n,即 p <= √n。因此思路一中只需要判断是否任何小于等于√n都不能整除n。整体时间复杂度变为O(n^1.5)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public int countPrimes(int n) { 2 int count = 0; 3 for (int i = 1; i < n; i++) { 4 if (isPrime(i)) count++; 5 } 6 return count; 7 } 8 9 private boolean isPrime(int num) { 10 if (num <= 1) return false; 11 // Loop's ending condition is i * i <= num instead of i <= sqrt(num) 12 // to avoid repeatedly calling an expensive function sqrt(). 13 for (int i = 2; i * i <= num; i++) { 14 if (num % i == 0) return false; 15 } 16 return true; 17 }
思路3:埃拉托色尼筛选法:对于当前数i(i要从2开始),把它的倍数2i,3i,4i......都标记为不是质数,然后 i 的下一个没有被标记过的数一定是质数。
优化:(1)如果一个数已经被标记为不是质数了,直接跳过,因为它的倍数之前一定已经标记过了;(2)对于数i,不用从2i开始标记,直接从i * i开始标记(之前的也一定被标记过)。
算法空间复杂度为O(n),时间复杂度为O(n log log n)。(证明见wiki)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int countPrimes(int n) { 3 boolean[] notPrime = new boolean[n]; 4 int count = 0; 5 for (int i = 2; i < n; i++) { 6 if (notPrime[i] == false) { 7 count++; 8 for (long j = i; i * j < n; j++) { 9 notPrime[(int)(i * j)] = true; 10 } 11 } 12 } 13 14 return count; 15 } 16 }
Insert Interval
插入区间
思路:区间类题目。先把区间按照start大小插入进去,然后就就跟排好序的merge intervals一样了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for an interval. 3 * public class Interval { 4 * int start; 5 * int end; 6 * Interval() { start = 0; end = 0; } 7 * Interval(int s, int e) { start = s; end = e; } 8 * } 9 */ 10 public class Solution { 11 public List<Interval> insert(List<Interval> intervals, Interval newInterval) { 12 List<Interval> res = new ArrayList<Interval>(); 13 if (intervals == null || intervals.size() == 0) { 14 res.add(newInterval); 15 return res; 16 } 17 18 for (int i = 0; i < intervals.size(); i++) { 19 if (intervals.get(i).start > newInterval.start) { 20 intervals.add(i, newInterval); 21 break; 22 } else if (i == intervals.size() - 1) { 23 intervals.add(newInterval); 24 break; 25 } 26 } 27 28 res.add(intervals.get(0)); 29 for (int i = 1; i < intervals.size(); i++) { 30 Interval cur = intervals.get(i); 31 Interval last = res.get(res.size() - 1); 32 if (cur.start <= last.end) { 33 last.end = Math.max(last.end, cur.end); 34 } else { 35 res.add(cur); 36 } 37 } 38 return res; 39 40 } 41 }
Merge Intervals
合并区间
思路:区间类题目。先把所有区间按照start排好序,把第一个区间加到合并集中。然后遍历之后的每个区间,只需要与合并集中最后一个区间进行比较合并。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for an interval. 3 * public class Interval { 4 * int start; 5 * int end; 6 * Interval() { start = 0; end = 0; } 7 * Interval(int s, int e) { start = s; end = e; } 8 * } 9 */ 10 public class Solution { 11 public List<Interval> merge(List<Interval> intervals) { 12 13 List<Interval> res = new ArrayList<Interval>(); 14 15 if (intervals == null || intervals.size() == 0) { 16 return res; 17 } 18 19 Comparator<Interval> c = new Comparator<Interval>() { 20 public int compare(Interval i1, Interval i2) { 21 return i1.start - i2.start; 22 } 23 }; 24 Collections.sort(intervals, c); 25 res.add(intervals.get(0)); 26 for (int i = 1; i < intervals.size(); i++) { 27 Interval cur = intervals.get(i); 28 Interval last = res.get(res.size() - 1); 29 if (cur.start <= last.end) { 30 last.end = Math.max(last.end, cur.end); 31 } else { 32 res.add(cur); 33 } 34 } 35 return res; 36 } 37 }
Power of Three
判断3的次方
思路1:循环或者递归。复杂度O(lgn)
思路2:题目规定不能用loop或者递归。可以调用java的log函数来做。时间仍然是O(lgn)
思路3:求出int中3的次方的最大值,判断max是否能被n整除。时间是O(1)。power of tow,power of four 均与这题类似。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Solution { public boolean isPowerOfThree(int n) { int Max3PowerInt = 1162261467; if (n > 0 && n <= Max3PowerInt) { if (Max3PowerInt % n == 0) { return true; } } return false; } }
Rectangle Area
矩形面积
思路:设矩形1的上下左右边分别为u1,d1,l1,r1,矩形2的上下左右边分别为u2,d2,l2,r2。则两矩形不相交的充要条件是:u1在d2下方 或 u2在d1下方 或 l1在r2右边 或 l2在r1右边。其余情况一定相交。相交面积的求法是:相交矩形的左边是l1和l2其中靠右的边,相交矩形的右边是r1和r2其中靠左的边,相交矩形的上边是u1和u2的靠下的边,相交矩形的下边是d1和d2的靠上的边。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) { 3 int area1 = (C - A) * (D - B); 4 int area2 = (G - E) * (H - F); 5 if (A >= G || E >= C || B >= H || F >= D) { 6 return area1 + area2; 7 } 8 9 int w = Math.min(C, G) - Math.max(A, E); 10 int h = Math.min(D, H) - Math.max(B, F); 11 return area1 + area2 - w * h; 12 13 } 14 }