Skip to content

哈希

两数之和

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

分析

已知目标和一个数,我们只需要记录这个数另一个互补的数是否也在,所以要检验是否存在的最好方法就是利用之前已经访问过的数,其已经被存入哈希表。

O(n),T(n)

java
class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int[] ans = new int[2];
        for (int i = 0; i< nums.length;i++) {
            int tmp = target - nums[i];
            if (map.containsKey(tmp)) {
                ans[0] = i;
                ans[1] = map.get(tmp);
                break;
            }
            map.put(nums[i], i);
        }
        return ans;
    }
}

字母异位词分组

题目

****

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]

输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

分析

只是位置发生改变,但是字母的组成不会改变,只需要分析各类字母的数量。

java
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (int i = 0; i < strs.length; i++) {
            int[] count = new int[26];
            char[] str = strs[i].toCharArray();
            for (Character c : str) {
                int index = c - 'a';
                count[index]++;
            }
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < count.length; j++) {
                if (count[j] != 0) {
                    sb.append((char) 'a' + j); // 特征字母
                    sb.append(count[j]); // 特征次数
                }
            }
            String s = sb.toString();

            List<String> list = map.getOrDefault(s, new ArrayList<String>());
            list.add(strs[i]);
            map.put(s, list);

        }
        return new ArrayList<List<String>>(map.values());
    }
}

最长连续序列

题目

````__

输入:nums = [100,4,200,1,3,2]

输出:4

解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

分析

java
class Solution {
    /**
     * 我们要以n的复杂度解决,只能遍历几次数组,那么就需要利用之前的信息。
     * 如果当前的数字的邻域刚好有之前出现过的数字,
     * 则以当前数字所在序列就是这个+1
     * @param nums
     * @return
     */
    public int longestConsecutive(int[] nums) {
        int ans = 0;
        Set<Integer> set = new HashSet<>();
        for (Integer integer : nums) {
            set.add(integer);
        }
        for (Integer i : set) {
            if(!set.contains(i-1)){
                // 从最底层开始,序列中间的数不会被重复计算
                int len = 1;
                int _i = i;
                while (set.contains(_i+1)) {
                    len++;
                    _i++;
                }
                ans = Math.max(ans, len);
            }
        }
        return ans;
    }
}

双指针

移动零

题目

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。

输入: nums = [0,1,0,3,12]

输出: [1,3,12,0,0]

分析

如果有这样一串数

10034050

左指针指向已处理的尾部,右指针指向待处理的头部。我们可以将左右内部视为一个全为“0”的区间(虽然实际不是)

如果右非零,说明可以填充过去。然后"移动"这个0序列区间。

否则扩大右侧0区间。最后移动到末尾时,从左侧开始全部重新填一般0

O(n)

java
class Solution {
    public void moveZeroes(int[] nums) {
        int left = 0, right = 0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]!=0){
                continue;
            }
            left = i;
            right = i+1;
            break;
        }
        while (right<nums.length) {
            if(nums[right]!=0){
                nums[left++] = nums[right++];
                continue;
            }
            right++;
        }
        while (left<nums.length) {
            nums[left++]=0;
        }
    }
}

盛最多水的容器

题目

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

分析

为什么双指针的做法是正确的?

双指针代表了什么?

双指针代表的是 可以作为容器边界的所有位置的范围。在一开始,双指针指向数组的左右边界,表示 数组中所有的位置都可以作为容器的边界,因为我们还没有进行过任何尝试。在这之后,我们每次将 对应的数字较小的那个指针 往 另一个指针 的方向移动一个位置,就表示我们认为 这个指针不可能再作为容器的边界了。

由于指针向内部移动,水的宽度一定在减少,水的高度取决于最低的高度,所以要增加高度,必要条件是丢弃当前最低的高度,否则一定是往减少的方向发展的。

另一个方面来说,我们的问题由这些高度的集合组成,当丢弃掉一个高度后,说明我们已经比较过了这个集合的最大取值,并且这个集合不会再参与后续问题。

java
class Solution {
    public int maxArea(int[] height) {
        int left = 0, right = height.length-1;
        int ans = 0;
        while (left<right) {
            ans = Math.max(ans,  Math.min(height[left],height[right])*(right-left));

            if(height[left]<height[right]){
                left++;
            }else{
                right--;
            }
        }
        return ans;
    }
}

三数之和

题目

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

解释:

nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。

nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。

nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。

不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。

注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]

输出:[]

解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]

输出:[[0,0,0]]

解释:唯一可能的三元组和为 0 。

分析

可以看作第一层循环选择两数之和的一个target,第二层循环就是找两数之和,不过要避免重复解,以及两个重复数可以被达成目标的情况。这里是哈希的做法。双指针还没学会。。。

以下代码的实际运行效率还是差了点,但是能过,复杂度也算O(n^2)。

java
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        Set<Integer> used_k = new HashSet<>();
        Arrays.sort(nums);
        for(int k = 0;k<nums.length-2;k++){
            if(used_k.contains(nums[k])){ // 若存在重复,则提前出现的数应该包含后出现的重复数的解集
                continue;
            }
            used_k.add(nums[k]);
            int target = -nums[k];
            Set<Integer> map = new HashSet<>();
            Set<Integer> used = new HashSet<>();
            for(Integer i=k+1;i<nums.length;i++){
                if(used.contains(nums[i])){
                    continue;
                }
                int t = target - nums[i];
                if(map.contains(t)){
                    List<Integer> _ans = new ArrayList<>();
                    _ans.add(nums[k]);
                    _ans.add(nums[i]);
                    _ans.add(t);
                    used.add(nums[i]);
                    used.add(t);
                    ans.add(_ans);
                }
                map.add(nums[i]);
            }
        }
        return ans;
    }
}

单调栈

接雨水

题目

分析

双向单调栈,感觉有水的想象力就很明显了

java
class Solution {
    private int[] height;

    public int trap(int[] height) {
        if (height.length < 3) {
            return 0;
        }
        this.height = height;
        List<Integer> left = new ArrayList<>();
        List<Integer> right = new ArrayList<>();
        {
            int left_height = 0;
            for (int i = 0; i < height.length; i++) {
                if (left_height > height[i]) {
                    continue;
                }
                left.add(i);
                left_height = height[i];
            }
        }
        {
            int right_height = 0;
            int left_max = left.getLast();
            for (int i = height.length - 1; i >= left_max; i--) {
                if (right_height > height[i]) {
                    continue;
                }
                right.add(i);
                right_height = height[i];
            }
        }
        int ans = 0;
        if (left.size() > 1) {
            int _h = left.getFirst();
            for (int i = 1; i < left.size(); i++) {
                ans += this.getSize(_h, left.get(i));
                _h = left.get(i);
            }
        }
        if (right.size() > 1) {
            int _h = right.getFirst();
            for (int i = 1; i < right.size(); i++) {
                ans += this.getSize(right.get(i), _h);
                _h = right.get(i);
            }
        }
        return ans;
    }

