當前快看:第三章《數組與循環》第8節:數組與循環經典例題

2022-12-30 10:33:15 來源:51CTO博客

?利用數組和循環可以解決很多經典問題,比如對數字的查找、排列、篩選等。本小節甄選了其中一些有代表性的問題集中進行講解,認真學習這些經典例題不僅有助于鞏固Java語言的相關知識點,還對提高邏輯思維能力有很大幫助。

3.8.1求整數位數

題目:由用戶從控制臺上任意輸入一個整數,求其位數(例如輸入168,運算結果為3)。?

根據Java語言整數之間的除法運算規則可知:任何一個整數被10整除所得的商都比原數字少1位,商再被10整除,又少1位。當商為0時,再被10整除多少次,位數都不變。被10整除多少次使得商成為0,整數就是多少位。例如整數168,第1次整除后商為16,第2次整除后商為1,第3次整除后商為0。總共經過3次被10整除得到商為0,所以168就是3位數。?


(相關資料圖)

根據這個解題原理,可以把程序大致分為下面的幾個部分:?

聲明一個整型變量count作為計數器,其初始值設為0,用來統計整數位數。?把輸入的整數用10整除,如果商不為0,則對商繼續用10整除,直到商為0為止。每做一次整除操作,計數器自增1。作經過n次整除,當商為0時,計數器的值就是整數位數。?如果用戶輸入的整數是0,特殊處理一下?

以下是求整數位數程序的完整代碼:?

【例03_15 求整數位數】

Exam03_15.java?

