【韩顺平-数据结构】十大算法(学习笔记)

本文详细讲解了数据结构中的十大经典算法,包括二分查找、分治算法、动态规划、KMP算法、贪心算法以及图的最小生成树算法(普利姆与克鲁斯卡尔算法),并提供了相关问题的代码实现,如背包问题和最短路径问题,帮助读者深入理解这些算法的应用。
摘要由CSDN通过智能技术生成

一、二分查找(非递归)

1、非递归二分查找概述

  1. 二分查找法只适用于从有序的数列中进行查找(比如数字和字母等),将数列排序后再进行查找
  2. 二分查找法的运行时间为对数时间 O(㏒₂n),即查找到需要的目标位置最多只需要 ㏒₂n 步

2、非递归二分查找代码实现

package work.rexhao.search;

/**
 * 非递归二分查找
 */
public class BinarySearchNoRecursion {
   
    public static void main(String[] args) {
   
        int[] num = new int[]{
   10, 12, 23, 32, 45, 64, 65, 69, 83};
        System.out.println(binarySearchNoRecursion(10, num));
        System.out.println(binarySearchNoRecursion(65, num));
        System.out.println(binarySearchNoRecursion(99, num));
    }

    public static int binarySearchNoRecursion(int target, int[] num) {
   
        int left = 0;
        int right = num.length - 1;
        int mid;
        while (left <= right) {
   
            mid = (left + right) / 2;
            if (target == num[mid]) {
   
                return mid;
            } else if (target > num[mid]) {
   
                // 目标值比中间值大 --> 向右找
                left = mid + 1;
            } else {
   
                // 目标值比中间值小 --> 向左找
                right = mid - 1;
            }
        }
        return -1;
    }
}

3、补:递归二分查找代码实现

package work.rexhao.search;

import java.util.Arrays;

/**
 * 二分查找
 */
public class BinarySearch {
   
    public static void main(String[] args) {
   
        int[] num = new int[]{
   10, 12, 23, 32, 45, 64, 65, 69, 83};
        System.out.println(Arrays.toString(num));
        System.out.println(binarySearch(num, 69, 0, num.length - 1));
        System.out.println(binarySearch(num, 99, 0, num.length - 1));
    }

    public static int binarySearch(int[] num, int target, int left, int right) {
   
        if (left > right) {
   
            return -1;
        }
        if (num[(left + right) / 2] == target) {
   
            return (left + right) / 2;
        } else if (num[(left + right) / 2] > target) {
   
            return binarySearch(num, target, left, (left + right) / 2);
        } else {
   
            return binarySearch(num, target, (left + right) / 2 + 1, right);
        }
    }
}

二、分治算法(DC)

1、分治算法介绍

分治法(Divide-and-Conquer§)是一种很重要的算法。

字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

2、分治算法的基本步骤

  1. 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
  2. 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
  3. 合并:将各个子问题的解合并为原问题的解

3、分治算法设计模式

if |p| <= n0
	then return(ADHOC(P))
// 将p分解为较小的子问题p1,P2,…,Pk
for if <- 1 to k
do yi <-  DC递归解决pi
T <- MERGE(y1,x2,,vk) 合并子问题
return(T)
  1. 其中|P|表示问题p的规模
  2. n0为一國值,表示当问题P的规模不超过no时,问题已容易直接解出,不必再继续分解。
  3. ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)求解。
  4. 算法MERGE(y1,x2,…,vk)是该分治法中的合并子算法,用于将P的子问题p1,p2,…,pk的相应的解y1,x2,…,vk合并为P的解。

4、汉诺塔代码实现

package work.rexhao.algorithm.dc;

import java.util.Collections;

/**
 * 汉诺塔
 */
public class hanoiDemo {
   
    static int count = 0;

    public static void main(String[] args) {
   
        hanoiTower(5, 'A', 'B', 'C');
    }

