LeetCode 题解

在 Github 上修改

简单记录一下做过的 LeetCode,不定期更新

leetcode

3-无重复字符的最长子串

参考:二指针问题

#include <string.h>

int max(int a, int b) {
    return a > b ? a : b;
}

int lengthOfLongestSubstring(char *s) {
    bool vis[128] = {0};
    int len = strlen(s);
    int ans = 0;
    for (int i = 0, j = 0; i < len; i++) {
        while (j < len && !vis[s[j]]) {
            vis[s[j]] = true;
            j++;
        }
        ans = max(ans, j - i); 
        // abcdabc
        if (len - i <= ans) {
            break;
        }
        vis[s[i]] = false;
    }
    return ans;
}

5-最长回文子串

// 还有更优的算法 

func longestPalindrome(s string) string {
    if s == "" {
        return s
    }
    maxL, maxR := 0, 0
    for i := 0; i < len(s) - 1; i++ {
        if l, r := test(s, i, i); r - l > maxR - maxL {
            maxL, maxR = l, r
        }
        if l, r := test(s, i, i + 1); r - l > maxR - maxL {
            maxL, maxR = l, r
        }
    }
    return s[maxL: maxR + 1]
}

func test(s string, l, r int) (int, int) {
    for l >=0 && r < len(s) && s[l] == s[r] {
        l--
        r++
    }
    return l + 1, r - 1
}

25-K 个一组翻转链表

详见:反转链表

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

参考:二分查找求上下界

35-搜索插入位置

参考:二分查找求上下界

46-全排列

如下

47-全排列 II

这里不贴原题代码了,按照下面的代码稍微改改就可以了

/*
 * 全排列
 *
 * 方法1:每次递归选择一个数,将其标记并加入到答案数组中
 * 由于选择时是按顺序选择的,所以输出也是有序的,不过前提是输入也是有序的
 * 如:123 -> 123 -> 132 -> 213 -> 231 -> 312 -> 321
 *
 * 方法2:123 全排列 = (1) 1 + 23 全排列、(2) 2 + 13 全排列、(3) 3 + 12 全排列
 * 可以看出,对于 123 全排列,需要进行三次子递归处理(三个数字各出现在首位一次)
 * 为了方便,只需要将每个数字和当前首位数字交换一下,然后递归即可
 * 但是由于上面的交换,会导致输出不是从小到大排列的
 *
 * 方法3:直接调用 next_permutation 函数,输出有序,自带去重(需要保证输入有序)
 */

#include <iostream>
using namespace std;
#define N 110

bool vis[N];
char path[N];

void perm1(char s[], int x, int len) {
    if (x >= len) {
        for (int i = 0; i < x; i++) {
            cout << path[i];
        }
        cout << endl;
        return;
    }

    for (int i = 0; i < len; i++) {
        if (!vis[i]) {

            // 去重检查,需要保证输入有序
            // 如果当前元素和前一个元素相等,比如:12335,在遍历处理第二个 3 时重复了,跳过
            // 其中 !vis[i - 1] 是必需的,表示 s[i - 1] 未被遍历处理的情况下遍历到了 s[i],跳过处理,如果 s[i - 1] 已经被遍历到是不能跳过的,否则就没有输出了
            if (i > 0 && s[i] == s[i - 1] && !vis[i - 1]) continue;

            vis[i] = 1;
            path[x] = s[i];          // 选择一个数标记并加入数组
            perm1(s, x + 1, len);    // 递归
            vis[i] = 0;              // 还原
        }
    }
}

void perm2(char s[], int x, int len) {
    if (x >= len) {
        cout << s << endl;
        return;
    }

    for (int i = x; i < len; i++) {

        // 重复元素检测,由于当前需要将 s[x] 和 s[i] 交换然后递归,所以可以通过检测 s[i] 前面是否有和 s[i] 相同的元素
        // 比如:12353,第一个 3 和 1 交换得到 3 2135 递归,随后第二个 3 和 1 交换同样得到 3 2351,这两种排列是重复的
        bool dup = 0;
        for (int j = i - 1; j >= x; j--) {
            if (s[i] == s[j]) {
                dup = 1;
                break;
            }
        }
        if (dup) continue;

        swap(s[x], s[i]);      // 将后面的数交换到首尾
        perm2(s, x + 1, len);  // 递归处理
        swap(s[x], s[i]);      // 还原
    }
}

void perm3(char s[], int x, int len) {
    do {
        cout << s << endl;
    } while (next_permutation(s, s + len));
}

int main() {
    char s1[N] = "1234";
    char s2[N] = "1233";

    puts("(1) 无重复元素");

    puts("-----");
    perm1(s1, 0, 4);

    puts("-----");
    perm2(s1, 0, 4);

    puts("-----");
    perm3(s1, 0, 4);

    puts("(2) 有重复元素");

    puts("-----");
    perm1(s2, 0, 4);

    puts("-----");
    perm2(s2, 0, 4);

    puts("-----");
    perm3(s2, 0, 4);

    return 0;
}

50-Pow(x, n)

参考:乘法快速幂

double myPow(double x, long long n) {
    double ret = 1;
    long long nn = n < 0 ? -n : n;
    while (nn) {
        if (nn & 1) ret *= x;
        x *= x;
        nn >>= 1;
    }
    return n < 0 ? 1 / ret : ret;
}

53-最大子序和

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if (nums.size() <= 0) return 0;
        int ans = nums[0];
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
            ans = max(ans, sum);
            if (sum < 0) {
                sum = 0;
            }
        }
        return ans;
    }
};

62-不同路径

最简单的动态规划了,$dp[i][j]=dp[i-1][j] + dp[i][j-1]$

#include <string.h>
#define MAXN 111

int dp[MAXN][MAXN];

int uniquePaths(int n, int m) {
    memset(dp, 0, sizeof(dp));
    dp[1][1] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (i - 1 >= 1) {
                dp[i][j] += dp[i - 1][j];
            }
            if (j - 1 >= 1) {
                dp[i][j] += dp[i][j - 1];
            }
        }
    }
    return dp[n][m];
}

63-不同路径 II

func uniquePathsWithObstacles(input [][]int) int {
    n, m := len(input), len(input[0])
    cnt := make([][]int, n)
    for i := 0; i < n; i++ {
        cnt[i] = make([]int, m)
        for j := 0; j < m; j++ {
            if i == 0 && j == 0 && input[i][j] == 0{
                cnt[i][j] = 1
                continue
            }
            if i > 0 && input[i][j] == 0 {
                cnt[i][j] += cnt[i - 1][j]
            }
            if j > 0 && input[i][j] == 0 {
                cnt[i][j] += cnt[i][j - 1]
            }
        }
    }
    return cnt[n - 1][m - 1]
}

64-最小路径和

class Solution {
    public int minPathSum(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        int dp[][] = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                dp[i][j] = 0x3f3f3f3f;
                if (i == 0 && j == 0) {
                    dp[i][j] = grid[i][j];
                    continue;
                }
                if (i - 1 >= 0) {
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + grid[i][j]);
                }
                if (j - 1 >= 0) {
                    dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + grid[i][j]);
                }
            }
        }
        return dp[n - 1][m - 1];
    }
}

69-x 的平方根

参考:二分答案

#define int long long

int mySqrt(int x) {
    int ans = 0;
    int l = 0, r = 0x7fffffff;
    while (l < r) {
        int m = l + ((r - l) >> 1);
        if (m * m <= x) l = m + 1, ans = m;
        else r = m;
    }
    return ans;
}

70-爬楼梯

class Solution {
public:
    int climbStairs(int n) {
        if (n == 1) return 1;
        if (n == 2) return 2;
        int a = 1, b = 2;
        for (int i = 3; i <= n; i++) {
            int c = a + b;
            a = b;
            b = c;
        }
        return b;
    }
};

72-编辑距离

动态规划 TODO

#include <string.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 1010
int dp[MAXN][MAXN];

int minDistance(char* s1, char* s2){
    int l1 = strlen(s1);
    int l2 = strlen(s2);

    // 0~i
    // 0~j
    for (int i = 0; i <= l1; i++)
        dp[i][0] = i;
    for (int i = 0; i <= l2; i++)
        dp[0][i] = i;
    for (int i = 1; i <= l1; i++) {
        for (int j = 1; j <= l2; j++) {
            if (s1[i - 1] == s2[j - 1]) {
                dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1]) + 1);
            } else {
                dp[i][j] = min(dp[i - 1][j - 1] /* 替换 */, min(dp[i - 1][j], dp[i][j - 1]) /* 插入  | 删除 */) + 1;
            }
        }
    }

    return dp[l1][l2];
}

74-搜索二维矩阵

参考:二分查找

92-反转链表 II

详见:反转链表

98-验证二叉搜索树

中序遍历一下就行了,判断是否递增

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    long last;

    // = = 故意的吧
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        last = Long.MIN_VALUE;
        return dfs(root);
    }

    private boolean dfs(TreeNode node) {
        if (node == null) {
            return true;
        }
        boolean left = dfs(node.left);
        if (!left) {
            return false;
        }
        if (node.val > last) {
            last = node.val;
        } else {
            return false;
        }
        return dfs(node.right);
    }
}

110-平衡二叉树

public boolean isBalanced(TreeNode root) {
  return checkBalanced(root) >= 0;
}

