【韩老师零基础30天学会Java 04】cloneable 递归 阶乘 斐波那契数列 猴子吃桃 老鼠出迷宫 汉罗塔 八皇后(仅介绍)

方法的使用

子方法置null主方法不影响

public class MyTest01 {
    public static void main(String[] args) {

        A a = new A();
        a.name = "111";

        a.temp(a);

        System.out.println(a.name);
    }
}

class A {
    String name;

    public void temp(A b) {
        b.name = "222";
        b = null;
    }
}
  • a.name = 222
  • 因为:temp(A b) 是一个全新的变量,只是和 实际参数 相同的指向,
    • 改变:b.name = “222”; 形参也变。
    • 但是:把b 变量,指向null 和 调用无关。代码中 一定别这样写。

子方法new主方法的值不变

    public void temp(A a) {
        a = new A();
        a.name = "333";
    }
  • A的Name结果 依然是 111。
    • new 了后,就是一个 新的指向了,和 形参a无关了。

Cloneable接口

public class MyTest01 {
    public static void main(String[] args) {

        A a = new A();
        a.name = "111";

        System.out.println(a.name);
        //内存地址 不相同:A@75bd9247
        System.out.println(a);
        
        A a1 = a.myClone();
        System.out.println(a1.name);
        
        System.out.println(a1);
    }
}

class A implements Cloneable {
    String name;

    /*@Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }*/

