數(shù)據(jù)結(jié)構(gòu)與算法12-棧思想算法題

算法,是我們程序員縱向發(fā)展所必須攀登的一座大山一罩,下面我們做一些算法題,難度逐漸遞增撇簿。當(dāng)然我們碰見解不開的題時千萬不要?dú)怵H聂渊,有時候一天做出一道題,都是很不容易的四瘫。

  • 充分閱讀題目.了解題目背后的關(guān)鍵意思;
  • 分析題目,涉及到哪些數(shù)據(jù)結(jié)構(gòu),對問題進(jìn)行分類. 到底屬于鏈表問題, 棧思想問題, 字符串問題,二叉樹問題,圖相關(guān)問題,排序問題; 與你之前所接觸過的算法題有沒有類似,找到問題的解題思路
  • 實(shí)現(xiàn)算法. 在算法的實(shí)現(xiàn)的過程,并不是一蹴而就, 肯定是需要不斷的調(diào)試,修改的;
  • 驗(yàn)證算法正確性
  • 找到題源, 看其他的開發(fā)者對齊的解決思路.
  • 找到題解建議之后, 對于其他優(yōu)秀思路,分析它的優(yōu)勢,并且學(xué)習(xí)它的思路.并且寫成其他解法的代碼
  • 算法題的解題能力來自于2點(diǎn): 對于數(shù)據(jù)結(jié)構(gòu)與算法核心問題是否夯實(shí) + 是否有足夠多且足夠耐心的積累;

其中第五步和第六步格外的重要汉嗽,我們在作出算法題之后,不要沉迷于短暫的喜悅之中找蜜,而是要多看看其他大神的解題思路饼暑,取其精華,學(xué)習(xí)思想,算法最重要的還是思想弓叛,只做題的話就有點(diǎn)揀了芝麻丟了西瓜的意思了

我們做算法的時候可以多利用棧的思想(先進(jìn)后出)解決問題彰居,有時候會特別方便,那么什么時候可以利用棧思想呢撰筷?大體分為以下三點(diǎn):

  • 數(shù)據(jù)是線性的
  • 問題中常常涉及到數(shù)據(jù)的來回比較,匹配問題;例如,每日溫度,括號匹配,字符串解碼,去掉重復(fù)字母等問題.
  • 問題中涉及到數(shù)據(jù)的轉(zhuǎn)置,例如進(jìn)制問題.鏈表倒序打印問題等

Tip:注意并不是說棧思想只是一個解決的的參考思想.并不是萬能的.它適用于以上這樣的情況下去解決問題;

1. 進(jìn)制轉(zhuǎn)化問題(LeetCode-簡單)

給定一個十進(jìn)制的數(shù)字N陈惰,將其轉(zhuǎn)化為M進(jìn)制(以8為例)。

解題思路:
  1. 初始化一個空棧S
  2. 當(dāng)十進(jìn)制N非零時,循環(huán)執(zhí)行以下操作
    • 把N與8求余得到的八進(jìn)制數(shù)壓入棧S;
    • N更新為N與8的商;
  3. 當(dāng)棧S非空時,循環(huán)執(zhí)行以下操作
    • 彈出棧頂元素e;
    • 輸出e;
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存儲空間初始分配量 */

typedef int Status;
typedef int SElemType; /* SElemType類型根據(jù)實(shí)際情況而定闭专,這里假設(shè)為int */

/* 順序棧結(jié)構(gòu) */
typedef struct
{
    SElemType data[MAXSIZE];
    int top; /* 用于棧頂指針 */
}SqStack;

//4.1 構(gòu)建一個空棧S
Status InitStack(SqStack *S){
    
    S->top = -1;
    return OK;
}


//4.2 將棧置空
Status ClearStack(SqStack *S){
    
    //疑問: 將棧置空,需要將順序棧的元素都清空嗎?
    //不需要,只需要修改top標(biāo)簽就可以了.
    S->top = -1;
    return OK;
}

//4.3 判斷順序棧是否為空;
Status StackEmpty(SqStack S){
    if (S.top == -1)
        return TRUE;
    else
        return FALSE;
}

//4.4 返回棧的長度
int StackLength(SqStack S){
    return S.top + 1;
}

//4.5 獲取棧頂
Status GetTop(SqStack S,SElemType *e){
    if (S.top == -1)
        return ERROR;
    else
        *e = S.data[S.top];
    
    return OK;
    
}

//4.6 插入元素e為新棧頂元素
Status PushData(SqStack *S, SElemType e){
    
    //棧已滿
    if (S->top == MAXSIZE -1) {
        return ERROR;
    }
    
    //棧頂指針+1;
    S->top ++;
    //將新插入的元素賦值給棧頂空間
    S->data[S->top] = e;
    
    return OK;
}

//4.7 刪除S棧頂元素,并且用e帶回
Status Pop(SqStack *S,SElemType *e){
    
    //空棧,則返回error;
    if (S->top == -1) {
        return ERROR;
    }
    
    //將要刪除的棧頂元素賦值給e
    *e = S->data[S->top];
    //棧頂指針--;
    S->top--;
    
    return OK;
}

