LeetCode刷题笔记

23. Merge k Sorted Lists

要点:

1. 学会数据结构PriorityQueue(优先队列)的用法, 通过给优先队列传入自定义的经过复写compare方法的比较器实现大根堆或者小根堆。

2. PriorityQueue中不能存放null值,所以每次更新优先队列都需要作判空检查,如遇null值直接剔除。 

 

 1 import java.util.Comparator;
 2 import java.util.PriorityQueue;
 3 
 4 class Solution {
 5     public ListNode mergeKLists(ListNode[] lists) {
 6         //要点1
 7         PriorityQueue<ListNode> nodesQueue = new PriorityQueue<>(new Comparator<ListNode>() {
 8             @Override
 9             //将compare方法重写为比较ListNode的val值大小
10             public int compare(ListNode o1, ListNode o2) {
11                 return o1.val-o2.val;
12             }
13         });
14 
15         ListNode head = new ListNode(0);
16         for(ListNode node:lists)
17             if(node!=null)
18                 nodesQueue.add(node);
19 
20         ListNode p = head;
21         while(!nodesQueue.isEmpty()){
22             //判空检查,如果ListNode的下一个Node是null,则取出该Node后不再将其下一个节点放回优先队列。
23             if(nodesQueue.peek().next==null){
24                 p.next = new ListNode(nodesQueue.poll().val);
25                 p = p.next;
26             }else{
27                 ListNode tmp = nodesQueue.poll();
28                 p.next = new ListNode(tmp.val);
29                 p = p.next;
30                 nodesQueue.add(tmp.next);
31             }
32         }
33         return head.next;
34     }
35 
36 }

29. Divide Two Integers

class Solution {
    //用dividend循环减去diviso并计数减了多少次,这种方法会tle
    public static int divide(int dividend, int divisor) {
        //处理两种溢出情况
        if(dividend == -2147483648){
            if(divisor == 1)
                return -2147483648;
            if(divisor == -1)
                return 2147483647;
        }
        /**
           32位整数的取值范围是-2147483648~+2147483647
           将两个数都转为负数,以处理最大溢出-2147483648
        */
        
        //保存符号
        int flag = 1;
        if((dividend>0&&divisor<0)||(dividend<0&&divisor>0))
            flag = -1;
        //都转为负数
        dividend = dividend>0?-dividend:dividend;
        divisor = divisor>0?-divisor:divisor;
        int res = 0;
        if(dividend>divisor)
            return 0;
        //开始循环取商
        while(dividend<0){
            int k = 1;
            //divisor_tmp是变量,只要比dividend小就每次加倍,同时k作为商的计数也加倍
            int divisor_tmp = divisor;
            int tmp=0;
            while(dividend<divisor_tmp){
                tmp = divisor_tmp;
                //divisor_tmp在加倍的过程中有可能溢出,需要处理
                if(divisor_tmp+divisor_tmp>0||divisor_tmp+divisor_tmp==-2147483648)
                    break;
                divisor_tmp += divisor_tmp;
                k += k;
            }
            //如果跳出加倍循环后,dividend比divisor_tmp大,说明divisor_tmp加倍超过了dividend,需要撤销上一次的加倍
            if(dividend>divisor_tmp){
                divisor_tmp -= tmp;
                k>>=1;
            }
            //从divideng减去divisor_tmp并保存加倍值
            dividend -= divisor_tmp;
            res += k;
        }
        return res*flag;
    }

}

32. Longest Valid Parentheses

连同下面的解释,参考自 https://www.acwing.com/solution/LeetCode/content/114/

算法

