【吐血整理】LeetCode与剑指Offer百道经典题解总结

本文整理了LeetCode与剑指Offer中百余道经典的题目,整理出了数组、字符串、链表、二叉树、栈、回溯、动态规划、位运算中的经典问题以及问题对应的链接地址和个人提供的题解,方便查阅以及练习。以下是百题的索引,点击即可找到题目的出处并练习,也可直接查阅答案。

建议刚开始刷题的朋友直接按一下的Tag分类一个专题一个专题地进行学习与作答,完全理解这一百题,一线大厂非你莫属。

文章目录

一.数组 & 字符串 & 双指针

1.两数之和

https://leetcode-cn.com/problems/two-sum

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 使用HashMap优化暴力搜索,时间复杂度O(n),空间复杂度O(n)
        int[] res = new int[2];
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int cur = nums[i];
            int need = target - cur;
            if (map.containsKey(need)) {
                res[0] = i;
                res[1] = map.get(need);
                break;
            } else {
                map.put(cur, i);
            }
        }
        return res;
    }
}

2.三数之和

https://leetcode-cn.com/problems/3sum

class Solution {
    /* 思路是现将nums排序,然后确定k为第一个数,再确定k左边的数left以及数组最右边的数right,让后面这两个数形成对撞指针,当sum < 0时,说明left太小,让left右移;若sum > 0,则说明right太大,right--; */
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums); // 先将nums排序
        for (int k = 0; k < n; k++) {
            // 当前数字已经大于0,那么之后的数子在相加也不可能等于0,结束程序
            if (nums[k] > 0) {
                break;
            }   
            // 跳过重复结果
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            // 定义左右指针
            int left = k + 1, right = n - 1;      
            // 不断地缩小范围
            while (left < right) {
                // sum为总和
                int sum = nums[k] + nums[left] + nums[right];
                if (sum < 0) {
                    left++;
                } else if (sum > 0) {
                    right--;
                } else {
                    // sum == 0,记录当前结果
                    res.add(Arrays.asList(nums[k], nums[left], nums[right]));
                    // 避免记录重复结果
                    while (left + 1 < right && nums[left + 1] == nums[left]) {
                        left++;
                    }
                    while (left < right - 1 && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    // 缩小窗口
                    left++;
                    right--;
                }
            }
        }
        return res;
    }
}

3.顺时针打印矩阵

https://leetcode-cn.com/problems/spiral-matrix

class Solution {
    /* 记录上下左右四个指针用来标识矩阵 */
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int rows = matrix.length;
        if (rows == 0) {
            return res;
        }
        int cols = matrix[0].length;
        // 左、右、上、下指针
        int left = 0, right = cols - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            // 从左到右收集
            for (int i = left; i <= right; i++) {
                res.add(matrix[top][i]);
            }
            // 从上到下收集
            for (int i = top + 1; i <= bottom; i++) {
                res.add(matrix[i][right]);
            }
            // 从右到左收集(需要防止第一次收集过)
            if (top != bottom) {
                for (int i = right - 1; i >= left; i--) {
                    res.add(matrix[bottom][i]);
                }
            }
            // 从下到上收集(需要防止第二次收集过)
            if (left != right) {
                for (int i = bottom - 1; i >= top + 1; i--) {
                    res.add(matrix[i][left]);
                }
            }
            // 收集完一圈,缩小矩阵
            left++;
            right--;
            top++;
            bottom--;
        }
        return res;
    }
}

4.构建乘积数组

https://www.nowcoder.com/practice/94a4d381a68b47b7a8bed86f2975db46?tpId=13&tqId=11204&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

import java.util.ArrayList;
import java.util.HashMap;

public class Solution {
    /* 画图可知,每个区域对应的下三角已经上三角的区域 */
    public int[] multiply(int[] A) {
        int length = A.length;
        int[] B = new int[length];
        if(length != 0 ){
            B[0] = 1;
            //计算下三角连乘
            for(int i = 1; i < length; i++){
                B[i] = B[i-1] * A[i-1];
            }
            int temp = 1;
            //计算上三角
            for(int j = length-2; j >= 0; j--){
                temp *= A[j+1];
                B[j] *= temp;
            }
        }
        return B;
    }
}

5.约瑟夫环问题

https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
public int LastRemaining_Solution(int n, int m) {
    // 约瑟夫环
    if (n == 0) {
        // 当前没有人参与
        return -1;
    }
    if (n == 1) {
        // 剩余该人生还,索引为0
        return 0;
    }
    // 约瑟夫环递推公式:f(n, m) = (f(n - 1, m) + m) % n
    return (LastRemaining_Solution(n - 1, m) + m) % n;
}
}

public class Solution {
/* 使用链表来模拟这一淘汰过程 */
public int LastRemaining_Solution(int n, int m) {
    if (n <= 0 || m <= 0) {
        return -1;
    }
    ArrayList<Integer> list = new ArrayList<>();
    // 先将数组添加到链表中
    for (int i = 0; i < n; i++) {
        list.add(i);
    }
    // 初始化索引-1
    int index = -1;
    // 当幸存者大于1时,进行淘汰
    while (list.size() > 1) {
        // 计算出需要淘汰的索引
        index = (index + m) % list.size();
        list.remove(index);
        // 记得让索引自减,以达下一轮的目标
        index--;
    }
    // 只剩一个元素时就是幸存者
    return list.get(0);
    }
}

6.左旋转字符串

https://www.nowcoder.com/practice/12d959b108cb42b1ab72cef4d36af5ec?tpId=13&tqId=11196&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 首先反转整体字符串,然后反转前半部分,再反转后半部分即可 */
    public String LeftRotateString(String str,int n) {
        if (str == null || str.length() == 0) {
            return str;
        }
        // 分三部分反转即可
        int len = str.length();
        n = n % str.length();
        // 反转全部
        char[] chars = str.toCharArray();
        reverse(chars, 0, len - 1);
        // 反转第一部分
        reverse(chars, 0, len - n - 1);
        // 反转第二部分
        reverse(chars, len - n, len - 1);
        return new String(chars);
    }
    
    /*
    * 反转left到right之间的字符
    */
    private void reverse(char[] chars, int left, int right) {
        while (left < right) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
    }
}

7.丑数判断

https://leetcode-cn.com/problems/ugly-number

class Solution {
    /* 依次除以2、3、5,然后判断最后为不为1即可知是否为丑数 */
    public boolean isUgly(int num) {
        if (num <= 0) {
            return false;
        }
        while (num % 2 == 0) {
            num /= 2;
        }
        while (num % 3 == 0) {
            num /= 3;
        }
        while (num % 5 == 0) {
            num /= 5;
        }
        return num == 1;
    }
}

8.第N个丑数

https://leetcode-cn.com/problems/ugly-number-ii

class Solution {
    /* 使用dp存储第n个丑数,定义p2、p3、p5分别指向dp中的第n个2、3、5类型的丑树,每次选出一个最小值用于作为当前的丑数*/
    public int nthUglyNumber(int n) {
        if (n < 1) {
            throw new RuntimeException("n不可小于1");
        }
        int[] dp = new int[n];
        dp[0] = 1;
        int p2 = 0, p3 = 0, p5 =0;
        for (int i = 1; i < n; i++) {
            // 获取丑数队列的最小值作为下一个丑数
            int min = Math.min(dp[p2] * 2, Math.min(dp[p3] * 3, dp[p5] * 5));
            // 将符合条件的丑数索引递增
            if (min == dp[p2] * 2) {
                p2++;
            }
            if (min == dp[p3] * 3) {
                p3++;
            }
            if (min == dp[p5] * 5) {
                p5++;
            }
            // 设置最小值为当前丑数
            dp[i] = min;
        }
        return dp[n - 1];
    }
}

9.调整数组使奇数位于偶数前面

https://www.nowcoder.com/practice/beb5aa231adc45b2a5dcc5b62c93f593?tpId=13&tqId=11166&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
     /* 使用插入排序,原地交换,能保证最后结果的有序性 */
     public void reOrderArray(int [] A) {
        if (A == null) {
            throw new RuntimeException("数组不能为空");
        }
        int cur = 0; // cur表示当前奇数的末尾
        for (int i = 0; i < A.length; i++) {
            // 为奇数时,往前交换,直到遇到cur
            int temp = i;
            if (A[temp] % 2 == 1) {
                while (temp > cur) {
                    swap(A, temp, temp - 1);
                    temp--;
                }
                cur++;//奇数位后移
            }
        }
    }
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
 }

10.替换空格

https://www.nowcoder.com/practice/4060ac7e3e404ad1a894ef3e17650423?tpId=13&tqId=11155&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 统计空格数然后加上字符数创建一个新的字符数组,再从头到尾填充,最后转化为字符串 */
    public String replaceSpace(StringBuffer str) {
        char[] chars = str.toString().toCharArray();
        // 统计空格数和非空格数
        int spaceCount = 0, charCount = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == ' ') {
                spaceCount++;
            } else {
                charCount++;
            }
        }
        // 计算最终所需长度
        int len = charCount + 3 * spaceCount;
        // 声明新数组
        char[] res = new char[len];
        int index1 = 0, index2 = 0;
        while (index1 < res.length && index2 < chars.length) {
            if (chars[index2] != ' ') {
                res[index1++] = chars[index2++];
            } else {
                // 遇到了空格,插入%20
                char[] aux = new char[]{'%', '2', '0'};
                for (char c:aux) {
                    res[index1++] = c;
                }
                index2++;
            }
        }
        return new String(res);
    }
}

11.实现pow函数

https://leetcode-cn.com/problems/powx-n

class Solution {
    /* 思路是将a^n递归拆分为a^(n/2)*a^(n/2) 「针对n为偶数情况」 
    *  或拆分为a^(n/2)*a^(n/2)*a *「针对n为奇数的情况」,最后再考虑符号
    */
    public double myPow(double x, long n) {
        // 边界条件
        if (n == 0) {
            return 1.0;
        }
        // 递归终止条件,当n==1或-1时返回x或1/x
        if (n == 1 || n == - 1) {
            return (n == 1) ? x : 1 / x;
        }
        // 递归拆分: a^b = a^(b/2) * a^(b/2) (b为偶数)
        // 递归拆分: a^b = a^(b/2) * a^(b/2) * a (b为奇数)
        long temp = Math.abs(n);
        double res = myPow(x, temp / 2);
        if (temp % 2 == 0) {
            // 偶数次幂
            res *= res;
        } else {
            // 奇数次幂
            res *= res * x;
        }
        // 是否为正数次幂
        if (n >= 0) {
            return res;
        } else {
            return 1 / res;
        }
    }
}

12.实现sqrt函数「int和double」

https://leetcode-cn.com/problems/sqrtx

class Solution {
    /* 夹逼法,int型,无需精度,舍弃小数点,只保留整数 */
    public int mySqrt(int x) {
        // 使用夹逼定理,一直缩小区间逼近
        long left = 0, right = x, mid = left + (right - left) / 2;
        while (left <= right) {
            mid = left + (right - left) / 2;
            if (mid*mid == x || mid*mid < x && (mid+1)*(mid+1) > x) {
                break;
            }
            if (mid*mid < x) {
                // 向右区间逼近
                left = mid + 1;
            } else {
                // 向左区间逼近
                right = mid - 1;
            }
        }
        return (int)mid;
    }
    
    /* 夹逼法,double型,需讨论精度 */
    public double mySqrt(double x) {
        // 使用夹逼定理,一直缩小区间逼近,附带精度校验
        double precision = 0.001;
        double left = 0.0, right = x, mid = left + (right - left) / 2;
        while (left <= right) {
            mid = left + (right - left) / 2;
            // 符合精度要求
            if (Math.abs(mid*mid - x) <= precision) {
                break;
            }
            if (mid*mid < x) {
                // 向右区间逼近
                left = mid + precision;
            } else {
                // 向左区间逼近
                right = mid - precision;
            }
        }
        return mid;
    }
}

