leetcode-栈-队列-堆

LeetCode-栈-队列-堆

LeetCode 225 - Implement Stack using Queues - 用队列实现栈 - easy

使用队列实现栈的下列操作:

push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空

注意:

你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的

你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。


题干要求,不能使用队列的back函数,也就是纯单向队列。

那么就需要借助临时队列。

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
    }
    
    /** Push element x onto stack. */
    void push(int x) {

        // 构造临时队列
        std::queue<int> queTmp;
        queTmp.push(x);
        int nTmp = 0;

        while(!queData.empty())
        {
            nTmp = queData.front();
            queTmp.push(nTmp);
            queData.pop();
        }
        
        while(!queTmp.empty())
        {
            nTmp = queTmp.front();
            queData.push(nTmp);
            queTmp.pop();
        }
        //queData.swap(queTmp);

    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int nTmp = queData.front();
        queData.pop();
        return nTmp;
    }
    
    /** Get the top element. */
    int top() {
        return queData.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queData.empty();
    }

private:
    std::queue<int> queData;
};

不知道为啥,不用nTmp,直接push(front())的话内存消耗要更大。

LeetCode 232 - Implement Queue using Stack- 用栈实现队列 - easy

使用栈实现队列的下列操作:

push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
class MyQueue {
public:
    /** Initialize your data structure here. */
    MyQueue() {
    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        std::stack<int> sTmp;
        while(!sData.empty())
        {
            sTmp.push(sData.top());
            sData.pop();
        }
        
        sData.push(x);
        
        while(!sTmp.empty())
        {
            sData.push(sTmp.top());
            sTmp.pop();
        }
        // 这里就不能用swap了
        //sTmp.swap(sData);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        int n = sData.top();
        sData.pop();
        return n;
    }
    
    /** Get the front element. */
    int peek() {
        return sData.top();
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return sData.empty();
    }
private:
    std::stack<int> sData;
};

LeetCode 155 - Min Stack - 最小栈 - easy

设计一个支持 push ,pop ,top 操作,并能在常数时间(时间复杂度O(1))内检索到最小元素的栈。

push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

示例:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

前两道题时间复杂度都是O(n)。 思路是用空间换时间,但是只4增加一个私有变量nMin是无法记录所有状态的,比如pop之后,,,

一个变量不行,就增加多个变量:最小值栈,top记录当前状态的最小值。

需要注意,访问容器一定要判断是否为空。

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        sData.push(x);
        if(sMin.empty())
        {
            sMin.push(x);
        }
        else
        {
            sMin.push( x < sMin.top() ? x : sMin.top());
        }
        
    }
    
    void pop() {
        sData.pop();
        sMin.pop();
    }
    
    int top() {
        return sData.top();
    }
    
    int getMin() {
        return sMin.top();
    }
private:
    std::stack<int> sData;

    // 最小值栈,top记录当前状态的最小值。
    std::stack<int> sMin;
};

Leetcode 946 - Validate Stack Sequences - 验证栈序列 - Medium

类似的还有poj平台1363 Rails。

给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

思路是,借助栈存储pushed序列,一段判断pushed.top() == poped.front(),匹配则均pop。如果最后栈空了,则返回true。

这个复杂度为O(n),不是n方。

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> s;
        int nSize = pushed.size();
        int j = 0;

        for (int i = 0; i < nSize; ++i)
        {
            s.push(pushed[i]);
            while(!s.empty() && s.top() == popped[j])
            {
                s.pop();
                ++j;
            }
        }

        return s.empty() ? true : false;
    }
};

LeetCode 224 - Basic Calculator - Hard

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格  。

示例 :

输入: "(1+(4+5+2)-3)+(6+8)"
输出: 23

说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。


需要基于数字栈和符号栈,实现逆波兰式,也叫后缀表达式。

设计状态机:

'0'-'9'
parentheses
'0'-'9'
'+''-'
'0'-'9'
begin
number state
operaition state

框架:

for (int i = 0; i < nLen; ++i)
{
    if ( s[i] == ' ' ) continue;

    switch(state)
    {
        case STATE_BEGIN:
            // judge STATE
            --i
                break;
        case STATE_NUM:
            // if num, number = ...
            // else, compute, then STATE_OP
            break;
        case STATE_OP:
            // if +-, push, then set bCalc
            // if (, STATE_NUM, unset bCalc
            // if ), compute
            // if num, STATE_NUM
            break;
    } 
}

if (nRes != 0) 
{
    stackNum.push(nRes);
    compute(stackNum, stackOp);
}
else if( nRes == 0 && stackNum.empty())
{
    return 0;
}

完整版:

