目录
1.gas-station
2.jump-game
3.jump-game-ii
4.minimum-window-substring
5.maximum-subarray
6.maximal-rectangle
题目:环形路上有n个加油站,第i个加油站的汽油量是gas[i]。你有一辆车,车的油箱可以无限装汽油。从加油站i走到下一个加油站(i+1)花费的油量是cost[i],你从一个加油站出发,刚开始的时候油箱里面没有汽油。求从哪个加油站出发可以在环形路上走一圈。返回加油站的下标,如果没有答案的话返回-1。注意:答案保证唯一。
分析:从最后一个加油站start开始出发,如果油量充足,可以一直往前走end++;如果油量不充足,向后寻找新的出发点即start--,这样就可以利用已有的计算的结果。最终start==end时,也就是车已经走完一圈了,如果这时有剩余量,那么从该起始点出发就能到达终点。
public int canCompleteCircuit(int[] gas, int[] cost) {
int start = gas.length - 1,end = 0;
int rest = gas[start] - cost[start];
while(start > end){
if(rest >= 0){//可到达end,继续往前走
rest = rest + gas[end] - cost[end];
end++;
}
else{
start--;
rest = rest + gas[start] - cost[start];
}
}
return rest >= 0 ? start:-1;
}
题目:给出一个非负整数数组,你最初在数组第一个元素的位置,数组中的元素代表你在这个位置可以跳跃的最大长度,判断你是否能到达数组最后一个元素的位置。例如:A =[2,3,1,1,4], 返回 true;A =[3,2,1,0,4], 返回 false.
分析:用max来标记能到达的最远处的点,遍历整个数组,如果能到达该点,计算从该点出发能到达的最远点从而更新max。
public boolean canJump(int[] A) {
if(A.length <= 1)
return true;
int max = 0;
for(int i = 0;i < A.length;i++){
if(max < i)
return false;
max = Math.max(max,i + A[i]);//在该点能到达的最大点
}
if(max >= A.length - 1)
return true;
return false;
}
题目:给出一个非负整数数组,你最初在数组第一个元素的位置,数组中的元素代表你在这个位置可以跳跃的最大长度,你的目标是用最少的跳跃次数来到达数组的最后一个元素的位置。例如:给出数组 A =[2,3,1,1,4],最少需要两次才能跳跃到数组最后一个元素的位置。(从数组下标为0的位置跳长度1到达下标1的位置,然后跳长度3到数组最后一个元素的位置)
分析:计算每一点能到达的最远的地方,遍历从当前点能到达的每个点,若该点还没到达过,到达该点的步数为到达当前点步数+1。用一个数组来存储到达该点所需的最小步数,第一次到达终点的步数肯定是最小值。
public int jump(int[] A) {
int len = A.length;
if(len < 2)
return 0;
int[] dp = new int[len];
for(int i = 0;i < len;i++){
int max = Math.min(len-1,i + A[i]);//从该点出发能到达的最远点
for(int j = i + 1;j <= max;j++){
if(dp[j] == 0)
dp[j] = dp[i] + 1;
}
if(dp[len-1] != 0)//第一次到达终点即为最小值
return dp[len-1];
}
return -1;
}
题目:给出两个字符串S和T,要求在O(n)的时间复杂度内在S中找出最短的包含T中所有字符的子串。例如:S ="ADOBECODEBANC",T ="ABC",找出的最短子串为"BANC".
分析:滑动窗口。先统计出子串T各字符的个数,可以存在hashMap中(一般输入字母串的字符只有 128 个,可以用大小为128的数组来代替HashMap)。然后再遍历S串,初始化begin=end=0,对于S中的每个遍历到的字母,都在 HashMap 中的映射值减1,如果减1后的映射值仍大于等于0,说明当前遍历到的字母是T串中的字母,使用一个计数器 cnt,使其自减1,当计数为0时,说明begin到end之间已包含T中所有字符,记录窗口长度w;然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被包含在窗口;继续后移end,直到begin到end之间包含T中所有字符,重新记录最小的窗口;循环直到end到S中的最后一个字符。
public String minWindow(String S, String T) {
if(S.length() < T.length() || S.length() == 0 || T.length() == 0)
return "";
int[] a = new int[128];
//统计T中各字符的个数
for(int i = 0;i < T.length();i++)
a[T.charAt(i)]++;
//注意S中未必包含T中所有字符,所以最小长度是S的长度加1
int start = 0,end = 0,minLen = S.length() + 1,head = 0;
int count = T.length();//计数器,标记窗口中还缺失的字符个数
while(end < S.length()){
if(a[S.charAt(end)] > 0)
count--;
a[S.charAt(end++)]--;
while(count == 0){
if(minLen > end - start){//更新窗口最小值
head = start;
minLen = end - start;
}
if(a[S.charAt(start)] == 0)//窗口左边开始移除元素
count++;
a[S.charAt(start++)]++;
}
}
if(minLen == S.length() + 1)
return "";
return S.substring(head,head + minLen);
}
题目:请计算给出的数组(至少含有一个数字)中具有最大和的子数组(子数组要求在原数组中连续)。例如:给出的数组为[−2,1,−3,4,−1,2,1,−5,4],子数组[−2,1,−3,4,−1,2,1,−5,4],具有最大的和:6. 拓展:如果你已经提出了O(n)的解决方法,请尝试使用分治算法来解决这道题。这道题分治的解法更巧妙一些。
分析:最简单的方法是使用贪心法,见剑指offer面试题42 https://blog.csdn.net/Nibaby9/article/details/104126765
另外还可以用分治法解决,见蓝桥杯分治法与动态规划例2 https://blog.csdn.net/Nibaby9/article/details/79900267
题目:给出一个只包含0和1的二维矩阵,找出最大的全部元素都是1的长方形区域,返回该区域的面积。
分析:把矩阵沿着某一行切下来,然后把切的行作为底面,将自底面往上的矩阵看成一个直方图,直方图的中每个项的高度就是从底面行开始往上连续为1的数量。这样就把题转化成求直方图中最大矩阵面积问题了,largest-rectangle-in-histogram见leetcode之栈leetcode5 https://blog.csdn.net/Nibaby9/article/details/104525658
public int maximalRectangle(char[][] matrix) {
if(matrix.length == 0)
return 0;
int max = 0;
int row = matrix.length,col = matrix[0].length;
int[] height = new int[col];
for(int i = 0;i < row;i++){//沿每一行切割
for(int j = 0;j < col;j++){
if(matrix[i][j] == '1')//注意,matrix类型为char型
height[j]++;
else
height[j] = 0;
}
int val = largestRectangleArea(height);
if(max < val)
max = val;
}
return max;
}
public int largestRectangleArea(int[] height) {
if(height.length == 0)
return 0;
int max = 0;
Stack s = new Stack<>();
int i = 0;
while(!s.empty() || i < height.length){
if(s.empty() || i < height.length && height[i] >= height[s.peek()])
s.push(i++);
else{
int high= height[s.pop()];
//stack.peek() + 1为左边界,右边界为i
int width = s.empty()? i : i- s.peek() - 1;
if(high * width > max)
max = high * width;
}
}
return max;
}