堆栈刷题记录(更新中)

是上一篇的后续,特别说明,以下序号均为力扣序号。

栈:

20:

问题类型:括号匹配。

解决思路:栈。

解决方法:

  1. 如果字符数量为单数那么一定匹配不到
  2. 左括号入栈
  3. 遇到右括号时
    1. 栈是否为空:若空说明匹配不到,防止溢出直接返回
    2. 栈不为空且匹配到了对应括号:左括号出栈
    3. 不空也匹配不到:出现了独立的右括号,匹配不到
  4. 最后检查栈是否为空,这一步是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。一次遍历中,将遍历过的元素的下标存入栈中。每次遇到新元素时有两种可能:

  1. 当前元素小于等于栈顶元素:
    1. 直接入栈(保证栈中从顶到低元素从小到大排列)
  2. 当前元素大于栈顶元素:
    1. 记录当前元素与栈顶元素之间下标的差(i-st.top())存入结果vector的相应位置(vec[st.top]);
    2. 取出所有比当前值小的元素后,将当前元素下标压入栈。

注:本题中下标的对应有点意思,可以考虑一下。

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中当前元素与栈顶元素的大小关系

    1. 当前元素小于栈顶元素:压入栈
    2. 当前元素大于栈顶元素:将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;
    }
};

问题类型:字符串解码

解决方法:栈

解决思路:

创建一个数字栈,一个字母栈,开始遍历:

  1. 遍历遇到数字:存储数字(number=number*10+s[i]-'0';)
  2. 遍历遇到字母:存储字符串(str=str+'s[i]';)
  3. 遍历遇到左括号'[':将存储的数字压入数字栈并将容器置零,将存储的字符串压入字符栈并将容器置空。
  4. 遍历遇到右括号']':将数字栈顶数字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);
 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值