πŸ”¬ Computer Science/Algorithm

[자료ꡬ쑰] λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack)

dev-ong 2024. 7. 17. 09:03

1. λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack) μ΄λž€?

μŠ€νƒ(Stack)은 ν•œμͺ½ λμ—μ„œλ§Œ 데이터λ₯Ό λ„£κ³  λΊ„ 수 μžˆλŠ” ν›„μž…μ„ μΆœ(LIFO:Last In, First Out) ν˜•νƒœμ˜ 자료ꡬ쑰 μž…λ‹ˆλ‹€.
즉, κ°€μž₯ λ¨Όμ € λ“€μ–΄μ˜¨ 데이터가 κ°€μž₯ λ§ˆμ§€λ§‰μ— λ‚˜κ°€κ³ , λ§ˆμ§€λ§‰μ— λ“€μ–΄μ˜¨ 데이터가 κ°€μž₯ λ¨Όμ € λ‚˜κ°€λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

 

  • λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack)은 νŠΉμ •ν•œ μˆœμ„œλ₯Ό μœ μ§€ν•˜λ©΄μ„œ 데이터λ₯Ό κ΄€λ¦¬ν•˜λŠ” μŠ€νƒ 자료ꡬ쑰 μž…λ‹ˆλ‹€.
    • Monotone은 μ¦κ°€ν•˜κ±°λ‚˜ κ°μ†Œν•˜λŠ” λ“±μ˜ μΌμ •ν•œ κ²½ν–₯이 λ³΄μž„μ„ μ˜λ―Έν•©λ‹ˆλ‹€.
  • λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack)은 μ¦κ°€ν•˜κ±°λ‚˜ κ°μ†Œν•˜λŠ” μΌμ •ν•œ κ²½ν–₯을 κ°€μ§„ μŠ€νƒμž…λ‹ˆλ‹€.
  • λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack)은 νŠΉμ • λ¬Έμ œμ— λŒ€ν•΄ μ‹œκ°„ λ³΅μž‘λ„λ₯Ό O(N)으둜 μ€„μ—¬μ£ΌλŠ” 맀우 효율적인 해결책을 μ œκ³΅ν•©λ‹ˆλ‹€.
    • λ‹€μŒ/이전 λ°μ΄ν„°μ˜ μ΅œλŒ“κ°’/μ΅œμ†Ÿκ°’μ„(Nearest Greater/Smaller Element) μ°ΎλŠ” λ¬Έμ œμ— μœ μš©ν•˜κ²Œ μ“°μž…λ‹ˆλ‹€.

 

2. λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack)의 μ’…λ₯˜ 및 κ΅¬ν˜„

μŠ€νƒμ€ LIFO(Last In, First Out) 자료 ꡬ쑰이기 λ•Œλ¬Έμ— λͺ¨λ…Έν†€ μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ₯Ό μ œκ±°ν•˜λ©΄μ„œ 단쑰 증가/κ°μ†Œ μˆœμ„œλ₯Ό μœ μ§€ν•©λ‹ˆλ‹€. μƒˆλ‘œμš΄ μš”μ†ŒλŠ” 항상 μ΅œμƒμœ„μ— μΆ”κ°€λ©λ‹ˆλ‹€.

1) 단쑰 증가 μŠ€νƒ(Monotone Increasing Stack)

  • μŠ€νƒμ˜ μš”μ†Œλ“€μ΄ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬λ˜λŠ” μŠ€νƒ
    • μ˜€λ¦„μ°¨μˆœμ„ μœ μ§€ν•œλ‹€λŠ” 것은 μŠ€νƒμ˜ μ•„λž˜μͺ½λΆ€ν„° μœ„μͺ½μœΌλ‘œ 올라갈수둝 값이 μ»€μ§„λ‹€λŠ” 의미
    • μŠ€νƒμ˜ μ΅œμƒμœ„μ—μ„œλΆ€ν„° μ‹œμž‘ν•˜μ—¬ μ•„λž˜λ‘œ λ‚΄λ €κ°ˆμˆ˜λ‘ 값이 μž‘μ•„μ§
