栈,队列,堆个人刷题汇总

目录

  • 224 简单的加减运算
  • 295 输出中位数
  • 剑指offer 09 双栈模拟队列
  • 636函数的独占时间
  • 456 132模式
  • 880 索引处的解码字符串
  • 735 行星碰撞
  • 394 字符串解码
  • 215 TOP K
  • 641 设计循环双端队列
  • 933最近的请求次数
  • 622 设计循环队列
  • 26 删除排序树组中的重复项
  • 283移动0问题
  • 11 盛最多水的容器

224 简单的加减运算

类型:栈 难度 困难

思路:对于(1+(4+5+2)-3)+(6+8)这样一个式子,只有加减两种,所以借鉴了大神的思路,判断扫描到的字符,如果是左括号,则表示开启新的一波计算,将之前的计算结果和左括号前的运算度也入栈,如果是右括号,则表示这一波的计算结束了,这时候需要将之前存的运算符出栈,把之前的结果也出栈,和当前的结果进行计算,

res*=stack.top(); stack.pop(); res+=stack.top();stack.pop();

295 输出中位数

类型:堆,优先队列 难度 困难

思路:用c++中的priority_queue 创建两个优先队列去模拟堆,一个小顶堆small_queue用来存储数据中较大的一半,一个大顶堆big_queue用来存储数据中较大的一半,然后 small_queue.top() > big_queue.top(),但是还需要保证一点,由于求的是中位数,所以small_queue.size() 和 big_queue.size() 要么相等,要么相差一

如果相等的话,直接输出 两个队列 top 的平均值,否则的话,哪个队列的size更大就输出谁的 top ,在 插入 过程中,需要调整两个队列的size,让尺寸保持上面的标准

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    
    void addNum(int num) {
        small_queue.push(num);
        big_queue.push(small_queue.top());
        small_queue.pop();
        if (small_queue.size() < big_queue.size()) {
            small_queue.push(big_queue.top());
            big_queue.pop();
        }        
    }
    
    double findMedian() {
        int size = big_queue.size() + small_queue.size();
        return size % 2 == 1 ? small_queue.top() : (small_queue.top() + big_queue.top()) / 2.0;
    }
private:
    //保证小顶堆的堆顶>大顶堆堆顶,且两个堆size一样或者小.size() = 大.size()+1
    priority_queue<int, vector<int>, greater<int>> big_queue; //小顶堆
    priority_queue<int, vector<int>, less<int>> small_queue;//大顶堆
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

剑指offer 09 双栈模拟队列

类型:栈 队列 难度 简单

思路:两个栈 s1 s2,用s1模拟入队,出队的时候,进行判断,如果两个栈都为空,返回-1,如果s2为空,则把s1的元素都出栈然后入栈到s2中,如果s2不为空,则直接输出s2.top()

636函数的独占时间

类型:栈 难度 中等 88.62% 100%

思路:计算函数的独占时间,思路类似于最简答的括号匹配,不过需要对时间进行简单的计算,设计两个数据结构,一个vectorres存储最后结果,一个stack>来存储每个任务时间,这两个Int分别是函数id和时间,如果是start的话,则入栈,如果是end的话,先出栈计算运行时间,然后如果此时栈非空,则需要更新这个函数的独占时间,即减去刚出栈的这个函数的独占时间。

class Solution {
public:
    vector<int> exclusiveTime(int n, vector<string>& logs) {
        vector<int> res(n,0);
        for(auto &log:logs)
        {
            int index1 = log.find(':',0);
            int index2 = log.rfind(':');
            int fun_id = atoi(log.substr(0,index1).c_str());
            string status = log.substr(index1+1, index2-index1-1);
            int time = atoi(log.substr(index2+1).c_str());
            if(status == "start")
            {
                s.push(make_pair(fun_id, time));
            }else if(status == "end")
            {
                int st_time = s.top().second;
                int index = s.top().first;
                s.pop();
                int run_time = time - st_time + 1;
                res[index] += run_time;
                if(!s.empty())
                {
                    res[s.top().first] -= run_time;
                }
            }
        }
        return res;
    }
private:
    stack<pair<int, int>> s;
};

456 132模式

类型:栈 难度 中等

思路:要求 i

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        if(nums.size()<3) return false;
        stack<int> max;
        int n=nums.size();
        int third=INT_MIN;//初始化不能为0,因为要进行比较,初始化为0会出错。
        for(int i=n-1;i>=0;i--)
        {
            if(nums[i]<third) return true;
            while(!max.empty() && nums[i]>max.top())//若找到比栈中还大的元素,则更新
            {
                third=max.top();
                max.pop();
            }
            max.push(nums[i]);
        }
        return false;
    }
};

880 索引处的解码字符串

类型:栈 难度 中等