(双指针扫描、贪心) O(n)O(n)
假设当前从前到后统计合法括号子串,令(的权值为1,)的权值为-1。首先记录start为某个起点,则在i向后移动的过程中,若当前[start,i]区间和等于0,该字符串是合法的;若区间和大于0,则说明目前缺少右括号,可以不修改start;若区间和小于0,则说明区间已经不合法了,需要修正start为i+1。初始时start从0开始即可。
可是对于…((((合法)(((这种情况,以上算法不能够准确捕捉到最长的合法子串,此时我们逆向考虑,将以上过程反向,从后向前统计,即可处理所有的情况。

时间复杂度

两次线性扫描,故时间复杂度为O(n)O(n)。

class Solution {
    public int longestValidParentheses(String s) {
       char[] chars = s.toCharArray();
        int[] marks = new int[chars.length];
        for(int i=0; i<chars.length;i++)
            marks[i] = chars[i]=='('?1:-1;

        int pr = 0;
        int pl = 0;
        int distance = 0;
        int tmp = 0;
        while(pr<marks.length){
            tmp+=marks[pr];
            if(tmp<0){
                tmp = 0;
                pl = pr+1;
            }
            if(tmp == 0)
                distance = pr-pl+1>distance?pr-pl+1:distance;
            pr++;
        }

        pr = marks.length-1;
        pl = pr;
        tmp = 0;
        while(pl>=0){
            tmp += marks[pl] ;
            if(tmp>0) {
                tmp = 0;
                pr = pl-1;
            }
            if(tmp == 0)
                distance = pr-pl+1>distance?pr-pl+1:distance;

            pl--;
        }
        return distance;
    }
}

554. Brick Wall

这是在FreeWheel面试中遇到的一道题目,思考一下题目并不难。

穿过最少砖的直线肯定是穿过缝隙最多的线,所以可以遍历整个List,获取每一行每条缝隙对应的砖宽, 将砖宽度和砖宽度出现的次数存入Map,最后遍历map获取出现次数最多的宽度,总行数-出现次数即为结果。

要注意几个边界条件:

1. 如果每行都只有一块相同长度的砖块,(例如输入为:[[1][1][1]]),则任何划线方式在每行都会穿过一块砖,注意题目说明了不能在墙的左右边界划线;

2. 只要有任意一行出现了两块以及两块以上的砖,则至少可以穿越一条砖缝。

import java.util.*;
    class Solution {
        public int leastBricks(List<List<Integer>> wall) {
            Map<Integer, Integer> map = new HashMap<>();
            int max = 1;//初始为1,如果不是出现了[[1][1][1]]的输入,则至少能穿越一个缝隙
            boolean flag = true;//此标志就是为了判断是否出现了[[1][1][1]]的输入
            for(List list: wall){
                int tmp = 0;
                if(list.size()>1)//只要有任意一行砖块的数量超过1块,则不可能是类似于[[1][1][1]]的输入
                    flag = false;
//统计每个砖缝出现的位置,在砖缝出现位置最多的地方划线即可
                for(int i = 0; i<list.size()-1; i++){
                    tmp+=(Integer)list.get(i);
                    if(!map.containsKey(tmp))
                        map.put(tmp,1);
                    else{
                        int cnt = map.get(tmp)+1;
                        map.put(tmp, cnt);
                        max = cnt>max?cnt:max;
                    }
                }
            }
            if(!flag)
                return wall.size()-max;
            else 
                return wall.size();
        }
    }

深度优先搜索专题

130. 被围绕的区域

先用dfs把跟边缘相连的‘O’标记为'#', 这样剩下的‘O’都是被围绕的区域。再遍历这个二维字符数组,把所有的'O'换为‘X’, 所有的'#'换回'O'

class Solution {
    public void solve(char[][] board) {
//行数或者列数小于等于2的矩阵所有的元素都在边上
if(board == null||board.length<3||board[0].length<3) return;
//用dfs将所有的'O'标记为'#'
for(int i = 0; i < board.length; i++){ for(int j = 0; j<board[0].length;j++){ boolean isEdge = (i==0 || j==0 || i==board.length-1 || j==board[0].length-1 ); if(isEdge && board[i][j] == 'O'){ dfs(board, i, j); } } } //把所有的'O'换为‘X’, 所有的'#'换回'O' for(int i = 0; i < board.length; i++){ for(int j = 0; j<board[0].length;j++){ if(board[i][j] == 'O') board[i][j] = 'X'; if(board[i][j] == '#') board[i][j] = 'O'; } } }
//dfs深度优先遍历
void dfs(char[][] board, int i, int j){ if(i<0||i>=board.length||j<0||j>=board[0].length||board[i][j]=='X'||board[i][j]=='#') return; board[i][j] = '#'; dfs(board, i-1, j); dfs(board, i+1, j); dfs(board, i, j-1); dfs(board, i, j+1); } }

DFS使用栈来把递归转为循环

非递归的方式,我们需要记录每一次遍历过的位置,我们用 stack 来记录,因为它先进后出的特点。

class Solution {
    public void solve(char[][] board) {
        //棋盘的行数或者列数小于3的时候所有的元素都是边上的元素
        if (board == null || board.length < 3 || board[0].length < 3) return;
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 从边缘第一个是o的开始搜索
                boolean isEdge = (i == 0 || j == 0 || i == m - 1 || j == n - 1);
                if (isEdge && board[i][j] == 'O') {
                    dfs(board, i, j);
                }
            }
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
                if (board[i][j] == '#') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    public void dfs(char[][] board, int i, int j) {
        int[] dx = {-1, 0, 1, 0};
        int[] dy = {0, 1, 0, -1};
        Stack<int[]> stack = new Stack<>();
        stack.push(new int[]{i,j});
        board[i][j] = '#';
        while (!stack.isEmpty()) {
            int[] current = stack.pop();
            for(int k = 0; k<4; k++){
                int x = current[0] + dx[k];
                int y = current[1] + dy[k];
                if(x>0 && x<board.length && y>0 && y<board[0].length && board[x][y] == 'O'){
                    board[x][y] = '#';
                    stack.push(new int[]{x, y});
                }
            }
        }
    }
}

与本题及其类似的零一道题:

200. Number of Islands

class Solution {
    public int numIslands(char[][] grid) {
        if(grid == null || grid.length == 0 || (grid.length == 1 && grid[0] == null))
            return 0;
        int cnt = 0;
        for(int i = 0; i<grid.length; i++){
            for(int j = 0; j<grid[0].length; j++){
                if(grid[i][j] == '1'){
                    cnt++;
                    //用深搜将所有与(i, j)相连的1都变为0
                    dfs(grid, i, j);
                }
            }
        }
        return cnt;
    }
    
    int[][] d = {{-1, 0},{0, 1},{1, 0},{0, -1}};
    void dfs(char[][] grid, int i, int j){
        if(i>=0 && i< grid.length && j>=0 && j<grid[0].length && grid[i][j] == '1'){
            grid[i][j] = '0';
            for(int k = 0; k < 4; k++){
                int x = i + d[k][0];
                int y = j + d[k][1];
                dfs(grid, x, y);
            }
            return;
        }
        return;
    }
}

 

329. Longest Increasing Path in a Matrix

朴素dfs, 暴力深搜,TLE

package 搜索与遍历_BFS_DFS_树和图的遍历.DFS.leetcode深搜题.leetCode329;

public class leetcode329Longest_Increasing_Path_in_a_Matrix_朴素DFS {
    int m, n;
    int[][] dirs = {{-1, 0}, {0, 1,}, {1, 0}, {0, -1}};

    public int longestIncreasingPath(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || (matrix.length == 1 && matrix[0].length == 0))
            return 0;
        m = matrix.length;
        n = matrix[0].length;
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ans = Math.max(ans, dfs(i, j, matrix));
            }
        }
        return ans;
    }


    int dfs(int i, int j, int[][] matrix) {
        int res = 0;
        for (int[] d : dirs) {
            int x = i + d[0];
            int y = j + d[1];
            if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
                res = Math.max(res, dfs(x, y, matrix));
            }
        }
        return ++res;
    }
}

记忆化搜索, accpet:

class Solution {
    int m, n;
    int[][] dirs = {{-1,0},{0,1,},{1,0},{0,-1}};
    public int longestIncreasingPath(int[][] matrix) {
        if(matrix == null || matrix.length == 0 || (matrix.length == 1 && matrix[0].length == 0))
            return 0;
        m = matrix.length;
        n = matrix[0].length;
        int[][] cache = new int[m][n];
        int ans = 0;
        for(int i = 0; i<m; i++){
            for(int j = 0; j<n; j++){
                ans = Math.max(ans,dfs(i, j, matrix,cache));
            }
        }
        return ans;
    }
    

    int dfs(int i, int j, int[][] matrix, int[][] cache){
        if(cache[i][j] != 0)
            return cache[i][j];
        for(int[] d :  dirs){
            int x = i + d[0];
            int y = j + d[1];
            if(x>=0 && x<m&&y>=0&&y<n&&matrix[x][y]>matrix[i][j]){
                cache[i][j] = Math.max(cache[i][j], dfs(x,y,matrix,cache));
            }
        }
        return ++cache[i][j];
    }
}

695. Max Area of Island

package 搜索与遍历_BFS_DFS_树和图的遍历.DFS.leetcode深搜题;

public class leetCode695Max_Area_of_Island {
    int[][] dirs = {{-1,0},{0,1},{1,0},{0,-1}};
    int m, n;
    public int maxAreaOfIsland(int[][] grid) {
        if(grid == null || grid.length == 0 || (grid.length == 1 && grid[0].length == 0))
            return 0;
        m = grid.length;
        n = grid[0].length;
        //boolean[][] visited = new boolean[m][n];
        int max = 0;
        for(int i = 0; i<m; i++){
            for(int j = 0; j<n; j++){
                if(grid[i][j] == 1)
                    max = Math.max(max, dfs(i, j, grid));
            }
        }
        return max;
    }

    int dfs(int i, int j, int[][] grid){
        grid[i][j] = 0;
        int res = 0;
        for(int[] d:dirs){
            int x = i+d[0];
            int y = j+d[1];
            if(x>=0 && x<m && y>=0 && y<n && grid[x][y] == 1){
                res += dfs(x, y, grid);
            }
        }
        return ++res;
    }
}

并查集专题

两道并查集

547. Friend Circles

 

200. Number of Islands

 

LintCode

386. 最多有k个不同字符的最长子字符串

描述

给定字符串S,找到最多有k个不同字符的最长子串T

样例

样例 1:

输入: S = "eceba" 并且 k = 3
输出: 4
解释: T = "eceb"

样例 2:

输入: S = "WORLD" 并且 k = 4
输出: 4
解释: T = "WORL" 或 "ORLD"

挑战

O(n) 时间复杂度

public static int lengthOfLongestSubstringKDistinct(String s, int k) {
        // write your code here
        int result = 0;
        int left = 0;
        //声明一个HashMap,用来存储k Distinct,还可以根据value用来判断元素是否可以删除
        HashMap<Character, Integer> map = new HashMap<>();
        for (int right = 0; right < s.length(); right++) {
            //右指针依次把字符串中的字符放到map中
            map.put(s.charAt(right),right);
            //当map元素大于k Distinct时,得到满足要求的子字符串
            while (map.size() > k) {
                // 删除最左的字符,删除成功,则退出循环
                char leftChar = s.charAt(left);
                //如果map.get(leftChar) == left条件成立,则说明leftChar从一开始
                //在left位置被添加到map中去后就没有更新过, 对应leftChar的下标还是
                //left, 如果在第一次被加到left位置之后,后面又出现了这个元素,则这个
                //map.get(leftChar)时得到的是最新添加该元素的下标;
                //在移动左指针的时候,如果这个元素在left和right之间只出现了一次,就要 
                //从map的统计中去掉。
                if (map.get(leftChar) == left) {
                    map.remove(leftChar);
                }
                // 左指针右移
                left++;
            }
            //子结果即左右指针之间的长度
            int subResult = right - left + 1;
            //结果取最大
            result = Math.max(result,subResult);
        }
        return result;
    }

 简单题专题

463. Island Perimeter

package 刷题;

public class Solution463 {
    int m, n;
    public int islandPerimeter(int[][] grid) {
        if(grid==null || grid.length==0 || (grid.length == 1 && grid[0].length== 0))
            return 0;
        m = grid.length;
        n = grid[0].length;
        int res = 0;
        for(int i = 0; i<m; i++){
            for(int j = 0; j<n; j++){
                if(grid[i][j] == 1)
                    res += find(i, j, grid);
            }
        }
        return res;
    }

    int find(int i, int j, int[][] grid){
        int res = 0;
        if(i-1<0 || grid[i-1][j] == 0)
            res++;
        if(i+1>=m || grid[i+1][j] == 0)
            res++;
        if(j-1<0 || grid[i][j-1] == 0)
            res++;
        if(j+1>=n || grid[i][j+1] == 0)
            res++;
        return res;
    }
}

733. Flood Fill

package 刷题;

public class Solution733 {
    int m, n;
    int[][] dirs = {{-1,0},{0,1},{1,0},{0,-1}};
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        if(image==null || image.length==0 || (image.length==1 && image[0].length == 0))
            return image;
        m = image.length;
        n = image[0].length;
        int oldColor = image[sr][sc];
        if(sr<0 || sr>m || sc<0 || sc>n || oldColor == newColor)
            return image;
        dfs(image, sr, sc, newColor, oldColor);
        return image;
    }

    void dfs(int[][] image, int sr, int sc, int newColor, int oldColor){
        if(sr<0 || sr>=m || sc<0 || sc>=n || image[sr][sc]!=oldColor)
            return;
        image[sr][sc] = newColor;
        for(int[] d : dirs){
            dfs(image, sr+d[0], sc+d[1],newColor, oldColor);
        }
        return;
    }
}

104. Maximum Depth of Binary Tree

class Solution {public int maxDepth(TreeNode root) {
        if(root==null)
            return 0;
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

111. Minimum Depth of Binary Tree

class Solution {
//很多人写出的代码都不符合 1,2 这个测试用例,是因为没搞清楚题意

//题目中说明:叶子节点是指没有子节点的节点,这句话的意思是 1 不是叶子节点

//题目问的是到叶子节点的最短距离,所以所有返回结果为 1 当然不是这个结果

    public int minDepth(TreeNode root) {
        if(root == null)
            return 0;
        if(root.left==null && root.right == null)
            return 1;
        if(root.left == null)
            return minDepth(root.right)+1;
        if(root.right == null)
            return minDepth(root.left)+1;
        else
            return Math.min(minDepth(root.left),minDepth(root.right))+1;
    }
}

 

posted @ 2019-08-17 17:38  lllunaticer  阅读(259)  评论(0编辑  收藏  举报