动态规划、贪心算法、分治算法和回溯算法的完整 Java 实现

动态规划、贪心算法、分治算法和回溯算法的完整 Java 实现

以下是动态规划、贪心算法、分治算法和回溯算法的完整 Java 实现,包含详细注释和应用场景分析:

import java.util.*;

public class CoreAlgorithms {

    public static void main(String[] args) {
        // 动态规划示例
        System.out.println("动态规划 - 斐波那契数列(10): " + dpFibonacci(10));
        System.out.println("动态规划 - 零钱兑换(18): " + coinChange(new int[]{1, 2, 5}, 18));

        // 贪心算法示例
        System.out.println("\n贪心算法 - 活动选择: ");
        activitySelection();
        
        // 分治算法示例
        int[] arr = {12, 11, 13, 5, 6, 7};
        System.out.println("\n分治算法 - 归并排序:");
        System.out.println("排序前: " + Arrays.toString(arr));
        mergeSort(arr, 0, arr.length - 1);
        System.out.println("排序后: " + Arrays.toString(arr));
        
        // 回溯算法示例
        System.out.println("\n回溯算法 - N皇后问题(4皇后):");
        solveNQueens(4);
    }

    // ========== 动态规划算法 ==========
    /**
     * 1. 斐波那契数列(基础DP)
     * 时间复杂度: O(n)
     */
    public static int dpFibonacci(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];
    }

    /**
     * 2. 零钱兑换问题(完全背包DP)
     * 时间复杂度: O(amount * coins.length)
     */
    public static int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1); // 初始化为大值
        dp[0] = 0;
        
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }

    // ========== 贪心算法 ==========
    /**
     * 活动选择问题(区间调度)
     * 时间复杂度: O(n log n)
     */
    public static void activitySelection() {
        // 活动格式: [开始时间, 结束时间]
        int[][] activities = {{1, 4}, {3, 5}, {0, 6}, {5, 7}, {3, 9}, {5, 9}, {6, 10}, {8, 11}, {8, 12}, {2, 14}, {12, 16}};
        
        // 按结束时间排序
        Arrays.sort(activities, (a, b) -> a[1] - b[1]);
        
        List<int[]> selected = new ArrayList<>();
        int lastEnd = -1;
        
        for (int[] act : activities) {
            if (act[0] >= lastEnd) { // 贪心选择:选择最早结束且不冲突的活动
                selected.add(act);
                lastEnd = act[1];
            }
        }
        
        System.out.println("选择活动数量: " + selected.size());
        System.out.println("选择活动详情:");
        for (int[] act : selected) {
            System.out.println(Arrays.toString(act));
        }
    }

    // ========== 分治算法 ==========
    /**
     * 归并排序(经典分治)
     * 时间复杂度: O(n log n)
     */
    public static void mergeSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = left + (right - left) / 2;
            
            // 分治:分解问题
            mergeSort(arr, left, mid);
            mergeSort(arr, mid + 1, right);
            
            // 合并:解决子问题
            merge(arr, left, mid, right);
        }
    }
    
    private static 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);
    }

    // ========== 回溯算法 ==========
    /**
     * N皇后问题
     * 时间复杂度: O(n!)
     */
    public static void solveNQueens(int n) {
        List<List<String>> solutions = new ArrayList<>();
        char[][] board = new char[n][n];
        for (char[] row : board) Arrays.fill(row, '.');
        
        backtrack(board, 0, solutions);
        
        // 打印解决方案
        System.out.println("解决方案数量: " + solutions.size());
        for (int i = 0; i < solutions.size(); i++) {
            System.out.println("方案 " + (i + 1) + ":");
            for (String row : solutions.get(i)) {
                System.out.println(row);
            }
            System.out.println();
        }
    }
    
    private static void backtrack(char[][] board, int row, List<List<String>> solutions) {
        if (row == board.length) {
            solutions.add(formatSolution(board));
            return;
        }
        
        for (int col = 0; col < board.length; col++) {
            if (isValid(board, row, col)) {
                board[row][col] = 'Q'; // 放置皇后
                backtrack(board, row + 1, solutions); // 递归下一行
                board[row][col] = '.'; // 回溯:撤销选择
            }
        }
    }
    
    private static boolean isValid(char[][] board, int row, int col) {
        // 检查列
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q') return false;
        }
        
        // 检查左上对角线
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] == 'Q') return false;
        }
        
        // 检查右上对角线
        for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) {
            if (board[i][j] == 'Q') return false;
        }
        
        return true;
    }
    
    private static List<String> formatSolution(char[][] board) {
        List<String> solution = new ArrayList<>();
        for (char[] row : board) {
            solution.add(new String(row));
        }
        return solution;
    }
}

算法详解与输出示例

1. 动态规划 (Dynamic Programming)

核心思想:将复杂问题分解为重叠子问题,存储子问题解避免重复计算
适用场景:最优子结构、重叠子问题的问题
示例输出

动态规划 - 斐波那契数列(10): 55
动态规划 - 零钱兑换(18): 4  // 使用 [1,2,5] 硬币凑 18 元最少需要 4 枚(5+5+5+3)

2. 贪心算法 (Greedy Algorithm)

