算法練習(3) :遞歸(1.1.15-1.1.21)

本系列博客習題來自《算法(第四版)》,算是本人的讀書筆記,如果有人在讀這本書的,歡迎大家多多交流。為了方便討論,本人新建了一個微信群(算法交流),想要加入的,請添加我的微信號:zhujinhui207407 謝謝。另外,本人的個人博客 http://www.kyson.cn 也在不停的更新中,歡迎一起討論

算法(第4版)

知識點

  • java的基本語法:遞歸調用
  • 直方圖預習
  • 斐波那契數列的遞歸調用實現以及優化

題目

1.1.15 編寫一個靜態方法 histogram(),接受一個整型數組 a[] 和一個整數 M 為參數并返回一個大小為 M 的數組,其中第 i 個元素的值為整數 i 在參數數組中出現的次數。如果 a[] 中的值均在 0 到 M-1之間,返回數組中所有元素之和應該和 a.length 相等。


1.1.15 Write a static method histogram() that takes an array a[] of int values and an integer M as arguments and returns an array of length M whose i th entry is the number of times the integer i appeared in the argument array. If the values in a[] are all between 0 and M–1, the sum of the values in the returned array should be equal to a.length.

分析

這道題如果只是看題目的話,絕對會把你繞暈,但如果你看函數的話,一下就豁然開朗了:histogram(直方圖),對的,這道題目其實就是讓你去實現一個直方圖。關于直方圖詳細,請看這個習題:算法練習(14):直方圖(1.1.32)

因此這道題也就有兩種解法,一種是按題目上的字面信息解法,另外一種就是我們通過直方圖的定義來解答。

答案

解法一:

public static int[] histogram(int[] a,int M)
{
    int[] b = new int[M];
    int n = 0;
    int m = 0;
    for(int i = 0; i < M; i++)
    {
        for(int j = 0; j < a.length ; j++ )
        {
            if(i == a[j])
            {
                n++;
            }
            b[i] = n;
        }
        n = 0;
    }
    
    for(int i = 0 ; i < M ; i++)
    {
        m = m + b[i];
    }

    return b;
}

解法二:(感謝讀者 蛋黃七分熟 提出)

public static int[] histogram2(int[] a,int M)
{
    int[] h = new int[M];
    int N = a.length;
    for (int i = 0; i < N; i++)
        if (a[i] < M)
            h[a[i]]++;
    return h;
}

測試用例

public static void main(String[] args) {
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 12 };
    int M = 13;
    int b[] = histogram(a, M);
    System.out.println("調用函數后獲取的數組:");
    for (int i = 0; i < b.length; i++) {
        System.out.println(b[i]);
    }       
}

代碼索引

HistogramSample.java

題目

1.1.16 給出 exR1(6) 的返回值:


1.1.16 Give the value of exR1(6):

public static String exR1(int n)
{
   if (n <= 0) return "";
   return exR1(n-3) + n + exR1(n-2) + n;
}

分析

遞歸的情況:
f(6)=f(3)+6+f(4)+6  
因為f(3)=f(0)+3+f(1)+3 ??f(0)="" ?? f(1)=f(-2)+1+f(-1)+1=11
所以f(6)=31136+f(4)+6
又因為f(4)=f(1)+4+f(2)+4 ?? f(1)=11 ??f(2)=f(-1)+2+f(0)+2=22
從而f(6)=311361142246

答案

311361142246

題目

1.1.17 找出以下遞歸函數的問題:

public static String exR2(int n) {
    String s = exR2(n - 3) + n + exR2(n - 2) + n;
    if (n <= 0)
        return "";
    return s;
}

答案

這段代碼中的基礎情況永遠不會被訪問。調用 exR2(3) 會產生調用 exR2(0)、exR2(-3) 和exR2(-6),循環往復直到發生 StackOverflowError。

題目

1.1.18 請看以下遞歸函數:

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

mystery(2, 25) 和 mystery(3, 11) 的返回值是多少?給定正整數 a 和 b,mystery(a,b)計算的結果是什么?將代碼中的 + 替換為 * 并將 return 0 改為 return 1,然后回答相同
的問題。

答案

    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;
    }
    public static int mystery1(int a, int b) {
        if (b == 0)
            return 1;
        if (b % 2 == 0)
            return mystery1(a*a, b/2);
        return mystery1(a*a, b/2) * a;
    }
      
    public static void main(String args[]) {  
        System.out.println(mystery(2,25));  // 輸出50
        System.out.println(mystery(3,11));  //輸出33
        System.out.println(mystery1(2,25));  // 輸出33554432
        System.out.println(mystery1(3,11));  //輸出177147
    }
//這道題目考了一個思想,數據和操作,即第一個參數是數據,第二個參數是操作的。這在實際編程中也是一種解耦的思想

題目

1.1.19 在計算機上運行以下程序:

public class Fibonacci {
    public static long F(int N) {
        if (N == 0)
            return 0;
        if (N == 1)
            return 1;
        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(N) 結果的最大 N 值是多少?開發 F(N) 的一個更好的實現,用數組保存已經計算過的值。

答案

    public class Fibonacci {
        public static long F(int N)  {
              if (N == 0) return 0;
              if (N == 1) return 1;
              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));
        }
   }