    private int getSize(int l, int r) {
        if (l >= r - 1) {
            return 0;
        }
        int h = Math.min(this.height[l], this.height[r]);
        int res = 0;
        for (int i = l + 1; i < r; i++) {
            res += h - this.height[i];
        }
        return res;
    }
}

矩阵

矩阵置零

题目

解答

稍微注意一下,如果记录0的点位后,逐个清除,复杂度是O(mn+k(m+n)),所以需要合并置零,以达到O(mn)

java
class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        Set<Integer> row = new HashSet<>();
        Set<Integer> col = new HashSet<>();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    row.add(i);
                    col.add(j);
                }
            }
        }
        for (Integer i : row) {
            for (int j = 0; j < n; j++) {
                matrix[i][j] = 0;
            }
        }
        for(int i =0;i<m;i++){
            for(Integer j: col){
                matrix[i][j]=0;
            }
        }
    }
}

螺旋矩阵

题目

分析

常规的模拟,就不要想着写什么优雅的代码了,直接全部面向过程。

java
class Solution {
    private int[] dir = new int[] { 0, 1 };
    int max_row, max_col;

    public List<Integer> spiralOrder(int[][] matrix) {
        this.max_row = matrix.length;
        this.max_col = matrix[0].length;
        List<Integer> ans = new ArrayList<>();
        int max_count = matrix.length * matrix[0].length;
        int i = 0, j = -1;
        while (this.max_row!=0&&this.max_col!=0) {
            int row_d = this.dir[0], col_d = this.dir[1], l=0;
            if(col_d!=0){
                while (l++<this.max_col) {
                    j+=col_d;
                    ans.add(matrix[i][j]);
                }
            }else{
                while (l++<this.max_row) {
                    i+=row_d;
                    ans.add(matrix[i][j]);
                }
            }
            turn();
        }
        return ans;
    }

    private void turn() {
        int row_d = this.dir[0], col_d = this.dir[1];
        if (row_d == 0 && col_d == 1) {
            this.dir[0] = 1;
            this.dir[1] = 0;
            this.max_row--;
        } else if (row_d == 1 && col_d == 0) {
            this.dir[0] = 0;
            this.dir[1] = -1;
            this.max_col--;
        } else if (row_d == 0 && col_d == -1) {
            this.dir[0] = -1;
            this.dir[1] = 0;
            this.max_row--;
        } else {
            this.dir[0] = 0;
            this.dir[1] = 1;
            this.max_col--;
        }
    }
}

旋转图像

题目

____``[****](https://baike.baidu.com/item/%E5%8E%9F%E5%9C%B0%E7%AE%97%E6%B3%95)********

plain
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

plain
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

  • <font style="color:rgba(38, 38, 38, 0.75);background-color:rgb(240, 240, 240);">n == matrix.length == matrix[i].length</font>
  • <font style="color:rgba(38, 38, 38, 0.75);background-color:rgb(240, 240, 240);">1 <= n <= 20</font>
  • <font style="color:rgba(38, 38, 38, 0.75);background-color:rgb(240, 240, 240);">-1000 <= matrix[i][j] <= 1000</font>

解答

模拟,特性是4点旋转矩阵

java
class Solution {
    public void rotate(int[][] matrix) {
        /**
         * 起点为k=0,1,2...(k,k),k<=n>>1;
         * 当n=4,l=n-1=3,(0,1)(1,3)(3,2)(2,0)
         * 4点旋转,设(i,j)(j,l-i)(l-i,l-j)(l-j,i)
         * 外层控制点范围[k,k+l)
         */
        int N = matrix.length;
        int max_k = N >> 1;
        int l = N -1;
        for (int k = 0; k < max_k; k++) {
            int n = N - 2 * k;
            int len = n - 1;
            for (int i = k, j = k; j < k + len; j++) {
                int a1 = matrix[i][j], a2 = matrix[j][l - i], a3 = matrix[l - i][l - j], a4 = matrix[l - j][i];
                matrix[i][j] = a4;
                matrix[j][l - i] = a1;
                matrix[l - i][l - j] = a2;
                matrix[l - j][i] = a3;
            }
        }
    }
}

搜索二维矩阵 II

解答

二分

很自然的想到二分,不过注意是区间特性,只能在行内查找

java
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        /**
         * 每一行就是一段可能重复或不重复的递增区间段,先二分确定行,如果存在行,再二分查找行内
         */
        int m = matrix.length, n = matrix[0].length;
        for (int i = 0; i < m && matrix[i][0] <= target; i++) {
            int left = 0, right = n;
            while (left < right) {
                int mid = (left + right) >> 1;
                if (matrix[i][mid] == target) {
                    return true;
                }
                if (matrix[i][mid] > target) {
                    right = mid;
                } else {
                    left = mid+1;
                }
            }
        }
        return false;
    }
}

Z字形

从左下角开始,如果大就++col,如果小就--y,没出界前找到了就返回true

java
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int x = 0, y = n - 1;
        while (x < m && y >= 0) {
            if (matrix[x][y] == target) {
                return true;
            }
            if (matrix[x][y] > target) {
                --y;
            } else {
                ++x;
            }
        }
        return false;
    }
}

二分

搜索插入位置

模板题,懒得说。

java
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length;
        while (left<right) {
            int mid = (left+right)>>1;
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]>target){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        // 数组的边界
        if(left>=nums.length){
            return nums.length;
        }
        if(nums[left]>target){
            return left;
        }
        return left+1;
    }
}

搜索二维矩阵

模板题,懒得说

java
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int left = 0, right = m;
        while (left < right) {
            int mid = (left + right) >> 1;
            // System.out.printf(" mid = %d\n", mid);
            if (matrix[mid][0] <= target && target <= matrix[mid][n - 1]) {
                /** 在行内 */
                int _left = 0, _right = n;
                while (_left < _right) {
                    int _mid = (_left + _right) >> 1;
                    // System.out.printf("_mid = %d\n",_mid);
                    if (matrix[mid][_mid] == target)
                        return true;
                    else if (matrix[mid][_mid] > target) {
                        _right = _mid;
                    } else {
                        _left = _mid + 1;
                    }
                }
                break;
            } else if (matrix[mid][0] > target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return false;
    }
}

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

两次模板找边界,边界容易出错。

