算法与设计(王晓东第四版)第二章(递归与分治策略)上

文章探讨了Java中递归实现阶乘和分治策略在二分检索法、大整数乘法及Strassen矩阵乘法中的应用,强调了它们对时间效率的影响。同时,提到了棋盘覆盖问题的递归解决方案,展示了如何通过分治策略解决复杂问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递归:

阶乘:

import java.util.Scanner;
public class factorial {
    public static int factorialdemo(int n)
    {
        if(n==0)return 1;
        else return n*factorialdemo(n-1);
    }
    public static void main(String [] args)
    {
        Scanner sc = new Scanner(System.in);
        int a=sc.nextInt();
        int b=factorialdemo(a);
        System.out.println(b);
        sc.close();
    }
}

输出结果:

(1)

 (2)

(3) 

 

结论:

显然输入的原数据越大,操作时间越长

分治:

概念:将一个规模较大的问题,分成k个子问题,分别求出每个子问题的解,合并起来就变成了原问题的解。每个子问题的规模要大致相当,这样效率最高

当规模为n时的时间效率:

二分检索法:

public static int BinarySearch(int[]a,int x,int n) {
        int left=0;
        int right=n-1;
        while(left<=right)
        {
            int middle=(left+right)/2;
            if(x==a[middle])return middle;
            if(x>a[middle])left=middle+1;
            else right=middle-1;
        }
        return -1;
    }
    public static void main(String [] args)
        {
            int i,j,k,f;
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入母数组长度");
            i=sc.nextInt();
            int[]a=new int[i];
            System.out.println("请输入母数组");
            for(f=0;f<i;f++)
                a[f]=sc.nextInt();
            System.out.println("请输入要查找的元素");
            j=sc.nextInt();
            k=BinarySearch(a, j, i)+1;
            if(k>0)
                System.out.println("该元素在第"+k+"位上");
            else
                System.out.println("该元素不在数组中");
            sc.close();
        }

输出结果:

(1)处理内容较多时,查找时间差距较大

时间效率最好结果:

时间效率最坏结果:

 (2)处理内容较少时,查找时间差距不大

时间效率最好结果:

 时间效率最坏结果:

大整数的乘法:

设X,Y都是n位的二进制整数,计算XY,直接每位相乘需要进行O(n^{2})步运算,使用大整数乘积算法,可大大节省运算时间

原来XY=(A2^{n}+B)(C2^{n}+D)=AC2^{n}+(AD+CB)2^{n}+BD

时间效率为

现将XY改写为AC2^{n}+((A-B)(D-C)+AC+BD)2^{n/2}+BD,则现在只需要计算三次n/2位整数的乘法【AC,BD,(A-B)(D-C)】

此时时间效率为

大大减少了运算次数

 Strassen矩阵乘法:

定义矩阵C[I][j]= \sum_{k=1}^{n}A[I][k]B[I][j],计算的时间效率为O(n^{3}),设n为2的幂,A,B,C都可分成4个(n/2)*(n/2)的方阵,通过计算8个n/2阶方阵的乘积和4个n/2阶方阵的加法,时间效率为

该方法并没有提高效率,本质原因是没有减少乘法运算次数。Strassen提出了一种新算法,只用7次乘法,但增加了加减次数,运算方法如下


时间效率为

 计算时间复杂性有较大提升

棋盘覆盖:

在一个2^{k}*2^{k}(k>=0)方格组成的棋盘中,只有一个特殊方格和其他方格不同,特殊方格在棋盘上有4^{k}种情况,用L型骨牌覆盖除特殊方格外所有方格,且不能重叠,有(4^{k}-1)/3个骨牌,当k>0时,将棋盘分为四个2^{k-1}*2^{k-1}子棋盘,特殊方格必位于四个子棋盘之一中,用一个L型骨牌覆盖这三个字棋盘会和处,这样被L型骨牌覆盖的方格就成为该子棋盘中的特殊方格,就将原问题转化为四个规模较小的子问题,递归分割,直到棋盘简化为1*1的棋盘,问题就解决了