10 (top)
5
2
1
  • μŠ€νƒμ˜ 맨 μœ„μ— μžˆλŠ” μš”μ†Œκ°€ 항상 κ·Έ μ•„λž˜μ— μžˆλŠ” μš”μ†Œλ³΄λ‹€ ν¬κ±°λ‚˜ 같은 경우λ₯Ό μœ μ§€ν•˜λŠ” μŠ€νƒ
    • μŠ€νƒμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•  λ•Œ, κ·Έ μš”μ†Œκ°€ μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ³΄λ‹€ μž‘κ±°λ‚˜ 같을 λ•ŒκΉŒμ§€ κΈ°μ‘΄ μš”μ†Œλ₯Ό 제거
    • μ΄λ•Œ, 단쑰성을 μœ μ§€ν•΄μ•Όν•©λ‹ˆλ‹€. (μŠ€νƒμ— μžˆλŠ” μš”μ†Œλ“€μ΄ 항상 νŠΉμ • μˆœμ„œ(μ˜€λ¦„μ°¨μˆœ)λ₯Ό μœ μ§€)
    • 쀑볡 κ°’ ν—ˆμš© 여뢀에 따라 값이 같은 κ²½μš°μ— λŒ€ν•œ 제거 μ—¬λΆ€λ₯Ό μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.
Deque<Integer> stack = new ArrayDeque<>();
for (int i=0; i<arr.length; i++) {
    // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œκ°€ ν˜„μž¬ 값보닀 ν¬κ±°λ‚˜ κ°™μœΌλ©΄ pop();
    while (!stack.isEmpty() && stack.peekFirst() >= arr[i]) {
    	stack.removeFirst();
    }
    // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ³΄λ‹€ μž‘μ„λ•ŒκΉŒμ§€ κΈ°μ‘΄ μš”μ†Œλ₯Ό μ œκ±°ν•˜κ³ , ν˜„μž¬ μš”μ†Œ μŠ€νƒμ— μΆ”κ°€
    stack.offerFirst(arr[i]);
}

2) 단쑰 κ°μ†Œ μŠ€νƒ(Monotone Decreasing Stack)

  • μŠ€νƒμ˜ μš”μ†Œλ“€μ΄ λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ μ •λ ¬λ˜λŠ” μŠ€νƒ
    • λ‚΄λ¦Όμ°¨μˆœμ„ μœ μ§€ν•œλ‹€λŠ” 것은 μŠ€νƒμ˜ μ•„λž˜μͺ½λΆ€ν„° μœ„μͺ½μœΌλ‘œ 올라갈수둝 값이 μž‘μ•„μ§„λ‹€λŠ” 의미
    • μŠ€νƒμ˜ μ΅œμƒμœ„μ—μ„œλΆ€ν„° μ‹œμž‘ν•˜μ—¬ μ•„λž˜λ‘œ 갈수둝 값이 컀짐
1 (top)
2
5
10
  • μŠ€νƒμ˜ 맨 μœ„μ— μžˆλŠ” μš”μ†Œκ°€ 항상 κ·Έ μ•„λž˜μ— μžˆλŠ” μš”μ†Œλ³΄λ‹€ μž‘κ±°λ‚˜ 같은 경우λ₯Ό μœ μ§€ν•˜λŠ” μŠ€νƒ
    • μŠ€νƒμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•  λ•Œ, κ·Έ μš”μ†Œκ°€ μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ³΄λ‹€ ν¬κ±°λ‚˜ 같을 λ•ŒκΉŒμ§€ κΈ°μ‘΄ μš”μ†Œλ₯Ό 제거
    • μ΄λ•Œ, 단쑰성을 μœ μ§€ν•΄μ•Όν•©λ‹ˆλ‹€. (μŠ€νƒμ— μžˆλŠ” μš”μ†Œλ“€μ΄ νŠΉμ • μˆœμ„œ(λ‚΄λ¦Όμ°¨μˆœ)λ₯Ό μœ μ§€)
    • 쀑볡 κ°’ ν—ˆμš© 여뢀에 따라 값이 같은 κ²½μš°μ— λŒ€ν•œ 제거 μ—¬λΆ€λ₯Ό μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.