//4.8 從棧底到棧頂依次對棧中的每個元素打印
Status StackTraverse(SqStack S){
    int i = 0;
    printf("此棧中所有元素");
    while (i<=S.top) {
        printf("%d ",S.data[i++]);
    }
    printf("\n");
    return OK;
}

解題如下:

void conversion(int N){
    
    SqStack S;
    SElemType e;
    //1.初始化一個空棧S
    InitStack(&S);
    
    //2.
    while (N) {
        PushData(&S, N%8);
        N = N/8;
    }
    
    //3.
    while (!StackEmpty(S)) {
        Pop(&S, &e);
        printf("%d\n",e);
    }
   
}

2. 爬樓梯問題(LeetCode-中等)

假設(shè)你正在爬樓梯奴潘。需要 n 階你才能到達(dá)樓頂。每次你可以爬 1 或 2 個臺階影钉。你有多少種不同的方法可以爬到樓頂呢画髓?注意:給定 n 是一個正整數(shù)。
示例例1:
輸?入: 2
輸初: 2
解釋: 有2種?方法可以爬到樓頂
①. 1階+1階
②. 2階
示例例2:
輸?入: 3
輸初: 3
解釋: 有3種?方法可以爬到樓頂
①. 1階+1階+1階
②. 1階+2階
③. 2階+1階

我們分為兩種方法解答:①.遞歸法 ②. 動態(tài)規(guī)劃法

方法一:遞歸求解法
/*
 f(n) = f(n-1) + f(n-2);
 f(1)=1;
 f(2)=1;
 */
int ClimbStairs_1(int n){
    
    if (n<1)  return 0;
    if (n == 1) return 1;
    if (n == 2) return 2;
    
    return ClimbStairs_1(n-1) + ClimbStairs_1(n-2);
}

方法二:動態(tài)規(guī)劃法
int ClimbStairs(int n){
    if(n==1) return 1;
    int temp = n+1;
    int *sum = (int *)malloc(sizeof(int) * (temp));
    sum[0] = 0;
    sum[1] = 1;
    sum[2] = 2;
    
    for (int i = 3; i <= n; i++) {
        sum[i] = sum[i-1] + sum[i-2];
    }
    
    return sum[n];
}

3. 楊輝三角(LeetCode-中等)

給定一個非負(fù)整數(shù) numRows平委,生成楊輝三角的前 numRows 行奈虾。在楊輝三角中,每個數(shù)是它左上方和右上方的數(shù)的和廉赔。
示例:
輸入: 5
輸出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]

我們利用動態(tài)規(guī)劃法解決這個問題肉微。
思路:

  1. 第一層循環(huán)控制行數(shù)i : 默認(rèn)[i][0] = 1,[i][i] = 1
  2. 第二層循環(huán)控制列數(shù)j : triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
int** generate(int numRows, int* returnSize){
    
    *returnSize = numRows;
    
    int **res = (int **)malloc(sizeof(int*)*numRows);
    
    for (int i = 0; i < numRows; i++) {
        res[i] = (int *)malloc(sizeof(int)*(i+1));
        res[i][0] = 1;
        res[i][i] = 1;
        
        for (int j = 1; j < i; j++) {
            res[i][j] = res[i-1][j] + res[i-1][j-1];
        }
    }
    
    return res;
    
}
動態(tài)規(guī)劃

動態(tài)規(guī)劃(英語:Dynamic programming,簡稱 DP)是一種在數(shù)學(xué)蜡塌、管理理科學(xué)碉纳、計(jì)算機(jī)科學(xué)、經(jīng)濟(jì)學(xué)和?生物信息 學(xué)中使?用的馏艾,通過把原問題分解為相對簡單的?子問題的?方式求解復(fù)雜問題的方法劳曹。

動態(tài)規(guī)劃常常適?用于有重疊?子問題和最優(yōu)?子結(jié)構(gòu)性質(zhì)的問題,動態(tài)規(guī)劃?方法所耗時間往往遠(yuǎn)少于樸素解法琅摩。

動態(tài)規(guī)劃背后的基本思想非常簡單铁孵。?致上,若要解一個給定問題房资,我們需要解其不不同部分(即?問題)蜕劝,再根據(jù)子問題的解以得出原問題的解。動態(tài)規(guī)劃往往用于優(yōu)化遞歸問題轰异,例如斐波那契數(shù)列岖沛,如果運(yùn)用遞歸的方式來求解會重復(fù)計(jì)算很多相同的子問題,利?用動態(tài)規(guī)劃的思想可以減少計(jì)算量搭独。通常許多子問題?常相似烫止,為此動態(tài)規(guī)劃法試圖僅僅解決每個子問題一次,從?而減少計(jì)算量戳稽。

?旦某個給定?子問題的解已經(jīng)算出,則將其記憶化存儲,以便下次需要同一個?問題解之時直接查表惊奇。這種做法在重復(fù)子問題的數(shù)目關(guān)于輸入的規(guī)模呈指數(shù)增?時特別有用互躬。

4. 括號匹配檢驗(yàn)(LeetCode-中等)

