《算法(第四版)》练习答案,自己做的,记录一下~
如有错误,烦请不吝赐教!
很多代码题没写,其他的题25,31,32待做。
1-1-1.
a.7
b.200.0000002,没溢出,正常算就可以
c.true,因为&&的优先级高于||
1-1-2.
a.1.618,类型自动转换了,整型转换为浮点型
b.10.0,类型自动转换了,整型转换为浮点型
c.true,类型自动转换了,整型转换为浮点型,这里4.1>4.0也是true
d.33,类型自动转换了,转换为字符串,但是1和2还是能计算的,如果要得到字符串123可以尝试1+""+2+“3”,或者把1和2中的一个变为字符串类型,如"1"。
1-1-3.
命令行操作如图所示,代码见下。
public class Exercise1_1_3 {
public static void main(String[] args) {
Integer a = Integer.parseInt(args[0]);
Integer b = Integer.parseInt(args[1]);
Integer c = Integer.parseInt(args[2]);
if(a==b&&b==c) System.out.println("equal");
else System.out.println("not equal");
}
}
1-1-4.
a.错误,then不是关键字,把then去掉。
b.正确
c.正确
d.错误,c=0后应该加分号。
1-1-5.
public static void main(String[] args) {
double x=1,y=0.2;
if(x>0&&x<1&&y>0&&y<1) StdOut.println("true");
else StdOut.println("false");
}
1-1-6.
首先i是0到15,16次循环,输出16行。
然后每次循环内先输出f的值,再将f和g的值相加赋值给f。
再将已经被赋值为f+g的f减掉g后赋值给g。循环输出此规则产生的数列中的元素。
实际上,从循环开始,f记录的是前两个数相加的和,g记录的是上一次两数相加的和。
没错,这个循环计算得到的结果就是斐波那契数列(Fibonacci Sequence)。
an=a(n-1)+a(n-2)(n>=3,n∈N*)
而循环的过程如上所述,其实就是斐波那契数列产生的过程,从初始的两个数0和1开始的累加过程。
程序结果如下所示。
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
1-1-7.
第一段输出:3.00009
double t = 9.0;
while(Math.abs(t-9.0/t)>.001)
t = (9.0/t+t)/2.0;
StdOut.printf("%.5f\n",t);
第二段输出499500
1+2+3+…+999 = 1000*499+500
int sum = 0;
for(int i=1;i<1000;i++)
for(int j=0;j<i;j++)
sum++;
StdOut.println(sum);
第三段输出10000
int sum = 0;
for(int i=1;i<1000;i*=2)
for(int j=0;j<1000;j++)
sum++;
StdOut.println(sum);
1-1-8.
第一个输出b,输出字符b。
第二个输出197,因为有加号,加号两边没有字符串,这里加号不是连接的作用,是相加的作用,所以输出的是b和c的ASCII码值的和。
第三个输出e,a的ASCII码值加上4转换为字符输出后是e。
System.out.println('b');
System.out.println('b'+'c');
System.out.println((char)('a'+4));
1-1-9.
String s="";
for(int n=N;n>0;n/=2)
s = (n%2)+s;
1-1-10.
问题在于没有初始化数组a。
在Java中,未初始化就使用变量会报错,这与C++不同。
但是Java类的域会被初始化为默认值。
1-1-11.
略。
1-1-12.
这个地方原题目的第三个循环是输出i,应该是输出a[i],否则这个题目就没什么意义了。
下面是程序:
int[] a = new int[10];
for(int i=0;i<10;i++)
a[i] = 9-i;
for(int i=0;i<10;i++)
a[i] = a[a[i]];
for(int i=0;i<10;i++)
System.out.println(a[i]);
下面是答案,手写一下就明白了。
0
1
2
3
4
4
3
2
1
0
1-1-13.
略。
1-1-14.
输入从1开始。
public static int lg(int n){
int temp=1;
int res=0;
while(temp<=n){
temp *= 2;
res++;
}
return res-1;
}
1-1-15.
public static int[] histogram(int a[],int M){
int[] res = new int[M];
for(int i=0;i<a.length;i++){
if(a[i]<M) res[a[i]]++;
}
return res;
}
1-1-16.
这是个递归程序,这个题没啥实际意义。
应该是为了理解:1.递归。2.加号做连接功能。这两点的。
public static String exR1(int n){
if(n<=0) return "";
return exR1(n-3) + n + exR1(n-2) + n;
}
1-1-17.
这段代码的出口永远调用不到,调用exR2(3)会产生调用exR2(0)和exR2(-3),循环往复直到发生java.lang.StackOverflowError
public static String exR2(int n){
String s = exR2(n-3) + n + exR2(n-2) + n;
if(n<=0) return "";
return s;
}
1-1-18.
mystery(2,25)输出50。
mystery(3,11)输出33。
+换*后的结果为:
33554432
177147
这里说一下第一个结果的过程,其他类似:
mystery(2,25)
mystery(2+2,25/2)+2
mystery(4+4,12/2)
mystery(8+8,6/2)
mystery(16+16,3/2)+16
mystery(32+32,1/2)+32
最终结果32+16+2=50
public static int mystery(int a,int b){
if(b==0) return 0;
if(b%2 == 0) return mystery(a+a,b/2);
return mystery(a+a,b/2) + a;
}
1-1-19.
下面原始程序的问题在于重复计算,每次的结果没有保存,比如计算F(4)的时候实际上F(3)的结果又重新算了一遍。
public static long F(int N){
if(N==0) return 0;
if(N==1) return 1;
return F(N-1)+F(N-2);
}
下面是改进的代码,其实就是把中间结果保存了一遍。
最后结果是溢出的,但是速度很快,1秒左右就运行完毕了。
static long[] F_temp = new long[100];
public static long F(int N){
if(N==0) return 0;
if(N==1) return 1;
if(F_temp[N]!=0) return F_temp[N];
return F(N-1)+F(N-2);
}
public static void main(String[] args){
for(int N=0;N<100;N++){
StdOut.println(N+" "+F(N));
F_temp[N] = F(N);
}
}
1-1-20.
根据对数运算法则:ln(MN) = lnM + lnN,所以递归把阶乘写成和的形式就可以了。
写递归就注意怎么把问题划分为子问题和出口就可以了。
public static double ln(int N){
if(N==1) return 0;
return Math.log(N)+ln(N-1);
}
1-1-21.
略。
1-1-22.
加一个参数记录深度即可。
public static int rank(int key,int[] a)
{ return rank(key,a,0,a.length-1,0);}
public static int rank(int key,int[] a,int lo,int hi,int deep)
{ //如果key存在于a[]中,它的索引不会小于lo且不会大于hi
StdOut.println(lo+" "+hi+" "+deep);
if(lo>hi) return -1;
int mid = lo+(hi-lo)/2;
if(key<a[mid]) return rank(key,a,lo,mid-1,deep+1);
else if(key>a[mid]) return rank(key,a,mid+1,hi,deep+1);
else return mid;
}
1-1-23.
略。
1-1-24.
如何从命令行接受参数见第三题,这里不再赘述接受命令行参数的算法。
public static int gcd(int p,int q)
{
StdOut.println(p+" "+q);//添加这一行输出p和q的中间结果
if(q==0) return p;
int r = p%q;
return gcd(q,r);
}
105和24的计算结果如下。
105 24
24 9
9 6
6 3
3 0
3
1111111和1234567的最大公约数运行结果为1,说明两数互质。
1111111 1234567
1234567 1111111
1111111 123456
123456 7
7 4
4 3
3 1
1 0
1
1-1-25.
待做。
1-1-26.
第一个判断排列a和b,把最小的放在位置a,第二个判断排列a和c,把较小的放在a。
这样经过两个判断后,位置a的数字是三个里最小的,这时候把b和c位置的数字比较一下就可以了。
1-1-27.
递归层数100层,每层调用两个递归调用,次数在2^100数量级。
保存中间结果的代码略,可以参考19题。
1-1-28.
略。
1-1-29.
略。找到值之后中心扩散一下就可以了。
1-1-30.
略。结合欧几里得算法判断一下就可以了。
1-1-31.
待做。
1-1-32.
待做。
1-1-33.
略。
1-1-34.
1.只需要两个变量记录最大最小值更新。
2.需要记录所有值。
3.需要记录k个值的数组。
4.使用一个变量累加。
5.使用一个变量累加,一个变量计数,来计算平均值。
6.需要记录所有的数。
7.需要记录所有的数。
8.需要记录所有的数。
1-1-35.
注释的代码是准确概率,未注释的代码是模拟掷骰子。
大约N为50000左右比较准确,
public static void main(String[] args){
int SLIDES = 6;
int N = 1000000;
Random r = new Random();
int i1,i2;
double[] dist = new double[2*SLIDES+1];
for(int i=0;i<N;i++)
{
i1 = r.nextInt(6)+1;
i2 = r.nextInt(6)+1;
dist[i1+i2] += 1.0;
}
for(int i=2;i<=2*SLIDES;i++)
{
dist[i] /= 1000000.0;
}
System.out.println(dist[2]);
// int SLIDES = 6;
// double[] dist = new double[2*SLIDES+1];
// for(int i=1;i<=SLIDES;i++)
// for(int j=1;j<=SLIDES;j++)
// dist[i+j] += 1.0;
// for(int k=2;k<=2*SLIDES;k++)
// dist[k] /= 36.0;
// System.out.println(dist[2]);
}
1-1-36.
略。
1-1-37.
略。
1-1-38.
略。
1-1-39.
略。