leetcode
1、合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
import java.util.Arrays;
public class merge_56 {
public static void main(String[] args) {
int[][] intervals = {{1,4},{0,4}};
int[][] result = merge(intervals);
}
public static int[][] merge(int[][] intervals) {
if(intervals.length == 0)return intervals;
// 先按照区间起始位置排序
Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
// 遍历区间
int[][] res = new int[intervals.length][2];
int idx = -1;
for (int[] interval: intervals) {
// 如果结果数组是空的,或者当前区间的起始位置 > 结果数组中最后区间的终止位置,
// 则不合并,直接将当前区间加入结果数组。
if (idx == -1 || interval[0] > res[idx][1]) {
res[++idx] = interval;
} else {
// 反之将当前区间合并至结果数组的最后区间
res[idx][1] = Math.max(res[idx][1], interval[1]);
}
}
return Arrays.copyOf(res, idx + 1);
}
}
2、旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
链表中的点已经相连,一次旋转操作意味着:
先将链表闭合成环
找到相应的位置断开这个环,确定新的链表头和链表尾
新的链表头在哪里?
在位置 n-k 处,其中 n 是链表中点的个数,新的链表尾就在头的前面,位于位置 n-k-1。
我们这里假设 k < n
如果 k >= n 怎么处理?
k 可以被写成 k = (k // n) * n + k % n 两者加和的形式,其中前面的部分不影响最终的结果,因此只需要考虑 k%n 的部分,这个值一定比 n 小。
算法
算法实现很直接:
找到旧的尾部并将其与链表头相连 old_tail.next = head,整个链表闭合成环,同时计算出链表的长度 n。
找到新的尾部,第 (n - k % n - 1) 个节点 ,新的链表头是第 (n - k % n) 个节点。
断开环 new_tail.next = None,并返回新的链表头 new_head。
- 时间复杂度:O(N),其中 NNN 是链表中的元素个数
- 空间复杂度:O(1),因为只需要常数的空间
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null || head.next == null)return head;
int n = 0;
ListNode old_tail = head;
//计算链表长度
for(n = 1;old_tail.next != null; n++){
old_tail = old_tail.next;
}
//将尾节点指向头结点,形成环路
old_tail.next = head;
//找到旋转后的尾节点
for(int i = 0; i < n - k % n -1;i++){
head = head.next;
}
ListNode pre = head.next;
head.next = null;
return pre;
}
}
3、不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
动态规划
我们令 dp[i][j] 是到达 i, j 最多路径
动态方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]
注意,对于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在边界,所以只能为 1
时间复杂度:O(m∗n)
空间复杂度:O(m∗n)
class Solution {
public int uniquePaths(int m, int n) {
int[][] cur = new int[n][m];
for(int i = 0 ;i < m;i++){
cur[0][i] = 1;
}
for(int i = 0 ;i < n;i++){
cur[i][0] = 1;
}
for(int i = 1 ;i < n;i++){
for(int j = 1 ;j < m;j++){
cur[i][j] = cur[i - 1][j] + cur[i][j - 1];
}
}
return cur[n - 1][m - 1];
}
}
4、不同路径2
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
和上一题区别不大
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid[0].length;
int n = obstacleGrid.length;
int[][] cur = new int[n][m];
for(int i = 0 ;i < m;i++){
if(obstacleGrid[0][i] == 1){
break;
}
cur[0][i] = 1;
}
for(int i = 0 ;i < n;i++){
if(obstacleGrid[i][0] == 1){
break;
}
cur[i][0] = 1;
}
for(int i = 1 ;i < n;i++){
for(int j = 1 ;j < m;j++){
if(obstacleGrid[i][j] == 1){
cur[i][j] = 0;
continue;
}
cur[i][j] = cur[i - 1][j] + cur[i][j - 1];
}
}
return cur[n - 1][m - 1];
}
}
5、最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
动态规划,不断更新每个位置的最小值;
时间复杂度 O(M×N): 遍历整个 gridgridgrid 矩阵元素。
空间复杂度 O(1) : 直接修改原矩阵,不使用额外空间。
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
for(int i = 0; i < m ; i++){
for(int j = 0; j < n ; j++){
if(i == 0 && j == 0)continue;
else if(i == 0)grid[0][j] += grid[0][j - 1];
else if(j == 0) grid[i][0] += grid[i - 1][0];
else{
grid[i][j] = Math.min(grid[i - 1][j] + grid[i][j] ,
grid[i][j - 1] + grid[i][j]);
}
}
}
return grid[m - 1][n - 1];
}
}