倒序之递归方法与递归分形

为什么说是倒序呢,因为其实由一个简单的界面开发再到界面上的递归分形之间还有一些过渡的内容,但今天这篇我们先跳过它们先来了解所谓的"递归"究竟是什么。

一、递归案例

1.引例与分析

public class test{
    public void add(int i){
    //递归方法
        if(i>30){
            return
            //退出
        }
    System.out.println("递归"+i);
    i++;
    add(i)
    }
    public void main (String[] args){
    test t0 = new test();
    t0.add(1);
    //这里数字填几,最终递归+i就从几开始往后
    }
}

由于递归是一个全新的概念,我们从一个案例代码开始着手分析。

首先,解释一下add方法。add方法是一个递归方法,它接受一个整数参数i。在方法内部,首先通过一个条件判断,判断i是否大于3。如果满足条件,就会执行return语句,即结束当前方法的执行。这个return语句的作用是退出方法。需要注意的是,return语句后面的语句将永远不会执行,因为它是一个无法到达的语句(Unreachable statement)。

如果条件不满足,那么会继续执行下面的语句。接着会打印一个字符串"递归方法"加上参数i的值。然后将参数i自增1,再次调用add方法。这个地方是递归调用的关键。通过递归调用,add方法会不断地调用自身,直到满足终止条件。

在main方法中,我们创建了一个test对象t0。然后通过调用t0的add方法并传入参数1,来启动整个递归调用过程。最终,当i的值大于30时,add方法会结束,整个程序执行完毕。

2.知识回顾:如何创建一个新的方法

在Java中,您可以使用以下语法来创建一个新的方法:

访问修饰符 返回类型 方法名(参数列表) {

    // 方法的实现代码

}

访问修饰符定义谁可以访问该方法。常见的访问修饰符有:`public`、`private`、`protected` 等等

返回类型是指方法执行后返回的数据类型。如果方法不返回任何值,可以使用 `void` 关键字。

方法名是个人给方法起的名字,可以根据需求自由命名。

参数列表是传递给方法的参数,在方法名称后面用括号括起来。还可以指定每个参数的类型名称

下面是一个示例:

public void greet() {

    System.out.println("Hello, world!");

}

以上示例创建了一个名为 "greet" 的方法,该方法不接受任何参数,且返回类型为 void。方法体(方法的实现部分)中的代码将打印出 "Hello, world!"。

要调用这个方法,只需要在代码的其他地方使用方法名并添加括号即可:

greet();

以上是递归的基本方法,下面开始正式进入递归分形。

二、有关递归分形的基本代码

首先需要套用一个画图的模板(由一个绘图界面和画笔监听器组成),如何利用java实现画图板我会在下篇介绍。

1.画图板代码

先给出一个绘图界面的基本代码:

public class RecursionFractalUI {
    public void RecursionFractalshowUI(){

        JFrame jf = new JFrame();
        FlowLayout fl = new FlowLayout();

        Pencil pencil = new Pencil();
        jf.addMouseListener(pencil); //鼠标监听器

        jf.setLayout(fl);
        jf.setTitle("全能画图板");
        jf.setVisible(true);
        jf.setSize(1600,1300);
        jf.setLocation(100,100);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Graphics g = jf.getGraphics(); //画笔
        pencil.g=g;

        JButton jbt = new JButton("递归分形");
        jf.add(jbt);
        jbt.addActionListener(pencil);//为按钮添加一个监听器,pencil是监听器对象

        }
    public static void main (String[]args){
        RecursionFractalUI pencilUI= new RecursionFractalUI();
        pencilUI.RecursionFractalshowUI();
    }
}

对所创建的窗体的部分代码含义进行解释(逻辑)

  Pencil pencil = new Pencil();
        jf.addMouseListener(pencil);

Graphics g = jf.getGraphics(); //画笔
        pencil.g=g;

        JButton jbt = new JButton("递归分形");
        jf.add(jbt);
        jbt.addActionListener(pencil)

1.创建了一个名为 "pencil" 的 Pencil 对象,并将其赋值给一个名为 "pencil" 的变量。

2. 将 "pencil" 对象作为鼠标监听器(MouseListener)添加到名为 "jf" 的对象上。这意味着 "pencil" 对象将接收鼠标事件并相应地执行相应的操作。

3. 使用 jf.getGraphics() 方法获取了一个 Graphics 对象,并将其赋值给名为 "g" 的变量。Graphics 类提供了一组用于绘图的方法,可以在屏幕或其他图形设备上进行绘制操作。

4. 将 "g" 对象赋给了 "pencil" 对象的一个成员变量 "g","pencil" 对象就可以使用 "g" 对象进行绘图操作。