13.旋转打印二维数组

https://leetcode-cn.com/problems/spiral-matrix

class Solution {
    /* 定义上下左右四个指针即可 */
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int rows = matrix.length;
        if (rows == 0) {
            return res;
        }
        int cols = matrix[0].length;
        int left = 0, right = cols - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            // 从左到右收集
            for (int i = left; i <= right; i++) {
                res.add(matrix[top][i]);
            }
            // 从上到下收集
            for (int i = top + 1; i <= bottom; i++) {
                res.add(matrix[i][right]);
            }
            // 从右到左收集(需要防止第一次收集过)
            if (top != bottom) {
                for (int i = right - 1; i >= left; i--) {
                    res.add(matrix[bottom][i]);
                }
            }
            // 从下到上收集(需要防止第二次收集过)
            if (left != right) {
                for (int i = bottom - 1; i >= top + 1; i--) {
                    res.add(matrix[i][left]);
                }
            }
            // 收集完一圈
            left++;
            right--;
            top++;
            bottom--;
        }
        return res;
    }
}

14.二维数组的查找

https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 在左下角开始寻找,若小于target,则往右寻找,若大于target,则往上寻找 */
    public boolean Find(int target, int [][] array) {
        if (array.length == 0) {
            return false;
        }
        int rows = array.length;
        int cols = array[0].length;
        int i = rows - 1, j = 0;
        while (i >= 0 && j < cols) {
            if (array[i][j] < target) {
                // 需要更大的数,往右侧走
                j++;
            } else if (array[i][j] > target) {
                // 需要更小的数,往上走
                i--;
            } else {
                return true;
            }
        }
        return false;
    }
}

15.数字中超过一半的数字

https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&tqId=11181&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {

    /* 利用快排的partition函数,时间复杂度为O(nlogn) */
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array.length == 0) {
            return 0;
        }
         if (array.length == 1) {
            return array[0];
         }
        int left = 0, right = array.length - 1, mid = left + (right - left)/2;
        int index = partition(array, left, right);
        while (index != mid) {
            if (index < mid) {
                // 需要在index的右边继续寻找,并且进行排序
                index = partition(array, index + 1, right);
            } else {
                // 需要在index的左边继续寻找,并且进行排序
                index = partition(array, left, index - 1);
            }
        }
        // 确定该数字出现是否超过了一半[若该数字出现超过半数,那么该数左边和右边都应该和它相等]
        if (array[index - 1] != array[index] || array[index] != array[index + 1]) {
            return 0;
        }
        return array[index];
    }
    private int partition(int[] array, int left, int right) {
        // 选取base
        int base = array[left];
        int i = left, j = right;
        while (i < j) {
            while (i < j && array[j] >= base) {
                j--;
            }
            while (i < j && array[i] <= base) {
                i++;
            }
            // 交换i, j
            swap(array, i, j);
        }
        // i, j相重合
        swap(array, left, i);
        return i;
    }
    private void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

16.盛最多水的容器

https://leetcode-cn.com/problems/container-with-most-water

class Solution {
    /* 对撞指针法,时间复杂度O(n) */
    public int maxArea(int[] height) {
        if (height == null) {
            throw new RuntimeException("input cant be null");
        }
        int left = 0, right = height.length - 1, res = 0;
        while (left < right) {
            // 比较当前的面积和之前的面积哪个更大
            res = Math.max(res, Math.min(height[left], height[right]) * (right - left));
            // 不断地移动短板才有可能扩大面积
            if (height[left] <= height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return res;
    }
}

17.删除数组重复项

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/

class Solution {
/* 数组重复项最多为2个,返回删除后的长度 */
public int removeDuplicates(int[] nums) {
        int n = nums.length;
        if (n <= 2) {
            return n;
        }
        int cur = 1; // 直接从第2个元素开始考虑
        // 从第3个元素开始考虑重复项
        for (int i = 2; i < n; i++) {
            // 当当前元素不等于cur-1位置的元素,说明没有两个以上重复项,cur继续往右走并交换
            if (nums[i] != nums[cur - 1]) {
                nums[++cur] = nums[i];
            }
        }
        // 返回最后的长度
        return cur + 1;
    }
}

18.在排序数组中寻找第一个和最后一个

https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array

class Solution {
    /* 使用二分搜索获取第一个和最后一个元素,时间复杂度O(logn) */
    public int[] searchRange(int[] nums, int target) {
        if (nums == null) {
            throw new RuntimeException("input cant be null");
        }
        int[] res = {-1, -1};
        res[0] = getBegin(nums, target);
        res[1] = getEnd(nums, target);
        return res;
    }
    private int getBegin(int[] array, int k) {
        int left = 0,right = array.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (array[mid] < k) {
                // 搜索右边
                left = mid + 1;
            } else if (array[mid] > k) {
                // 搜索左边
                right = mid - 1;
            } else {
                // 获取到第一个出现的位置
                if (mid == 0 || array[mid - 1] != array[mid]) {
                    return mid;
                } else {
                    // 继续往左搜索
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
     private int getEnd(int[] array, int k) {
         int left = 0,right = array.length - 1;
          while (left <= right) {
            int mid = left + (right - left) / 2;
            if (array[mid] < k) {
                // 搜索右边
                left = mid + 1;
            } else if (array[mid] > k) {
                // 搜索左边
                right = mid - 1;
            } else {
                // 获取到最后一个出现的位置
                if (mid == array.length -1 || array[mid + 1] != array[mid]) {
                    return mid;
                } else {
                    // 继续往右搜索
                    left = mid + 1;
                }
            }
        }
         return -1;
    }
}

19.旋转图像

https://leetcode-cn.com/problems/rotate-image

class Solution {
    /* 使用辅助空间,寻找旋转后图像与原图像的对应关系 */
    public void rotate(int[][] matrix) {
        if (matrix == null) {
            throw new RuntimeException("input cant be null");
        }
        // 使用辅助空间 + 遍历 对应关系映射
        int rows = matrix.length;
        if (rows == 0) {
            return;
        }
        int cols = matrix[0].length;
        int[][] aux = new int[rows][cols];
        // aux[i][j] = matrix[n-j-1][i];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                aux[i][j] = matrix[rows-j-1][i];
            }
        }
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                matrix[i][j] = aux[i][j];
            }
        }
    }
}

20.最大子序和

https://leetcode-cn.com/problems/maximum-subarray

class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        // dp[i]表示i及之前的最大连续和
        // dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])-->接上之前的连续和,或从当前开始作为最大连续和
        int[] dp = new int[n];
        dp[0] = nums[0];
        int res = dp[0];
        for (int i = 1; i < n; i++) {
            // 接上最大子序列和或直接从当前开始算起
            dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

21.接雨水

https://leetcode-cn.com/problems/trapping-rain-water

class Solution {
    /* 暴力搜索法 O(n^2),每到一个位置,就从当前位置向左搜索最大值并记录,再从当前位置向右搜索最大值并记录,然后取两者之间的较小值减去当前高度即可得到当前位置的雨水量 */
    public int trap(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        int res = 0, n = height.length;
        for (int i = 1; i < n - 1; i++) {
            int leftMax = 0, rightMax = 0;
            // 向左找最大的
            for (int j = i; j >= 0; j--) {
                leftMax = Math.max(leftMax, height[j]);
            }
            // 向右找最大的
            for (int j = i; j < n; j++) {
                rightMax = Math.max(rightMax, height[j]);
            }
            // 累加结果
            res += Math.min(leftMax, rightMax) - height[i];
        }
        return res;
    }
    
    /* 先构建leftMaxs和rightMaxs数组,最后再进行累加,时间复杂度O(n) */
    public int trap(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        int res = 0, n = height.length;
        int[] leftMaxs = new int[n];
        int[] rightMaxs = new int[n];
        leftMaxs[0] = height[0];
        rightMaxs[n - 1] = height[n - 1];
        // 从左往右构建leftMaxs数组
        for (int i = 1; i < n; i++) {
            leftMaxs[i] = Math.max(leftMaxs[i - 1], height[i]);
        }
        // 从右往左构建rightMaxs数组
        for (int i = n - 2; i >= 0; i--) {
            rightMaxs[i] = Math.max(rightMaxs[i + 1], height[i]);
        }
        // 进行累加
        for (int i = 1; i < n - 1; i++) {
            res += Math.min(leftMaxs[i], rightMaxs[i]) - height[i];
        }
        return res;
    }
}

22.颜色分类

https://leetcode-cn.com/problems/sort-colors

 class Solution {
    public void sortColors(int[] nums) {
        // TODO: 这是三路快排的划分应用
        partition3Way(nums);
    }

    private void partition3Way(int[] nums) {
        //基数为1
        int base = 1;
        int i = 0;
        int cur = i;
        int j = nums.length - 1;
        //在[0...i)区间,值为0
        //在[i...cur)区间,值为1
        //在(j...n - 1]区间,值为2
        //当cur >= j,终止划分
        while (cur <= j) {
            //三路快排的交换过程
            int curValue = nums[cur];
            if (curValue < base) {
                swap(nums, i++, cur++);
            } else if (curValue > base) {
                swap(nums, cur, j--);
            } else {
                cur++;
            }
        }
    }
    
    private void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

23.杨辉三角

https://leetcode-cn.com/problems/pascals-triangle

class Solution {
    /* 注意要首先在最左边填充1,填充完中间部分后,在最右边填充1 */
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<>();
        if (numRows == 0) {
            return res;
        }
        res.add(Arrays.asList(1));
        for (int i = 1; i < numRows; i++) {
            ArrayList<Integer> oneRes = new ArrayList<>();
            // 第一个1
            oneRes.add(1);
            for (int j = i - 1; j > 0; j--) {
                // 根据上一行填充中间部分
                List<Integer> lastRows = res.get(i - 1);
                oneRes.add(lastRows.get(j - 1) + lastRows.get(j));
            }
            // 最后一个1
            oneRes.add(1);
            // 将本次结果添加进res
            res.add(oneRes);
        }
        return res;
    }
}   

24.和为k的子数组

https://leetcode-cn.com/problems/subarray-sum-equals-k

class Solution {
    /* 暴力破解法,O(n^2) */
    public int subarraySum(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            int temp = 0;
            for (int j = i; j < nums.length; j++) {
                temp += nums[j];
                if (temp == k) {
                    res++;
                }
            }
        }
        return res;
    }
    
    /* 动态规划法 + 哈希表,时间复杂度O(n) */
    public int subarraySum(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        // dp[i]表示[0, i - 1]的累计和
        int[] dp = new int[nums.length + 1];
        dp[0] = nums[0];
        for (int i = 1; i < dp.length; i++) {
            dp[i] = dp[i - 1] + nums[i - 1];
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        int res = 0;
        for (int i = 0; i < dp.length; i++) {
            if (map.containsKey(dp[i] - k)) {
                res += map.get(dp[i] - k);
            }
            map.put(dp[i], map.getOrDefault(dp[i], 0) + 1);
        }
        return res;
    }
}

25.求众数

https://leetcode-cn.com/problems/majority-element-ii

class Solution {
    /* 使用哈希表作为计数器,时间复杂度O(n),空间复杂度O(k),k为不重复元素个数 */
    public List<Integer> majorityElement(int[] nums) {
        if (nums == null || nums.length == 0) {
            return new ArrayList<>();
        }
        List<Integer> res = new ArrayList<>();
        HashMap<Integer, Integer> map  =new HashMap<>();
        HashSet<Integer> set  =new HashSet<>();
        if (nums.length <= 2) {
            for (int i = 0; i < nums.length; i++) {
                if (!set.contains(nums[i])) {
                    set.add(nums[i]);
                    res.add(nums[i]);
                }
            }
            return res;
        }
        int k = nums.length / 3, n = nums.length;
        for (int i = 0; i < n; i++) {
            if (map.containsKey(nums[i])) {
                if (map.get(nums[i]) + 1 > k && !set.contains(nums[i])) {
                    set.add(nums[i]);
                    res.add(nums[i]);
                } else {
                    map.put(nums[i], map.get(nums[i]) + 1);
                } 
            } else {
                map.put(nums[i], 1);
            }
        }
        return res;
    }
}

26.返回数据流中第k大元素

https://leetcode-cn.com/problems/kth-largest-element-in-a-stream

class KthLargest {

    // 默认小顶堆
    private PriorityQueue<Integer> minHeap;
    // 记录k
    private int k = -1;

    /* 使用小顶堆来收集TOP K大的元素 */
    public KthLargest(int k, int[] nums) {
        this.k = k;
        this.minHeap = new PriorityQueue<>(k);
        for (int i = 0; i < nums.length; i++) {
            add(nums[i]);
        }
    }
    
    /* 在小顶堆中添加元素并返回第top k */
    public int add(int val) {
        if (minHeap.size() == k) {
            // 当元素大于堆头,删除堆头并添加该元素->重新建堆,时间复杂度O(logn)
            if (val > minHeap.peek()) {
                minHeap.poll();
                minHeap.offer(val);
            }  
        } else {
            minHeap.offer(val);
        }
        return minHeap.peek();
    }
}

27.滑动窗口最大值

https://leetcode-cn.com/problems/sliding-window-maximum

class Solution {
    /* 暴力破解法, 依次收集滑动窗口的最大值,时间复杂度 O(n * k) */
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k > nums.length) {
            return new int[]{};
        }
        int[] res = new int[nums.length - k + 1];
        for (int i = 0; i < res.length; i++) {
            int temp = nums[i];
            // 收集当前窗口的最大值
            for (int j = i; j < i + k; j++) {
                temp = Math.max(temp, nums[j]);
            }
            res[i] = temp;
        }
        return res;
    }
    