核心思想:每一步选择当前最优解,希望最终达到全局最优
适用场景:局部最优能导致全局最优的问题
示例输出

贪心算法 - 活动选择: 
选择活动数量: 4
选择活动详情:
[1, 4]
[5, 7]
[8, 11]
[12, 16]

3. 分治算法 (Divide and Conquer)

核心思想:将问题分解为多个子问题,递归解决后合并结果
适用场景:可分解为独立子问题的问题
示例输出

分治算法 - 归并排序:
排序前: [12, 11, 13, 5, 6, 7]
排序后: [5, 6, 7, 11, 12, 13]

4. 回溯算法 (Backtracking)

核心思想:通过尝试所有可能的选择,发现无效解时回溯
适用场景:求所有解、组合优化、约束满足问题
示例输出

回溯算法 - N皇后问题(4皇后):
解决方案数量: 2
方案 1:
.Q..
...Q
Q...
..Q.

方案 2:
..Q.
Q...
...Q
.Q..

四大算法对比分析

算法核心思想时间复杂度空间复杂度典型应用场景
动态规划存储子问题解O(子问题数×子问题规模)O(子问题数)最短路径、背包问题、编辑距离
贪心算法局部最优选择通常O(n log n)O(1)活动选择、霍夫曼编码、最小生成树
分治算法分-解-合O(n log n)O(n)归并排序、快速排序、最近点对
回溯算法尝试-回溯指数级O(kⁿ)O(递归深度)N皇后、数独、组合优化

算法选择决策树

问题类型
有最优子结构?
子问题重叠?
动态规划
分治算法
局部最优导致全局最优?
贪心算法
需要穷举所有解?
回溯算法
其他算法

各算法最佳实践

动态规划

// 通用DP框架
public int dpSolution(int[] params) {
    int n = params.length;
    // 1. 定义DP数组
    int[] dp = new int[n + 1];
    
    // 2. 初始化基础状态
    dp[0] = 0;
    
    // 3. 状态转移
    for (int i = 1; i <= n; i++) {
        dp[i] = Math.min(dp[i - 1], dp[i - 2]) + params[i - 1];
    }
    
    // 4. 返回结果
    return dp[n];
}

贪心算法

// 贪心选择框架
public void greedySolution(List<Item> items) {
    // 1. 按贪心策略排序
    Collections.sort(items, Comparator.comparingDouble(Item::valuePerWeight));
    
    double totalValue = 0;
    // 2. 贪心选择
    for (Item item : items) {
        if (canTake(item)) {
            take(item);
            totalValue += item.value;
        }
    }
}

分治算法

// 分治三步骤
public Result divideConquer(Problem problem) {
    // 1. 分解:将大问题分解为子问题
    if (problem.isSimple()) {
        return solveDirectly(problem);
    }
    
    Problem[] subProblems = split(problem);
    
    // 2. 解决:递归解决子问题
    Result result1 = divideConquer(subProblems[0]);
    Result result2 = divideConquer(subProblems[1]);
    
    // 3. 合并:合并子问题解
    return mergeResults(result1, result2);
}

回溯算法

// 回溯框架
public void backtrack(State state, List<Choice> choices) {
    // 1. 终止条件
    if (isSolution(state)) {
        recordSolution(state);
        return;
    }
    
    // 2. 遍历选择
    for (Choice choice : choices) {
        // 3. 做出选择
        makeChoice(state, choice);
        
        // 4. 递归进入下一层
        backtrack(state, getNextChoices());
        
        // 5. 撤销选择
        undoChoice(state, choice);
    }
}

性能优化技巧

  1. 动态规划优化

    • 状态压缩:滚动数组减少空间复杂度
    • 记忆化搜索:递归+缓存替代迭代DP
    // 斐波那契记忆化搜索
    private int fibMemo(int n, int[] memo) {
        if (n <= 1) return n;
        if (memo[n] > 0) return memo[n];
        memo[n] = fibMemo(n-1, memo) + fibMemo(n-2, memo);
        return memo[n];
    }
    
  2. 回溯剪枝

    for (Choice choice : choices) {
        if (!isValid(state, choice)) continue; // 剪枝:跳过无效选择
        makeChoice(state, choice);
        backtrack(state, nextChoices);
        undoChoice(state, choice);
    }
    
  3. 贪心算法证明

    • 贪心选择性质:局部最优 → 全局最优
    • 最优子结构:问题最优解包含子问题最优解
  4. 分治并行化

    // 使用ForkJoinPool并行处理子问题
    Result left = forkJoinPool.submit(() -> divideConquer(leftProblem)).join();
    Result right = forkJoinPool.submit(() -> divideConquer(rightProblem)).join();
    

常见问题解决方案

问题类型推荐算法替代方案
最短路径问题动态规划 (Dijkstra)BFS (无权图)
任务调度问题贪心算法动态规划
大规模排序分治 (归并/快排)堆排序
组合优化 (如N皇后)回溯算法启发式搜索
资源分配问题贪心算法动态规划
字符串编辑距离动态规划有限自动机

提示:实际应用中常组合使用多种算法,如回溯+剪枝、贪心+动态规划验证等混合策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值