Deque<Integer> stack = new ArrayDeque<>();
for (int i=0; i<arr.length; i++) {
    // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œκ°€ ν˜„μž¬ 값보닀 μž‘κ±°λ‚˜ κ°™μœΌλ©΄ pop();
    while (!stack.isEmpty() && stack.peekFirst() <= arr[i]) {
    	stack.removeFirst();
    }
    // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ³΄λ‹€ 클 λ•ŒκΉŒμ§€ κΈ°μ‘΄ μš”μ†Œλ₯Ό μ œκ±°ν•˜κ³ , ν˜„μž¬ μš”μ†Œ μŠ€νƒμ— μΆ”κ°€
    stack.offerFirst(arr[i]);
}

 

 

3. λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack) κ΅¬ν˜„ - μ˜ˆμ‹œλ‘œ 확인

개인적으둜 λͺ¨λ…Έν†€ μŠ€νƒμ˜ μ’…λ₯˜μ— λŒ€ν•΄ μ΄ν•΄ν•˜λŠ”λ°μ— 쑰금 μ‹œκ°„μ΄ κ±Έλ ΈμŠ΅λ‹ˆλ‹€. 단쑰 증가 μŠ€νƒκ³Ό 단쑰 κ°μ†Œ μŠ€νƒμ˜ μ •λ ¬ μˆœμ„œκ°€ μ–΄λ””λΆ€ν„° μ‹œμž‘ν•˜λŠ”μ§€μ— λŒ€ν•œ μ •ν™•ν•œ 이해λ₯Ό λͺ»ν•˜κ³  λ¬΄μž‘μ • μ΄ν•΄ν•˜λ €κ³  ν•˜λ‹€λ³΄λ‹ˆ ν—·κ°ˆλ ΈμŠ΅λ‹ˆλ‹€. 

κ·Έλž˜μ„œ λ§ˆμ§€λ§‰μœΌλ‘œ λ°°μ—΄ μ˜ˆμ‹œλ₯Ό μ •λ¦¬ν•˜λ©΄μ„œ 아직은 쑰금 ν—·κ°ˆλ¦¬λŠ” 뢀뢄을 μ •λ¦¬ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