    public A myClone() {
        try {
            //不继承 clone 无法调用 clone方法
            Object clone = this.clone();
            return (A) clone;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

递归

简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变
得简洁

各种数学问题如:8皇后问题,汉诺塔,阶乘问题
,迷宫问题,球和篮子的问题

  • 汉诺塔,大板子放在下面,每次只能移动一个盘子。

(google编程大赛)[简单演示]

2.各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.

八皇后问题说明
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

有92个解

·执行一个方法时,就创建一个新的受保护的独立空间(栈空间)方法的局部变量是独立的,不会相互影响,比如n变量
3.如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引
用类型的数据.
4.递归必须向退出递归的条件逼近,否则就是无限递归,出现
StackOverflowError,死龟了:)
5.当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就
将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

阶乘递归实现

		int jc = jc(3);
        System.out.println(jc);

    //2 2 * f(1)
    //3 3 * f(2),然后在调用上面
    //边界条件,前阶段(不停的调用自己),返回阶段。
    public static int jc(int n) {
        if (n == 1) {
            return 1;
        } else {
            return n * jc(n - 1);
        }
    }

//factorial 阶乘

递归简单的输出

public class RecursionTest {
    public static void main(String[] args) {
        T t = new T();
        t.test(4);
    }
}

class T {
    //分析
    public void test(int n) {
        if (n > 2) {
            System.out.println("我的输出" + n);
            test(n - 1);
        }
        System.out.println("n=" + n);
    }
}
我的输出4 //大于2,先输出
我的输出3 //-1后为3,又调用递归的方法输出。

n=2 //3-1为2,2时 直接输出 n=2
n=3 //返回上一步 n为3的情况。
n=4 //在返回一步,n为4的情况。

斐波那契

1.请使用递归的方式求出斐波那契数1,1,2.3.5,8,13…给你一个整数n,求出它的值是多

使用数组保存
  		int n = 5;
        //因为数组0位置是0,所以要多创建一个
        int[] ints = new int[n + 1];
        
        //斐波那契数组赋值
        feiBoNaQie(ints, ints.length - 1);//就是5
        //打印数组
        System.out.println(Arrays.toString(ints));

        //你要打印的某个值,就是数组的 最后一个值
        System.out.println(ints[n]);

    public static void feiBoNaQie(int[] arr, int index) {

        if (index == 1) {
            //1索引
            arr[1] = 1;
        } else {
            //不为1,就继续递归。
            feiBoNaQie(arr, index - 1);
            //递归后,就是为 先执行1了。1索引执行过了。
            //当前索引为 前2个索引 + 前一个索引的 和。
            arr[index] = arr[index - 2] + arr[index - 1];
        }
    }
求第几个数
        int i = 5;

        int j = fibonacci(7);

        System.out.println(j);

    /*
    请使用递归的方式求出斐波那契数 1,1,2,3,5,8,13...给你一个整数 n, 求出它的值是多
    思路分析
    1. 当 n = 1 斐波那契数 是 1
    2. 当 n = 2 斐波那契数 是 1
    3. 当 n >= 3 斐波那契数 是前两个数的和
    4. 这里就是一个递归的思路
    */
    public static int fibonacci(int n) {
        if (n >= 1) {
            //如果 是第一个值 或 第二个值,就赋值为1
            if (n == 1 || n == 2) {
                return 1;
            } else {
                //否则就是:前两个斐波那契 数的和 (前2个数 和 前1个数)
                return fibonacci(n - 1) + fibonacci(n - 2);
            }
            
        } else {
            System.out.println("要求输入的 n>=1 的整数");
            return -1;
        }
    }

猴子吃桃

猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第10天时,想再吃时(即还没吃),发现只有1个桃子了。问题:最初共多少个桃子?

第一天剩余: X/2-1

第二天剩余:( X/2-1)/ 2 -1

反推等差数列

1 4 10 22 46 94 190 382 766 1534

  • 规律为:(前一个数+1 )* 2
    public static void peach2() {
        //1 4 10
        //(1+1) * 2 = 4;

        //假如第一天为1
        int x = 1;

        //在求9天。第10天为 1534
        for (int i = 1; i < 10; i++) {
            x = (x + 1) * 2;
            System.out.print(x+" ");
        }
    }
        //最初是第一天,桃子的数量。
        int n = 1;
        //方法里 要传递 第10天
        System.out.println(houZi1(11 - n));
    public static int houZi1(int i) {
        if (i == 1) {
            return 1;
        }
        return (houZi1(i - 1) + 1) * 2;
    }
递归编写
        //桃子问题
        int day = 1;
        int peachNum = peach(day);
        if (peachNum != -1) {
            System.out.println("第 " + day + "天有" + peachNum + "个桃子");
        }

    /*
    以后每天猴子都吃其中的一半, 然后再多吃一个。
    当到第 10 天时,想再吃时(即还没吃) , 发现只有 1 个桃子了

    思路分析 逆推
    1. day = 10 时 有 1 个桃子。在第9天的基础上,吃了2个,又吃了1个,所以剩1个。
    2. day = 9 时 有 (day10 + 1) * 2 = 4。在第8天的基础上,吃了5个,又吃了1个,所以剩4个。
    3. day = 8 时 有 (day9 + 1) * 2 = 10。
    4. 规律就是 前一天的桃子 = (后一天的桃子 + 1) *2//就是我们的能力
    5. 递归
    */
    public static int peach(int day) {
        if (day == 10) {//第 10 天, 只有 1 个桃
            return 1;
        } else if (day >= 1 && day <= 9) {
            return (peach(day + 1) + 1) * 2;//规则
        } else {
            System.out.println("day 在 1-10");
            return -1;
        }
    }

老鼠出迷宫

1.小球得到的路径,和程序员设置的找路策略有
关即:找路的上下左右的顺序相关
2.再得到小球路径时,可以先使用(下右上左),
再改成(上右下左),看看路径是不是有变化3.测试回溯现象
4.扩展思考:如何求出最短路径?1

绘图
[1, 1, 1, 1, 1, 1, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 1, 1, 1, 1, 1, 1]
        int[][] arr = new int[8][7];

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                //如果是首行 或末尾行。首次索引 或 末尾索引,就赋值为1
                if (i == 0 || i == arr.length - 1 ||
                        j == 0 || j == arr[i].length - 1) {
                    arr[i][j] = 1;
                }
            }
        }

        for (int i = 0; i < arr.length; i++) {
            System.out.println(Arrays.toString(arr[i]));
        }

//第二种方式

        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //1到6索引
        for (int i = 1; i <= 6; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }
		
		//31 和 32 画障碍物
        map[3][1] = 1;
        map[3][2] = 1;
