第64题:滑动窗口的最大值
难易度:⭐⭐⭐
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口:
他们的最大值分别为{4,4,6,6,6,5};
针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个:
{[2,3,4],2,6,2,5,1},
{2,[3,4,2],6,2,5,1},
{2,3,[4,2,6],2,5,1},
{2,3,4,[2,6,2],5,1},
{2,3,4,2,[6,2,5],1},
{2,3,4,2,6,[2,5,1]}。
解析:
本题的思路是使用双端队列
双端队列用来保存窗口最大数值的index值
每次都是用队列的队尾与新进入的数字进行比较,如果队列队尾要比新的值小,那么就出队,需要注意的是,此处应该使用while循环,而不是if判断:
while(!deque.isEmpty() && num[deque.peekLast()] < num[i]){
deque.pollLast();
}
同时,还需要注意的点是需要判断对首数字是否过期,对于数组
{2,3,4,2,6,2,5,1}
来说,当index = 5的时候,原本对首的数字4过期,当满足deque.peekFirst() == i - size时,满足过期的条件。整理好思路后就可以写代码了。
代码如下:
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> arrayList = new ArrayList<>();
if(num == null || num.length == 0 || size == 0 || num.length < size){
return arrayList;
}
Deque<Integer> deque = new LinkedList<>();
for(int i = 0;i < num.length;i++){
while(!deque.isEmpty() && num[deque.peekLast()] < num[i]){
deque.pollLast();
}
deque.addLast(i);
// 判断对首数字是否过期
if(deque.peekFirst() == i - size){
deque.pollFirst();
}
if(i >= size - 1){
arrayList.add(num[deque.peekFirst()]);
}
}
return arrayList;
}
}
第65题:矩阵中的路径
难易度:⭐⭐⭐
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如:
a b c e
s f c s
a d e e
矩阵中包含一条字符串"bcced"的路径,
但是矩阵中不包含"abcb"路径,
因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
又是一道我想破头没写出来的题目,思路其实还是比较简单的,即:
暴力递归
当矩阵中坐标为(row,col)的格子和路径字符串中的下标pathLength的字符一样时,使用暴力递归的思想,从相邻的四个格子中去定位路径字符串中下标为pathLenth + 1的字符。
如果四个相邻的个子都没有匹配字符串中下标为pathLength + 1的字符,则表明当前路径字符串中下标为pathLength的字符在矩阵中的定位是错误的,那么则需要返回到前一个字符pathLength - 1,然后重新定位
本题同时还要使用一个矩阵大小的boolean数组,去记录,哪些点是被访问过的,因为题中有明确说明,“好马不吃回头草”。当走过的部分有被标记过则无法再走。
代码如下:
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str){
if(matrix == null || matrix.length == 0 || str == null || str.length == 0 || rows * cols != matrix.length || rows * cols < str.length){
return false;
}
boolean [] visited = new boolean[rows * cols];
int[] pathLength = {0};
for(int i = 0;i < rows;i++){
for(int j = 0;j < cols;j++){
if(hasPathCore(matrix,rows,cols,str,i,j,visited,pathLength)){
return true;
}
}
}
return false;
}
public boolean hasPathCore(char[] matrix,int rows,int cols,char[] str,int row,int col,boolean[] visited,int[] pathLength){
boolean res = false;
if(row >= 0 && row < rows && col >= 0 && col < cols && !visited[row * cols + col] && matrix[row * cols + col] == str[pathLength[0]]){
visited[row * cols + col] = true;
pathLength[0]++;
if(str.length == pathLength[0]){
return true;
}
res = hasPathCore(matrix,rows,cols,str,row + 1,col,visited,pathLength)
|| hasPathCore(matrix,rows,cols,str,row - 1,col,visited,pathLength)
|| hasPathCore(matrix,rows,cols,str,row,col + 1,visited,pathLength)
|| hasPathCore(matrix,rows,cols,str,row,col - 1,visited,pathLength);
if(!res){
visited[row * cols + col] = false;
pathLength[0]--;
}
}
return res;
}
}
第66题:机器人的运动范围
难易度:⭐⭐⭐
题目描述:
地上有一个m行和n列的方格。
一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
但是,它不能进入方格(35,38),因为3+5+3+8 = 19。
请问该机器人能够达到多少个格子?
解析:
本题和上一题实际上都属于一个类型的问题,都属于暴力递归
本题的解析,在二刷的时候会进行改进,写的详细一些,敬请期待
代码如下:
public class Solution {
public int movingCount(int threshold, int rows, int cols){
if(threshold < 0 || rows < 0 || cols < 0){
return 0;
}
boolean[] visited = new boolean[rows * cols];
int count = movingCountCore(threshold,rows,cols,0,0,visited);
return count;
}
public int movingCountCore(int threshold,int rows,int cols,int row,int col,boolean[] visited){
int res = 0;
if(check(threshold,rows,cols,row,col,visited)){
visited[row * cols + col] = true;
res = 1 + movingCountCore(threshold,rows,cols,row + 1,col,visited)
+ movingCountCore(threshold,rows,cols,row - 1,col,visited)
+ movingCountCore(threshold,rows,cols,row,col + 1,visited)
+ movingCountCore(threshold,rows,cols,row,col - 1,visited);
}
return res;
}
public boolean check(int threshold,int rows,int cols,int row,int col,boolean[] visited){
if(row >= 0 && row < rows && col >= 0 && col < cols && !visited[row * cols + col] && (getDigitSum(row) + getDigitSum(col) <= threshold)){
return true;
}
return false;
}
public int getDigitSum(int target){
int res = 0;
while(target != 0){
res += target % 10;
target /= 10;
}
return res;
}
}
第67题:剪绳子
难易度:⭐⭐⭐
题目描述:
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。
请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
解析:
思路一:
贪心算法:
贪心策略如下:
当绳子的长度大于等于5的时候,我们需要尽可能多去剪出长度为3的绳子;当剩下的绳子长度为4的时候,把绳子简称两段长度为4的绳子。因为2 × 2 > 3 × 1
贪心策略的证明非常难,在这里就不予以证明了。
代码如下:
public class Solution {
// 贪心策略
public int cutRope(int target) {
if(target < 2){
return 1;
}
if(target == 2){
return 1;
}
if(target == 3){
return 2;
}
int timesOf3 = target / 3;
if(target - timesOf3 * 3 == 1){
timesOf3--;
}
int timesOf2 = (target - timesOf3 * 3) / 2;
return (int)(Math.pow(3,timesOf3) * Math.pow(2,timesOf2));
}
}
本题使用动态规划也可以求解,在二刷的时候,会进行补充和改进,谢谢大家。