Given a non-negative integer, you could swap two digits at most once to get the maximum valued number. Return the maximum valued number you could get.
Example 1:
Input: 2736 Output: 7236 Explanation: Swap the number 2 and the number 7.
Example 2:
Input: 9973 Output: 9973 Explanation: No swap.
Note:
- The given number is in the range [0, 108]
這道題給了一個數字,我們有一次機會可以置換該數字中的任意兩位,讓返回置換后的最大值,當然如果當前數字就是最大值,也可以選擇不置換,直接返回原數。那么最簡單粗暴的方法當然就是將所有可能的置換都進行一遍,然后更新結果 res,取其中較大的數字,這樣一定會得到置換后的最大數字,這里使用了整型數和字符串之間的相互轉換,參見代碼如下:
解法一:
class Solution { public: int maximumSwap(int num) { string str = to_string(num); int res = num, n = str.size(); for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { swap(str[i], str[j]); res = max(res, stoi(str)); swap(str[i], str[j]); } } return res; } };
下面這種解法是一種更優解,思路是這樣的,由於希望置換后的數字最大,那么肯定最好的高位上的小數字和低位上的大數字進行置換,比如題目匯總的例子1。而如果高位上的都是大數字,像例子2那樣,很有可能就不需要置換。所以需要找到每個數字右邊的最大數字(包括其自身),這樣再從高位像低位遍歷,如果某一位上的數字小於其右邊的最大數字,說明需要調換,由於最大數字可能不止出現一次,這里希望能跟較低位的數字置換,這樣置換后的數字最大,所以就從低位向高位遍歷來找那個最大的數字,找到后進行調換即可。比如對於 1993 這個數:
1 9 9 3
9 9 9 3 (back數組)
9 9 1 3
我們建立好back數組后,從頭遍歷原數字,發現1比9小,於是從末尾往前找9,找到后一置換,就得到了 9913。
解法二:
class Solution { public: int maximumSwap(int num) { string res = to_string(num), back = res; for (int i = back.size() - 2; i >= 0; --i) { back[i] = max(back[i], back[i + 1]); } for (int i = 0; i < res.size(); ++i) { if (res[i] == back[i]) continue; for (int j = res.size() - 1; j > i; --j) { if (res[j] == back[i]) { swap(res[i], res[j]); return stoi(res); } } } return stoi(res); } };
下面這種解法建了十個桶,分別代表數字0到9,每個桶存該數字出現的最后一個位置,也就是低位。這樣從開頭開始遍歷數字上的每位上的數字,然后從大桶開始遍歷,如果該大桶的數字對應的位置大於當前數字的位置,說明低位的數字要大於當前高位上的數字,那么直接交換這兩個數字返回即可,其實核心思路跟上面的解法蠻像的,參見代碼如下:
解法三:
class Solution { public: int maximumSwap(int num) { string str = to_string(num); vector<int> buckets(10, 0); for (int i = 0; i < str.size(); ++i) { buckets[str[i] - '0'] = i; } for (int i = 0; i < str.size(); ++i) { for (int k = 9; k > str[i] - '0'; --k) { if (buckets[k] <= i) continue; swap(str[i], str[buckets[k]]); return stoi(str); } } return num; } };
我們也可以進一步的優化空間,實際上只關注兩個需要交換的位置即可,即高位上的小數字和低位上的大數字,分別用 pos1 和 pos2 指向其位置,均初始化為 -1,然后用一個指針 mx 指向低位最大數字的位置,初始化為 n-1,然后從倒數第二個數字開始往前遍歷,假如 str[i] 小於 str[mx],說明此時高位上的數字小於低位上的數字,是一對兒潛在可以交換的對象(但並不保證上最優解),此時將 pos1 和 pos2 分別賦值為 i 和 mx;若 str[i] 大於 str[mx],說明此時 str[mx] 不是低位最大數,將 mx 更新為 i。循環結束后,若 pos1 不為 -1,說明此時找到了可以交換的對象,而且找到的一定是最優解,直接交換即可,參見代碼如下:
解法四:
class Solution { public: int maximumSwap(int num) { string str = to_string(num); int n = str.size(), mx = n - 1, pos1 = -1, pos2 = -1; for (int i = n - 2; i >= 0; --i) { if (str[i] < str[mx]) { pos1 = i; pos2 = mx; } else if (str[i] > str[mx]) { mx = i; } } if (pos1 != -1) swap(str[pos1], str[pos2]); return stoi(str); } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/670
類似題目:
參考資料:
https://leetcode.com/problems/maximum-swap/
https://leetcode.com/problems/maximum-swap/discuss/107068/Java-simple-solution-O(n)-time
https://leetcode.com/problems/maximum-swap/discuss/107153/simple-c-using-stdstring-and-stdstoi