java
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0,right = nums.length;
        while (left<right) {
            int mid= (left+right)>>1;
            if(nums[mid]>=target){
                right = mid;
            }
            else{
                left=mid+1;
            }
        }
        int start = left;
        if(start>=nums.length||nums[start]!=target){
            return new int[]{-1,-1};
        }
        left = 0;
        right = nums.length;
        while (left<right) {
            int mid= (left+right)>>1;
            if(nums[mid]>target){
                right = mid;
            }
            else{
                left=mid+1;
            }
        }
        int end = left;
        if(nums.length==end||nums[end]!=target)
            end--;
        return new int[]{start,end};
    }
}

搜索旋转排序数组

``****``````****``****``````****````````````

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0

输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3

输出:-1

这个先要确定边界k的位置,否则没法将其视为有序数组。

INFO

这里的确定边界和下一题的解题方法是一样的,可以先看看下一题。

然后由于需要logN,不能去复制数组,则只有通过一点数学手段找到数字了

java
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length-1;
        while (left<right) {
            int mid = (left+right)>>1;
            if(nums[mid]<=nums[right]){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        int l = nums.length, bias = left;
        left = 0; right = nums.length-1;
        while (left<right) {
            int mid = (left+right)>>1;
            int _m = (mid+bias)%l;
            if(nums[_m]>=target){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        if(nums[(left+bias)%l]==target){
            return (left+bias)%l;
        }
        return -1;
    }
}

寻找旋转排序数组中的最小值

``````****``
  • ````
  • ````
``****``****``****``
java
class Solution {
    public int findMin(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int mid = (left + right) >> 1;
            if (nums[mid] <= nums[right]) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return nums[left];
    }
}

滑动窗口

无重复字符的最长子串

java
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] str = s.toCharArray();
        int l = s.length();
        if(l<2){
            return l;
        }
        Map<Character, Integer> set = new HashMap<>();
        int left = 0, right = 0;
        int max_l = 1;
        set.put(str[0],0);
        for(int i=1;i<l;i++){
            if(set.containsKey(str[i])){
                // 如果包含重复字符,就要收缩到重复字符的右边
                int j = left;
                left = set.get(str[i]) + 1;
                while (j<left) {
                    set.remove(str[j++]);
                }
                right = i;
                set.put(str[i], i);
                
            }else{
                // 正常情况扩展右边,并比较最大值
                right++;
                max_l = Math.max(max_l, right-left+1);
                set.put(str[i],i);
            }
        }
        return max_l;
    }
}

找到字符串中所有字母异位词

java
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 初始化
        int[] count = new int[26];
        int l = s.length();
        char[] str = s.toCharArray();
        char[] _p = p.toCharArray();
        int p_l  = p.length();
        List<Integer> ans = new ArrayList<>();
        int[] p_count = new int[26];
        if(l<p_l){
            return ans;
        }
        for (int i = 0; i < p_l; i++) {
            count[str[i]-'a']++;
            p_count[_p[i]-'a']++;
        }
        if(isValid(count, p_count))
            ans.add(0);

        int right = p_l;
        int left = 0;
        // 移动固定窗口,维护窗口信息
        while (right<l) {
            count[str[left]-'a']--;
            left++;
            count[str[right]-'a']++;
            right++;
            if(isValid(count, p_count))
                ans.add(left);
        }
        return ans;
    }
    private boolean isValid(int[] a,int[] b){
        for (int i = 0; i < 26; i++) {
            if(a[i]!=b[i])
                return false;
        }
        return true;
    }
}

最小栈

easy target,常数时间就需要内存来维护信息

java
class MinStack {
    private List<Integer> stack = new ArrayList<>();
    private List<Integer> minStack = new ArrayList<>();

    public MinStack() {
        
    }
    
    public void push(int val) {
        stack.add(val);
        if(minStack.isEmpty()){
            minStack.add(val);
        }else{
            minStack.add(Math.min(minStack.getLast(), val));
        }
    }
    
    public void pop() {
        stack.removeLast();
        minStack.removeLast();
    }
    
    public int top() {
        return stack.getLast();
    }
    
    public int getMin() {
        return minStack.getLast();
    }
}

字符串解码

遇到[就入栈(递归栈),]就出栈,线性遍历一次就够了。

java
class Solution {
    int i = 0;
    char[] str;
    public String decodeString(String s) {
        str = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        int l = s.length();
        for(;i<l;i++){
            if(str[i]>='a'&&str[i]<='z'){
                sb.append(str[i]);
            }else if(str[i]>='0'&&str[i]<='9'){
                int num_left = i;
                StringBuilder num = new StringBuilder();
                num.append(str[i]);
                while (str[++i]!='[') {
                    num.append(str[i]);
                }
                Integer ratio = new Integer(num.toString());
                // 现在i指向的是[
                i++;
                String sub_str = decodeString(s);
                // System.out.printf("将要返回%d倍的",ratio);
                // System.out.println(sub_str);
                while (ratio-->0) {
                    sb.append(sub_str);
                }
            }else {
                return sb.toString();
            }
        }
        return sb.toString();
    }
}

每日温度

显然的单调栈。

java
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] ans = new int[temperatures.length];
        int l = temperatures.length;
        List<Integer> stack = new LinkedList<>();
        for (int i = 0; i < l; i++) {
            while (!stack.isEmpty()) {
                int top = stack.getLast();
                if (temperatures[top] < temperatures[i]) {
                    ans[top] = i - top;
                    stack.removeLast();
                }else{
                    break;
                }
            }
            stack.add(i);
        }
        return ans;
    }
}

有效的括号

注意顺序

java
class Solution {
    public boolean isValid(String s) {
        char[] str = s.toCharArray();
        List<Character> stack = new LinkedList<>();
        for (char ch : str) {
            switch (ch) {
                case '(':
                case '{':
                case '[':
                    stack.add(ch);
                    break;
                case ')':
                    if(!stack.isEmpty()&&stack.getLast()=='('){
                        stack.removeLast();
                    }else{
                        return false;
                    }
                    break;
                case '}':
                    if(!stack.isEmpty()&&stack.getLast()=='{'){
                        stack.removeLast();
                    }else{
                        return false;
                    }
                    break;
                case ']':
                    if(!stack.isEmpty()&&stack.getLast()=='['){
                        stack.removeLast();
                    }else{
                        return false;
                    }
                    break;
                default:
                    break;
            }
        }
        if (stack.isEmpty()) {
            return true;
        }
        return false;
    }
}

链表

相交链表

能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

显然尾部要合并,然后可以算出长度,让两边长度相等后,再逐个找是否相同。

