【算法】二刷lc记录2

##21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 
  • 思路

模拟+归并

  1. 因为涉及到对头节点的操作,所以定义一个dummy,然后合并时需要知道为节点位置,再定义一个tail节点。
  • 注意点 1.后面的查漏补缺只会有一个执行

code

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode tail = dummy;
        ListNode a = l1;
        ListNode b = l2;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                tail.next = l1;
                tail = tail.next;
                l1 = l1.next;
            }else{
                tail.next = l2;
                tail = tail.next;
                l2 = l2.next;
            }
        }

        while(l1 != null){
            tail.next = l1;
            tail = tail.next;
            l1 = l1.next;
        }

        while(l2 != null){
            tail.next = l2;
            tail = tail.next;
            l2 = l2.next;
        }

        return dummy.next;
    }
}

##25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
  • 思路

##23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。
  • 思路

##22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

有效括号组合需满足:左括号必须以正确的顺序闭合。
  • 思路

dfs

  1. 这里需要知道一个结论:如果一串括号是合法的,他的每个位置的前缀的左括号数量都要大于等于右括号的数量。
  2. 我们就可以去dfs, 左括号的添加限制只有数量小于n,而右括号的添加还需满足其数量小于等于左括号数量

code

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs(n,0,0,"");
        return res;
    }

    public void dfs(int n,int l,int r,String path){
       if(l == n && r == n){
           res.add(path);
           return;
       }
       if(l <= n)dfs(n,l + 1,r,path + "(");
       if(r <= n && l > r)dfs(n,l,r + 1,path + ")");
    }
}

##25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
  • 思路

##26. 删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
  • 思路

双指针

一个k指针表示0 -> k 的元素都是不重复的,另一个指针遍历数组。

  • **注意点:**第一个元素一定不重复,k直接从1开始

code

class Solution {
    public int removeDuplicates(int[] nums) {
        int k = 1;
        for(int i = 1; i < nums.length; i ++){
            if(nums[i] != nums[i - 1]){
                nums[k ++] = nums[i];
            }
        }
        return k;
    }
}

##27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
  • 思路

双指针

思路与上题类似。。。 code

class Solution {
    public int removeElement(int[] nums, int val) {
        int k = 0;
        for(int i = 0; i < nums.length; i ++){
            if(nums[i] != val)nums[k ++] = nums[i];
        }
        return k;
    }
}a

##33. 搜索旋转排序数组


整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
  • 思路

二分

1.因为在旋转有序数组之后,在分割点两边满足左边的数都大于右边的数,所以满足可以二分的条件,两边满足一些性质。 2.通过二分寻找到分割点,在通过target与nums[0]比较判断所在区间,再次二分即可。 (画画图可以更好理解)

code

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        int l = 0, r = n - 1;
        while(l < r){
            int mid = r + l + 1 >> 1;
            if(nums[mid] >= nums[0])l = mid ;
            else r = mid - 1;
        }
        if(target >= nums[0])l = 0;
        else{
            l = r + 1;
            r = n - 1;
        }
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target)r = mid;
            else l = mid + 1;
        }
  // 这里写成nums[r], 当数组只有一个元素时, 两个二分查找代码都没有走, 而l在上面被+1, 这时会越界, 而r是length-1还是0, 不会产生越界
        if(nums[r] != target)return -1;
        return r;
    }
}

##34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

` 思路

二分

根据y总的二分大法hh做即可hh code

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int n = nums.length;
        if(n == 0)return new int[]{-1,-1};
        int[] res = new int[2];
        int l = 0, r = n - 1;

        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target)r = mid;
            else l = mid + 1;
        }
        if(nums[r] != target)return new int[]{-1,-1};
        res[0] = r;

        l = 0;
        r = n - 1;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(nums[mid] <= target)l = mid;
            else r = mid - 1;
        }
        res[1] = r;
        return res;
    }
}

##35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。
  • 思路

二分 从左往右看,找到第一个大于等于target的数既是所需位置

  • 注意点

这里的有边界是可能取到n的。 code

class Solution {
    public int searchInsert(int[] nums, int target) {
        int l = 0, r = nums.length;
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target)r = mid;
            else l = mid + 1;
        }
        return r
    }
}

##54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

模拟

思路:建立x y两个方向的向量(注意这里要满足顺时针的方向),走下一步之前判断是否要变向,变向条件为

  • 下一步位置越界或已经走过。 可以变4个方向,设一个方向数d,在需要变相时,因为是四个一循环,则 将其 + 1 % 4求出下一个方向

code

class Solution {
   public List<Integer> spiralOrder(int[][] matrix) {
       int n = matrix.length;
       if(n == 0)return new ArrayList<>();
       int m = matrix[0].length;

       List<Integer> res = new ArrayList<>();

       int[] dx = {0,1,0,-1};
       int[] dy = {1,0,-1,0};
       boolean[][] st = new boolean[n][m];

       for(int i = 0,x = 0,y = 0,d = 0; i < m * n; i ++){
           st[x][y] = true;
           res.add(matrix[x][y]);

           int a = x + dx[d];
           int b = y + dy[d];

           if(a < 0 || a >= n || b < 0 || b >= m ||st[a][b]){
               d = (d + 1) % 4;
               a = x + dx[d];
               b = y + dy[d];
           }
           x = a;
           y = b;
       }
       return res;
   }
}

##56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

贪心模拟

  • 思路

以每个区间的第一个数排序,之后再分情况跟新答案

1.如果当前区间的最小值大于上个区间的最大值,则不合并区间。

  1. 反之则将两个区间合并,最小值为上个区间最小值,最大值为max(上个区间最大值,当前区间最大值)

本题可以加一个index记录区间个数

code

class Solution {
    public int[][] merge(int[][] a) {
        int[][] res = new int[a.length][2];
        Arrays.sort(a,(x,y) ->x[0] - y[0]);
        int idx = -1;
        for(int[] b : a ){
            if(idx == -1 || b[0] > res[idx][1]){
                res[++ idx] = b;
            }else{
                res[idx][1] = Math.max(b[1],res[idx][1]);
            }
        }
        return Arrays.copyOf(res,idx + 1);
    }
}

##200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。
  • 思路

dfs(flood fill) or 并查集

1.遍历网格所有位置,如果该位置为1,就通过dfs把和他相连的所有为1的岛屿置为0; 2.这种dfs可以先定义四个方向数组以深度搜索

  • 注意点 递归时的边界处理。 对于网格的副本处理。
全部评论

相关推荐

11-07 13:31
怀化学院 Java
勇敢牛牛不怕难:又疯一个
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务