1. 程式人生 > Android開發 >演演算法基礎篇-關於棧的演演算法題分析(二)

演演算法基礎篇-關於棧的演演算法題分析(二)

算法系列篇章-可以參照如下順序閱讀

1.字串編碼

給定一個經過編碼的字串,返回它解碼後的字串。

編碼規則為: k[encoded_string],表示其中方括號內部的encoded_string 正好重複 k 次。注意 k 保證為正整數。

你可以認為輸入字串總是有效的;輸入字串中沒有額外的空格,且輸入的方括號總是符合格式要求的。

此外,你可以認為原始資料不包含數字,所有的數字只表示重複的次數k ,例如不會出現像 3a2[4] 的輸入。

// 示例
s = "3[a]2[bc]",返回 "aaabcbc".
s = "3[a2[c]]",返回 "accaccacc".
s = "2[abc]3[cd]ef",返回 "abcabccdcdcdef".
複製程式碼

1.1 思路分析

  • 例如:12[a]為例;
  • 1.遍歷字串S,如果當前字元不為方括號"]" 則入棧stack1中;
  • 2.如果當前字元遇到了方括號"]"則:
  • ① 首先找到要複製的字元,例如stack="12[a"
    ,那麼我要首先獲取字元a;將這個a儲存在另外一個棧去tempStack;
  • ② 接下來,要找到需要備份的數量,因為出棧過字元"a",則當前的top指向了"[",也就是等於2;
  • ③ 而12對於字串是2個字元,我們要通過遍歷找到數字12top上限/下限的位置索引,此時上限curTop = 2,下限通過出棧,top = -1;
  • 根據範圍[-1,2],讀取出12儲存到strOfInt字串中來,並且將字元"12\0",轉化成數字12;
  • ⑤ 當前top=-1,將tempStack中的字元a,複製12份入棧到stack中來;
  • ⑥ 為當前的stack擴容,在stack字元的末尾新增字元結束符合'\0';

1.2 程式碼實現

char *stringCode(char * inputCode){
    // 1.獲取字串的長度
    int length = (int)strlen(inputCode);
    // 2.初始棧空間
    int size = 50;
    // 3.建立一個棧 存放操作字串
    char *stack = (char *)malloc(sizeof(char)*size);
    // 4.初始化棧頂位置
    int top = -1;
    
    // 5.遍歷字串 知道找到']',全部入棧
    for(int i = 0; i < length; i++){
        char temp = inputCode[i];
        if (temp != ']') { // 6.如果沒有找到
            // 6.1 判定超界情況 如果當前棧頂位置已經到了最後一個空間 對棧擴容
            if(top == size - 1){
                stack = realloc(stack,(size += 50)*sizeof(char));
            }
            
            // 6.2 沒有超界的情況全部入棧
            stack[++top] = inputCode[i];
        
        }else{ // 7.已經找到 ']'
            // 7.1 先獲取要編碼的字串 對stack做出棧查詢 直到找到'['
            // 建立臨時編碼棧
            int tempSize = 10;
            int tempTop = -1;
            char *tempCode = (char *)malloc(sizeof(char)*tempSize);
            while (stack[top] != '[') {
                // 7.2 超界處理
                if(tempTop == tempSize - 1){
                    tempCode = realloc(tempCode,(tempSize += 10)*sizeof(char));
                }
                // 7.3 沒有超界的時候將字串壓棧
                tempTop++;
                tempCode[tempTop] = stack[top];
                top--;
            }
            
            
            // 8.繼續查詢 需要編碼的次數
            char strInt[11];
            // 8.1 記錄當前棧頂位置
            int curTop = top;
            // 8.2 移除'['之後
            top--;
            // 9. 繼續查詢棧直到top=-1
            while(top != -1 && stack[top] >= '0' && stack[top] <= '9'){
                top--;
            }
            
            // 10.找到數字的前一個位置 和 '['的位置之後,拿到該數字
            for(int j = top+1; j < curTop; j++){
                strInt[j - (top+1)] = stack[j];
            }
            
            // 11.新增結束字元
            strInt[curTop] = '\0';
            // 12.獲取當前的編碼次數
            int curNum = atoi(strInt);
            
            // 13.開始編碼
            for (int k = 0; k < curNum; k++) {
                // 14.將結果儲存在stack中
                int kk = tempTop;
                while (kk != -1) {
                    if (top == size - 1) { // 超界處理
                        stack = realloc(stack,(size += 50)*sizeof(char));
                    }
                    top++;
                    stack[top] = tempCode[kk];
                    kk--;
                }
            }
            // 15.釋放臨時編碼陣列
            free(tempCode);
            tempCode = NULL;
        }
    }
    
    return stack;
}
複製程式碼

1.3 結果預期

2.去除重複字母 Leetcode 316

2.1 題目概述

給你一個僅包含小寫字母的字串,請你去除字串中重複的字母,使得每個字母只出現一次。需保證返回結果的字典序最小(要求不能打亂其他字元的相對位置)

示例1: 輸入:"bcabc" 輸出:"abc" 示例2: 輸入:"cbacdcbc" 輸出:"acdb"

2.2 字典序