// 由于 Java 只能有一个返回值,无法同时返回高度和是否平衡
// 所以定义返回值为子树的高度,当为 -1 时表示不平衡
//
// 注意不能只看根节点的左右子树高度,必须判断每个子树
// 比如:
//        1
//       /  \
//      2    3
//     /      \
//    4        5
//   /          \
//  6            7
public int checkBalanced(TreeNode node) {
  if (node == null) return 0;
  int left = checkBalanced(node.left);
  if (left < 0) {
    return -1;
  }
  int right = checkBalanced(node.right);
  if (right < 0) {
    return -1;
  }
  return Math.abs(left - right) <= 1 ? Math.max(left, right) + 1 : -1;
}

124-二叉树中的最大路径和

递归,如果路径和小于0则丢弃。最大路径要么:

  • 跨子树根节点:左子树 + 右子树 + 当前节点权值
  • 不跨子树根节点:左子树 或 右子树的最大值 + 当前节点权值
#define INF 0x3f3f3f3f

int ans;

int max(int a, int b) {
    return a > b ? a : b;
}

int dfs(struct TreeNode* root) {
    int left = 0, right = 0;
    if (root->left != NULL) {
        left = max(dfs(root->left), 0);
    }
    if (root->right != NULL) {
        right = max(dfs(root->right), 0);
    }
    ans = max(ans, left + right + root->val); // 跨子树根节点
    return max(left, right) + root->val; // 取左右子树最大值向上回溯
}

//      5
//    4  8
//  11  13 4
// 7 2      1
int maxPathSum(struct TreeNode* root) {
    ans = -INF;
    dfs(root);
    return ans;
}

129-求根到叶子节点数字之和

简单递归一下就行

class Solution {
    int ans;
    public int sumNumbers(TreeNode root) {
        ans = 0;
        if (root == null) return 0;
        dfs(root, 0);
        return ans;
    }

    void dfs(TreeNode node, int n) {
        if (node.left == null && node.right == null) { // 叶子节点
            ans += n * 10 + node.val;
            return;
        }
        if (node.left != null)
            dfs(node.left, n * 10 + node.val);
        if (node.right != null)
            dfs(node.right, n * 10 + node.val);
    }
}

133-克隆图

递归遍历一下就行了,用一个 vis 标记是否节点是否已创建

func cloneGraph(node *Node) *Node {
    vis := make(map[int]*Node)
    return clone(node, vis)
}

// 返回克隆后的节点
func clone(node *Node, vis map[int]*Node) *Node {
    if node == nil {
        return nil
    }
    if existNode, exist := vis[node.Val]; exist { // 节点已经创建了
        return existNode
    }
    newNode := &Node{ Val: node.Val }
    vis[node.Val] = newNode
    for _, nextNode := range node.Neighbors {
        newNextNode := clone(nextNode, vis)
        newNode.Neighbors = append(newNode.Neighbors, newNextNode)
    }
    return newNode
}

141-环形链表

同下

142-环形链表 II

/*
 * 单链表判环,并找出起点
 * 时间复杂度 O(n),空间复杂度 O(1)
 *
 * 分析:
 *
 * head -> node -> cycle_start -> node -> node -> node
 *                      ^                          |
 *                      |--------------------------v
 *
 * 如图所示,使用快慢指针遍历链表,假设头节点 head 到 环起始节点 cycle_start 的长度为 m1,从环起始节点 cycle_start 到快慢指针相遇点的长度为 m2,环的长度为 cycle_length
 * 相遇时,slow 指针所走长度为 m1 + m2,fast 指针比 slow 节点多走了一圈,其所走长度为 2 * (m1 + m2) = m1 + m2 + cycle_length,推导出 cycle_length = m1 + m2,即 slow 指针所走的长度
 * 另外,由于 slow 指针到 cycle_start 节点的距离为 cycle_length - m2 = m1,head 指针到 cycle_start 节点的距离也为 m1,所以只需要从 相遇点 和 head 节点同时遍历直到相遇就能找到环起始节点了
 * 为什么快指针和慢指针一定会相遇 且 相遇时前者比后者恰好多走一圈:https://www.zhihu.com/question/23208893/answer/117115415
 */
class ListNode {
    int val;
    ListNode next;

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString() {
        return "ListNode [val=" + val + "]";
    }
}

public class LinkedListCycle {
    public static ListNode checkCycle(ListNode head) {
        if (head == null) return null;

        ListNode slow = head, fast = head;
        do {
            if (fast.next == null || fast.next.next == null) {
                return null; // 不存在环
            }
            slow = slow.next;
            fast = fast.next.next;
        } while (slow != fast);

        ListNode start = head;
        while (start != slow) {
            start = start.next;
            slow = slow.next;
        }
        return start; // 环起点
    }

    // test
    public static void main(String[] args) {
        ListNode c7 = new ListNode(7, null);
        ListNode c6 = new ListNode(6, c7);
        ListNode c5 = new ListNode(5, c6);
        ListNode c4 = new ListNode(4, c5);
        ListNode c3 = new ListNode(3, c4);
        ListNode c2 = new ListNode(2, c3);
        ListNode c1 = new ListNode(1, c2);

        System.out.println(checkCycle(c1));

        c7.next = c3;

        System.out.println(checkCycle(c1));
    }
}

155-最小栈

/*
 * 在普通的栈操作上添加了一个求最小值的操作
 *
 * 举个例子:
 * push 4  [4]           min=4
 * push 2  [4, 2]        min=2
 * push 3  [4, 2, 3]     min=2
 * push 1  [4, 2, 3, 1]  min=1
 * pop     [4, 2, 3]     min=2
 * pop     [4, 2]        min=2
 * pop     [4]           min=4
 *
 * 很容易想到,如果要在 O(1) 时间内求栈中的最小元素,需要用额外的空间来换取时间
 * 可以看出,如果能在栈操作过程中记录下最小值,当然只用单个变量记录当前的最小值肯定不行,因为还有出栈操作,出栈后最小值可能往回变大
 * 所以可以,记录下每一个时刻栈中的最小值
 * 即再用一个栈
 * - 每次 push 后同步插入一个元素 (min(top(), x)),表示当前栈中的最小值
 * - 每次  pop 后同步移除栈顶元素
 */
class MinStack {
    private Stack<Integer> items = new Stack<>();
    private Stack<Integer> minItems = new Stack<>();

    public MinStack() {}
    
    public void push(int x) {
        int min = minItems.empty() ? x : Math.min(minItems.peek(), x);
        items.push(x);
        minItems.push(min);
    }
    
    public void pop() {
        items.pop();
        minItems.pop();
    }
    
    public int top() {
        return items.peek();
    }
    
    public int getMin() {
        return minItems.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();
 */

160-相交链表

/**
 * 判断链表是否相交,并求出交点
 * 时间复杂度 O(n),空间复杂度 O(1)
 */
class ListNode {
    int val;
    ListNode next;

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString() {
        return "ListNode[ " + "val: " + val + " ]";
    }
}

public class IntersectionLinkedList {
    public static ListNode checkAcross(ListNode head1, ListNode head2) {
        if (head1 == null || head2 == null) return null;

        int length1 = 1, length2 = 1;
        ListNode p = head1, q = head2;
        while (p.next != null) {
            p = p.next;
            length1++;
        }

        while (q.next != null) {
            q = q.next;
            length2++;
        }

        if (p != q) return null;

        int lengthc = Math.abs(length1 - length2);
        p = head1;
        q = head2;
        if (length1 > length2) {
            for (int i = 0; i < lengthc; i++) {
                p = p.next;
            }
        } else {
            for (int i = 0; i < lengthc; i++) {
                q = q.next;
            }
        }

        while (p != q) {
            p = p.next;
            q = q.next;
        }

        return p;
    }

    // test
    public static void main(String[] args) {
        ListNode p6 = new ListNode(6, null);
        ListNode p5 = new ListNode(5, p6);
        ListNode p4 = new ListNode(4, p5);
        ListNode p3 = new ListNode(3, p4);
        ListNode p2 = new ListNode(2, p3);
        ListNode p1 = new ListNode(1, p2);

        ListNode q2 = new ListNode(8, p5);
        ListNode q1 = new ListNode(9, q2);

        System.out.println(checkAcross(p1, q1));
    }
}

162-寻找峰值

由于时间复杂度要求 logN,所以二分一下就行了

int findPeakElement(int* nums, int n) {
    int l = 0, r = n;
    while (l < r) {
        int m = (l + r) >> 1;

        // 满足条件:1 3 2
        if ((m - 1 < 0 || nums[m] > nums[m - 1]) && (m + 1 >= n || nums[m] > nums[m + 1])) {
            return m;
        }
        // 倒序:3 2 1,左边一定有峰值
        if ((m - 1 < 0 || nums[m - 1] > nums[m]) && (m + 1 >= n || nums[m] > nums[m + 1])) {
            r = m;
        } else { // 非正序,右边一定有峰值:3 1 3 / 1 2 3
            l = m + 1;
        }
    }
    // 1 2 3 4 3
    //     m
    //       l   r=5
    //         m
    return -1; // un
}

179-最大数

排序处理一下就行了,排序按 ab 或 ba 取最大值排序

object Solution {
    def largestNumber(nums: Array[Int]): String = {
        val res = nums.map(_.toString).sortWith((a, b) => (a + b) > (b + a)).mkString
        if (res.forall(_ == '0')) "0" else res
    }
}

191-位1的个数

// 最简单的做法当然是不断除以 2,然后判断末尾是否是 1 即可,复杂度:O(logN),其实已经足够快了
// 不过还有优化的空间,如下所示,二进制中存在大量 0,参考 “231-2的幂”,通过 n & (n - 1) 不断“去掉”最后一个 1,从而跳过一些多余的判断,达到优化的目的
//   10010000001000010
// ^ 10010000001000001
// -------------------
// = 10010000001000000
// ^ 10000000000111111
// -------------------
// = 10010000000000000
//   10000000000000000
//   00000000000000000
public class Solution {
    public int hammingWeight(int n) {
        int ans = 0;
        while (n != 0) {
            ans ++;
            n &= (n - 1);
        }
        return ans;
    }
}

198-打家劫舍

#define N 111
int a[N][2]; // 0 yes 1 no

#define max(a, b) ((a) > (b) ? (a) : (b))

int rob(int* nums, int n) {
    if (n <= 0) return 0;
    
    a[0][0] = nums[0];
    for (int i = 1; i < n; i++) {
        a[i][0] = a[i - 1][1] + nums[i];
        a[i][1] = max(a[i - 1][0], a[i - 1][1]);
    }
    return max(a[n - 1][0], a[n - 1][1]);
}

200-岛屿数量

简单搜索

int ans;
int n, m;
char **grid;
int dir[4][2] = {-1, 0, 1, 0, 0, 1, 0, -1};

void dfs(int x, int y) {
    grid[x][y] = '0';
    for (int i = 0; i < 4; i++) {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] != '1') {
            continue;
        }
        dfs(tx, ty);
    }
}