思路:首先不可能把解码后的字符串都展开,因为可能会特别长,所以我们需要计算每一步展开后的长度 long long curLength,比如 lee2code3 ,如果当前扫描到是字母的话,就把curLength++,如果是数字的话,就把当前长度乘上这个数字,如果当前长度 >= K,就结束遍历,后面的字符我们就没必要关注了

然后开始计算最后结果,如果最后是因为乘了数字导致的>=K,则首先curLength要除以这个数字,也就是去掉复印件,得到原件,然后K也需要对curLength取模,因为比如说 3*2>5, 相当于是把3复制了两倍,比5大,实际对应的就是原件的第二个,

如果最后不是因为乘了数字,就需要一步一步往前退,知道退到了上面K取模之后的值

class Solution {
public:
    string decodeAtIndex(string S, int K) {
        long long curLength=0;
        int index=0;
        for(auto s:S)
        {
            if(isdigit(s))
            {
                curLength *=(s-'0');
            }
            else
            {
                curLength++;
            }
            if(curLength >= K)
                break;
            index++;
        }
        for(int i=index;i>=0;i--)
        {
            if(isdigit(S[i]))
            {
                curLength/=(S[i]-'0');
                K%=curLength;
            }
            else
            {
                if(K%curLength == 0)
                {
                    string res(1,S[i]);
                    return res;
                }
                curLength--;
            }
        }
        return "";
    }
};

735 行星碰撞

类型:堆 难度 中等 82.72% 33.33%

思路:进行分情况讨论,如果是当前栈顶和扫描到的数字符号相同,则直接把扫描到的数字入栈,如果栈顶是负数,扫描到的是正数,则也是直接入栈,上述两种情况均不会发生碰撞

可能发生碰撞的情况是,栈顶是一个正数,扫描到的是一个负号,这时候需要判断如果负数的绝对值大则需要出栈,继续重复上述流程,如果正数绝对值更大,则直接扫描下一个数字

class Solution {
public:
    bool sameAbs(int a, int b)
    {
        if(a<0 && b<0)
            return true;
        else if(a>0 && b>0)
            return true;
        else 
            return false;
    }
    vector<int> asteroidCollision(vector<int>& asteroids) {
        for(auto asteroid:asteroids)
        {
            M:if(st.empty())
                st.push(asteroid);
            else if(sameAbs(asteroid, st.top())) //同号
                st.push(asteroid);
            else if(asteroid > 0 && st.top() < 0) //- + 
                    st.push(asteroid);
            else // +-
            {
                if(abs(asteroid) > abs(st.top())) // - > +
                {
                   st.pop();
                   goto M;
                }
                else if(abs(asteroid) == abs(st.top())) //- = +
                {
                    st.pop();
                    continue;
                }
                else 
                    continue;
            }
        }
        vector<int> res(st.size(), 0);
        int size = st.size()-1;
        for(int i=size;i>=0;i--)
        {
            res[i] = st.top();
            st.pop();
        }
        return res;
    }
private:
    stack<int> st;
};

这里附上耗时最短的大佬的想法,处理思路一致,用数组代替栈,思路更加巧妙

class Solution {
public:
    vector<int> asteroidCollision(vector<int>& asteroids) {
        int len = asteroids.size();
        int stack[len];
        int top = 0;
        int i = 0;
        while(i < len){
            if (top == 0){
                stack[top] = asteroids[i];
                top++;
                i++;
            }else if(asteroids[i] * stack[top-1] >0 || (asteroids[i]>0&&stack[top-1]<0)){
                stack[top] = asteroids[i];
                top++;
                i++;
            }else{
                    if(abs(stack[top-1]) > abs(asteroids[i])){
                        i++;
                        continue;
                    }else if (abs(stack[top-1]) == abs(asteroids[i])){{
                        top--;
                        i++;
                    }
                    }else if  (abs(stack[top-1]) < abs(asteroids[i])){
                        top--;
                    }
                }
        }
        vector<int>res;
        for (int i = 0; i < top; i++){
            res.push_back(stack[i]);
        }
        return res;
    }
};

394 字符串解码

类型:栈 难度 中等 100% 100% (我也不知道为什么有点繁琐的代码能双百,挺离谱的,应该是正常的波动范围?)

思路:见代码注释