假設(shè)表達(dá)式中允許包含兩種括號:圓括號與方括號,其嵌套順序隨意,即() 或者[([][])]都是正確的.而這 [(]或者(()])或者([()) 都是不正確的格式. 檢驗(yàn)括號是否匹配的方法可用"期待的急迫程度"這個概念來描述.

第一種解法:鏈?zhǔn)酱鎯?/p>

typedef int Status;
typedef int SElemType; /* SElemType類型根據(jù)實(shí)際情況而定,這里假設(shè)為int */
/* 鏈棧結(jié)構(gòu) */

typedef struct StackNode
{
    SElemType data;
    struct StackNode *next;
}StackNode,*StackNodePtr;

typedef struct
{
    int count;
    StackNodePtr top;

}LinkStack;


Status InitStack(LinkStack *S){
    
    S->top = (StackNodePtr)malloc(sizeof(StackNode));

    if (S->top == NULL) {
        return ERROR;
    }
    S->top = NULL;
    S->count = 0;
    return OK;
}
Status Push(LinkStack *S ,char data){
    if (!S) return ERROR;
    
    StackNodePtr temp = (StackNodePtr)malloc(sizeof(StackNode));
    temp->data = data;
    temp->next = S->top;
    S->top = temp;
    S->count++;
    return OK;
}
Status Pop(LinkStack *S ,char *e){
    if (!S || !S->top) return ERROR;
    *e = S->top->data;
    StackNodePtr node = S->top;
    S->top = S->top->next; // 更換頭結(jié)點(diǎn)
    S->count--;
    free(node);
    return OK;
}

Status BracketsCheck(char *string) {
    LinkStack stack;
    InitStack(&stack);
    char *p = string;
    while (*p) {
        if (*p == '[' || *p == '(') {
            if (!Push(&stack, *p)) {
                return FALSE;
            }
        } else if (*p == ']') {
            char bracket = 0;
            if (!Pop(&stack, &bracket) || bracket != '[') {
                return FALSE;
            }
        } else if (*p == ')') {
            char bracket = 0;
            if (!Pop(&stack, &bracket) || bracket != '(') {
                return FALSE;
            }
        }
        p++;
    }
    return TRUE;
}
          if (BracketsCheck("([]())")) {
             printf("括號格式正確\n");
         } else {
             printf("括號格式錯誤\n");
         }

第二種解法:順序存儲

typedef struct {
    char * base ;//棧底指針
    char * top ; //棧頂指針
    int    stacksize;  //棧MaxSize
}SqStack;
//初始化棧
/*
 思路:
 1. 如果棧底為空
 2. 分配一個最大容量Stack_Init_Size的數(shù)組,棧底/棧頂都指向與它.[參考圖空棧情況]
 3. 初始化棧的最大容易Stack_Init_Size
 */
int Init(SqStack *stack){
    stack->base = (char *)malloc(sizeof(char)*Stack_Init_Size);
    stack->top = stack->base;
    
    if (stack->base == NULL) {
        return -1;
    }
    stack->stacksize = Stack_Init_Size;
    printf("初始化成功\n");
    
    return 0;
}
//獲取棧頂數(shù)據(jù)
/*
 思路:
 1.判斷棧是否為空
 2.非空,則棧定指針-1,返回棧頂元素;
 */
char GetTop(SqStack stack){
    if (stack.base == stack.top) {
        printf("沒有數(shù)據(jù)\n");
        return '#';
    }
    return *(stack.top-1);
}
//往棧中插入元素
/*
 思路:
 1.判斷棧是否已滿,若滿則返回ERROR #問題:如何判斷棧是否已滿?
 2.棧滿,則續(xù)容空間 #問題:如何給已滿棧續(xù)容空間?
 3.將元素element壓棧
 4.棧頂指針加"1"
 */
Status PushData(SqStack *stack,char element){
    if (stack->top - stack->base == Stack_Init_Size) {
        //棧滿擴(kuò)容
        stack->base = (char*)realloc(stack->base, Stack_Increment*sizeof(char));
        stack->top = stack->base + stack->stacksize;
        stack->stacksize += Stack_Increment;

    }
    *stack->top = element;
    stack->top +=1;
    
    return OK;
}

//刪除棧頂元素
/*
 思路:
 1.判斷棧是否已空
 2.非空,則獲取棧頂元素,并將棧頂減"1";
 */
char PopData(SqStack *stack){
    if(stack->top==stack->base){
        printf("棧為空\n");
        return '#';
    }
    //printf("刪除數(shù)據(jù)成功");
    return *--stack->top;
}
//釋放椝汤桑空間
int Destroy(SqStack *stack){
    free(stack->base);
    stack->stacksize=0;
    return 0;
}
//處理數(shù)據(jù)吼渡,借助棧判斷
/*
 思路:
 1. 將第0個元素壓棧
 2. 遍歷[1,strlen(data)]
    (3). 取棧頂字符
    (4). 檢查該字符是左括號("(","[")
         a.是左"(",則判斷緊接其后的data[i]是為右")"
            YES->壓棧,NO->出棧
         b.是左"[",則判斷緊跟其后的data[i]是為右"]"
            YES->壓棧,NO->出棧
         c.表示式如果以"#"結(jié)尾,則判斷緊跟其后的data是為左"(""]"
            YES->壓棧,NO->-1;
 
 3.遍歷結(jié)束,則判斷棧是否為空,為空則表示匹配成功;否則匹配失敗;
 [ ( [ ] [ ] ) ]
 1 2 3 4 5 6 7 8
 */
