1. 程式人生 > >leetcode經典題系列------棧和佇列

leetcode經典題系列------棧和佇列

題目一:用佇列實現棧
描述:設計一個棧,支援如下操作,這些操作的演算法複雜度需要是常數級,O(1) , 棧的內部儲存資料的結構為佇列,佇列的方法只能包括push、peek(front)、pop、size、empty等標準的佇列方法
思路:利用兩個佇列實現棧

class MyStack{
public:
    MyStack(){}
    void push(int x){
        std::queue<int>temp_queue;
        temp_queue.push(x);
        while (!_data.empty()) {
            temp_queue.push(_data.front());
            _data.pop();
        }
        
while (!temp_queue.empty()) { _data.push(temp_queue.front()); temp_queue.pop(); } } int pop(){ int x=_data.front(); _data.pop(); return x; } int top(){ return _data.front(); } bool empty(){
return _data.empty(); } private: std::queue<int> _data; };

題目二:使用棧實現佇列
描述:設計一個佇列,佇列支援如下操作,這些操作的演算法複雜度需要是常數級, O(1),佇列的內部儲存資料的結構為棧,棧的方法只能包括push、top、pop、size、 empty等標準的棧方法
思路:利用2個棧,在 佇列呼叫push的時候,利用臨時棧 調換元素次序

class MyQueue{
public:
    MyQueue(){}
    void push(int x){
        std::stack
<int>temp_stack; while (!_data.empty()) { temp_stack.push(_data.top()); _data.pop(); } temp_stack.push(x); while (!temp_stack.empty()) { _data.push(temp_stack.top()); temp_stack.pop(); } } int pop(){ int x=_data.top(); _data.pop(); return x; } int peek(){ return _data.top(); } bool empty(){ return _data.empty(); } private: std::stack<int>_data; };

題目三:包含min函式的棧
描述:設計一個棧,支援如下操作,這些操作的算 法複雜度需要是常數級,O(1)

實現:
1.push(x) : 將元素x壓入棧中
2.pop() : 彈出(移除)棧頂元素
3.top() : 返回棧頂元素
4.getMin() : 返回棧內最小元素


思路:
1.1個變數MIN無法完成記錄棧中所有狀態下的最小值。
2.棧的每個狀態,都需要有一個變數記錄最小值。可以用一個最小值棧,記錄棧每個狀態下的最小值

class MinStack{
public:
    MinStack(){}
    void push(int x){
        _data.push(x);
        if(_min.empty()){
            _min.push(x);
        }
        else{
            if(x>_min.top()){
                x=_min.top();
            }
            _min.push(x);
        }
    }
    void pop(){
        _data.pop();
        _min.pop();
    }
    int top(){
        return _data.top();
    }
    int getMin(){
        return _min.top();
    }
private:
    std::stack<int>_data;
    std::stack<int>_min;
};

題目四:合法的出棧序列
描述:已知從1至n的數字序列,按順序入棧,每個數字入棧後即可出棧, 也可在棧中停留,等待後面的數字入棧出棧後,該數字再出棧,求該數字序列的出棧序列是否合法?
比如 3 2 5 4 1 合法 3 1 2 4 5不合法,因為1不可能在2之前

思路:
使用棧和佇列模擬入站出站操作
步驟:
1 出站結果儲存在佇列order中
2 按元素順序,將元素push進入棧
3 每push一次元素,即檢查是否和佇列首部元素相同,若相同則彈出佇列首元素,彈出戰頂元素,直到兩元素不同
4 若最終棧為空,說明序列合法,反之不合法

bool check_is_valid_order(std::queue<int>&order)//檢查序列儲存在佇列
{
    std::stack<int>S;//模擬戰
    int n=(int)order.size();//n為序列長度,將1-n按順序入站
    for (int i=1; i<=n; i++) {
        S.push(i); //將i入站
        while (!S.empty()&&S.top()==order.front()) {
            S.pop();
            order.pop();//只要S不空且佇列頭部與戰頂相同,即彈出元素
        }
    }
    if(S.empty()!=false){//如果最終棧不空,則說明序列不合法
        return false;
    }
    return true;
}

