※ 이번 문제에서 알게된 점

§ insert() 함수

dq.insert함수는 insert(pos, val);로 값이 들어가야 해서 dq.insert(dq.end()+i, dq.begin()+i);처럼 사용하지 말아야 한다.

위와 같은 의미로 다시 코드를 짜고 싶다면 다음과 같이 dq.insert(dq.end()+i, dq[i]); 와 같이 사용해야 한다.

dq.insert(dq.end()+i+1, dq[i]); // 정상적인 작동
dq.insert(dq.end()+i+1, dq.begin(i));  // dq.begin(i) 부분에 오류발생
dq.insert(dq.end()+i+1, dq.begin()+i); // dq.insert() 부분에 오류발생

 

 

 

§ front 와 begin 의 차이

가장 고생했던 부분이 있는데 다음과 같다.

 

<정답코드>

for (int i = 0; i < k-1; i++) {
    dq.push_back(dq.front());
    dq.pop_front();
}
cout << dq.front() << ", ";
dq.erase(dq.begin());

// 7 3
// <3, 6, 2, 7, 5, 1, 4>

 

<오답코드>

for (int i = 0; i < k; i++) {
    dq.insert(dq.end()+i+1, dq[i]);
    dq.erase(dq.begin()+i);
}
dq.pop_back();

// 7 3
// <2, 4, 7, 7, 7, 7, 7>

 

front()는 첫 "요소"를 반환하는 것이다.

begin()은 그 요소의 참조 즉, "주소값"을 반환한다.

즉, begin은 컬렉션을 통해 반복하는 데 사용할 수 있는 해당 요소의 iterators를 반환하지만

      front는 컬렉션의 첫 번째 요소에 대한 참조만 반환 (포인터 개념)합니다.

 

따라서 push_back(값); 메소드를 활용하기 위해서는 front를 사용하는 것이 좋다.

dq.push_back(dq.front());

 

 

 

 

 

 

§ my solution 

추출,삽입이 빠른 덱으로  k - 1번씩 밀고, 맨 앞에있는 값을 제거하는 알고리즘을, 모든 값이 없어질 때 까지 반복

#include <iostream>
#include <deque>
using namespace std;

deque<int> dq;
int main(){
    int n, k;   cin >> n >> k;
    cout << '<';


    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1) {
        for (int i = 0; i < k-1; i++) {
            dq.push_back(dq.front());
            dq.pop_front();
        }
        cout << dq.front() << ", ";
        dq.erase(dq.begin());
    }
    cout << dq[0];
    cout << '>';
}

 

 

§ 시행착오 

#include <iostream>
#include <deque>
using namespace std;

deque<int> dq;
int main(){
    int n, k;   cin >> n >> k;
    cout << '<';


    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1) {
            if (dq.size() > 2) {
                cout << dq[k-1] << ", ";
                for (int i = 0; i < k; i++) {
                    dq.insert(dq.end()+i+1, dq[i]);
                    dq.erase(dq.begin()+i);
                }
                dq.pop_back();
            }
            else if(dq.size() <= 2){
                cout << dq[0] << ", "<< dq[1];
                break;
            }
    }
    cout << dq[0];
    cout << '>';
}

왜 이따구로 풀었을까?

 

- 일단 먼저 다음과 같이 접근하였다.

1 2 3 4
                    7  6  5

             front                  back <-- 삽입
                    1 2 3 4 5 6 7
                    (1 2 3) 4 5 6 7 [1 2 3]
                    4 5 6 7 1 2
                    (4 5 6) 7 1 2 [4 5 6]
                    7 1 2 4 5
                    4 5 7 1
                    1 4 5
                    4 5 1
>> 그대로 출력       5 1 4

왜 안될까?  => k값이 3이라 저렇게 출력되는 것처럼 보일 뿐, 다음과 같은 테스트 케이스에서는 오답으로 나온다.

10 1
<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>

5 5
<5, 1, 3, 4, 2>

10 4
<4, 8, 2, 7, 3, 10, 9, 1, 6, 5>

따라서 예제의 특수성에 빠지지 않도록 조심할 것!

https://www.acmicpc.net/problem/10828

 

 

 

 

 

 

 

 

※ 이번 문제에서 알게된 점

if (!s.compare("문자열"))

§ a.compare(b) 함수