int ExecuteData(SqStack stack,char* data){
    PushData(&stack,data[0]);
    for(int i=1;i<strlen(data);i++){
        char top = GetTop(stack);
        switch(top){
            case '(':
                if(data[i]==')')PopData(&stack);
                else PushData(&stack,data[i]);
                break;
            case '[':
                if(data[i]==']')PopData(&stack);
                else PushData(&stack,data[i]);
                break;
            case '#':
                if(data[i]=='('||data[i]=='['){
                    PushData(&stack,data[i]);
                    break;
                }
                else
                    default:return -1;break;
        }
    }
    
    //如果棧為空,則返回"0"->匹配成功 否則返回"-1"匹配失敗
    if(stack.top==stack.base){
        Destroy(&stack);
        return 0;
    }
    else{
        
        Destroy(&stack);
        return -1;
    }
}
        SqStack stack;
        Init(&stack);
        char data[180];
        printf("請輸入要校驗(yàn)的符號:\n");
        scanf("%s",data);
        int result = ExecuteData(stack,data);
        if(result==0)printf("括號是正確匹配的\n");
        else printf("括號匹配不正確\n");

5. 每日氣溫

根據(jù)每日 氣溫 列表,請重新生成一個列表乓序,對應(yīng)位置的輸入是你需要再等待多久溫度才會升高的天數(shù)寺酪。如果之后都不會升高,請輸入 0 來代替替劈。
例如寄雀,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的輸出應(yīng)該是 [1, 1, 4, 2, 1, 1, 0, 0]陨献。
提示:氣溫 列表長度的范圍是 [1, 30000]盒犹。每個氣溫的值的都是 [30, 100] 范圍內(nèi)的整數(shù)。
提示:氣溫 列表長度的范圍是 [1, 30000]眨业。每個氣溫的值的均為華氏度急膀,都是在 [30, 100] 范圍內(nèi)的整數(shù)。
解題關(guān)鍵: 實(shí)際上就是找當(dāng)前元素 從[i,TSize] 找到大于該元素時. 數(shù)了幾次. 首先最后一個元素默認(rèn)是0,因?yàn)樗竺嬉呀?jīng)沒有元素了.

typedef struct Temp {
    int index;/* 用于棧頂指針 */
    int temp;
} Temp;

int* dailyTemperatures(int* T, int TSize, int* returnSize) {
    int *ans = (int *)calloc(TSize, sizeof(int ));
    Temp *stack = (Temp *)malloc(sizeof(Temp) * TSize);
    *returnSize = TSize;
    int top = 0;
    for (int i = 0; i < TSize; i++) {
        printf("%d   %d\n",top,stack[top].temp);
        if (top > 0 && T[i] > stack[top].temp) { // 棧中有值龄捡,且當(dāng)前溫度比棧頂溫度更高
            for (; top > 0 && T[i] > stack[top].temp; top--) { // 遍歷比棧頂溫度低的溫度
                ans[stack[top].index] = i - stack[top].index; // 計(jì)算相隔天數(shù)
            }
        }
        // 入棧當(dāng)前溫度卓嫂。每次都是入棧新的溫度
        top++;
        stack[top].index = i;
        stack[top].temp = T[i];
    }
    return ans;
}
        int temperatures[8] = {73, 74, 75, 71, 69, 72, 76, 73};
           int returnSize;
           int *result = dailyTemperatures(temperatures, 8, &returnSize);
           for (int i = 0; i < returnSize; i++) {
               printf("%d ", result[i]);
           }
           printf("\n");

typedef struct Temp {
    int index;/* 用于棧頂指針 */
    int temp;
} Temp;