5. 创建了一个名为 "jbt" 的 JButton 对象,并将其文本设置为 "递归分形"。

6. 将 "jbt" 对象添加到 "jf" 对象中。

7. 为 "jbt" 按钮添加了一个 ActionListener,这意味着当用户点击该按钮时,"pencil" 对象将执行相应的操作。

2.监听器代码

这是一个监听器部分的代码,注意被注释掉的部分为相对比较进阶的内容,在这里不进行过多阐释,有兴趣的可以自己去看看。

public class Pencil implements ActionListener, MouseListener{
    //实现动作,鼠标监听器的接口
    int x1,y1,x2,y2;
    Graphics g;
    String name=" ";


    public void actionPerformed(ActionEvent e) {
        System.out.println("---*点击了按钮!");

      String btname = e.getActionCommand();
        System.out.println(btname);
        name=btname;//按钮文本传给全局变量
        }



        //int count = 0;
        public void mouseClicked(MouseEvent e){
        System.out.println("点击");//点击的同时获取坐标
            int x =e.getX();
            int y =e.getY();

        }

        //Random random = new Random();
        public void simplifyRect(int x,int y,int w,int h){
            if(w<10||h<10){
                return;
            }
        g.drawRect(x,y,w,h);//x,y,w,h,实心,空心
            //g.setColor(new Color(random.nextInt(Integer.MAX_VALUE)));//随机颜色
        g.fillRect(x+w/3,y+h/3,w/3,h/3);
        simplifyRect(x,y,w/3,h/3);
        simplifyRect(x+w/3,y,w/3,h/3);
        simplifyRect(x+2*w/3,y,w/3,h/3);
        simplifyRect(x,y+h/3,w/3,h/3);
        simplifyRect(x+2*w/3,y+h/3,w/3,h/3);
        simplifyRect(x,y+2*h/3,w/3,h/3);
        simplifyRect(x+w/3,y+2*h/3,w/3,h/3);
        simplifyRect(x+2*w/3,y+2*h/3,w/3,h/3);
        }

        /*public void Triangle(int x1, int y1,int x2,int y2,int x3,int y3){
            if(x3-x2<20){
                return;
            }
            g.drawLine(x1, y1, x2, y2);
            g.drawLine(x2,y2,x3,y3);
            g.drawLine(x1,y1,x3,y3);
            int mx1 = (x1+x2)/2;
            int my1 = (y1+y2)/2;
            int mx2 = (x3+x1)/2;
            int my2 = (y3+y1)/2;
            int mx3 = (x3+x2)/2;
            int my3 = (y2+y3)/2;
            g.drawLine(mx1,my1,mx3,my3);
            g.drawLine(mx2,my2,mx3,my3);
            g.drawLine(mx1,my1,mx2,my2);
            Triangle(x1,y1,mx1,my1,mx2,my2);
            Triangle(mx1,my1,x2,y2,mx3,my3);
            Triangle(mx2,my2,mx3,my3,x3,y3);

        }*/

        public void mousePressed(MouseEvent e){
        System.out.println("按下");
            int x=e.getX();
            int y=e.getY();
            System.out.println("x;"+x+"y:"+y);
            x1=x;
            y1=y;
        }


        public void mouseReleased(MouseEvent e){
        System.out.println("释放");
            int x=e.getX();
            int y=e.getY();
            System.out.println("x;"+x+"y:"+y);
            x2=x;
            y2=y;
            if(name.equals("递归分形")){
                simplifyRect(x1,y1,x2-x1,y2-y1);
            //Triangle(x1,y1,x1-150,y1+300,x1+150,y1+300);
            }
        }


        public void mouseEntered(MouseEvent e){
        System.out.println("进入");
        }


        public void mouseExited(MouseEvent e){
        System.out.println("退出");
        }

}

下面对监听器部分的代码进行详细解释:

1.首先实现动作监听器和鼠标监听器的接口,定义全局变量x1,y1,x2,y2和画笔g