    /* 使用一个降序的队列充当滑动窗口去收集每个窗口的最大值,时间复杂度O(n) */
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k > nums.length) {
            return new int[]{};
        }
        int[] res = new int[nums.length - k + 1];
        // 降序队列
        LinkedList<Integer> queue = new LinkedList<>();
        int cur = 0;
        for (int i = 0; i < nums.length; i++) {
            // 1.维护降序队列
            while (!queue.isEmpty() && nums[i] > nums[queue.peekLast()]) {
                queue.pollLast();
            }
            queue.offerLast(i);
            // 2.确保队头不超出范围
            if (queue.peekFirst() == i - k) {
                queue.pollFirst();
            }
            // 3.当滑动窗口形成时,依次取队头作为滑动窗口的最大值
            if (i >= k - 1) {
                res[cur++] = nums[queue.peekFirst()];
            }
        }
        return res;
    }
    
}

28.数据流的中位数

https://leetcode-cn.com/problems/find-median-from-data-stream/

class MedianFinder {
    /* 使用大顶堆和小顶堆的解法 */
    private PriorityQueue<Integer> maxHeap;
    private PriorityQueue<Integer> minHeap;
    private int count;
    
    /** initialize your data structure here. */
    public MedianFinder() {
        maxHeap = new PriorityQueue<>((x, y) -> y - x);
        minHeap = new PriorityQueue<>();
        count = 0;
    }

    public void addNum(int num) {
        count += 1;
        maxHeap.offer(num);        
        minHeap.offer(maxHeap.poll());
        // 是奇数的话,需要把小顶堆中删去堆头并添加到大顶堆中,保持大顶堆元素比小顶堆多一个
        if ((count & 1) == 1) {
            maxHeap.offer(minHeap.poll());
        }
    }
    
    public double findMedian() {
        if (!maxHeap.isEmpty()) {
            if (maxHeap.size() - minHeap.size() == 1) {
                // 当大顶堆比小顶堆元素多1时,去大顶堆的堆头即为中位数
                return (double)maxHeap.peek();
            } 
            if (maxHeap.size() == minHeap.size()) {
                // 当大顶堆与小顶堆元素相同时,取两者堆头/2即获得中位数
                return (double)(maxHeap.peek() + minHeap.peek()) / 2;
            }
        }
        return 0.0;
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

29.滑动窗口中位数

https://leetcode-cn.com/problems/sliding-window-median

class Solution {
    /* 暴力搜索 O(n^2) */
    public double[] medianSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k > nums.length) {
            return new double[]{};
        }
        double[] res = new double[nums.length - k + 1];
        for (int i = 0; i < nums.length - k + 1; i++) {
            res[i] = getMedian(nums, i, i + k - 1);
        }
        return res;
    }

    private double getMedian(int[] nums, int i, int j) {
        // 获取[i, j]间的中位数
        int len = j - i + 1;
        double[] temp = new double[len];
        int cur = 0; double res = 0;
        for (int k = i; k <= j; k++) {
            temp[cur++] = nums[k];
        }
        Arrays.sort(temp);
        if (len % 2 == 0) {
            res = (temp[len / 2 - 1] + temp[len / 2]) / 2;
        } else {
            res = temp[len / 2];
        }
        return res;
    }
}

30.子数组最大平均数

https://leetcode-cn.com/problems/maximum-average-subarray-i

class Solution {
        public double findMaxAverage(int[] nums, int k) {
            if (nums == null || nums.length == 0 || k <= 0 || nums.length < k) {
                throw new RuntimeException("数组和k参数不合法");
            }
            double max = 0;
            // 第一个窗口
            for (int i = 0; i < k; i++) {
                max += nums[i];
            }
            double temp = max;
            // 遍历,删头加尾确定窗口最大值
            for (int i = 1; i <= nums.length - k; i++) {
                temp = temp - nums[i - 1] + nums[i + k - 1];
                max = Math.max(max, temp);
            }
            return max / k;
        }
    }

31.有效字母异位词

https://leetcode-cn.com/problems/valid-anagram

class Solution {
    /*  O(n) */
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        int[] nums = new int[26]; 
        // 每个位置记录字符出现次数
        for (int i = 0; i < s.length(); i++) {
            nums[s.charAt(i)-'a']++;
        }
        // 字符抵消
        for (int i = 0; i < t.length(); i++) {
            nums[t.charAt(i)-'a']--;
        }
        // 如果仍有字符没有被抵消,说明不是异位词
        for (int count: nums) {
            if (count != 0) {
                return false;
            }
        }
        return true;
    }
}

32.翻转单词序列

https://www.nowcoder.com/practice/3194a4f4cf814f63919d0790578d51f3?tpId=13&tqId=11197&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 先将整体反转,然后对单词进行逐个反转,时间复杂度O(n) */
    public String ReverseSentence(String str) {
        if (str == null || str.trim().equals("")) {
            return str;
        }
        char[] chars = str.toCharArray();
        // 反转整个单词序列
        reverse(chars, 0, chars.length - 1);
        // 依次反转每个单词
        int left = 0, right = 0;
        while (left < chars.length) {
            // 如果开头就是空格,那么两者一起跳过
            if (chars[left] == ' ') {
                left++;
                right++;
            } else if (right == chars.length - 1) {
                // 如果right已经走到结尾,直接反转
                reverse(chars, left, right);
                break;
            } else if (chars[right] != ' ') {
                // 如果right遇到的不是空格,那么继续寻找单词的末尾空格
                right++;
            } else if (chars[right] == ' ') {
                // right已经走到一个单词的空格处,反转
                reverse(chars, left, --right);
                left = ++right;
            }
        }
        return new String(chars);
    }
    private void reverse(char[] chars, int i , int j) {
        while (i < j) {
            char temp = chars[i];
            chars[i] = chars[j];
            chars[j] = temp;
            i++;
            j--;
        }
    }
}

33.无重复字符的最长子串

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

class Solution {
    /* 使用HashSet保存窗口出现的字符,时间复杂度O(n) */
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        char[] chars = s.toCharArray();
        HashSet<Character> set = new HashSet<>();
        int left = 0, right = 0, res = 0;
        while (left <= right && right < chars.length) {
            if (!set.contains(chars[right])) {
                // 滑动窗口右扩张
                set.add(chars[right++]);
                res = Math.max(res, right - left);
            } else {
                // 滑动窗口左缩小
                set.remove(chars[left++]);
            }
        }
        return res;
    }
}

34.七进制转化

https://leetcode-cn.com/problems/base-7/

class Solution {
    public String convertToBase7(int num) {
        if (num == 0) {
            return "0";
        }
        boolean flag = false; // 是否为负数
        if (num < 0) {
            num = -1 * num;
            flag = true;
        }
        StringBuilder builder = new StringBuilder();
        // 商为下一次的值,余数为当前的七进制值,从后往前追加
        while (num > 0) {
            int v = num % 7;// 余数
            num /= 7; // 商
            builder.insert(0, v);
        }
        // 负数处理
        if (flag) {
            builder.insert(0, "-");
        }
        return builder.toString();
    }
}

二.位运算

1.位1的个数

https://leetcode-cn.com/problems/number-of-1-bits

public class Solution {
    /* 解法1,循环n次,n为二进制数长度 */
    public int hammingWeight(int n) {
        int res = 0;
        String bin = Integer.toBinaryString(n);
        for (int i = 0; i < bin.length(); i++) {
            if (bin.charAt(i) == '1') {
                res++;
            }
        }
        return res;
    }
    
     /* 解法2,循环k次,k为二进制数中最高位的1的位数 */
    public int hammingWeight(int n) {
        int res = 0;
        while (n != 0) {
            if ((n & 1) == 1) {
                res++;
            }
            // 无符号右移,消去末尾1位
            n >>>= 1;
        }
        return res;
    }

    /* 解法3,循环res次 */
    public int hammingWeight(int n) {
        int res = 0;
        while (n != 0) {
            res++;
            n = (n - 1) & n;
        }
        return res;
    }
}

2.2的幂

https://leetcode-cn.com/problems/power-of-two

class Solution {
    public boolean isPowerOfTwo(int n) {
        // 一个数是2的幂,那么这个数的二进制中只有1个1,使用(n-1)&n消去1后为0
        if (n == 0) {
            return false;
        }
        // 转化为long型防止溢出
        long ln = (long)n;
        if (((ln - 1) & ln) != 0) {
            return false;
        }
        return true;
    }
}

3.只出现一次的数字

https://leetcode-cn.com/problems/single-number

class Solution {
    public int singleNumber(int[] nums) {
        // 1.任何数异或0都等于原数
        // 2.相同的数异或为0
        int res = 0;
        for (int value: nums) {
            res ^= value;
        }
        return res;
    }
}

4.只出现一次的数字2

https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&tqId=11193&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.HashMap;
public class Solution {
    /* 使用HashMap记录数字出现的次数, 时间复杂度O(n),空间复杂度O(k),k为不重复的元素个数 */
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if (array == null || array.length == 0) {
            return;
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            // 记录每个元素出现的次数
             map.put(array[i], map.getOrDefault(array[i], 0) + 1);
        }
        boolean first = false;
        for (int i = 0; i < array.length; i++) {
             if (map.getOrDefault(array[i], 0) == 1) {
                 if (!first) {
                     // 填充第一个元素
                     num1[0] = array[i];
                     first = true;
                     // 标识已经找到
                     map.put(array[i], -1);
                 } else {
                     // 第二个元素
                     num2[0] = array[i];
                 }
             }
        }
    }
    
    /*
    使用位运算,先将所有的元素异或一遍,相当于两个不重复的元素之间做了一次异或,
    然后根据异或得到的结果,获取结果的最低位1出现的位置,再通过该位置将数组分割成两个部分,
    这两个部分分别含有一个唯一的元素,再次异或即可分别获取。时间复杂度O(n),空间复杂度O(1)
    */
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if (array == null || array.length == 0) {
            return;
        }
        // 全体异或
        int res = 0;
        for (int value: array) {
            res ^= value;
        }
        // 获取异或结果的最低位1的位置
        int index = getLower1Index(res);
        // 根据这个位置将数组分割成两个部分来进行异或,就可以得到相应的唯一元素
        int res1 = 0, res2 = 0;
        for (int value: array) {
            if (getLower1Index(value) == index) {
                res1 ^= value;
            } else {
                res2 ^= value;
            }
        }
        num1[0] = res1;
        num2[0] = res2;
    }
    /* 获取低位的1位置 */
    private int getLower1Index(int num) {
        int index = -1;
        while ((num & 1) != 1) {
            // 当最低位不为1,则无符号右移动
            index++;
            num >>>= 1;
        }
        return index;
    }
}

