经典算法的Java实现36题.docx

上传人:b****5 文档编号:5116396 上传时间:2022-12-13 格式:DOCX 页数:76 大小:183.62KB
下载 相关 举报
经典算法的Java实现36题.docx_第1页
第1页 / 共76页
经典算法的Java实现36题.docx_第2页
第2页 / 共76页
经典算法的Java实现36题.docx_第3页
第3页 / 共76页
经典算法的Java实现36题.docx_第4页
第4页 / 共76页
经典算法的Java实现36题.docx_第5页
第5页 / 共76页
点击查看更多>>
下载资源
资源描述

经典算法的Java实现36题.docx

《经典算法的Java实现36题.docx》由会员分享,可在线阅读,更多相关《经典算法的Java实现36题.docx(76页珍藏版)》请在冰豆网上搜索。

经典算法的Java实现36题.docx

经典算法的Java实现36题

经典算法的Java实现

(1)河内塔问题:

说明:

河内之塔(TowersofHanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学家EdouardLucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒(Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disc),并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损,而也就是世界末日来临之时。

解法:

如果柱子标为ABC,要由A搬至C,在只有一个盘子时,就将它直接搬至C,当有两个盘子,就将B当作辅助柱。

如图所示:

 

 

图1图2

图3

事实上,若有n个盘子,则移动完毕所需之次数为2^n-1,所以当盘数为64时,则所需次数为:

264-1=18446744073709551615为5.05390248594782e+16年,也就是约5000世纪,如果对这数字没什么概念,就假设每秒钟搬一个盘子好了,也要约5850亿年左右。

 

实现:

//Java程序的实现

importjava.io.*;

publicclassHanoi{

publicstaticvoidmain(Stringargs[])throwsIOException{

intn;

BufferedReaderbuf;

buf=newBufferedReader(newInputStreamReader(System.in));

System.out.print("请输入盘数:

");

n=Integer.parseInt(buf.readLine());

Hanoihanoi=newHanoi();

hanoi.move(n,'A','B','C');

}

publicvoidmove(intn,chara,charb,charc){

if(n==1)

System.out.println("盘"+n+"由"+a+"移至"+c);

else{

move(n-1,a,c,b);

System.out.println("盘"+n+"由"+a+"移至"+c);

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[i]=fib[i-1]+fib[i-2];

for(inti=0;i

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;i<=r;i++)

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{

publicstaticvoidmain(String[]args){

finalintN=50000;

intsum=0;

for(inti=1;i

doublex=Math.random();

doubley=Math.random();

if((x*x+y*y)<1)

sum++;

}

System.out.println("PI="+(double)4*sum/N);

}

}

(5)最大公因数、最小公倍数

说明:

解法:

最大公因数使用辗转相除法来求,最小公倍数则由这个公式来求:

GCD*LCM=两数乘积

实现:

//java程序实现

importjava.io.*;

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));

System.out.print("请输入第一个数:

");

intx=Integer.parseInt(ln.readLine());

System.out.print("请输入第二个数:

");

inty=Integer.parseInt(ln.readLine());

System.out.println("GCDof("+x+","+y+")="+GcdLcm.gcdOf(x,y));

System.out.println("LCMof("+x+","+y+")="+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

实现:

//java程序实现

publicclassArmstrong{

publicstaticvoidmain(String[]args){

System.out.println("寻找Armstrong数:

");

for(inti=100;i<=999;i++){

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]分别进行排序(由小到大),道理很简单,只要先计算某时之前总共来访了多少访客,然后再减去某时之前的离开访客,就可以轻易的解出这个问题

实现:

//java实现

importjava.io.*;

importjava.util.*;

publicclassMaxVisit{

publicstaticintmaxGuest(int[]x,int[]y,inttime){

intnum=0;

for(inti=0;i

if(time>x[i])

num++;

if(time>y[i])

num--;

}

returnnum;

}

publicstaticvoidmain(String[]args)throwsIOException{

BufferedReaderbuf=newBufferedReader(newInputStreamReader(System.in));

System.out.println("输入来访时间与离开时间(0~24):

");

System.out.println("范例:

1015");

System.out.println("输入-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()];

for(inti=0;i

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的乱数,将产生的乱数当作索引取出阵列值,并与目前阵列走访到的值相交换,如此就不用担心乱数重复的问题了,阵列走访完毕后,所有的数字也就重新排列了。

至于如何判断花色?

这只是除法的问题而已,取商数判断花色,取余数判断数字,您可以直接看程式比较清楚。

实现:

//java实现

publicclassShuffleCard{

publicstaticvoidmain(Stringargs[]){

finalintN=52;

int[]poker=newint[N+1];

//初始化阵列

for(inti=1;i<=N;i++)

poker[i]=i;

//洗牌

for(inti=1;i<=N;i++){

intj=(int)(Math.random()*N);

if(j==0)

j=1;

inttmp=poker[i];

poker[i]=poker[j];

poker[j]=tmp;

}

for(inti=1;i<=N;i++){

//判断花色

switch((poker[i]-1)/13){

case0:

System.out.print("桃");break;

case1:

System.out.print("心");break;

case2:

System.out.print("砖");break;

case3:

System.out.print("梅");break;

}

//扑克牌数字

intremain=poker[i]%13;

switch(remain){

case0:

System.out.print("K");break;

case12:

System.out.print("Q");break;

case11:

System.out.print("J");break;

default:

System.out.print(remain+"");break;

}

if(i%13==0)

System.out.println("");

}

}

}

(9)约瑟夫问题(JosephusProblem)

说明:

据说着名犹太历史学家Josephus有过以下的故事:

在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

解法:

约瑟夫问题可用代数分析来求解,将这个问题扩大好了,假设现在您与m个朋友不幸参与了这个游戏,您要如何保护您与您的朋友?

只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈内圈是排列顺序,而外圈是自杀顺序,如下图所示:

使用程式来求解的话,只要将阵列当作环状来处理就可以了,在阵列中由计数1开始,每找到三个无资料区就填入一个计数,直而计数达41为止,然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列,41个人而报数3的约琴夫排列如下所示:

1436138152243031634425175403161826737198352792032104121112839122233132923

由上可知,最后一个自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道约琴夫与他的朋友并没有遵守游戏规则了。

实现:

//java实现

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;

System.out.println("约琴夫排列:

");

for(inti=0;i<41;i++)

System.out.print(man[i]+"");

System.out.println("\nL表示3个存活的人要放的位置:

");

for(inti=0;i<41;i++){

if(man[i]>(41-alive))

System.out.print("L");

else

System.out.print("D");

if((i+1)%5==0)

System.out.print("");

}System.out.println();

}

}

(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进行递回处理

实现:

//java实现

publicclassPermutation{

publicstaticvoidperm(int[]num,inti){

if(i

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 军事

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1