C++에서 string 헤더에 제공되는 함수.

a와 b의 문자열이 서로 같다면 return 0
a와 b의 문자열이 서로 다르면 return -1

 

§ if (!s.compare("문자열")) 

문자열이 같으면 0을 반환하므로 if(0)은 if (false)와 동일하게 취급된다.
따라서 if (!s.compare("문자열")은 if (!0) 즉, if (!false) == if(true)이다.

 

 

 

 

§ my solution 

#include <iostream>
#include <string>
#include <stack>
using namespace std;

int main(){
    int n;  cin >> n;
    stack<int> intStack;
    for (int i = 0; i < n; i++) {
        string s;   cin >> s;
        if (!s.compare("push")){
            int push_num;   cin >> push_num;
            intStack.push(push_num);
        }

        else if (!s.compare("pop")){
            if (intStack.empty())
                cout << "-1" << endl;
            else{
                cout << intStack.top() << endl;
                intStack.pop();
            }
        }

        else if (!s.compare("size")){
            cout << intStack.size() << endl;
        }

        else if (!s.compare("empty")){
            if (intStack.empty())
                cout << "1" << endl;
            else
                cout << "0" << endl;
        }

        else if (!s.compare("top")){
            if (intStack.empty())
                cout << "-1" << endl;
            else
                cout << intStack.top() << endl;

        }
    }
}

 

※ Recursion (순환 / 재귀)

흔히들 재귀라 부르는 recursion은 함수가 자기자신을 호출하여 문제를 해결하는 프로그래밍 기법이다.

보통 우리는 이렇게 반복을 요하는 문제에서는 반복문(for, while)을 사용하는 경우가 많다. 또한, 이런 반복문의 효율성은 무시하지 못할 것이다. 일반적으로 재귀보다는 반복문이 더욱 효율적이고 값 도출시간도 빠르다.

 

출처:&nbsp;https://www.scaler.com/topics/python/recursion-in-python/

 

 

§  최대공약수

auto gcd(int A, int B){
    if (B == 0)
        return A;
    else
        return gcd(B, A%B);
}

https://chan4im.tistory.com/58

 

[Baekjoon/백준] 13241번: [Recursion] (C/C++) ★★☆

※ 이번 문제에서 알게된 점 § 재귀함수로 구현하는 gcd, lcm 함수 // 최대공약수 auto gcd(long long A, long long B){ if (B == 0) return A; else return gcd(B, A%B); } // 최소공배수 auto lcm (long long A, long long B){ return A*B

chan4im.tistory.com

https://www.acmicpc.net/problem/13241

 

13241번: 최소공배수

정수 B에 0보다 큰 정수인 N을 곱해 정수 A를 만들 수 있다면, A는 B의 배수이다. 예: 10은 5의 배수이다 (5*2 = 10) 10은 10의 배수이다(10*1 = 10) 6은 1의 배수이다(1*6 = 6) 20은 1, 2, 4,5,10,20의 배수이다. 다

www.acmicpc.net

 

 

 

 

 

 

 

 

 

※ 재귀를 사용하는 이유

이렇게 반복문보다 효율이 떨어지는 재귀를 도대체 우리는 사용하는 이유와 배워야 하는 이유가 무엇일까?

바로 재귀만이 갖는 자기자신의 호출로 특정 문제나 알고리즘에서 매우 강력(powerful)한 경우가 있다.

 

재귀로 짜면 간단하게 2~3줄로 나올 코드가 반복문으로 몇십줄이 넘어갈 수 도 있으니 말이다.

 

대표적인 예를 들어보자면 다음과 같다.

- 거듭제곱, 팩토리얼의 계산

백준 10872번: https://www.acmicpc.net/problem/10872

 

10872번: 팩토리얼

0보다 크거나 같은 정수 N이 주어진다. 이때, N!을 출력하는 프로그램을 작성하시오.

www.acmicpc.net

- 피보나치 수열의 계산

백준 2747번: https://www.acmicpc.net/problem/2747

 

2747번: 피보나치 수

피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다. 이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n ≥ 2)가

www.acmicpc.net

- 하노이탑 문제

백준 1914번: https://www.acmicpc.net/problem/1914

 

1914번: 하노이 탑

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로

www.acmicpc.net

 

 