import java.util.Scanner;public class Exam03_15 {  public static void main(String[] args) {    Scanner sc = new Scanner(System.in);    int num;// 被求位數的整數    int count = 0;// 位數計數器    System.out.println("請輸入一個整數");    num = sc.nextInt();    if (num != 0) {      while (num != 0) {// 如果num還沒變成0        num = num / 10;// 反復被10整除        count++;// 位數計數器+1      }    } else {// 對數字0特殊處理      count = 1;    }    System.out.println("該數字是一個" + count + "位數");  }}

3.8.2串數求和

題目:求a+aa+aaa+...+aa...a的值,其中a是1位正整數,加數的個數以字母n表示,n≥1。a與n的值由用戶輸入。?

為幫助讀者理解題目,此處先用一個具體的數值作為舉例。假設a的值是2,n的值是5,套用題目中對a和n的解釋,a+aa+aaa+...+aa...a就表示2+22+222+2222+22222。如果把這些要相加的數字以豎式的形式表示,則可以得到如圖3-5的表示形式:?

圖3-5 串數求和豎式表示形式?

如果分別以個位、十位、百位的順序來看圖3-5的豎式,可以看出:豎式中出現了n個2,n-1個20,n-2個200...。也就是說:2+22+222+...+22...2等于2×n+20×(n-1)+200×(n-2)…。進一步觀察發現:2×n+20×(n-1)+200×(n-2)…是由n個乘法表達式組成,從前到后,各個乘法表達式遵循的規律是:被乘數都是前一表達式的10倍,而乘數每次減少1。如果表達式中的數字2被替換成1-9中的任意數字,就可求出a+aa+aaa+...+aa...a的值。以下是求a+aa+aaa+...+aa...a值的程序代碼:?

【例03_16 串數求和】

Exam03_16.java?

import java.util.Scanner;public class Exam03_16 {  public static void main(String[] args) {    Scanner sc = new Scanner(System.in);    int a, n;// 聲明a和n    int sum = 0;// 最終求和的結果    System.out.println("請輸入a的值");    a = sc.nextInt();    System.out.println("請輸入n的值");    n = sc.nextInt();    while (n >= 1) {      sum = sum + a * n;      a = a * 10;// 使a擴大10倍,為下一輪計算做準備      n--;// 使n減去1,為下一輪計算做準備    }    System.out.println("總和為:" + sum);  }}

3.8.3猴子吃桃

題目:猴子第1天摘下若干個桃子,當即吃了一半后又多吃了1個。第2天又將剩下的桃子吃掉一半,又多吃了1個。以后每天都吃了前1天剩下的一半零1個。到第10天想再吃時,見只剩下1個桃子了。求第1天共摘了多少桃。?

通過分析題目不難看出:每天桃子的數量都有兩種,分別是“吃以前”的數量和“吃以后”的數量。題目中已知第10天桃子的數量是“吃以前”的數量,并且要求解的也是第1天“吃以前”桃子的數量,因此解題過程中只討論每天“吃以前”桃子的數量。以循環方式倒推,以第10天為起點,循環1次可算出第9天桃子的數量,循環2次可算出第8天桃子數量,按此規律,共循環9次可算出最初桃子的數量。假設每天桃子的數量是num,根據猴子吃桃的規律可知,前1天桃子的數量是(num+1)×2,并且num的初始值是1。按照這樣的規律就可很快求出第1天猴子摘了多少桃。以下是本題目的程序代碼:?

【例03_17 猴子吃桃】

Exam03_17.java?

public class Exam03_17 {  public static void main(String[] args) {    int num = 1;// 最后一天桃子的數量    for (int i = 1; i <= 9; i++) {// 循環9次可推算出第1天桃子的數量      num = (num + 1) * 2;    }    System.out.println("第1天摘了" + num + "個桃子");  }}

經程序計算,第1天摘了1534個桃子,各位讀者可對照這個答案驗證程序是否編寫正確。?

3.8.4找到最大值

題目:由用戶任意輸入5個整數,保存到數組中,編寫程序找到這五個數中的最大值。?

通過閱讀題目可知,本題目的程序可以分為兩部分:?

1、用戶輸入數據并保存到數組中 ?

2、從數組中找到最大值?

輸入數據的操作很簡單,下面重點分析如何找到所輸入數據的最大值。從數組中找到最大值的過程,就如同是武俠小說中擂臺比武一樣。在眾多比武者中,先有一個站上擂臺,后續的比武的人輪番登臺,打勝者留在臺上,敗者下臺。當所有參與比武的人都登臺比武一次后,留在擂臺上的就是最強者。?

具體實現過程如下:?

設定一個變量max,其初始值設為數組下標為0的數組元素?從數組中下標為1開始到數組最后一個元素為止,用循環的方式依次取出每個數組元素與變量max做比較,如果取出的數組元素比max的值更大,則用該元素的值替換max的值?比較結束后,max變量的值即是數組中的最大值?

以下是實現找到最大元素的完整代碼:?

【例03_18找到最大值】

Exam03_18.java?

import java.util.Scanner;public class Exam03_18 {  public static void main(String[] args) {    Scanner sc = new Scanner(System.in);    int array[] = new int[5];// 用于保存元素的數組    int max;    for (int i = 0; i < 5; i++) {// 循環輸入5個整數      System.out.println("請輸入第" + (i + 1) + "個整數");      array[i] = sc.nextInt();    }    max = array[0];// 以數組頭一個元素初始化max    for (int i = 1; i < array.length; i++) {// 輪番比較      if (array[i] > max) {// 如當前元素比max的值還大        max = array[i];// 以當前元素替換max值      }    }    System.out.println("最大值是" + max);  }}

3.8.5數字排列

題目:用1、2、3、4四個數字,能組成多少個每位都互不相同的三位數?都是多少??

分析題目可知:個位、十位、百位三個位上都有1、2、3、4四種可能。根據嵌套循環的執行特點,可以用3層循環把個位、十位、百位所有排列組合的可能性全部列舉一遍。具體做法是:分別用外、中、內3層循環分別列舉出百位、十位和個位數字的4種可能性。同時以一個int型變量count作為計數器,每當排列出一種組合的可能性,都判斷一下這種組合是否滿足三個位上的數值互不相同的條件。如果滿足這個條件,則打印數字,并且給計數器加1。當三層循環全部運行結束,計數器的值就是所有滿足條件三位數的個數。數字排列的程序代碼如下:?

【例03_19數字排列】

Exam03_19.java?

public class Exam03_19 {  public static void main(String[] args) {    int count = 0;// 計數器    for (int x = 1; x <= 4; x++) {// 列舉百位的4種可能性      for (int y = 1; y <= 4; y++) {// 列舉十位的4種可能性        for (int z = 1; z <= 4; z++) {// 列舉個位的4種可能性          if (x != y && y != z && x != z) {// 判斷3個位數是否出現重復            count++;// 每排列出一個個十百位都不重復的數字都使計數器+1            System.out.println(x * 100 + y * 10 + z);// 打印符合條件的數字          }        }      }    }    System.out.println("共有" + count + "個三位數");  }}

這個題目是典型的用循環窮舉所有可能性的題目,它的正確答案是排列出24個滿足條件的三位數。與之相類似的題目還有很多,例如:用足夠多的可自行思考如何編寫程序解出該題目。?

3.8.6打印楊輝三角

題目:打印出如下楊輝三角形,總共打印9行?

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

......

為幫助讀者解答題目,此處先簡單介紹一下楊輝三角。楊輝三角是二項式系數在三角形中的一種幾何排列。楊輝三角的特征是:第1行有1個數字,第2行有2個數字...以此類推,第n行有n個數字。每行最左邊和最右邊的數字都是1,其余數字是其左上方和正上方數字之和。打印楊輝三角的操作可以借助內外兩層嵌套循環以及二維數組來實現,具體實現辦法是:?

外層負責循環控制打印行數,內層循環控負責制打印每行的數字?每打印一個數字,都把這個數字存放到二維數組的對應位置上?根據楊輝三角的形狀可知,存儲數字的二維數組每一行長度并不相同,因此可以首先把二維數組初始化為n行。每次執行外層循環時,根據每一行存儲數字的個數單獨初始化二維數組各行的長度。即第1次執行外層循環時,單獨初始化二維數組的第1行長度為1。第2次執行外層循環時,初始化二維數組的第2行長度為2,以此類推。?我們把要打印的數字稱為“當前數字”。在打印過程中,需要判斷當前數字的位置,如果當前數字位于本行的首位或末位,直接把數字1存入數組。否則從二維數組中取出當前數字左上方和正上方的數字并求和,這個和就是當前數字的值。把計算出的當前數字存入數組的對應位置,之后打印當前數字。?

以下是打印楊輝三角的完整代碼:?

【例03_20打印楊輝三角】

Exam03_20.java?

public class Exam03_20 {  public static void main(String[] args) {    int[][] array = new int[9][];// 創建一個9行的二維數組    for (int i = 0; i < array.length; i++) {      array[i] = new int[i + 1];// 為每個一維數組單獨指定長度      for (int j = 0; j <= i; j++) {        if (j == 0 || j == i) {// 每行首位和末位的數字都是1          array[i][j] = 1;        } else {// 計算本行其余數字          array[i][j] = array[i - 1][j - 1] + array[i - 1][j];        }        System.out.print(array[i][j] + "\t");      }      System.out.println();    }  }}

3.8.7冒泡排序

題目:由用戶從控制臺任意輸入5個整數并存入數組,并用冒泡排序將數組中的數字按從小到大的方式排列。?

由題目可知,本題分兩步完成,分別是輸入數字存入數組和用冒泡排序對數字完成排列。輸入數字并存入數組的操作很簡單,下面重點講述冒泡排序的實現原理。冒泡排序的基本思想是: 讓較大的數慢慢往后排,較小的數慢慢往前排。這個排序的具體操作過程為:?

從數組的頭部開始,依次比較相鄰的兩個元素,如果前一個元素比后一個大,則交換這兩個元素。?假設數組長度為n,則經過n-1次比較和交換后,數組中最大的元素就被交換到了數組的末尾,第1輪操作完成。?把步驟1反復執行n-1次,但每執行1次,比較的次數都比上一輪減少1次。減少比較次數的理由是:第1輪經過n-1次比較和交換,數組最大的元素已經到達數組末尾,因此第2輪比較時,位于數組末尾的最大元素可以不用再次參與比較和交換的操作。同理,第2輪操作完成后,數組中第2大的元素就到達了數組的倒數第2位,因此第3輪操作時,數組中最后2個元素可以不用參與比較和交換,之后的輪次也是如此。?整個操作過程可以用兩層嵌套循環完成,外層循環負責控制比較多少輪,內層循環負責比較和交換兩個相鄰元素。?

圖3-6展示了冒泡排序的基本算法原理?

圖3-6 冒泡排序原理?

圖3-6中演示了如何用冒泡排序完成5個數組元素的排序過程。圖中每個方框表示一輪操作,每輪操作中深色背景的元素為當前要進行比較和交換的兩個相鄰元素。?

以下是冒泡排序的完成程序代碼:?

【例03_21冒泡排序】

Exam03_21.java?

import java.util.Scanner;public class Exam03_21 {  public static void main(String[] args) {    Scanner sc = new Scanner(System.in);    int[] data = new int[5];    for (int i = 0; i < data.length; i++) {      System.out.println("請輸入第" + (i + 1) + "個整數");      data[i] = sc.nextInt();    }    for (int i = 0; i < data.length - 1; i++) {// n個數字進行n-1輪比較      for (int j = 0; j < data.length - 1 - i; j++) {        if (data[j] > data[j + 1]) {// 比較相鄰兩個數字          int temp = data[j];          data[j] = data[j + 1];          data[j + 1] = temp;        }      }    }    System.out.println("元素排序之后是:");    for (int x : data) {      System.out.print(x + "  ");    }  }}

3.8.8猴子分桃

題目:海灘上有一堆桃子,5只猴子來分。第1只猴子把這堆桃子平均分為5份,多了1個,它把多的1個扔入海中,拿走了1份。第2只猴子把剩下的桃子又平均分成5份,又多了1個,它同樣把多的1個扔入海中,拿走了1份,第3、4、5只猴子都是這樣做的,問海灘上原來最少有多少桃子??

通過閱讀題目可知:假設每只猴子在分桃子時看到的桃子的數量是total,則這只猴子之前那只猴子看到的桃子數量是 total × 5/4 + 1,根據這個規律,以及分桃子的次數就可以倒推出最初海灘上最初有多少桃。但這需要知道第5只猴子分桃時看到的桃子的數量,這個數是多少?暫且不知,但可以肯定的是,第5只猴子至少拿走1只桃子。繼續深入分析題目可以得知:第2-5只猴子分桃時看到桃子的數量total一定是4的倍數,因為前一只猴子都是把桃子平均分成5份,留下4份。而第1只猴子看到的桃子數量不一定是4的倍數,因為它是把并不是從4堆平均分好的桃子中取走了自己的一份。?

經過以上分析可以確定解題思路可以分為兩大步:?

一、用循環的方式推算每只猴子看到的桃子數。?

二、根據之前的分析結果檢驗推算的正確性:如果第2-5只猴子看到的桃子數量total是4的倍數,那么推算所得到的桃子個數一定是合理的。因此,第2-5只猴子看到的桃子數量是否是4的倍數是檢驗推算結果合理性的判斷依據。?

如何推算每只猴子看到的桃子數量呢?題目中并沒有說第5只猴子看到多少桃,但可以肯定:第5只猴子至少拿走1只桃。假設第5只猴子拿走num個桃子,那么它看到桃子的數量就是num × 5 + 1。再由第5只猴子看到桃子的數量,就可以推算出前面任意一只猴子所看到桃子的數量。所以,在整個推算過程中,是以第5只猴子“拿走”桃子的數量為起點進行推算的。?

程序中可以用變量num來表示第5只猴子拿走桃子的數量。首先設置num的初始值為1,也就是說先假設第5只猴子拿走了1只桃子,以此為起點推算每只猴子看到桃子的數量。每推算一次,都要檢驗推算結果的合理性。推算過程中一旦檢驗出第2-5只猴子所看到的桃子數量不是4的倍數,就可以知道最初設定的num值是不正確的,于是立刻停止本輪推算操作,然后用一個比num值大1的數字代替num,重新開始新一輪推算的過程。具體來說就是:最初以num的值為1開始進行推算,邊推算邊檢測,如果發現num為1的情況下,推算出的第2-5只猴子看到桃子的數量不是4的倍數,就把num的值換成2重新開始推算,如果num的值為2仍不能推算出滿足條件的結果,則繼續把num的值換成3再次重新開始推算,直到以某個num的值為起點推算出第2-5只猴子所看到的桃子數量都是4的倍數,就可以進一步推算得到第1只猴子所看到的桃子數量,這個數量就是海灘上原來有多少桃子,此時就可以徹底結束推算操作。?

實際編碼過程中,可以設置一個變量checkedTime作為計數器,每開始新一輪推算時都設置它的初始值為0。在每一輪推算過程中,從第5只猴子開始,每當算出1只猴子所看到桃子數量,就看看這個數量是不是4的倍數。如果是的話,就進行checkedTime++的操作。當某一輪推算過程中,checkedTime達到了4,則說明第2-5這4只猴子所看到的桃子數量都是4的倍數,進而說明這一輪推算已經得到了正確解,推算操作可以徹底結束。?

在實際推算開始之前無法預知實際需要推算多少輪才能得到正解。程序可以用while循環完成反復推算的過程,只要未獲得正解,就一輪一輪的推算下去。一旦求得正解,用break語句中止循環即可。具體做法是,設置一個boolean型變量finish,其初始值為false,表示還沒有獲得正解。設置while循環的條件為“finish==false”,這樣只要finish的值為false,虛擬機就會一直執行循環。當某一輪循環中checkedTime的值達到4,就表明已經獲得正解,此時設置finish的值為true即可中止循環。以下是猴子分桃程序的完整代碼:?

【例03_22猴子分桃】

Exam03_22.java?

public class Exam03_22 {  public static void main(String[] args) {    int num = 1; // 第5只猴子拿走桃子的數量    int total = 0;// 每只猴子看到的桃子的數量    boolean finish = false;    while (finish == false) {// 沒有獲得合理解就不停止循環      int checkedTime = 0;// 計數器      for (int i = 5; i >= 1; i--) {        if (i == 5) {// 推算第5只猴子看到桃子的數量          total = num * 5 + 1;        } else {// 推算第1-4只猴子看到桃子的數量          total = total * 5 / 4 + 1;        }        if (total % 4 != 0) {// 如果發現某只猴子看到桃子數不是4的倍數          break;// 停止循環開始下一輪推算        } else {          checkedTime++;// 推算出一次合理結果就執行checkedTime++        }      }      if (checkedTime == 4) {// 本輪如果達到連續4次推算說明已獲得正解        finish = true;      } else {// 本輪推算未獲得正解        num++;// num++為下一輪推算做準備      }    }    System.out.println("第5只猴子拿走" + num + "只桃子");    System.out.println("海灘上總共有" + total + "只桃子");  }}

本程序的運行結果是:?

第5只猴子拿走255只桃子?海灘上總共有3121只桃子?

需要提醒各位讀者:程序此處得到的正解并非唯一解,而是正解中的最小值。其實如果不控制循環,使之無限運行,可以算出無數個滿足條件的桃子數量。

除此文字版教程外,小伙伴們還可以??點擊這里??觀看我在本站的視頻課程學習Java。

標簽: 冒泡排序 滿足條件

上一篇:每日動態!開始使用 VMware Tanzu Mission Control 和 Tanzu Kubernetes Grid
下一篇:視點!算法的時間、空間復雜度如何比較?