三.回溯

1.全排列

https://leetcode-cn.com/problems/permutations

class Solution {
    // 存储结果
    private List<List<Integer>> res = new ArrayList<>();
    // 是否已使用该位置
    private boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
            used = new boolean[nums.length];
            // 单个答案的数据数
            int count = 0;
            ArrayList<Integer> oneRes = new ArrayList<>();
            // 回溯搜索
            backTraceSearch(count, nums, oneRes);
            return res;
    }
    private void backTraceSearch(int count, int[] nums, ArrayList<Integer> oneRes) {
        // 回溯终止条件
        if (count == nums.length) {
            res.add(new ArrayList<>(oneRes));
            return;
        }
        // 回溯搜索
        for (int i = 0; i < nums.length; i++) {
            if (!used[i]) {
                // 没有使用过
                used[i] = true;
                oneRes.add(nums[i]);
                backTraceSearch(count + 1, nums, oneRes);
                // 状态回溯
                oneRes.remove(oneRes.size() - 1);
                used[i] = false;
            }
        }
        return;
    }
}

2.组合总数

https://leetcode-cn.com/problems/combinations

class Solution {
    // 存储结果
    private List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> combine(int n, int k) {
            // 开始索引
            int index = 1;
            ArrayList<Integer> oneRes = new ArrayList<>();
            // 回溯搜索
            backTraceSearch(index, k, n, oneRes);
            return res;
    }
    private void backTraceSearch(int index, int k, int n, ArrayList<Integer> oneRes) {
        // 回溯终止条件
        if (oneRes.size() == k) {
            res.add(new ArrayList<>(oneRes));
            return;
        }
        // 回溯搜索
        for (int i = index; i <= n; i++) {
            oneRes.add(i);
            backTraceSearch(i + 1, k, n, oneRes);
            // 状态回溯
            oneRes.remove(oneRes.size() - 1);
        }
        return;
    }
}

3.N皇后问题

https://leetcode-cn.com/problems/n-queens-ii/

class Solution {
    private int res = 0;
    // 记录已经摆放了的皇后位置
    private int[] record;

    public int totalNQueens(int n) {
        record = new int[n];
        int row = 0;
        backTraceSearch(row, n);
        return res;
    }
    private void backTraceSearch(int row, int n) {
        // 回溯终止条件
        if (row == n) {
            res++;
            return;
        }
        // 回溯搜索,逐列寻找摆放皇后的位置
        for (int col = 0; col < n; col++) {
            if (check(row, col)) {
                // 该位置可以摆放皇后,继续往上一行放置皇后
                record[row] = col;
                backTraceSearch(row + 1, n);
                // 状态回溯
                record[row] = 0;
            }
        }
        return;
    }
    /* 检查该位置是否可放置皇后 */
    private boolean check(int row, int col) {
        for (int i = 0; i < row; i++) {
            // 检查该列以下是否存在皇后 
            if (record[i] == col) {
                return false;
            }
             // 检查对角线是否含皇后
             if (Math.abs(i - row) == Math.abs(record[i] - col)) {
                 return false;
             }
        }
        return true;
    }
}

4.复原IP地址

https://leetcode-cn.com/problems/restore-ip-addresses

class Solution {
    private List<String> res = new ArrayList<>();
    
    public List<String> restoreIpAddresses(String s) {
        // 当前索引
        int index = 0;    
        // 当前结果 
        ArrayList<String> oneRes = new ArrayList<>();
        backTraceSearch(s, index, oneRes);
        return res;
    }
    private void backTraceSearch(String s, int index, ArrayList<String> oneRes) {
        // 回溯终止条件
        if (oneRes.size() == 4) {
            // 已经是最后一个位置
            if (index == s.length()) {
                res.add(String.join(".", oneRes));
            }
            return;
        }
        // 回溯搜索
        for (int i = 1; i <= 3; i++) {
            // IP段最长为3位
            if (index + i > s.length()) {
                break;
            }
            // 获取分段
            String segment = s.substring(index, index + i);
            // 分段校验
            if (segment.startsWith("0") && segment.length() > 1 || 
                 segment.length() == 3 && Integer.valueOf(segment) > 255) {
                break;
            }
            oneRes.add(segment);
            backTraceSearch(s, index + i, oneRes);
            // 状态回溯
            oneRes.remove(oneRes.size() - 1);
        }
        return;
    }
}

5.括号生成

https://leetcode-cn.com/problems/generate-parentheses

class Solution {
    private List<String> res = new ArrayList<>();

    public List<String> generateParenthesis(int n) {
        // 括号总数、左括号数量、右括号数量
        int count = 0, left = 0, right = 0;
        backTraceSearch(count, left, right, n, new StringBuilder());    
        return res;
    }
    private void backTraceSearch(int count, int left, int right, int n, StringBuilder oneRes) {
        // 回溯终止条件
        if (count == 2 * n) {
            res.add(oneRes.toString());
            return;
        }
        // 回溯搜索
        if (left < n) {
            oneRes.append("(");
            backTraceSearch(count + 1, left + 1, right, n, oneRes);
            oneRes.deleteCharAt(oneRes.length() - 1);
        }
        if (right < left) {
            oneRes.append(")");
            backTraceSearch(count + 1, left, right + 1, n, oneRes);
            oneRes.deleteCharAt(oneRes.length() - 1);
        }
        return;
    }

}   

6.岛屿数量

https://leetcode-cn.com/problems/number-of-islands/

class Solution {
    //         x-1,y
    //  x,y-1   x,y     x,y+1
    //         x+1,y
    // 方向数组
    private int[][] direction = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    // 最终结果
    private int res = 0;
    // 是否已被标记过
    private boolean[][] marked;
    // 地图
    private char[][] grid;
        
    /* 使用回溯搜索 + 深度优先搜索的方式,上下左右地搜索 */
    public int numIslands(char[][] grid) {
        int rows = grid.length;
        if (rows == 0) {
            return res;
        }
        int cols = grid[0].length;
        marked = new boolean[rows][cols];
        this.grid = grid;
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // 如果是没被标记过的陆地,则在此进行回溯搜索(dfs)
                if (!marked[i][j] && grid[i][j] == '1') {
                    res++;
                    backTraceSearch(i, j);
                }
            }
        }
        return res;
    }
    private void backTraceSearch(int row, int col) {
        // 标记该位置被访问
        marked[row][col] = true;
        // 进行上右下左的回溯搜索
        for (int i = 0; i < 4; i++) {
            int x = row + direction[i][0];
            int y = col + direction[i][1];
            // 剪枝,越界的和非陆地、被访问过的区域不再访问
            if (x >= 0 && y >= 0 && x < grid.length && y < grid[0].length && grid[x][y] == '1' && marked[x][y] == false) {
                backTraceSearch(x, y);
            }
        }
    }
}

7.正则表达式匹配

https://leetcode-cn.com/problems/regular-expression-matching/

/* 暴力递归匹配 */

class Solution {
    public boolean isMatch(String s, String p) {
    //如果正则串p为空字符串s也为空这匹配成功,如果正则串p为空但是s不是空则说明匹配失败
    if (p.isEmpty()) {
        return s.isEmpty();
    }
    //判断s和p的首字符是否匹配,注意要先判断s不为空
    boolean headMatched = !s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.');
    if (p.length() >= 2 && p.charAt(1) == '*') {//如果p的第一个元素的下一个元素是*
        //则分别对两种情况进行判断
        return isMatch(s, p.substring(2)) ||
            (headMatched && isMatch(s.substring(1), p));
    } else if (headMatched) {//否则,如果s和p的首字符相等
        return isMatch(s.substring(1), p.substring(1));
    }else {
        return false;
    }
}
}



/* 回溯搜索 */
public class Solution {
    public boolean isMatch(String str, String pattern) {
    if (str == null || pattern == null) {
        return false;
    }
    int strIndex = 0;
    int patternIndex = 0;
    return matchCore(str.toCharArray(), strIndex, pattern.toCharArray(), patternIndex);
}
  
  
/*
*当模式中的第二个字符不是“*”时:
*1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模*式都后移一个字符,然后匹配剩余的。
*2、如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。
*
*而当模式中的第二个字符是“*”时:
*如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继*续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方*式:
*1、模式后移2字符,相当于x被忽略;
*2、字符串后移1字符,模式后移2字符;
*3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位;
*/
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
    // 有效性检验:str到尾,pattern到尾,匹配成功
    if (strIndex == str.length && patternIndex == pattern.length) {
        return true;
    }
    // pattern先到尾,匹配失败
    if (strIndex != str.length && patternIndex == pattern.length) {
        return false;
    }
    //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
    if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
        // 分三种情况匹配
        if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) ||
         (pattern[patternIndex] == '.' && strIndex != str.length)) {
             // 模式的前一个和字符串的前一个是想同的,或模式前一个为.,继续向右匹配
            return matchCore(str, strIndex, pattern, patternIndex + 2)// 模式后移2,视为x*匹配0个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex + 2)// 字符串后移1位,模式后移2位,视为x*匹配1个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex);// x*匹配1个,再匹配str中的下一个
        } else {
            // 直接略去模式的x*,接着往下匹配
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
    }
    //模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
    if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
        return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
    }
    return false;
    }
}

8.单词搜索

https://leetcode-cn.com/problems/word-search/

class Solution {
        
    private boolean[][] marked;

    //        x-1,y
    // x,y-1  x,y    x,y+1
    //        x+1,y
    private int[][] direction = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
    // 盘面上有多少行
    private int m;
    // 盘面上有多少列
    private int n;
    private String word;
    private char[][] board;

    public boolean exist(char[][] board, String word) {
        m = board.length;
        if (m == 0) {
            return false;
        }
        n = board[0].length;
        marked = new boolean[m][n];
        this.word = word;
        this.board = board;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int i, int j, int start) {
        if (start == word.length() - 1) {
            // 已经是最后一个字符时,若一样则为true
            return board[i][j] == word.charAt(start);
        }
        // 当当前start与board的该位置匹配,则继续dfs
        if (board[i][j] == word.charAt(start)) {
            marked[i][j] = true;
            for (int k = 0; k < 4; k++) {
                // 上下左右进行搜索
                int newX = i + direction[k][0];
                int newY = j + direction[k][1];
                // 当前没有被标记到则进行搜索
                if (inArea(newX, newY) && !marked[newX][newY]) {
                    if (dfs(newX, newY, start + 1)) {
                        return true;
                    }
                }
            }
            marked[i][j] = false;
        }
        return false;
    }

