常用算法的归纳(欢迎大佬补充[特殊字符][特殊字符])

一、先梳理常用算法分类(按高频排序)

以下是编程中最核心、最常考的算法,覆盖 90% 以上的基础题:

  1. 双指针法
  2. 动态规划
  3. 深度优先搜索(DFS)/ 广度优先搜索(BFS)
  4. 二分查找
  5. 哈希表(散列表)
  6. 并查集(Union-Find)
  7. 滑动窗口

二、逐个拆解:核心思路 + 应用场景 + 例子

1. 双指针法
核心思路

用两个指针(变量)在数组 / 链表上移动,一个快一个慢,或一个左一个右,通过指针的相对移动缩小处理范围,把 O (n²) 的时间复杂度降到 O (n)

应用场景
  • 数组 / 链表的遍历(比如找两数之和、反转字符串);
  • 有序数组去重、合并两个有序数组;
  • 链表的快慢指针(找环、找中间节点)
简单例子(反转字符串)
// 输入:["h","e","l","l","o"] → 输出:["o","l","l","e","h"]
public void reverseString(char[] s) {
    int left = 0;          // 左指针从头部开始
    int right = s.length - 1; // 右指针从尾部开始
    while (left < right) {
        // 交换左右指针的元素
        char temp = s[left];
        s[left] = s[right];
        s[right] = temp;
        left++;  // 左指针右移
        right--; // 右指针左移
    }
}
2. 动态规划(DP)
核心思路

大事化小,小事化了”—— 把复杂问题拆成重叠的子问题,记录子问题的解(存在 dp 数组里),避免重复计算,最终推导全局最优解。核心步骤:定义 dp 数组含义 → 找状态转移方程 → 确定初始条件 → 计算结果。

应用场景
  • 最值问题(比如最长递增子序列、最小路径和);
  • 计数问题(比如不同路径数);
  • 背包问题(01 背包、完全背包)。
简单例子(斐波那契数列)
// 求第n个斐波那契数(dp[i] = dp[i-1] + dp[i-2])
public int fib(int n) {
    if (n <= 1) return n;
    int[] dp = new int[n+1];
    dp[0] = 0; // 初始条件
    dp[1] = 1; // 初始条件
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2]; // 状态转移方程
    }
    return dp[n];
}
3. 深度优先搜索(DFS)/ 广度优先搜索(BFS)
核心思路
  • DFS:“一条路走到黑,走不通就回头”(递归 / 栈实现),适合找所有解、遍历树 / 图;
  • BFS:“层层递进,先近后远”(队列实现),适合找最短路径、层级遍历。
应用场景
  • DFS:子集、组合、全排列(回溯本质是 DFS 的一种)、树的前 / 中 / 后序遍历;
  • BFS:二叉树的层序遍历、迷宫最短路径、朋友圈问题。
简单例子(BFS 遍历二叉树)
// 层序遍历二叉树:从上到下,一层一层输出节点值
public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root); // 根节点入队
    while (!queue.isEmpty()) {
        int size = queue.size(); // 当前层的节点数
        List<Integer> level = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll(); // 出队
            level.add(node.val);
            if (node.left != null) queue.offer(node.left); // 左孩子入队
            if (node.right != null) queue.offer(node.right); // 右孩子入队
        }
        result.add(level);
    }
    return result;
}
4. 二分查找
核心思路

针对有序数组,每次取中间值和目标值比较,缩小一半查找范围,时间复杂度 O (logn),比遍历快得多。核心条件:左边界≤右边界,每次更新左 / 右边界。

应用场景
  • 有序数组找目标值、找目标值的第一个 / 最后一个位置;
  • 找峰值、平方根、旋转有序数组的最小值。
简单例子(找目标值)
// 有序数组nums中找target的下标,找不到返回-1
public int search(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2; // 避免溢出
        if (nums[mid] == target) {
            return mid; // 找到目标值
        } else if (nums[mid] < target) {
            left = mid + 1; // 目标值在右半区
        } else {
            right = mid - 1; // 目标值在左半区
        }
    }
    return -1; // 没找到
}
5. 哈希表(散列表)
核心思路

通过 “哈希函数” 把键映射到对应位置,实现O (1) 时间的增删改查,核心是解决哈希冲突(Java 的 HashMap 用链表 + 红黑树解决)。

应用场景
  • 快速查找(比如两数之和、统计元素出现次数);
  • 去重、映射(比如字符异位词、罗马数字转整数)。
简单例子(两数之和)
// 找数组中两个数的下标,和为target
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[]{map.get(complement), i};
        }
        map.put(nums[i], i); // 存数值和下标
    }
    return new int[0];
}
6. 并查集(Union-Find)
核心思路

处理 “动态连通性” 问题,比如判断两个元素是否在同一个集合、合并两个集合,核心操作是 “查找(找根节点)” 和 “合并(合并两个集合)”。

应用场景
  • 朋友圈问题、岛屿数量;
  • 图的连通分量、最小生成树(Kruskal 算法)
简单例子(初始化 + 查找 + 合并)
class UnionFind {
    private int[] parent; // 父节点数组
    public UnionFind(int n) {
        parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i; // 初始时,每个元素的父节点是自己
        }
    }
    // 查找根节点(路径压缩,优化效率)
    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }
    // 合并两个集合
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            parent[rootY] = rootX;
        }
    }
}
7. 滑动窗口
核心思路

用一个 “窗口”(左、右指针)在数组 / 字符串上滑动,动态维护窗口内的元素,把 O (n²) 降到 O (n),适合处理子数组 / 子字符串问题。

应用场景
  • 最长无重复子串、最小覆盖子串;
  • 子数组的最大和、找所有字母异位词。
简单例子(最长无重复子串)
// 找字符串中最长无重复字符的子串长度
public int lengthOfLongestSubstring(String s) {
    Set<Character> set = new HashSet<>();
    int left = 0; // 窗口左边界
    int maxLen = 0;
    for (int right = 0; right < s.length(); right++) {
        // 如果窗口内有重复字符,左指针右移,直到无重复
        while (set.contains(s.charAt(right))) {
            set.remove(s.charAt(left));
            left++;
        }
        set.add(s.charAt(right)); // 右指针右移,加入字符
        maxLen = Math.max(maxLen, right - left + 1); // 更新最大长度
    }
    return maxLen;
}

总结

  1. 优先掌握:双指针、哈希表、二分查找(入门易、应用广);
  2. 重点突破:动态规划、DFS/BFS(稍难,但占面试题核心);
  3. 补充学习:并查集、滑动窗口(解决特定场景问题);
  4. 算法选择技巧
    • 有序数组→二分查找;
    • 最值 / 子问题→动态规划;
    • 数组 / 字符串遍历优化→双指针 / 滑动窗口;
    • 树 / 图遍历→DFS/BFS;
    • 快速查找 / 统计→哈希表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值