栈思想
指的是利用栈的特性(先进后出)去解决问题,那么什么问题适合用栈思想解决了?
- 数据是线性的。
- 问题中常常涉及到数据的来回比较、匹配问题。
例如:每日温度、括号匹配、字符串解码、去掉重复字母等问题。 - 问题中涉及到数据的转置。
例如:进制问题、链表倒序打印问题等
注意并不是说栈思想只是一个解决的的参考思想,并不是万能的,它适用于以上这样的情况下去解决问题;
利用栈思想解决问题时,首先需要透彻的解析问题之后,找到问题解决的规律,才能使用它解决。
思想只有指导作用,遇到不同的题目,需要个例分析.在基本思想上去找到解决问题之道。
算法题解
1. 有效的括号 (Leetcode-20)
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例1:
输入:"()"
输出:true
示例2:
输入:"([)]"
输出:false
解题思路
- 遇见左字符,将做字符入栈
- 遇见右字符
- 如果栈是空的,说明
括号无效
- 如果栈不为空,将栈顶字符出栈,与右字符匹配
- 如果左右字符不匹配,说明
括号无效
- 如果左右字符匹配,继续扫描下一个字符
- 如果左右字符不匹配,说明
- 所有字符扫描完毕后
- 栈为空, 说明
括号有效
- 栈不为空,说明
括号无效
代码实现
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 行。
解题思路
在杨辉三角中,每个数是它左上方和右上方的数的和。
- 第一层循环行数i: 默认[i][0] = 1,[i][i] = 1
- 第二层循环控制列数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".
解题思路
- 遍历字符串 S
- 如果当前字符不为方括号"]" 则入栈stack中;
- 如果当前字符遇到了方括号"]" 则:
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