    private boolean inArea(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
 }

四.动态规划

1.买卖股票最佳时机

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

class Solution {
    public int maxProfit(int[] prices) {
        // dp[i] = Math.max(0, dp[i - 1] + prices[i] - prices[i - 1])
        // dp[i] 表示第i天卖出可以获取到第最大利润
        if (prices == null || prices.length <= 1) {
            return 0;
        }
        int res = 0;
        int[] dp = new int[prices.length];
        dp[0] = 0;// 第一天利润为0
        for (int i = 1; i < prices.length; i++) {
            // 当今天可以买时则累加,不然则为0
            dp[i] = Math.max(0, dp[i - 1] + prices[i] - prices[i - 1]);
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

2.买卖股票最佳时机含冷冻期

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown

class Solution {
    public int maxProfit(int[] prices) {
        // 定义dp[i][j]表示第i天处于j状态时的利润
        // j = 0,不持股;昨天可不持股或持股卖出
        // j = 1,持股;昨天一直持股或冷冻期买入
        // j = 2,冷冻期;前天持股卖出 ,冷冻期为卖出股票后的一天,所以昨天不持股
        if (prices == null || prices.length < 2) {
            return 0;
        }
        int[][] dp = new int[prices.length][3];
        // 第一天边界条件
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;

        for (int i = 1 ; i < prices.length; i++) {
            // 不持股,可能昨天不持股或持股卖出
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            // 持股,可能昨天持股或冷冻期买入
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][2] - prices[i]);
            // 冷冻期,昨天不持股
            dp[i][2] = dp[i - 1][0];
        }
        // 最后的最大利润在不持股或冷冻期两者中
        return Math.max(dp[prices.length - 1][0], dp[prices.length - 1][2]);
    }
}

3.买卖股票最佳时机含手续费

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee

class Solution {
    public int maxProfit(int[] prices, int fee) {
        // dp[i][j]表示在i天之前各状态可获取的最大利润
        // j定义不持有状态为0,持有状态为1
        // j = 0,昨天不持有或昨天持有卖出
        // j = 1,昨天持有或昨天不持有买入(需要算手续费)
        if (prices == null || prices.length < 2) {
            return 0;
        }
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < prices.length; i++) {
            // 不持股,昨天不持股或卖出(含手续费)
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
            // 持股,昨天持股或买入
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        // 最大值在不持股的情况下发生
        return dp[prices.length - 1][0];
    }
}

4.不同路径

https://leetcode-cn.com/problems/unique-paths

class Solution {
 public int uniquePaths(int m, int n) {
        //  状态转移方程: dp[i][j] = dp[i + 1][j] + dp[i][j + 1] (设机器人从左下角出发,只能往右和上走,目标在右上角)
        int[][] dp = new int[m + 1][n + 1];
        //已知目标的左边一行和下边一列只能有一个走法:向右或向上
        for (int i = 1; i <= m ; i++) {
            dp[i][n] = 1;
        }
        for (int i = 1; i <= n ; i++) {
            dp[m][i] = 1;
        }
        for (int i = m - 1; i >= 1; i--) {
            //填充dp[i][j], 求dp[1][1]
            for (int j = n - 1; j >= 1 ; j--) {
                dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
            }
        }
        return dp[1][1];
    }
}

5.最长回文子串

https://leetcode-cn.com/problems/longest-palindromic-substring

class Solution {
    /*
    * 中心扩散法,时间复杂度O(n^2)
    */
    public String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        String res = "";
        for (int i = 0; i < s.length(); i++) {
            int len1 = lenOfPalindrome(s, i, i);// 从i为分界点扩散
            int len2 = lenOfPalindrome(s, i, i + 1);// 以i与i+1之间为分界点扩散
            int max = Math.max(len1, len2);
            if (max > res.length()) {
                // 确定以i为中心的和max为直径的回文起始点和结束点
                int left = i - (max -1) / 2;
                int right = i + max / 2;
                // 截取出最长子串
                res = s.substring(left, right + 1);
            }
        }
        return res;
    }
    private int lenOfPalindrome(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
        }
        return right - left - 1;
    }
}

6.编辑距离「二维dp」

https://leetcode-cn.com/problems/edit-distance

class Solution {
    public int minDistance(String word1, String word2) {
         int rows = word1.length();
         int cols = word2.length();
         // 状态转移方程: dp[i][j] = min{dp[i][j - 1] + 1//增, dp[i - 1][j] + 1//删, dp[i - 1][j - 1] + 0或1//改}
         // 其中dp[i - 1][j - 1] + 0代表着word1和word2的第i,j个位置元素相同,所以无需改;
         //dp[i - 1][j - 1] + 1代表着word1和word2的第i,j个位置元素不相同,所以要改
         int[][] dp = new int[rows + 1][cols + 1];
         // 填充word1为""时转化为word2的编辑距离
         for (int i = 1; i <= cols; i++) {
             dp[0][i] = i; 
         }
         // 填充word2为""时转化为word1的编辑距离
         for (int i = 1; i <= rows; i++) {
             dp[i][0] = i; 
         }
         // 填充dp
         for (int i = 1; i <= rows; i++) {
             for (int j = 1; j <= cols; j++) {
                 int addCount = dp[i - 1][j] + 1; // 增
                 int deleteCount = dp[i][j - 1] + 1; // 删
                 int changeCount = dp[i - 1][j - 1];  // 改
                 if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
                     changeCount++;
                 }

                 dp[i][j] = Math.min(addCount, Math.min(deleteCount, changeCount));
             }
         }
         return dp[rows][cols];
    }
}

7.最长公共子序列「二维dp」

https://leetcode-cn.com/problems/longest-common-subsequence

class Solution {
 public int longestCommonSubsequence(String text1, String text2) {
        if (text1.length() == 0 || text2.length() == 0) {
            return 0;
        }
        int rows = text1.length();
        int cols = text2.length();
        int[][] dp = new int[rows + 1][cols + 1];
        // 填充dp表,状态转移方程: 
        // 1.text1[i] == text2[j],dp[i][j] = dp[i - 1][j] + 1
        // 2.text1[i] != text2[j],dp[i][j] = max{dp[i - 1][j], dp[i][j - 1]}
        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= cols; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    // 相等,取dp[i - 1][j - 1]的值 + 1
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    // 不想等,取dp[i - 1][j]和dp[i][j - 1]中的最大值
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[rows][cols];
    }
}

8.最长上升子序列

https://leetcode-cn.com/problems/longest-increasing-subsequence

class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums.length <= 1) {
            return nums.length;
        }
        // 状态转移方程 dp[i] = max{dp[i-1], dp[i-2],..., dp[0]} + 1,其中dp[i-1], dp[i-2]等需要小于nums[i],这个过程是往后寻找,接上最长上升子序列的过程,所以每个dp都表示了当前i能够与之前范围组成的最长的上升子序列长度。
        int[] dp = new int[nums.length];
        // 默认自身就是一个最长上升子序列
        int res = 1;
        for (int i = 0; i < nums.length; i++) {
            dp[i] = 1;// 默认以自身开始
            for (int j = i; j >= 0; j--) {
                if (nums[i] > nums[j]) {
                    // 往后寻找
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

9.换钱最少货币数

https://leetcode-cn.com/problems/coin-change

class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount < 1 || coins.length == 0) {
            return 0;
        }
        // dp[i]表示凑i元最少所需的货币数
        // 状态转移方程: dp[i] = min(dp[i-j] + 1); j表示某货币数值
        int[] dp = new int[amount + 1];
        // 先对货币进行排序
        Arrays.sort(coins);
        // 默认不可换钱
        Arrays.fill(dp, Integer.MAX_VALUE);
        // 边界条件
        for (int i = 0; i < coins.length; i++) {
            // 直接兑换
            if (coins[i] < dp.length) {
              dp[coins[i]] = 1;
            }
        }
        // 填充dp[i]
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                int coin = coins[j];
                // 可兑换
                if (i > coin && dp[i - coin] != Integer.MAX_VALUE) {
                    dp[i] = Math.min(dp[i], dp[i - coin]+ 1);
                }
            }
        }
        return (dp[amount] == Integer.MAX_VALUE) ? - 1 : dp[amount];
    }
}

10.矩阵最小路径和

https://leetcode-cn.com/problems/minimum-path-sum/

class Solution {
    public int minPathSum(int[][] grid) {
        // dp[i]表示从起点走到该位置的最小和
        // 状态转移方程: dp[i] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        if (grid == null || grid.length == 0) {
            return 0;
        }
        int rows = grid.length, cols = grid[0].length;
        int[][] dp = new int[rows][cols];
        // 边界状态
        dp[0][0] = grid[0][0];
        // 第一行和第一列直接累加
        for (int i = 1; i < rows; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int i = 1; i < cols; i++) {
            dp[0][i] = dp[0][i - 1] + grid[0][i];
        }
        // 填充dp
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < cols; j++) {
                // 从上一行或上一列中寻找最小的进行相加d
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[rows - 1][cols - 1];
    }
}

11.龙与地下城

https://leetcode-cn.com/problems/dungeon-game

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        if (dungeon == null || dungeon.length == 0) {
            return 0;
        }
        int rows = dungeon.length;
        int cols = dungeon[0].length;
        int[][] dp = new int[rows][cols];
        int identValue = dungeon[rows - 1][cols - 1];
        dp[rows - 1][cols - 1] = (identValue > 0) ? 1 : 1 - identValue;
        for (int i = rows - 2; i >= 0; i--) {
            int val = dp[i + 1][cols - 1] - dungeon[i][cols - 1];
            dp[i][cols - 1] = (val <= 0) ? 1 : val;
        }
        for (int i = cols - 2; i >= 0; i--) {
            int val = dp[rows - 1][i + 1] - dungeon[rows - 1][i];
            dp[rows - 1][i] = (val <= 0) ? 1 : val;
        }
        for (int i = rows - 2; i >= 0; i--) {
            for (int j = cols - 2; j >= 0; j--) {
                int min = Math.min(dp[i + 1][j], dp[i][j + 1]);
                // 不断填充dp,当当前期望值小于等于0,说明当前的补血多于扣血,当前血量设置为1
                int val = (min - dungeon[i][j]) <= 0 ? 1 : min - dungeon[i][j];
                dp[i][j] = val;
            }
        }
        return dp[0][0];
    }

}

12.打家劫舍

https://leetcode-cn.com/problems/house-robber

class Solution {
    public int rob(int[] nums) {
        // dp[i]表示第i天获得的最高利益,有两种选择,不偷窃或放弃前一天的结果,偷窃
        // dp[i] = Math.max(dp[i-2] + nums[i] (隔天偷盗), dp[i - 1](不偷盗))
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length + 1]; 
        dp[1] = nums[0];
        // 从第2天开始
        for (int i = 2; i <= nums.length; i++) {
            // dp[i - 1],不偷窃
            // dp[i - 2] + nums[i - 1],隔天偷窃
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
        }
        return dp[nums.length];
    }
}

13.剪绳子

https://www.nowcoder.com/practice/57d85990ba5b440ab888fc72b0751bf8?tpId=13&tqId=33257&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