int numIslands(char** input, int gridSize, int* gridColSize) {
    if (gridSize == 0) {
        return 0;
    }

    n = gridSize;
    m =  gridColSize[0];
    ans = 0;
    grid = input;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == '1') {
                ans++;
                dfs(i, j);
            }
        }
    }
    return ans;
}

204-计数质数

参考:素数筛

#include <stdio.h>
#include <string.h>
#define LL long long
#define MAXN 1500000

bool is_prime[MAXN + 1];
int prime[MAXN];
int tot;

int solve(int n) {
    tot = 0;
    for (int i = 2; i <= n; i++) is_prime[i] = true;
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            prime[tot++] = i;
        }
        for (int j = 0; j < tot; j++) {
            if ((LL) i * prime[j] > n) break;
            is_prime[i * prime[j]] = false;
            if (i % prime[j] == 0) { 
                break;
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (is_prime[i]) ans++;
    }
    return ans;
}

int countPrimes(int n) {
    return solve(n - 1);
}

206-反转链表

/**
 * 逆置单链表
 * 时间复杂度 O(n),空间复杂度 O(1)
 */
class ListNode {
    int val;
    ListNode next;

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public class ReverseLinkedList {

    /*
     * LeetCode 206
     *
     * head 始终指向头结点,p 指向当前需要逆置到头结点的节点,q 是一个辅助指针,用于遍历过程中临时指向 p 的下一个节点
     * 从第二个节点开始,依次将节点插入到头结点处
     *
     * h
     * 1 2 3 4 5
     *   p
     *
     * h
     * 3 2 1 4 5
     *       p q
     */
    public static ListNode reverse(ListNode head) {
        if (head == null) return null; // 0
        if (head.next == null) return head; // 1

        ListNode p = head.next;
        ListNode q = null;
        head.next = null; // 尾指针的下一个节点置为空
        while (p != null) {
            q = p.next;
            p.next = head; // 插入到头结点
            head = p;
            p = q;
        }

        return head;
    }

    /*
     * LeetCode 92
     * 
     * 翻转 [l, r] 区间,下标从 1 开始
     *
     * 题目分为两种情况,一种是从第一个节点开始翻转,一种是中间节点开始翻转
     * 为了简单起见,可以新建一个虚拟的头指针
     *
     * (1) 先找到翻转起点的前一个节点
     * (2) 从翻转起点的后一个节点开始依次翻转,将其插入到起点前一个节点的后面
     *
     *  revPre  revHead    p     q
     *    1         2      3     4     5
     *
     *    1         3      2     4     5
     *
     *                           p     q
     *
     *    1         4      3     2     5
     *
     *                        revHeadCopy
     *                                 p     q
     */
    public ListNode reverseBetween(ListNode head, int l, int r) {
        ListNode virtualHead = new ListNode(0);
        virtualHead.next = head;

        ListNode revPre = virtualHead;
        for (int i = 1; i < l; i++) revPre = revPre.next;

        ListNode revHead = revPre.next; // 翻转起点
        ListNode revHeadCopy = revHead;
        ListNode p = revHead.next; // 从翻转起点的下一个节点开始翻转
        for (int i = l + 1; i <= r && p != null; i++) { // 依次翻转
            ListNode q = p.next;
            p.next = revHead; // 插入到头结点
            revHead = p;
            p = q;
        }
        revPre.next = revHead; // 原翻转起点前一个节点的 next 指针修改
        revHeadCopy.next = p;  // 原翻转起点的 next 指针修改
        return virtualHead.next;
    }

    /*
     * 每 K 个一组进行反转
     *
     * 1 2 3  4 5
     * 3 2 1  5 4
     *
     * 分段处理一下就行了,同样地,为了方便,添加一下虚拟头结点
     */
    public ListNode reverseKGroup1(ListNode head, int k) {
        if (k == 1) return head;

        ListNode virtualHead = new ListNode(0);
        virtualHead.next = head;

        ListNode c = virtualHead;
        while (c.next != null) {
            ListNode groupPre = c;               // 每组翻转头结点的前一个节点
            ListNode groupHead = groupPre.next;  // 每组翻转的头结点
            ListNode groupHeadCopy = groupHead;
            ListNode p = groupHead.next;         // 从头结点的后一个节点开始翻转
            for (int i = 2; i <= k && p != null; i++) {
                ListNode q = p.next;
                p.next = groupHead;  // 插入到头结点
                groupHead = p;
                p = q;
            }
            groupPre.next = groupHead; // 原翻转起点前一个节点的 next 指针修改
            groupHeadCopy.next = p;    // 原翻转起点的 next 指针修改
            c = groupHeadCopy;         // 修改 c 为下一组翻转区间头节点的前一个节点
        }

        return virtualHead.next;
    }

    /*
     * LeetCode 25
     *
     * 每 K 个一组进行反转,如果不足 k 个保持不变,可以通过长度判断
     *
     * 1 2 3  4 5
     * 3 2 1  4 5
     *
     * 分段处理一下就行了,同样地,为了方便,添加一下虚拟头结点
     */
    public ListNode reverseKGroup2(ListNode head, int k) {
        if (k == 1) return head;

        int length = 0;
        for (ListNode p = head; p != null; p = p.next) length++;

        ListNode virtualHead = new ListNode(0);
        virtualHead.next = head;

        ListNode c = virtualHead;
        while (c.next != null && length >= k) {
            ListNode groupPre = c;               // 每组翻转头结点的前一个节点
            ListNode groupHead = groupPre.next;  // 每组翻转的头结点
            ListNode groupHeadCopy = groupHead;
            ListNode p = groupHead.next;         // 从头结点的后一个节点开始翻转
            for (int i = 2; i <= k && p != null; i++) {
                ListNode q = p.next;
                p.next = groupHead;  // 插入到头结点
                groupHead = p;
                p = q;
            }
            groupPre.next = groupHead; // 原翻转起点前一个节点的 next 指针修改
            groupHeadCopy.next = p;    // 原翻转起点的 next 指针修改
            c = groupHeadCopy;         // 修改 c 为下一组翻转区间头节点的前一个节点
            length -= k;
        }

        return virtualHead.next;
    }

    // test
    public static void main(String[] args) {
        ListNode n5 = new ListNode(5, null);
        ListNode n4 = new ListNode(4, n5);
        ListNode n3 = new ListNode(3, n4);
        ListNode n2 = new ListNode(2, n3);
        ListNode n1 = new ListNode(1, n2);

        ListNode p = new ReverseLinkedList().reverseKGroup2(n1, 3);
        while (p != null) {
            System.out.print(p.val + " ");
            p = p.next;
        }
    }
}

207-课程表

拓扑排序有向图判环,参考:

class Solution {
    private int n;
    private List<Integer>[] edges;
    private int[] de;

    public boolean canFinish(int nn, int[][] input) {
        n = nn;
        edges = new ArrayList[n];
        de = new int[n];

        for (int i = 0; i < n; i++) {
            edges[i] = new ArrayList<>();
        }
        for (int i = 0; i < input.length; i++) {
            int u = input[i][0];
            int v = input[i][1];
            // 0,1 => 0>1
            edges[u].add(v);
            de[v]++;
        }
        return check();
    }

    private boolean check() {
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (de[i] == 0) {
                q.add(i);
            }
        }
        int cnt = 0;
        while (!q.isEmpty()) {
            int u = q.poll();
            cnt++;
            for (int i = 0; i < edges[u].size(); i++) {
                int v = edges[u].get(i);
                de[v]--;
                if (de[v] == 0) {
                    q.add(v);
                }
            }
        }
        return cnt == n;
    }
}

208-实现 Trie (前缀树)

字典树模板题,参考:

class Trie {
    static int N = 26;
    static class Node {
        Node[] next = new Node[N];
        int vis;
    }

    private Node root;

    public Trie() {
        root = new Node();
    }
    
    public void insert(String word) {
        Node node = root;
        Node next;
        for (int i = 0; i < word.length(); i++) {
            int pos = getPos(word.charAt(i));
            next = node.next[pos];
            if (next == null) {
                next = new Node();
                node.next[pos] = next;
            }
            node = next;
        }
        node.vis++;
    }
    
    public boolean search(String word) {
        return searchInternal(word, false);
    }
    
    public boolean startsWith(String prefix) {
        return searchInternal(prefix, true);
    }

    private boolean searchInternal(String word, boolean prefix) {
        Node node = root;
        Node next;
        for (int i = 0; i < word.length(); i++) {
            int pos = getPos(word.charAt(i));
            next = node.next[pos];
            if (next == null) {
                return false;
            }
            node = next;
        }
        if (prefix) {
            return true;
        }
        return node.vis > 0;
    }

    private int getPos(char c) {
        return c - 'a';
    }
}

209-长度最小的子数组

二指针简单题

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int ans = 0;
        int sum = 0;
        for (int i = 0, j = 0; i < nums.length; i++) {
            while (j < nums.length && sum < s) {
                sum += nums[j++];
            }
            if (sum >= s) {
                if (ans == 0) {
                    ans = j - i;
                } else {
                    ans = Math.min(ans, j - i);
                }
            }
            sum -= nums[i];
        }
        return ans;
    }
}

222-完全二叉树的节点个数

TODO

228-汇总区间

简单,记录一下有序区间左起点就行

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> ans = new ArrayList<>();
        int l = -1;
        for (int i = 0; i < nums.length; i++) {
            if (i + 1 >= nums.length || nums[i] + 1 != nums[i + 1]) {
                if (l == -1) {
                    ans.add(String.valueOf(nums[i]));
                } else {
                    ans.add(String.format("%s->%s", l, nums[i]));
                    l = -1;
                }
            } else {
                if (l == -1) {
                    l = nums[i];
                }
            }
        }
        return ans;
    }
}

231-2的幂

// 2 的幂对应的二进制中一定只有一个 1
// 所以只需要 n & (n - 1),它的作用是去掉二进制中的最后一个 1,如果结果大于 0,说明不只一个 1,不是 2 的幂
//   00010000
// ^ 00011111
// ----------
// = 00000000
class Solution {
    public boolean isPowerOfTwo(int n) {
        return n > 0 && (n & (n - 1)) == 0;
    }
}

232-用栈实现队列

/**
 * 两个栈实现队列
 * 为了保持先进先出的特性,使用两个栈
 *
 * - 判空:如果栈 1 和栈 2 都为空则为空
 * - 入队:直接进入栈 1 中
 * - 出队:先判断栈 2 中是否存在元素
 * --- 如果存在:直接出栈
 * --- 如果不存在:将栈 1 中的元素倒入栈 2 中,再出栈
 */
public class DoubleStack2Queue<T> {
    private Stack<T> s1 = new Stack<>();
    private Stack<T> s2 = new Stack<>();

    public DoubleStack2Queue() { }

    // 判空
    private boolean empty() {
        return s1.empty() && s2.empty();
    }

    // 入队
    public void push(T x) {
        s1.push(x);
    }

    // 出队
    public T pop() {
        if (s2.empty()) {
            while (!s1.empty()) {
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }

    // test
    public static void main(String[] args) {
        DoubleStack2Queue<Integer> q = new DoubleStack2Queue<>();
        q.push(1);
        q.push(2);
        System.out.println(q.pop());
        System.out.println(q.pop());
        q.push(2);
        q.push(3);
        q.push(4);
        q.push(5);
        while (!q.empty()) {
            System.out.println(q.pop());
        }
    }

}

233-数字 1 的个数

数位 DP 模板题,参考:数位DP

#define N 100
int bit[N]; // 13 => 3 1
int dp[N][N];

int dfs(int pos, int num, bool limit, bool fzero) {
    if (pos == -1) {
        return num;
    }
    if (!limit  && !fzero && dp[pos][num] != -1) {
        return dp[pos][num];
    }
    int ans = 0;
    int end = limit ? bit[pos] : 9;
    for (int i = 0; i <= end; i++) {
        int num1 = num + (i == 1);
        ans += dfs(pos - 1, num1, limit && i == end, !i && fzero);
    }
    if (!limit && !fzero) dp[pos][num] = ans;
    return ans;
}

int countDigitOne(int n) {
    int l = 0;
    while (n) {
        bit[l++] = n % 10;
        n /= 10;
    }
    memset(dp, -1, sizeof(dp));
    return dfs(l - 1, 0, true, true);
}

236-二叉树的最近公共祖先

TODO

239-滑动窗口最大值

单调队列模板题,详情:单调队列-栈

func maxSlidingWindow(nums []int, k int) []int {
    type Item struct {
        id  int
        val int
    }
    res := make([]int, 0, len(nums));
    queue := make([]Item, len(nums))
    in, out := 0, 0
    front, rear := 0, 0
    for _, num := range nums {
        for rear > front && num >= queue[rear - 1].val { // 递减
            rear--
        }
        queue[rear] = Item{in, num} // 进队
        rear++
        in++
        if in - out == k { // k个元素,出队
            res = append(res, queue[front].val)
            if out == queue[front].id {
                front++
            }
            out++
        }
    }
    return res;
}

279-完全平方数

简单 BFS 搜索,应该有更优解法

class Solution {
    public int numSquares(int n) {
        int maxNum = (int)Math.sqrt(n); // 25 1 4 9 16 25
        int[] square = new int[maxNum];
        for (int i = 0; i < maxNum; i++) {
            square[i] = (i + 1) * (i + 1);
        }
        // 1 4 9 16

        // ans[i] = min(ans[i], ans[i - squre[i]] + 1) 
        Queue<Node> q = new LinkedList<>();
        q.add(new Node(0, 0));
        while (!q.isEmpty()) {
            Node u = q.poll();
            if (u.val == n) {
                 return u.cnt;
            }
            for (int i = 0; i < maxNum; i++) {
                int nextVal = u.val + square[i];
                if (nextVal <= n) {
                    if (nextVal == n) {
                        return u.cnt + 1;
                    }
                    q.add(new Node(nextVal, u.cnt + 1));
                }
            }
        }
        return 0; // xx
    }

    static class Node {
        int val;
        int cnt;

        Node() {}
        Node(int val, int cnt) {
            this.val = val;
            this.cnt = cnt;
        }
    }
}

303-区域和检索 - 数组不可变

参考:一维前缀和

392-判断子序列

bool isSubsequence(char *s, char *t) {
    int s_len = strlen(s);
    int t_len = strlen(t);
    int i = 0;
    for (int j = 0; i < s_len && j < t_len; j++) {
        if (t[j] == s[i]) i++;
    }
    return i >= s_len;
}

404-左叶子之和

简单题目,注意由于需要判断是否是左叶子

  • 默认情况下不能遍历到叶子节点(叶子节点不知道自己是否是左叶子),所以需要在叶子节点的父节点进行判断
  • 或者向下带上一个参数,表示是否是左儿子节点
class Solution(object):
    def sumOfLeftLeaves(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        return self.cal(root)
        return self.cal2(root, False)
        
    def cal(self, node):
        if node is None:
            return 0

        res = 0
        if node.left is not None and node.left.left is None and node.left.right is None:  # 左叶子
            res = node.left.val
            
        return self.cal(node.left) + self.cal(node.right) + res

    def cal2(self, node, is_left):
        if node is None:
            return 0

        if is_left and node.left is None and node.right is None:
            return node.val

        return self.cal2(node.left, True) + self.cal2(node.right, False)

429-N叉树的层序遍历

使用队列遍历一下就行了

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> result = new ArrayList();
        if (root == null) {
            return result;
        }
        Queue<Node> q = new LinkedList<>();
        q.add(root);
        int nowLevelNum = 1;
        int nextLevelNum = 0;
        List<Integer> items = new ArrayList<>();
        while (!q.isEmpty()) {
            Node now = q.poll();
            items.add(now.val); // 出队时添加到列表中
            for (Node next : now.children) {
                nextLevelNum++;
                q.add(next);
            }
            nowLevelNum--;
            if (nowLevelNum == 0) { // 这一层遍历完成,新建一个列表
                result.add(items);
                items = new ArrayList<>();
                nowLevelNum = nextLevelNum;
                nextLevelNum = 0;
            }
        }
        return result;
    }
}

421-数组中两个数的最大异或值

先把所有数字插入到 01 字典树中,然后遍历每个数,从树中找出与其异或最大的值即可

#define MAXN 500010

int sz;
int ch[MAXN][2];
int val[MAXN];

int get_id(int n, int m) {
    return (int)(n & ((long long)1 << m)) > 0;
}

void build(int *nums, int n) {
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
    val[0] = 0;
    for (int i = 0; i < n; i++) {
        int u = 0;
        for (int j = 31; j >= 0; j--) {
            int id = get_id(nums[i], j);
            int v = ch[u][id];
            if (!v) {
                val[sz] = 0;
                memset(ch[sz], 0, sizeof(ch[sz]));
                ch[u][id] = sz++;
                v = ch[u][id];
            }
            u = v;
        }
        val[u] = nums[i];
    }
}

int find(int *nums, int n) {
    int mx = 0;
    for (int i = 0; i < n; i++) {
        int u = 0;
        for (int j = 31; j >= 0; j--) {
            int id = get_id(nums[i], j);
            if (ch[u][id ^ 1] != 0) {
                u = ch[u][id ^ 1];
            } else {
                u = ch[u][id];
            }
        }
        if ((val[u] ^ nums[i]) > mx) {
            mx = (val[u] ^ nums[i]);
        }
    }
    return mx;
}


int findMaximumXOR(int* nums, int n) {
    build(nums, n);
    return find(nums, n);
}

435-无重叠区间

基础的贪心题目,将区间按右端点排序,依次贪心,得到最多不重叠的区间数,最后用总数减去即可

class Solution {
    public int eraseOverlapIntervals(int[][] input) {
        // 1 2       1
        // 1   3
        //   2 3     2
        //     3 4   3

        if (input == null || input.length == 0) {
            return 0;
        }

        int n = input.length;
         Interval[] intervals = new Interval[n];
         for (int i = 0; i < n; i++) {
             intervals[i] = new Interval(input[i][0], input[i][1]);
         }
         Arrays.sort(intervals);
         int cnt = 1;
         Interval last = intervals[0];
         for (int i = 1; i < n; i++) {
             if (intervals[i].start >= last.end) {
                 last = intervals[i];
                 cnt++;
             }
         }
         return n - cnt; // 2
    }