#include <iostream>
using namespace std;

int fac(int num){
    if (num > 1)
        return num * fac(num-1);  // num: 해결된 부분  // fac(num-1): 남은부분
    else
        return 1;
}

int main() {
    int num;
    cin >> num;
    cout << fac(num);
}

이때, 재귀를 멈추는 부분 

if (num > 1)
    return num * fac(num-1);
else
    return 1;

위와 같은 재귀를 멈추는 부분이 없다면 시스템 stack을 모두 사용한 이후 Err가 발생하며 멈출 것이다. (stackoverflow)

 

 

이렇게 반복에 비해 알고리즘을 훨씬 명확하고 간결하게 나타낼 수 있다는 장점이 있으나 아래와 같이 수행속도면에서는 현저히 떨어지는 것을 볼 수 있다.

좌) for 반복문&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;우) 재귀함수

 

 

 

 

 

 

 

※ Recursion의 시간복잡도

재귀는 작은 동일한 문제들로 분해하여 해결하는 방법인 분할정복 알고리즘의 일종이다.

5! -> 4! -> 3! -> 2! -> 1! 으로 작은 문제 즉, 본래의 problem을 subproblem으로 나누어 최소 size부터 해결하는 방식이다.

최소 subproblem부터 다시 본래의 problem size로 돌아가면서 정복하는, 이런 방식을 분할정복 알고리즘이라 부른다.

 

그렇다면 이런 재귀함수의 시간복잡도 즉, 성능은 어떻게 될까?

이를 위한 예시는 피보나치 수열문제로 한다.

int fib(int num){
    if (num <= 1)
        return num; 
    else
        return fib(num-1) + fib(num-2);
}

위의 문제에서 피보나치식의 n번째 계산을 위한 연산횟수를 T(n)이라 하자.

T(0) = 1, T(1) = 1이므로 

T(n) = T(n-1) + T(n-2) + 1 > 2 x T(n-2) > 2x2 x T(n-4) > . . . 2 ^ (n/2) x T(0)이므로 

 

 

 

int fib(int num){
    int arr[100] = { 0, 1, };
    if (num < 2 || arr[num] != 0)
        return arr[num];
    else{
        arr[num] = fib(num-1) + fib(num-2);
        return arr[num];
    }
}

그렇다면 다음 예제는 어떨까?

이 기법은 이미 구한 값을 메모리에 저장해두고 다시 사용하는 메모이제이션 (memoization)을 채택한 방법이다.

위 방법의 시간복잡도를 구해보면

T(0) = 1, T(1) = 1이고

T(2) = T(1) + T(0) + 1 = 3

T(3) = T(2) + T(1) + 1 = 5

T(4) = T(3) + T(2) + 1에서 T(2)는 저장한 피보나치 수를 가져오기만 하면 되기 때문에 T(2) = 1이다.

T(3) + T(2) + 1 = 7이고, 같은 이유로 T(5) = T(4) + T(3) + 1에서 T(3) = 1이므로 T(5) = 9이다.

따라서 T(n) = 2n - 1이라고 할 수 이므로

 

 

 

※ 다중 순환 (multiple recursion)

재귀함수는 아래와 같이  3종류로 나눌 수 있다.

- 선형 순환 (linear recursion)

  ex) 팩토리얼계산, 거듭제곱 계산

- 이진 순환 (binary recursion)

  ex) 피보나치수열, 하노이탑 문제

- 다중 순환 (multiple recursion)

 

 

 

 

 

 

※ Queue

큐는 FIFO(First-In-First-Out) 입출력 형태, 즉 선입선출 방식을 가진 자료구조이다.

출처)https://velog.io/@awesomeo184/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%ED%81%90Queue

 

enqueue와 dequeue의 시간복잡도는 둘 다 O(1)의 시간을 갖는다.

단, 탐색의 시간복잡도는 특정 data를 찾을 때까지 돌아야 하므로 O(n)의 시간을 갖는다.

 

 

 

※ queue Container (STL) ★

queue 컨테이너는 deque 클래스의 인터페이스를 제한해 전형적인 큐 메모리 구조의 인터페이스를 제공하는 형태이다.

출처)&nbsp;http://www.tcpschool.com/cpp/cpp_container_adapter

 

 

※ queue 선언방식

#include <queue>

