刷題篇-棧

一、設計一個最小棧

題目要求:設計一個支持push,pop,top等操作並且可以在O(1)O(1)時間內檢索出最小元素的堆棧。

  • push(x)–將元素x插入棧中
  • pop()–移除棧頂元素
  • top()–得到棧頂元素
  • getMin()–得到棧中最小元素

樣例

MinStack minStack = new MinStack();
minStack.push(-1);
minStack.push(3);
minStack.push(-4);
minStack.getMin();   --> Returns -4.
minStack.pop();
minStack.top();      --> Returns 3.
minStack.getMin();   --> Returns -1.

來源:劍指offer
思路:主要是完成最後一個功能getMin()–得到棧中最小元素,其他三個功能正常棧中都有。可以用兩個棧來完成,一個棧是正常的棧,另一個保存前i個數的最小值。例如原棧stack<int> s={-1, 3, -4},則輔助棧stack<int> t={-1, -1, -4}

  • 當插入xx時,s={-1, 3, -4, x}, t = {-1, -1, -4, min(x, -4)}
  • 當移除棧頂元素時將s,t棧頂元素出棧即可
  • 得到棧中最小元素時返回輔助棧的棧頂元素即可

程序(CPP)

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> s, t;
    MinStack() {
        
    }
    
    void push(int x) {
        s.push(x);
        if(!t.empty()) t.push(min(t.top(), x));
        else t.push(x);
    }
    
    void pop() {
        s.pop();
        t.pop();
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return t.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();
 */

二、編輯器

題目要求:你將要實現一個功能強大的整數序列編輯器。在開始時,序列是空的。編輯器共有五種指令,如下:

  • 1、“I x”,在光標處插入數值x。
  • 2、“D”,將光標前面的第一個元素刪除,如果前面沒有元素,則忽略此操作。
  • 3、“L”,將光標向左移動,跳過一個元素,如果左邊沒有元素,則忽略此操作。
  • 4、“R”,將光標向右移動,跳過一個元素,如果右邊沒有元素,則忽略次操作。
  • 5、“Q k”,假設此刻光標之前的序列爲a1,a2,…,an,輸出max1≤i≤kSi,其中Si=a1+a2+…+ai。

輸入格式
第一行包含一個整數Q,表示指令的總數。
接下來Q行,每行一個指令,具體指令格式如題目描述。

輸出格式
每一個“Q k”指令,輸出一個整數作爲結果,每個結果佔一行。

數據範圍
1Q1061≤Q≤10^6
x103,|x|≤10^3,
1kn1≤k≤n

輸入樣例

8
I 2
I -1
I 1
Q 3
L
D
R
Q 2

輸出樣例

2
3

思路:用兩個棧來維護:左邊的棧sl和右邊的棧sr

  • 當插入一個元素的時候相當於左邊的棧sl插入一個元素,同時需要更新前綴和數組s及前綴和數組的前i個數的最小值數組f
  • 當刪除一個元素時相當於刪除左邊的棧sl的棧頂元素
  • 當向左移動時相當於左邊的棧sl的棧頂元素出棧,並將該元素放入右邊的棧sr入棧
  • 當向右移動時時同理
  • 查詢時輸出前綴和數組的前i個數的最小值數組f對應位置的值即可

程序(CPP)

#include <iostream>
#include <cstdio>
#include <map>
#include <vector>
#include <algorithm>
#include <limits.h>
using namespace std;

const int maxn = 1e6+10;
int sl[maxn], sr[maxn], tl, tr;  // 左右兩個棧及其棧頂位置
int s[maxn], f[maxn]; // 前綴和數組和保留前i個數的最小值
void push_l(int x)
{
    sl[++tl] = x;
    s[tl] = s[tl-1]+x;
    f[tl] = max(f[tl-1], s[tl]);
}

int main()
{
    int Q; cin >> Q;
    f[0] = INT_MIN;
    while(Q--)
    {
        char t; cin >> t;
        int x;
        if(t=='I')
        {
            cin >> x;
            push_l(x);
        }
        else if(t=='D')
        {
            if(tl>0) tl--;
        }
        else if(t=='L')
        {
            if(tl>0) sr[++tr] = sl[tl--];
        }
        else if(t=='R')
        {
            if(tr>0) push_l(sr[tr--]);
        }
        else if(t=='Q')
        {
            cin >> x;
            cout << f[x] << endl;
        }
    }
    return 0;
}

三、火車進棧

題目描述
\quad這裏有n列火車將要進站再出站,但是,每列火車只有1節,那就是車頭。
\quad這n列火車按1到n的順序從東方左轉進站,這個車站是南北方向的,它雖然無限長,只可惜是一個死衚衕,而且站臺只有一條股道,火車只能倒着從西方出去,而且每列火車必須進站,先進後出。
\quad也就是說這個火車站其實就相當於一個棧,每次可以讓右側頭火車進棧,或者讓棧頂火車出站。

輸入格式
輸入一個整數n,代表火車數量。

輸出格式
按照《字典序》輸出前20種答案,每行一種,不要空格。

數據範圍
1n201≤n≤20

輸入樣例

3

輸出樣例

123
132
213
231
321

思路1
\quad因爲只需要枚舉20種情況,因此可以枚舉出前n個數的排列組合再依次判斷每種組合是否是合法的出棧順序。判斷一種出棧順序是否合法算法如下:

  • 同時使用一個隊列q和一個堆棧s來解決該問題,其中,q存儲待判斷的出棧序列,而s用於模擬序列中每個元素的入棧和出棧過程。
  • 這裏按照1-n的順序將每個元素壓入棧s:
  • 每次壓入一個元素,檢查棧頂元素與隊列頭元素是否相同,若相同,s與q同時執行pop操作。
  • 若最終棧s爲空,說明q裏存放的序列是合理的出棧順序,否則就是不合理的出棧序列。

程序(CPP)

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;

int n;
vector<int> a;
// 判斷出棧順序是否合法
bool check()
{
    stack<int> s;
    queue<int> q;
    for(int i = 0; i < n; i++) q.push(a[i]);
    for(int i = 1; i <= n; i++)
    {
        s.push(i);
        while(!s.empty() && s.top()==q.front()) s.pop(), q.pop();
    }
    if(s.size()==0) return true;
    else return false;
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
        a.push_back(i);
    int cnt = 0;
    do
    {
        if(check())
        {
            for(int i = 0; i < n; i++) cout << a[i];
            cout << endl;
            cnt++;
            if(cnt==20) break;
        }
    }while(next_permutation(a.begin(), a.end()));
    return 0;
}

四、火車進出棧問題

題目描述
一列火車n節車廂,依次編號爲1,2,3,…,n。
每節車廂有兩種運動方式,進棧與出棧,問n節車廂出棧的可能排列方式有多少種。

輸入格式
輸入一個整數n,代表火車的車廂數。

輸出格式
輸出一個整數s表示n節車廂出棧的可能排列方式數量。

數據範圍
1n600001≤n≤60000

輸入樣例

3

輸出樣例

5

思路:推導數學公式,是Catalan數,需要用到高精度。Catn=C2nnn+1Cat_n=\frac{C_{2n}^n}{n+1}
程序(CPP)

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void multi(vector<long long> &a, int b)
{
    long long t = 0;
    for(int i = 0; i < a.size(); i++)
    {
        a[i] = a[i]*b+t;
        t = a[i] / 1000000000;
        a[i] %= 1000000000;
    }
    while(t)
    {
        a.push_back(t%1000000000);
        t /= 1000000000;
    }
}
void di(vector<long long> &a, int b)
{
    long long t = 0;
    for(int i = a.size()-1; i >= 0; i--)
    {
        a[i] += t*1000000000;
        t = a[i]%b;
        a[i] /= b;
    }
    while(a.size()>1 && a.back()==0) a.pop_back();
}
int main()
{
    int n; cin >> n;
    vector<long long> res;
    res.push_back(1);
    for(int i = 2*n, j=1; j<=n; i--, j++)
    {
        multi(res, i);
        di(res, j);
    }
    di(res, n+1);
    printf("%lld", res.back());
    for(int i = res.size()-2; i >= 0; i--)
    {
        printf("%09lld", res[i]);
    }
    return 0;
}

五、直方圖中最大的矩形

題目描述
\quad直方圖是由在公共基線處對齊的一系列矩形組成的多邊形。矩形具有相等的寬度,但可以具有不同的高度。
\quad例如,圖例左側顯示了由高度爲2,1,4,5,1,3,3的矩形組成的直方圖,矩形的寬度都爲1。
在這裏插入圖片描述
\quad通常,直方圖用於表示離散分佈,例如,文本中字符的頻率。現在,請你計算在公共基線處對齊的直方圖中最大矩形的面積。圖例右圖顯示了所描繪直方圖的最大對齊矩形。

輸入格式
\quad輸入包含幾個測試用例。每個測試用例佔據一行,用以描述一個直方圖,並以整數n開始,表示組成直方圖的矩形數目。然後跟隨n個整數h1hnh_1,…,h_n。這些數字以從左到右的順序表示直方圖的各個矩形的高度。每個矩形的寬度爲1。同行數字用空格隔開。當輸入用例爲n=0時,結束輸入,且該用例不用考慮。

輸出格式
\quad對於每一個測試用例,輸出一個整數,代表指定直方圖中最大矩形的區域面積。
每個數據佔一行。請注意,此矩形必須在公共基線處對齊。

數據範圍
1n1000001≤n≤100000,
0hi10000000000≤h_i≤1000000000

輸入樣例

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

輸出樣例

8
4000

思路1:暴力枚舉,枚舉n2n^2個子矩形,時間複雜度爲O(n2)O(n^2)

#include <iostream>
using namespace std;

const int maxn = 1e5+10;
long long a[maxn];

int main()
{
    int n; 
    while(cin >> n && n!=0)
    {
        for(int i = 1; i <= n; i++) cin >> a[i];
        long long res = 0;
        for(int i = 1; i <= n; i++)
        {
            long long h = a[i];
            for(int j = i; j <= n; j++)
            {
                h = min(h, a[j]);
                res = max(res, (j-i+1)*h);
            }
        }
        cout << res << endl;
    }
    return 0;
}

思路2:單調棧

#include <iostream>
using namespace std;

const int maxn = 1e5+10;
long long a[maxn], s[maxn], w[maxn];

int main()
{
    int n; 
    while(cin >> n && n!=0)
    {
        for(int i = 1; i <= n; i++) cin >> a[i];
        long long res = 0;
        int p = 0;
        a[n+1] = 0;
        for(int i = 1; i <= n+1; i++)
        {
            if(a[i]>s[p]) s[++p]=a[i], w[p]=1;
            else
            {
                int width = 0;
                while(s[p]>a[i])
                {
                    width += w[p];
                    res = max(res, width*s[p]);
                    p--;
                }
                s[++p] = a[i], w[p] = width+1;
            }
        }
        cout << res << endl;
    }
    return 0;
}
發佈了222 篇原創文章 · 獲贊 99 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章