單調棧的應用
402. 移掉K位數字
316. 去除重複字母
321. 拼接最大數
1.題目鏈接
402. 移掉K位數字
題目描述
解題思路
貪心+單調棧
本題採用貪心思路+單調棧
- 如果字符串按照數字大小升序排列,只需要刪除最後K個字符即可;
- 如果非升序排列,需要從前到後遍歷,刪除字符串中每個逆序排列的字符。由於是從前到後遍歷,所以先刪除的一定是高位的數字,可以保證刪除後得到的最終數字最小。
- 舉例來說:如果字符串num = "123456789", k = 3,我們只需要刪除最後3個數字,得到"123456"。如果字符串num = "1432219", k = 3,需要從前到後遍歷查找逆序數字,進行刪除,第一個逆序數字爲'4',第二個逆序數字爲'3',第三個逆序數字爲第二個'2',最後得到"1219"。
所以可以採用棧實現,每次遍歷,判斷如果棧非空,且當前數字大於棧頂數字,且k還有剩餘(不爲0),將棧頂數字出棧。最後將當前數字入棧。
如果遍歷完成後,k仍有剩餘,則依次將棧頂數字出棧。最後棧中保存的數字即爲所求。按照從棧底到棧頂輸出即可。
注意:特判場景,如果最後所有數字均出棧,即棧爲空,需要返回"0"。
貪心+用String實現單調棧
對時間複雜度進行優化
AC代碼
貪心+單調棧
class Solution {
public String removeKdigits(String num, int k) {
if(k == 0) return num;
if(k == num.length()) return "0";
Stack<Character> s = new Stack<>();
int index = 0;
while(index < num.length()){
while(k > 0 && s.size() > 0 && num.charAt(index) < s.peek()){
s.pop();
k--;
}
//前導0情況的處理
if(s.size() == 0 && num.charAt(index) == '0') {
index++;
continue;
}
s.push(num.charAt(index));
index++;
}
String ans = "";
while(k > 0){
s.pop();
k--;
}
if(s.size() == 0) return "0";
while(!s.isEmpty()) ans += s.pop();
return new StringBuffer(ans).reverse().toString();
}
}
2.題目鏈接
題目描述
解題思路
這篇解析真的寫太棒了
AC代碼
class Solution {
public String removeDuplicateLetters(String s) {
char strToChar[] = s.toCharArray();
int[] countChar = new int[100];
StringBuffer tack = new StringBuffer();
boolean[] existChar = new boolean[100];
for(int i = 0; i < strToChar.length; i++){
countChar[strToChar[i]-'0']++;
}
Stack<Character> st = new Stack<>();
for(int i = 0; i < strToChar.length; i++){
countChar[strToChar[i]-'0']--;
//此行代碼很關鍵字,如果棧中已經存在該元素了,則直接跳過無需考慮,剛開始自己就是一直卡在這個條件判斷上,浪費了很多時間。
if(existChar[strToChar[i]-'0'] == true) continue;
while(st.size() > 0 && strToChar[i] < st.peek() && countChar[st.peek()-'0'] > 0){
existChar[st.pop()-'0'] = false;
}
st.push(strToChar[i]);
existChar[strToChar[i]-'0'] = true;
}
StringBuffer ans = new StringBuffer();
while(!st.isEmpty()) ans.append(st.pop());
return ans.reverse().toString();
}
}
利用Stringbuffer替代棧的功能(實現單調棧)
//利用Stringbuffer替代棧的功能
class Solution {
public String removeDuplicateLetters(String s) {
char strToChar[] = s.toCharArray();
int[] countChar = new int[100];
boolean[] existChar = new boolean[100];
StringBuffer tack = new StringBuffer();
for(int i = 0; i < strToChar.length; i++){
countChar[strToChar[i]-'0']++;
}
for(int i = 0; i < strToChar.length; i++){
countChar[strToChar[i]-'0']--;
if(existChar[strToChar[i]-'0']) continue;
while(tack.length() > 0 && strToChar[i] < tack.charAt(tack.length()-1) && countChar[tack.charAt(tack.length()-1)-'0'] > 0){
existChar[tack.charAt(tack.length()-1)-'0']=false;
tack.deleteCharAt(tack.length()-1);
}
tack.append(strToChar[i]);
existChar[strToChar[i]-'0'] = true;
}
return tack.toString();
}
}
3.題目鏈接
題目描述
解題思路
將數字數組轉換爲字符串更加方便處理。然後利用單調棧處理
和第一道題類似,只不過這一次是兩個數組,而不是一個,並且是求最大數。
最大最小是無關緊要的,關鍵在於是兩個數組,並且要求從兩個數組選取的元素個數加起來一共是 k。
然而在一個數組中取 k 個數字,並保持其最小(或者最大),我們已經會了(利用單調棧)。但是如果問題擴展到兩個,會有什麼變化呢?
實際上,問題本質並沒有發生變化。 假設我們從 nums1 中取了 k1 個,從 num2 中取了 k2 個,其中 k1 + k2 = k。而 k1 和 k2 這 兩個子問題我們是會解決的。由於這兩個子問題是相互獨立的,因此我們只需要分別求解,然後將結果合併即可。
假如 k1 和 k2 個數字,已經取出來了。那麼剩下要做的就是將這個長度分別爲 k1 和 k2 的數字,合併成一個長度爲 k 的數組合併成一個最大的數組。
以題目的 nums1 = [3, 4, 6, 5] nums2 = [9, 1, 2, 5, 8, 3] k = 5 爲例。 假如我們從 num1 中取出 1 個數字,那麼就要從 nums2 中取出 4 個數字。
運用第一題的方法,我們計算出應該取 nums1 的 [6],並取 nums2 的 [9,5,8,3]。 如何將 [6] 和 [9,5,8,3],使得數字儘可能大,並且保持相對位置不變呢?
實際上這個過程有點類似歸併排序中的治,而上面我們分別計算 num1 和 num2 的最大數的過程類似歸併排序中的分。
AC代碼
class Solution {
//利用單調棧求出nums數組中個數爲k的最大數
String pickMax(int[] nums,int k){
int tot = nums.length;
Stack<Integer> st = new Stack<>();
StringBuffer ans = new StringBuffer();
if(tot == k){
for(int i : nums) ans.append(i);
return ans.toString();
}
else if(k != 0){
for(int i = 0; i < nums.length; i++){
while(st.size() != 0 && nums[i] > st.peek()){
if(st.size() + tot > k) st.pop();
else break;
}
tot--;
if(st.size() < k) st.push(nums[i]);
}
}else return "";
while(!st.isEmpty()) {
ans.append(st.pop());
}
return ans.reverse().toString();
}
//類似歸併排序的過程,合併兩個字符串
String merge(String a,String b){
StringBuffer ans = new StringBuffer();
if(a.length()==0) return b;
if(b.length()==0) return a;
int aindex = 0;
int bindex = 0;
while(aindex < a.length() && bindex < b.length()){
if(a.charAt(aindex) > b.charAt(bindex)) ans.append(a.charAt(aindex++));
else if(a.charAt(aindex) < b.charAt(bindex)) ans.append(b.charAt(bindex++));
//當字符串當前字符相同時候,必須接着比較後序元素,例如a=[604]\b=[67]
else if(a.charAt(aindex) == b.charAt(bindex)){
int astart = aindex+1;
int bstart = bindex+1;
boolean flag = false;
while(astart < a.length() && bstart<b.length()){
flag = false;
if(a.charAt(astart) == b.charAt(bstart)){
astart++;
bstart++;
}else if(a.charAt(astart) < b.charAt(bstart)){
flag = true;
ans.append(b.charAt(bindex++));
break;
}else{
ans.append(a.charAt(aindex++));
flag = true;
break;
}
}
if(astart==a.length()&&bstart<b.length()) ans.append(b.charAt(bindex++));
else if(astart<a.length()&&bstart==b.length()) ans.append(a.charAt(aindex++));
else if(flag == false) ans.append(a.charAt(aindex++));
}
}
while(aindex<a.length()) ans.append(a.charAt(aindex++));
while(bindex<b.length()) ans.append(b.charAt(bindex++));
return ans.toString();
}
//對比兩個字符串的大小,取大者
String cmp(String a,String b){
if(a.length()==0) return b;
if(b.length()==0) return a;
for(int i = 0; i < a.length(); i++){
if(a.charAt(i) < b.charAt(i)) return b;
else if(a.charAt(i) > b.charAt(i)) return a;
}
return a;
}
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int len1 = nums1.length;
int len2 = nums2.length;
String a1 = "";
String a2 = "";
String ans = "";
for(int i = 0; i <= k; i++){
if(i <= len1 && k-i <= len2){
a1 = pickMax(nums1,i);
a2 = pickMax(nums2,k-i);
//System.out.println("a1:"+a1);
//System.out.println("a2:"+a2);
String temp = merge(a1,a2);
//System.out.println(temp);
ans = cmp(ans,temp);
//System.out.println("dd:"+ans);
}
}
int p[] = new int[ans.length()];
for(int i = 0; i < p.length;i++) p[i]=ans.charAt(i)-'0';
return p;
}
}
//參考答案2
class Solution {
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int m = nums1.length;
int n = nums2.length;
int[] ans = new int[k];
int len = Math.min(k, m);
for (int i=Math.max(0, k-n); i<=len; i++) {
int[] sub1 = maxKArray(nums1, i);
int[] sub2 = maxKArray(nums2, k-i);
int[] array = combineArray(sub1, sub2, k);
for (int j=0; j<k; j++) {
if (array[j] == ans[j]) continue;
if (array[j] > ans[j]) ans = array;
break;
}
}
return ans;
}
public int[] maxKArray(int[] nums, int k) {
if (k == 0) return new int[0];
int[] res = new int[k];
int cursor = -1;
for (int i=0; i<nums.length; i++) {
while (cursor>=0 && nums[i]>res[cursor] && nums.length-i>k-cursor-1) {
cursor--;
}
if (cursor < k-1)
res[++cursor] = nums[i];
}
return res;
}
public int[] combineArray(int[] nums1, int[] nums2, int k) {
int[] res = new int[k];
int i = 0;
int i1 = 0;
int i2 = 0;
while (i1 < nums1.length && i2 < nums2.length)
res[i++] = deepCompare(nums1, nums2, i1, i2)? nums1[i1++] : nums2[i2++];
while (i1 < nums1.length)
res[i++] = nums1[i1++];
while (i2 < nums2.length)
res[i++] = nums2[i2++];
return res;
}
public boolean deepCompare(int[] nums1, int[] nums2, int i1, int i2) {
while (i1 < nums1.length && i2 < nums2.length) {
if (nums1[i1] == nums2[i2]) {
i1++;
i2++;
continue;
}
return nums1[i1] > nums2[i2];
}
return i1 < nums1.length;
}
}
作者:chidehang
鏈接:https://leetcode-cn.com/problems/create-maximum-number/solution/java-chai-fen-zi-wen-ti-he-bing-qiu-jie-by-chideha/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。