queue<데이터타입> 이름;

 

 

 

[queue 관련 백준문제]

백준 10854번: https://www.acmicpc.net/problem/10854

 

10854번: Divisions

David is a young boy and he loves numbers. Recently he learned how to divide two numbers. David divides the whole day. He is happy if the result of the division is an integer, but he is not very amused if this is not the case. After quite a while he decide

www.acmicpc.net

백준 11866번: https://www.acmicpc.net/problem/11866

 

11866번: 요세푸스 문제 0

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 1,000)

www.acmicpc.net

백준 1158번: https://www.acmicpc.net/problem/1158

 

1158번: 요세푸스 문제

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)

www.acmicpc.net

백준 2164번: https://www.acmicpc.net/problem/2164

 

2164번: 카드2

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다. 이제 다음과 같은 동작을 카드가

www.acmicpc.net

 

백준 12789번(stack+queue): https://www.acmicpc.net/problem/12789

 

12789번: 도키도키 간식드리미

인하대학교 학생회에서는 중간, 기말고사 때마다 시험 공부에 지친 학우들을 위해 간식을 나눠주는 간식 드리미 행사를 실시한다. 승환이는 시험 기간이 될 때마다 간식을 받을 생각에 두근두

www.acmicpc.net

 

 

 

 

 

 

※ deque Container (STL) ★

deque 컨테이너는 double-ended queue를 의미하며 양쪽에 끝이 있는 큐이다.

이 컨테이너는 양 끝에서 빠르게 요소를 삽입, 삭제할 수 있다는 점에서 매우 powerful하다!

또한, 중간에서 원소를 추가, 삭제하는 경우(insert, erase), vector보다 효율이 좀 더 좋다.

(vector는 앞에서 원소를 추가하는 것이 불가능 하기 때문에 모든 원소를 뒤쪽으로 밀어야 하기 때문)

 

 

※ deque 선언방식

#include <deque>

deque<데이터타입> 이름;

 

[deque 관련 백준문제]

백준 10866번: https://www.acmicpc.net/problem/10866

 

10866번: 덱

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지

www.acmicpc.net

백준 1021번: https://www.acmicpc.net/problem/1021

 

1021번: 회전하는 큐

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가

www.acmicpc.net

백준 2161번:

https://www.acmicpc.net/problem/2161

 

2161번: 카드1

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다. 이제 다음과 같은 동작을 카드가

www.acmicpc.net

백준 2346번: https://www.acmicpc.net/problem/2346

 

2346번: 풍선 터뜨리기

1번부터 N번까지 N개의 풍선이 원형으로 놓여 있고. i번 풍선의 오른쪽에는 i+1번 풍선이 있고, 왼쪽에는 i-1번 풍선이 있다. 단, 1번 풍선의 왼쪽에 N번 풍선이 있고, N번 풍선의 오른쪽에 1번 풍선

www.acmicpc.net

백준 20301번: https://www.acmicpc.net/problem/20301

 

20301번: 반전 요세푸스

첫째 줄에 정수 $N$, $K$, $M$이 주어진다. ($1 \leq N \leq 5\ 000$, $1 \leq K, M \leq N$)

www.acmicpc.net

 

 

 

 

※ Queue 자료구조의 구현

위에서 STL 즉, 이미 구현되어 있는 자료구조를 사용한 것을 보았다. 우리는 그 자료구조가 어떻게 구현되어 있는지 뜯어볼 필요가 있다.

 

(pseudo code) 의사코드로 먼저 표현

 

 

 

※ Deque자료구조의 구현

위에서 STL 즉, 이미 구현되어 있는 자료구조를 사용한 것을 보았다. 우리는 그 자료구조가 어떻게 구현되어 있는지 뜯어볼 필요가 있다.

 

(pseudo code) 의사코드로 먼저 표현

 

 

 

 

 

§ 예제. 

 

 

 

 

 

 

 

 

※ Stack

스택은 LIFO(Last-In-First-Out) 입출력 형태, 즉 후입선출 방식을 가진 자료구조이다.

https://images.app.goo.gl/aei6cA:jvUhK2WN86

위의 그림에서 4, 5번째 그림을 보자.    Last In: 3  /  First Out: 3

즉, 가장 마지막에 들어온 data가 가장 먼저 나가는 것을 알 수 있다.

 