class Solution {
public:
    string decodeString(string s) {
        int i=0,num=0;
        bool noNum=true;
        for(;i<s.size();i++)
        {
            //遇到数字需要确定到底需要重复几次,如果是100[a]这种,不能直接把1入栈,而应该一直扫描到左括号才行
            if(isdigit(s[i]))
            {
                if(s[i+1] == '[')
                {   num=num*10 + (s[i]-'0');
                    stNum.push(num);
                    num=0;
                }
                else     
                    num=num*10 + (s[i]-'0');
                noNum=false;
            }
            //之前没有数字或数字被处理完了
            if(noNum) 
            {
                res+=s[i];
            }
            //有数字说明 之后会出现字母和括号
            else if(!noNum) 
            {
                if(s[i] == '[')
                    stBor.push(s[i]);
                else if(s[i] == ']')
                {
                    /*
                    有右括号的话,需要把左括号之前的元素出栈,存入temp
                    由于是栈,所以实际的顺序需要反过来,这里借用了temp_2进行翻转
                    翻转过后根据数字进行重复,再入栈
                    再入栈是为了处理 3[a2[bc]] 这种情况
                    如果不再入栈,而是直接加入结果,最后的结果就成了 bcbcaaa
                    所以需要把bcbc再入栈,栈中为 [abcbc,再扫描到右括号就继续上面相同的操作
                    
                    结束的依据是,如果数字栈为空,表示当前扫描到的数字都处理完了
                    此时出栈并加入结果字符串中
                    
                    扫描过程中遇到了字母,直接入栈就行
                    */
                    while(stBor.top()!='[')
                    {
                        temp+=stBor.top();
                        stBor.pop();
                    }
                    stBor.pop();
                    temp_2 = string(temp.rbegin(), temp.rend());
                    int time = stNum.top();
                    stNum.pop();
                    for(int j=0;j<time;j++) //根据数字进行重复
                    {
                        for(int k=0;k<temp_2.size();k++)
                        {
                            stBor.push(temp_2[k]); //在入栈主要是为了处理括号套括号的情况
                        }
                    } 
                    temp = string();
                    temp_2 = string();
                    //数字栈为空,表示目前扫描到的数字都处理完毕了,拼入结果字符串中
                    if(stNum.empty()) 
                    { 
                        noNum=true;
                        int size = stBor.size();
                        for(int j=0;j<size;j++)
                        {
                            temp+=stBor.top();
                            stBor.pop();
                        }
                        temp_2 = string(temp.rbegin(), temp.rend());
                        res+=temp_2;
                        
                    }
                    temp = string();
                    temp_2 = string();
                       
                }
                else if(isalpha(s[i]))
                {
                    stBor.push(s[i]);
                }
            }
            
        }
        return res;
    }
private:
    string temp;
    string temp_2;
    string res;
    stack<int> stNum;
    stack<char> stBor;
};

另一种递归范例,处理思路一致

class Solution {
public:
    // 递归实现
    string dfs(const string &s, int &i) {
        string res = "";
        int num = 0;
        for (; i < s.size(); i++) {
            char c = s[i];
            if (c >= '0' && c <= '9') {
                num = num * 10 + (c - '0');
            } else if (c == '[') {
                string tmp1 = "";
                string tmp2 = dfs(s, ++i);
                while (num > 0) {
                    tmp1 += tmp2;
                    num--;
                }
                res += tmp1;
            } else if (c == ']') {
                return res;
            } else {
                res += c;
            }
        }
        return res;
    }
    string decodeString(string s) {
        int i = 0;
        return dfs(s, i);
    }
};

215 TOP K

类型:小顶堆 难度 中等
思路:维护一个size为K的小顶堆

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        for(int i=0;i<nums.size();i++)
         {
            if(myQue.size() < k)
                myQue.push(nums[i]);
            else if(myQue.top() < nums[i]){
                myQue.pop();
                myQue.push(nums[i]);
            }
        }
        return myQue.top();
    }
private:
    priority_queue<int, vector<int>, greater<int>> myQue;
};

641 设计循环双端队列

类型 队列 难度 中等
思路:我觉得这应该是个简单题,用双向循环链表去模拟一下,链表增删这不是基本功吗我寻思

class MyCircularDeque {
public:
    /** Initialize your data structure here. Set the size of the deque to be k. */
    int size=0,maxsize=0;
    struct LNode{
        int data;
        LNode *next;
        LNode *pre;
        LNode() : data(-1), next(NULL), pre(NULL) {}
        LNode(int v) : data(v), next(NULL), pre(NULL) {}
    };
    LNode *head;
    LNode *tail;
    MyCircularDeque(int k) {
        maxsize=k;
        head = new LNode();
        tail = new LNode();
        head->next = tail;
        head->pre = tail;
        tail->next = head;
        tail->pre = head;
    }
    
    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    bool insertFront(int value) {
        if(isFull())
            return false;
        struct LNode * node = new LNode(value);
        node->pre = head;
        head->next->pre = node;
        node->next = head->next;
        head->next = node;
        ++size;
        return true;
    }
    
    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    bool insertLast(int value) {
        if(isFull())
            return false;
        struct LNode *node = new LNode(value);
        node->pre = tail->pre;
        tail->pre = node;
        node->pre->next = node;
        node->next = tail;
        ++size;
        return true;
    }
    
    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    bool deleteFront() {
        if(isEmpty())
            return false;
        struct LNode * node = head->next;
        struct LNode * node2 = head->next->next;
        head->next = node2;
        node2->pre = head;
        delete(node);
        --size;
        return true;
    }
    
    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    bool deleteLast() {
        if(isEmpty())
            return false;
        struct LNode* node = tail->pre;
        struct LNode * node_2 = tail->pre->pre;
        tail->pre = node_2;
        node_2->next = tail;
        delete(node);
        --size;
        return true;
    }
    
