面试准备——Java经典算法【大厂必备,高级程序开发员的必经之路】

二分查找算法

核心思想

  • 有序数组中通过不断将搜索范围减半来快速找到目标值。
  • 时间复杂度:O(log n)。
  • 空间复杂度:O(1)(非递归实现)。

代码实现

public int binarySearch(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2; // 防止溢出
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1; // 未找到
}

面试案例

  • 问题:给定一个升序数组 [1, 3, 5, 7, 9] 和目标值 5,使用二分查找找到目标值的索引。
  • 答案:返回索引 2

分治算法

核心思想

  • 将问题分解为若干个子问题,分别解决后合并结果。
  • 经典应用:归并排序、快速排序。

代码实现(归并排序)

public void mergeSort(int[] arr, int left, int right) {
    if (left >= right) return;
    int mid = left + (right - left) / 2;
    mergeSort(arr, left, mid);
    mergeSort(arr, mid + 1, right);
    merge(arr, left, mid, right);
}

private void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[right - left + 1];
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    System.arraycopy(temp, 0, arr, left, temp.length);
}

面试案例

  • 问题:对数组 [38, 27, 43, 3, 9, 82, 10] 进行排序。
  • 答案:排序后为 [3, 9, 10, 27, 38, 43, 82]

动态规划算法

核心思想

  • 将复杂问题分解为子问题,保存子问题的解以避免重复计算。
  • 经典应用:背包问题、最长公共子序列、斐波那契数列。

代码实现(斐波那契数列)

public int fibonacci(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];
}

面试案例

  • 问题:求斐波那契数列第 10 项的值。
  • 答案:返回 55

KMP算法

核心思想

  • 在字符串匹配中利用前缀表(Partial Match Table)避免回溯,提高效率。
  • 时间复杂度:O(n + m),其中 n 是主串长度,m 是模式串长度。

代码实现

public int kmpSearch(String text, String pattern) {
    int[] lps = computeLPSArray(pattern);
    int i = 0, j = 0;
    while (i < text.length()) {
        if (text.charAt(i) == pattern.charAt(j)) {
            i++;
            j++;
        }
        if (j == pattern.length()) {
            return i - j; // 匹配成功
        } else if (i < text.length() && text.charAt(i) != pattern.charAt(j)) {
            if (j != 0) {
                j = lps[j - 1];
            } else {
                i++;
            }
        }
    }
    return -1; // 匹配失败
}

private int[] computeLPSArray(String pattern) {
    int[] lps = new int[pattern.length()];
    int len = 0, i = 1;
    while (i < pattern.length()) {
        if (pattern.charAt(i) == pattern.charAt(len)) {
            len++;
            lps[i] = len;
            i++;
        } else {
            if (len != 0) {
                len = lps[len - 1];
            } else {
                lps[i] = 0;
                i++;
            }
        }
    }
    return lps;
}

面试案例

  • 问题:在字符串 "ABABDABACDABABCABAB" 中查找子串 "ABABCABAB" 的起始位置。
  • 答案:返回索引 10

贪心算法

核心思想

  • 每一步都选择局部最优解,最终希望达到全局最优解。
  • 经典应用:活动选择问题、最小生成树(Prim/Kruskal)、霍夫曼编码。

代码实现(活动选择问题)

public int activitySelection(int[] start, int[] end) {
    int n = start.length;
    int[][] activities = new int[n][2];
    for (int i = 0; i < n; i++) {
        activities[i][0] = start[i];
        activities[i][1] = end[i];
    }
    Arrays.sort(activities, (a, b) -> a[1] - b[1]);
    int count = 1, lastEnd = activities[0][1];
    for (int i = 1; i < n; i++) {
        if (activities[i][0] >= lastEnd) {
            count++;
            lastEnd = activities[i][1];
        }
    }
    return count;
}

面试案例

  • 问题:给定活动的开始时间和结束时间 [[1, 2], [3, 4], [0, 6], [5, 7]],最多可以参加多少个活动?
  • 答案:返回 3

普利姆算法

核心思想
  • 构造最小生成树,每次选择当前集合到其他顶点的最小权值边。
  • 时间复杂度:O(V^2) 或 O(E log V)(优先队列优化)。

代码实现

public int primMST(int[][] graph) {
    int n = graph.length;
    boolean[] visited = new boolean[n];
    int[] key = new int[n];
    Arrays.fill(key, Integer.MAX_VALUE);
    key[0] = 0;
    int totalWeight = 0;

    for (int i = 0; i < n; i++) {
        int u = -1;
        for (int j = 0; j < n; j++) {
            if (!visited[j] && (u == -1 || key[j] < key[u])) {
                u = j;
            }
        }
        visited[u] = true;
        totalWeight += key[u];
        for (int v = 0; v < n; v++) {
            if (graph[u][v] != 0 && !visited[v] && graph[u][v] < key[v]) {
                key[v] = graph[u][v];
            }
        }
    }
    return totalWeight;
}