java
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int la = getLen(headA);
        int lb = getLen(headB);
        if(la>lb){
            return findCommon(headA, headB, la - lb);
        }
        return findCommon(headB, headA, lb-la);
    }
    private int getLen(ListNode a){
        if(a==null)
            return 0;
        int count = 1;
        while (a.next!=null) {
            a = a.next;
            count++;
        }
        return count;
    }
    private ListNode findCommon(ListNode lon, ListNode shor, int derta){
        while (derta-->0) {
            lon = lon.next;
        }
        while (lon!=shor) {
            lon = lon.next;
            shor = shor.next;
        }
        return lon;
    }
}

反转链表

简单的变量交换。。

java
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null)
            return null;
        if(head.next==null)
            return head;
        ListNode pre = null, post;
        while (head!=null) {
            post = head.next;
            head.next = pre;
            pre = head;
            head = post;
        }
        return pre;
    }
}

回文链表

可以将后半段反转过来,再逐个对比看是否相等。

怎么找中间的反转点:快慢指针

java
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head.next == null) {
            return true;
        }
        if (head.next.next == null) {
            return head.val == head.next.val;
        }
        return reverseSolution(head);
    }

    private boolean reverseSolution(ListNode head) {
        ListNode fast = head, slow = head;
        // 如果fast变为null,说明长度为偶数
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode end = reverseList(slow);
        return isSame(head, end);
    }

    private ListNode reverseList(ListNode head) {
        if (head == null)
            return null;
        if (head.next == null)
            return head;
        ListNode pre = null, post;
        while (head != null) {
            post = head.next;
            head.next = pre;
            pre = head;
            head = post;
        }
        return pre;
    }

    private boolean isSame(ListNode a, ListNode b) {
        while (a != null && b != null) {
            if (a.val != b.val)
                return false;
            a = a.next;
            b = b.next;
        }
        return true;
    }
}

环形链表

典。

java
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head, slow = head;
        int s = 0;
        while (fast!=null && fast.next!=null) {
            fast  = fast.next.next;
            slow = slow.next;
            ++s;
            if(fast == slow){
                fast = head;
                return true;
            }
        }
        return false;
    }
}

环形链表 II

太典了。

java
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        int s = 0;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            ++s;
            if (fast == slow) {
                fast = head;
                while (fast!=slow) {
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;
            }
        }
        return null;
    }
}

合并两个有序链表

”简单题“,但是有点烦人。

java
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null && list2 == null)
            return null;
        if (list1 == null)
            return list2;
        if (list2 == null)
            return list1;

        ListNode head, tail, tmp;
        if (list1.val > list2.val) {
            head = list2;
            tmp = list2.next;
            list2.next = null;
            list2 = tmp;
        } else {
            head = list1;
            tmp = list1.next;
            list1.next = null;
            list1 = tmp;
        }
        tail = head;
        while (list1 != null && list2 != null) {
            if (list1.val > list2.val) {
                tmp = list2.next;
                list2.next = null;
                tail.next = list2;
                tail = tail.next;
                list2 = tmp;
            } else {
                tmp = list1.next;
                list1.next = null;
                tail.next = list1;
                tail = tail.next;
                list1 = tmp;
            }
        }
        if (list1 == null && list2 != null)
            tail.next = list2;
        else if (list2 == null && list1 != null)
            tail.next = list1;
        return head;
    }
}
javascript
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function (list1, list2) {
    if (list1 == null)
        return list2;
    if (list2 == null)
        return list1;
    var listhead;
    if (list1.val < list2.val) {
        var tmp = list1.next;
        list1.next = null;
        listhead = list1;
        list1 = tmp;
    } else {
        var tmp = list2.next;
        list2.next = null;
        listhead =list2
        list2 = tmp;
    }
    var listtail = listhead;
    while (list1 != null && list2 != null) {
        if (list1.val < list2.val) {
            var tmp = list1.next;
            list1.next = null;
            listtail.next = list1;
            list1 = tmp;
        } else {
            var tmp = list2.next;
            list2.next = null;
            listtail.next = list2;
            list2 = tmp;
        }
        listtail = listtail.next;
    }
    if(list1!=null){
        listtail.next = list1;
    }
    else{
        listtail.next = list2;
    }
    return listhead;
};

两数相加

,emm,还是有点小烦人的。

java
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int add = 0;
        ListNode head, tail, tmp;
        {
            int res = l1.val + l2.val + add;
            l1 = l1.next;
            l2 = l2.next;
            add = res / 10;
            head = new ListNode(res % 10);
            tail = head;
        }

        while (l1 != null && l2 != null) {
            int res = l1.val + l2.val + add;
            l1 = l1.next;
            l2 = l2.next;
            add = res / 10;
            tmp = new ListNode(res % 10);
            tail.next = tmp;
            tail = tmp;
        }
        if (l1 == null) {
            l1 = l2;
        }
        while (add != 0) {
            if (l1 == null) {
                tail.next = new ListNode(add);
                break;
            }
            int res = l1.val + add;
            l1 = l1.next;
            add = res / 10;
            tmp = new ListNode(res % 10);
            tail.next = tmp;
            tail = tmp;
        }
        if (l1 != null) {
            tail.next = l1;
        }
        return head;
    }
}

删除链表的倒数第 N 个结点

实际上不可能操作数更小了,只是确实某种意义上来说是一次遍历。

java
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode right = head, left, before_left = null;
        while (n-->0) {
            right = right.next;
        }
        left = head;
        while (right!=null) {
            right = right.next;
            before_left = left;
            left = left.next;
        }
        if(before_left==null){
            return left.next;
        }
        before_left.next = left.next;
        return head;
    }
}

两两交换链表中的节点

java
class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null)
            return null;
        if (head.next == null)
            return head;
        ListNode a = head, b = head.next, listHead, listTail;
        a.next = b.next;
        b.next = a;
        listHead = b;
        listTail = a;
        while (listTail.next != null) {
            a = listTail.next;
            if (a.next == null)
                break;
            b = a.next;
            listTail.next = b;
            a.next = b.next;
            b.next = a;
            listTail = a;
        }
        return listHead;
    }
}

随机链表的复制

java
class Solution {
    public Node copyRandomList(Node head) {
        Map<Node, Node> map = new HashMap<>();
        Node tmp = head, last=null, first=null;
        while (tmp!=null) {
            Node new_node = new Node(tmp.val);
            if(last!=null)
                last.next = new_node;
            else
                first = new_node;
            map.put(tmp, new_node);
            tmp = tmp.next;
            last = new_node;
        }
        tmp = head;
        while (tmp!=null) {
            if(tmp.random!=null){
                map.get(tmp).random = map.get(tmp.random);
            }
            tmp = tmp.next;
        }
        return first;
    }
}

LRU 缓存

