1.List 是一个集合的接口。允许存在重复的元素,主要有两种实现类。
ArrayList 与 LinkedList
2.一些常用的方法:
List<Integer> list = new ArrayList<>;
//向集合中添加元素
list.add(element);
//获取元素
list.get(index);
//获取集合的长度
list.size();
//删除集合的元素
list.remove(index);
//清空集合中的元素
list.clear();
//判断集合是否为空
list.isEmpty();
核心思想:
(1)汉诺塔问题是一个经典的递归问题,属于分治算法的一种。
(2)将n个盘子从某个柱子上移动到指定的柱子上,将其变为将n-1个盘子移动到辅助的柱子上,把最后一个盘子直接移动到目标柱上。
(3)此时问题的规模就缩小到了这 n-1 个盘子移动到目标数上。
(4)问题的规模为: H(n) = H(n - 1) + 1 + H(n - 1)
代码部分:
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
//获取源柱上盘子的个数[对于获取集合中元素的个数通过size方法来完成]
int n = A.size();
//调用移动盘子的方法
move(n, A, B, C);
}
public void move(int n, List<Integer> A, List<Integer> B, List<Integer> C){
if(n == 0){
return;
}
//将n-1个盘子移动到辅助柱上
move(n-1, A, C, B);
C.add(0, A.remove(0));
//再将n-1个盘子移动到目标柱上
move(n-1, B, A, C);
}
}
对于代码的执行流程与内存机制分析:
(1)对于递归调用一次,就会生成一个方法栈。
(2)当递归结束后,会返回并销毁方法栈。在返回时会执行整个方法。
(3)具体的执行流程:
假设有3个盘子,有A、B、C三个柱子
(1)在第一次调用move()方法时,对其中两个盘子进行递归处理
move(2, a, c, b)
第二次调用move()方法时,再对一个盘子进行处理
move(1, a, b, c)
第三次调用move()方法时,再次进入递归调用
move(0, a, c, b)
此时 n == 0, 递归调用结束。
(2)函数返回,继续执行下面的代码
n = 1时,此时会执行
C.add(0, A.remove(0));
A = a, B = b, C = c 此时执行该代码会让A盘最上面的盘子放到目标柱C上
继续执行下面对剩余n - 1个在原来辅助柱上的盘子移动到目标柱上
move(0, b, a, c) 执行到上面的递归调用,因为n = 0时什么都不会返回,在接下来也不对其考虑了。
函数继续返回,返回 n = 2时,此时会执行
C.add(0, A.remove(0));
A = a, B = c, C = b,此时执行该代码会让A盘最上面的盘子放到目标柱B上
继续执行下面的递归调用,move(1, c, a, b),进入move方法中还会递归调用一次n= 0,所以不展开讨论,继续向下执行,此时会执行C.add(0, A.remove(0));
,将C柱最上边的盘子移动到B柱上,然后执行下面的递归调用,此时传入的参数也为0了,所以不返回任何信息
递归继续返回,返回到move(3, a, b, c),此时会执行
C.add(0, A.remove(0));
, A = a、B = b、C = c,所以就将A柱最上边的盘子移动到C柱上。
执行下面的递归调用remove(2, b, a, c),进入上边的递归调用remove(1, b, c, a) 再次执行调用传入的参数为0了,所以从参数为1开始返回,此时会执行C.add(0, A.remove(0));
,代表将B柱最上边的移动到A柱上,执行下边的递归调用时参数为0了,所以继续返回。
此时参数为2,执行此时会执行C.add(0, A.remove(0));
,代表将B柱最上边的盘子移动到C柱上,执行下边的的递归调用remove(1, a, b, c),此时执行上边的递归调用,参数为零不考虑,之后会执行C.add(0, A.remove(0));
,代表将A柱上边的盘子移动到C柱上。
此时整个递归程序就执行并返回完毕啦