面试案例

  • 问题:给定邻接矩阵 [[0, 2, 0, 6, 0], [2, 0, 3, 8, 5], [0, 3, 0, 0, 7], [6, 8, 0, 0, 9], [0, 5, 7, 9, 0]],求最小生成树的总权重。
  • 答案:返回 16

克鲁斯卡尔算法

核心思想

  • 按边权值从小到大排序,依次加入生成树,确保不形成环。
  • 时间复杂度:O(E log E)。

代码实现

// 使用并查集实现克鲁斯卡尔算法
public int kruskalMST(int[][] edges, int n) {
    Arrays.sort(edges, (a, b) -> a[2] - b[2]);
    int[] parent = new int[n];
    for (int i = 0; i < n; i++) parent[i] = i;

    int totalWeight = 0;
    for (int[] edge : edges) {
        int u = edge[0], v = edge[1], weight = edge[2];
        if (find(parent, u) != find(parent, v)) {
            union(parent, u, v);
            totalWeight += weight;
        }
    }
    return totalWeight;
}

private int find(int[] parent, int x) {
    if (parent[x] != x) parent[x] = find(parent, parent[x]);
    return parent[x];
}

private void union(int[] parent, int x, int y) {
    parent[find(parent, x)] = find(parent, y);
}

面试案例

  • 问题:与普利姆算法相同的问题。
  • 答案:返回 16

迪杰斯特拉算法

核心思想

  • 单源最短路径算法,适用于非负权图。
  • 时间复杂度:O(V^2) 或 O(E log V)(优先队列优化)。

代码实现

public int dijkstra(int[][] graph, int src, int dest) {
    int n = graph.length;
    int[] dist = new int[n];
    boolean[] visited = new boolean[n];
    Arrays.fill(dist, Integer.MAX_VALUE);
    dist[src] = 0;

    for (int i = 0; i < n - 1; i++) {
        int u = -1;
        for (int j = 0; j < n; j++) {
            if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
                u = j;
            }
        }
        visited[u] = true;
        for (int v = 0; v < n; v++) {
            if (graph[u][v] != 0 && !visited[v] && dist[u] + graph[u][v] < dist[v]) {
                dist[v] = dist[u] + graph[u][v];
            }
        }
    }
    return dist[dest];
}

面试案例

  • 问题:给定邻接矩阵 [[0, 10, 0, 0, 0], [10, 0, 5, 0, 0], [0, 5, 0, 20, 1], [0, 0, 20, 0, 2], [0, 0, 1, 2, 0]],求从节点 0 到节点 4 的最短路径。
  • 答案:返回 16

弗洛伊德算法

核心思想

  • 多源最短路径算法,适用于任意权图(包括负权图)。
  • 时间复杂度:O(V^3)。

代码实现

public void floydWarshall(int[][] graph) {
    int n = graph.length;
    int[][] dist = new int[n][n];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            dist[i][j] = graph[i][j];
        }
    }

    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE &&
                    dist[i][k] + dist[k][j] < dist[i][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }
}

面试案例

  • 问题:与迪杰斯特拉算法相同的问题。
  • 答案:返回 16

马踏棋盘算法

核心思想

  • 回溯法解决骑士遍历问题,尝试所有可能的移动路径。
  • 时间复杂度:O(8(N2))。

代码实现

public boolean knightTour(int N) {
    int[][] board = new int[N][N];
    int[] dx = {2, 1, -1, -2, -2, -1, 1, 2};
    int[] dy = {1, 2, 2, 1, -1, -2, -2, -1};

    if (!solveKTUtil(board, 0, 0, 1, dx, dy)) {
        System.out.println("Solution does not exist");
        return false;
    } else {
        printSolution(board);
        return true;
    }
}

private boolean solveKTUtil(int[][] board, int x, int y, int movei, int[] dx, int[] dy) {
    if (movei == board.length * board.length) return true;

    for (int k = 0; k < 8; k++) {
        int next_x = x + dx[k];
        int next_y = y + dy[k];
        if (isSafe(next_x, next_y, board)) {
            board[next_x][next_y] = movei;
            if (solveKTUtil(board, next_x, next_y, movei + 1, dx, dy)) return true;
            board[next_x][next_y] = -1; // Backtracking
        }
    }
    return false;
}

private boolean isSafe(int x, int y, int[][] board) {
    return x >= 0 && x < board.length && y >= 0 && y < board.length && board[x][y] == -1;
}

面试案例

  • 问题:在一个 8x8 的棋盘上,从左上角出发,能否完成马踏棋盘?
  • 答案:输出解决方案或提示无解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值