    class Interval implements Comparable<Interval> {
        int start;
        int end;
        
        public Interval(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public int compareTo(Interval to) {
            return this.end - to.end;
        }
    }
}

448-找到所有数组中消失的数字

依次将每个元素标记为出现,然后从 1 遍历到 n 就能找出消失的数字

但是由于不能使用额外空间,所以比较 trick,比如出现了 4,即在原数组下标为 4 的元素置为相反数,利用原数组进行标记,如果是负数,即表示出现过

func findDisappearedNumbers(nums []int) []int {
    for _, n := range nums {
        idx := abs(n) - 1
        if (nums[idx] > 0) {
            nums[idx] = -nums[idx]
        }
    }
    ret := make([]int, 0)
    for i := range nums {
        if nums[i] > 0 {
            ret = append(ret, i + 1)
        }
    }
    return ret
}

func abs(n int) int {
    if n < 0 {
        n = -n
    }
    return n
}

450-删除二叉搜索树中的节点

当被移除节点既有左儿子又有右儿子时,将其替换为左子树中的最大节点或者右子树中的最小节点即可

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
struct TreeNode* leftMax(struct TreeNode* node) {
    while (node->right != NULL) node = node->right;
    return node;
}

// 删除当前子树中的 key,并返回子树根节点
struct TreeNode* deleteNode(struct TreeNode* node, int key) {
    if (node == NULL) {
        return node;
    }

    if (key < node->val) node->left = deleteNode(node->left, key); // 左边
    else if (key > node->val) node->right = deleteNode(node->right, key); // 右边
    else { // 等于
        if (node->left == NULL) {
            return node->right;
        }
        if (node->right == NULL) {
            return node->left;
        }
        // 如果左右子树都不为空,则将当前节点替换为左子树中的最大节点,随后递归将左子树中的最大节点删除
        node->val = leftMax(node->left)->val;
        node->left = deleteNode(node->left, node->val);
    }
    return node;
}

494-目标和

由于数组和不大,所以可以使用一个二维数组表示前 i 个数字和为 j 的组合数。由于 j 可能有负数,所以 j 整体平移 2 * N

#define N 1000
int a[20][N * 4 + 1];

int pos(int j) {
    return j + 2 * N;
}

int findTargetSumWays(int* nums, int n, int s) {
    memset(a, 0, sizeof(a));
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += nums[i];
    }
    if (s > sum) {
        return 0;
    }

    a[0][pos(nums[0])] = 1;
    a[0][pos(-1 * nums[0])] += 1;
    for (int i = 1; i < n; i++) {
        for (int j = -N; j <= N; j++) {
            int pj = pos(j);
            a[i][pj] = a[i - 1][pj - nums[i]] /* + */ + a[i - 1][pj + nums[i]]; /* - */
        }
    }    
    return a[n - 1][pos(s)];
}

547-朋友圈

简单搜索一下就行,判断是否所有节点都可达,也可以使用并查集

class Solution {
    public int findCircleNum(int[][] input) {
        int n = input.length;
        boolean vis[] = new boolean[n];
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                dfs(i, n, input, vis);
                ans++;
            }
        }
        return ans;
    }

    private void dfs(int s, int n, int[][] input, boolean vis[]) {
        vis[s] = true;
        for (int t = 0; t < n; t++) {
            if (input[s][t] > 0 && !vis[t]) {
                dfs(t, n, input, vis);
            }
        }
    }
}

551-学生出勤记录 I

class Solution(object):
    def checkRecord(self, s):
        """
        :type s: str
        :rtype: bool
        """
        return s.count('A') <= 1 and 'LLL' not in s

557-反转字符串中的单词 III

class Solution(object):
    def reverseWords(self, s):
        """
        :type s: str
        :rtype: str
        """
        fir = True
        ss = ""
        for part in s.split(" "):
            if not fir:
                ss += " "
            ss += part[::-1]
            fir = False
        return ss

559-N叉树的最大深度

func maxDepth(root *Node) int {
    if root == nil {
        return 0
    }
    max := 0
    dfs(root, 1, &max);
    return max;
}

func dfs(rt *Node, depth int, maxDepth *int) {
    if depth > *maxDepth {
        *maxDepth = depth;
    }
    for _, child := range rt.Children {
        dfs(child, depth + 1, maxDepth);
    }
}

563-二叉树的坡度

func findTilt(root *TreeNode) int {
    sum := 0
    dfs(root, &sum)
    return sum
}

func dfs(rt *TreeNode, sum *int) int {
    if (rt == nil) {
        return 0
    }
    left := dfs(rt.Left, sum)
    right := dfs(rt.Right, sum)
    if left > right {
        *sum += left - right
    } else {
        *sum += right - left
    }
    return left + right + rt.Val
}

572-另一个树的子树

树不大的话简单递归一下就行了,

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if (s == null) return t == null;
        return same(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
    }

    private boolean same(TreeNode s, TreeNode t) {
        if (s == null && t == null) return true; 
        if (s != null && t != null && s.val == t.val) {
            return same(s.left, t.left) && same(s.right, t.right);
        }
        return false;
    }
}

605-种花问题

简单贪心问题,如果当前能种则种即可

class Solution(object):
    def canPlaceFlowers(self, flowerbed, n):
        """
        :type flowerbed: List[int]
        :type n: int
        :rtype: bool
        """
        last = -2
        count = 0
        flowerbed.append(0)
        for i, c in enumerate(flowerbed):
            if c == 1:
                last = i
            if c == 0 and i - last > 1 and i + 1 < len(flowerbed) and flowerbed[i + 1] != 1:
                count += 1
                last = i
        return count >= n

617-合并二叉树

class Solution {
    // 返回值为合并后的子树根节点
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if (t1 == null) {
            return t2;
        }
        if (t2 == null) {
            return t1;
        }
        t1.val += t2.val;

        // 合并左右子树
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);
        return t1;
    }
}

647-回文子串

以每个节点为中心,向两边扩展即可,需要分为奇数位和偶数位两种情况

  • 这里的解法有优化的空间
  • 应该还有更优的解法
int count(char *s, int l, int r, int n) {
    int c = 0;
    while (l >= 0 && r < n && s[l] == s[r]) l--, r++, c++;
    return c;
}

int countSubstrings(char * s) {
    int n = strlen(s);
    int ans = 0;
    for (int i = 0; i < n; i++) {
        ans += count(s, i, i, n); // 212
        ans += count(s, i, i + 1, n); // 2112
    }
    return ans;
}

662-二叉树最大宽度

其实和节点的值没啥关系,根据二叉树的关系给每个节点编个号,左儿子 = 父节点 * 2,右儿子 = 父节点 * 2 + 1,然后分层遍历一下树,对于每一层,用最右边的节点序号减去最左边的节点序号,算一下最大值就行

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Queue<TreeNode> q = new LinkedList<>();
        int ans = 1;
        root.val = 1;
        q.add(root);
        while (!q.isEmpty()) {
            int size = q.size();
            int left = 0, right = 0;
            for (int i = 0; i < size; i++) {
                TreeNode rt = q.poll();
                if (i == 0) {
                    left = rt.val;
                } else if (i == size - 1) {
                    right = rt.val;
                }

                if (rt.left != null) {
                    rt.left.val = rt.val << 1;
                    q.add(rt.left);
                }
                if (rt.right != null) {
                    rt.right.val = rt.val << 1 | 1;
                    q.add(rt.right);
                }
            }
            if (size >= 2) {
                ans = Math.max(ans, right - left + 1);
            }
        }
        return ans;
    }
}

684-冗余连接

简单并查集一下就行了

func findRedundantConnection(edges [][]int) []int {
    n := len(edges)
    f := make([]int, n + 1)
    for i := 0; i < n; i++ {
        f[i] = i
    }
    for i := 0; i < n; i++ {
        u, v := edges[i][0], edges[i][1]
        if u > v {
            u, v = v, u
        }
        if !add(f, u, v) {
            return []int{u, v}
        }
    }
    return nil
}