O(1)的操作,那只有引入hash表了,但是算法题又不能之间让你用LinkedHashTable,所以要自己构造链表及其节点。

java
class LRUCache {
    Map<Integer, Node> map = new HashMap<>();
    private final int capcacity;

    private class Node {
        int key;
        int value;
        Node pre;
        Node next;

        public Node(int v, int k) {
            this.value = v;
            this.key = k;
            this.pre = null;
            this.next = null;
        }
    }

    private int size;
    Node head, tail;

    public LRUCache(int capacity) {
        this.capcacity = capacity;
        this.head = null;
        this.tail = null;
    }

    public int get(int key) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            toFirst(node);
            return node.value;
        }
        return -1;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            toFirst(node);
            node.value = value;
        } else if (size == 0) {
            Node node = new Node(value, key);
            map.put(key, node);
            head = node;
            tail = node;
            size = 1;
        } else if (size < capcacity) {
            Node node = new Node(value, key);
            map.put(key, node);
            head.pre = node;
            node.next = head;
            head = node;
            size++;
        } else {
            map.remove(tail.key);
            tail = tail.pre;
            Node node = new Node(value, key);
            map.put(key, node);
            if (tail == null) {
                head = node;
                tail = node;
            } else {
                tail.next = null;
                head.pre = node;
                node.next = head;
                head = node;
            }
        }
    }

    private void toFirst(Node node) {
        if (node == head) {
            return;
        }
        Node pre = node.pre;
        Node next = node.next;
        if (pre != null) {
            pre.next = next;
        }
        if (next != null) {
            next.pre = pre;
        } else {
            tail = pre;
        }
        node.pre = null;
        node.next = head;
        head.pre = node;
        head = node;
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

排序链表

O(n log n) 时间复杂度就想到归并,所以还得先写一个合并有序链表,自顶向下是占用n的空间

常数空间需要自底向上。以下是自底向上的做法。

javascript
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var sortList = function (head) {
    var length = 0;
    for (var tmp = head; tmp != null; tmp = tmp.next)length++;
    var dummyRoot = new ListNode(0, head);
    // 子串长度从单个节点开始,这样就可以利用merge进行排序
    for (var l = 1; l < length; l <<= 1) {
        var pre = dummyRoot, sub_h = dummyRoot.next;
        while (sub_h != null) {
            var sub_tail = sub_h;
            // 找到第一个子串尾部
            for (var i = 1; i < l && sub_tail?.next != null; i++, sub_tail = sub_tail.next);
            var sub_h2 = sub_tail.next;
            sub_tail.next = null; // 分割子串之间
            var sub_tail2 = sub_h2;
            // 找到第二个子串尾部
            for (var i = 1; i < l && sub_tail2?.next != null; i++, sub_tail2 = sub_tail2.next);
            // 下一轮开头就是这次子串2尾部的下一个
            var next = null;
            if(sub_tail2!=null){
                next = sub_tail2.next;
                sub_tail2.next = null;
            }
            var mergedList = merge(sub_h, sub_h2);
            pre.next = mergedList;
            while(pre.next!=null) pre = pre.next;
            // 下一轮开头就是这次子串2尾部的下一个
            sub_h = next;
        }
    }
    return dummyRoot.next;
};
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 */
function merge(list1, list2) {
    if (list1 == null)
        return list2;
    if (list2 == null)
        return list1;
    var listhead;
    if (list1.val < list2.val) {
        var tmp = list1.next;
        list1.next = null;
        listhead = list1;
        list1 = tmp;
    } else {
        var tmp = list2.next;
        list2.next = null;
        listhead = list2
        list2 = tmp;
    }
    var listsub_tail = listhead;
    while (list1 != null && list2 != null) {
        if (list1.val < list2.val) {
            var tmp = list1.next;
            list1.next = null;
            listsub_tail.next = list1;
            list1 = tmp;
        } else {
            var tmp = list2.next;
            list2.next = null;
            listsub_tail.next = list2;
            list2 = tmp;
        }
        listsub_tail = listsub_tail.next;
    }
    if (list1 != null) {
        listsub_tail.next = list1;
    }
    else {
        listsub_tail.next = list2;
    }
    return listhead;
}

技巧

只出现一次的数字

同样的数异或为0

java
class Solution {
    public int singleNumber(int[] nums) {
        int a = 0, l = nums.length;
        for (int i = 0; i < l; i++) {
            a^=nums[i];
        }
        return a;
    }
}

数组

最大子数组和

分治是很精妙,但是实际用起来的时间消耗比dp大。。。

java
class Solution {
    public int maxSubArray(int[] nums) {
        return divSolution(nums);
    }

    private int dpSolution(int[] nums) {
        int l = nums.length;
        int[] dp = new int[l + 1];
        int ans = nums[0];
        for (int i=0;i<l;i++) {
            dp[i+1] = Math.max(nums[i], nums[i]+dp[i]);
            ans = Math.max(ans, dp[i+1]);
        }
        return ans;
    }

    class Node {
        int leftMax, rightMax, max, sum;
        Node(int l, int r, int max, int sum){
            this.leftMax = l;
            this.rightMax = r;
            this.max = max;
            this.sum = sum;
        }
        Node(){

        }
    }
    int[] nums;
    private int divSolution(int[] nums){
        this.nums = nums;
        Node node = resolve(0, nums.length-1);
        return node.max;
    }
    private Node resolve(int left, int right){
        if(left==right){
            return new Node(nums[left],nums[left], nums[left], nums[left]);
        }
        int mid = (left+right)>>1;
        Node leftNode = resolve(left, mid);
        Node rightNode = resolve(mid+1, right);
        Node node = new Node();
        node.sum = leftNode.sum +rightNode.sum;
        node.leftMax = Math.max(leftNode.leftMax, rightNode.leftMax+leftNode.sum);
        node.rightMax = Math.max(rightNode.rightMax, leftNode.rightMax+rightNode.sum);
        node.max = Math.max( leftNode.rightMax+rightNode.leftMax,Math.max(leftNode.max, rightNode.max));
        return node;
    }
}

合并区间

java
class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b)->{
            return a[0] - b[0];
        });
        List<int[]> list = new ArrayList<>();
        int start = intervals[0][0];
        int end = intervals[0][1];
        for(int i=1;i<intervals.length;i++){
            int[] item = intervals[i];
            if(item[0]<=end){
                end = Math.max(end, item[1]);
            }else{
                list.add(new int[]{start, end});
                start = item[0];
                end = item[1];
            }
        }
        list.add(new int[]{start, end});
        int[][] ans = new int[list.size()][];
        for (int i=0;i<ans.length;i++) {
            ans[i] = list.get(i);
        }
        return ans;
    }
}