​​​​​​​

 代码实现:

public void chessBoard(int tr/*棋盘左上角方格行号*/,int tc/*棋盘左上角方格列号*/,int dr/*特殊方格行号*/,int dc/*特殊方格列号*/,int size/*棋盘规格 */)
    {
        int [][] board=new int [tr][tc];
        int tile=0;
        if(size==1)return;
        int t=tile++,//L型骨牌牌号
            s=size/2;//分割棋盘
        //覆盖左上棋盘
        if(dr<tr+s&&dc<tc+s)//特殊方格在此棋盘中
            chessBoard(tr,tc,dr,dc,s);
        else//此棋盘中无特殊方格
        {
            board[tr+s-1][tc+s-1]=t;//用t号L型骨牌覆盖右下角
            chessBoard(tr,tc,tr+s-1,tc+s-1,s);//覆盖其余方格
        }
        //覆盖右上棋盘
        if(dr>=tr+s&&dc<tc+s)
        chessBoard(tr,tc+s,dr,dc,s);
        else
        {
            board[tr+s-1][tc+s]=t;//用t号L型骨牌覆盖左下角
            chessBoard(tr,tc+s,tr+s-1,tc+s,s);
        }
        //覆盖左下棋盘
        if(dr>=tr+s&&dc>=tc+s)
        chessBoard(tr+s,tc,dr,dc,s);
        else
        {
            board[tr+s][tc+s-1]=t;//用t号L型骨牌覆盖右上角
            chessBoard(tr+s,tc,tr+s,tc+s-1,s);
        }
        //覆盖右下棋盘
        if(dr>=tr+s&&dc>=tc+s)
        chessBoard(tr+s,tc+s,dr,dc,s);
        else
        {
            board[tr+s][tc+s-1]=t;//用t号L型骨牌覆盖左上角
            chessBoard(tr+s,tc+s,tr+s,tc+s-1,s);
        }

主函数:

public static void main(String[]arg)
    {
        int rd,cd,size;
        System.out.println("请输入棋盘大小[应满足log2(size)为整数]");
        Scanner sc=new Scanner(System.in);
        size=sc.nextInt();
        int [][]chess=new int[size][size];
        System.out.println("请输入特殊方格位置");
        rd=sc.nextInt();
        cd=sc.nextInt();
        chess[rd][cd]=-1;
        chessBoard(0, 0, rd, cd, size,chess);
        System.out.println("棋盘:");
        for(int i=0;i<size;i++)
        {
            for(int j=0;j<size;j++)
                System.out.print(chess[i][j]+"\t");
            System.out.println();
        }
        sc.close();
    }

输出结果:

(1)数据量较小

(2)数据量较大

T(k)是该算法覆盖一个2^{k}*2^{k}棋盘所需时间

 T(k)=O(4^{k}​​​​​​​),该算法是一个在渐进意义下最优的算法

总结:

错误:

(1)System.out.println()输出一个结果占据一整行,如需多个数据输出在同一行,需用System.out.print()(与C++相似)

(2)java与C++区别之一是:java每个编写程序都必须规定其公用public(只有公用的数据可以在其他的程序中使用),或私有private属性(其他程序不能使用);

java中输入Scanner sc = new Scanner(System.in);int a=sc.nextInt();将输入的值赋给a

C++中输入cin>>a;

java中输出System.out.println("用时"+time+"纳秒");

C++中输入cout<<a;

(3)计时操作:

用long millis1=System.nanoTime()放在操作之前,该语句可获取当前的时间(精确到纳秒),将long millis2=System.nanoTime();放在操作之后,再次获取一个时间,最后两时间相减long time=millis2-millis1;就可以得到进行操作运算的具体时间

总结:

学习了java的基础编写,并在VScode上运用和熟悉相关操作,体会了各编写语言之间的差异。学习了java中递归的概念操作,和部分分治策略的编写思路

书名:算法设计分析 作者:王晓东 图书目录 第1章 算法引论 1.1 算法程序 1.2 表达算法的抽象机制 1.3 描述算法 1.4 算法复杂性分析 小结 习题 第2章 递归分治策略 2.1 速归的概念 2.2 分治法的基本思想 2.3 二分搜索技术 2.4 大整数的乘法 2.5 Strassen矩阵乘法 2.6 棋盘覆盖 2.7 合并排序 2.8 快速排序 2.9 线性时间选择 2.10 最接近点对问题 2.11 循环赛日程表 小结 习题 第3章 动态规划 3.1 矩阵连乘问题 3.2 动态规划算法的基本要素 3.3 最长公共子序列 3.4 凸多边形最优三角剖分 3.5 多边形游戏 3.6 图像压缩 3.7 电路布线 3.8 流水作业调度 3.9 0-1背包问题 3.10 最优二叉搜索树 小结 习题 第4章 贪心算法 4.1 活动安排问题 4.2 贪心算法的基本要素 4.2.1 贪心选择性质 4.2.2 最优子结构性质 4.2.3 贪心算法动态规划算法的差异 4.3 最优装载 4.4 哈夫曼编码 4.4.1 前缀码 4.4.2 构造哈夫曼编码 4.4.3 哈夫曼算法的正确性 4.5 单源最短路径 4.5.1 算法基本思想 4.5.2 算法的正确性和计算复杂性 4.6 最小生成树 4.6.1 最小生成树性质 4 6.2 Prim算法 4.6.3 Kruskal算法 4.7 多机调度问题 4.8 贪心算法的理论基础 4.8.1 拟阵 4.8.2 带权拟阵的贪心算法 4.8.3 任务时间表问题 小结 习题 第5章 回溯法 5.1 回溯法的算法框架 5.1.1 问题的解空间 5.1.2 回溯法的基本思想 5.1.3 递归回溯 5.1.4 迭代回溯 5.1.5 子集树排列树 5.2 装载问题 5.3 批处理作业调度 5.4 符号三角形问题 5.5 n后问题 5.6 0-1背包问题 5.7 最大团问题 5.8 图的m着色问题 5.9 旅行售货员问题 5.10 圆排列问题 5.11 电路板排列问题 5.12 连续邮资问题 5.13 回溯法的效率分析 小结 习题 第6章 分支限界法 6.1 分支限界法的基本思想 6.2 单源最短路径问题 6.3 装载问题 6.4 布线问题 6.5 0-1背包问题 6.6 最大团问题 6.7 旅行售货员问题 6.8 电路板排列问题 6.9 批处理作业调度 小结 习题 第7章 概率算法 7.1 随机数 .2 数值概率算法 7.2.1 用随机投点法计算л值 7.2.2 计算定积分 7.2.3 解非线性方程组 7.3 舍伍德算法 7.3.1 线性时间选择算法 7.3.2 跳跃表 7.4 拉斯维加斯算法 7.4.1 n后问题 7.4.2 整数因子分解 7.5 蒙特卡罗算法 7.5.1 蒙特卡罗算法的基本思想 7.5.2 主元素问题 7.5.3 素数测试 小结 习题 第8章 NP完全性理论 8.1 计算模型 8.1.1 随机存取机RAM 8.1.2 随机存取存储程序机RASP 8.1.3 RAM模型的变形简化 8.1.4 图灵机 8.1.5 图灵机模型RAM模型的关系 8.1.6 问题变换计算复杂性归约 8.2 P类NP类问题 8.2.1 非确定性图灵机 8.2.2 P类NP类语言 8.2.3 多项式时间验证 8.3 NP完全问题 8.3.1 多项式时间变换 8.3.2 Cook定理 8.4 一些典型的NP完全问题 8.4.1 合取范式的可满足性问题 8.4.2 3元合取范式的可满足性问题 8.4.3 团问题 8.4.4 顶点覆盖问题 8.4.5 子集和问题 8.4.6 哈密顿回路问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值