Java 递归算法的相关实现
递归本质:程序调用自身的编程技巧叫做递归。
递归的三个条件:1.边界条件 2.递归前进段 3.递归返回段
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
递归是用栈机制实现的,每深入一层,都要占去一块栈的数据区域。
注意:
1.递归一定要有条件限定,保证递归可以能够停下来,否则会形成死循环并发生栈内存溢出(StackOverflowError)
2.递归中虽然限定了停止下来的条件,但是递归次数不能太多,否则也会发生栈内存溢出
3.禁止构造方法递归
一:递归阶乘
/**阶乘递归实现*/
public static int Factorial(int n){
if (1==n){//边界条件,阶乘到最后一个数时候返回1
return 1;
}else {
return n*Factorial(n-1);
}
}
//调用
int content=Factorial(10);
Log.d("aa",content+"");
//结果
aa: 3628800
二:斐波那契数列
1,1,2,3,5,8,13,21,34,55,89,144和无穷大。这个数列从第3项开始,每一项都等于前两项之和,这一系列数字被称为斐波那契数列,随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割的数值0.6180339887..…
/**斐波那契数列(兔子数列)*/
public static int BirthRabbit(int x){
if (x==1||x==2){//
return 1;
}else {
return BirthRabbit(x-1)+BirthRabbit(x-2);
}
}
//调用
for (int i=1;i<=20;i++){
System.out.println("兔子第" + i + "个月的总数为:" + BirthRabbit(i));//输出20个月,每一个月的兔子数
}
int content=BirthRabbit(10);//输出10个月的兔子数
Log.d("aa",content+"");
//结果
aa: 55
三:汉罗塔问题
/**
* 汉诺塔问题
*
* @param dish 盘子个数(也表示名称)
* @param from 初始塔座
* @param temp 中介塔座
* @param to 目标塔座
*/
public static void move(int dish,String from,String temp,String to){
if (dish==1){
System.out.println("将盘子"+dish+"从塔座"+from+"移动到目标塔座"+to);
}else {
move(dish-1,from,to,temp);//a为初始塔座,b为目标塔座,c为中介塔座
System.out.println("将盘子"+dish+"从塔座"+from+"移动到目标"+to);//把a上面的最下面的一个盘子移动到c
move(dish-1,temp,from,to);//b为初始塔座,c为目标塔座,a为中介塔座
}
}
//调用
move(3,"A","B","C");
//结果
System.out: 将盘子1从塔座A移动到目标塔座C
System.out: 将盘子2从塔座A移动到目标B
System.out: 将盘子1从塔座C移动到目标塔座B
System.out: 将盘子3从塔座A移动到目标C
System.out: 将盘子1从塔座B移动到目标塔座A
System.out: 将盘子2从塔座B移动到目标C
System.out: 将盘子1从塔座A移动到目标塔座C
四:树的遍历
树的遍历:
1.深度优先(DFS)
1.前序遍历
2.中序遍历
3.后序遍历
2.广度优先(BFS)
1.层次遍历
前提:
//定义节点的数据结构
public class TreeNode {
String value=null;
TreeNode leftChildren=null;
TreeNode rightChildren=null;
public TreeNode(String value, TreeNode leftChildren, TreeNode rightChildren) {
this.value = value;
this.leftChildren = leftChildren;
this.rightChildren = rightChildren;
}
public TreeNode(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public TreeNode getLeftChildren() {
return leftChildren;
}
public void setLeftChildren(TreeNode leftChildren) {
this.leftChildren = leftChildren;
}
public TreeNode getRightChildren() {
return rightChildren;
}
public void setRightChildren(TreeNode rightChildren) {
this.rightChildren = rightChildren;
}
}
4.1:前序遍历
思路:先根节点-->左子树-->右子树
实现:
/**创建二叉树*/
public TreeNode getTargetTree() {
// 叶子节点
TreeNode G = new TreeNode("G");
TreeNode D = new TreeNode("D");
TreeNode E = new TreeNode("E", G, null);
TreeNode B = new TreeNode("B", D, E);
TreeNode H = new TreeNode("H");
TreeNode I = new TreeNode("I");
TreeNode F = new TreeNode("F", H, I);
TreeNode C = new TreeNode("C", null, F);
// 构造根节点
TreeNode root = new TreeNode("A", B, C);
return root;
}
/**前序遍历*/
public void preorderTreeNode(TreeNode node){
if (null!=node){
System.out.print(node.value);
}
if (null!=node.leftChildren){
preorderTreeNode(node.leftChildren);//递归实现
}
if (null!=node.rightChildren){
preorderTreeNode(node.rightChildren);//递归实现
}
}
//调用
TreeNode tree= getTargetTree();//构建树
System.out.print("前序遍历:");
preorderTreeNode(tree);
//结果:
System.out: 前序遍历:ABDEGCFHI
4.2 末子节点遍历
/**前序遍历拿到末节点信息*/
public void endOrderTreeNode(TreeNode node){
if (null!=node&&null==node.leftChildren&&null==node.rightChildren){
System.out.print(node.value);
}
if (null!=node.leftChildren){
endOrderTreeNode(node.leftChildren);
}
if (null!=node.rightChildren){
endOrderTreeNode(node.rightChildren);
}
}
或者:
/**前序遍历末子节点*/
public void preorderTreeNode2(TreeNode node){
if (null==node.leftChildren&&null==node.rightChildren){
System.out.print(node.value);
}
List lists=new ArrayList<>();
if (null!=node.leftChildren){
lists.add(node.leftChildren);
}
if (null!=node.rightChildren){
lists.add(node.rightChildren);
}
for (int i=0;i
4.3 中序遍历
思路:先左子树-->根节点-->右子树
实现:
/**
* 中序遍历
*/
public void inorderTreeNode(TreeNode node){
if(null != node){
if(null != node.leftChildren){
inorderTreeNode(node.leftChildren);
}
System.out.print(node.value);
if(null != node.rightChildren){
inorderTreeNode(node.rightChildren);
}
}
}
//调用
TreeNode tree= getTargetTree();
System.out.print("中序遍历:");
inorderTreeNode(tree);
//结果
中序遍历:DBGEACHFI
/**
* 后序遍历
*/
public void postorderTreeNode(TreeNode node){
if(null != node){
if(null != node.leftChildren){
postorderTreeNode(node.leftChildren);
}
if(null != node.rightChildren){
postorderTreeNode(node.rightChildren);
}
System.out.print(node.value);
}
}
//调用
TreeNode tree= getTargetTree();
System.out.print("后序遍历:");
postorderTreeNode(tree);
//结果
System.out: 后序遍历:DGEBHIFCA
4.5 层次遍历
思路:先根节点,然后第二层,第三层,依次往下走,(同层节点从左往右输出)
/**
* 层次遍历
*/
public void levelorderTreeNode(TreeNode node) {
if (null != node) {
LinkedList list = new LinkedList();
list.add(node);//添加当前节点 list.size()为1,添加的当前节点是A
TreeNode currentNode;
while (!list.isEmpty()) {
currentNode = list.poll(); //获取并移除此列表的头部
System.out.print(currentNode.value);//答应当前节点的值
if (null != currentNode.leftChildren) {
list.add(currentNode.leftChildren);//添加左子树到列表尾部
}
if (null != currentNode.rightChildren) {
list.add(currentNode.rightChildren);//添加右子树到列表的尾部
}
}
}
}
//调用
TreeNode tree= getTargetTree();
System.out.print("层次遍历:");
levelorderTreeNode(tree);
//结果
System.out: 层次遍历:ABCDEFGHI
知识点:
1,结点的度: 一个结点含有的子结点的个数称为该结点的度;
2,叶结点或终端结点: 度为0的结点称为叶结点;
3,非终端结点或分支结点: 度不为0的结点;
4,双亲结点或父结点: 若一个结点含有子结点,则这个结点称为其子结点的父结点;
5,孩子结点或子结点: 一个结点含有的子树的根结点称为该结点的子结点;
6,兄弟结点: 具有相同父结点的结点互称为兄弟结点;
7,树的度: 一棵树中,最大的结点的度称为树的度;
8,结点的层次: 从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
9,树的高度或深度: 树中结点的最大层次;
10,堂兄弟结点: 双亲在同一层的结点互为堂兄弟;
11,结点的祖先: 从根到该结点所经分支上的所有结点;
12,子孙: 以某结点为根的子树中任一结点都称为该结点的子孙。
13,森林: 由m(m>=0)棵互不相交的树的集合称为森林;
14,无序树: 树中任意节点的子结点之间没有顺序关系,这种树称为无序树,也称为自由树;
15,有序树: 树中任意节点的子结点之间有顺序关系,这种树称为有序树;
16,二叉树: 每个节点最多含有两个子树的树称为二叉树;
17,完全二叉树: 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树
18,满二叉树: 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。
19,哈夫曼树: 带权路径最短的二叉树称为哈夫曼树或最优二叉树;
END:当你远远凝视深渊时,深渊也在凝视你