栈思想-算法题解

栈思想

指的是利用栈的特性(先进后出)去解决问题,那么什么问题适合用栈思想解决了?

  1. 数据是线性的。
  2. 问题中常常涉及到数据的来回比较、匹配问题。
    例如:每日温度、括号匹配、字符串解码、去掉重复字母等问题。
  3. 问题中涉及到数据的转置。
    例如:进制问题、链表倒序打印问题等

注意并不是说栈思想只是一个解决的的参考思想,并不是万能的,它适用于以上这样的情况下去解决问题;
利用栈思想解决问题时,首先需要透彻的解析问题之后,找到问题解决的规律,才能使用它解决。
思想只有指导作用,遇到不同的题目,需要个例分析.在基本思想上去找到解决问题之道。

算法题解

1. 有效的括号 (Leetcode-20)

给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。
有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
    注意空字符串可被认为是有效字符串。
示例1:

输入:"()"
输出:true

示例2:

输入:"([)]"
输出:false

解题思路

  1. 遇见左字符,将做字符入栈
  2. 遇见右字符
  • 如果栈是空的,说明括号无效
  • 如果栈不为空,将栈顶字符出栈,与右字符匹配
    • 如果左右字符不匹配,说明括号无效
    • 如果左右字符匹配,继续扫描下一个字符
  1. 所有字符扫描完毕后
  • 栈为空, 说明括号有效
  • 栈不为空,说明括号无效

代码实现

bool isValid(char * s) {
    if (*s == 0) return true;
    int len = (int)strlen(s);
    if (len & 1) return false;
    
    char stack[len];
    int top = -1;
    for (int i = 0; i < len; i++) {
        if (s[i] == '(' || s[i] == '{' || s[i] == '[') {
            stack[++top] = s[i];
        } else if (top == -1) {
            return false;
        } else if (s[i] == stack[top] + 1 || s[i] == stack[top] + 2) {
            top--;
        }
    }
    return top == -1;
}

2. 数制转换

给定一个整数,将其转化为八进制,并以字符串形式输出。

代码实现

void conversion(int N) {
    int stack[N];
    int top = -1;
    while (N) {
        stack[++top] = N % 8;
        N = N / 8;
    }
    while (top != -1) {
        printf("%d", stack[top--]);
    }
    printf("\n");
}

3. 杨辉三角

给定一个非负整数 num,生成杨辉三角的前 num 行。

解题思路

在杨辉三角中,每个数是它左上方和右上方的数的和。

  1. 第一层循环行数i: 默认[i][0] = 1,[i][i] = 1
  2. 第二层循环控制列数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);
        res[i][0] = 1;
        res[i][i] = 1;
        
        for (int j = 1; j < i; j++) {
            res[i][j] = res[i - 1][j - 1] + res[i - 1][j];
        }
    }
    return res;
}

4. 爬楼梯问题

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数

递归实现

f(1) = 1;
f(2) = 2;
f(n) = f(n - 1) + f(n - 2)

int climbStairs(int n) {
    if (n == 1) return 1;
    if (n == 2) return 2;
    return climbStairs(n - 1) + climbStairs(n - 2);
}

动态规划

int climbStairs(int n) {
    if (n == 1) return 1;
    
    int *sum = (int *)malloc(sizeof(int) * n);
    sum[1] = 1;
    sum[2] = 2;
    for (int i = 3; i <= n; i++) {
        sum[i] = sum[i - 1] + sum[i - 2];
    }
    
    return sum[n];
}

4. 每日气温

根据每日 气温 列表,请重新生成一个列表,对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

示例

输入: [73, 74, 75, 71, 69, 72, 76, 73]
输出: [1, 1, 4, 2, 1, 1, 0, 0]

代码实现

跳跃对比,倒序遍历,利用已经有结果的位置进行跳跃,减少遍历次数

