思路一:贪心算法 NO.45跳跃游戏II的姊妹题,思路一样可以结合学习,题解参考徒手挖地球二三周目。
每次都在本次跳跃范围内找到下一跳最远的位置。如果最后最远的都为都不能到结尾,则false。
public boolean canJump(int[] nums) {
if (nums==null||nums.length==0)return true;
//end当前跳跃范围,maxPosition记录当前跳跃范围内下一跳最远的位置
int end=0,maxPosition=0;
for (int i = 0; i < nums.length-1; i++) {
//记录当前范围内下一跳最远的位置
maxPosition=Math.max(maxPosition,nums[i]+i);
//走到当前跳跃最远点
if (i==end){
//跳到最远的位置
end=maxPosition;
}
}
return end>=nums.length-1;
}
时间复杂度:O(n)
什么样的区间是重叠的需要合并?
[1,3]、[2,6] 第1个区间的右边界大于下一个区间的左边界即发生重叠。
需要合并成[第一个区间的左边界,max(第一个区间的右边界,第二个区间的右边界)]这个区间加入结果集。
public int[][] merge(int[][] intervals) {
List<int[]> res=new ArrayList<>();
if (intervals==null||intervals.length==0)return res.toArray(new int[0][]);
//每个区间按照区间左边界升序排序
Arrays.sort(intervals, (o1,o2)->o1[0]-o2[0]);
//遍历每个区间
for (int pre = 0; pre < intervals.length; pre++) {
//记录当前区间的左右边界值
int left=intervals[pre][0],right=intervals[pre][1];
//如果当前区间的右边界大于下一个区间的左边界,即发生重叠
while (pre<intervals.length-1&&right>=intervals[pre+1][0]){
right=Math.max(right,intervals[pre+1][1]);
pre++;
}
res.add(new int[]{left,right});
}
return res.toArray(new int[0][]);
}
时间复杂度:O(n*logn) 区间数组只需要遍历一次,主要是排序的时间复杂度。
思路一:暴力法 没啥说的直接B合并到A后面的预留位置,然后直接API对A进行排序。
public void merge(int[] A, int m, int[] B, int n) {
for (int i = 0; i < n; i++) {
A[m+i]=B[i];
}
Arrays.sort(A);
}
时间复杂度:O((m+n)*log(m+n)) 排序的复杂度
思路二:双指针法 最直接想到的双指针法就是像合并两个有序链表一样双指针分别指向两个数组开头,从前向后遍历两个数组。
但是本题中A数组要作为最终的结果数组,所以需要将A中的m个元素保存到A2数组中,然后像上述方法一样双指针遍历A2和B数组,合并保存到A数组中。
public void merge(int[] nums1, int m, int[] nums2, int n) {
//保存nums1的m个元素
int[] A2=new int[m];
for (int i = 0; i < m; i++) {
A2[i]=nums1[i];
}
//双指针比较并合并保存到nums1中
int i=0,j=0,index=0;
while (i<A2.length&&j<nums2.length){
nums1[index++]=(A2[i]<nums2[j]?A2[i++]:nums2[j++]);
}
//两个数组有剩余时保存到nums1后面
while (j<nums2.length)nums1[index++]=nums2[j++];
while (i<A2.length)nums1[index++]=A2[i++];
}
时间复杂度:O(m+n)
空间复杂度:O(m) 保存nums1的m个元素。
思路三:逆序双指针法 不使用额外的数组去保存nums1的m个元素,从而优化空间。
方法就是:逆序!其实就是将思路二都逆向进行。
双指针分别指向nums1和nums2的尾部,逆序遍历,比较大的元素优先合并入结果数组;从结果数组的尾部向前保存并入的元素。
public void merge(int[] nums1, int m, int[] nums2, int n) {
//指针都指向尾部,比较大的元素优先合并至nums1尾部
int i=m-1,j=n-1,index=nums1.length-1;
while (i>=0&&j>=0){
nums1[index--]=(nums1[i]>nums2[j]?nums1[i--]:nums2[j--]);
}
//检查是否有剩余
while (i>=0)nums1[index--]=nums1[i--];
while (j>=0)nums1[index--]=nums2[j--];
}
时间复杂度:O(m+n)
空间复杂度:O(1)
思路一:广度优先遍历 这道题可以解读为:腐烂橘子到达最远好橘子的最短路径。
写一个很简陋的BFS的框架:
while(队列不空){
node=队列.poll();
for(node的邻接节点){
if(邻接节点m未曾入队){
队列.add(m);
}
}
}
知道了BFS,这道题就比较简单了。
只有坏橘子才会入队,所以没有框架里邻接节点m未曾入队的检查,因为入过队的都变成坏橘子了。
public int orangesRotting(int[][] grid) {
int row = grid.length,col=grid[0].length;
Queue<int[]> queue=new LinkedList<>();
//遍历,统计新鲜橘子,坏橘子坐标入队
int count=0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (grid[i][j]==1)count++;
else if (grid[i][j]==2)queue.add(new int[]{i,j});
}
}
//round传染的轮次
int round=0;
//当队列不空并且还存在好橘子就广搜BFS
while (count>0&&!queue.isEmpty()){
round++;
//n记录当前坏橘子数量,防止出队入队导致不同轮次之间混乱
int n = queue.size();
for (int i = 0; i < n; i++) {
int[] rc = queue.poll();
int r=rc[0],c=rc[1];
//每个出队的坏橘子的四个正方向上邻接节点是否是好橘子,如果是就传染腐烂并入队
if (r-1>=0&&grid[r-1][c]==1){
grid[r-1][c]=2;
count--;
queue.add(new int[]{r-1,c});
}
if (r+1<row&&grid[r+1][c]==1){
grid[r+1][c]=2;
count--;
queue.add(new int[]{r+1,c});
}
if (c-1>=0&&grid[r][c-1]==1){
grid[r][c-1]=2;
count--;
queue.add(new int[]{r,c-1});
}
if (c+1<col&&grid[r][c+1]==1){
grid[r][c+1]=2;
count--;
queue.add(new int[]{r,c+1});
}
}
}
if (count>0)return -1;
else return round;
}
时间复杂度:O(n) n数组元素个数,整个过程遍历数组两次。
代码很冗长,但是思路还算清楚。
本人菜鸟,有错误请告知,感激不尽!
更多题解和学习记录博客:博客、github