push와 pop의 시간복잡도는 둘 다 O(1)의 시간을 갖는다.

단, 탐색의 시간복잡도는 특정 data를 찾을 때까지 돌아야 하므로 O(n)의 시간을 갖는다.

 

그렇다면 Stack을 왜 사용하는 것일까?

 

※ Stack의 사용이유

undo 즉, Ctrl+z라는 키워드처럼 최근에 실행한 명령어를 취소해야 하기 때문이라는 예를 들 수 있다.

 

조금 더 Computer Science적으로 접근해보면 OS에서 process가 바라보는 memory 영역은 크게 4가지로 나뉜다.

- Code: process가 실행할 코드와 매크로 상수가 기계어로 저장된 공간

- Data: 전역변수, static변수 등의 변수들이 저장된 공간

- Heap: malloc, calloc, realloc, new 와 같은 동적할당을 위한 메모리 공간으로 사용한 후 반드시 해제해야한다!

- Stack: heap에 비해 할당할 수 있는 메모리공간이 적지만 포인터로 접근해야하는 heap에 비해 data처리속도가 빠르다.

 

여기서 Stack Segment의 data를 기록하고 종료하는 메커니즘이 후입선출방식(LIFO)으로 이루어진다.

 

 

 

 

 

 

※ Stack Container (STL) ★

stack 컨테이너는 vector 클래스의 interface를 제한해 전형적인 stack메모리 구조의 interface를 제공한다.

stack 컨테이너는 stack 헤더파일에 정의되어 있다. 

http://www.tcpschool.com/cpp/cpp_container_adapter

stack 컨테이너는 stack 메모리구조 표현을 위해 다음과 같은 멤버 함수를 제공한다.

http://www.tcpschool.com/cpp/cpp_container_adapter

 

 

※ Stack 선언방식

#include <stack>

stack<데이터타입> 이름;

#include <iostream>
#include <stack>
using namespace std;

int main() {
    stack<int> intStack1;
    stack<int> intStack2;

    intStack1.push(1);
    intStack1.push(2);
    intStack1.push(3);

    intStack2.push(10);
    intStack2.push(20);
    intStack2.push(30);

    swap(intStack1, intStack2);

    while (!intStack1.empty()) {
        cout << intStack1.top() << endl;
        intStack1.pop();
    }
}

/*
 * 출력결과
 * 30
 * 20
 * 10
 */

출처: https://life-with-coding.tistory.com/406 [코딩젤리:티스토리]

 

 

 

[Stack 관련 백준문제]

백준 10828번: https://www.acmicpc.net/problem/10828

 

10828번: 스택

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지

www.acmicpc.net

백준 1874번: https://www.acmicpc.net/problem/1874

 

1874번: 스택 수열

1부터 n까지에 수에 대해 차례로 [push, push, push, push, pop, pop, push, push, pop, push, push, pop, pop, pop, pop, pop] 연산을 수행하면 수열 [4, 3, 6, 8, 7, 5, 2, 1]을 얻을 수 있다.

www.acmicpc.net

백준 3986번: https://www.acmicpc.net/problem/3986

 

3986번: 좋은 단어

이번 계절학기에 심리학 개론을 수강 중인 평석이는 오늘 자정까지 보고서를 제출해야 한다. 보고서 작성이 너무 지루했던 평석이는 노트북에 엎드려서 꾸벅꾸벅 졸다가 제출 마감 1시간 전에

www.acmicpc.net

백준 1725번: https://www.acmicpc.net/problem/1725

 

1725번: 히스토그램

첫 행에는 N (1 ≤ N ≤ 100,000) 이 주어진다. N은 히스토그램의 가로 칸의 수이다. 다음 N 행에 걸쳐 각 칸의 높이가 왼쪽에서부터 차례대로 주어진다. 각 칸의 높이는 1,000,000,000보다 작거나 같은

www.acmicpc.net

 

 

 

 

 

 

 

 

 

 

※ Stack 자료구조의 구현

위에서 STL 즉, 이미 구현되어 있는 자료구조를 사용한 것을 보았다. 우리는 그 자료구조가 어떻게 구현되어 있는지 뜯어볼 필요가 있다.

 

(pseudo code) 의사코드로 먼저 표현

 

 

§ 배열로 구현