    /** Get the front item from the deque. */
    int getFront() {
        if(isEmpty())
            return -1;
        return head->next->data;
    }
    
    /** Get the last item from the deque. */
    int getRear() {
        if(isEmpty())
            return -1;
        return tail->pre->data;
    }
    
    /** Checks whether the circular deque is empty or not. */
    bool isEmpty() {
        return (size == 0);
    }
    
    /** Checks whether the circular deque is full or not. */
    bool isFull() {
        return size==maxsize;
    }
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque* obj = new MyCircularDeque(k);
 * bool param_1 = obj->insertFront(value);
 * bool param_2 = obj->insertLast(value);
 * bool param_3 = obj->deleteFront();
 * bool param_4 = obj->deleteLast();
 * int param_5 = obj->getFront();
 * int param_6 = obj->getRear();
 * bool param_7 = obj->isEmpty();
 * bool param_8 = obj->isFull();
 */

933最近的请求次数

说实话,这个简单题我没搞懂题目是什么意思,虽然它很简单

class RecentCounter {
public:
	RecentCounter() {}
	int ping(int t) {
		que.push(t);
		while (que.front() < t - 3000) 
            que.pop();
		return que.size();
	}
private:
	queue<int>que;
};

622 设计循环队列

类型: 队列 难度:中等 99.85% 36ms 100% 15.5MB
思路:用数组模拟一个循环队列,牺牲一个存储单元作为判断队满的条件,(rear+1)%maxsize==front
然后获取队尾元素时需要注意一点,因为rear比实际的最后一个元素更靠后,所以需要判断一下,如果rear为0,则rear-1就越界了,所以需要判断一下,如不不越界,直接输出rear-1处的值,否则输出maxsize-1处的

class MyCircularQueue {
public:
    /** Initialize your data structure here. Set the size of the queue to be k. */
    int *que;
    int rear=0,front=0;
    int maxsize=0;
    MyCircularQueue(int k) {
        que = new int[k+1];
        maxsize=k+1;
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    bool enQueue(int value) {
        if(isFull())
            return false;
        que[rear] = value;
        rear=(rear+1)%maxsize;
        return true;
    }   
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    bool deQueue() {
        if(isEmpty())
            return false;
        front=(front+1)%maxsize;
        return true;
    }
    
    /** Get the front item from the queue. */
    int Front() {
        if(isEmpty())
            return -1;
        return que[front];
    }
    
    /** Get the last item from the queue. */
    int Rear() {
        if(isEmpty())
            return -1;
        int index = rear-1;
        if(index<0)
            index = maxsize-1;
        return que[index];
    }
    
    /** Checks whether the circular queue is empty or not. */
    bool isEmpty() {
        return rear==front;
    }
    
    /** Checks whether the circular queue is full or not. */
    bool isFull() {
        return (rear+1)%maxsize==front;
    }
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */

26 删除排序树组中的重复项

双指针法,一个正常遍历,一个标记非0元素该插入的位置

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
 		
	int j = 0;
    if(nums.empty())    return 0;
	for(int i = 1;i<nums.size();i++)	
	{
		if(nums[j] != nums[i])
		{
			nums[++j] = nums[i];
		}
	}
   	return j+1;
    }
};

283移动0问题

思路与上一题类似,两个指针

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int j=0;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i] != 0)
            {
                nums[j] = nums[i]; 
                if(i!=j) //avoid nums.size() == 0
                    nums[i]=0;
                j++;
            }
            
        }
    }
};

11 盛最多水的容器

中等
首先会选取数组的边界,然后如果把两边中较低的一边往中间挪动,容器的宽度减少,但是容器的高度可能会增加,如果把两边中较高的一边往中间挪动,容器的宽度减少,但是容器的高度最起码不会增加,因为容器的高度取决于较低的一边

class Solution {
public:
    int maxArea(vector<int>& height) {
        int left=0, right=height.size()-1, maxsize = 0;
        while(left < right)
        {
            maxsize = max(maxsize, min(height[left], height[right]) * (right - left));
            if(height[left] < height[right])
                left++;
            else 
                right--;
        }
        return maxsize;
    }
};

你可能感兴趣的:(刷题总结,c++,算法,数据结构)