53.最大子序和
该题目主要的思考就是在遍历数组时候是否考虑加上前面数组。
例如样例 [-2,1,-3,4,-1,2,1,-5,4]
我们从 下标 1 开始遍历,此时prev数组 = -2,那么此时我们如果将 -2 加入子数组中那么会导致综合减少(因为prev数组和 < 0),所以此时应当将 1 作为新子数组的头部,继续向后遍历(此时需要记录最大子数组和的值)
//最大子数组和
public int maxSubArray(int[] nums) {
if(nums.length == 0) {
return 0;
}
int prev = nums[0];
int ans = nums[0];
for(int i=1; i<nums.length; i++) {
if(ans < 0) {
prev = nums[i];
}else {
prev += nums[i];
}
ans = Math.max(ans,prev);
}
return ans;
}
54.螺旋矩阵
该题目使用的是模拟法,我们通过模拟走路的形式,就是需要标记一个方向即可,并且在到达临界时候更改方向。
//模拟法
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> order = new ArrayList<Integer>();
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return order;
}
int rows = matrix.length, columns = matrix[0].length;
boolean[][] visited = new boolean[rows][columns];
int total = rows * columns;
int row = 0, column = 0;
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int directionIndex = 0;
for (int i = 0; i < total; i++) {
order.add(matrix[row][column]);
visited[row][column] = true;
//计算可能下一步的位置
int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
//下一步越界,更改方向
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
directionIndex = (directionIndex + 1) % 4;
}
//计算真实下一步的位置
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
}
55.跳跃游戏
该题目我是通过记录一个最远到达的位置,通过搜索能够到达的最远位置内的点,然后不断更新最远距离,一旦能够到达的最远距离超过最后一位,则说明成功。
//跳跃游戏
public boolean canJump(int[] nums) {
//记录当前能到达的最远的点(换句话说也代表之前的点都可以到达)
int rightMost = 0;
for(int i=0; i<nums.length; i++) {
//在所有能够到达的点中尝试更新能够到达的最远的点
if(i <= rightMost) {
rightMost = Math.max(rightMost,i+nums[i]);
}
//如果能够到达的最远距离超过最后一位,说明成功
if(rightMost >= nums.length - 1) {
return true;
}
}
return false;
}
56.合并区间
该题目需要注意的就是先排序后再分情况讨论。
这里为什么需要排序呢?
如果不排序的话,那么我们就无法确认这个混乱的区间集合如何进行合并,所以这里的我采用的是对做区间进行升序排序,这样可以保证当需要分割出新的区间时,我们可以判断出来(即下一位区间的左边界 > 当前区间的右边界)
排序后一共有三种情况,我在代码的注解中有写到。
在这里插入代码片 public int[][] merge(int[][] intervals) {
Arrays.sort(intervals,(o1,o2) -> {return o1[0] - o2[0];});
/*
1.下一个数组的左边界 > 当前数组的右边界。此时需要将当前数组插入List,并更换当前数组
2.下一位数组的左边界 <= 当前数组的右边界。
2.1 下一位数组的右边界 > 当前数组的右边界。更新当前数组右边界
2.2 下一位数组的右边界 <= 当前数组右边界。(包含关系)不做处理
*/
List<int[]> list = new ArrayList<>();
int left = -1;
int right = left;
for(int[] interval : intervals) {
//初次访问
if(left == -1) {
left = interval[0];
right = interval[1];
continue;
}
//情况 1
if(interval[0] > right) {
list.add(new int[]{left,right});
left = interval[0];
right = interval[1];
}else {
if(interval[1] > right) { //情况 2.1
right = interval[1];
}else { //情况 2.2(不作处理)
}
}
}
//最后还会剩下一个组合,需要记得添加进去
list.add(new int[]{left,right});
int[][] ans = new int[list.size()][2];
for(int i=0; i<ans.length; i++) {
ans[i] = list.get(i);
}
return ans;
}
58.最后一个单词长度
该题比较简单,你可以正序遍历也可以反序遍历。
不管怎么样都是o(n)时间复杂度。
反序遍历就是当找到第一个非空字符开始记录,直到碰到空格为止,
下面代码我是用正序遍历,思路类似(不过正序稍微考虑的多点)
public int lengthOfLastWord(String s) {
//去除开头的空格
s = s.trim();
if(s.length() == 0) {
return 0;
}
//记录单词起始和结尾
int len = 0;
//用于标记是否经过一个完整的单词
boolean flag = false;
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) == ' ') {
flag = true;
}else {
if(flag) {
len = 1;
flag = false;
}else {
len++;
}
}
}
return len;
}
59.螺旋矩阵2
该题和54一样的,也是使用模拟法,模拟走路的方式就行了。
上个题目我是直接使用后面题解,下面我就自己写个带注解的版本。
public int[][] generateMatrix(int n) {
int[][] directions = {
{0,1}, //向右
{1,0}, //向下
{0,-1},//向左
{-1,0} //向上
};
//用于判断是否被访问过/添加过
boolean[][] isVisit = new boolean[n][n];
int[][] ans = new int[n][n];
//标记方向
int index = 0;
int row = 0;
int col = 0;
int num = 1;
for(int i=0; i<n*n; i++) {
ans[row][col] = num++;
//标记
isVisit[row][col] = true;
//获得当前前进方向
int[] direction = directions[index % 4];
int nextRow = row + direction[0];
int nextCol = col + direction[1];
if(nextCol >= n || nextCol < 0 || nextRow >= n || nextRow < 0 || isVisit[nextRow][nextCol]) {
//改变方向
index++;
direction = directions[index % 4];
}
row += direction[0];
col += direction[1];
}
return ans;
}