//具體能算到多少不知道,部分結果如下:
0 0
1 1
2 1
3 2
4 3
5 5
6 8
7 13
8 21
9 34
10 55
11 89
12 144
13 233
14 377
15 610
16 987
17 1597
18 2584
19 4181
20 6765
21 10946
22 17711
23 28657
24 46368
25 75025
26 121393
27 196418
28 317811
29 514229
30 832040
31 1346269
32 2178309
33 3524578
34 5702887
35 9227465
36 14930352
37 24157817
38 39088169
39 63245986
40 102334155
41 165580141
42 267914296
43 433494437
44 701408733
45 1134903170
46 1836311903
47 2971215073
48 4807526976
49 7778742049
50 12586269025
//這是計算了5分鐘左右能計算到第50個,越往后越慢,因為使用了遞歸的方式,每次的時間都是所有之前的時間的總和

//改良版本:
public class Fibonacci {
    
    
    public static long F1(int N) {
        if (N == 0) return 0;
        if (N == 1) return 1;
        long f = 0;
        long g = 1;
        for (int i = 0; i < N ; i++) {
            f = f + g;
            g = f - g;
        }
        return f;
    }
    
    public static void main(String[] args) { 
        for (int N = 0; N < 100; N++)
            System.out.println(N + " " + F1(N));
    }
}
/**需要注意的是,long類型大小限制,后面就會出現負數
81 37889062373143906
82 61305790721611591
83 99194853094755497
84 160500643816367088
85 259695496911122585
86 420196140727489673
87 679891637638612258
88 1100087778366101931
89 1779979416004714189
90 2880067194370816120
91 4660046610375530309
92 7540113804746346429
93 -6246583658587674878
94 1293530146158671551
95 -4953053512429003327
96 -3659523366270331776
97 -8612576878699335103
98 6174643828739884737
99 -2437933049959450366

所以需要替換成其他類型比如BigInteger

*/

1.1.20 編寫一個遞歸的靜態方法計算 ln(N!) 的值。

分析

背景知識

  • ln是數學中的對數符號。
    數學領域自然對數用ln表示,前一個字母是小寫的L(l),不是大寫的i(I)。
    ln 即自然對數 ln=loge a。
    e底數對數通常用于ln,而且e還是一個超越數
  • N!是N的階乘
  • 對數的運算法則:兩個正數的積的對數,等于同一底數的這兩個數的對數的和,即

題目中我們使用歸納法來解決問題
令f(N)= ln(N!)
f(1) = ln(1!) = 0
f(2) = ln(2!) = ln(2 * 1) = ln2 + ln1
f(3) = ln(3!) = ln(3 * 2 * 1) = ln3 + ln2 + ln1 = f2 + ln3
f(4) = ln(4!) = ln(4 * 3 * 2 * 1) = ln4 + ln3 + ln2 + ln1 = f(3) + ln4
f(5) = ln(5!) = ln(5 * 4 * 3 * 2 * 1) = ln5 + ln4 + ln3 + ln2 + ln1 = f(4) + ln5

...
f(n) = f(n-1) +lnn

答案

public static double logarithmic(int N) {
    if (N == 0)
        return 0;
    if (N == 1)
        return 0;
    return (logarithmic(N - 1)  + Math.log(N));
}

public static void main(String[] args) {
    double result = logarithmic(2);
    System.out.println(result);
}

代碼索引

Logarithmic.java

題目

1.1.21 編寫一段程序,從標準輸入按行讀取數據,其中每行都包含一個名字和兩個整數。然后用printf() 打印一張表格,每行的若干列數據包括名字、兩個整數和第一個整數除以第二個整數 的結果,精確到小數點后三位。可以用這種程序將棒球球手的擊球命中率或者學生的考試分數 制成表格。


1.1.21 Write a program that reads in lines from standard input with each line containing a name and two integers and then uses printf() to print a table with a column of the names, the integers, and the result of dividing the first by the second, accurate to three decimal places. You could use a program like this to tabulate batting averages for baseball players or grades for students.

分析

關于“標準輸入”,本書的庫提供了相應的類StdIn來處理,我們來看一下



書中也給出了相應的例子

public class Average
{
     public static void main(String[] args)
     {  // Average the numbers on StdIn.
        double sum = 0.0;
        int cnt = 0;
        while (!StdIn.isEmpty())
        {  // Read a number and cumulate the sum.
           sum += StdIn.readDouble();
           cnt++; 
        }
        double avg = sum / cnt;
        StdOut.printf("Average is %.5f\n", avg);
     }
}

答案

public static void main(String[] args) {
    int M = 3;
    int index = 0;
    String[] strs = new String[M];
    while (index < M)
        strs[index++] = StdIn.readLine();
    for (int i = 0; i < strs.length; ++i) {
        String[] arr = strs[i].split("\\s+");
        double temp = Double.parseDouble(arr[1]) / Double.parseDouble(arr[2]);
        StdOut.printf("%-10s   %-10s   %-10s   %-13.3f\n", arr[0], arr[1], arr[2], temp);
    }
}

代碼索引

Recursion.java

視頻講解

頂級程序員教你學算法(3) :遞歸(1.1.15-1.1.21)

廣告

我的首款個人開發的APP壁紙寶貝上線了,歡迎大家下載。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。