java
class Solution {
    public void rotate(int[] nums, int k) {
        int l = nums.length;
        int[] tmp = new int[l];
        for (int i = 0; i < tmp.length; i++) {
            tmp[i] = nums[i];
        }
        for (int i = 0; i < tmp.length; i++) {
            int index = (i+k);
            while(index>=l)
                index -=l;
            nums[index] = tmp[i];
        }
    }
}
java
// 待做

java
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int l = nums.length;
        int[] right = new int[l];
        right[l-1]=1;
        for (int i = l-2; i>=0; i--) {
            right[i] = right[i+1]*nums[i+1];
        }
        int left = 1;
        for (int i = 0; i < l; i++) {
            int tmp = nums[i];
            nums[i] = left * right[i];
            left*=tmp;
        }
        return nums;
    }
}

java
class Solution {
    public int firstMissingPositive(int[] nums) {
        int l = nums.length;
        // 找到第一个连续的正数区间末尾,这个区间是[1,N]的子区间
        for (int index = 0; index < l; index++) {
            int i = nums[index];
            if (i > 0) {
                if (i <= l) {
                    if (nums[i - 1] != i) {
                        int tmp = nums[i - 1];
                        nums[i - 1] = i;
                        nums[index] = tmp;
                        index--;
                    }else if(index+1!=i){
                        nums[index]=-1;
                    }
                } else
                    nums[index] = -1;
            }
        }
        for (int i : nums) {
            System.out.println(i);
        }
        int i;
        for (i = 0; i < l; i++) {
            if (nums[i] <= 0)
                return i + 1;
        }
        return i+1;
    }
}

贪心

买卖股票的最佳时机

策略:只能买一次最大的,某日能获得的最大利润就是前几天的最低价格开始。

java
class Solution {
    public int maxProfit(int[] prices) {
        int  l =prices.length;
        int max = 0, min = prices[0];
        for (int j=1;j<l;j++) {
            int i = prices[j];
            if(i<min){
                min = i;
                continue;
            }
            max = Math.max(max, i-min);
        }
        return max;
    }
}

跳跃游戏

策略:尽可能的远,因为你能达到>=终点的地方,说明你可以少跳一点达到终点。

java
class Solution {
    public boolean canJump(int[] nums) {
        int index = 0, lastIndex = -1;
        int l = nums.length;
        while (index!=lastIndex&&index<l) {
            if(index+1==nums.length)
                return true;
            int i = nums[index];
            int r = 0, r_i = 0;
            for(int j = 1;j<=i&&index+j<l;j++){
                int res =  j+nums[index+j];
                if(res>=r){
                    r_i = j;
                    r = res;
                }
            }
            lastIndex = index;
            index += r_i;
        }
        return false;
    }
}
java
public class Solution {
    public boolean canJump(int[] nums) {
        int n = nums.length;
        int rightmost = 0;
        for (int i = 0; i < n; ++i) {
            if (i <= rightmost) {
                rightmost = Math.max(rightmost, i + nums[i]);
                if (rightmost >= n - 1) {
                    return true;
                }
            }
        }
        return false;
    }
}

跳跃游戏 II

DP

爬楼梯

java
class Solution {
    public int climbStairs(int n) {
        int pre_1 = 0, pre_2 = 1, cur = 0;
        for(int i=0;i<n;i++){
            cur = pre_1+pre_2;
            pre_1 = pre_2;
            pre_2= cur;
        }
        return cur;
    }
}

杨辉三角

java
class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> list = new ArrayList<>();
        {
            List<Integer> a = new ArrayList<>();
            a.add(1);
            list.add(a);
        }
        for (int i = 2; i <= numRows; i++) {
            List<Integer> row = new ArrayList<>();
            List<Integer> last_row = list.get(i-2);
            row.add(1);
            for (int j = 1; j < i-1 ; j++) {
                row.add(last_row.get(j-1)+last_row.get(j));
            }
            row.add(1);
            list.add(row);
        }
        return list;
    }
}

解法1:dp

javascript
/**
 * @param {number[]} nums
 * @return {number}
 */
var jump = function(nums) {
    var count = Array.from({length:nums.length},()=>Infinity);
    count[0]=0;
    for(var i=0;i<nums.length;i++){
        for(var j=1;j<=nums[i];j++){
            if(i+j<nums.length)
            count[i+j]=Math.min(count[i+j], count[i]+1);
        }
    }
    return count[nums.length-1];
};

解法2: 贪心

javascript
/**
 * @param {number[]} nums
 * @return {number}
 */
var jump = function (nums) {
    var count =0;
    var rightBound = 0;
    var reachAble = nums[0];
    for(var i=0;i<nums.length-1/* 最后一格必定能到达,所以只需要判断之前的层数 */;i++){
        // 基于上一层的最大范围内,跳数共用
        if(i<=rightBound){
            // 更新最大范围
            if(reachAble<i+nums[i]){
                reachAble = i+nums[i];
            }
            if(i==rightBound){
                // 到达上次最大边界,下一层需要的跳数+1
                rightBound = reachAble;
                count++;
            }
        }
    }
    return count;
};

二叉树

java
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal2(TreeNode root) {
        List<TreeNode> stack = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        Set<TreeNode> set = new HashSet<>();
        if (root != null) {
            stack.add(root);
        }
        while (stack.size() != 0) {
            TreeNode t = stack.getLast();

            if (t.left != null && !set.contains(t.left)) {
                stack.add(t.left);
                continue;
            }
            if (!set.contains(t)) {
                set.add(t);
                list.add(t.val);
            }

            if (t.right != null && !set.contains(t.right)) {
                stack.add(t.right);
                continue;
            }
            stack.removeLast();
        }
        return list;
    }
    private List<Integer> ans = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        solution(root);
        return ans;
    }
    private void solution(TreeNode t){
        if(t==null)
        return;
        solution(t.left);
        ans.add(t.val);
        solution(t.right);
    }
}

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

java
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null)
            return null;
        invertTree(root.left);
        invertTree(root.right);
        TreeNode t = root.right;
        root.right = root.left;
        root.left = t;
        return root;
    }
}

java
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null)
            return null;
        invertTree(root.left);
        invertTree(root.right);
        TreeNode t = root.right;
        root.right = root.left;
        root.left = t;
        return root;
    }

    public boolean isSymmetric(TreeNode root) {
        if (root.left==null&&root.right!=null||root.left!=null&&root.right==null) {
            return false;
        }
        invertTree(root.left);
        return compare(root.left, root.right);
    }
    private boolean compare(TreeNode l, TreeNode r){
        if(l==null&&r!=null||l!=null&&r==null)
            return false;
        return l==null&&r==null||compare(l.left, r.left) && compare(l.right, r.right) && l.val == r.val;
    }
}