import java.lang.Math;
public class Solution {
    public int cutRope(int target) {
        // 动态规划解法,dp表示绳子的长度:
        // 当长度为1时,最大乘积只能为1
        // 当长度为2时,最大乘积为1
        // 当长度为3时,最大乘积为2
        // 当长度大于等于4时,可以换分为不同的小段,每一段即一个dp
        switch (target) {
                case 2 : return 1;
                case 3 : return 2;
        }
        // 以下是可以分段的情形,dp1 ,2, 3直接就可使用
        int[] dp = new int[target + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        // 构造dp
        for (int i = 4; i <= target; i++) {
            for (int j = 1; j <= i / 2; j++) {
                // 将绳子切分成两段
                // dp[i - j] * dp[j] 表示两段的乘积
                dp[i] = Math.max(dp[i], dp[i - j] * dp[j]);
            }
        }
        return dp[target];
    }
}

14.爬楼梯

https://leetcode-cn.com/problems/climbing-stairs

class Solution {
    /* 斐波那契数列类问题 */
    public int climbStairs(int n) {
        if (n <= 3) {
            return n;
        }
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for (int i = 4; i <= n; i++) {
            // 状态转移方程:dp[i] = dp[i - 1] + dp[i - 2]
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

15.单词拆分

https://leetcode-cn.com/problems/word-break

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // 动态规划解法,dp[i]=true表示[0,i)的单词可以使用wordDict表示,那么只需要判断[i,length)的单词可不可以表示即可
        // 状态转移方程:dp[i] = dp[0...j] && set.contains(0, j)
        // 将wordDict添加到hashSet中
        HashSet<String> set = new HashSet<>(wordDict);
        boolean[] dp = new boolean[s.length() + 1];
        // 初始条件,肯定可以拆分成空串
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            // 在该串中继续找子串,只要匹配即为true
            for (int j = 0; j <= i; j++) {
                if (dp[j] && set.contains(s.substring(j, i))) {
                    dp[i] = true;
                }
            }
        }
        return dp[s.length()];
    }
}

16.丑数2

https://leetcode-cn.com/problems/ugly-number-ii

class Solution {
    public int nthUglyNumber(int n) {
        if (n < 1) {
            throw new RuntimeException("n不可小于1");
        }
        int[] dp = new int[n];
        dp[0] = 1;
        int p2 = 0, p3 = 0, p5 =0;
        for (int i = 1; i < n; i++) {
            int min = Math.min(dp[p2] * 2, Math.min(dp[p3] * 3, dp[p5] * 5));
            if (min == dp[p2] * 2) {
                p2++;
            }
            if (min == dp[p3] * 3) {
                p3++;
            }
            if (min == dp[p5] * 5) {
                p5++;
            }
            dp[i] = min;
        }
        return dp[n - 1];
    }
}

17.完全平方数

https://leetcode-cn.com/problems/perfect-squares

class Solution {
    public int numSquares(int n) {
        // dp[i]表示i含有的最小平方数个数
        // dp[i] = min(dp[i], dp[i - j^2]) (i - j^2 >=0)
        if (n <= 3) {
            return n;
        }
        int[] dp = new int[n + 1];
        // 边界条件
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for (int i = 4; i <= n; i++) {
            // 默认所含有的完全平方数都为1的情况
            dp[i] = i;
            for (int j = 0; i - j * j >= 0; j++) {
                // 往前寻找完全平方数
                // dp[i - j * j] + 1
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1); 
            }
        }
        return dp[n];
    }
}

18.三角形最小路径和

https://leetcode-cn.com/problems/triangle

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        if (triangle == null || triangle.size() == 0) {
            return 0;
        }
        // dp[i][j]表示i, j这个位置的最小路径和
        // dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j]
        // 边界条件,第一列和最后的斜边直接填充
        int n = triangle.size();
        int[][] dp = new int[n][n];
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            dp[i][0] = dp[i - 1][0] + triangle.get(i).get(0);
        }
        for (int i = 1; i < n; i++) {
            dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i);
        }
        // 填充dp中间部分
        for (int i = 2; i < n; i++) {
            List<Integer> list = triangle.get(i);
            // 填充该list中间部分
            for (int j = 1; j < list.size() - 1; j++) {
                dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j]) + list.get(j);
            }
        }
        // 寻找最后的路径最小和
        int res = dp[n - 1][0];
        for (int i = 1; i < n; i++) {
            res = Math.min(res, dp[n - 1][i]);
        }
        return res;
    }
}

19.乘积最大子序列

https://leetcode-cn.com/problems/maximum-product-subarray

class Solution {
    public int maxProduct(int[] nums) {
        int[] dp_max = new int[nums.length+1];
        int[] dp_min = new int[nums.length+1];
        if(nums.length == 0) return 0;
        int max = Integer.MIN_VALUE;
        // 由于存在负数,所以需要维护两个数组
        // dp_max[i] 指的是以第 i 个数结尾的 乘积最大 的连续子序列
        // dp_min[i] 指的是以第 i 个数结尾的 乘积最小 的连续子序列
        dp_max[0] = 1;
        dp_min[0] = 1;
        for (int i = 1;i <= nums.length;i++){
            // 如果数组的数是负数,那么会导致 max 变成 min,min 变成 max
            // 故需要交换dp 
            if(nums[i-1] < 0){
                int temp = dp_min[i-1];
                dp_min[i-1] = dp_max[i-1];
                dp_max[i-1] = temp;
            }
            dp_min[i] = Math.min(nums[i-1],dp_min[i-1]*nums[i-1]);
            dp_max[i] = Math.max(nums[i-1],dp_max[i-1]*nums[i-1]);
            max = Math.max(max,dp_max[i]);
        }
        return max;
    }
}

五.二叉树

1.前序和后序重建二叉树

https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=13&tqId=11157&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return helper(pre, in, 0, pre.length - 1, 0, in.length - 1);
    }
    private TreeNode helper(int[] pre, int[] in, int preLeft, int preRight, int inLeft, int inRight) {
        //
        if (preLeft > preRight || inLeft > inRight) {
            return null;
        }
        // 由前序遍历序列获取根节点
        int rootValue = pre[preLeft];
        TreeNode root = new TreeNode(rootValue);   
        // 在中序遍历序列中,搜索出根节点的位置
        int index = findRootIndex(rootValue, in);
        // 通过根节点位置,在中序遍历序列中可划分出左子树和右子树的中序遍历序列
        // 在前序遍历中,可划分出左子树和右子树的前序遍历序列,继续递归地构建树
        // preLeft + index - inLeft:通过preLeft + 左子树节点数(index - inLeft)确定左子树前序遍历的右边界
        // preLeft + index - inLeft + 1:通过前序遍历序列的右边界+1确定中序遍历序列的左边界
        root.left = helper(pre, in, preLeft + 1, preLeft + index - inLeft, inLeft, index - 1);
        root.right = helper(pre, in, preLeft + index - inLeft + 1, preRight, index + 1, inRight);
        return root;
    }
    private int findRootIndex(int target, int[] array) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == target) {
                return i;
            }
        }
        return -1;
    }
     
}

2.序列化反序列化二叉树

https://www.nowcoder.com/practice/cf7e25aa97c04cc1a68c8f040e71fb84?tpId=13&tqId=11214&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    String Serialize(TreeNode root) {
        StringBuilder builder = new StringBuilder();
        return _Serialize(root, builder).toString();
    }
    private StringBuilder _Serialize(TreeNode root, StringBuilder builder) {
        // 递归终止条件
        if (root == null) {
            builder.append("#!");
            return builder;
        }
        // 使用前序的方式序列化
        builder.append(root.val + "!");
        _Serialize(root.left, builder);
        _Serialize(root.right, builder);
        return builder;
    }
    
    TreeNode Deserialize(String str) {
       return _Deserialize(str.split("!"));
    }
    
    private int index = -1;
    private TreeNode _Deserialize(String[] strs) {
        index++;
        if (index >= strs.length || "#".equals(strs[index])) {
            return null;
        }
        TreeNode root = new TreeNode(Integer.valueOf(strs[index]));
        root.left = _Deserialize(strs);
        root.right = _Deserialize(strs);
        return root;
    }
}

3.二叉树的子结构

https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88?tpId=13&tqId=11170&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if (root1 == null || root2 == null) {
            return false;
        }
        boolean res = false;
        if (root1 != null && root2 != null) {
            // 判断该位置是否为子结构
            res = check(root1, root2);
        }
        if (!res) {
            // 在左子树中寻找
            res = HasSubtree(root1.left, root2);
        }
        if (!res) {
            // 在右子树中寻找
            res = HasSubtree(root1.right, root2);
        }
        return res;
    }
    private boolean check(TreeNode root1, TreeNode root2) {
        // 子结构判断条件
        if (root1 == null && root2 == null || root2 == null) {
            return true;
        }
        if (root1 == null && root2 != null) {
            return false;
        }
        if (root1.val != root2.val) {
            return false;
        }
        // 再次校验左右子树是否都满足子结构
        return check(root1.left, root2.left) && check(root1.right, root2.right);
    }
}

4.二叉树镜像

https://www.nowcoder.com/practice/564f4c26aa584921bc75623e48ca3011?tpId=13&tqId=11171&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if (root == null) {
            return;
        }
        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

5.从上往下打印二叉树

https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701?tpId=13&tqId=11175&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 使用队列来存储节点 */
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode poll = queue.poll();
            if (poll.left != null) {
                queue.offer(poll.left);
            }
            if (poll.right != null) {
                queue.offer(poll.right);
            }
            // 添加层序遍历结果
            res.add(poll.val);
        }
        return res;
    }
}

6.验证二叉搜索树的后序遍历序列

https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd?tpId=13&tqId=11176&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        // 两个特殊情况处理
         if (sequence.length == 0){
            return false;
        }
        if (sequence.length == 1){
            return true;
        }
        int left = 0, right = sequence.length - 1;
        return check(sequence, left, right);
    }
    private boolean check(int[] sequence, int left, int right) {
        if (left >= right) {
            return true;
        }
        // 根节点
        int root = sequence[right];
        // 划分节点
        int index = -1;
        // 从右往左找第一个比根节点小的节点,划分左右子树
        for (int i = right - 1; i >= left; i--) {
            if (sequence[i] < root) {
                index = i;
                break;
            }
        }
        for (int i = index; i >= left; i--) {
            if (sequence[i] >= root) {
                return false;
            }
        }
        // 划分左右子树
        return check(sequence, left, index) && check(sequence, index + 1, right - 1);
    }
}

7.二叉搜索树转化双向链表

https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    /* 使用栈模拟中序遍历过程 */
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null || pRootOfTree.left == null && pRootOfTree.right == null) {
            return pRootOfTree;
        }
        Stack<TreeNode> stack  = new Stack<>();
        // 是第一个获取到的节点
        boolean first = true;
        TreeNode root = pRootOfTree, res = pRootOfTree, pre = null;
        while (!stack.isEmpty() || root != null) {
            if (root != null) {
                stack.push(root);
                root = root.left;
            } else {
                TreeNode pop = stack.pop();
                root = pop.right;
                 
                if (first) {
                    res = pop;
                    pre = pop;
                    first = false;
                } else {
                    pre.right = pop;
                    pop.left = pre;
                    pre = pop;
                }
            }
        }
        return res;
    }
}

8.平衡二叉树校验

https://leetcode-cn.com/problems/balanced-binary-tree

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 判断是否为平衡二叉树
        return getMaxDepth(root) != -1;
    }
    
    private int getMaxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = getMaxDepth(root.left);
        int right = getMaxDepth(root.right);
        // 判断左右子树的深度是否为-1
        if (left == -1 || right == -1) {
            return -1;
        }
        // 如果左右子树深度差值大于1则返回-1表示该树不是平衡二叉树,如果小于1则返回最大深度
        return Math.abs(left - right) > 1 ? -1 : Math.max(left, right) + 1;
    }
}

9.二叉树的深度

https://leetcode-cn.com/problems/maximum-depth-of-binary-tree

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

10.二叉树的中序下一节点

https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode root)
    {
        if (root == null) {
            return null;
        }
        // 1.有右子树,找右子树最左
        if (root.right != null) {
            return findMostLeft(root.right);
        } else {
            // 2.3.无右子树,当当前节点是父节点的左子树时,父节点就是后继节点
            TreeLinkNode parent = root.next;
            while (parent != null && parent.left != root) {
                root = parent;
                parent = parent.next;
            }
            return parent;
            
        }
    }
    private TreeLinkNode findMostLeft(TreeLinkNode root) {
        if (root.left == null) {
            return root;
        }
        return findMostLeft(root.left);
    }
//--------------------------------------------------------------
    private List<TreeLinkNode> list = new ArrayList<>();
        
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode == null) {
            return null;
        }
        TreeLinkNode root = pNode;
        // 找到树的根节点
        while (root.next != null) {
            root = root.next;
        }
        // 获取中序遍历结果集
        inOrder(root);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) == pNode) {
                // 返回下一个节点
                return (i >= list.size() - 1) ? null : list.get(i + 1);
            }
        }
        return null;
    }
    
    private void inOrder(TreeLinkNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        list.add(root);
        inOrder(root.right);
    }

}

11.之字形打印二叉树

https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal

