LeetCode 1884 Egg Drop With 2 Eggs and N Floors
方法:DP
时间复杂度:O(n2)
空间复杂度:O(n)
想法:这题不属于我第二轮复习的题目,就是前几天随便点开了个题写一写的时候做到的。思考过程还挺欢乐的,所以打算稍微写一下。题目说有这么一个楼层f,在<=f的楼层往下扔鸡蛋,鸡蛋不会出问题,但再往上的话鸡蛋就会碎。然后手里有两个鸡蛋,最少扔多少次鸡蛋能完全确定这个楼层f是多少。
我们考虑n=100的情况,即总共有100层楼,这是题目里面给的一个样例,说是在最优解下,第一次要在第9楼扔鸡蛋。假设鸡蛋碎了,那就拿另一个鸡蛋从1楼试到8楼(闭区间);如果没碎,就拿着俩鸡蛋继续网上研究上面10-100这91层楼。所以假如说使用dp[i]代表拿着两个鸡蛋确定i层高的楼所需要的最少步数,那么如果说第一次我们从i-j-1这个地方扔鸡蛋,要么鸡蛋碎了,然后问题变为从1到i-j-1每一层扔第二个鸡蛋,这种情况需要的步数是i-j-1-1+1+1(扔鸡蛋)=i-j;要么鸡蛋没碎,拿着俩鸡蛋研究i-j+1~i这些楼层,总共是j层楼,所以是dp[j]。因为我们在扔之前是不知道扔这一下鸡蛋会不会碎的,题目问的是"minimum number of moves that you need to determine with certainty",with certainty一方面是说唯一确定这个楼层,另一方面是说,对于一个给定高度的楼,不管这个楼的f层在哪,我用这套算法都能在这些步数之内确定。因此对于i层楼考虑的它的第j层楼层,循环j的时候取的值应该是Math.max(dp[j] + 1, i - j)。然后对于确定dp[i]的时候,dp[i]会在所有的j中,取最小的Math.max(dp[j] + 1, i - j)。
代码:
class Solution {
public int twoEggDrop(int n) {
int[] dp = new int[n + 1];
dp[1] = 1;
for (int i = 2; i <= n; i++) {
int minv = Integer.MAX_VALUE;
for (int j = 1; j < i; j++) {
minv = Math.min(minv, Math.max(dp[j] + 1, i - j));
}
dp[i] = minv;
}
return dp[n];
}
}
LeetCode 75 Sort Colors
方法:三指针
时间复杂度:O(n)
空间复杂度:O(1)
想法:题目Follow Up就是问我们"Could you come up with a one-pass algorithm using only constant extra space?",因此哪怕有很多种能通过OJ的写法,如果不能满足这一条的话也没多大意思。这道题是很少见的三指针算法,一个指针起名叫p0,指向下一个想要放0的位置,那么在它左边所有值都应为0。一个指针起名叫p2,指向下一个想要放2的位置(从后往前挪),那么在它右边的所有值都应该为2。所以再搞一个指针我叫它p1,一开始为0,放在一个while里面循环。
第一个if:如果p1指向的当前值是0,那就扔给p0指向的那个地方,即两值交换位置,这样p0指向的就会是0,p1指向的会是一个别的什么元素(待会继续讨论)。这时候p0往前挪1,即p0++。
第二个if:如果p1指向的当前值是1,啥也不干直接p1++找下一个元素。
第三个if:如果p1指向的当前值是2,那么就扔给p2指向的那个地方,两个值交换位置,这样p2指向的就会是2。p2--。
这里有一个问题,因为p1是从左往右扫的,根据上面说的if,如果找到一个为2的元素,那么2就会被扔到数组后面去,因为p2是不管它自己指向的是啥元素的,这时候p2有可能扔一个2回来,也有可能扔一个0回来,所以这个地方p1不能++,扔回来的元素还需要再处理。如果扫到为0的元素,交换的时候,因为2都已经被扔到数组后边去了,所以这时候p0指向的元素要么是0,要么是1,但,p1是从左往后扫描的,并且p1应该在p0右边,如果p0这时候指向的是0,之前p1扫描的时候早就应该把它扔到数组前面去了,因此这时候p0一定指向1。那么换完之后,p1会指向1,这时p1需要++,不然的话可能会出现p0超过p1的情况导致出错。
代码:
class Solution {
public void sortColors(int[] nums) {
int n = nums.length;
int p0 = 0, p2 = n - 1;
int p1 = 0;
while (p1 <= p2) {
if (nums[p1] == 0) {
swap(nums, p0, p1);
p0++;
p1++;
}
else if (nums[p1] == 1) {
p1++;
}
else {
swap(nums, p1, p2);
p2--;
}
}
}
private void swap(int[] nums, int l, int r) {
int tmp = nums[l];
nums[l] = nums[r];
nums[r] = tmp;
}
}
LeetCode 201 Bitwise AND of Numbers Range
方法1:位运算
时间复杂度:O(1)
空间复杂度:O(1)
想法:这个题是要算从left到right闭区间内所有整数的按位与,但数据规模非常大,肯定不能直接上去循环与。想清楚了其实非常简单,问题就是left和right这俩数,按照32位二进制整数表示法,从左往右看,左边一模一样的位数有多少。比方说5和7,0101和0111,一样的前缀就是01xx。xx最后用0补全。那么第一种写法就是俩数直接往后挪,挪到什么时候一样,记下挪了多少步,然后再把left挪回去,返回。
代码:
class Solution {
public int rangeBitwiseAnd(int left, int right) {
int i = 0;
while (left != right) {
i++;
left >>= 1;
right >>= 1;
}
return (left << i);
}
}
方法2:位运算
时间复杂度:O(1)
空间复杂度:O(1)
想法:我当时只想出了上面那种做法,这一种做法参考的www.cnblogs.com/grandyang/p… 。第二种做法是lowbit,就是right一直减它最后的那个1,减到什么时候小于等于了left,就说明它只剩下跟left的共同前缀了。单写一个lowbit也是很常见的函数,然后在while里面,从while里出来就直接返回right就行了。
代码:
class Solution {
public int rangeBitwiseAnd(int left, int right) {
while (right > left) {
right -= lowbit(right);
}
return right;
}
private int lowbit(int x) {
return x & (-x);
}
}