直径路径必然经过一种根节点,所以遍历时用每个经过子树节点的长度来更新,并返回最大深度给上层用于更新。

java
class Solution {
    int D = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        getMax(root);
        return D;
    }
    private int getMax(TreeNode r){
        if(r==null)
            return 0;
        int l1 = getMax(r.left);
        int l2 = getMax(r.right);
        D = Math.max(D, l1+l2);
        return Math.max(l1, l2)+1;
    }
}

二叉树的层序遍历

java
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> lists = new ArrayList<>();

        List<Pair<TreeNode, Integer>> stack = new LinkedList<>();
        if (root == null)
            return lists;
        stack.add(new Pair<TreeNode, Integer>(root, 0));
        List<Integer> tmp = new ArrayList<>();
        int last_level = 0;
        while (stack.size() != 0) {
            Pair<TreeNode, Integer> p = stack.removeFirst();
            TreeNode t = p.getKey();
            Integer t_l = p.getValue();
            if (t_l != last_level) {
                last_level = t_l;
                lists.add(tmp);
                tmp = new ArrayList<>();
            }
            tmp.add(t.val);
            if (t.left != null)
                stack.add(new Pair(t.left, t_l + 1));
            if (t.right != null)
                stack.add(new Pair(t.right, t_l + 1));
        }
        lists.add(tmp);
        return lists;
    }
}

java
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isValidBST = function(root) {
    var valid = (root, left, right) => {
        if(root==null)
            return true;
        if(root.val <= left || root.val >= right)
            return false;
        return valid(root.left, left, root.val) && valid(root.right, root.val, right);
    }
    return valid(root, -Infinity, Infinity);
};

中序遍历

java
var kthSmallest = function(root, k) {
  var stack = [root];
  var count = 0;
  while(stack.length>0){
    var node = stack[stack.length-1];
    if(node.left!=null){
        stack.push(node.left)
        node.left = null;
        continue;
    }
    count++;
    if(count==k){
        return node.val;
    }
    stack.pop();
    if(node.right!=null){
        stack.push(node.right);
    }
  }  
};

二叉树的右视图

javascript
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var rightSideView = function (root) {
    if(root==null){
        return [];
    }
    var queue = [{
        node: root,
        level: 0
    }];
    var ans = []
    while (queue.length > 0) {
        var item = queue.shift();
        if (ans.length == item.level) {
            ans.push(item.node.val);
        } else {
            ans[item.level] = item.node.val;
        }
        if (item.node.left != null) {
            queue.push({
                node: item.node.left,
                level: item.level + 1
            });
        }
        if (item.node.right != null) {
            queue.push({
                node: item.node.right,
                level: item.level + 1
            });
        }
    }
    return ans;
};

javascript
/**
 * @param {TreeNode} root
 * @return {void} Do not return anything, modify root in-place instead.
 */
var flatten = function(root) {
  if(root==null){
    return null;
  }
  myflat(root);
};
/**
 * @param {TreeNode} root
 * @param {TreeNode} insertion
 * @return {TreeNode} tail
 */
function myflat(root){
  if(root.left==null&&root.right==null){
    return root;
  }
  if(root.left==null&&root.right!=null){
    return myflat(root.right);
  }
  if(root.left!=null&&root.right==null){
    root.right = root.left;
    root.left= null;
    return myflat(root.right);
  }
  var right_tail = myflat(root.right);
  myflat(root.left).right = root.right;
  root.right = root.left;
  root.left = null;
  return right_tail;
}

javascript
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
var buildTree = function(preorder, inorder) {
  var root = new TreeNode(preorder[0], null, null);
  var index = inorder.findIndex(i=>i==root.val);
  if(index>0)
    root.left = buildTree(preorder.slice(1, index+1), inorder.slice(0, index));
  if(index+1<preorder.length)
    root.right = buildTree(preorder.slice(index+1), inorder.slice(index+1));
  return root;
};

javascript
var sum;
var target;
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {number}
 */
var pathSum = function(root, targetSum) {
    sum = 0;
    target = targetSum;
    search(root);
    return sum;
};
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
function search(root){
    if(root==null){
        return [];
    }
    var arr_l = search(root.left);
    var arr_r = search(root.right);
    var arr = arr_l.concat(arr_r);
    for(let i = 0;i<arr.length;){
        arr[i]+=root.val;
        if(arr[i]==target){
            sum++;
        }
        i++;
    }
    if(root.val==target){
        sum++;
    }
    arr.push(root.val);
    return arr;
}

javascript
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    var a1 = find(root,p);
    var a2 = find(root, q);
    if(a1.length>a2.length)
     {
        tmp = a2;
        a2 = a1;
        a1 = tmp;
     }
     var i = 0, j = a2.length - a1.length;
     while(i<a1.length){
        if(a1[i]==a2[i+j]){
            return a1[i];
        }
        i++;
     }
    
};
/**
 * @param {TreeNode} root
 * @param {TreeNode} target
 * @return {number[]}
 */
function find(root, target){
    if(root==null)
        return [];
    if(root==target)
        return [target];
    var left = find(root.left,target);
    if(left.length>0){
        left.push(root)
        return left;
    }
    var right = find(root.right,target);
    if(right.length>0){
        right.push(root);
        return right;
    }
    return [];
}

javascript
var maxPath;
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxPathSum = function (root) {
    maxPath = -Infinity;
    custom(root);
    return maxPath;
};
/**
 * @param{TreeNode} root
 * @return {number}
 */
function custom(root) {
    var left = -Infinity;
    if (root.left != null)
        left = custom(root.left)
    var right = -Infinity;
    if (root.right != null)
        right = custom(root.right);
    var max_son = Math.max(left, right);
    var tmp = root.val;
    tmp = Math.max(root.val, max_son + root.val);
    maxPath = Math.max(maxPath, tmp, left + right + root.val);
    return tmp;
}

回溯

javascript
var ans;
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
    ans = [];
    custom(nums, []);
    return ans;
};
/**
 * @param {number[]} list
 * @param {number[]} tmp
 */
function custom (list, tmp){
    if(list.length==1){
        ans.push(tmp.concat(list));
        return;
    }
    var l = list.length;
    for(var i =0;i<l;i++){
        custom(list.filter(it=>it!=list[i]), tmp.concat([list[i]]));
    }
}

javascript
var ans;
var g_nums;
var l;
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsets = function(nums) {
    ans = [];
    g_nums = nums;
    l = g_nums.length;
    for(var i = 0;i<=l;i++){
        custom(0,[],i);
    }
    return ans;
};
/**
 * @param {number} start
 * @param {number[]} tmp
 * @param {number} remain
 */
