發表文章

目前顯示的是 8月, 2021的文章

[Java] JDBC 使用 MySQL ResultSet

圖片
練習 JDBC 使用 MySQL,將 SELECT * FROM table; 的結果存成 ResultSet ,然後將他一個個 .next() 取出來每一 row 的數據,再getInt() 或 getString()。 我的練習是有三個 table 分別為 player、weapon_list 和 armor_list,在 player 中每一筆資料是角色,有名字有等級有職業有武器和防具,而武器防具只存放 id,要連去另外兩個 table weapon_list 和 armor_list 用的。練習 SELECT * FROM player; 將每個角色的資料印出來時,本來的打算是 getInt(weapon_id) 用這個去別的 table 再 query 一次取名字出來(或更進一步取攻擊力防禦力之類的),像這樣: Connection connection = DriverManager.getConnection("...."); Statement statement = connection.createStatement(); ResultSet result = statement.executeQuery("SELECT * FROM player"); while(result.next()) { ResultSet weaponQuery = statement.executeQuery("SELECT name FROM weapon_list WHERE id=" + result.getInt("weapon_id")); String weaponTemp = weaponQuery.getString("name"); ResultSet armorQuery = statement.executeQuery("SELECT name FROM armor_list WHERE id=" + result.getInt("armor_id")); String armorTemp = armorQuery.getString("name"); ...

[Java] GUI 滑鼠左右鍵控制

圖片
前天想試試看把 Windows 經典的踩地雷寫出來,規則不多也有想法,就是卡在滑鼠點擊這個上面,方案換來換去終於完成 版本一,用 JButton 和 ActionListener 要點擊的地面,最初很直覺既然是按鍵的樣子就用 JButton 來做,造了 JButton[81] (以最簡單的 9x9 有 81 格),其中每個 JButton 都 addActionListener(JFrame),然後 JFrame.add(JButton) 都是在 for 中完成,然後 @override 中 for 裡 if(e.getSource()==JButton[i]){ } 去處理這些地被點擊該做的事,算蠻輕鬆的。 大概是: public class MyFrame extends JFrame implements ActionListener { JButton button; MyFrame() { for(int i=0; i 但是很快地發現我還有右鍵功能啊,找了一下似乎沒辦法,在 stackOverFlow 上發現也有人問同樣的問題,而且他也說他要做踩地雷 XD (不過我沒有去找過任何範本)從網友的回應得知可以從 MouseListener 中用 SwingUtilities.isRightMouseClick(e) 這個方向去解決。 版本二,用 JLabel 和 MouseListener 其實最早還是用 JButton 配 SwingUtilities.isRightMouseClick(),是後來想反正左鍵功能也要用 SwingUtilities 做了那就改 JLabel ,但又覺得 JButton 有個按下的回饋感還蠻不錯的,應該還會改回去。這個方案其實很快也發現行不通了,因為我找不到點擊後直接指定哪一塊地有動作。 大概是: public class MyFrame extends JFrame implements ActionListener { JLabel label; MyFrame() { for(int i=0; i 我沒辦法在最裡面的 if 收到按下右鍵時,拿 i 來處理事情,被說 scope 看不到 i,超乎我想像。結果就是這方法行不通。 版本三,JFram...

palindrome 迴文判斷兩方向

圖片
初次解 LeetCode 的 5. Longest Palindromic Substring ,沒有想太多,把迴文的判斷一個個用上,有更長的就取代掉舊的,結果沒有 time out 但成果非常不滿意,耗時 1966ms ,只快過 5.01% 的投稿,記憶體耗 39.7 MB,只少於 41.33% 的投稿。不得不思考有沒有別的方法。原本我在過去幾題迴文的判斷,是因為已經知道字串的起點與終點,所以我是「由外而內包夾」,如下: 在這題的想法是這樣的,先讓 i 由 index = 1 到 index = 全長 - 2,(選擇 1 開始是因為當整個字串不存在任何迴文部份時,s.charAt(0) 至少是一個單獨迴文且最長的字串,全長 - 2 是保留往後探訪的空間),例如上圖 i = 1 ,再往後一層 j for loop,j 從最後也就是右邊,逐一往左愈來愈小,形成一個「包夾」檢查是否迴文,所以下一個檢查 index = 1+1 和 index = 5-1,接著是 1+2 和 5-2 ,當兩個指標相等或左右順序相反則跳出 while ,這樣的迴文字串夠長的話就賦值給 result ,全部跑完則 return 。 雖然有加入一些判斷例如 i 太大(接近右邊尾聲)如果已經得到的迴文長度 max 也夠大,即使再找出新的迴文恐怕長度也不長就沒有意義,所以 i + max > s.length() 就結束,但還是耗費相當大的時間。 考慮到由外而內包夾的方法,可能在外層有前後對應,但是靠向中間一旦沒迴文的對稱時,那之前的步驟都付諸流水,不如把迴文的判斷「由內向外」試試看,不過這又牽扯迴文的字數是奇數還是偶數,奇數如 ABCBA、0987890,而偶數如ABCCBA、12344321: 奇數時: 例如上圖 i = 3,檢查前一個和後一個也就是 index = 2 和 index = 4,有相同時開始做迴文檢查,逐步往外 index = 1 和 index = 5,直到不再對稱,長度夠長就賦值給 result。 偶數時: 例如上圖 i = 3,檢查下一個 i = 4 是否相等,是就開始做迴文檢查逐步往外 index = 2 和 index = 5,是再繼續 index = 1 和 index = 6,直到不再對稱。 這樣做的表現非常的好,投稿後耗時...

效能檢討

圖片
之前在 CodeWars 上解完 Kata 後都會 submit 看別人的答案,我覺得很多沒思考到的方法都是這樣學到的,然而無論是 Python 或 Java 都會看到很神奇的一行解法,沒有一行也能夠在我寫上數十行的題目下簡單兩三行搞定,真的非常佩服。 最近在 LeetCode 解 58. Length of Last Word ,題目要求將一個字串(很長是句子甚至文章),找出最後一個單字的長度。我覺得這很容易呀?將 String 以空白字元 " " split 後,取字串陣列的最後一項,return 他的字長。 輕鬆完成 submit 後看到這樣的結果: 花費時間只贏過 52.84% 的投稿,花費記憶體贏過 30.25% 的投稿,雖然之前我沒有在注意這個評比,也知道這結果非常不理想。再想想看能不能改進,就想說從句子字串最後面往回找,碰到空白就停,稍微處理特殊的情形(最後就是連續空白字元),如此再重新 submit 一個沒有用到內建那些方便方法的版本 成果很明顯,花費時間贏過 100% 的投稿,花費記憶體贏過 82.20% 的投稿。 另外像 125. Valid Palindrome 這一題,要求判斷一個字串句子不考慮空白與符號,只看字母和數字,是否為迴文?這題最初我利用 String 的 replaceAll() 把非字母與數字的全取代成空字元 "",toLowerCase() 轉小寫,然後 for loop 從最初到中間,檢查是否前後一致,來判斷是否迴文。然後這是投稿結果: 時間贏過 29.25% 的投稿,記憶體贏過 20.14% 的投稿。真的是慘不忍睹。再三思考後我用 2 pointer 一個指前,一個指後,遇到非字母數字則跳過向中間靠攏,是字母或數字再來轉小寫字母比較,省去多數不必要動作,成果是: 時間贏過 98.23% 投稿,記憶體贏過 50.47% 投稿。 除了拼 Acceptance 還有效能可以挑戰的,又多了一個樂趣。

Pow(x,n)實作

圖片
這是 LeetCode 第50題: LeetCode Problem 50. Pow(x,n) ,初探 LeetCode 寫了兩三題 Easy 後看到這個想說這難度怎麼有 Medium?就稍微寫了一下 Recursive Function,大概就是 myPow(x,n) 處理 base part n=0 傳回1,然後 n > 0 時 return x * myPow(x,n-1),而 n < 0 時 return 1/myPow(x, n*-1) submit 後 LeetCode 很不客氣用 n = 2 32 - 1 ,回給我 time out。被殺個措手不及。 是我太大意了,既然這麼大的數字那能不能用一半一半 log 2 n 呢?想了一下指數律就好了不難,x n = ( x n/2 ) * ( x n/2 ) * ( x n%2 ) 第二次我就把 recursive part 改成 return myPow(x,n/2) * myPow(x,n/2) * myPow(x, n%2) ,但是仍然 time out 。 這樣都還是逾時真的超乎我想像,但想一想再更精簡一點,用空間換時間,就不要算兩次,我先 double q = myPow(x,n/2) 和 double r = myPow(x,n%2),然後 return q*q*r ,過了。 但一樣又打了回來,下一個關卡是 LeetCode 用 n = - 2 32 讓我回家。 我的錯,他是知道有人會在 n < 0 時 return 1/myPow(x, n*-1) 嗎?Integer.MIN_VALUE 變號就直接炸了,確實學了一課。 最後送過是我把 base part 考慮完整一點,n=0 return 1,n=1 return x,n=-1 return 1/x,拿掉 n < 0 的部份,全部交由對半分的遞迴去跑。很感動的第五次給過了。 LeetCode 玩下來會比 CodeWars 多了一個挫敗感,因為 LeetCode 會把失敗的投稿算入,真的要在自己機器上考量萬全再送,不然表現慘不忍睹。好處就是怎麼死的?是因為什麼 input 而失敗? LeetCode 給得很清楚,可針對症狀下藥,這部份 CodeWars 就常常鬼打牆了。

[Java] 指針型時鐘

圖片
其實這是高中時的一個題目,當時是在玩 Visual Basic,當時沒有完成。現在用 java GUI 完成,而 JFrame 和 Graphics2D 的知識來自 YouTuber Bro Code ,寫的還蠻順的,20年前抓取系統時間都還是自00:00:00到現在經過的秒數,要自己換算時、分與秒,現在 java 的 LocalTime().now() 都有很方便的 getHour()、getMinute()等功能,剩下的就是將時間換成坐標畫圖了。 坐標的部份問題是,一般數學上的極坐標和直角坐標轉換,是依據直角坐標上有向角的定義,標準位置角是從 x 軸正向逆時針旋轉,而且 x 軸以右為正,y 軸以上為正。 只是畫刻度倒沒什麼關係,還是可以用一般直角坐標上的換算,但是指針的走向就不行了,時鐘上是從 π/2 而且是順時針旋轉,再加上電腦上的 y 軸是往下為正(左上 left top 為 (0, 0),x 往右增加,y 往下增加)所以要想個辦法轉換成電腦上的,數學上極坐標[ r , θ ] 換成直角坐標是( r cos θ , r sin θ ),如下左圖: 而電腦上是上右圖。整理一下差別: 數學上: x 軸向右為正,y 軸向上為正 角度逆時針為正角 從 x 軸正向開始為始邊(時鐘3點方向開始) 電腦上模擬時鐘: x 軸向右為正,y 軸向下為正 角度順時針為正角 從 y 軸負向開始為始邊(時鐘12點方向開始) 我的想法是若 y 反方向改為向下為正,那轉換就改為 ( h + r cos θ , k - r sin θ ) ,此處 ( h , k ) 為平移,也就是圓參數式的圓心。再來順時針的問題,就將 θ 改為 - θ,負角就是改為逆時針取角。最後角度的起始點是 12 點,也就是一般直角坐標的90度,那就將角度加上 π/2 ,這樣會得到 t 秒時的坐標為 ( h + r cos ( π/2 - ( 2πt/60)) , k - r cos ( π/2 - ( 2πt/60))) 到此大致比較大的問題都解決了,剩下就是一些微調和新功能,例如:將 timer 設為 100 也...