天天看點

【模闆】單調棧——圖文詳解棧的妙用

題目背景

模闆題,無背景。

2019.12.12 更新資料,放寬時限,現在不再卡常了。

題目描述

給出項數為 nn 的整數數列 a_{1 \dots n}a1…n​。

定義函數 f(i)f(i) 代表數列中第 ii 個元素之後第一個大于 a_iai​ 的元素的下标,即 f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}f(i)=mini<j≤n,aj​>ai​​{j}。若不存在,則 f(i)=0f(i)=0。

試求出 f(1\dots n)f(1…n)。

輸入格式

第一行一個正整數 nn。

第二行 nn 個正整數 a_{1\dots n}a1…n​。

輸出格式

一行 nn 個整數 f(1\dots n)f(1…n) 的值。

輸入輸出樣例

輸入 #1複制

5
1 4 2 3 5
           

輸出 #1複制

2 5 4 5 0
           

 概念:單調遞增或單調減的棧,跟單調隊列差不多,但是隻用到它的一端

題意分析:給一個數字n代表輸入n個數;第二行輸入這n個數

結果輸出每個數右邊第一個比它大的數;若無輸出0

1,雙for曆遍

對每一個曆遍元素,運用一個for循環,去查找它右邊第一個大于它的數

2,單調棧(重點講)

以樣本資料為例子:

5

1 4 2 3 5

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
滿足條件2
【模闆】單調棧——圖文詳解棧的妙用
滿足條件1
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
滿足條件1
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
滿足條件2
【模闆】單調棧——圖文詳解棧的妙用
滿足條件1
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
滿足條件2
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
滿足條件2
【模闆】單調棧——圖文詳解棧的妙用

這就是單調棧運作的具體流程圖 在本題中,我們在維護單調棧的同時,也需要記錄其結果的對應下标 于是,我們需要 建立相關的數組來記錄相應的資料

1,資料數組:記錄輸入的n個數  dataArrary[]

2.下标數組:記錄曆遍元素的下标 cursorArray[]

3.結果數組:記錄最終的答案 answerArray[]

另需一個top值,來表示目前棧中的元素數量

條件1:若此時棧為空或曆遍元素小于等于棧頂元素,把其加入棧中(壓棧)

條件2:若此時曆遍元素大于棧頂元素,彈出棧頂元素,循環,直至不滿足條件2或棧為空

轉換為對應的代碼為:

for (int i = 1; i < data.length; i++) {
            //滿足條件二
            while (top != 0 && data[i] > data[cursor[top]]) {
                answer[cursor[top]] = i;
                stack.pop();
                top--;
            }
            //滿足條件一
            stack.push(data[i]);
            top++;
            cursor[top] = i;
        }
           

資料轉換過程:

【模闆】單調棧——圖文詳解棧的妙用

dataArray=[0,1,4,2,3,5]

cursorArray=[0,0,0,0,0,0]

answerArray=[0,0,0,0,0,0]

Top=0

i=1

data[1]=1

滿足條件一

cursor[top]=i

Top++;

Top=1

cursorArray=[0,1,0,0,0,0]

answerArray=[0,0,0,0,0,0]

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用

dataArray=[0,1,4,2,3,5]

Top=1

i=2

data[i]=4

While(滿足條件二)

Answer[cursor[top]]=i

Top--;

Top=0;

cursorArray=[0,1,0,0,0,0]

answerArray=[0,2,0,0,0,0]

【模闆】單調棧——圖文詳解棧的妙用

Top=0

滿足條件一:

cursor[top]=i

Top++

Top=1

cursorArray=[0,2,0,0,0,0]

answerArray=[0,2,0,0,0,0]

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用

dataArray=[0,1,4,2,3,5]

cursorArray=[0,2,0,0,0,0]

answerArray=[0,2,0,0,0,0]

Top=1

i=3

data[i]=2

滿足條件一:

Top++

cursor[top]=i

Top=2

cursorArray=[0,2,3,0,0,0]

answerArray=[0,2,0,0,0,0]

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用

dataArray=[0,1,4,2,3,5]

cursorArray=[0,2,3,0,0,0]

answerArray=[0,2,0,0,0,0]

Top=2

i=4

dataArray[i]=3

While(滿足條件2):

Answer[curosr[top]]=i

Top--;

Top=1;

cursorArray=[0,2,3,0,0,0]

answerArray=[0,2,0,4,0,0]

【模闆】單調棧——圖文詳解棧的妙用

滿足條件1

Top++;

Cursor[top]=i

Top=2

cursorArray=[0,2,4,0,0,0]

answerArray=[0,2,0,4,0,0]

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用

dataArray=[0,1,4,2,3,5]

cursorArray=[0,2,4,0,0,0]

answerArray=[0,2,0,4,0,0]

Top=2

i=5

dataArray[i]=5

While(滿足條件2):

answer[cursor[top]]=i

Top--

Top=0;

cursorArray=[0,2,4,0,0,0]

answerArray=[0,2,5,4,5,0]

【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用
【模闆】單調棧——圖文詳解棧的妙用

  放下源碼:

import java.io.*;
import java.util.Stack;

public class 單調棧 {


    public static void main(String[] args) throws IOException {
        StreamTokenizer r = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
        Stack stack = new Stack();
        r.nextToken();
        int n = (int) r.nval;

        int data[] = new int[n + 1];
        int cursor[] = new int[n + 1];
        int answer[] = new int[n + 1];
        int top = 0;

//輸入資料
        for (int i = 1; i < data.length; i++) {
            r.nextToken();
            data[i] = (int) r.nval;
        }

//維護單調棧
        for (int i = 1; i < data.length; i++) {
            //滿足條件二
            while (top != 0 && data[i] > data[cursor[top]]) {
                answer[cursor[top]] = i;
                stack.pop();
                top--;
            }
            //滿足條件一
            stack.push(data[i]);
            top++;
            cursor[top] = i;
        }


//輸出結果
        for (int i = 1; i < answer.length; i++) {
            if (i != answer.length - 1) {
                pr.print(answer[i] + " ");
                pr.flush();
            } else {
                pr.print(answer[i]);
                pr.flush();
            }
        }
    }
}
           

順便附贈一個C++代碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
int data[maxn], cursor[maxn], answe[maxn], top = 0; //top==0表示空棧 

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &data[i]);
	//從棧底到棧頂的下标對應的數組值(優先級)依次遞減 
	for (int i = 1; i <= n; ++i) {
		//将先進棧(前面的元素下标)且<arr[i]的值的坐标清除掉
		//并記錄data[i]的下标作為前面的答案 
		while (top && data[cursor[top]] < order[i]) answer[cursor[top--]] = i;  
		curso[++top] = i; //存儲arr的數組下标 
	}
	for (int i = 1; i <= n; ++i) {
		if (i == 1) printf("%d", answer[i]);
		else printf(" %d", answer[i]);
	}
    return 0;
}