C# 算法題系列(二) 各位相加、整數反轉、回文數、羅馬數字轉整數
各位相加
給定一個非負整數 num
,反復將各個位上的數字相加,直到結果為一位數。
示例:
輸入: 38 輸出: 2 解釋: 各位相加的過程為:3 + 8 = 11, 1 + 1 = 2。 由於 2 是一位數,所以返回 2。
進階:
你可以不使用循環或者遞歸,且在 O(1) 時間復雜度內解決這個問題嗎?
題目地址 https://leetcode-cn.com/problems/add-digits/
代碼模板
public class Solution { public int AddDigits(int num) { } }
測試數據
輸入 1 輸出 1 輸入 10 輸出1 輸入 38 輸出 2 輸入 199 輸出 1 輸入 8888 輸出 5
筆者的方法:
使用了while循環,除一次計算一次,原始數和各位數和同時變化。時間在70ms內。
public static int Csum(int num) { if (num < 10) //小於10的數直接返回 return num; int shi = 0; //記錄個位數相加 while (num > 0) {if (num >= 10) { shi += num % 10; num = num / 10; } else if (num < 10) { shi += num; num = num / 10; } if (shi >= 10) shi = shi % 10+ shi / 10; //超過10的個位數重新變化 } return shi; }
方法二 棄九驗算法
同樣在 60-70ms
public class Solution { public int AddDigits(int num) { if(num==0) return 0; if(num%9==0) return 9; return num%9; } }
整數反轉
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
示例 1:
輸入: 123 輸出: 321
示例 2:
輸入: -123 輸出: -321
示例 3:
輸入: 120 輸出: 21
註意:
假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍為 [?231, 231 ? 1]。請根據這個假設,如果反轉後整數溢出那麽就返回 0
題目地址 https://leetcode-cn.com/problems/reverse-integer/
代碼模板
public class Solution { public int Reverse(int x) { } }
筆者方法 68ms左右
public class Solution { public int Reverse(int x) { int num = 0; while (x != 0) { int i = x % 10; x = x / 10; //C# int32 範圍 [-2147483647~2147483647] if (num > int.MaxValue / 10 ) return 0; if (num < int.MinValue / 10) return 0; num = num * 10 + i; } return num; } }
回文數
判斷一個整數是否是回文數。回文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。
題目地址:https://leetcode-cn.com/problems/palindrome-number
示例 1:
輸入: 121 輸出: true
示例 2:
輸入: -121 輸出: false 解釋: 從左向右讀, 為 -121 。 從右向左讀, 為 121- 。因此它不是一個回文數。
示例 3:
輸入: 10 輸出: false 解釋: 從右向左讀, 為 01 。因此它不是一個回文數。
進階:
你能不將整數轉為字符串來解決這個問題嗎?
代碼模板
public class Solution { public bool IsPalindrome(int x) { } }
筆者的代碼
運行時間在120ms左右,筆者的思路是:如果一個數字的反序還是等於這個數,那麽這個數就是回文數。
以下代碼無法解決反序後可能溢出,可以利用上一題的代碼進行溢出檢查。
當然,一個int類型的數,如果是回文,那麽他的反序肯定不會溢出,反之其反序發生溢出則肯定不是回文數。
public class Solution { public bool IsPalindrome(int x) { if (x < 0) return false; int xx = x; int num = 0; //x的反序 while (xx != 0) //求反序 { int i = xx % 10; xx = xx / 10; num = num * 10 + i; } if (x == num) //如果x的反序num==x,那麽這個數字是回文數 return true; else return false; } }
加try-catch,耗時增加 10~20ms
try { while (xx != 0) { int i = xx % 10; xx = xx / 10; num = num * 10 + i; } } catch { return false; }
官方這道題給出了示例代碼,耗時120ms左右,思路是只反序一半,反序後的原始數、反序一半的數進行比較,也就不用檢查溢出。
public class Solution { public bool IsPalindrome(int x) { // 特殊情況: // 如上所述,當 x < 0 時,x 不是回文數。 // 同樣地,如果數字的最後一位是 0,為了使該數字為回文, // 則其第一位數字也應該是 0 // 只有 0 滿足這一屬性 if(x < 0 || (x % 10 == 0 && x != 0)) { return false; } int revertedNumber = 0; while(x > revertedNumber) { revertedNumber = revertedNumber * 10 + x % 10; x /= 10; } // 當數字長度為奇數時,我們可以通過 revertedNumber/10 去除處於中位的數字。 // 例如,當輸入為 12321 時,在 while 循環的末尾我們可以得到 x = 12,revertedNumber = 123, // 由於處於中位的數字不影響回文(它總是與自己相等),所以我們可以簡單地將其去除。 return x == revertedNumber || x == revertedNumber/10; } }
別人用字符串方式進行判斷(雖然題目說不能用string),耗時150-180ms,不太穩定
public class Solution { public bool IsPalindrome(int x) { string str = x.ToString(); for (int i = 0; i < str.Length / 2; ++i) { if (str[i] != str[str.Length - 1 - i]) { return false; } } return true; } }
羅馬數字轉整數
羅馬數字包含以下七種字符: I
, V
, X
, L
,C
,D
和 M
。
題目地址 https://leetcode-cn.com/problems/roman-to-integer/submissions/
字符 數值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
例如, 羅馬數字 2 寫做 II
,即為兩個並列的 1。12 寫做 XII
,即為 X
+ II
。 27 寫做 XXVII
, 即為 XX
+ V
+ II
。
通常情況下,羅馬數字中小的數字在大的數字的右邊。但也存在特例,例如 4 不寫做 IIII
,而是 IV
。數字 1 在數字 5 的左邊,所表示的數等於大數 5 減小數 1 得到的數值 4 。同樣地,數字 9 表示為 IX
。這個特殊的規則只適用於以下六種情況:
I
可以放在V
(5) 和X
(10) 的左邊,來表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左邊,來表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左邊,來表示 400 和 900。
給定一個羅馬數字,將其轉換成整數。輸入確保在 1 到 3999 的範圍內。
示例 1:
輸入: "III" 輸出: 3
示例 2:
輸入: "IV" 輸出: 4
示例 3:
輸入: "IX" 輸出: 9
示例 4:
輸入: "LVIII" 輸出: 58 解釋: L = 50, V= 5, III = 3.
示例 5:
輸入: "MCMXCIV" 輸出: 1994 解釋: M = 1000, CM = 900, XC = 90, IV = 4.
筆者的方法:
時間200ms左右,
思路是
- 把所有的情況放到哈希表中
- 每次取一個位
- 把 i 和 i+1 放一起,試試有沒有區配的,有的話把 i 和 i+1 放一起
- 沒有的話,就是 只是計 i
public class Solution { public int RomanToInt(string s) { char[] c = s.ToCharArray(); //將其轉為字符數組 int sum = 0; //值 Hashtable hashtable = new Hashtable(); //7個基本單位 hashtable.Add("I", 1); hashtable.Add("V", 5); hashtable.Add("X", 10); hashtable.Add("L", 50); hashtable.Add("C", 100); hashtable.Add("D", 500); hashtable.Add("M", 1000); //加上6種情況 hashtable.Add("IV", 4); hashtable.Add("IX", 9); hashtable.Add("XL", 40); hashtable.Add("XC", 90); hashtable.Add("CD", 400); hashtable.Add("CM", 900);
/*
* 六種情況
IV 4 IX 9
XL 40 XC 90
CD 400 CM 9000
*/
for (int i = 0; i < c.Length; i++) { if (i + 1 < c.Length && hashtable.ContainsKey(c[i].ToString() + c[i + 1].ToString())) //如果發現兩位一起能區配的話 { sum += int.Parse(hashtable[c[i].ToString() + c[i + 1].ToString()].ToString()); //獲取值,HashTable的類型都是Object! i++; //跳兩位 } else { sum += int.Parse(hashtable[c[i].ToString()].ToString()); } } return sum; } }
換成字典
public class Solution { public int RomanToInt(string s) { char[] c = s.ToCharArray(); //將其轉為字符數組 int sum = 0; //值 Dictionary<string, int> dictionary = new Dictionary<string, int>(); //7個基本單位 dictionary.Add("I", 1); dictionary.Add("V", 5); dictionary.Add("X", 10); dictionary.Add("L", 50); dictionary.Add("C", 100); dictionary.Add("D", 500); dictionary.Add("M", 1000); //加上6種情況 dictionary.Add("IV", 4); dictionary.Add("IX", 9); dictionary.Add("XL", 40); dictionary.Add("XC", 90); dictionary.Add("CD", 400); dictionary.Add("CM", 900);
/*
* 六種情況
IV 4 IX 9
XL 40 XC 90
CD 400 CM 9000
*/
for (int i = 0; i < c.Length; i++) { if (i + 1 < c.Length && dictionary.ContainsKey(c[i].ToString() + c[i + 1])) //如果發現兩位一起能區配的話 { sum += dictionary[c[i].ToString() + c[i + 1].ToString()]; //獲取值,HashTable的類型都是Object! i++; //跳兩位 } else { sum += dictionary[c[i].ToString()]; } } return sum; } }
以上兩個例子都會進行較多的裝箱拆箱,下面主要使用if-else,switch,空間花銷較大,但是如果測試例子較多,進行大量計算,時間會相對少一點。
public class Solution { public int RomanToInt(string s) { int sum = 0; //值 for (int i = 0; i < s.Length; i++) { if (i + 1 < s.Length) //如果後面還有別的字符 { if (s[i] == ‘I‘) { int a = 0; switch (s[i + 1]) //"i%" { case ‘V‘: a = 4; i++; break; case ‘X‘: a = 9; i++; break; default: a = 1; break; } sum += a; } else if (s[i] == ‘X‘) { int a = 0; switch (s[i + 1]) //"X%" { case ‘L‘: a = 40; i++; break; case ‘C‘: a = 90; i++; break; default: a = 10; break; } sum += a; } else if (s[i] == ‘C‘) { int a = 0; switch (s[i + 1]) //"X%" { case ‘D‘: a = 400; i++; break; case ‘M‘: a = 900; i++; break; default: a = 100; break; } sum += a; } else { int a = 0; switch (s[i]) {case ‘V‘: a = 5; break;case ‘L‘: a = 50; break;case ‘D‘: a = 500; break; case ‘M‘: a = 1000; break; } sum += a; } } else { int a = 0; switch (s[i]) { case ‘I‘: a = 1; break; case ‘V‘: a = 5; break; case ‘X‘: a = 10; break; case ‘L‘: a = 50; break; case ‘C‘: a = 100; break; case ‘D‘: a = 500; break; case ‘M‘: a = 1000; break; } sum += a; } } return sum; } }
C# 算法題系列(二) 各位相加、整數反轉、回文數、羅馬數字轉整數