題目五 簡單計算器


利用狀態機的概念,在操作符狀態或數字狀態下進行相應的處理
測試:
std::string s = "1+(2-3)";
Solution solve;
printf("%d\n", solve.calculate(s));

 

//字串轉數字
void changeStrToNum(std::string s){
    int number=0;
    for(int i=0;i<s.length();i++){
        if (s[i] >= '0' && s[i] <= '9'){
            number = number * 10 + s[i] - '0';
        }
    }
    printf("%d\n",number);
}
class Solution {
public:
    int calculate(std::string s) {
        //三個狀態
        static const int STATE_BEGIN=0;
        static const int NUMBER_STATE=1;
        static const int OPERATION_STATE=2;
        std::stack<int>number_stack;
        std::stack<char>operation_stack;
        int number=0;//當前的數字
        int STATE=STATE_BEGIN;//當前狀態
        int compute_flag=0;//1 為準備計算 0 為 先不能計算
        for(int i=0;i<s.length();i++){//遍歷字串
            if(s[i]==' ') continue;
            switch (STATE) {
                case STATE_BEGIN:
                    if(s[i]>='0'&&s[i]<='9'){//是數字
                        STATE=NUMBER_STATE;//當前應該轉換為數字狀態
                    }
                    else{
                        STATE=OPERATION_STATE;
                    }
                    i--;//這次只是判斷,所以應該退格,因為for迴圈中i會遞增
                    break;
                    
                case NUMBER_STATE:
                    if(s[i]>='0'&&s[i]<='9'){//是數字
                        number=number*10+s[i]-'0';//字元轉數字
                    }
                    else{//不是數字,說明到操作符了
                        number_stack.push(number);
                        if(compute_flag==1){
                            //可以計算了
                            compute(number_stack, operation_stack);
                        }
                        number=0;//本次數字儲存完畢,清零
                        i--;//只是判斷下下次應該的狀態,所以i退格
                        STATE=OPERATION_STATE;
                    }
                    break;
                case OPERATION_STATE:
                    if(s[i]=='+'||s[i]=='-'){
                        operation_stack.push(s[i]);
                        compute_flag=1;//先標記為可以計算,如果遇到(,在標示為不可計算
                    }
                    else if(s[i]=='('){//左括號,將要為數字模式,並且先不能計算
                        STATE=NUMBER_STATE;
                        compute_flag=0;
                    }
                    else  if(s[i]>='0'&&s[i]<='9'){
                        STATE=NUMBER_STATE;
                        i--;//下次為數字模式,退格
                    }
                    else if(s[i]==')'){//右括號,開始計算
                        compute(number_stack, operation_stack);
                    }
                    break;
            }
        }
        if(number!=0){
            number_stack.push(number);
            compute(number_stack, operation_stack);
        }
        if(number==0&&number_stack.empty()){
            
            int ii=0;
            return 0;
        }
        return number_stack.top();
    }
private:
    void compute(std::stack<int> &number_stack,
                 std::stack<char> &operation_stack){
        if (number_stack.size() < 2){
            return;
        }
        int num2 = number_stack.top();
        number_stack.pop();
        int num1 = number_stack.top();
        number_stack.pop();
        if (operation_stack.top() == '+'){
            number_stack.push(num1 + num2);
        }
        else if(operation_stack.top() == '-'){
            number_stack.push(num1 - num2);
        }
        operation_stack.pop();
    }
};

題目六 :求陣列中第K大的數

描述:
已知一個未排序的陣列,求這個陣列中第K大的數字。 如,array = [3,2,1,5,6,4] , k = 2,return 5
思路:常規我們可以對陣列進行排序,然後找出第K大的數,時間複雜度為O(nlogn),有沒有更優化的方法,有的,利用二叉堆,時間負責度位nlogk
維護一個K大小的最小堆,堆中元素個數小於K時,新元素直接進入堆;否則,當 堆頂小於新元素時,彈出堆頂,將新元素加入堆。
解釋: 由於堆是最小堆,堆頂是堆中最小元素,新元素都會保證比堆頂小(否則新元素
替換堆頂),故堆中K個元素是已掃描的元素裡最大的K個;堆頂即為第K大的數。

