演演算法基礎篇-關於棧的演演算法題分析(二)
阿新 • • 發佈:2020-06-24
算法系列篇章-可以參照如下順序閱讀
1.字串編碼
給定一個經過編碼的字串,返回它解碼後的字串。
編碼規則為: k[encoded_string]
,表示其中方括號內部的encoded_string
正好重複 k
次。注意 k
保證為正整數。
你可以認為輸入字串總是有效的;輸入字串中沒有額外的空格,且輸入的方括號總是符合格式要求的。
此外,你可以認為原始資料不包含數字,所有的數字只表示重複的次數k
,例如不會出現像 3a
或 2[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
個字元,我們要通過遍歷找到數字12
的top
上限/下限的位置索引,此時上限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;
}
複製程式碼