func add(f []int, u, v int) bool {
    x := find(f, u)
    y := find(f, v)
    if x == y {
        return false
    }
    f[x] = y
    return true
}

func find(f []int, x int) int {
    if f[x] == x {
        return x
    }
    f[x] = find(f, f[x])
    return f[x]
}

695-岛屿的最大面积

简单搜索一下就行了,取最大值

class Solution {

    int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
    public int maxAreaOfIsland(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;

        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 1) {
                    ans = Math.max(ans, dfs(i, j, n, m, grid));
                }
            }
        }
        return ans;
    }

    // 1 1 0 0 0
    // 1 1 0 0 0
    // 0 0 0 1 1
    // 0 0 0 1 1
    int dfs(int x, int y, int n, int m, int[][] grid) {
        int area = 1;
        grid[x][y] = 0;
        for (int i = 0; i < dir.length; i++) {
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
            if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] == 0) {
                continue;
            }
            area += dfs(tx, ty, n, m, grid);
        }
        return area;
    }
}

732-我的日程安排表 III

即求 [a, b) 区间内覆盖的最大次数,直接使用前缀和即可,由于数值比较离散,所以需要用一个 map 起到离散的作用。由于 golang 内置 map 无序,所以需要手动 sort 一下,如果是其他语言,直接使用 TreeMap 即可

type MyCalendarThree struct {
    vis map[int]int
}


func Constructor() MyCalendarThree {
    return MyCalendarThree{
        vis: make(map[int]int, 400),
    }
}


func (c *MyCalendarThree) Book(start int, end int) int {
    c.vis[start]++
    c.vis[end]--
    ans := 0
    sum := 0
    var keys []int
    for k := range c.vis {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, k := range keys {
        sum += c.vis[k]
        if sum > ans {
            ans = sum
        }
    }
    return ans
}

746-使用最小花费爬楼梯

题意是真的难懂

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        if (n == 1) {
            return cost[0];
        }

        vector<int> a(n);
        a[0] = cost[0];
        a[1] = cost[1];
        for (int i = 2; i < n; i++) {
            a[i] = min(a[i - 1], a[i - 2]) + cost[i];
        }
        return min(a[n - 1], a[n - 2]);
    }
};

743-网络延迟时间

随表搜一搜就行了,判断是否可达,如果同一个节点有多个路径可达取最小值

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        // build
        List<Edge>[] g = new ArrayList[n + 1];
        for (int i = 1; i <= n; i++) {
            g[i] = new ArrayList<Edge>();
        }
        for (int i = 0; i < times.length; i++) {
            int u = times[i][0];
            int v = times[i][1];
            int w = times[i][2];
            g[u].add(new Edge(v, w));
        }
      
        // process
        int dp[] = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            dp[i] = -1; // unreachable
        }
        dfs(k, k, 0, g, dp);
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (dp[i] == -1) {
                return -1;
            }
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }

    private void dfs(int u, int pre, int time, List<Edge>[] g, int dp[]) {
        if (dp[u] == -1) {
            dp[u] = time;
        } else {
            if (time >= dp[u]) {
                return;
            }
            dp[u] = time;
        }
        for (int i = 0; i < g[u].size(); i++) {
            int v = g[u].get(i).v;
            int w = g[u].get(i).w;
            if (v == pre) {
                continue;
            }
            dfs(v, u, time + w, g, dp);
        }
    }

    static class Edge {
        public int v;
        public int w;

        public Edge(){}
        public Edge(int v, int w) {
            this.v = v;
            this.w = w;
        }
    }
}

785-判断二分图

染色 dfs 遍历一下就行了

class Solution {
    public boolean isBipartite(int[][] graph) {
        int n = graph.length;
        int color[] = new int[n];
        for (int i = 0; i < n; i++) {
            if (color[i] == 0) {
                color[i] = 1;
                boolean ok = dfs(graph, i, color);
                if (!ok) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean dfs(int[][] graph, int u, int[] color) {
        for (int i = 0; i < graph[u].length; i++) {
            int v = graph[u][i];
            if (color[v] == color[u]) {
                return false;
            }
            if (color[v] == 0) {
                color[v] = -color[u];
                if (!dfs(graph, v, color)) {
                    return false;
                }
            }
        }
        return true;
    }
}

795-区间子数组个数

TODO

827-最大人工岛

不难,先用 DFS 搜出所有的陆地块,同时对其编号并记录每块陆地所在的陆地块序号,然后枚举所有的海洋,判断其上下左右是否是陆地,如果是则把面积相加,最后取最大值即可

#define N 55
#define max(a, b) ((a) > (b) ? (a) : (b))

int tot;
int size[N * N];
int f[N][N];
int ans;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};

void cal_size(int **grid, int n, int m, int x, int y, int tot, int *size) {
    (*size)++;
    f[x][y] = tot;
    grid[x][y] = -1;
    for (int k = 0; k < 4; k++) {
        int tx = x + dir[k][0];
        int ty = y + dir[k][1];
        if (tx >= 0 && tx < n && ty >= 0 && ty < m && grid[tx][ty] == 1) {
            cal_size(grid, n, m, tx, ty, tot, size);
        }
    }
}

int largestIsland(int** grid, int n, int* gridColSize) {
    tot = 0;
    ans = 0;
    memset(f, 0, sizeof(f));
    memset(size, 0, sizeof(size));

    int m = gridColSize[0];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                tot++;
                cal_size(grid, n, m, i, j, tot, &size[tot]);
                ans = max(ans, size[tot]);
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 0) {
                int sum = 1;
                int vis[4] = {0}; // map?
                int debug = 0;
                for (int k = 0; k < 4; k++) {
                    int ti = i + dir[k][0];
                    int tj = j + dir[k][1];
                    if (ti >= 0 && ti < n && tj >= 0 && tj < m && grid[ti][tj] != 0) {
                        int flag = 1;
                        for (int x = 0; x < k; x++) {
                            if (vis[x] == f[ti][tj]) {
                                flag = 0;
                                break;
                            }
                        }
                        if (flag) {
                            debug++;
                            sum += size[f[ti][tj]];
                            vis[k] = f[ti][tj];
                        }
                    }
                }
                ans = max(ans, sum);
            }
        }
    }
    return ans;
}

// 1 0
// 0 1

// 1 1
// 1 0

//   1
// 1 0 1
//   1

834-树中距离之和

TODO

863-二叉树中所有距离为 K 的结点

将树转换一下,把 target 当做根节点,然后找出所有深度为 K 的节点即可

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        Map<Integer, List<Integer>> graph = new HashMap();
        List<Integer> ans = new ArrayList<>();
        convert(root, graph);
        dfs(graph, target.val, target.val, k, 0, ans);
        return ans;
    }

    private void dfs(Map<Integer, List<Integer>> graph, int u, int pre, int k, int dep, List<Integer> ans) {
        if (dep == k) {
            ans.add(u);
            return;
        }
        List<Integer> to = graph.get(u);
        if (to == null) {
            return;
        }
        for (int i = 0; i < to.size(); i++) {
            int v = to.get(i);
            if (v != pre) {
                dfs(graph, v, u, k, dep + 1, ans);
            }
        }
    }

    private void convert(TreeNode root, Map<Integer, List<Integer>> graph) {
        if (root == null) {
            return;
        }
        if (root.left != null) {
            addEdge(root.val, root.left.val, graph);
            convert(root.left, graph);
        }
        if (root.right != null) {
            addEdge(root.val, root.right.val, graph);
            convert(root.right, graph);
        }
    }

    private void addEdge(int u, int v, Map<Integer, List<Integer>> graph) {
        if (graph.get(u) == null) {
            graph.put(u, new ArrayList<>());
        }
        if (graph.get(v) == null) {
            graph.put(v, new ArrayList<>());
        }
        graph.get(u).add(v);
        graph.get(v).add(u);
    }
}

933-最近的请求次数

# -*- coding: UTF-8 -*-

class RecentCounter(object):

    def __init__(self):
        self.pings = []
        self.left = 0

    def ping(self, t):
        """
        :type t: int
        :rtype: int
        """
        self.pings.append(t)
        while t - self.pings[self.left] > 3000:
            self.left += 1
        return len(self.pings) - self.left

951-翻转等价二叉树

class Solution {
    public boolean flipEquiv(TreeNode root1, TreeNode root2) {
        if (root1 == null && root2 == null) return true;
        if (root1 != null && root2 != null && root1.val == root2.val) { // 根节点相等,比较左子树和右子树,分为两种情况
            return flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)
                || flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left);
        }
        return false;
    }
}

968-监控二叉树

TODO

991-坏了的计算器

贪心问题,从结果倒推,如果是奇数就加一,否则除以2,直到小于等于原始值,再补上一个差值即可

class Solution {
    public int brokenCalc(int x, int y) {
        // 5 => 8
        // 5 => 4 => 8

        // 5 => 7
        // 5 => 4 => 8 => 7  7=8-1  
        // 5 => 10 => 9 => 8 => 7

        // 3 => 10
        // 3 => 6 => 5 => 10

        int count = 0;
        while (y > x) {
            if (y % 2 == 1) {
                y += 1;
            } else {
                y /= 2;
            }
            count++;
        }
        return count + (x - y);
    }
}

