1. 程式人生 > >二、佇列,棧,堆(小象)

二、佇列,棧,堆(小象)

225.用佇列實現棧

思考:佇列的特性先進先出,棧後進先出,所以用一個臨時temp_queue來新增新的元素,再把所有元素都匯入到原始的queue之中,那麼刪除就正好是最新插入的元素了。

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    queue<int> obj;
    /** Push element x onto stack. */
    void push(int x) {
        queue<int> temp_queue;
        temp_queue.push(x);
        while(!obj.empty()){
            temp_queue.push(obj.front());
            obj.pop();
            //一開始沒有pop導致超時了,把棧和佇列想的和連結串列一樣
            //以為會覆蓋值,後來想想不一樣,它直接向後繼續添加了。
        }
        while(!temp_queue.empty()){
            obj.push(temp_queue.front());
            temp_queue.pop();
        }
        
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int x = obj.front();
        obj.pop();
        return x;
    }
    
    /** Get the top element. */
    int top() {
        return obj.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return obj.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * bool param_4 = obj.empty();
 */

232.用棧實現佇列

class MyQueue {
public:
    /** Initialize your data structure here. */
    MyQueue() {
        
    }
    stack<int> data;
    stack<int> temp_stack;
    /** Push element x to the back of queue. */
    void push(int x) {
        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();
        }
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        int x = data.top();
        data.pop();
        return x;
    }
    
    /** Get the front element. */
    int peek() {
        return data.top();
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return data.empty();
        
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * bool param_4 = obj.empty();
 */

155.最小棧

思考:如果用一個元素minOfStack來儲存最小值,在新增的時候沒有問題;可是在刪除的時候就會存在恰好是最小的那個元素唄刪除了,那麼此時最小值仍然是被刪除的元素就發生了錯誤。並且如果是用遍歷棧來查詢最小值,那麼時間複雜度就不是O(1)。所以可以構造一個臨時儲存最小值的棧,棧中每一個位正好對應當前情況下棧的最小值。

程式設計的技巧:當一個操作無論在條件發生不發生的時候都會執行,可以放在條件語句之外。

例如:當前新增元素x如果大於min_stack_top,就把x賦值為min_stack_top;如果小於則直接新增;因為無論小於還是大於都要新增一個新的元素到min_stack_top。

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    stack<int> stack_data;
    stack<int> min_stack;
    void push(int x) {
        stack_data.push(x);
        if(min_stack.empty()){
            min_stack.push(x);
        }
        else{
            if(x > min_stack.top()){
               x =  min_stack.top() ;  //關鍵!
            }
            min_stack.push(x);
        }
    }
    
    void pop() {
        stack_data.pop();
        min_stack.pop();
    }
    
    int top() {
        return stack_data.top();
    }
    
    int getMin() {
        return min_stack.top();
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

例題4.合法的出棧序列

思考:使用棧和佇列來模擬入棧和出棧的過程。

用佇列來儲存出棧序列,然後用棧來模擬出棧入棧的過程,因為出戰序列的第一位肯定是棧中的top元素,所以每次只用對比top元素和出戰序列的front,兩者相等就同時彈出,否則就再入棧。

#include<stack>
#include<queue>
bool check_is_valid_order(queue<int> &order)
{
    stack<int> s;
    int num = order.size();
    for(int i =1 ;i <= num;i++){
        s.push(i);
        // while(order.front()==s.top()) 如果棧已經彈空了,佇列也是空了,都是NULL,繼續彈出就會出錯,所以修改下列。
        while(order.front()==s.top()&&!s.empty()){
                s.pop();
                oder.pop();
          }
    }
    if(!s.empty()){
        return false;        
    }
    return true;
}

224.基礎計數器

思考:畫出有窮狀態機就可以解決了。

class Solution {
public:
    void compute(stack<int> &numbers,stack<char> &operation){
        if(numbers.size()<2){
            return ;
        }
        int num2 = numbers.top();
        numbers.pop();
        int num1 = numbers.top();
        numbers.pop();
        if(operation.top() == '+'){
            numbers.push(num1 + num2);
        }
        else if(operation.top() == '-') {
             numbers.push(num1 - num2);
        }
        operation.pop();
    }
    int calculate(string s) {
        static const int BEGIN_STATE = 0;
        static const int NUMBER_STATE = 1;
        static const int OPERATOR_STATE = 2;
        stack<int> numbers_stack;
        stack<char> operation_stack;
        int number = 0;
        int STATE = BEGIN_STATE;
        int compute_flag = 0;
        //遇到數字和+-的時候才有用 才需要推格操作
        for(int i = 0;i<s.size();i++){
            if(s[i] == ' '){
                continue;
            }
            switch(STATE){
                case BEGIN_STATE:
                    if(s[i]>='0' && s[i]<='9'){
                        STATE = NUMBER_STATE;
                    }
                    else{
                        STATE = OPERATOR_STATE;
                    }
                    i--;
                    break;
                case NUMBER_STATE:
                    if(s[i]>='0' && s[i]<='9'){
                        number = number * 10 + s[i] - '0';
                    }
                    //'123'字串在把他變成了數字之後狀態仍然沒有改變。
                    //所以判斷compute_flag
                    else{
                        numbers_stack.push(number);
                        if(compute_flag==1){
                            compute(numbers_stack,operation_stack);
                        }
                        //如果字串第一個'123'此時compute_flag = 0 前面沒有數字不能計算。
                        number = 0;
                        i--;
                        STATE = OPERATOR_STATE;
                    }
                    break;
                case OPERATOR_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(numbers_stack,operation_stack);
                    }
                    break;
            }
        }
        if(number!=0){
              numbers_stack.push(number);
              compute(numbers_stack,operation_stack);
        }
        if(number==0 && numbers_stack.empty()){
            return 0;
        }
        return numbers_stack.top();
    }
};

STL優先順序佇列構造堆

priority_queue<int> heap; //構造預設堆
priority_queue<int,vector<int>,greater<int>> small_heap; //構造預設最小堆
priority_queue<int,vector<int>,less<int>> big_heap; //構造預設最大堆
int test[] = {61,2,4,5,6,7,9,0};
vector<int>  vec(test,test+8); 
	

  
for( unsigned int i = 0;i < vec.size();i++){
		big_heap.push(vec.at(i));
	}
	
for(unsigned int i = 0;i < vec.size();i++){
		cout<<big_heap.top()<<endl;
		big_heap.pop();
	}

遇到的問題和積累:

1error “<”: 有符號/無符號不匹配 
答:”detector 是一個Vector容器 ,detecot.size() 在容器說明中 被定義為: unsigned int 型別, 而j是int 型別,所以會出現: 有符號/無符號不匹配警告,修改int i -->>  unsigned int / size_t

2.error:  error C2065: “cout”: 未宣告的識別符號 error C2065: “endl”: 未宣告的識別符號
答:沒#include<iostream> ,之後再using namespace std

3.vector 兩種訪問 1、陣列 2、 vec.at(i) //這種會檢測訪問越界

4.big_heap[8](61,6,9,2,5,4,7,0)    這個排序和按照二叉樹 而不是陣列的排序 pop()檢查過才知道

 

215.陣列中第K個最大的元素

思考: 第k大 用最小堆來處理,直接輸出堆頂元素,保證最小堆有k個元素,那麼堆頂元素是第k個元素中最小的,就是整個vector中第k大的。這裡我一開始寫的想用兩個for迴圈,第一次是儲存k個元素的堆;第二次是和堆頂元素進行比較。後來看了別人的程式碼,哇,深感自己太不會變通了。直接一次迴圈就可以了!~

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
      priority_queue<int,vector<int>,greater<int>>  small_heap;
        for(int i = 0; i <nums.size();i++){
            if(i<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();
    }
};

295.資料流的中位數

思考:

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {
        
    }
    //如果你用了else if 語句,當你條件滿足時就不會再往下繼續判斷條件了;
    //而if語句會把每一個條件都判斷一下。
    
    //兩個約束:1、最大堆的堆頂小於最小堆的堆頂,這樣就把資料流中間的數字找到了
    //2、兩個堆之間的數目相差不超過1
    void addNum(int num) {
        if(big_heap.empty()){
            big_heap.push(num);
            return ;
        }
        if(small_heap.size()==big_heap.size()){
            //既然要儲存到big_heap,那麼肯定是和big_heap的堆頂比較,只要小於就說明屬於這一半。
            if(num < big_heap.top()){
                big_heap.push(num);
            }
            else {
                small_heap.push(num);
            }

        }
        else if(small_heap.size() > big_heap.size()){
            if(num >= small_heap.top()){
               big_heap.push(small_heap.top());
                  small_heap.pop();//又當做連結串列直接覆蓋了,這裡如果你不把top()彈出來,
                //那麼你這個堆裡面就多了一個元素呀。
                small_heap.push(num);
            }
            else{
                  big_heap.push(num);
            }
        }
        else if(small_heap.size() < big_heap.size()){
           if(num <=big_heap.top()){
               small_heap.push(big_heap.top());
               big_heap.pop();
               big_heap.push(num);
           }
            else{
                small_heap.push(num);
            }
        }
    }
    
    double findMedian() {
        if(small_heap.size()==big_heap.size()){
           return  (small_heap.top() + big_heap.top())/2;
        }
        else if(small_heap.size() > big_heap.size()){
           return  small_heap.top();
        }
        
        return big_heap.top();
    }
    priority_queue<double,vector<double>,greater<double>> small_heap;
    priority_queue<double,vector<double>,less<double> > big_heap;
	//最小堆和最大堆定義反了!!!debug半天!
};

總結:這個程式碼真調了無數次。。。

1、第一個錯誤,當small_heap 和 big_heap 中的數 相等的時候,就只用判斷num和small_heap.top()和big_heap.top()大小,如果大於small_heap.top(),肯定屬於small_heap,直接插入,反之亦然。

2、優先佇列 priority_queue<double,vector<double>,greater<double>> small_heap;定義的時候出錯了,第一個把資料型別弄成了int,導致輸出的中位數出錯了;第二個是排序錯了,把兩個正好弄反了,應該是最大堆,對應的遞減啊!最小堆對應的遞增啊!想下就清楚了!~!~