# ALG101 筆記 ###### tags: `程式導師` `題目` ## 寫在解題之前 面對同個題目,如果有不同的輸入範圍,就會需要不同的解法。 就像同樣是蓋房子,要蓋出 101 跟蓋出雙層公寓,需要的材料與建造方式就會不同。 而不同的範圍代表的是不同的限制: - 空間限制 - 時間限制 - 型態限制 ### 空間限制 如果程式有分變數型態的話,通常佔用的記憶體容量會像這樣: - int:4 bytes - double: 8 bytes - JS 中的 Number:8 bytes(不分浮點數或整數) ### 時間限制 這部分跟時間複雜度相關,可看看[這個筆記](https://notcommon.coderbridge.io/2020/06/17/cook-algorithm-complexity/) ### 型態限制 - int: -2147483648 ~ 2147483647(也就是 $-2^{31}$ ~ $2^{31}-1$) - 查看 JS Number 可儲存的最大整數:`Number.MAX_SAFE_INTEGER` - 查看 JS Number 可儲存的最小整數:`Number.MIN_SAFE_INTEGER` - 在 JS 檢查整數是否在可使用的安全範圍內:`.Number.isSafeInteger(<整數>)` - 浮點數精準度問題,例如 `0.1 + 0.2 === 0.3 //false` 因為溢出問題。 **因此開始解題前,要記得問清楚題目範圍。** pseudo code 大概長這樣 ```javascript for (i from 1 to 100) do if i < 100 then print i i = i + 1 end if end for ``` ## Unit 1 ### 1.5 實戰練習:fizz buzz 老師的 pseudo code 寫錯了?只能列出 1~n 中 3、5、15 倍數的數。 但題目要的是 1 ~ n 都列出 ```javascript for (i from 1 to n) do if (i mod 5 === 0 && i mod 3 === 0) then print "FizzBuzz" else if (i mod 5 === 0) then print "Buzz" else if (i mod 3 === 0) then print "Fizz" else print i end if end for ``` 但這不是最好的解答,可以 Google 找一下 ### 1.6 實戰練習:找最小值 假設今天有一副撲克牌,一次只能抽一張起來看,你會怎麼找到最小的牌? ```javascript let min = arr[0] for (i from 0 to n-1) do if (arr[i] < min) do min = arr[i] end if end for ``` ### 1.7 作業 #### 1. 字串反轉 給一個字串 str,請輸出 str 反過來的結果 範例輸入:hello 範例輸出:olleh PS. 可以用 str[i] 取得第 i 個字,例如說 str="abc",str[0] 就是 'a' ```javascript let newStr = "" for (i from 0 to -(str's length)) do put str[i] in newStr end for //檢討:想著反過來,連結字串,直接用 + 的就可以 let s = '' for (i from n-1 to 0) do s+= str[i] end for ``` #### 2. 陣列總和 給一個陣列 arr,裡面全都包含了數字(整數),請輸出陣列加總的結果(總和保證不超過 int 範圍) 範例輸入:[1, 2, 3] 範例輸出:6 ```javascript let sum = 0 for (i from 0 to n-1) do sum = sum + arr[i] end for print sum ``` #### 3. 找最大值 給一個陣列 arr,裡面全都包含了數字(整數),請輸出陣列中的最大值 範例輸入:[1, 2, 3] 範例輸出:3 ```javascript let max = 0 for (i from 0 to n-1) do if (max < str[i]) then max = str[i] end if end for print max ``` ## Unit 2 ### 作業 1. 找次小值,用文字寫下步驟流程 ```javascript let arr = [10, 8, 6] let min = Infinity let min2 = Infinity for(let i=0; i<arr.length; i++) { if (arr[i] < min) { min2 = min min = arr[i] } else if (arr[i] < min2) { min2 = arr[i] } } console.log(min, min2) ``` 1. i 是 0,arr[0] = 10,min 無限大,min2 無限大 2. 因為 10 小於無限大 3. min2 = min = 無限大 4. min = arr[0] = 10 5. i 小於陣列長度(3),進入 for 迴圈 6. i 是 1,arr[1] = 8,min=10、min2=無限大 7. 因為 8 小於 10 8. 所以 min2=10、min=8 9. i 小於陣列長(3),進入 for 迴圈 10. i=2、arr[2]=6、min=8、min2=10 11. 因為 arr[2]=6 小於 min=8 12. 所以 min2=8、min=6 ### 作業 2. 大小寫互換,用文字寫下步驟流程 ```javascript let str = "hELLo" let ans = '' for(let i=0; i<str.length; i++) { let code = str.charCodeAt(i) if (code >= 97 && code <= 122) { ans += String.fromCharCode(code - 32) } else if (code >= 65 && code <= 90) { ans += String.fromCharCode(code + 32) } else { ans += str[i] } } console.log(ans) ``` 1. str 是要轉換的字串 2. ans 是要印出的字串 3. 用迴圈的方式遍歷整個字串,目前 i=0 4. 當 i < 字串長度,繼續下一步驟,否則跳到步驟 11 5. 找出 str[i] 代表的 ASCII code,設定為 code 6. 檢查 code 數字是否大於等於 97 或 小於等於 122? 7. 是的話,就將 code - 32,並轉回英文字母,放入 ans 字串內(大寫轉小寫),i 加 1,回到第 4 步驟 8. 不是的話,檢查 code 數字是否大於等於 65 或 小於等於 90? 9. 是的話,就將 code + 32,並轉回英文字母,放入 ans 字串內(小寫轉大寫),加 1,回到第 4 步驟 10. 不是的話,就直接放入 ans 字串內,i 加 1,回到第 4 步驟 11. 印出 ans ### 作業 3. 印出因數 ```javascript let num = 30 for(let i=1; i<=num; i++) { if (num % i === 0) { console.log(i) } } ``` 1. 要檢查到 30 2. 用迴圈的方式從 1 檢查到 30,目前是 i 是 1 3. 當 i 小於等於 30,繼續下一步驟,否則結束迴圈 5. 如果 30 除以 i 的餘數是 0,就印出 i,i 加 1,回到第 3 步驟 6. 如果 30 除以 i 的餘數不是 0,i 加 1,回到第 3 步驟 ## Unit 3 給三個題目,用白話文改寫 並且提出在解題中有可能碰到的問題或是需要特別注意的地方 1. [LIOJ 1010:靈魂伴侶](https://oj.lidemy.com/problem/1010?_ga=2.128507980.1723475375.1592060559-1459048255.1590328535) 是要判斷兩個數字是否相等,但題目已有限制兩數字的最大值與最小值,在 JS 安全範圍之內,因此沒有需要額外說明的數字範圍。 2. [LIOJ 1015:音速小子]() 是要把輸入值乘以 34,因為最大值在安全範圍內,所以沒有需要額外說明的數字範圍。 3. [LIOJ 1017:貪婪的小偷]() 從現有的物品中,從價值最高的開始挑,直到達到能帶走的最大數量,由於 (能帶走的最大值是 1000)*(物品的最大價值是 100000),最大數字總和是 $10^8$ 在 JS 安全數字範圍內,所以沒有要額外說明的數字範圍。 --- **LidemyOJ JavaScript 讀取輸入列模板** ```javascript var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin }); var lines = [] // 讀取到一行,先把這一行加進去 lines 陣列,最後再一起處理 rl.on('line', function (line) { lines.push(line) }); // 輸入結束,開始針對 lines 做處理 rl.on('close', function() { solve(lines) }) // 上面都不用管,只需要完成這個 function 就好,可以透過 lines[i] 拿取內容 function solve(lines) { } ``` ## Unit 5 ### Unit 5-3 - 計算生命靈數的重點在於,需要一直重複執行「加總」,所以就專門寫一個 function 處理加總。 - 由於條件規定總和必須是一位數(也就是小於 10),如果不是一位數,就要進入加總 function 再處理。 - 由於不確定該加總多少次,所以不適用 if 條件式。要用 while,只要設定一個條件(沒小於 10),就進入加總。 - 值得注意的是,如何重複丟入加總後的數字? ```javascript= var readline = require('readline'); const { count } = require('console'); var rl = readline.createInterface({ input: process.stdin }); var lines = [] // 讀取到一行,先把這一行加進去 lines 陣列,最後再一起處理 rl.on('line', function (line) { lines.push(line) }); // 輸入結束,開始針對 lines 做處理 rl.on('close', function() { solve(lines) }) // 上面都不用管,只需要完成這個 function 就好,可以透過 lines[i] 拿取內容 function solve(lines) { var stringArr = lines[0].split(" "); //["1991", "11", "7"] var numbers = Number(stringArr[0] + stringArr[1] + stringArr[2]); var spiritNumber = countSum(numbers); while (spiritNumber >= 10) { spiritNumber = countSum(spiritNumber); } console.log(spiritNumber); } //以這個純數字處理的寫法比較合理,適合處理 Number 型態的迴圈 const countSum = (number) => { var sum = 0; while (number % 10 != 0) { sum += number % 10; number = Math.floor(number / 10); } return sum; } //這是以字串迴圈的想法處理 Number 型態迴圈,雖然一樣可行,但處理數字的話,就用上面的方式 const countSum = (number) => { var sum = 0; var str = String(number); for (var i =0; i<str.length; i++) { sum += Number(str[i]); } return sum; } ``` ## Unit 6:實作內建函式 ### `Array.map()` 傳入一個陣列後,`.map()` 會對陣列中每個元素統一執行特定動作,再以一個新陣列回傳結果。 ```javascript function double(n) { return n*2; } let arr = [1, 2, 3] let newArr = arr.map(double) function map(arr, callback) { let result = [] for(let i=0; i<arr.length; i++) { result[i] = callback(arr[i]) } return result } console.log(map([1, 2, 3], double)) ``` ## `String.repeat()` 依據輸入的數字,作為重複字串的次數。 ```javascript function repeat(str, n) { let result = "" for(let i=0; i<n; i++) { result+=str } return result } console.log(repeat('abc', 3)) //"abcabcabc" ``` ## `Array.lastIndexOf()` 依據輸入的內容,在 array 中倒著找該內容的 index 位置。 功能跟 `.indexOf()` 相同,只是找的順序不同。 ```javascript function lastIndexOf(array, target) { for (var i=array.length-1; i>=0; i--) { if (array[i] === target) { return i; } } return -1; // 這部分要特別注意,必須放在 for 迴圈之外,財表示整個迴圈都沒有,因此回傳 -1 } console.log(lastIndexOf([2, 1, 2], 3)) ```