LeetCode刷题思路与模板总结

数组

二分查找

        注意边界条件的选择,常选用左闭右闭或者左闭右开,采用左闭右闭即[left,right]时,注意循环判断中采用<=;采用左闭右开时,注意循环判断中采用<。

双指针法

        使用两个指针在一个for循环下完成两个for循环的工作

滑动窗口法

        滑动窗口是双指针法的一种特殊形式,通常用于解决子数组或子字符串问题。

        常用模板:

int left = 0, right = 0;
while (right < s.length()) {
    // 扩大窗口
    char c = s.charAt(right);
    right++;

    // 更新窗口状态

    // 当窗口满足条件时,尝试缩小窗口
    while (窗口满足条件) {
        // 更新结果
        char d = s.charAt(left);
        left++;

        // 更新窗口状态
    }
}

链表

虚拟头节点

        对于需要遍历所有节点的链表,可以添加一个虚拟头节点,使得头节点的便利不再特殊,简化代码。

哈希表

数组实现

        如果要记录的键为字母,则可以采用数组来进行记录,相较于map节省了大量空间。

set实现

        当要记录的键为范围较大的数字时,如果仍然采用数组,会造成较大的空间浪费,而set与map不会提前开辟所有可能键的空间,如果不需要记录每个键对应的值,只看是否重复,则采用set。

map实现

        采用理由类似set,区别在于map需要记录的是键值对,当要保存键对应的元素时采用。

字符串

KMP算法

        用于在一个主串(文本串)中查找一个模式串(子串)的出现位置

  1. 构建 next 数组

    • next 数组记录了模式串中每个位置的最长相等前缀和后缀的长度。

    • 通过 next 数组,可以在匹配失败时快速移动模式串的指针,避免重复匹配。

  2. 匹配过程

    • 使用双指针法,一个指针遍历主串,另一个指针遍历模式串。

    • 当字符匹配时,两个指针同时后移。

    • 当字符不匹配时,根据 next 数组移动模式串的指针,而不是回溯主串的指针。

private int[] getNext(String pattern) {
        int n = pattern.length();
        int[] next = new int[n];
        next[0] = 0;
        int j = 0;
        for (int i = 1; i < n; i++) {
            while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }
            if (pattern.charAt(i) == pattern.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }

    // KMP 匹配
    public int kmp(String text, String pattern) {
        int[] next = getNext(pattern);
        int j = 0;
        for (int i = 0; i < text.length(); i++) {
            while (j > 0 && text.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }
            if (text.charAt(i) == pattern.charAt(j)) {
                j++;
            }
            if (j == pattern.length()) {
                return i - j + 1;
            }
        }
        return -1;
    }

二叉树

前序遍历

        遍历顺序:根节点 -> 左子树 -> 右子树

        应用场景

  • 二叉树的构造:例如根据前序遍历和中序遍历构造二叉树。

  • 深度优先搜索(DFS):前序遍历是 DFS 的一种实现方式。

  • 复制二叉树:可以通过前序遍历复制一棵二叉树。

void preorder(TreeNode root) {
    if (root == null) return;
    System.out.print(root.val + " "); // 访问根节点
    preorder(root.left);              // 递归左子树
    preorder(root.right);             // 递归右子树
}

中序遍历

        遍历顺序:左子树 -> 根节点 -> 右子树

        应用场景

  • 二叉搜索树(BST)的属性:中序遍历 BST 会得到一个升序序列。

  • 表达式树:中序遍历可以还原中缀表达式。

  • 验证二叉搜索树:通过中序遍历检查是否满足升序。

void inorder(TreeNode root) {
    if (root == null) return;
    inorder(root.left);               // 递归左子树
    System.out.print(root.val + " "); // 访问根节点
    inorder(root.right);              // 递归右子树
}

后序遍历

        遍历顺序:左子树 -> 右子树 -> 根节点

        应用场景

  • 普通二叉树的属性:例如计算二叉树的高度、判断二叉树是否平衡等。

  • 删除二叉树:后序遍历可以确保在删除根节点之前先删除子节点。

  • 表达式树:后序遍历可以得到后缀表达式(逆波兰表达式)。

void postorder(TreeNode root) {
    if (root == null) return;
    postorder(root.left);              // 递归左子树
    postorder(root.right);             // 递归右子树
    System.out.print(root.val + " "); // 访问根节点
}

回溯法

        采用递归进行纵向遍历,采用for循环进行横向遍历,常在横向时进行剪枝操作来提高性能。

        模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

  应用场景:

        组合问题
        切割问题
        子集问题
        排列问题
        棋盘问题

贪心算法

通过局部最优来达到整体最优

动态规划

基本步骤:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组
public class DynamicProgrammingTemplate {
    public static void main(String[] args) {
        // 确定dp数组以及下标的含义
        // 例如:dp[i] 表示以i结尾的子数组的最大/最小值等

        // 确定dp数组的长度
        int n = /* 数组或问题的规模 */;
        int[] dp = new int[n + 1]; // 有时候需要多一个空间,具体问题具体分析

        // dp数组如何初始化
        // 初始化dp数组的起始值,通常是dp[0]或者dp[1]
        dp[0] = /* 初始化值 */;

        // 确定遍历顺序
        // 通常是根据问题的性质来确定是正序遍历还是倒序遍历
        for (int i = 1; i <= n; i++) {
            // 确定递推公式
            // dp[i] = /* 根据dp[i-1], dp[i-2]...等之前的值来更新dp[i]的值 */;

            // 举例推导dp数组
            // 打印dp数组的值,用于调试和理解dp数组的更新过程
            System.out.println("dp[" + i + "] = " + dp[i]);
        }

        // 根据dp数组的最终结果来返回答案
        // int result = dp[n]; // 假设最终结果是dp数组的最后一个值
        // return result;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值