int* dailyTemperatures(int* T, int TSize, int* returnSize) {
    /* 存儲結(jié)果 */
//    int *ans = (int *)malloc(sizeof(int) * TSize);
     int *ans = (int *)calloc(TSize, sizeof(int ));
    /* 存儲原始的氣溫?cái)?shù)據(jù) */
    Temp *stack = (Temp*)malloc(sizeof(Temp) * TSize);
    *returnSize = TSize;

    int top = 0;
    /* 73, 74, 75, 71, 69, 72, 76, 73 */
    for (int i = 0; i < TSize; i++) {
        printf("%d   %d\n",top,stack[top].temp);
        if (top > 0 && T[i] > stack[top].temp) { // 棧中有值,且當(dāng)前溫度比棧頂溫度更高
            for (; top > 0 && T[i] > stack[top].temp; top--) { // 遍歷棧內(nèi)的溫度有沒有比當(dāng)前溫度低的溫度
                ans[stack[top].index] = i - stack[top].index; // 計(jì)算相隔天數(shù)
                printf("內(nèi)層循環(huán)%d\n",top);

            }
        }
        // 入棧當(dāng)前溫度聘殖。每次都是入棧新的溫度
        top++;
        stack[top].index = i;
        stack[top].temp = T[i];
    }
    return ans;;
    
}
/*
暴力法1:
1. 從左到右開始遍歷,從第一個數(shù)到最后一個數(shù)開始遍歷. 最后一個數(shù)因?yàn)楹竺鏇]有元素,默認(rèn)是0,不需要計(jì)算;
2. 從[i+1,TSize]遍歷,每個數(shù)直到找到比它大的數(shù),數(shù)的次數(shù)就是對應(yīng)的值;
*/
int  *dailyTemperatures_1(int* T, int TSize, int* returnSize){
    
    int *result = (int *)malloc(sizeof(int) * TSize);
    *returnSize = TSize;
    result[TSize-1] = 0;
    
    for(int i = 0;i < TSize-1;i++)
        if(i>0 && T[i] == T[i-1])
            result[i] = result[i-1] == 0 ? 0 : result[i-1]-1;
        else{
            for (int j = i+1; j < TSize; j++) {
                if(T[j] > T[i]){
                    result[i] = j-i;
                    break;
                }
                if (j == TSize-1) {
                    result[i] = 0;
                }
            }
        }
    
    return result;
}
/*
 跳躍對比:
 1. 從右到左遍歷. 因?yàn)樽詈笠惶斓臍鉁夭粫偕?默認(rèn)等于0;
 2. i 從[TSize-2,0]; 從倒數(shù)第二天開始遍歷比較. 每次減一;
 3. j 從[i+1,TSize]遍歷, j+=result[j],可以利用已經(jīng)有結(jié)果的位置進(jìn)行跳躍,從而減少遍歷次數(shù)
 -若T[i]<T[j],那么Result = j - i;
 -若reuslt[j] == 0,則表示后面不會有更大的值,那么當(dāng)前值就應(yīng)該也是0;
 
 */

int  *dailyTemperatures_2(int* T, int TSize, int* returnSize){
    
    int *result = (int *)malloc(sizeof(int) * TSize);
    *returnSize = TSize;
    result[TSize-1] = 0;
    
    for (int i=TSize-2; i >= 0; i--) {
        for (int j = i+1; j < TSize; j+=result[j]) {
            if (T[i] < T[j]) {
                result[i] = j-i;
                break;
            }else
            {
                if (result[j] == 0) {
                    result[i] = 0;
                    break;
                }
            }
        }
    }
    
    return result;
}
/*
 思路:
 1. 初始化一個棧(用來存儲索引),value數(shù)組
 1. 棧中存儲的是元素的索引值index;
 2.將當(dāng)前元素和棧頂元素比較;
    如果棧為空,那么直接將當(dāng)前元素索引index 存儲到棧中;
    如果棧頂元素>當(dāng)前元素,則將當(dāng)前元素索引index 存儲到棧中;
    如果棧頂元素<當(dāng)前元素,則將當(dāng)前元素索引index-棧頂元素index,計(jì)算完畢則將當(dāng)前棧頂元素移除,將當(dāng)前元素索引index 存儲到棧中

 */
int* dailyTemperatures_3(int* T, int TSize, int* returnSize) {
    
    int* result = (int*)malloc(sizeof(int)*TSize);
    // 用棧記錄T的下標(biāo)晨雳。
    int* stack_index = malloc(sizeof(int)*TSize);
    *returnSize = TSize;
    // 棧頂指針。
    int top = 0;
    int tIndex;
    
    for (int i = 0; i < TSize; i++)
        result[i] = 0;
    
    for (int i = 0; i < TSize; i++) {
        printf("\n循環(huán)第%d次,i = %d\n",i,i);
       
        // 若當(dāng)前元素大于棧頂元素就斤,棧頂元素出棧悍募。即溫度升高了,所求天數(shù)為兩者下標(biāo)的差值洋机。
        while (top > 0 && T[i] > T[stack_index[top-1]]) {
            tIndex = stack_index[top-1];
            result[tIndex] = i - tIndex;
            top--;
            printf("tIndex = %d; result[%d] = %d, top = %d \n",tIndex,tIndex,result[tIndex],top);
        }
        
        // 當(dāng)前元素入棧坠宴。
        stack_index[top] = i;
        printf("i= %d;  StackIndex[%d] = %d ",i,top,stack_index[top]);
        top++;
        
        printf(" top = %d \n",top);
    }
    
    return result;
}

6.一只青蛙一次可以跳上1級臺階,也可以跳上2級绷旗。喜鼓。。衔肢。庄岖。它也可以跳上n級,此時該青蛙跳上一個n級的臺階總共有多少種跳法角骤?

用數(shù)學(xué)歸納法可以證明:f(n)=2n?1f(n)=2n?1.