public static void main(String[] args) {
    int[][] testArrays = {
        {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
        {10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
        {5, 5, 4, 4, 3, 3, 2, 2, 1, 1},
        {3, 6, 1, 9, 4, 7, 2, 8, 5, 10}
    };

    for (int[] arr : testArrays) {
        System.out.println("Monotonic Stack for array: " + Arrays.toString(arr));
        incresing(arr);
        decresing(arr);
        System.out.println();
    }
}

ν…ŒμŠ€νŠΈ 배열을 μœ„μ™€ 같이 4κ°€μ§€ μ •λ„λ‘œ 생성해두고 단쑰 증가 μŠ€νƒκ³Ό 단쑰 κ°μ†Œ μŠ€νƒμ„ 좜λ ₯ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

*각 μŠ€νƒμ€ 2번 λͺ©μ°¨μ˜ μ½”λ“œλ₯Ό μˆ˜ν–‰ν•΄μ„œ 생성

 

μœ„ μ½”λ“œμ— λŒ€ν•œ 좜λ ₯ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

Monotonic Stack for array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Monotonic Increasing Stack: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 
Monotonic Decreasing Stack: 10, 

Monotonic Stack for array: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Monotonic Increasing Stack: 1, 
Monotonic Decreasing Stack: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 

Monotonic Stack for array: [5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
Monotonic Increasing Stack: 1, 
Monotonic Decreasing Stack: 1, 2, 3, 4, 5, 

Monotonic Stack for array: [3, 6, 1, 9, 4, 7, 2, 8, 5, 10]
Monotonic Increasing Stack: 10, 5, 2, 1, 
Monotonic Decreasing Stack: 10,

 

단쑰 증가 μŠ€νƒμ€ μš”μ†Œλ“€μ΄ μ˜€λ¦„μ°¨μˆœ(μŠ€νƒμ˜ μ•„λž˜λΆ€ν„° μ΅œμƒμœ„κΉŒμ§€)으둜 μ •λ ¬λ˜μ–΄ μžˆλŠ” μŠ€νƒ

단쑰 κ°μ†Œ μŠ€νƒμ€ μš”μ†Œλ“€μ΄ λ‚΄λ¦Όμ°¨μˆœ(μŠ€νƒμ˜ μ•„λž˜λΆ€ν„° μ΅œμƒμœ„κΉŒμ§€)으둜 μ •λ ¬λ˜μ–΄ μžˆλŠ” μŠ€νƒ

인 점을 머리속에 κΌ­ 담아두고 좜λ ₯ κ²°κ³Όλ₯Ό 확인해보면, μ œλŒ€λ‘œ 좜λ ₯λ˜λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

μ½”λ“œ κ΅¬ν˜„μ‹œ 쀑볡 값은 ν—ˆμš©ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— 3번 μ˜ˆμ‹œμ²˜λŸΌ 쀑볡 값이 μ—†λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

λ§Œμ•½ 쀑볡을 ν—ˆμš©ν•œλ‹€λ©΄ μ•„λž˜μ™€ 같은 κ²°κ³Όκ°€ 좜λ ₯λ©λ‹ˆλ‹€.

Monotonic Stack for array: [5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
Monotonic Increasing Stack: 1, 1, 
Monotonic Decreasing Stack: 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,

 

단쑰 증가 μŠ€νƒ ν•¨μˆ˜λŠ” μ•„λž˜μ™€ κ°™μ„λ•Œ λ§ˆμ§€λ§‰ μ˜ˆμ‹œμΈ {3, 6, 1, 9, 4, 7, 2, 8, 5, 10} 에 λŒ€ν•œ 단쑰 증가 μŠ€νƒ 생성 과정을 ꡬ체적으둜 λ³΄κ² μŠ΅λ‹ˆλ‹€.

public static void incresing(int[] arr) {
    Deque<Integer> stack = new ArrayDeque<>();
    for (int i=0; i<arr.length; i++) {
        // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œκ°€ ν˜„μž¬ 값보닀 ν¬κ±°λ‚˜ κ°™μœΌλ©΄ pop();
        while (!stack.isEmpty() && stack.peekFirst() >= arr[i]) {
        	stack.removeFirst();
        }
        // μŠ€νƒμ˜ μ΅œμƒμœ„ μš”μ†Œλ³΄λ‹€ μž‘μ„λ•ŒκΉŒμ§€ κΈ°μ‘΄ μš”μ†Œλ₯Ό μ œκ±°ν•˜κ³ , ν˜„μž¬ μš”μ†Œ μŠ€νƒμ— μΆ”κ°€
        stack.offerFirst(arr[i]);
    }
}

 

  • λ°°μ—΄: {3, 6, 1, 9, 4, 7, 2, 8, 5, 10}
λ°°μ—΄ μš”μ†Œ μ„€λͺ… μŠ€νƒ
첫번째 μš”μ†Œ: 3 μŠ€νƒμ΄ λΉ„μ–΄μžˆμœΌλ―€λ‘œ μš”μ†Œ 3 μΆ”κ°€  [3]
λ‘λ²ˆμ§Έ μš”μ†Œ: 6 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(3)κ°€ 6보닀 μž‘μœΌλ―€λ‘œ 6을 μŠ€νƒμ— μΆ”κ°€ [6, 3]
μ„Έλ²ˆμ§Έ μš”μ†Œ: 1 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(6)κ°€ 1보닀 ν¬λ―€λ‘œ, 6을 μŠ€νƒμ—μ„œ 제거 [3]
μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(3)κ°€ 1보닀 ν¬λ―€λ‘œ, 3을 μŠ€νƒμ—μ„œ 제거 [ ]
μŠ€νƒμ΄ λΉ„μ—ˆμœΌλ―€λ‘œ μš”μ†Œ 1 μΆ”κ°€ [1]
λ„€λ²ˆμ§Έ μš”μ†Œ: 9 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(1)κ°€ 9보닀 μž‘μœΌλ―€λ‘œ, 9λ₯Ό μŠ€νƒμ— μΆ”κ°€ [9, 1]
λ‹€μ„―λ²ˆμ§Έ μš”μ†Œ: 4 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(9)κ°€ 4보닀 ν¬λ―€λ‘œ, 9λ₯Ό μŠ€νƒμ—μ„œ 제거 [1]
μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(1)κ°€ 4보닀 μž‘μœΌλ―€λ‘œ, 4λ₯Ό μŠ€νƒμ— μΆ”κ°€ [4, 1]
μ—¬μ„―λ²ˆμ§Έ μš”μ†Œ: 7 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(4)κ°€ 7보닀 μž‘μœΌλ―€λ‘œ, 7을 μŠ€νƒμ— μΆ”κ°€ [7, 4, 1]
일곱번째 μš”μ†Œ: 2 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(7)κ°€ 2보닀 ν¬λ―€λ‘œ, 7을 μŠ€νƒμ—μ„œ 제거 [4, 1]
μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(4)κ°€ 2보닀 ν¬λ―€λ‘œ, 4λ₯Ό μŠ€νƒμ—μ„œ 제거 [1]
μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(1)κ°€ 2보닀 μž‘μœΌλ―€λ‘œ, 2λ₯Ό μŠ€νƒμ— μΆ”κ°€ [2, 1]
μ—¬λŸλ²ˆμ§Έ μš”μ†Œ: 8 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(2)κ°€ 8보닀 μž‘μœΌλ―€λ‘œ, 8을 μŠ€νƒμ— μΆ”κ°€ [8, 2, 1]
μ•„ν™‰λ²ˆμ§Έ μš”μ†Œ: 5 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(8)κ°€ 5보닀 ν¬λ―€λ‘œ, 8을 μŠ€νƒμ—μ„œ 제거 [2, 1]
μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(2)κ°€ 5보닀 μž‘μœΌλ―€λ‘œ, 5λ₯Ό μŠ€νƒμ— μΆ”κ°€ [5, 2, 1]
μ—΄λ²ˆμ§Έ μš”μ†Œ: 10 μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œ(5)κ°€ 10보닀 μž‘μœΌλ―€λ‘œ, 10을 μŠ€νƒμ— μΆ”κ°€ [10, 5, 2, 1]

 

μ΅œμ’…μ μœΌλ‘œ μŠ€νƒμ„ 좜λ ₯ν•˜λ©΄ [10, 5, 2, 1] 이 좜λ ₯λ©λ‹ˆλ‹€.

μŠ€νƒμ˜ 맨 μœ„ μš”μ†Œκ°€ ν˜„μž¬ λ°°μ—΄ μš”μ†Œλ³΄λ‹€ ν¬κ±°λ‚˜ κ°™μœΌλ©΄ μŠ€νƒμ—μ„œ μ œκ±°ν•˜κ³ , ν˜„μž¬ μš”μ†Œλ₯Ό μŠ€νƒμ— μΆ”κ°€ν•˜λ©΄μ„œ 단쑰성을 μœ μ§€ν•  수 μžˆλŠ” κ²ƒμž…λ‹ˆλ‹€.

 

4. λͺ¨λ…Έν†€ μŠ€νƒ(Monotone stack) κ΄€λ ¨ 문제

 

[λ°±μ€€] 탑 보기 - μžλ°”(Java)

문제 링크https://www.acmicpc.net/problem/22866문제 μ„€λͺ…μΌμ§μ„ μœΌλ‘œ λ‹€μ–‘ν•œ λ†’μ΄μ˜ 건물이 총 Nκ°œκ°€ μ‘΄μž¬ν•œλ‹€. 각 건물 μ˜₯μƒμ—μ„œ μ–‘ μ˜†μ— μ‘΄μž¬ν•˜λŠ” 건물의 μ˜†μ„ λͺ‡ 개 λ³Ό 수 μžˆλŠ”μ§€ κΆκΈˆν•΄μ‘Œλ‹€. i번째 건

coding-vvon.tistory.com

 

 

참고 링크

 

λͺ¨λ…Έν†€ μŠ€νƒ, 큐,덱에 κ΄€ν•˜μ—¬.

μ„œλ‘ . μŠ€νƒ, 큐, 덱 λ¬Έμ œλ“€μ„ ν‘ΈλŠ” λ™μ•ˆ λ§Žμ€ μ•Œκ³ λ¦¬μ¦˜λ“€μ΄ ν•΄λ‹Ή μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜λŠ” 것을 λ³΄μ•˜λ‹€ ν•˜λ‚˜κ°™μ΄ ν•΄λ‹Ή ν˜•μ‹μ„ λ”°λ₯΄κ³  μžˆλŠ” 것을 보고 λ­”κ°€ μ •ν˜•ν™”λœ μ•Œκ³ λ¦¬μ¦˜μ΄ μžˆλ‚˜? 생각이 λ“€μ—ˆλŠ”

velog.io