核心递归方法
//老韩解读
//1. findWay 方法就是专门来找出迷宫的路径
//2. 如果找到, 就返回 true ,否则返回 false
//3. map 就是二维数组, 即表示迷宫
//4. i,j 就是老鼠的位置, 初始化的位置为(1,1)
//5. 因为我们是递归的找路, 所以我先规定 map 数组的各个值的含义
// 0 表示可以走(未走过) 1 表示障碍物 2 表示可以走(通路) 3 表示走过, 但是走不通是死路
//6. 当 map[6][5] =2 就说明找到通路,就可以结束, 否则就继续找.
//7. 先确定老鼠找路策略 下->右->上->左

    public static boolean findWay(int[][] map, int i, int j) {
        if (map[6][5] == 2) {//说明已经找到
            return true;
        } else {
            if (map[i][j] == 0) {//当前这个位置 0,说明表示可以走
                //我们假定可以走通。这个路径不可用,下面会改为3
                map[i][j] = 2;
                //使用找路策略, 来确定该位置是否真的可以走通
                //下->右->上->左
                if (findWay(map, i + 1, j)) {//先走下
                    return true;
                } else if (findWay(map, i, j + 1)) {//右
                    return true;
                } else if (findWay(map, i - 1, j)) {//上
                    return true;
                } else if (findWay(map, i, j - 1)) {//左
                    return true;
                } else {
                    //表示走过, 但是走不通是死路。上面假定 是错误的。我走失败了,不玩了。
                    map[i][j] = 3;
                    return false;
                }
            } else { //map[i][j] = 1 , 2, 3
                //为2时,这个点已经测试过了 返回false。
                return false;
            }
        }
    }
        map[3][1] = 1;
        map[3][2] = 1;

        findWay(map, 1, 1);
[1, 1, 1, 1, 1, 1, 1]
[1, 2*, 0, 0, 0, 0, 1]
[1, 2, 2, 2, 0, 0, 1]
[1, 1, 1, 2, 0, 0, 1]
[1, 0, 0, 2, 0, 0, 1]
[1, 0, 0, 2, 0, 0, 1]
[1, 0, 0, 2, 2, 2*, 1]
[1, 1, 1, 1, 1, 1, 1]
//老鼠的起始位置 和 结束位置增加了*号
扩展
//上->右->下->左
if(findWay2(map, i - 1, j)) {//先走上
	return true;
} else if(findWay2(map, i, j + 1)){//右
	return true;
} else if(findWay2(map, i+1, j)) {//下
	return true;
} else if(findWay2(map, i, j-1)){//左
	return true;
}
[1, 1, 1, 1, 1, 1, 1]
[1, 2*, 2, 2, 2, 2, 1]
[1, 0, 0, 0, 0, 2, 1]
[1, 1, 1, 0, 0, 2, 1]
[1, 0, 0, 0, 0, 2, 1]
[1, 0, 0, 0, 0, 2, 1]
[1, 0, 0, 0, 0, 2*, 1]
[1, 1, 1, 1, 1, 1, 1]
回溯现象
        map[2][2] = 1;
[1, 1, 1, 1, 1, 1, 1]
[1, 2*, 2, 2, 2, 2, 1]
[1, 0, 1, 0, 0, 2, 1]
[1, 1, 1, 0, 0, 2, 1]
  • 结果如下:
    • 2调用下面的时候,返回的结果为 false,那又往 右边查询。所以 还是会找到对的路的
[1, 1, 1, 1, 1, 1, 1]
[1, 2, 2, 2, 0, 0, 1]
[1, 3, 1, 2, 0, 0, 1]
[1, 1, 1, 2, 0, 0, 1]
[1, 0, 0, 2, 0, 0, 1]
[1, 0, 0, 2, 0, 0, 1]
[1, 0, 0, 2, 2, 2, 1]
[1, 1, 1, 1, 1, 1, 1]
  • 使用图 求出最短路径,深度优先,广度优先 遍历方式。

  • 弗洛伊德算法求出最短距离