遞歸式證明:
當(dāng)n = 1 時隅忿, 只有一種跳法心剥,即1階跳:Fib(1) = 1;
當(dāng)n = 2 時, 有兩種跳的方式背桐,一階跳和二階跳:Fib(2) = Fib(1) + Fib(0) = 2;
當(dāng)n = 3 時优烧,有三種跳的方式,
第一次跳出一階后链峭,后面還有Fib(3-1)中跳法畦娄;
第一次跳出二階后,后面還有Fib(3-2)中跳法弊仪;
第一次跳出三階后熙卡,后面還有Fib(3-3)中跳法
Fib(3) = Fib(2) + Fib(1)+Fib(0)=4;
當(dāng)n = n 時,共有n種跳的方式励饵,
第一次跳出一階后驳癌,后面還有Fib(n-1)中跳法;
第一次跳出二階后曲横,后面還有Fib(n-2)中跳法……………………..
第一次跳出n階后, 后面還有 Fib(n-n)中跳法.
Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+……….+Fib(n-n)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-1)
又因?yàn)镕ib(n-1)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-2)
兩式相減得:Fib(n)-Fib(n-1)=Fib(n-1)
=====》 Fib(n) = 2*Fib(n-1) n >= 2
遞歸等式如下:
n = 1 f(n) = 1;
n = 2 f(n) = 2;
n > 2 f(n) = 2 * f(n-1);

所以:f(n)=2?f(n?1)=2?2(n?2)....=2n?1?f(0)=2n?1f(n)=2?f(n?1)=2?2(n?2)....=2n?1?f(0)=2n?1

int FrogJump12nStepRecursive(int n)
{
   if (n == 1){
       return 1;
   }else if (n == 2){
       return 2;
   }else{
       return 2 * FrogJump12nStepRecursive(n - 1);
   }
}

7. 取出重復(fù)字符

給你一個僅包含小寫字母的字符串喂柒,請你去除字符串中重復(fù)的字母,使得每個字母只出現(xiàn)一次禾嫉。需保證返回結(jié)果的字典序最性纸堋(要求不能打亂其他字符的相對位置)
字符串解碼

給定一個經(jīng)過編碼的字符串,返回它解碼后的字符串熙参。

編碼規(guī)則為:k[encoded_string]艳吠,表示其中方括號內(nèi)部的encoded_string正好重復(fù)k次,k為正整數(shù)孽椰。

你可以認(rèn)為輸入字符串總是有效的昭娩;輸入字符串中沒有額外的空格,且輸入的方括號總是符合格式要求的黍匾。

此外栏渺,你可以認(rèn)為原始數(shù)據(jù)不包含數(shù)字,所有的數(shù)字只表示重復(fù)的次數(shù) k 锐涯,例如不會出現(xiàn)像3a或2[4]的輸入磕诊。

s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef"
解題關(guān)鍵:
字典序: 字符串之間比較和數(shù)字比較不一樣; 字符串比較是從頭往后挨個字符比較,那個字符串大取決于兩個字符串中第一個對應(yīng)不相等的字符; 例如 任意一個a開頭的字符串都大于任意一個b開頭的字符串;例如字典中apple 大于 book;
題目的意思,你去除重復(fù)字母后,需要按最小的字典序返回.并且不能打亂其他字母的相對位置;
例如 bcabc 你應(yīng)該返回abc, 而不是bca,cab;
例如 cbacdcbc 應(yīng)該返回acdb,而不是cbad,bacd,adcb
例如 zab,應(yīng)該返回zab,而不是abz;

char *removeDuplicateLetters(char *s)
{
    /*
     ① 特殊情況處理,s為空,或者字符串長度為0;
     ② 特殊情況,s的長度為1,則沒有必要后續(xù)的處理,則直接返回s;
     */
    if (s == NULL || strlen(s) == 0) {
        return "";
    }
    if (strlen(s) == 1) {
        return s;
    }
    
    char record[26] = {0};
    int len = (int)strlen(s);
    
    char* stack = (char*)malloc(len * 2 * sizeof(char));
    //memset(void *s, int ch, size_t n) 將stack len*2*sizeof(char)長度范圍的空間填充0;
    memset(stack, 0, len * 2 * sizeof(char));
    //stack 棧頂賦初值為-1;
    int top = -1;
    
    //1.統(tǒng)計(jì)每個字符的頻次
    int i;
    for (i = 0; i < len; i++) {
        record[s[i] - 'a']++;
    }
    
    //2.遍歷s,入棧
    for (i = 0; i < len; i++) {
        
        
        int isExist = 0;
        for (int j = 0; j <= top; j++) {
            if (s[i] == stack[j]) {
                isExist = 1;
                break;
            }
        }
        
        if (isExist == 1) {
            record[s[i] - 'a']--;
        } else {
            while (top > -1 && stack[top] > s[i] && record[stack[top] - 'a'] > 1) {
               
                // 跳過該元素,頻次要減一
                record[stack[top] - 'a']--;
                // 出棧
                top--;
            }

            top++;
            // 入棧
            stack[top] = s[i];
        }
    }
    
    //結(jié)束棧頂添加字符結(jié)束符
    stack[++top] = '\0';
    
    return stack;
}
    printf("去掉重復(fù)字母! LeetCode-困難 \n");
    
    char *s ;
    s = removeDuplicateLetters("bcabc");
    printf("%s\n",s);
 
    s = removeDuplicateLetters("zab");
    printf("%s\n",s);

    s = removeDuplicateLetters("cbacdcbc");
    printf("%s\n",s);
    
    printf("\n");