function custom(start, tmp, remain){
    if(remain==0){
        ans.push(tmp);
        return;
    }
    if(remain<0){
        return;
    }
    for(var i = start;i<l;i++){
        custom(i+1, tmp.concat([g_nums[i]]), remain-1);
    }

}

javascript
var ans;
var map = [,,"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"];
/**
 * @param {string} digits
 * @return {string[]}
 */
var letterCombinations = function(digits) {
    ans =[];
    if(digits.length)
    custom(digits, "");
    return ans;
};
/**
 * @param {string} list
 * @param {string} tmp
 */
function custom(list,tmp){
    if(list.length==0){
        ans.push(tmp);
        return;
    }
    var li = map[Number(list.charAt(0))];
    for(var i=0;i<li.length;i++){
        custom(list.slice(1),tmp+li.charAt(i));
    }
}

javascript
/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {
    if(candidates.length==0){
        return [];
    }
    if(target)
    var ans = [];
    for(var i = 0; i<candidates.length;i++){
        var el = candidates[i];
        if(el>target)
            continue;
        if(el==target){
            ans.push([target]);
            continue;
        }
        var sub = combinationSum(candidates.slice(i), target-el);
        if(sub.length!=0){
            sub.forEach(i=>{
                ans.push([el,...i])
            })
        }
    }
    return ans;
};

javascript
var map;
var target;
var tag;
var diri = [-1,0,1,0], dirj=[0,1,0,-1];
var w,h;
/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
    tag = board.map(i=>i.map(i=>1));
    map = board;
    target = word;
    h = board.length;
    w = board[0].length;
    for(var i = 0;i<h;i++){
        for(var j=0;j<w;j++){
            if(tag[i][j]&&board[i][j]==word[0]){
                if(custom(i,j,0)){
                    return true;
                }
            }
        }
    }
    return false;
};
function custom (i,j,index){
    if((i<0||i>=h||j<0||j>=w)){
        return false;
    }
    if(map[i][j]!=target[index]||tag[i][j]==0){
        return false;
    }
    if(index==target.length-1){
        return true;
    }
    tag[i][j]=0;
    for(var k = 0;k<4;k++){
        if(custom(i+diri[k],j+dirj[k],index+1)){
            return true;
        }
    }
    tag[i][j]=1;
    return false;
}

javascript
/**
 * @param {string} s
 * @return {string[][]}
 */
var partition = function(s) {
    var l = s.length;
    if(l==1){
        return [[s]];
    }
    var ans = test(s)?[[s]]:[];
    for(var i =1;i<l;i++){
        var sub = s.slice(0,i);
        if(!test(sub)){
            continue;
        }
        var sol = partition(s.slice(i,l));
        sol.forEach(i=>{
            ans.push([sub, ...i]);
        })
    }
    return ans;
};
function test(s){
    var l = s.length;
    var lim = l>>1;
    for(var i=0;i<lim;i++){
        if(s[i]!=s[l-1-i]){
            return false;
        }
    }
    return true;
}

子串

和为 K 的子数组

解法1:暴力前缀和

javascript
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function (nums, k) {
    var pre = Array.from({ length: nums.length+1 }, () => 0);
    for (var i = 1; i <= nums.length; i++) {
        pre[i] = pre[i - 1] + nums[i-1];
    }
    var count = 0;
    for(var s = 0;s<nums.length;s++){
        var l_uppper = nums.length-s+1;
        for(var l = 1;l<l_uppper;l++){
            if(pre[s+l]-pre[s]==k){
                count++;
            }
        }
    }
    return count;
};

解法2:hash优化

解法1的瓶颈在于,对于某个指定的终点/起点,我们需要寻找减去起点后符合要求的起点/终点,从而导致又遍历了一次数组。由于k和起点在这一层循环定了下来,那么符合要求的pre[j]-k/pre[i]+k也是可确定的,只是不知道有多少符合条件,所以可以用hash表省去统计终点符合条件的起点/终点个数。

javascript
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function (nums, k) {
    var map = new Map();
    map.set(0, 1); // 目标为0时可以是空数组,所以至少存在1个
    var pre = 0;
    var count = 0;
    var l = nums.length;
    // 向前统计起点
    for (var i = 0; i < l; i++) {
        pre += nums[i];
        var t = pre - k;
        if (map.has(t)) {
            count += map.get(t); 
        }
        map.set(pre, map.has(pre) ? (map.get(pre) + 1) : 1);
    }
    return count;
};

图论

常规搜索。

javascript
/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function (grid) {
    var h = grid.length, w = grid[0].length;
    var count = 0;
    var sig = Array.from({ length: h }, () => Array.from({ length: w }, () => true))
    var dir = [[1, 0], [-1, 0], [0, 1], [0, -1]];
    var search = function (i, j) {
        if (i >= 0 && i < h && j >= 0 && j < w && sig[i][j]) {
            sig[i][j] = false;
            if (grid[i][j] == '1')
                for (var k = 0; k < 4; k++) {
                    search(i + dir[k][0], j + dir[k][1]);
                }
        }
    }
    for (var i = 0; i < h; i++) {
        for (var j = 0; j < w; j++) {
            if (sig[i][j]&&grid[i][j]=='1'){
                search(i,j);
                count++;
            }
        }
    }
    return count;
};

javascript
/**
 * @param {number[][]} grid
 * @return {number}
 */
var orangesRotting = function (grid) {
    var startMin = 0;
    var nextMin = 0;
    var queue = [];
    var h = grid.length, w = grid[0].length;
    var o_count = 0;
    for (var i = 0; i < h; i++) {
        for (var j = 0; j < w; j++) {
            if (grid[i][j] > 0) {
                o_count++;
                if (grid[i][j] == 2) {
                    queue.push([i, j]);
                }
            }
        }
    }
    var rot_count = queue.length;
    var last_rot_count = rot_count;
    var dir = [[0, 1], [0, -1], [1, 0], [-1, 0]];
    do {
        startMin = nextMin;
        last_rot_count = rot_count;
        var l = queue.length
        for (var i = 0; i < l; i++) {
            var pos = queue[i];
            for (var k = 0; k < 4; k++) {
                var _d = dir[k];
                var y = pos[0] + _d[0], x = pos[1] + _d[1];
                if (x >= 0 && x < w && y >= 0 && y < h && grid[y][x] == 1) {
                    grid[y][x] = 2;
                    rot_count++;
                    queue.push([y,x]);
                }
            }
        }
        nextMin++;
    } while (rot_count > last_rot_count);
    return o_count==rot_count?startMin:-1;
};