class Solution {
    /* 层次遍历,带上深度条件 */
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int depth = 0;
        while (!queue.isEmpty()) {
            ArrayList<Integer> oneRes = new ArrayList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode poll = queue.poll();
                if (poll.left != null) {
                     queue.offer(poll.left);
                }
                if (poll.right != null) {
                    queue.offer(poll.right);
                }
                if ((depth & 1) == 0) {
                    // 从左到右添加结果
                    oneRes.add(poll.val);
                } else {
                    // 从右到左添加结果
                    oneRes.add(0, poll.val);
                }
            }
            res.add(oneRes);
            depth++;
        }
        return res;
    }
}

12.有序数组转化二叉搜索树

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {        
        return helper(nums);
    }
    /* 每次都取出数组的中间位置的值作为根节点,然后以此为分割取两边作为子树 */
    private TreeNode helper(int[] nums) {
        if (nums.length == 0) {
            return null;
        }
        if (nums.length == 1) {
            return new TreeNode(nums[0]);
        }
        int len = nums.length;
        // 有序数组的中间节点作为根节点
        TreeNode root = new TreeNode(nums[len / 2]);
        root.left = helper(Arrays.copyOfRange(nums, 0, len / 2));
        root.right = helper(Arrays.copyOfRange(nums, len / 2 + 1, len));
        return root;
    }
}

13.路径总和

https://leetcode-cn.com/problems/path-sum-ii

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> oneRes = new ArrayList<>();
        _pathSum(root, sum, res, oneRes);
        return res;
    }
    /* 回溯搜索 */
    private void _pathSum(TreeNode root, int sum,  List<List<Integer>> res, List<Integer> oneRes) {
        if (root == null) {
            return;
        }
        // 回溯终止条件,遇到叶子节点
        if (root.left == null && root.right == null) {
            if (sum == root.val) {
                oneRes.add(sum);
                res.add(new ArrayList<Integer>(oneRes));
            }
            return;
        }
        // 左子树不为空,新开辟一个路径搜索
        if (root.left != null) {
            List<Integer> temp = new ArrayList<>(oneRes);
            temp.add(root.val);
            _pathSum(root.left, sum - root.val, res, temp);
        }
        // 右子树不为空,新开辟一个路径搜索
        if (root.right != null) {
            oneRes.add(root.val);
            _pathSum(root.right, sum - root.val, res, oneRes);
        }
        return;
    }
}

14.二叉搜索树第k小节点

https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst

class Solution {

    /* 中序遍历第k次即获取到了第k小的元素 */
    public int kthSmallest(TreeNode root, int k) {
        Stack<TreeNode> stack = new Stack<>();
        int count = 0;
        // 中序遍历
        while (!stack.isEmpty() || root != null) {
            if (root != null) {
                stack.push(root);
                root = root.left;
            } else {
                // 弹出的节点为中序遍历的节点
                TreeNode pop = stack.pop();
                root = pop.right;
                // 已经弹出了第k个节点
                if (++count == k) {
                    return pop.val;
                }
            }
        }
        return -1;
    }
}

15.找树左下角的值

https://leetcode-cn.com/problems/find-bottom-left-tree-value

class Solution {
    /* 按层序遍历获取最后一层第一个节点的值即为最左节点值 */
    public int findBottomLeftValue(TreeNode root) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int res = 0;
        while (!queue.isEmpty()) {
            // 当前的队列容量即为下一层的节点数量
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode poll = queue.poll();
                if (poll.left != null) {
                    queue.offer(poll.left);
                }
                if (poll.right != null) {
                    queue.offer(poll.right);
                }
                // 该层的第一个节点即为树左下角的值(最后一次更新的)
                if (i == 0) {
                    res = poll.val;
                }
            }
        }
        return res;
 }
}

16.最长同值路径(节点间最长路径)

https://leetcode-cn.com/problems/longest-univalue-path

class Solution {
    private int res = 0;
    public int longestUnivaluePath(TreeNode root) {
       helper(root);
       return res;
    }
    /* 考虑同值情况下的最长路径 */
    private int helper(TreeNode root) {
       // 递归终止条件
       if (root == null) {
           return 0;
       }
       // 获取左子树最长同值路径
       int left = helper(root.left);
       // 获取右子树最长同值路径
       int right = helper(root.right);
       int leftNum = 0;
       int rightNum = 0;
       // 判断是否同值,同值则组成一条路径,累加+1,不同则为0
       if (root.left != null && root.left.val == root.val) {
           leftNum += left + 1;
       }
       if (root.right != null && root.right.val == root.val) {
           rightNum += right + 1;
       }
       // 更新最长路径
       res = Math.max(res, leftNum + rightNum);
       // 返回其中一条路径
       return Math.max(leftNum, rightNum);
    }
    
    
    /* 不考虑同值情况下的最长路径 */
    private int helper(TreeNode root) {
        // 递归终止条件
        if (root == null) {
            return 0;
        }
        // 递归获取左右子节点的最长同值路经
        int left = helper(root.left);
        int right = helper(root.right);
        res = Math.max(res, left + right);
        return Math.max(left, right) + 1;
    }
}

17.二叉树最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree

class Solution {

    private TreeNode res = null;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        helper(root, p, q);
        return res;
    }

    private boolean helper(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return false;
        }
        int left = helper(root.left, p, q) ? 1 : 0; // 查看左子树含不含p或q
        int right = helper(root.right, p, q) ? 1 : 0; // 查看右子树含不含p或q
        int mid = (root == p || root == q) ? 1 : 0; // 当前节点是否等于p或1
        // 当满足以下条件,说明root就是最近公共祖先
        // 1. left = 1, right = 1, mid = 0 在左子树和右子树中分别含有pq
        // 2. left = 1, right = 0, mid = 1 在左子树中含有p或q,且root = p或q
        // 3. left = 0, right = 1, mid = 1 在右子树中含有p或q,且root = p或q
        if (left + right + mid >= 2) {
            res = root;
            return true;
        }
        // 当前至少有一边查找到了p或q
        return left + right + mid >= 1;
    }
}

18.二叉搜索树最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

class Solution {
    private TreeNode res = null;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        helper(root, p, q);
        return res;       
    }

    private boolean helper(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return false;
        }
        int left = helper(root.left, p, q) ? 1 : 0;
        int right = helper(root.right, p, q) ? 1 : 0;
        int mid = (root == p || root == q) ? 1 : 0;
        if (left + right + mid >= 2) {
            res = root;
            return true;
        }
        return left + right + mid >= 1;
    }
}

19.二叉树的右视图

https://leetcode-cn.com/problems/binary-tree-right-side-view

class Solution {
    /* 层序遍历,收集右侧节点值 */
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode poll = queue.poll();
                // 已经是一层的最右边,收集结果
                if (i == size - 1) {
                    res.add(poll.val);
                }
                if (poll.left != null) {
                    queue.offer(poll.left);
                }
                if (poll.right != null) {
                    queue.offer(poll.right);
                }
            }
        }
        return res;
    }
}

20.左叶子之和

https://leetcode-cn.com/problems/sum-of-left-leaves

class Solution {

    private int res = 0;

    /* 采用前序遍历 + 左叶子结点flag标识 */
    public int sumOfLeftLeaves(TreeNode root) {
        preOrder(root, false);
        return res;
    }
    private void preOrder(TreeNode root, boolean flag) {
        if (root == null) {
            return;
        }
        if (flag && root.left == null && root.right == null) {
            // 左叶子节点
            res += root.val;
        }
        preOrder(root.left, true); // 左直接子树一定会有左叶子结点
        preOrder(root.right, false); // 右直接子树不存在左叶子节点
    }
}

六.链表

1.反转链表

https://leetcode-cn.com/problems/reverse-linked-list

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode pre = head, cur = head.next, later = null;
        while (cur != null) {
            // 先记录好later的指针
            later = cur.next;
            // 反转
            cur.next = pre;
            // 后移一位
            pre = cur;
            cur = later;
        }
        head.next = null;
        return pre;
    }
    
    // 递归地反转
    public ListNode reverseList(ListNode head) { 
       return helper(null, head);
   }
    private ListNode helper(ListNode pre, ListNode cur) {
        //递归终止条件
        if (cur == null) {
            return pre;
        }
        //递归过程
        ListNode later = cur.next;
        cur.next = pre;
        return helper(cur, later);
    }
}

2.反转局部链表

https://leetcode-cn.com/problems/reverse-linked-list-ii/

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (head == null || head.next == null || m == n) {
            return head;
        }
        // 获取到m、m前一个节点、n、n后一个节点
        ListNode preM = null, M = head, N = head, laterN = null;
        ListNode counter = head;
        int count = 0;
        while (counter != null) {
            count++;
            preM = count == m - 1 ? counter : preM;
            M = count == m ? counter : M;
            N = count == n ? counter : N;
            laterN = count == n + 1 ? counter : laterN;
            counter = counter.next;
        }
        // 连接preM和N
        if (preM != null) {
            preM.next = N;
        }
        // 反转M到N之间的链表
        ListNode pre = M, cur = M.next, later = null;
        while (cur != laterN) {
            // 记录后一个节点
            later = cur.next;
            // 反转
            cur.next = pre;
            // 后移
            pre = cur;
            cur = later;
        }
        // 连接M与laterN
        M.next = laterN;
        // 如果preM为null,说明是从head开始反转到N的,返回N即可;不为null则说明反转了中间部分,返回head
        return preM == null ? N : head;
    }
}

3.删除链表重复节点

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // 为null或只存在一个节点
        if (head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        // 定义pre、cur和later指针
        ListNode pre = dummy, cur = head, later = cur.next;
        while (cur != null && later != null) {
            if (later.val == cur.val) {
                // 当later和cur相等,则让later向后走,直到不等,然后pre与later连接,跳过相等部分
                while (later != null && later.val == cur.val) {
                    later = later.next;
                }
                // 跳过相等部分
                pre.next = later;
                cur = later;
                // later注意判空
                later = later == null ? null : later.next;
            } else {
                // later与cur不想等,三者都后移一位
                pre = pre.next;
                cur = cur.next;
                later = later.next;
            }
        }
        return dummy.next;
    }
}

4.链表中环的入口

https://leetcode-cn.com/problems/linked-list-cycle-ii/

public class Solution {
    /* 使用HashSet记录遍历过的节点,当出现了重复节点时即为环的入口 */
    public ListNode detectCycle(ListNode head) {
        HashSet<ListNode> set = new HashSet<>();
        while (head != null) {
            if (set.contains(head)) {
                return head;
            } else {
                set.add(head);
                head = head.next;
            }
        }
        return head;
    }


    /* 使用快慢指针相遇找到环形链表的一点,然后从链表头开始于该节点一起往前走,链表头与该节点相遇即为环的入口 */
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        // 寻找环状链表中的一点
        while (true) {
            if (slow == null || fast == null || fast.next == null) {
               // 不成环
               return null;
            }
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                // 相遇
                break;
            }
        }
        ListNode p1 = head, p2 = slow;
        // 寻找链表头与slow相遇的点
        while (p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
}

5.链表是否有环

https://leetcode-cn.com/problems/linked-list-cycle

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) {
            return false;
        }
        ListNode slow = head, fast = head;
        while (true) {
            if (slow == null || fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
    }
}

6.相交链表公共节点

https://leetcode-cn.com/problems/intersection-of-two-linked-lists

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 先获取到headA和headB的长度,然后在进行长度的对其
        int len1 = 0, len2 = 0;
        ListNode counterA = headA, counterB = headB;
        while (counterA != null) {
            len1++;
            counterA = counterA.next;
        }
        while (counterB != null) {
            len2++;
            counterB = counterB.next;
        }
        // 头部对齐
        int x = Math.abs(len1 - len2);
        if (len1 > len2) {
            for (int i = 0; i < x; i++) {
                headA = headA.next;
            }
        }
        if (len2 > len1) {
            for (int i = 0; i < x; i++) {
                headB = headB.next;
            }
        }
        // 相遇则为起点
        while (headA != headB) {
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }
}