#include <iostream>
using namespace std;

inline void Err (char* ErrMessage){
    printf("%s\n", ErrMessage);
    exit(1);  // 실패상태코드로 프로그램 강제 종료
}

const int Max_Stack_Size = 10;

class stack{
private:
    int top;
    int arr[Max_Stack_Size];
public:
    bool isEmpty();
    bool isFull();
    int push(int data);
    void pop();
    void show();
    void Quit();

    stack() { top = -1; }
    ~stack(){ }
};

bool stack::isEmpty() {
    return top == -1;
}
bool stack::isFull() {
    return top == Max_Stack_Size - 1;
}

int stack::push(int data) {
    if (isFull())
        Err("Stack is Full");
    arr[++top] = data;
}
void stack::pop(){
    if (isEmpty())
        Err("Stack is Empty");
    arr[--top];
}

void stack::show(){
    if (isEmpty())
        cout << "Empty Stack" << endl;
    if (isFull())
        cout << "Full Stack" << endl;
    for (int i = 0; i <= top; i++) {
        cout << arr[i] << ' ';
    }
    cout << endl;
}

void stack::Quit(){
    exit(0);
}

int main() {
    stack stack;
    int arr[Max_Stack_Size], i=0;
    int data;
    int num;  // 1. isEmpty  /  2. isFull / 3. push / 4. pop / 5. show / 6. Quit
    while (true){
        cout << "1. push / 2. pop / 3. isEmpty  / 4. isFull / 5. show / 6. Quit"<< endl;
        cin >> num;
        if (num == 1) {
            cout << "push할 숫자를 입력하세요" << endl;
            cin >> data;
            stack.push(data);
        }
        if (num == 2)
            stack.pop();
        if (num == 3)
            stack.isEmpty();
        if (num == 4)
            stack.isFull();
        if (num == 5)
            stack.show();
        if (num == 6)
            stack.Quit();
    }
}

 

 

 

feat. 백준 9012번: https://www.acmicpc.net/problem/9012

 

9012번: 괄호

괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고

www.acmicpc.net

§ 예제. 괄호검사 알고리즘. 

조건 1): 왼쪽과 오른쪽 괄호 개수 동일

조건 2): 왼쪽이 먼저 나와야 함

조건 3): 서로 다른 타입의 왼쪽과 오른쪽 괄호쌍을 교차하지 말것.

 

Ex. 

C++로 쉽게 풀어쓴 자료구조 / 천인국.최영규 저

 

(pseudo code) 의사코드로 먼저 표현

 

※ STL을 포함한 stack 실전 구현

#include <iostream>
#include <stack>
#include <map>
using namespace std;

bool checkBracket(char* s){
    stack<char> stack;  // 괄호를 검사하기 위해 사용할 stack
    map<char, char> bracketMap = {{')', '('},
                                  {']', '['},
                                  {'}', '{'}};

    for(int i=0; s[i]!='\0'; i++){
        // 왼쪽 괄호를 만나는 경우 stack에 push
        if(s[i]=='(' || s[i]=='[' || s[i]=='{'){
            stack.push(s[i]);
        }
        // 오른쪽 괄호를 만나는 경우 stack의 top과 비교
        if(s[i]==')' || s[i]==']' || s[i]=='}'){
            if(stack.empty()){
                return false;   // stack이 비어있는 경우 false
            }else{
                if(bracketMap.find(s[i])->second != stack.top()){
                    return false;   // stack의 top과 괄호의 짝이 맞지 않는 경우 false
                }else{
                    stack.pop();    // stack의 top과 괄호의 짝이 맞는 경우 pop
                }
            }
        }
    }

    // 최종적으로 stack이 empty된 경우 true, 아닌경우 flase
    if(stack.empty()){
        return true;
    }else{
        return false;
    }
}

int main() {
    char* c1 = "1. {A[(i+1)]=0;}";
    char* c2 = "2. if((i==0)&&(j==0)";
    char* c3 = "3. A[(i+1])=0;";

    cout << c1 << " : " << (checkBracket(c1) ? "True" : "False") << endl;
    cout << c2 << " : " << (checkBracket(c2) ? "True" : "False") << endl;
    cout << c3 << " : " << (checkBracket(c3) ? "True" : "False") << endl;
    return 0;
}

 

+ Recent posts