1003-检查替换后的词是否有效

简单题目,不断删除abc,判断是否可以全部被删除

#define MAXN 20010

int top;
char stack[MAXN];

bool isValid(char *s) {
    int n = strlen(s);
    top = 0;
    for (int i = 0; i < n; i++) {
        stack[top++] = s[i];
        // aabc bc
        while (top >= 3 
            && stack[top - 1] == 'c' 
            && stack[top - 2] == 'b'
            && stack[top - 3] == 'a') {
                top -= 3;
        }
    }
    return top == 0;
}

1137-第 N 个泰波那契数

递推一下就行了

class Solution(object):
    def tribonacci(self, n):
        """
        :type n: int
        :rtype: int
        """
        ret = [0, 1, 1]
        
        i = 3
        while i <= n:
            ret.append(ret[i - 1] + ret[i - 2] + ret[i - 3])
            i += 1
        
        return ret[n]

1161-最大层内元素和

class Solution {
public:
    int maxLevelSum(TreeNode* root) {
        if (root == NULL) return 0;
        int max_sum = 0;
        int max_dep = 0;
        int dep = 0;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int size = q.size();
            int sum = 0;
            while (size--) {
                TreeNode* u = q.front();
                q.pop();
                sum += u->val;
                if (u->left != NULL) q.push(u->left);
                if (u->right != NULL) q.push(u->right);
            }
            dep++;
            if (sum > max_sum) {
                max_dep = dep;
                max_sum = sum;
            }
        }
        return max_dep;
    }
};

1219-黄金矿工

从每个点开始搜索一遍就行了

class Solution {
    int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
    public int getMaximumGold(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                ans = Math.max(ans, dfs(i, j, n, m, grid));
            }
        }
        // ans = dfs(2, 1, n, m, grid);
        return ans;
    }

    // 0 6 0
    // 5 8 7
    // 0 9 0

    // 1, 0, 7 
    // 2, 0, 6
    // 3, 4, 5
    // 0, 3, 0
    // 9, 0, 20
    int dfs(int x, int y, int n, int m, int grid[][]) {
        int t = grid[x][y];
        int maxNext = 0;
        grid[x][y] = -1;
        for (int i = 0; i < dir.length; i++) {
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
            if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] <= 0) {
                continue;
            }
            maxNext = Math.max(maxNext, dfs(tx, ty, n, m, grid));
        }
        grid[x][y] = t;
        return maxNext + t;
    }
}

1249-移除无效的括号

TODO

1254-统计封闭岛屿的数目

简单的DFS搜一下就行,通过递归如果触及到边界则不是封闭岛屿

class Solution {
    private int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };

    public int closedIsland(int[][] grid) {
        int ans = 0;
        int n = grid.length;
        int m = grid[0].length;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) {
                    if (dfs(grid, n, m, i, j)) {
                        ans++;
                    }
                }
            }
        }
        return ans;
    }

    private boolean dfs(int[][] grid, int n, int m, int x, int y) {
        boolean status = true;
        if (x == 0 || y == 0 || x == n - 1 || y == m - 1) {
            status = false;
        }
        for (int i = 0; i < dir.length; i++) {
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
            if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] != 0) {
                continue;
            }
            grid[tx][ty] = 1;
            boolean ok = dfs(grid, n, m, tx, ty);
            if (!ok) {
                status = false;
            }
        }
        return status;
    }
}

1267-统计参与通信的服务器

简单题目,判断一下同一行和同一列的服务器数量是否大于2即可

class Solution(object):
    def countServers(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        n, m = len(grid), len(grid[0])
        row = [0 for i in range(n)]
        col = [0 for i in range(m)]
        for i in range(n):
            for j in range(m):
                if grid[i][j] > 0:
                    row[i] += 1
                    col[j] += 1

        ans = 0
        for i in range(n):
            for j in range(m):
                if grid[i][j] > 0:
                    if row[i] >= 2 or col[j] >= 2:
                        ans += 1

        return ans

1291-顺次数

简单模拟了一下,其实完全可以打表的

class Solution {
    public List<Integer> sequentialDigits(int low, int high) {
        // 1000 130000
        // 1234
        // 2345
        // 3456
        // 4567
        // 5678
        // 6789
        // 
        // 10000
        //
        // 12345
        // 23456

        // 2000 9000
        // 2345

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

        int len_low = len(low);
        int len_high = len(high);
        for (int i = len_low; i <= len_high; i++) {
            int x = i == len_low ? highBit(low, len_low) : 1;
            for (int j = x; j <= 10 - i; j++) {
                int n = gen(i, j);
                if (n < low) {
                    continue;
                }
                if (n > high) {
                    break;
                }
                ans.add(gen(i, j));
            }
        }
        return ans;
    }

    private int highBit(int n, int len) {
        for (int i = 0; i < len - 1; i++) { // 1000 4
            n = n / 10;
        }
        return n;
    }

    private int len(int n) {
        // 101 => 3
        // 10 => 2
        // 8 => 1
        return (int) (Math.log10(n)) + 1;
    }

    private int gen(int len, int start) {
        // 4 + 2 => 2345
        int n = 0;
        for (int i = 0; i < len; i++) {
            n = n * 10 + start + i; // 2 => 23 ...
        }
        return n;
    }
}

1310-子数组异或查询

简单前缀和问题,只不过把和变成异或了

class Solution {
    public int[] xorQueries(int[] arr, int[][] queries) {
        int xor[] = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            if (i == 0) {
                xor[i] = arr[i];
            } else {
                xor[i] = xor[i - 1] ^ arr[i];
            }
        }

        int ans[] = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            int l = queries[i][0];
            int r = queries[i][1];
            if (l - 1 >= 0)
                ans[i] = xor[r] ^ xor[l - 1];
            else
                ans[i] = xor[r];
        }
        return ans;
    }
}

1373-二叉搜索子树的最大键值和

递归一下,返回当前子树的最大值、最小值、节点和,回溯时判断如果满足二叉搜索树的条件,则累加返回,注意一下叶子节点的判断逻辑

func maxSumBST(root *TreeNode) int {
    ans := 0
    dfs(root, &ans)
    return ans
}

type R struct {
    min, max, sum int
    empty bool
}

func dfs(rt *TreeNode, ans *int) R {
    if rt == nil {
        return R{0, 0, 0, true}
    }
    left := dfs(rt.Left, ans)
    right := dfs(rt.Right, ans)
    if (left.empty || left.max < rt.Val) && (right.empty || rt.Val < right.min) {
        sum := left.sum + rt.Val + right.sum
        if sum > *ans {
            *ans = sum
        }
        if left.empty {
            left.min = rt.Val
        }
        if right.empty {
            right.max = rt.Val
        }
        return R{left.min, right.max, sum, false}
    }
    return R{Min, Max, 0, false}
}

1361-验证二叉树

class Solution {
    //    树:n 个节点,n - 1 条边,连通 (根节点入度为 0,其他都是 1,同时如果 n > 1 时根节点出度需要大于 0)
    // 二叉树:每个节点的出度不超过 2
    public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
        int edgeCount = 0;
        int in[] = new int[n];
        int out[] = new int[n];
        for (int i = 0; i < leftChild.length; i++) { // i -> leftChild[i]
            if (leftChild[i] >= 0) {
                edgeCount++;
                in[leftChild[i]]++;
                out[i]++;
            }
        }
        for (int i = 0; i < rightChild.length; i++) { // i -> rightChild[i]
            if (rightChild[i] >= 0) {
                edgeCount++;
                in[rightChild[i]]++;
                out[i]++;
            }
        }
        if (edgeCount != n - 1) {
            return false;
        }
        int root = -1;
        for (int i = 0; i < n; i++) {
            if (in[i] == 0) {
                if (root != -1) {
                    return false;
                }
                root = i;
                if (n > 1 && out[i] <= 0) {
                    return false;
                }
            }
            if (out[i] > 2) {
                return false;
            }
        }
        return true;
    }
}

1492-n 的第 k 个因子

