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;

};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章