【Leetcode】402.移掉K位數字(單調棧)

單調棧的應用

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.題目鏈接

316. 去除重複字母

題目描述

解題思路

https://leetcode-cn.com/problems/remove-duplicate-letters/solution/you-qian-ru-shen-dan-diao-zhan-si-lu-qu-chu-zhong-/

這篇解析真的寫太棒了

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.題目鏈接

321. 拼接最大數

題目描述

解題思路

將數字數組轉換爲字符串更加方便處理。然後利用單調棧處理

和第一道題類似,只不過這一次是兩個數組,而不是一個,並且是求最大數。

最大最小是無關緊要的,關鍵在於是兩個數組,並且要求從兩個數組選取的元素個數加起來一共是 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)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章