经典算法的Java实现36题文档格式.docx
《经典算法的Java实现36题文档格式.docx》由会员分享,可在线阅读,更多相关《经典算法的Java实现36题文档格式.docx(76页珍藏版)》请在冰豆网上搜索。
publicvoidmove(intn,chara,charb,charc){
if(n==1)
System.out.println("
盘"
+n+"
由"
+a+"
移至"
+c);
else{
move(n-1,a,c,b);
System.out.println("
move(n-1,b,a,c);
}
}
(2)费式数列
Fibonacci为1200年代的欧洲数学家,在他的著作中曾经提到:
“若有一只免子每个月生一只小免子,一个月后小免子也开始生产。
起初只有一只免子,一个月后就有两只免子,二个月后有三只免子,三个月后有五只免子(小免子投入生产)......”。
如果不太理解这个例子的话,举个图就知道了,注意新生的小免子需一个月成长期才会投入生产,类似的道理也可以用于植物的生长,这就是Fibonacci数列,一般习惯称之为费氏数列,例如以下:
1、1、2、3、5、8、13、21、34、55、89......
依说明,我们可以将费氏数列定义为以下:
fn=fn-1+fn-2
ifn>
2
fn=1 ifn=0,1
实现:
//Java程序的实现:
publicclassFibonacci{
publicstaticvoidmain(String[]args){
int[]fib=newint[20];
fib[0]=0;
fib[1]=1;
for(inti=2;
i<
fib.length;
i++)
fib[i]=fib[i-1]+fib[i-2];
for(inti=0;
System.out.print(fib[i]+"
"
System.out.println();
}
(3)巴斯卡(Pascal)三角形
巴斯卡(Pascal)三角形基本上就是在解nCr,因为三角形上的每一个数字各对应一个nCr,其中n为row,而r为column,如下:
0C0
1C01C1
2C02C12C2
3C03C13C23C3
4C04C14C24C34C4
对应的数据如下图所示:
巴斯卡三角形中的nCr可以使用以下这个公式来计算,以避免阶乘运算时的数值溢位:
nCr=[(n-r+1)*nCr-1]/r
nC0=1
//java实现
importjava.awt.*;
importjavax.swing.*;
publicclassPascalextendsJFrame{
publicPascal(){
setBackground(Color.white);
setTitle("
巴斯卡三角形"
setSize(520,350);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(700,700);
setVisible(true);
privatelongcombi(intn,intr){
inti;
longp=1;
for(i=1;
=r;
p=p*(n-i+1)/i;
returnp;
publicvoidpaint(Graphicsg){
g.setColor(Color.white);
g.clearRect(0,0,getSize().width,getSize().height);
g.setColor(Color.red);
finalintN=12;
intn,r,t;
for(n=0;
n<
=N;
n++){
for(r=0;
r<
=n;
r++)
g.drawString("
+combi(n,r),(N-n)*20+r*40,n*20+50);
publicstaticvoidmain(Stringargs[]){
Pascalfrm=newPascal();
(4)蒙地卡罗法求PI
蒙地卡罗为摩洛哥王国之首都,该国位于法国与义大利国境,以赌博闻名。
蒙地卡罗的基本原理为以乱数配合面积公式来进行解题,这种以机率来解题的方式带有赌博的意味,虽然在精确度上有所疑虑,但其解题的思考方向却是个值得学习的方式。
蒙地卡罗的解法适用于与面积有关的题目,例如求PI值或椭圆面积,这边介绍如何求PI值;
假设有一个圆半径为1,所以四分之一圆面积就为PI,而包括此四分之一圆的正方形面积就为1,如下图所示:
如果随意的在正方形中投射飞标(点)好了,则这些飞标(点)有些会落于四分之一圆内,假设所投射的飞标(点)有n点,在圆内的飞标(点)有c点,则依比例来算,就会得到上图中最后的公式。
至于如何判断所产生的点落于圆内,很简单,令乱数产生X与Y两个数值,如果X^2+Y^2等于1就是落在圆内。
//java程序实现
publicclassPI{
finalintN=50000;
intsum=0;
for(inti=1;
N;
i++){
doublex=Math.random();
doubley=Math.random();
if((x*x+y*y)<
1)
sum++;
PI="
+(double)4*sum/N);
(5)最大公因数、最小公倍数
最大公因数使用辗转相除法来求,最小公倍数则由这个公式来求:
GCD*LCM=两数乘积
publicclassGcdLcm{
publicstaticintgcdOf(intm,intn){
intr;
while(n!
=0){
r=m%n;
m=n;
n=r;
returnm;
publicstaticintlcmOf(intm,intn){
returnm*n/gcdOf(m,n);
publicstaticvoidmain(String[]args)throwsIOException{
BufferedReaderln=newBufferedReader(newInputStreamReader(System.in));
请输入第一个数:
intx=Integer.parseInt(ln.readLine());
请输入第二个数:
inty=Integer.parseInt(ln.readLine());
GCDof("
+x+"
"
+y+"
)="
+GcdLcm.gcdOf(x,y));
LCMof("
+GcdLcm.lcmOf(x,y));
(6)阿姆斯壮数
在三位的整数中,例如153可以满足13+53+33=153,这样的数称之为Armstrong数,试写出一程式找出所有的三位数Armstrong数。
Armstrong数的寻找,其实就是在问如何将一个数字分解为个位数、十位数、百位数......,这只要使用除法与余数运算就可以了,例如输入input为abc,则:
a=input/100
b=(input%100)/10
c=input%10
publicclassArmstrong{
寻找Armstrong数:
for(inti=100;
=999;
inta=i/100;
intb=(i%100)/10;
intc=i%10;
if(a*a*a+b*b*b+c*c*c==i)
System.out.print(i+"
}System.out.println();
(7)最大访客数
现将举行一个餐会,让访客事先填写到达时间与离开时间,为了掌握座位的数目,必须先估计不同时间的最大访客数。
这个题目看似有些复杂,其实相当简单,单就计算访客数这个目的,同时考虑同一访客的来访时间与离开时间,反而会使程式变得复杂;
只要将来访时间与离开时间分开处理就可以了,假设访客i的来访时间为x[i],而离开时间为y[i]。
在资料输入完毕之后,将x[i]与y[i]分别进行排序(由小到大),道理很简单,只要先计算某时之前总共来访了多少访客,然后再减去某时之前的离开访客,就可以轻易的解出这个问题
importjava.util.*;
publicclassMaxVisit{
publicstaticintmaxGuest(int[]x,int[]y,inttime){
intnum=0;
x.length;
if(time>
x[i])
num++;
y[i])
num--;
returnnum;
publicstaticvoidmain(String[]args)throwsIOException{
BufferedReaderbuf=newBufferedReader(newInputStreamReader(System.in));
输入来访时间与离开时间(0~24):
范例:
1015"
输入-1结束"
java.util.ArrayListlist=newArrayList();
while(true){
System.out.print("
>
Stringinput=buf.readLine();
if(input.equals("
-1"
))
break;
list.add(input);
int[]x=newint[list.size()];
int[]y=newint[list.size()];
Stringinput=(String)list.get(i);
String[]strs=input.split("
x[i]=Integer.parseInt(strs[0]);
y[i]=Integer.parseInt(strs[1]);
Arrays.sort(x);
Arrays.sort(y);
for(inttime=0;
time<
25;
time++){
System.out.println(time+"
时的最大访客数:
+MaxVisit.maxGuest(x,y,time));
(8)洗扑克牌(乱数排列)
洗扑克牌的原理其实与乱数排列是相同的,都是将一组数字(例如1~N)打乱重新排列,只不过洗扑克牌多了一个花色判断的动作而已。
初学者通常会直接想到,随机产生1~N的乱数并将之存入阵列中,后来产生的乱数存入阵列前必须先检查阵列中是否已有重复的数字,如果有这个数就不存入,再重新产生下一个数,运气不好的话,重复的次数就会很多,程式的执行速度就很慢了,这不是一个好方法。
以1~52的乱数排列为例好了,可以将阵列先依序由1到52填入,然后使用一个回圈走访阵列,并随机产生1~52的乱数,将产生的乱数当作索引取出阵列值,并与目前阵列走访到的值相交换,如此就不用担心乱数重复的问题了,阵列走访完毕后,所有的数字也就重新排列了。
至于如何判断花色?
这只是除法的问题而已,取商数判断花色,取余数判断数字,您可以直接看程式比较清楚。
publicclassShuffleCard{
finalintN=52;
int[]poker=newint[N+1];
//初始化阵列
poker[i]=i;
//洗牌
intj=(int)(Math.random()*N);
if(j==0)
j=1;
inttmp=poker[i];
poker[i]=poker[j];
poker[j]=tmp;
//判断花色
switch((poker[i]-1)/13){
case0:
System.out.print("
桃"
break;
case1:
心"
case2:
砖"
case3:
梅"
}
//扑克牌数字
intremain=poker[i]%13;
switch(remain){
K"
case12:
Q"
case11:
J"
default:
System.out.print(remain+"
break;
}
if(i%13==0)
(9)约瑟夫问题(JosephusProblem)
据说着名犹太历史学家Josephus有过以下的故事:
在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
约瑟夫问题可用代数分析来求解,将这个问题扩大好了,假设现在您与m个朋友不幸参与了这个游戏,您要如何保护您与您的朋友?
只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈内圈是排列顺序,而外圈是自杀顺序,如下图所示:
使用程式来求解的话,只要将阵列当作环状来处理就可以了,在阵列中由计数1开始,每找到三个无资料区就填入一个计数,直而计数达41为止,然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列,41个人而报数3的约琴夫排列如下所示:
1436138152243031634425175403161826737198352792032104121112839122233132923
由上可知,最后一个自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道约琴夫与他的朋友并没有遵守游戏规则了。
publicclassJosephus{
publicstaticint[]arrayOfJosephus(intnumber,intper){
int[]man=newint[number];
for(intcount=1,i=0,pos=-1;
count<
=number;
count++){
do{
pos=(pos+1)%number;
//环状处理
if(man[pos]==0)
i++;
if(i==per){//报数为3了
i=0;
break;
}
}while(true);
man[pos]=count;
returnman;
}
publicstaticvoidmain(String[]args){
int[]man=Josephus.arrayOfJosephus(41,3);
intalive=3;
约琴夫排列:
41;
System.out.print(man[i]+"
\nL表示3个存活的人要放的位置:
if(man[i]>
(41-alive))
L"
else
D"
if((i+1)%5==0)
(10)排列组合
将一组数字、字母或符号进行排列,以得到不同的组合顺序,例如123这三个数的排列组合有:
123、132、213、231、312、321。
可以使用递回将问题切割为较小的单元进行排列组合,例如1234的排列可以分为1[234]、2[134]、3[124]、4[123]进行排列,这边利用旋转法,先将旋转间隔设为0,将最右边的数字旋转至最左边,并逐步增加旋转的间隔,例如:
1234->
旋转1->
继续将右边234进行递回处理
2134->
旋转12变为21->
继续将右边134进行递回处理
3124->
旋转123变为312->
继续将右边124进行递回处理
4123->
旋转1234变为4123->
继续将右边123进行递回处理
publicclassPermutation{
publicstaticvoidperm(int[]num,inti){
if(i<
num.length-1){
for(intj=i;
j<
=num.length-1;
j++){
inttmp=num[j];
//旋转该区段最右边数字至最左边
for(intk=j;
k>
i;
k--)
num[k]=num[k-1];
num[i]=tmp;
p