8.字符串編碼

給定一個經(jīng)過編碼的字符串纹腌,返回它解碼后的字符串霎终。
編碼規(guī)則為:k[encoded_string],表示其中方括號內(nèi)部的encoded_string正好重復(fù)k次升薯,k為正整數(shù)莱褒。
你可以認(rèn)為輸入字符串總是有效的;輸入字符串中沒有額外的空格涎劈,且輸入的方括號總是符合格式要求的广凸。
此外阅茶,你可以認(rèn)為原始數(shù)據(jù)不包含數(shù)字,所有的數(shù)字只表示重復(fù)的次數(shù) k 炮障,例如不會出現(xiàn)像3a或2[4]的輸入目派。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef"

char * decodeString(char * s){
   
   /*
 題目: 字符串編碼LeetCode-中等
 編碼規(guī)則為: k[encoded_string],表示其中方括號內(nèi)部的 encoded_string 正好重復(fù) k 次胁赢。注意 k 保證為正整數(shù)。你可以認(rèn)為輸入字符串總是有效的白筹;輸入字符串中沒有額外的空格智末,且輸入的方括號總是符合格式要求的。此外徒河,你可以認(rèn)為原始數(shù)據(jù)不包含數(shù)字系馆,所有的數(shù)字只表示重復(fù)的次數(shù) k ,例如不會出現(xiàn)像 3a 或 2[4] 的輸入顽照。
 例如:
 s = "3[a]2[bc]", 返回 "aaabcbc".z

 s = "3[a2[c]]", 返回 "accaccacc".
 s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
 */

/*
 思路:
 例如:12[a]為例;
 
 1.遍歷字符串 S
 2.如果當(dāng)前字符不為方括號"]" 則入棧stack中;
 2.如果當(dāng)前字符遇到了方括號"]" 則:
 ① 首先找到要復(fù)制的字符,例如stack="12[a",那么我要首先獲取字符a;將這個a保存在另外一個棧去tempStack;
 ② 接下來,要找到需要備份的數(shù)量,例如stack="12[a",因?yàn)槌鰲_^字符"a",則當(dāng)前的top指向了"[",也就是等于2;
 ③ 而12對于字符串是2個字符, 我們要通過遍歷找到數(shù)字12的top上限/下限的位置索引, 此時上限curTop = 2, 下限通過出棧,top = -1;
 ④ 根據(jù)范圍[-1,2],讀取出12保存到strOfInt 字符串中來, 并且將字符"12\0",轉(zhuǎn)化成數(shù)字12;
 ⑤ 當(dāng)前top=-1,將tempStack中的字符a,復(fù)制12份入棧到stack中來;
 ⑥ 為當(dāng)前的stack擴(kuò)容, 在stack字符的末尾添加字符結(jié)束符合'\0';
 
 */

char * decodeString(char * s){
   
    /*.
     1.獲取字符串長度
     2.設(shè)置默認(rèn)棧長度50
     3.開辟字符串棧(空間為50)
     4.設(shè)置棧頭指針top = -1;
     */
    int len = (int)strlen(s);
    int stackSize = 50;
    char* stack = (char*)malloc(stackSize * sizeof(char));
    int top = -1;
    
    //遍歷字符串,在沒有遇到"]" 之前全部入棧
    for (int i = 0; i < len; ++i) {
        if (s[i] != ']') {
            //優(yōu)化:如果top到達(dá)了棧的上限,則為棧擴(kuò)容;
            if (top == stackSize - 1) {
                stack = realloc(stack, (stackSize += 50) * sizeof(char));
            }
            //將字符入棧stack
            stack[++top] = s[i];
            printf("#① 沒有遇到']'之前# top = %d\n",top);
        }
        else {
            int tempSize = 10;
            char* temp = (char*)malloc(tempSize * sizeof(char));
            int topOfTemp = -1;
            
            printf("#② 開始獲取要復(fù)制的字符信息之前 # top = %d\n",top);
            //從棧頂位置開始遍歷stack,直到"["結(jié)束;
            //把[a]這個字母a 賦值到temp棧中來;
            //簡單說,就是將stack中方括號里的字符出棧,復(fù)制到temp棧中來;
            while (stack[top] != '[') {
                
                //優(yōu)化:如果topOfTemp到達(dá)了棧的上限,則為棧擴(kuò)容;
                if (topOfTemp == tempSize - 1) {
                    temp = realloc(temp, (tempSize += 10) * sizeof(char));
                }
                //temp棧的棧頂指針自增;
                ++topOfTemp;
                //將stack棧頂字符復(fù)制到temp棧中來;
                temp[topOfTemp] = stack[top];
                //stack出棧,則top棧頂指針遞減;
                top--;
            }
            printf("#② 開始獲取要復(fù)制的字符信息之后 # top = %d\n",top);
            
            //找到倍數(shù)數(shù)字.strOfInt字符串;
            //注意:如果是大于1位的情況就處理
            char strOfInt[11];
            //p記錄當(dāng)前的top;
            int curTop = top;
            printf("#③ 開始獲取數(shù)字,數(shù)字位置上限 # curTop = %d\n",curTop);
            
            //top--的目的是把"["剔除,才能找到數(shù)字;
            top--;
            //遍歷stack得出數(shù)字
            //例如39[a] 就要找到這個數(shù)字39.
            //p指向當(dāng)前的top,我就知道上限了; 那么接下來通過循環(huán)來找它的數(shù)字下限;
            //結(jié)束條件:棧指針指向?yàn)榭? stack[top] 不等于數(shù)字
            while (top != -1 && stack[top] >= '0' && stack[top] <= '9') {
                top--;
            }
            printf("#③ 開始獲取數(shù)字,數(shù)字位置下限 # top = %d\n",top);
            
            //從top-1遍歷到p之間, 把stack[top-1,p]之間的數(shù)字復(fù)制到strOfInt中來;
            //39中3和9都是字符. 我們要獲取到這2個數(shù)字,存儲到strOfInt數(shù)組
            for (int j = top + 1; j < curTop; ++j) {
                strOfInt[j - (top + 1)] = stack[j];
            }
            //為字符串strOfInt數(shù)組加一個字符結(jié)束后綴'\0'
            strOfInt[curTop - (top + 1)] = '\0';
            
            //把strOfInt字符串轉(zhuǎn)換成整數(shù) atoi函數(shù);
            //把字母復(fù)制strOfInt份到stack中去;
            //例如39[a],就需要把復(fù)制39份a進(jìn)去;
            int curNum = atoi(strOfInt);
            for (int k = 0; k < curNum ; ++k) {
                
                //從-1到topOfTemp 范圍內(nèi),復(fù)制curNum份到stackTop中去;
                int kk = topOfTemp;
                while (kk != -1) {
                    
                    //優(yōu)化:如果stack到達(dá)了棧的上限,則為棧擴(kuò)容;
                    if (top == stackSize - 1) {
                        stack = realloc(stack, (stackSize += 50) * sizeof(char));
                    }
                    
                    //將temp棧的字符復(fù)制到stack中;
                    //stack[++top] = temp[kk--];
                    ++top;
                    stack[top] = temp[kk];
                    kk--;
                    
                }
            }
            free(temp);
            temp = NULL;
        }
    }
    
    //realloc 動態(tài)內(nèi)存調(diào)整;
    //void *realloc(void *mem_address, unsigned int newsize);
    //構(gòu)成字符串stack后, 在stack的空間擴(kuò)容.
    char* ans = realloc(stack, (top + 1) * sizeof(char));
    ans[++top] = '\0';
    
    //stack 棧不用,則釋放;
    free(stack);
    return ans;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("字符串編碼問題!\n");
    
    char *s ;
    s = decodeString("12[a]");
    printf("字符編碼后的結(jié)果: %s\n\n\n\n",s);
    
    s = decodeString("3[a]2[bc]");
    printf("字符編碼后的結(jié)果: %s\n\n\n\n",s);

    s = decodeString("3[a2[c]]");
    printf("字符編碼后的結(jié)果: %s\n\n\n\n",s);

    s = decodeString("2[abc]3[cd]ef");
    printf("字符編碼后的結(jié)果: %s\n\n\n\n",s);

    printf("\n");
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末由蘑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子代兵,更是在濱河造成了極大的恐慌尼酿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件植影,死亡現(xiàn)場離奇詭異裳擎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)思币,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門鹿响,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谷饿,你說我怎么就攤上這事惶我。” “怎么了博投?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵绸贡,是天一觀的道長。 經(jīng)常有香客問我贬堵,道長恃轩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任黎做,我火速辦了婚禮叉跛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒸殿。我一直安慰自己筷厘,他們只是感情好鸣峭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酥艳,像睡著了一般摊溶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上充石,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天莫换,我揣著相機(jī)與錄音,去河邊找鬼骤铃。 笑死拉岁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惰爬。 我是一名探鬼主播喊暖,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼撕瞧!你這毒婦竟也來了陵叽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤丛版,失蹤者是張志新(化名)和其女友劉穎巩掺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體硼婿,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锌半,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了寇漫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刊殉。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖州胳,靈堂內(nèi)的尸體忽然破棺而出记焊,到底是詐尸還是另有隱情,我是刑警寧澤栓撞,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布遍膜,位于F島的核電站,受9級特大地震影響瓤湘,放射性物質(zhì)發(fā)生泄漏瓢颅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一弛说、第九天 我趴在偏房一處隱蔽的房頂上張望挽懦。 院中可真熱鬧,春花似錦木人、人聲如沸信柿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渔嚷。三九已至进鸠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間形病,已是汗流浹背客年。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工潭陪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人曾我。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓扇商,卻偏偏與公主長得像,于是被迫代替她去往敵國和親籽前。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353