字典序: 字串之間比較和數字比較不一樣; 字串比較是從頭往後挨個字元比較,那個字串大取決於兩個字串中第一個對應不相等的字元; 例如 任意一個a開頭的字串都大於任意一個b開頭的字串;例如字典中apple 大於 book;

  • 題目的意思,你去除重複字母后,需要按最小的字典序返回.並且不能打亂其他字母的相對位置;
  • 例如 bcabc 你應該返回abc,而不是bca,cab;
  • 例如 cbacdcbc 應該返回acdb,而不是cbad,bacd,adcb
  • 例如 zab,應該返回zab,而不是abz;

2.3 利用棧思想的思路分析

  • 1.針對字串資料異常的處理
  • 2.用一個record陣列來儲存字串中每個字元出現的次數
  • 3.分配一個棧陣列空間stack來儲存去除重複字母的結果,並利用它的特性幫助我們找到正確的次序;
  • 4.遍歷字串s
  • 5.從0~top,遍歷stack 判斷當前字元s[i]是否存在於棧stack中 如果當前字元是否存在於棧的定義一個falg 標記isExist,0表示不存在,1表示存在
  • 6.如果isExist存在,record[s[i]]位置上的出現次數減一,並繼續遍歷下一個字元; 表示當前的stack已經有這個字元了沒有必要處理這個重複的字母;
  • 7.如果isExist不存在,則需要迴圈一個找到一個正確的位置,然後在儲存起來,跳過棧中所有比當前字元大、且後面還會出現的元素,然後將當前字元入棧,通過一個while迴圈找到將棧中位置錯誤的資料,出棧.找當前合適的位置,則結束while迴圈,找到合理的位置後,則將當前字元s[i]入棧;
  • 8.直到遍歷完所有字元後,則為字串棧stack 新增一個結束符'\0',並返回當前字串首地址;

2.4 利用棧思想的程式碼實現

char *removeDuplicateLetters(char *S){
    // 1. 處理資料異常情況
    if (strlen(S) <= 1) {
        return S;
    }
    int length = (int)strlen(S);
    int top = -1;
    
    // 2.定義一個record陣列用來儲存字元出現的次數
    char record[26] = {0};
    for (int i = 0; i < length; i++) {
        record[S[i] - 'a']++;
    }
    
    // 3.分配一個棧陣列空間stack來儲存去除重複字母的結果 已'\0'為結束符
    char *stack = (char *)malloc(length * sizeof(char) + 1);
    /* memset(void *s,int ch,size_t n) 將stack len*2*sizeof(char)長度範圍的空間填充0; */
    memset(stack,0,length * sizeof(char) + 1);
    
    // 4.遍歷字串
    for (int i = 0; i < length; i++) {
        /* isExist 標記,判斷當前字元是否存在棧中  */
         int  isExist = 0;
         /*
          ①從0~top,遍歷stack 判斷當前字元s[i]是否存在於棧stack中
           如果當前字元是否存在於棧的flag,0表示不存在,1表示存在
           top指向棧頂(也是執行stack字串最後一個字元的位置,表示字串長度上限)
          */
        for (int j = 0; j<length; j++) {
            if (S[i] == stack[j]) {
                isExist = 1;
                break;
            }
        }
        
        //② 如果存在,record[s[i]]位置上的出現次數減一,並繼續遍歷下一個字元
        //③ 如果不存在,則需要迴圈一個正確位置儲存起來;
        //④ 如果不存在,跳過棧中所有比當前字元大、且後面還會出現的元素,然後將當前字元入棧
        // top > -1表示棧非空
        //stack[top] > s[i]表示棧頂元素比當前元素大
        //record[stack[top]] > 1表示後面還會出現
        //例如b,c因為不符合以下條件會直接入棧.stack[] = "bc",但是噹噹前字元是"a"時,由於bcabc,a不應該是在stack的順序是"bca",所以要把位置不符合的字元出棧;
        //top = 1,stack[top] > s[i],c>a; 並且stack[top] 在之後還會重複的出現,所以我們可以安心的把stack中的棧頂C出棧,所以stack[]="b",top減一後等於0; 同時也需要將record[c]出現次數減一;
        //top=0,stack[top]>s[i],b>a,並且stack[top] 在之後還會出現,所以stack把棧頂b出棧,所以此時棧stack[]="",top減一後等於-1,此時棧中位置不正確的字元都已經移除;
    
        if (isExist) {
            record[S[i] - 'a']--;
        }else{
            while (top > -1 && stack[top] > S[i] && record[stack[top] - 'a'] > 1) {
                /* 跳過該元素,頻次要減一 */
                record[stack[top] - 'a']--;
                /* 出棧 */
                top--;
            }
            //⑤ 結束while 迴圈;
            //迴圈結束的3種可能性:(1)移動到棧底(top == -1) ; (2)棧頂元素小於當前元素(stack[top] <= s[i]) (3)棧頂元素後面不出現(record[stack[top]] == 1)
            // 此時,當前元素要插入到top的下一個位置
            // top往上移動1位
            top ++;
            stack[top] = S[i];
        }
    }
    
    //結束棧頂新增字元結束符
    stack[++top] = '\0';
    
    return stack;
}
複製程式碼

2.5 利用棧思想的結果預期