921.使括号有效的最少添加(简单)
题目:
给定一个由 ‘(’ 和 ‘)’ 括号组成的字符串 S,我们需要添加最少的括号( ‘(’ 或是 ‘)’,可以在任何位置),以使得到的括号字符串有效。
从形式上讲,只有满足下面几点之一,括号字符串才是有效的:
它是一个空字符串,或者
它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者
它可以被写作 (A),其中 A 是有效字符串。
给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。
示例 1:
输入:"())"
输出:1
示例 2:
输入:"((("
输出:3
示例 3:
输入:"()"
输出:0
示例 4:
输入:"()))(("
输出:4
同“有效括号”这道题解法类似,同样使用栈的方法来存储’(‘字符,并且设立一个num 来记录需要添加的括号数量,当遇到’)’ 字符且栈不为空时出栈一次,为空时num 加一。 另外在遍历字符串结束后为解决’(’ 无法匹配的问题,需要查看栈中存储’(’ 的数量,将其与num 相加就是需要最终要添加的括号数量。
- 设立st栈来存储 ( 字符,设立num来记录缺省的括号数量
- 遍历字符串,当遇到字符 ( 时将其压栈
- 当遇到 ) 字符时分为两种情况,st栈为空时num加一,st栈不为空时出栈一次
- 字符串遍历结束将num与栈的大小相加
class Solution {
public:
int minAddToMakeValid(string S) {
stack<char> st;
int num = 0;
int len = S.length();
for(int i = 0; i < len; i++){
if(S[i] == '('){
st.push(S[i]);
}
else{
if(st.empty()){
++num;
}
else{
st.pop();
}
}
}
num += st.size();
return num;
}
};
739.每日温度(中等)
题目:
根据每日气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
给定一个列表
temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
使用常规方法逐次查找下一次更大值的时间复杂度为O(N),我们为了提高该算法的时间复杂度使用栈的方法。
我们在算法中引入栈,该算法的核心思想如下:
从左到右遍历该数组,用栈来存储特定的数字下标,将结果存储在一个数组中,这样就使用栈完成了“定位”和存储。
从左向右看,当栈为空时将当前的数组下标压栈,当下一个数字大于栈顶的数字时,将栈顶的数字弹出,并且将目前的数组下标减去栈顶的数字,此结果就是下一个温度更高之间的相隔天数。
而当栈顶元素大于等于当前元素时,同样将该下标压栈,当遇到下一个“更高”温度的下标时再出栈进行计算。
总之一句话,该栈的作用就在于保存拥有一个温度的日期下标,与比它更高温度日期之间所经过的天数。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& T) {
stack<int> st;
int len = T.size();
vector<int> res(len, 0);
for(int i = 0; i < len; ++i){
while(!st.empty() && T[st.top()] < T[i]){
int index = st.top(); st.pop();
res[index] = i- index;
}
st.push(i);
}
return res;
}
};
71.简化路径
题目:
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
示例 1:
输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:"/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
示例 3:
输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:"/a/./b/../../c/"
输出:"/c"
示例 5:
输入:"/a/../../b/../c//.//"
输出:"/c"
示例 6:
输入:"/a//bc/d//././/.."
输出:"/a/b/c"
典型的栈算法。总体而言分为三种情况,字符为 / 时,字符为 . 时,字符为其它时。
- 设置一个存储string类型的栈,开始遍历字符串
- 每次遍历前需要循环跳过斜杠,直到找到一个非斜杠字符
- 分三种情况
- 1.第一种是路径名为一个点,则直接跳过
- 2.第二种路径名为两个点,此时计数器加1,表示要跳过的路径长度
- 3.第三种是路径名为两个以上的点或者其他字符,此时再分两种情况,一种是计数器大于0时,跳过该路径,然后计数器减1;另一种是计数器为0时,此时直接在结果字符串中插入该路径名即可。
class Solution {
public:
string simplifyPath(string path) {
if(path.length() == 0) return "";
stack<string> st;
string tmp;
path += "/";
int now = 0;
while(now < path.length() - 1)
{
while(path[now] == '/' && now < path.length() - 1) now ++;
if(now >= path.length() - 1) break;
if(path[now] == '.' && (path[now + 1] =='.' || path[now + 1] == '/'))
{
if(path[now + 1] == '.' && (now + 2 > path.length() - 1 || path[now + 2] == '/'))
{
if(!st.empty()) st.pop();
now = now + 2;
continue;
}
else
if(path[now + 1] != '.')
{
now ++;
continue;
}
}
tmp = "";
while(path[now] != '/')
{
tmp += path[now];
now ++;
}
st.push(tmp);
}
tmp = "";
if(st.empty()) return "/";
while(!st.empty())
{
tmp = "/" + st.top() + tmp;
st.pop();
}
return tmp;
}
};
1019.链表中的下一个更大节点
题目:
给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, … 。
每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且 node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。
返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。
注意:在下面的示例中,诸如 [2,1,5] 这样的输入(不是输出)是链表的序列化表示,其头节点的值为 2,第二个节点值为 1,第三个节点值为 5 。
示例 1:
输入:[2,1,5]
输出:[5,5,0]
示例 2:
输入:[2,7,4,3,5]
输出:[7,0,5,5,0]
示例 3:
输入:[1,7,5,1,9,2,5,1]
输出:[7,9,9,9,0,5,0,0]
很明显,这道题可以通过两层循环来暴力破解,但这样就不符合使用栈的要求。
首先,我们设立一个存储链表节点数据的栈st, 这个栈用来存储一个节点以及之后小于它节点的数据,再设立一个存储结果的数组res,一个front指针,该指针作用之后会说到;遍历链表,同时将一个0插入数组res中用来占位,之后当栈为空并且当前链表的节点数值大于栈顶元素时,就要进行相关操作,反之继续将该链表数值压栈。
我们栈中存储的数据是要最终放到数组res中,因此如何定位相关数据对应的位置,这时front指针的作用就体现出来。 当要存储的位置不为0时,也就说明该位置已经有下一个最大的元素,此次front指针继续向数组左端运动;而当front前一处res的值为0时,说明该位置还未被发现它的下一个最大元素,此时将链表的当前节点数值赋给该front位置的res元素,之后出栈一次。
当链表遍历结束之后,res数组也就完成。总体时间复杂度为O(N)。
- 设立栈st, 数组res, 指示数组和链表对应位置的指针 i
- 遍历链表
- 每次遍历一个节点,就在数组res中添加一个元素,同时刷新front指针
- 当栈不为空,栈顶元素大于等于当前链表元素大小时,将当前链表元素压入栈st中
- 否则,当数组res[front - 1] 不为0时,front 指针继续向左前进;当数组res[front - 1] 为0时,将res[front-1] 赋值为当前链表节点数值
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
vector<int> res;
ListNode *node = head;
stack<int> st;
int i = 0;
while(node){
res.push_back(0);
int front = i;
while(!st.empty() && st.top() < node->val){
if(res[front - 1] == 0){
res[--front] = node->val;
st.pop();
}
else{
--front;
}
}
st.push(node->val);
i++;
node = node->next;
}
return res;
}
};