int findKthLargest(std::vector<int>&nums,int k)
{
    //構建最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int> > Q;
    for (int i=0; i<nums.size(); i++) {
        if(Q.size()<k){
            Q.push(nums[i]);
        }
        else if(nums[i]>Q.top()){
            Q.pop();
            Q.push(nums[i]);
        }
    }
    return Q.top();
}

題目七 尋找中位數

描述:
設計一個數據結構,該資料結構動態維護一組資料,且支援如下操作: 1.新增元素: void addNum(int num),將整型num新增至資料結構中。 2.返回資料的中位數: double findMedian(),返回其維護的資料的中位數。
中位數定義:
1.若資料個數為奇數,中位數是該組數排序後中間的數。[1,2,3] -> 2 2.若資料個數為偶數,中位數是該組數排序後中間的兩個數字的平均值。[1,2,3,4] -> 2.5

分析:
最直觀的方法:
儲存結構使用陣列,每次新增元素或查詢中位數時對陣列排序,
再計算結果。
時間複雜度:
1.比如 若新增元素時排序,addNum複雜度O(n)[插入排序,最好為O(n),最差為n的平方],findMedian複雜度O(1)
2.若查詢中位數時排序,addNum複雜度O(1),findMedian複雜度 O(nlogn)
若新增元素或查詢中位數是隨機的操作,共n次操作,按上述思想,整體復 雜度最佳為O(n^2)
所以應該尋找最優的方法:
思路:利用堆的性質
動態維護一個最大堆與一個最小堆,最大堆儲存一半資料,最小堆儲存 一般資料,維持最大堆的堆頂比最小堆的堆頂小,並且2個差一個元素
比如 如果最後 最大堆和最小堆個數一樣,那麼中位數為兩個堆頂的平方,如果其中一個比另一個多一個元素,那麼中位數就是多一個元素的堆的堆頂
新增元素的時候對堆進行調整有3種情況:
這裡 最大堆簡寫為 A,最小堆簡寫為 B
情況一:A和B一樣多
a 新元素大於最大堆,新元素插入B
b 新元素小於最大堆,新元素插入A
情況二:A比B多1
a 新元素大於A,新元素插入B
b 新元素小於A,A堆頂插入B,A堆頂移除,然後A插入新元素
情況三:A比B少1
a 新元素小於B,新元素插入A
b 新元素大於B,B堆頂插入A,B堆頂移除,然後B插入新元素

class MedianFinder{
public:
    MedianFinder(){}
    void addNum(int num){
        if(big_queue.empty()){
            big_queue.push(num);
            return;
        }
        if (big_queue.size() == small_queue.size()){
            if (num < big_queue.top()){
                big_queue.push(num);
            }
            else{
                small_queue.push(num);
            }
        }
        else if(big_queue.size() > small_queue.size()){
            if (num > big_queue.top()){
                small_queue.push(num);
            }
            else{
                small_queue.push(big_queue.top());
                big_queue.pop();
                big_queue.push(num);
            }
        }
        else if(big_queue.size() < small_queue.size()){
            if (num < small_queue.top()){
                big_queue.push(num);
            }
            else{
                big_queue.push(small_queue.top());
                small_queue.pop();
                small_queue.push(num);
            }
        }
    }
    double findMedian(){
        
        if (big_queue.size() == small_queue.size()){
            return (big_queue.top() + small_queue.top()) / 2;
        }
        else if (big_queue.size() > small_queue.size()){
            return big_queue.top();
        }
        return small_queue.top();
    }
private:
    std::priority_queue<double> big_queue;
    std::priority_queue<double, std::vector<double>,
    std::greater<double> > small_queue;
};