class Solution(object):
    def kthFactor(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: int
        """
        tot = 0
        for i in range(1, n + 1):
            if (n % i == 0):
                tot += 1
                if (tot == k):
                    return i
        return -1

1513-仅含 1 的子串数

遍历一次,然后得到每段 1 的长度,加一下就行了

int numSub(char *s) {
    int len = strlen(s);
    int mod = 1e9 + 7;
    int ans = 0;
    int cnt = 0;
    for (int i = 0; i < len; i++) {
        if (s[i] == '1') {
            cnt++;
        }
        // 1 + 2 + 3 + ... + n
        if ((s[i] == '0' || i == len - 1) && cnt > 0) {
            int t = (long long) cnt * (cnt + 1) / 2 % mod;
            ans = (ans + t) % mod;
            cnt = 0;
        }
    }
    return ans;
}

1519-子树中标签相同的节点数

DFS 搜一遍就行,递归返回当前子树的所有字母出现的次数,回溯时累加即可

func countSubTrees(n int, edges [][]int, labels string) []int {
    graph := build(n, edges)
    ans := make([]int, n)
    dfs(graph, labels, 0, 0, ans)
    return ans
}

func dfs(graph map[int][]int, labels string, u, pre int, ans []int) []int {
    cnt := make([]int, 26)
    cnt[labels[u] - 'a']++
    for _, v := range graph[u] {
        if v != pre {
            vcnt := dfs(graph, labels, v, u, ans)
            for i := 0; i < 26; i++ {
                cnt[i] += vcnt[i]
            }
        }
    }
    ans[u] = cnt[labels[u] - 'a']
    return cnt
}

func build(n int, edges [][]int) map[int][]int {
    graph := make(map[int][]int, n)
    for i := 0; i < len(edges); i++ {
        u := edges[i][0]
        v := edges[i][1]        
        if _, ok := graph[u]; !ok {
            graph[u] = make([]int, 0)
        }
        if _, ok := graph[v]; !ok {
            graph[v] = make([]int, 0)
        }
        graph[u] = append(graph[u], v)
        graph[v] = append(graph[v], u)
    }
    return graph
}

1557-可以到达所有点的最少点数目

统计一下入度为 0 的点的个数即可

class Solution {
public:
    vector<int> findSmallestSetOfVertices(int n, vector<vector<int>>& edges) {
        vector<int> d(n);
        vector<int> ans;
        for (int i = 0; i < edges.size(); i++) {
            int u = edges[i][0];
            int v = edges[i][1];
            d[v]++;
        }
        for (int i = 0; i < n; i++) {
            if (d[i] == 0) {
                ans.push_back(i);
            }
        }
        return ans;
    }
};

1582-二进制矩阵中的特殊位置

class Solution {
    public int numSpecial(int[][] mat) {
        int n = mat.length;
        if (n <= 0) {
            return 0;
        }
        int m = mat[0].length;
        int[] sumRow = new int[n];
        int[] sumCol = new int[m];
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (mat[i][j] == 1) {
                    sumRow[i] ++;
                    sumCol[j] ++;
                }
            }
        }
        
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (mat[i][j] == 1 && sumRow[i] == 1 && sumCol[j] == 1) {
                    ans++;
                }
            }
        }
        return ans;
    }
}

1583-统计不开心的朋友

两个 for 遍历对比一下就行了

class Solution {
    public int unhappyFriends(int n, int[][] preferences, int[][] pairs) {
        Map<Integer, Map<Integer, Integer>> mpt = new HashMap();
        for (int i = 0; i < n; i++) {
            Map<Integer, Integer> inner = new HashMap<>();
            int k = 0;
            for (int j = n - 2; j >= 0; j--) {
                inner.put(preferences[i][j], k++);
            }
            mpt.put(i, inner);
        }
        
        
        boolean[] vis = new boolean[n];
        int ans = 0;
        int k = pairs.length;
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < k; j++) {
               if (i == j) continue;
                //    x           y
                // pair[i][0] pair[i][1] 
                // pair[j][0] pair[j][1] 
                //    u           v
                int x = pairs[i][0];
                int y = pairs[i][1];
                int u = pairs[j][0];
                int v = pairs[j][1];
                
                // x-y u-v
                if (mpt.get(x).get(u) > mpt.get(x).get(y) && mpt.get(u).get(x) > mpt.get(u).get(v) && !vis[x]) {
                    vis[x] = true;
                    ans++;
                }
                // x-y v-u
                else if (mpt.get(x).get(v) > mpt.get(x).get(y) && mpt.get(v).get(x) > mpt.get(v).get(u) && !vis[x]) {
                    vis[x] = true;
                    ans++;
                }
                
                // y-x u-v
                if (mpt.get(y).get(u) > mpt.get(y).get(x) && mpt.get(u).get(y) > mpt.get(u).get(v) && !vis[y]) {
                    vis[y] = true;
                    ans++;
                }

                // y-x v-u
                else if (mpt.get(y).get(v) > mpt.get(y).get(x) && mpt.get(v).get(y) > mpt.get(v).get(u) && !vis[y]) {
                    vis[y] = true;
                    ans++;
                }
            }
        }
        return ans;
    }
}

1584-连接所有点的最小费用

最小生成树模板题

class Solution {
    static int n;
    static int[][] mpt;
    static int[] dis;
    static int[] cls;
    static int INF = 0x3f3f3f3f;
    public int minCostConnectPoints(int[][] points) {
        n = points.length;
        dis = new int[n];
        cls = new int[n];
        mpt = new int[n][n];
        for (int i = 0; i < n; i++) {
            mpt[i] = new int[n];
            for (int j = 0; j < n; j++) {
                mpt[i][j] = INF;
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                mpt[i][j] = mpt[j][i] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
            }
        }
        return prim(0);
    }
    
    int prim(int s) {
        int ans = 0;
        for (int j = 0; j < n; j++) {
            cls[j] = s;
            dis[j] = mpt[s][j];
        }
        dis[s] = -1;
        for (int i = 1; i < n; i++) {
            int Min = INF, k = 0;
            for (int j = 0; j < n; j++) {
                if (dis[j] != -1 && dis[j] < Min) {
                    Min = dis[j];
                    k = j;
                }
            }
            ans += dis[k];
            dis[k] = -1;
            for (int j = 0; j < n; j++) {
                if (dis[j] != -1 && mpt[k][j] < dis[j]) {
                    dis[j] = mpt[k][j];
                    cls[j] = k;
                }
            }
        }
        return ans;
    }
}

面试题21-调整数组顺序使奇数位于偶数前面

二指针,类似于快排

class Solution {
    public int[] exchange(int[] nums) {
        // 1  4 2 4 13 9 8 11 20
        // 1 11 2 4 13 9 8  4 20
        // 1 11 9 4 13 2 8  4 20
        // 1 11 9 13 4 2 8  4 20
        int l = 0, r = nums.length - 1;
        while (l < r) {
            while (l < r && (nums[r] & 1) == 0) r--; // 找到第一个奇数
            while (l < r && (nums[l] & 1) == 1) l++; // 找到第一个偶数
            if (l != r) {
                int t = nums[l];
                nums[l] = nums[r];
                nums[r] =t;
            }
        }
        return nums;
    }
}

面试题04.01-节点间通路

BFS 一下就完事了

class Solution {
    public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
        Map<Integer, List<Integer>> edges = new HashMap();
        for (int i = 0; i < graph.length; i++) {
            int u = graph[i][0];
            int v = graph[i][1];
            if (edges.get(u) == null) {
                List<Integer> to = new ArrayList<>();
                edges.put(u, to);
            }
            edges.get(u).add(v);
        }

        boolean vis[] = new boolean[n];
        Queue<Integer> q = new LinkedList<>();
        q.add(start);
        while (!q.isEmpty()) {
            int u = q.poll();
            if (u == target) {
                return true;
            }
            List<Integer> to = edges.get(u);
            if (to == null) continue;
            for (int i = 0; i < to.size(); i++) {
                int v = to.get(i);
                if (!vis[v]) {
                    vis[v] = true;
                    q.add(v);
                }
            }
        }
        return false;
    }
}

面试题04.10-检查子树

由于树比较大,所以直接暴露匹配可能有问题,所以将树转化为先序序列,使用字符串匹配即可。需要注意一下对空节点的处理

//    1
//   / \
//  2   3
//  /
// 4
//
// 1 2 4 x x x 3 x x

//    2
//   /  \
//  4    3
//
// 2 4 x x 3 x x
class Solution {
    public boolean checkSubTree(TreeNode t1, TreeNode t2) {
        String s1 = convert(t1);
        String s2 = convert(t2);
        return s1.indexOf(s2) >= 0;
    }

    private String convert(TreeNode t) {
        StringBuilder buf = new StringBuilder();
        dfs(t, buf);
        return buf.toString();
    }

    private void dfs(TreeNode t, StringBuilder buf) {
        if (t == null) {
            buf.append('x');
            return;
        }
        buf.append(t.val);
        buf.append(",");
        dfs(t.left, buf);
        dfs(t.right, buf);
    }
}

面试题04.12-求和路径

由于不一定从根节点开始和到叶子节点结束,如果对每个节点暴力搜索肯定不行。所以从根节点向下 dfs,记录一下前缀和,同时使用一个 map 记录前缀和为 s 的前缀有多少个,当到达某个节点时,则前缀和为 S 的路径数为 map[S - s]

class Solution {
    int ans;
    Map<Integer, Integer> vis = new HashMap<>();
    public int pathSum(TreeNode root, int sum) {
        if (root == null) {
            return 0;
        }
        ans = 0;
        dfs(root, sum, root.val);
        return ans;
    }

    private void dfs(TreeNode rt, int S, int s) {
        if (s == S) ans++;
        ans += vis.getOrDefault(s - S, 0);
        if (rt == null) {
            return;
        }
        
        incr(s);
        if (rt.left != null) {
            dfs(rt.left, S, s + rt.left.val);
        }
        if (rt.right != null) {
            dfs(rt.right, S, s + rt.right.val);
        }
        decr(s);
    }

    private void incr(int n) {
        Integer cnt = vis.getOrDefault(n, 0);
        vis.put(n, cnt + 1);
    }

    private void decr(int n) {
        Integer cnt = vis.get(n);
        vis.put(n, cnt - 1);
    }
}

面试题08.01-三步问题

#define N 1000000
#define MOD 1000000007
int a[N + 10] = {0, 1, 2, 4};

int waysToStep(int n) {
    if (a[n] > 0) return a[n];
    for (int i = 4; i <= N; i++) {
        a[i] = ((long long) a[i - 1] + a[i - 2] + a[i - 3]) % MOD;
    }
    return a[n];
}