    private static void hanoiTower(int num, char a, char b, char c) {
   
        // 如果只有一个盘
        if (num == 1) {
   
            System.out.println(++count + " : 第 1 个盘从 " + a + "->" + c);
        } else {
   
            // 如果我们有 n >= 2 情况,我们总是可以看做是两个盘 1.最下边的一个盘 2. 上面的所有盘

            // 1. 先把 最上面的所有盘 A->B, 移动过程会使用到 c
            hanoiTower(num - 1, a, c, b);

            // 2. 把最下边的盘 A->C
            System.out.println(++count + " : 第 " + num + " 个盘从 " + a + "->" + c);

            // 3. 把 B 塔的所有盘 从 B->C , 移动过程使用到 a 塔
            hanoiTower(num - 1, b, a, c);
        }
    }
}

三、动态规划(DP)

1、动态规划算法介绍

  1. 动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法
  2. 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
  3. 与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)
  4. 动态规划可以通过填表的方式来逐步推进,得到最优解。

2、背包问题

1)背包问题概述

有一个背包,容量为4kg,现有如下物品。要求达到的目标为装入的背包的总价值最大,并且重量不超出装入的物品不能重复。

物品 重量 价值
G 1 15
S 4 30
L 3 20

2)背包问题思路分析

背包问题主要是指一个给定容量的背包、若干具有一定价值和重量的物品,如何选择物品放入背包使物品的价值最大。其中又分 01 背包完全背包(完全背包指的是:每种物品都有无限件可用,且无限背包可以转化为 01 背包)

算法的主要思想,利用动态规划来解决。每次遍历到的第 i 个物品,根据 w[i]和 v[i]来确定是否需要将该物品放入背包中。即对于给定的 n 个物品,设 v[i]、w[i]分别为第 i 个物品的价值和重量,C 为背包的容量。再令 v[i][j]表示在前 i 个物品中能够装入容量为 j 的背包中的最大价值。

  1. v[i][0]=v[0][j]=0;
    表示填入表第一行和第一列是 0
  2. w[i]> jv[i][j]=v[i-1][j]
    当准备加入新增的商品的容量大于 当前背包的容量时,就直接使用上一个 单元格的装入策略
  3. j>=w[i]时:v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]}
    当准备加入的新增的商品的容量小于等于当前背包的容量,
  4. 装入的方式:
    1. v[i-1][j]: 就是上一个单元格的装入的最大值
    2. v[i] :表示当前商品的价值
    3. v[i-1][j-w[i]] : 装入 i-1 商品,到剩余空间 j-w[i]的最大值
    4. j>=w[i]v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]} :

3)背包问题代码实现

package work.rexhao.algorithm.dynamic;

/**
 * 01背包问题
 */
public class KnapsackProblem {
   
    public static void main(String[] args) {
   
        // 1. 定义二维数组
        int knapsackSize = 4;    // 背包容量
        int goodsType = 3;  // 物品种类
        int[][] value = new int[knapsackSize + 1][goodsType + 1];   // dp价值表
        int[] goodsSize = {
   0, 1, 4, 3};    // 每个物品的大小
        int[] goodsValue = {
   0, 15, 30, 20};  // 每个物品的价值

        // 2. 第一行为空(空背包)且第一列为空(背包容量为0)

        // 3. 遍历表
        for (int i = 1; i < knapsackSize + 1; i++) {
   
            // i:背包容量
            for (int j = 1; j < goodsType + 1; j++) {
   
                // j:物品类型
                knapsackProblem(knapsackSize, i, j, value, goodsSize, goodsValue);
            }
        }

        // 3.1 打印表
        for (int i = 0; i < goodsType + 1; i++) {
   
            for (int j = 0; j < knapsackSize + 1; j++) {
   
                System.out.print(value[j][i] + "\t");
            }
            System.out.println();
        }
        System.out.println("--------------------");

        // 4. 输出答案
        int ans = 0;
        for (int[] ints : value) {
   
            for (int i : ints) {
   
                ans = Math.max(ans, i);
            }
        }
        System.out.println("总价值最大为: " + ans);
    }

    private static void knapsackProblem(int knapsackSize, int i, int j, int[][] value, int[] goodsSize, int[] goodsValue) {
   
        if (goodsSize[j] == i) {
   
            // 放一个正好放满 --> 放(这个或上一个品种)中价值更高的
            value[i][j] = Math.max(goodsValue[j], value[i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wmh1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值