/*
 思路:
 1.创建一个result 结果数组.
 2.默认reslut[TSize-1] = 0;
 3.从TSize-2个元素遍历到第一个元素[TSize-2,0];
 4.从[i+1,TSize]遍历,j+=result[j];
    -若T[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数组
2. 栈中存储的是元素的索引值index;
3. 遍历整个温度数组从[0,TSize];
   (1).如果栈顶元素<当前元素,则将当前元素索引index-栈顶元素index,计算完毕则将当前栈顶元素移除,将当前元素索引index 存储到栈中; 出栈后,只要栈不为空.继续比较,直到栈顶元素不能满足T[i] > T[stack_index[top-1]]
(2).如果当前的栈为空,则直接入栈;
(3).如果当前的元素小于栈顶元素,则入栈
(4).while循环结束后,当前元素也需要入栈;
*/
int * dailyTemperatures(int * T, int TSize, int * returnSize) {
    *returnSize = TSize;
    int *result = (int *)malloc(sizeof(int) * TSize);
    int *stack_index = (int *)malloc(sizeof(int) * TSize);
    int top = 0;
    int tIndex;
    for (int i = 0; i < TSize; i++) {
        result[i] = 0;
        while (top > 0 && T[i] > T[stack_index[top - 1]]) {
            tIndex = stack_index[top - 1];
            result[tIndex] = i - tIndex;
            top--;
        }
        
        stack_index[top] = i;
        top++;
    }
    
    return result;
}

5. 字符串解码 (Leetcode-394)

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: 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. 遍历字符串 S
  2. 如果当前字符不为方括号"]" 则入栈stack中;
  3. 如果当前字符遇到了方括号"]" 则:
    3.1 首先找到要复制的字符,例如stack="12[a",那么我要首先获取字符a;将这个a保存在另外一个栈去tempStack;
    3.2 接下来,要找到需要备份的数量,例如stack="12[a",因为出栈过字符"a",则当前的top指向了"[",也就是等于2;
    3.3 而12对于字符串是2个字符, 我们要通过遍历找到数字12的top上限/下限的位置索引, 此时上限curTop = 2, 下限通过出栈,top = -1;
    3.4 根据范围[-1,2],读取出12保存到strOfInt 字符串中来, 并且将字符"12\0",转化成数字12;
    3.5 当前top=-1,将tempStack中的字符a,复制12份入栈到stack中来;
    3.6 为当前的stack扩容, 在stack字符的末尾添加字符结束符合'\0';

代码实现

char * decodeString(char * s) {
    int stackSize = 50;
    char *stack = (char *)malloc(sizeof(char) * stackSize);
    int top = -1;
    
    int length = (int)strlen(s);
    for (int i = 0; i < length; i++) {
        if (s[i] != ']') {
            if (top == stackSize - 1) {
                stack = (char *)realloc(stack, (stackSize += 50) * sizeof(char));
            }
            stack[++top] = s[i];
        } else {
            int strStackSize = 10;
            char *strStack = (char *)malloc(sizeof(char) * strStackSize);
            int strTop = -1;
            
            while (stack[top] != '[') {
                if (strTop == strStackSize - 1) {
                    strStack = (char *)realloc(strStack, sizeof(char) * (strStackSize += 10));
                }
                strStack[++strTop] = stack[top--];
            }
            
//            char kStack[11];
//            int currentTop = top;
//            top--;
//
//            while (top != -1 && stack[top] >= '0' && stack[top] <= '9') {
//                top--;
//            }
//
//            for (int j = top + 1; j < currentTop; j++) {
//                kStack[j - (top + 1)] = stack[j];
//            }
//
//            kStack[currentTop - (top + 1)] = '\0';
//            int curNum = atoi(kStack);
            
            top--;
            int curNum = 0;
            int j = 0;
            while (top != -1 && stack[top] >= '0' && stack[top] <= '9') {
                curNum += (stack[top] - '0') * pow(10, j++);
                top--;
            }
            
            for (int k = 0; k < curNum; k++) {
                int tempTop = strTop;
                while (tempTop != -1) {
                    if (top == stackSize - 1) {
                        stack = (char *)realloc(stack, sizeof(char) * (stackSize += 50));
                    }
                    stack[++top] = strStack[tempTop--];
                }
            }
        }
    }
    char *result = (char *)realloc(stack, sizeof(char) * (top + 2));
    result[++top] = '\0';
    
    return result;
}

Demo:https://github.com/ShoukaiWang/DataStructuresAndAlgorithms

你可能感兴趣的:(栈思想-算法题解)