是上一篇的后续,特别说明,以下序号均为力扣序号。
栈:
20:
问题类型:括号匹配。
解决思路:栈。
解决方法:
- 如果字符数量为单数那么一定匹配不到
- 左括号入栈
- 遇到右括号时
- 栈是否为空:若空说明匹配不到,防止溢出直接返回
- 栈不为空且匹配到了对应括号:左括号出栈
- 不空也匹配不到:出现了独立的右括号,匹配不到
- 最后检查栈是否为空,这一步是3(b)的后续,作用是查看是否有多余的左括号。
class Solution {
public:
bool isValid(string s) {
stack<char> st;
int size=s.size();
// 如果字符数量非偶数那么一定匹配不上
if(size%2!=0){
return false;
}
for(int i=0;i<size;i++){
// 将左括号压入栈
if(s[i]=='('||s[i]=='['||s[i]=='{'){
st.push(s[i]);
}else{
// 此时一定是右括号
// 如果栈里为空说明没有匹配到
if(st.empty()){
return false;
}
// 成功匹配括号,左括号正常出栈
else if(s[i]==')'&&st.top()=='(' ||
s[i]==']'&&st.top()=='[' ||
s[i]=='}'&&st.top()=='{' ){
st.pop();
}
// 非以上两种情况说明出现了仅存在独立的右括号的情况,匹配错误
else{
return false;
}
}
}
// 匹配完成后检查栈是否为空,可看出有无多余的左括号
return st.empty();
}
};
739:
问题类型:每日温度(单向栈)
解决思路:单向栈
解决方法:
初始化一个结果数组,令结果数组元素数量与原始数组元素数量相同,并初始化为0。一次遍历中,将遍历过的元素的下标存入栈中。每次遇到新元素时有两种可能:
- 当前元素小于等于栈顶元素:
- 直接入栈(保证栈中从顶到低元素从小到大排列)
- 当前元素大于栈顶元素:
- 记录当前元素与栈顶元素之间下标的差(i-st.top())存入结果vector的相应位置(vec[st.top]);
- 取出所有比当前值小的元素后,将当前元素下标压入栈。
注:本题中下标的对应有点意思,可以考虑一下。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int size=temperatures.size();
vector<int> result(size,0);
stack<int> st;
st.push(0);
for(int i=0;i<size;i++){
if(temperatures[i]<=temperatures[st.top()]){
st.push(i);
}else{
while(!st.empty()&&temperatures[i]>temperatures[st.top()]){
result[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
}
return result;
}
};
735:
问题类型:行星碰撞。
解决思路:栈或队列。
解决方法:。。。没看到很好的解法,我面向测试用例编程了。(被边界条件搞死)需要注意的是,如果先输入负值,再输入正值,那么行星不会相撞,因为错过了(题目说在一行上运行),唯一会相撞的情况只有先输入正值再输入负值。
代码是面向测试用例打补丁的。。。建议不要看。但是能运行。总之我是菜狗,别骂了。
class Solution {
public:
vector<int> asteroidCollision(vector<int>& asteroids) {
int size=asteroids.size();
stack<int> st;
vector<int> result;
st.push(asteroids[0]);
for(int i=1;i<size;i++){
if(st.empty()){
st.push(asteroids[i]);
continue;
}
if(asteroids[i]<0&&st.top()>0&&asteroids[i]*st.top()<0){
int flag=1;
while(!st.empty()&&asteroids[i]*st.top()<0){
if(abs(asteroids[i])<abs(st.top())){
flag=0;
break;
}else if(abs(asteroids[i])>abs(st.top())){
st.pop();
}else{
st.pop();
flag=0;
break;
}
}
if(flag) st.push(asteroids[i]);
}else{
st.push(asteroids[i]);
}
}
while(!st.empty()){
result.emplace_back(st.top());
st.pop();
}
reverse(result.begin(),result.end());
return result;
}
};
496:
问题类型:下一个更大元素。
解决思路:哈希表+单调栈。
解决思路:
单调栈的基本写法不变,此处在这里列出:
单调栈:循环整个vector并判断vector中当前元素与栈顶元素的大小关系
-
- 当前元素小于栈顶元素:压入栈
- 当前元素大于栈顶元素:将result中对应位置的值置换为当前元素;
想了想,这里的处理方法有点像dp。至于result(结果vector)中的值究竟存储下标还是具体值,建议根据题意确定,而且用题目明显指向下标时不要用值,明显指向值的时候不要用下标,会出奇怪的问题。不过单调栈中一定只能push进下标,因为下标不会重复,但数组中的值会重复。
之后就是根据题意索引,看代码即可。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
// nums1,nums2长度
int size1=nums1.size(); int size2=nums2.size();
// 存储单调栈变换后的nums2
vector<int> temp(size2,-1);
// 存储最终结果
vector<int> result(size1,-1);
// 单调栈
stack<int> st;
// 存储nums2的哈希表,方便存取
unordered_map<int,int> mp;
// 将nums2存储到哈希表中,方便以哈希的形式完成下标查找
for(int i=0;i<size2;i++){
// 以等号前面的索引等号后面的
mp[nums2[i]]=i;
}
// 完成nums2的单调栈构造
st.push(nums2[0]);
for(int i=1;i<size2;i++){
if(nums2[i]<st.top()){
st.push(nums2[i]);
}else{
while(!st.empty()&&nums2[i]>st.top()){
temp[mp[st.top()]]=nums2[i];
st.pop();
}
st.push(nums2[i]);
}
}
// 来回绕的索引
for(int i=0;i<size1;i++){
result[i]=temp[mp[nums1[i]]];
}
return result;
}
};
503:
问题类型:下一个更大元素II
解决思路:单调栈。且取余
解决思路:
单调栈的基本写法仍然不变,切记单调栈的栈中仅push下标。
循环类问题取余操作可以解决,切记切记。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
// 单调栈,且单调栈中是值,因此需要哈希表转换
// 长度
int size=nums.size();
// 结果数组
vector<int> result(size,-1);
stack<int> st;
unordered_map<int,int> mp;
for(int i=0;i<size;i++){
mp[nums[i]]=i;
}
st.push(0);
for(int i=1;i<2*size;i++){
int temp=i%size;
if(nums[temp]<=nums[st.top()]){
st.push(temp);
}else{
while(!st.empty()&&nums[temp]>nums[st.top()]){
result[st.top()]=nums[temp];
st.pop();
}
st.push(temp);
}
}
return result;
}
};
问题类型:字符串解码
解决方法:栈
解决思路:
创建一个数字栈,一个字母栈,开始遍历:
- 遍历遇到数字:存储数字(number=number*10+s[i]-'0';)
- 遍历遇到字母:存储字符串(str=str+'s[i]';)
- 遍历遇到左括号'[':将存储的数字压入数字栈并将容器置零,将存储的字符串压入字符栈并将容器置空。
- 遍历遇到右括号']':将数字栈顶数字times读出,使字符栈顶字符串重复times,将字符串栈顶元素放入字符串容器中并pop栈顶元素。
class Solution {
public:
string decodeString(string s) {
int size=s.size();
stack<int> nums;
stack<string> str;
int number=0;
string result="";
for(int i=0;i<size;i++){
if(s[i]>='0'&&s[i]<='9'){
number=number*10+(s[i]-'0');
}else if(s[i]>='a'&&s[i]<='z'||s[i]>'A'&&s[i]<'Z'){
result=result+s[i];
}else if(s[i]=='['){
nums.push(number);
number=0;
str.push(result);
result="";
}else{
int times=nums.top();
nums.pop();
for(int j=0;j<times;j++){
str.top()=str.top()+result;
}
result=str.top();
str.pop();
}
}
return result;
}
};
636:
解决思路:
设定stack<pair<int,int>>类型的栈方便存取,以auto item:logs的方式遍历可以少索引一层下标方便编写。
遍历一个字符串时:
1. 读入当前函数名称(id);
2. 判断为start还是end:
若为start:跳过start:后读出结束时间,若栈为空则压入此时的函数id和开始执行时间;若栈不为空则以结束时间-开始执行时间更新result中对应的运行时长,再压入此时新的函数id与开始执行时间。
若为end:跳过end:后读出结束时间,以此时的时间-开始执行时间更新result中对应的运行时长,pop()当前栈顶函数后判断栈是否为空,如果不为空则更新栈顶元素的开始执行时间为当前time+1;
建议直接看代码吧。。代码+注释比口述清晰。
class Solution {
public:
vector<int> exclusiveTime(int n, vector<string>& logs) {
stack<pair<int,int>> st;
vector<int> result(n,0);
for(auto item:logs){
int i=0;
int id=0;
// 读入数字
while(item[i]!=':'){
// 正常读入,i为循环变量
id=id*10+item[i]-'0';
i++;
}
// 绕开:
i++;
if(item[i]=='s'){
// 绕开start:
i+=6;
int time=0;
while(i<item.size()){
time=time*10+item[i]-'0';
i++;
}
// 为空直接压入栈
if(st.empty()){
st.push({id,time});
}
// 不为空可以计算出当在运行的函数已经运行了多久,中间为加号是递归情形
else{
result[st.top().first]+=time-st.top().second;
// 压入栈,若为递归情形,此时更新了开始执行时间;非递归情况压入新函数
st.push({id,time});
}
}
else{
// 绕开end:
i+=4;
int time=0;
while(i<item.size()){
time=time*10+item[i]-'0';
i++;
}
// 计算当前函数运行了多久
result[st.top().first]+=time-st.top().second+1;
st.pop();
// 若栈为空,更新开始执行时间
if(!st.empty()){
st.top().second=time+1;
}
}
}
return result;
}
};
堆:
23:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode dummy=-1;
ListNode* q=&dummy;
// 建立优先队列,cmp使用重载函数编写
priority_queue<ListNode*,vector<ListNode*>,cmp> pq;
// 将n个链表的头插入优先队列
for(ListNode* nNode:lists){
if(nNode){
pq.emplace(nNode);
}
}
// 循环插入
while(!pq.empty()){
// 优先队列(已经是最小堆)头插入链表
q->next=pq.top();
// 已经插入所以弹出
pq.pop();
// 链表指针后移到刚刚插入的元素
q=q->next;
// 将之后的值(旧分支链表当前的头)插入优先队列(可以自动重组最小堆)
if(q->next){
pq.emplace(q->next);
}
}
return dummy.next;
}
// 重载函数,为了适配优先队列(本质为堆)的建立而对cmp预处理
private:
struct cmp{
// 此处建立最小堆
bool operator()(ListNode* q1,ListNode*q2){
return q1->val>q2->val;
}
};
};
347:
注意pair<int,int>p的索引,p.first或p.second即可,不用额外加括号,没有first()!!!
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
int size=nums.size();
unordered_map<int,int> mp;
// 使用map判断出每个元素有几个
for(auto& a:nums){
mp[a]++;
}
// 构建优先队列
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> pq;
// 插入队列
for(auto& a:mp){
pq.push(a);
// 因为只要前k大的,所以数量多于k时,直接pop出最小的
if(pq.size()>k){
pq.pop();
}
}
// 输出
vector<int> result;
while(!pq.empty()){
result.emplace_back(pq.top().first);
pq.pop();
}
return result;
}
private:
// 此处构建最小堆
struct cmp{
bool operator()(pair<int,int>& q1,pair<int,int>& q2){
return q1.second>q2.second;
}
};
};
295:
问题类型:求中位数
解决方法:构造一个最大堆一个最小堆,对顶堆。
class MedianFinder {
public:
// 最大堆函数
struct less{
bool operator()(int q1,int q2){
return q1<q2;
}
};
// 最小堆函数
struct more{
bool operator()(int q1,int q2){
return q1>q2;
}
};
// 建立最大堆
priority_queue<int,vector<int>,less> small;
// 建立最小堆
priority_queue<int,vector<int>,more> big;
int n=0;
MedianFinder() {
n=1;
}
void addNum(int num) {
// 如果为空直接压入优先队列
if(small.empty()){
small.push(num);
return;
}
// 判断新加入的元素
if(num<small.top()){
small.push(num);
}
else{
big.push(num);
}
if(small.size()>big.size()+1){
big.push(small.top());
small.pop();
}
if(small.size()<big.size()-1){
small.push(big.top());
big.pop();
}
n++;
}
double findMedian() {
printf("%d\n",n);
if(n%2==1){
if(small.size()>big.size()){
return small.top();
}else{
return big.top();
}
}else{
printf("%d,%d\n",small.top(),big.top());
return ((long long)(small.top()+big.top()))*0.5;
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
767:
问题类型:重构字符串
解决方法:
构造一个哈希表,构造一个优先队列。通过哈希表统计并中转,完成优先队列的填入和最大堆的建立。
使用一个pair类型变量中转(last),每次弹出最大堆顶端元素,将元素存入last后该元素总数-1([i,c]中i-1),相隔一轮后将last重新加入堆,完成不相邻排列。
class Solution {
public:
string reorganizeString(string s) {
// 计算长度
int size=s.size();
// 结果
string result="";
// 哈希表
unordered_map<char,int> mp;
// 优先堆列,最大堆
priority_queue<pair<int,char>,vector<pair<int,char>>,cmp> pq;
// 填哈希表
for(char c:s){
mp[c]++;
}
// 由哈希表填优先队列
for(auto [c,i]:mp){
// printf("%c.",c);
// printf("%d",i);
pq.emplace(i,c);
}
// printf("\n");
// 利用last完成间隔输出
pair<int,char> last{-1,0};
while(!pq.empty()){
auto [i,c]=pq.top();
pq.pop();
// printf("此时读入的字符:%c,",c);
result=result+c;
// printf("字符此时次数:%d",i);
if(last.first>0){
pq.emplace(last);
}
last={i-1,c};
// printf("字符修改次数:%d\n",last.first);
}
// 返回值
if(last.first==0){
return result;
}else{
return "";
}
}
private:
struct cmp{
bool operator()(pair<int,char> q1,pair<int,char>q2){
return q1.first<q2.first;
}
};
};
703:
问题类型:第K大元素
解决方法:优先队列(最大/小堆)
class KthLargest {
public:
struct cmp{
bool operator()(int q1,int q2){
return q1>q2;
}
};
// 最小堆
priority_queue<int,vector<int>,cmp> pq;
int top=0;
KthLargest(int k, vector<int>& nums) {
top=k;
for(int i:nums){
pq.emplace(i);
// 要第k大的元素,那么保证最小堆中仅有k个元素,顶层就是第k大的
if(pq.size()>k){
pq.pop();
}
}
}
int add(int val) {
// 同理,添加返回值即可
pq.emplace(val);
if(pq.size()>top){
pq.pop();
}
return pq.top();
}
};
/**
* Your KthLargest object will be instantiated and called as such:
* KthLargest* obj = new KthLargest(k, nums);
* int param_1 = obj->add(val);
*/