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];
    }
}

 



 

posted @ 2020-06-17 18:00  我们村里的小花儿  阅读(194)  评论(0)    收藏  举报