汉罗塔

√汉诺塔传说
汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

假如每秒钟移动一次,共需多长时间呢?移完这些金片需要5845.54亿年以上,太阳系的预期寿命据说也就是数百亿年。真的过了
5845.54亿年,地球上的一切生命,连同梵塔、
庙宇等,都早已经灰飞烟灭

代码
        move(4, 'A', 'B', 'C');

    //方法
    //num 表示要移动的个数, a, b, c 分别表示 A 塔, B 塔, C 塔
    public static void move(int num, char a, char b, char c) {

        //如果只有一个盘 num = 1
        if (num == 1) {
            System.out.println(a + "->" + c);
        } else {
            //如果有多个盘, 可以看成两个 , 最下面的和上面的所有盘(num-1)

            //(1)先移动上面所有的盘到 b, 借助 c
            //把上面所有的盘(除了最后一个),看做是一个盘。
            move(num - 1, a, c, b);

            //(2)把最下面的这个盘, 移动到 c
            System.out.println(a + "->" + c);

            //(3)再把 b 塔的所有盘, 移动到 c (因为其中一个已经移动到C了,所以num-1),借助 a
            move(num - 1, b, a, c);
        }
    }

八皇后问题

√八皇后问题说明
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

√八皇后思路分析

1)第一个皇后先放第一行第一列
2)第二个皇后放在第二行第一列、然后判断是否OK,如果不OK,继
续放在第二列、第三列、依次把所有列都放完,找到一个合适
3)继续第三个皇后,还是第一列、第二列.……直到第8个皇后也能放
在一个不冲突的位置,算是找到了一个正确解
4)当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即
将第一个皇后,放到第一列的所有正确解,全部得到.
5)然后回头继续第一个皇后放第二列,后面继续循环执行1,2.3.4的
步骤【示意图】
说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通
过算法,用一个一维数组即可解决问题. arr[8] ={0,4,7,5, 2, 6,1,3}//对应arr下标表示第几行,即第几个皇后,arr[i] = val , val表
示第i+1个皇后,放在第i+1行的第val+1列

[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]

arr[8] ={0,4,7,5, 2, 6,1,3}

横0-7索引,竖0-7索引。
第一行:放在第0个索引位置
第二行:放在第4个索引位置

[1, 0, 0, 0, 0, 0, 0, 0]	1
[0, 0, 0, 0, 1, 0, 0, 0]	5
[0, 0, 0, 0, 0, 0, 0, 1]	8
[0, 0, 0, 0, 0, 1, 0, 0]	6
[0, 0, 1, 0, 0, 0, 0, 0]	3
[0, 0, 0, 0, 0, 0, 1, 0]	7
[0, 1, 0, 0, 0, 0, 0, 0]	2
[0, 0, 0, 1, 0, 0, 0, 0]	4
158 637 24
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java的`Cloneable`接口是一个标记接口,用于指示实现类可以被克隆。它并不包含任何方法,只是作为一个标记,告诉编译器该类可以被克隆。 要实现`Cloneable`接口,只需在类的声明添加`implements Cloneable`,然后重写`Object`类的`clone()`方法。 以下是一个示例: ```java public class Person implements Cloneable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } public static void main(String[] args) { Person person1 = new Person("Alice", 25); try { Person person2 = (Person) person1.clone(); System.out.println(person2.name); // 输:Alice System.out.println(person2.age); // 输:25 } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } ``` 在上面的示例,`Person`类实现了`Cloneable`接口,并重写了`clone()`方法。在`main()`方法,我们创建了一个`Person`对象`person1`,然后使用`clone()`方法创建了一个新的对象`person2`。最后,我们打印了新对象的属性,验证克隆是否成功。 需要注意的是,使用`clone()`方法进行对象的浅拷贝(shallow copy),即基本数据类型会被复制,而引用类型只是复制了引用,指向同一块内存。如果需要实现深拷贝(deep copy),需要在`clone()`方法对引用类型进行逐个复制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值