class Solution {
public:
    int calculate(string s) {
        // 测试中有大于(2**31-1的输入,所以用long
        std::stack<long> stackNum;
        std::stack<char> stackOp;
        long nNum = 0;
        enum State state = STATE_BEGIN;
        bool bCalc = false;
        int nLen = s.length();
        for (int i = 0; i < nLen; ++i)
        {
            if (s[i] == ' ') continue;

            switch (state)
            {
            case STATE_BEGIN:
                // judge STATE
                state = (s[i] >= '0' && s[i] <= '9')
                    ? STATE_NUM
                    : STATE_OP;
                --i;
                break;
            case STATE_NUM:
                // if num, number = ...
                // else, compute, then STATE_OP
                if (s[i] >= '0' && s[i] <= '9')
                {
                    nNum = nNum * 10 + s[i] - '0';
                }
                else
                {
                    stackNum.push(nNum);
                    if (bCalc)
                    {
                        compute(stackNum, stackOp);
                    }
                    nNum = 0;
                    --i;
                    state = STATE_OP;
                }
                break;
            case STATE_OP:
                // if +-, push, then set bCalc
                // if (, STATE_NUM, unset bCalc
                // if ), compute
                // if num, STATE_NUM
                if (s[i] == '+' || s[i] == '-')
                {
                    stackOp.push(s[i]);
                    bCalc = true;
                }
                else if (s[i] == '(')
                {
                    state = STATE_NUM;
                    bCalc = false;
                }
                else if (s[i] == ')')
                {
                    compute(stackNum, stackOp);
                }
                else if (s[i] >= '0' && s[i] <= '9')
                {
                    state = STATE_NUM;
                    --i;
                }
                break;
            }
        }

        if (nNum != 0)
        {
            stackNum.push(nNum);
            compute(stackNum, stackOp);
        }
        else if (nNum == 0 && stackNum.empty())
        {
            return 0;
        }
        return stackNum.top();
    }
private:
    void compute(std::stack<long> &stackNum, std::stack<char> &stackOp)
    {
        if (stackNum.size() < 2) return;

        long nNum1 = stackNum.top();
        stackNum.pop();
        long nNum2 = stackNum.top();
        stackNum.pop();

        char cOp = stackOp.top();
        if (cOp == '+')
        {
            stackNum.push(nNum2 + nNum1);
        }
        else if (cOp == '-')
        {
            stackNum.push(nNum2 - nNum1);
        }
        stackOp.pop();
    }
private:
    enum State {
        STATE_BEGIN = 0,
        STATE_NUM = 1,
        STATE_OP = 2
    };

};

LeetCode 215 - Kth Largest Element in an Array - 数组中第k大的数 - easy

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

二叉堆

最大/小二叉堆:最大/小值先出的完全二叉树。

最大堆:

1
4
6
7
10
33
99

最小堆:

1
4
6
7
99

堆又叫优先级队列,对应std::priority_queue容器。

int main()
{
    std::vector<int> v({ 6, 10, 1, 7, 99, 4, 33 });
    
    // 默认最大堆
    std::priority_queue<int> big_heap(v.begin(), v.end());

    // 最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int>> small_heap(v.begin(), v.end());
    
    // 最大堆
    std::priority_queue<int, std::vector<int>, std::greater<int>> big_heap_1;

    // 99
    std::cout << big_heap.top() << std::endl;
    // 1
    std::cout << small_heap.top() << std::endl;
    
    big_heap.push(1000);
    // 1000
    std::cout << big_heap.top() << std::endl;
    big_heap.pop();
    big_heap.pop();
    big_heap.pop();

    // 10
    std::cout << big_heap.top() << std::endl;

    return 0;
}

要想让top为第k大的数,那么二叉树其余元素为大于堆顶的k-1个数,也就是需要借助一个元素个数为k的最小堆。

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

LeetCode 295 - Find Median from Data Stream - 寻找中位数 - Hard

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

进阶:

  • 如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
  • 如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?

思路

最笨的方法,每次添加后都对数组排序,这样addNum为O(n),findMedian为O(1);若查找中位数时排序,则addNum为O(1),findMedian为O(nlogn)。

因为两种操作都是随机的,则n次操作最理想的复杂度为n方。

思路是,用最大堆和最小堆各存一半数据,保持最大堆top比最小堆top小。

搞清楚,最小堆存储大的那一半数据。


添加元素3种情况。

当两个堆size相同时:

  • 新元素小于两个top,则添加到小的那半数据里(最大堆);
  • 新元素大于两个top,则添加到大的那半数据里(最小堆)。

最大堆比最小堆多一个元素:

  • 新元素大于最大堆top,直接push进较少的最小堆里;
  • 新元素小于最大堆top,则肯定要push进最大堆,但首先要把top加进最小堆。

最大堆比最小堆少一个元素,类比上一种情况即可。

代码

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

    }

    void addNum(int num) {
        if (big_heap.empty())
        {
            big_heap.push(num);
            return;
        }
        int nBigHeap = big_heap.size();
        int nSmallHeap = small_heap.size();

        if (nBigHeap == nSmallHeap)
        {
            if (num < big_heap.top())
            {
                big_heap.push(num);
            }
            else
            {
                small_heap.push(num);
            }

        }
        else if (nBigHeap > nSmallHeap)
        {
            if (num > big_heap.top())
            {
                small_heap.push(num);
            }
            else
            {
                small_heap.push(big_heap.top());
                big_heap.pop();
                big_heap.push(num);
            }
        }
        else if (nBigHeap < nSmallHeap)
        {
            if (num < small_heap.top())
            {
                big_heap.push(num);
            }
            else
            {
                big_heap.push(small_heap.top());
                small_heap.pop();
                small_heap.push(num);
            }
        }

    }

    double findMedian() {
        if (big_heap.size() == small_heap.size())
        {
            return (big_heap.top() + small_heap.top()) / 2.0;
        }
        return (big_heap.size() > small_heap.size())
            ? big_heap.top()
            : small_heap.top();
    }
private:
    // 用最大堆和最小堆各存一半数据
    // 保持最大堆top比最小堆top小
    std::priority_queue<int> big_heap;
    std::priority_queue<int, std::vector<int>, std::greater<int>> small_heap;

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值