递归思想及其实现
知识点:
递归算法由来已久,在高中的学习和大学的时候都常常遇到一些递归算法。递归的算法的基本思想就是自己调用自己,如果要写一个递归算法首先要判断他的结束条件。我们这里就来写几个简单的递归算法来说明情况.
//用递归实现最大公约数
public static int result(int num1,int num2)
{
if(num1 == num2) //在这里就判断的递归的结束条件
{
return num2;
}
else
{
return result(abs(num1-num2),min(num1,num2));
}
}
//返回一个数的绝对值
public static int abs(int num)
{
return num>0?num:-num;
}
//返回2个数中的最小的一个
public static int min(int num1,int num2)
{
return num1>=num2?num2:num1;
}
这个是个最简单的递归算发,基本思想就是2求差,求完以后和最小的比较,如果两个相等就是最大大公约数,如果就考虑写判断结束条件,这样就实现了个简单的递归。
在就是汉若塔问题
汉若塔用递归思考首先考虑一种临界状态,把n-1个上面的盘从A—B, 就是把n从A移动到C,最后把n-1个盘从B---C,(注意在考虑把n-1个盘从B---C的时候就出现递归调用,如果把A,B盘交换就又重复上面的流程了,最后到n = 1的时候就返回)
代码实现
public void run(int n,char a,char b,char c)
{
if(n==1)
{
move(n,a,c);//等于1的时候把盘从A移动到C
}
else
{
run (n-1,a,b,c);//递归调用把a上面的n-1个盘移动到B上,怎么表示移动?把柱子//交换不就是移动了。
move(n,a,c);
run (n-1,b,a,c);//把把n-1个盘冲A移动到B上以后又重复前面的所有步骤,把n-1个盘冲当做启始柱子。所以把A和B柱子交换.
}
}
在此基础上理解了汉若塔的思想可以做个
GUI
的演示程序,为了给大家演示在此写了个
DOS
下用
*
表示的汉若塔(由于时间有限制没考虑程序的通用和封装)。
代码入下
import java.io.*;
public class ChinaTower
{
MyArray arr_y1 = null;
MyArray arr_y2 = null;
MyArray arr_y3 = null;
int num;
int sum_num = 0;//记数
int print = -1;//是否打印
PrintStream out = null;
PrintStream print_1 = null;
public ChinaTower(int n)
{
this.num = n;
arr_y1 = new MyArray(n);
arr_y2 = new MyArray(n);
arr_y3 = new MyArray(n);
arr_y1.init();
try
{
print_1 = new PrintStream("bb.txt");
}catch(Exception e)
{
e.printStackTrace();
}
}
//运行
public void run(int n,MyArray a,MyArray b,MyArray c)
{
if(n == 1)
{
move(n,a,c);
}
else
{
run(n-1,a,c,b);
move(n,a,c);
run(n-1,b,a,c);
}
}
//移动
public void move(int n,MyArray a,MyArray c)
{
c.state--;
int row_a = a.state;//汉若塔最顶层的元素位置,横坐标
int row_c = c.state;
for(int j=0;j<this.num*this.num;j++)
{
c.arr_y[row_c][j] = a.arr_y[row_a][j];
}
a.delete();
a.state++;
try
{
this.display();
}catch(Exception e)
{
e.printStackTrace();
}
}
//显示
public void display() throws Exception
{
// System.out.println(System.out);
if(this.print == 1)
{
System.setOut(print_1);
}
System.out.println("第" + (++this.sum_num) + "次移动状态");
for(int j=0;j<this.num*this.num*3;j++)
{
System.out.print("-");
}
System.out.println();
for(int i=0;i<this.num;i++)
{
for(int j=0;j<this.num*this.num;j++)
{
System.out.print(this.arr_y1.arr_y[i][j]);
}
for(int j=0;j<this.num*this.num;j++)
{
System.out.print(this.arr_y2.arr_y[i][j]);
}
for(int j=0;j<this.num*this.num;j++)
{
System.out.print(this.arr_y3.arr_y[i][j]);
}
System.out.println();
}
for(int j=0;j<this.num*this.num*3;j++)
{
System.out.print("-");
}
System.out.println();
}
//设置打印标志
public void print()
{
this.print = 1;
}
public void close()//关闭输入流
{
print_1.close();
}
public static void main(String args[])
{
ChinaTower po = new ChinaTower(3);
po.print();//是否打印到文件里
po.run(3,po.arr_y1,po.arr_y2,po.arr_y3);
po. close();
}
}
打印结果如下 :
![](https://p-blog.csdn.net/images/p_blog_csdn_net/maomao1221/ae5c26b9120b48e8bd866d139a8b3572.gif)
了解了递归的基本思想以后我们再来实现一个和wiodows窗口下的一个命令tree一样功能的Java程序.其他思想是用File类的
listFiles
()返回当前文件夹下的目录或文件(当然也可以传入个FileFilter 类进行过滤),用isDirectory()判断是否是目录,如果是就进入递归。实现了文件夹下的遍历以后就是显示效果的问题了。如┗ ━┣ ┏ ┗这些符号在什么时候因该用呢?仔细观察后发现如果用一个类包装File类进行某些属性的添加比较适合实现,包装类里面有其元素的父接点信息和其是不是最后一个元素,这样可以很容易判断文件前面的图。至于复杂一点的文件前面的字符串就要根据判断其父亲是不是最后一个元素来判断了,如果父亲是最后一个元素就用” ”,如果不是就用” |”(这个规律自己观察).这样包装File也很符合设计模式,我们这里只实现了一些简单的显示,如果要做某些功能的扩展在包装File的类里面写也很容易实现.
其代码如下:
import java.io.*;
public class Tree
{
//显示
public void display(FilePackage file_d)
{
File[] fl = file_d.listFiles();
//显示该目录下的所有文件
for(int i=0;i<fl.length;i++)
{
boolean islast = false;
if(i == fl.length-1)
{
islast = true;
}
//包装File
FilePackage fl_package = new FilePackage(fl[i],islast,file_d);
if(fl_package.isDirectory())
{
//如果是目录就进入递归调用
System.out.println(frontConstruct(fl_package,new StringBuffer()) + fl_package.getName());
display(fl_package);
}
else
{
System.out.println(frontConstruct(fl_package,new StringBuffer()) + fl_package.getName());
}
}
}
//拼文件前面的字符串,这样字符串都要反转,原因很简单,自己想下
public String frontConstruct(FilePackage file_d,StringBuffer buf)
{
FilePackage parent_file = file_d.getMy_Parent();
this.frontSign(file_d,buf);
while(parent_file!=null && parent_file.getMy_Parent() != null)
{
// System.out.println(parent_file.isLast());
if(parent_file.isLast())
{
buf.append(" ");
}
else
{
buf.append(" │");
}
parent_file = parent_file.getMy_Parent();
}
return buf.reverse().toString();
}
//生成文件前面的一个符号
public void frontSign(FilePackage file_d,StringBuffer buf)
{
boolean islast = file_d.isLast();
if(islast)
{
buf.append("─└");
}
else
{
buf.append("─├");
}
}
public static void main(String args[])
{
Tree tree = new Tree();
File file_current = null;
if(args.length < 1 || args.length >1)
{
file_current = new File("..");
}
else
{
file_current = new File(args[0]);
}
FilePackage file_current_temp = new FilePackage(file_current,true,null);
tree.display(file_current_temp);
}
}
显示结果如图:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/maomao1221/ce4d8a9a249d4c159cdabdbcd7e8cc55.png)
此例如果用在Javascript中可以做一个很好的扩充。
其他小知识点
今天写了个程序试图new byte[1024*1024*100],但是程序会报告一个内存益处错误,这个问题虚拟机执行这个操作的时候内存不够而导致,所以在工程的文件上传中最好定义一个固定长度的大小读数据,而不要根据Http的消息头的文件大小来设置数组大小,解决办法可以查java –X获得。(java –Xms200m –Xmx300m classfile就OK了)。
system.getProperty(String key)这个方法可以获得系统的环境变量。Java –D可以设置运行时候环境变量。
System.setOut(
PrintStream out
)设置了这个流后以后的打印回写入这个流里。(DOS命令下的>a.txt屏幕重定向 <a.txt键盘重定向,如执行java Test >a.txt会把原来程序的输出语句都打印到a.txt文件中)
Ant的build.xml中如果有”%{dir.infor}”这样的变量引用可以通过在执行ant令名的时候加 ant –Ddir.infor=xxx来赋值。
FileWrapper extends File中的继承问题(注意用super()来初始化File的基本信息)。
XML约束文件中的问题,一个dtd文件里面有中文字符,但是转成UTF-8编码保存后也会报告错误,必须要UTF-8前面3个字节删除掉(EF BB BF)。
学习总结
:
今天通过求最大公约数,汉若塔,8皇后问题,树形结构的练习对递归算法有了更清晰的思路和熟练度,总的来说今天一天还是过的比较充实。嘎嘎
学习中的问题:
今天学习中的几个小东西顺利解决了,Happy!