public class Pencil implements ActionListener, MouseListener{
    int x1,y1,x2,y2;
    Graphics g;

2.当有动作事件发生时该方法会被调用,比如我们点击按钮的时候,通过该方法动作监听器可以知道我们对按钮做什么

public void actionPerformed(ActionEvent e) {
        System.out.println("---*点击了按钮!");

3.长按CTRL点击MouseListener会出现有关鼠标的五种方法复制并改写成如下格式

这些方法分别用来处理鼠标点击、鼠标按下、鼠标释放、鼠标进入组件以及鼠标离开组件时触发的事件。

这些方法分别用来处理鼠标点击、鼠标按下、鼠标释放、鼠标进入组件以及鼠标离开组件时触发的事件。

这些方法的参数是一个名为"e"的MouseEvent鼠标事件对象,它包含了与鼠标事件相关的信息,例如鼠标在界面上的坐标等。

public void mouseClicked(MouseEvent e){  //点击     

public void mousePressed(MouseEvent e){   //按下   

public void mouseReleased(MouseEvent e){   //释放  

public void mouseEntered(MouseEvent e){    //进入  

public void mouseExited(MouseEvent e){      //离开

三、递归与递归分形

1.对递归图形的观察与初步分析

这是监听器部分的框架,下面就该完善扩充框架部分了,现在界面已经可以捕捉到我们在利用鼠标进行什么操作了,那我们就可以在方法下添加一些具体操作了,比如我们用鼠标点击后松开可以实现画图功能,那画什么图呢-------那必然是谢宾斯基地毯了,请看下图(图例给的是正方形,但其实只要是矩形皆可,实心空心自己定义)

从左到右分别为图一至图四, 图一我们先画一个空心矩形利用g.drawRect(坐标1,2,3,4),这里我们假设为(x,y,w,h),其中x和y为点击时鼠标的坐标可以当作矩形的一个顶点,然后在我们拖动的过程中相当于给这个矩形设置宽w和高h,最后松开鼠标可以得到一个任意的矩形(如下图)

图一完成后我们去观察图二,发现图二中心的矩形就是在图一的基础上取长和宽的三等分点的中间部分构成一个大小为原来1/9的的相似矩形,这是我们知道绘图方法是g.fillRect(1,2,3,4),但是这里需要我们注意的是新坐标的描述,仍然以左上角的顶点为起点则该起点的坐标为(x+w/3,y+h/3,width:w/3,height:h/3)

至此我们完成了谢宾斯基地毯的第一步,可到目前为止我们还没有用到递归方法,那我们不妨去看看图三,图三在图二的基础上又多出来8个小矩形,那么我们是否要按着刚才的方式用g.fillRect()的方法来再写8次呢,或许可以,但如果再看图四呢?那根本要写晕了,所以很显然我们递归分形的思维就是要在图二产生至图三乃至图四的过程中运用到了

2.递归的本质

在这里再次重申递归的定义:

递归是一种直接或间接调用自身函数的算法。递归的实质把问题分解成规模更小同类问题来解决。

递归算法的特点:

1.函数直接或间接调用自己。

2.必须有明确的递归结束条件。

那在我们进行递归分形时,递归思想就将问题不断的转化为与原问题性质相同的子问题,直到这个问题规模小到能被我们解决为止。(这里的理解相当于从图四到图一的一个过程,像是一种逆向思维化繁为简。而正向思维在上面我们已经有了从图一过渡到图二的一个过程,逆向思维有递归为我们提供有办法将图四简化成图三的思路,剩下我们需要思考的就剩如何将二者结合了)

所以,需要观察!!!!!!!

3.发现递归的蛛丝马迹

撇开图四,观察图三和图二和图四(更多的是图四)的差异(多出来的8个小矩形),只要能看出图三到图四是怎么递归的,一切问题就可以迎刃而解了

                                      拼图1号

拼图2号*8块

谜底揭晓:我们将图二看成一个整体设为拼图2号(有点类似数学里的换元法),用拼图2号去填充拼图1号填充8次即可得到图三,至于图四则是将拼图2号按比例缩小后填充至图三的空白处之中,根据递归结束条件我们可以得到不同递归次数的谢宾斯基地毯

4.在java语言下的递归

基本思路有了,下面该该考虑如何将这些思路转化为java语言

public void recursiveRect(int x,int y,int w,int h){里放一些递归结束的条件,无限递归 必定会产生栈内存溢出错误 } //创建一个递归矩形新方法

//下面进行8次递归调用(要调用这个方法,只需要在代码的其他地方使用方法名并添加括号和分号即可)

recursiveRect(x,y,w/3,h/3);//1
recursiveRect(x+w/3,y,w/3,h/3);//2
recursiveRect(x+2*w/3,y,w/3,h/3);//3
recursiveRect(x,y+h/3,w/3,h/3);//4
recursiveRect(x+2*w/3,y+h/3,w/3,h/3);//5
recursiveRect(x,y+2*h/3,w/3,h/3);//6
recursiveRect(x+w/3,y+2*h/3,w/3,h/3);//7
recursiveRect(x+2*w/3,y+2*h/3,w/3,h/3);//8

最后再添加些终止return条件,利用if(){},如果宽和高过小就不进行递归

完整版代码可参考2.1和2.2

  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值