PTA上的練習復盤
java01 編程題作業感悟:
可以用ai指導自己怎么調試,但是不要把調代碼這過程里面的精華交給ai,就是自己去修正錯誤不能讓ai代勞!~~~
1 scanner.close()
Scanner *** = new Scanner(System.in);
***.close();
---------------------------------------------------------------------------------------------------------------------------------
所以后面真的要close的是這個***哦,以前我都會把***寫成scanner,還以為所有都是用scanner關閉?!
***表示我自己取的一個變量名
2 其實同scanner輸入的變量類型可以一起定義
前面我寫的是Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
其實也可以寫成
int a = sc.nextInt(),b = sc.nextInt();
3 調試打印方法
例如第一道編程題目里面的——————————————————————————————
調試語句 System.out.println("當前i=" + i + ", sum=" + sum);
4?case
?標簽的值不需要連續,也不需要按順序排列。它們可以是任意離散的值
常見誤區對比
? 錯誤認知:
// 誤以為 case 必須連續(如 1, 2, 3...)
switch (num) {case 1: // 正確case 2: // 正確case 3: // 正確case 5: // ? 誤以為不允許“跳過”4printf("Number is 1-3 or 5");break;
}
? 正確寫法:
// case 值可以是任意離散值
switch (num) {case 1: // 允許case 10: // 允許case 100: // 允許printf("Number is 1, 10, or 100");break;case 5: // 位置隨意,不影響邏輯printf("Number is 5");break;
}
關鍵點:
- 值的獨立性:每個?
case
?標簽只需要與?switch
?表達式的類型兼容(如整數、枚舉、字符串等)。 - 順序無關性:
case
?標簽的順序不影響匹配邏輯,匹配是通過值直接跳轉的。 - 合并規則:連續的?
case
?標簽(無代碼間隔)會共享同一個執行塊。
5 直觀感受自己的不足吧,對比解法!
好的,我將對比分析你的代碼與最優雅解法之間的差異,幫助你理解如何優化代碼結構和邏輯表達:
你的原始代碼(優化后)
java
public class Demo1_2 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int limit = scanner.nextInt(),speed = scanner.nextInt();switch (test(limit, speed)) {case 0:System.out.println("Congratulations, you are within the speed limit!");break;case 1:System.out.println("You are speeding and your fine is 100.");break;case 2:System.out.println("You are speeding and your fine is 270.");break;case 3:System.out.println("You are speeding and your fine is 500.");break;}scanner.close();}//我想用switch case語句來實現,需要怎么做?/*一個樸素的想法就是將不同的類型對應其規定為case*/public static int test(int a , int b){int fine = 0;int c =b-a;if(c>= 1&&c<= 20){fine =1;}else if(c>= 21&&c<= 30){fine =2;}else if(c>=31){fine =3;}return fine;}
最優雅解法的核心邏輯
java
int overSpeed = actualSpeed - speedLimit;
if (overSpeed <= 0) {System.out.println("未超速");
} else if (overSpeed <= 20) {System.out.println("罰款100");
} else if (overSpeed <= 30) {System.out.println("罰款270");
} else {System.out.println("罰款500");
}
對比分析
-
函數封裝 vs 直接處理
- 你的代碼:將邏輯封裝在獨立函數中,通過返回值(0-3)間接映射罰款金額。這種設計提高了代碼復用性,但增加了函數調用的復雜度。
- 優雅解法:直接在?
main
?方法中處理邏輯,減少了函數調用層級,代碼更扁平化。
-
條件判斷的簡潔性
- 你的代碼:使用?
c >= 1 && c <= 20
?等多條件組合,覆蓋每個區間的邊界值。 - 優雅解法:利用條件的互斥性,通過?
overSpeed <= 20
?→?overSpeed <= 30
?→ 否則 的順序,隱式覆蓋邊界值,減少了條件復雜度。
- 你的代碼:使用?
-
邊界值處理
- 你的代碼:顯式包含邊界值(如?
c >= 21 && c <= 30
),邏輯更嚴謹但代碼稍冗長。 - 優雅解法:通過條件順序(如先判斷?
<=20
?再判斷?<=30
)隱式處理邊界,代碼更簡潔。
- 你的代碼:顯式包含邊界值(如?
-
變量命名
- 你的代碼:使用?
a
、b
、c
?作為參數和中間變量,語義不明確。 - 優雅解法:使用?
speedLimit
、actualSpeed
、overSpeed
,代碼可讀性更高。
- 你的代碼:使用?
優雅解法的優勢
- 更少的抽象層次:避免了額外的函數封裝和返回值映射,直接輸出結果,符合 “最小化抽象” 原則。
- 自然的條件順序:利用條件的互斥性,按數值范圍從小到大判斷,邏輯更直觀。
- 更清晰的變量命名:直接反映業務含義(限速、實際速度、超速值),減少理解成本。
你的代碼的優勢
- 可復用性:獨立的?
test
?函數便于在其他場景中復用。 - 擴展性:若后續需要增加更多罰款等級,你的代碼結構更易于擴展(只需修改函數內部邏輯)。
總結建議
=========================================================================
- 優先選擇簡潔性:對于簡單問題(如本題),直接在?
main
?方法中處理邏輯更符合 “KISS 原則”(保持簡單愚蠢)。 - 權衡復用性:若邏輯可能在多處使用,封裝成獨立函數更合理。
- 優化變量命名:使用有明確語義的變量名(如?
overSpeed
)提升可讀性。 - 簡化條件判斷:利用條件的互斥性,通過順序判斷隱式處理邊界值。
6?在java里面我要輸入多組數據,每組數據進行一樣的處理,在這個輸入的代碼塊要怎么寫?
在 Java 中,處理多組輸入數據并對每組數據進行相同操作時,你可以使用循環結合 Scanner 類來實現。以下是幾種常見的實現方式:
1. 已知輸入組數
如果題目明確告訴你有多少組數據,可以先讀取組數,再用循環處理每組數據:
java
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt(); // 讀取數據組數for (int i = 0; i < n; i++) {// 讀取每組數據(示例:讀取兩個整數)int a = scanner.nextInt();int b = scanner.nextInt();// 處理數據(示例:輸出兩數之和)System.out.println(a + b);}scanner.close();}
}
2. 未知輸入組數,以特定條件結束(如輸入 0 結束)
如果題目沒有明確告訴你有多少組數據,但指定了結束條件,可以使用 while 循環:
java
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (true) {int a = scanner.nextInt();int b = scanner.nextInt();// 判斷是否為結束條件(示例:a和b都為0時結束)if (a == 0 && b == 0) {break;}// 處理數據System.out.println(a + b);}scanner.close();}
}
3. 未知輸入組數,以文件結束符(EOF)結束
在控制臺中,Windows 用戶可以按 Ctrl+Z,Linux/Mac 用戶可以按 Ctrl+D 來模擬 EOF:
java
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {int a = scanner.nextInt();int b = scanner.nextInt();// 處理數據System.out.println(a + b);}scanner.close();}
}
4. 每行包含多個數據,處理整行
如果每行包含多個數據,并且需要將整行作為一個字符串處理,可以使用?scanner.nextLine()
:
java
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String line = scanner.nextLine();// 處理整行數據(示例:分割字符串并求和)String[] parts = line.split(" ");int sum = 0;for (String part : parts) {sum += Integer.parseInt(part);}System.out.println(sum);}scanner.close();}
}
關鍵說明:
- Scanner 類:用于從標準輸入(控制臺)讀取數據。
- 循環結構:根據題目要求選擇?
for
?循環(已知組數)或?while
?循環(未知組數)。 - 結束條件:通過判斷特定值(如 0)或文件結束符(EOF)來終止循環。
- 數據讀取:使用?
nextInt()
、nextLine()
?等方法讀取不同類型的數據。
7?hasNextline是什么用法?(前面提到的)
在 Java 中,Scanner.hasNextLine()
?是?Scanner
?類的一個方法,用于檢查輸入源中是否還有下一行數據可讀。它通常與?Scanner.nextLine()
?方法結合使用,用于逐行讀取文本輸入。
基本用法
hasNextLine()
?返回一個布爾值:
true
:如果輸入源中還有下一行(即使是空白行)。false
:如果輸入源已結束(例如,用戶按下了?Ctrl+D
(Linux/Mac)或?Ctrl+Z
(Windows))。
示例 1:逐行讀取輸入
java
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 循環讀取每一行,直到輸入結束while (scanner.hasNextLine()) {String line = scanner.nextLine(); // 讀取整行文本System.out.println("你輸入的是:" + line);}scanner.close();}
}
運行效果:
輸入:Hello World
輸出:你輸入的是:Hello World
輸入:Java is cool
輸出:你輸入的是:Java is cool
(用戶按 Ctrl+D 結束輸入)
與其他方法的區別
-
hasNextLine()
?vs?hasNext()
:hasNextLine()
:檢查是否還有下一行(無論行內是否有內容)。hasNext()
:檢查是否還有下一個標記(默認以空格分隔)。
java
// 輸入:Hello World\nJava is cool scanner.hasNext(); // true(下一個標記是 "Hello") scanner.hasNextLine(); // true(下一行是 "Hello World")
-
nextLine()
?vs?next()
:nextLine()
:讀取一整行(包括換行符),返回的字符串不含換行符。next()
:讀取下一個標記(遇到空格、制表符或換行符停止)。
常見場景
場景 1:處理多行輸入
java
// 輸入:
// 1 2 3
// 4 5 6
// 7 8 9Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {String line = scanner.nextLine();String[] numbers = line.split(" ");for (String num : numbers) {System.out.println(Integer.parseInt(num));}
}
場景 2:跳過空行
java
while (scanner.hasNextLine()) {String line = scanner.nextLine();if (!line.isEmpty()) { // 忽略空行System.out.println("處理:" + line);}
}
注意事項
-
避免混用?
nextLine()
?和其他?nextXxx()
?方法:java
// 錯誤示例: int num = scanner.nextInt(); // 讀取整數后,換行符仍留在緩沖區 String line = scanner.nextLine(); // 直接讀取了剩余的換行符,導致跳過輸入// 正確做法: int num = scanner.nextInt(); scanner.nextLine(); // 手動消耗掉換行符 String line = scanner.nextLine();
-
輸入結束條件:
- 在控制臺中,用戶需要手動觸發結束(
Ctrl+D
?或?Ctrl+Z
)。 - 如果從文件讀取,文件結束時?
hasNextLine()
?自動返回?false
。
- 在控制臺中,用戶需要手動觸發結束(
總結
hasNextLine()
?是逐行讀取輸入的關鍵方法,適用于:
- 處理多行文本(如文件內容、多行輸入)。
- 需要整行處理的場景(如字符串分割、正則匹配)。
- 配合?
while
?循環實現連續讀取,直到輸入結束。
?8 最大公約數的算法思路
GCD(最大公約數)是指能夠同時整除兩個數的最大正整數。計算 GCD 最常用的方法是歐幾里得算法(輾轉相除法),其核心思想是:
對于兩個數 a 和 b(a > b),它們的 GCD 等于 b 和 a% b 的 GCD,遞歸或迭代這個過程直到余數為 0。
歐幾里得算法步驟:
- 用較大數除以較小數,得到余數。
- 用較小數除以余數,得到新的余數。
- 重復步驟 2,直到余數為 0。此時的除數就是 GCD。
示例:計算 GCD (48, 18)
- 48 ÷ 18 = 2 余 12 → GCD (48, 18) = GCD (18, 12)
- 18 ÷ 12 = 1 余 6 → GCD (18, 12) = GCD (12, 6)
- 12 ÷ 6 = 2 余 0 → GCD (12, 6) = 6
- 最終結果:GCD (48, 18) = 6
代碼實現(Java):
java
// 方法1:遞歸實現
public static int gcd(int a, int b) {if (b == 0) {return a;}return gcd(b, a % b);
}// 方法2:迭代實現(更高效)
public static int gcd(int a, int b) {while (b != 0) {int temp = b;b = a % b;a = temp;}return a;
}
關鍵點:
- 遞歸邏輯:
gcd(a, b) = gcd(b, a % b)
,直到b
為 0。 - 迭代邏輯:用臨時變量存儲中間值,不斷更新
a
和b
直到余數為 0。
9 有關最大公約數最小公倍數的代碼修正歷程?
關鍵改進點
-
移除冗余的首行讀取:
java
// 刪除了循環前的這兩行 // int x = scanner.nextInt(); // int y = scanner.nextInt();
-
使用?
trim()
?處理首尾空格:
trim是java里面很好的內置方法哦~~!
-
java
String line = scanner.nextLine().trim();
-
空行檢查:
java
if (line.isEmpty()) continue;
10 對比我的修正代碼和優雅代碼,感受進步
以下是一個更簡潔優雅的標準答案,我會標注與你代碼的主要區別:
優雅版代碼
import java.util.Scanner;public class Demo1_3 {public static void main(String[] args) {try (Scanner scanner = new Scanner(System.in)) { // try-with-resources自動關閉資源while (scanner.hasNextLine()) {String[] parts = scanner.nextLine().trim().split("\\s+"); // 使用\s+處理多個空格int a = Integer.parseInt(parts[0]);int b = Integer.parseInt(parts[1]);System.out.printf("%d %d%n", gcd(a, b), lcm(a, b)); // 格式化輸出}} catch (Exception e) {System.err.println("輸入格式錯誤: " + e.getMessage()); // 異常處理}}// 使用遞歸實現GCD (更簡潔)public static int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}public static int lcm(int a, int b) {return Math.abs(a * b) / gcd(a, b); // 處理負數情況}
}
我的代碼
import java.util.Arrays;
import java.util.Scanner;
public class {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String line = scanner.nextLine().trim(); // 讀取整行并去除首尾空格if (line.isEmpty()) continue; // 跳過空行//System.out.println("讀取的行: " + line);// 分割輸入的兩個整數String[] parts = line.split(" ");int a = Integer.parseInt(parts[0]);int b = Integer.parseInt(parts[1]);//System.out.println("分割后的部分: " + Arrays.toString(parts));// 計算GCD和LCMint gcdResult = gcd(a, b);int lcmResult = lcm(a, b);// 輸出結果System.out.println(gcdResult + " " + lcmResult);}}public static int lcm(int a, int b){// 先除后乘避免溢出return a / gcd(a, b) * b;}public static int gcd(int a,int b) {while (b != 0) {int temp = b;b = a % b;a = temp;}return a;}
}
與你代碼的主要區別
特性 | 你的代碼 | 優雅版代碼 |
---|---|---|
資源管理 | 手動調用scanner.close() | 使用try-with-resources 自動關閉 |
空格處理 | split(" ") | split("\\s+") (處理多個空格) |
GCD 實現 | 迭代循環 | 遞歸(更簡潔) |
異常處理 | 無 | 捕獲輸入格式錯誤 |
輸出格式 | 字符串拼接 | printf 格式化輸出 |
負數處理 | 未處理 | Math.abs() 處理負數 |
關鍵優化點解釋
-
資源自動關閉:
java
try (Scanner scanner = new Scanner(System.in)) {// 代碼塊結束時自動關閉scanner }
-
正則表達式處理空格:
java
split("\\s+") // 匹配任意數量的空白字符(空格、制表符等)
-
遞歸 GCD:
java
public static int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b); }
-
格式化輸出:
java
System.out.printf("%d %d%n", gcd, lcm); // 更清晰的輸出控制
-
異常處理:
java
catch (Exception e) {System.err.println("輸入格式錯誤: " + e.getMessage()); }
總結
你的代碼已經正確實現了核心功能,而優雅版代碼主要在以下方面進行了提升:
- 代碼簡潔性:遞歸、正則表達式減少了代碼量
- 健壯性:異常處理和資源管理更完善
- 可讀性:
printf
和方法設計更符合 Java 風格
在實際開發中,建議優先考慮代碼的健壯性和可維護性,而不是一味追求簡潔。你的實現已經很好,這些優化點可以作為未來的參考!
謝謝ai的點評,/(ㄒoㄒ)/~~,感覺就是這樣才能進步啊!!!加油!今天先寫到這里啦~明天繼續加油~!~!~!~!