7.复杂链表的复制

https://leetcode-cn.com/problems/copy-list-with-random-pointer

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {

    
    

    /* 使用HashMap存储旧节点对应新节点的映射,然后按照旧节点关系建立新节点的关系 */
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        HashMap<Node, Node> map = new HashMap<>();
        Node cur = head;
        // 存储新旧节点映射关系
        while (cur != null) {
            map.put(cur, new Node(cur.val));
            cur = cur.next;
        }
        // 根据旧节点建立新节点件关系
        boolean first = true;
        Node res = null;
        while (head != null) {
            map.get(head).next = map.get(head.next);
            map.get(head).random = map.get(head.random);
            if (first) {
                res = map.get(head);
                first = false;
            }
            head  = head.next;
        }
        return res;
    }
}

8.合并两个有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists

class Solution {
    /* 归并排序的mereg函数 */
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode res = dummy;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                dummy.next = l1;
                l1 = l1.next;
            } else {
                dummy.next = l2;
                l2 = l2.next;
            }
            dummy = dummy.next;
        }
        if (l1 != null) {
            dummy.next = l1;
        }
        if (l2 != null) {
            dummy.next = l2;
        }
        return res.next;
    }
}

9.删除链表倒数第k个节点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null) {
            return null;
        }
        // 让fast节点比slow先走n步
        ListNode slow = head, fast = head;
        for (int i = 0; i < n; i++) {
            fast = (fast == null) ? null : fast.next;
        }
        // 删除倒数第N个节点
        ListNode pre = null;
        while (fast != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next;
        }
        // 判断是否删除的是头节点
        if (pre == null) {
            return head.next;
        }
        // 非头节点
        pre.next = slow.next;
        return head;
    }
}

10.两数相加

https://leetcode-cn.com/problems/add-two-numbers

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        int x = 0; // 进位
        // 1.双方都不为空都情况,累加并带上进位
        while (l1 != null && l2 != null) {
            int sum = l1.val + l2.val + x; // 考虑进位
            x = sum / 10; // 获取新的进位
            int val = sum % 10; // 获取新的节点值
            cur.next = new ListNode(val);
            cur = cur.next;
            l1 = l1.next;
            l2 = l2.next;
        }
        // 2.有一者为空的情况,只加上不为空的一方
        while (l1 != null) {
            int sum = l1.val + x;
            x = sum / 10;
            int val = sum % 10;
            cur.next = new ListNode(val);
            cur = cur.next;
            l1 = l1.next;
        }
        while (l2 != null) {
            int sum = l2.val + x;
            x = sum / 10;
            int val = sum % 10;
            cur.next = new ListNode(val);
            cur = cur.next;
            l2 = l2.next;
        }
        // 3.最后判断是否发生溢出
        if (x == 1) {
            cur.next = new ListNode(1);
        }
        return dummy.next;
    }
}

11.将有序链表转化为二叉搜索树

https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    /* 确定中间的节点作为根节点,不断地递归即可(类似于从前序和中序构造二叉树,不过这里直接从中序的中间节点取值) */
    public TreeNode sortedListToBST(ListNode head) {
        return helper(head);
    }
    private TreeNode helper(ListNode head) {
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            return new TreeNode(head.val);
        }
        // 快慢指针确定中间节点以及中间节点前一个节点
        ListNode slow = head, fast = head, pre = null;
        while (slow != null && fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        // slow即为根节点
        TreeNode root = new TreeNode(slow.val);
        // 获取到右区间
        ListNode right = slow.next;
        // 断链
        pre.next = null;
        slow.next = null;
        // 递归构建左右子树
        root.left = helper(head);
        root.right = helper(right);
        return root;
    }
}

七.栈

1.用两个栈实现队列

https://leetcode-cn.com/problems/implement-queue-using-stacks

class MyQueue {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    /** Initialize your data structure here. */
    public MyQueue() {
        stack1 = new Stack<>(); // 中转栈
        stack2 = new Stack<>(); // 值栈
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        move(stack2, stack1);
        stack1.push(x);
        move(stack1, stack2);
    }
    private void move(Stack<Integer> s1, Stack<Integer> s2) {
        while (!s1.isEmpty()) {
            s2.push(s1.pop());
        }
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        return stack2.pop();
    }
    
    /** Get the front element. */
    public int peek() {
        return stack2.peek();
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack2.isEmpty();
    }
}

2.含有getMin的栈

https://leetcode-cn.com/problems/min-stack/

class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;

    /** initialize your data structure here. */
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int x) {
        stack.push(x);
        // 更新minStack使得其栈头最小
        if (minStack.isEmpty() || !minStack.isEmpty() && x <= minStack.peek()) {
            minStack.push(x);
        }
    }
    
    public void pop() {
        int pop = stack.pop();
        // 如果弹出了当前最小值,删去minStack栈头
        if (pop == minStack.peek()) {
            minStack.pop();    
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

3.有效括号

https://leetcode-cn.com/problems/valid-parentheses

class Solution {
    /* 使用栈来做匹配 */
    public boolean isValid(String s) {
        if ("".equals(s.trim())) {
            return true;
        }
        Map<Character, Character> map = new HashMap<>();
        map.put('(', ')');
        map.put('[', ']');
        map.put('{', '}');
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 左半括号直接入栈
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            }
            // 校验
            if (c == ')' || c == '}' || c == ']') {
                // 没有左括号可匹配或弹出不匹配返回falsea
                if (stack.isEmpty() || c != map.get(stack.pop())) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }
}

4.下一个更大元素

https://leetcode-cn.com/problems/next-greater-element-i

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        // 先使用单调栈获取到nums2中每个元素对应的下一个更大元素存储至HashMap中
        Stack<Integer> stack = new Stack<>(); // 由上之下递增
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums2.length; i++) {
            while (!stack.isEmpty() && nums2[i] > stack.peek()) {
                // 不满足单调栈
                map.put(stack.pop(), nums2[i]);
            }
            stack.push(nums2[i]);
        }
        // stack中剩下的即为找不到下一个更大元素的
        while (!stack.isEmpty()) {
            map.put(stack.pop(), -1);
        }
        // 遍历nums1,使用map获取对应元素的下一个更大元素
        for (int i = 0; i < nums1.length; i++) {
            nums1[i] = map.get(nums1[i]);
        }
        return nums1;
    }
}

5.每日温度

https://leetcode-cn.com/problems/daily-temperatures/

class Solution {
    /* 使用单调栈来记录某个元素索引的下一个最大元素索引,之后相建减即为天数 */
    public int[] dailyTemperatures(int[] nums) {
        int[] res = new int[nums.length];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < nums.length; i++) {
            while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
                // 不满足单调栈,弹出
                int index = stack.pop();
                // 计算天数
                res[index] = i - index;
            }
            // 满足单调栈结构
            stack.push(i);
        }
        return res;
    }
}

6.验证栈序列

https://leetcode-cn.com/problems/validate-stack-sequences

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        if (pushed.length != popped.length) {
            return false;
        }
        Stack<Integer> stack = new Stack<>();
        int index = 0; // 当前匹配到的弹出序列位置
        for (int i = 0; i < pushed.length; i++) {
            stack.push(pushed[i]);
            // 开始匹配
            while (!stack.isEmpty() && stack.peek() == popped[index]) {
                // 弹出以表示匹配
                stack.pop();
                // 索引++,表示匹配
                index++;
            }
        }
        // 匹配完则说明完全匹配
        return stack.isEmpty();
    }
}

7.最长有效括号

https://leetcode-cn.com/problems/longest-valid-parentheses/

八.其他

1.36进制的加法与整形转化

/*
         * @Author ARong
         * @Description 36进制的正整数相加,返回十进制结果
         * @Date 2020/2/17 11:45 上午
         * @Param [a, b]
         * @return long
         **/
        private long plus(String a, String b) {
            String value36bit = get36BitPlus(a, b);
            System.out.println(value36bit);
            long value10bit = get10BitValue(value36bit);
            System.out.println(value10bit);
            return value10bit;
        }

    /*
     * @Author ARong
     * @Description 将36进制转化为10进制
     * @Date 2020/2/17 12:13 下午
     * @Param [value36bit]
     * @return long
     **/
    private long get10BitValue(String value36bit) {
        // 这里不考虑正负
        long res = 0;
        char[] chars = value36bit.toCharArray();
        int n = chars.length;
        for (int i = n - 1; i >= 0; i--) {
            int up = n - i - 1;
            res += getInt(chars[i]) * Math.pow(36, up);
        }
        return res;
    }

    /*
     * @Author ARong
     * @Description 36位字符串相加
     * @Date 2020/2/17 12:12 下午
     * @Param [a, b]
     * @return java.lang.String
     **/
    private String get36BitPlus(String a, String b) {
        StringBuilder res = new StringBuilder();
        int x = 0; // 进位
        // 36进制相加
        char[] chars1 = a.toCharArray();
        char[] chars2 = b.toCharArray();
        int n1 = chars1.length;
        int n2 = chars2.length;
        int cur1 = n1 - 1, cur2 = n2 - 1;
        // 两者都有位
        while (cur1 >= 0 && cur2 >= 0) {
            int sum = getInt(chars1[cur1--]) + getInt(chars2[cur2--]) + x;
            x = sum / 36; // 进位
            sum = sum % 36; // 考虑溢出

            res.insert(0, getChar(sum)); // 添加36位计算结果
        }
        // chars1有位
        while (cur1 >= 0) {
            int sum = getInt(chars1[cur1--]) + x;
            x = sum / 36; // 进位
            sum = sum % 36; // 考虑溢出

            res.insert(0, getChar(sum)); // 添加36位计算结果
        }
        // chars2有位
        while (cur2 >= 0) {
            int sum = getInt(chars2[cur2--]) + x;
            x = sum / 36; // 进位
            sum = sum % 36; // 考虑溢出

            res.insert(0, getChar(sum)); // 添加36位计算结果
        }
        // 判断最后是否溢出
        if (x > 0) {
            res.insert(0, getChar(x));
        }
        return res.toString();
    }

    /*
     * @Author ARong
     * @Description 将数字转化为36位
     * @Date 2020/2/17 1:07 下午
     * @Param [num]
     * @return char
     **/
    private char getChar(int num) {
        if (num >= 0 && num <= 9) {
            // '0'的ascii码为48
            int n = 48 + num;
            return (char)n;
        }
        if (num >= 10 && num <= 35) {
            // 'a'的ascii码为97
            int n = 97 + (num - 10);
            return (char)n;
        }
        return '0';
    }

    /*
     * @Author ARong
     * @Description 通过字符获取十进制
     * @Date 2020/2/17 1:06 下午
     * @Param [c]
     * @return int
     **/
    private int getInt(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        }
        if (c >= 'a' && c <= 'z') {
            return 10 + (c - 'a');
        }
        return -1;
    }

2.(SQL)排名排序

https://leetcode-cn.com/problems/rank-scores/

# 第一种解法
select s1.Score,
# 查询比s1.Score高的分数的去重数量,即为当前分数的排名
(select count(distinct s2.Score) from Scores s2 where s2.Score >= s1.Score) as Rank
from Scores s1
# 注意降序排序
order by s1.Score desc


# 第二种解法,MySQL 8之后可以使用窗口函数来统计排名,主要有RANK()/DENSE_RANK()/ROW_NUMBER()
select s1.Score, dense_rank(order by s1.Score DESC)  as Rank
from Score s1 

3.(SQL)第二高的薪水

https://leetcode-cn.com/problems/second-highest-salary/

select 
# ifnull(xx, null),当xx为null时,返回null
ifnull(
# 使用limit 1,1限制查找第1行开始的第一个数据,即第二高的排名
(select distinct e.Salary from Employee e order by e.Salary desc limit 1,1),
null)  
as SecondHighestSalary
发布了309 篇原创文章 · 获赞 205 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/104443688