제가 보는 책입니다.

블로그는 참조만 해주시고 진짜 공부하려면

책 사는 것을 권장합니다.

진짜 좋은 책입니다.

Head First - Design Patterns 입니다.


오늘은 옵저버 패턴에 대해서 알려드릴게요!!!!


옵저버 패턴은 어떠한 이벤트가 발생했을 때 객체들에게 새소식을 전해줄 수 있는 패턴입니다.

객체 쪽에서는 계속해서 정보를 받을지 여부를 실행중에 결정할 수 있습니다.

옵저버 패턴은 JDK에서 가장 많이 쓰이는 패턴이라고 합니다.

일대다 관계 그리고 느슨한 결합 키워드를 항상 기억하세요~


옵저버 패턴 = 출판사 + 구독자

라고 생각하면 이해하기 쉬울 것 같습니다.

출판사는 subject, 구독자를 observer라고 부릅니다.


옵저버 패턴의 정의

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식

일대다 의존성(subject에 observer들이 일대다로 연결되며, 의존함)을 정의함.

이미지 출처 클릭


옵저버 패턴을 구현하는 방법은 여러 가지가 있지만, 대부분 주제(subject) 인터페이스와 옵저버(observer) 인터페이스가 들어있는 클래스 디자인을 바탕.

옵저버 패턴은 느슨한 결합을 한다고 했는데

그럼 느슨한 결합은 무엇일까요??

두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해 서로 잘 모른다는 것을 의미합니다.

옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 객체 디자인을 제공합니다.


왜일까요??


주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스를 구현한다는 것 뿐입니다. 옵저버의 구상 클래스가 무엇인지, 옵저버가 무엇을 하는지 등에 대해서 알 필요가 없습니다.


- 옵저버는 언제든지 새로 추가할 수 있습니다. 주제는 Observer 인터페이스를 구현하는 객체의 목록에만 의존하기 때문에 언제든지 추가/삭제를 할 수 있습니다.


- 새로운 형식의 옵저버를 추가하려고 해도 주제를 전혀 변경할 필요가 없습니다. 옵저버가 되어야 하는 새로운 구상 클래스가 생겼다고 가정하면, 새로운 클래스 형식을 받아들일 수 있도록 주제를 바꿔야할 필요는 없습니다. 새로운 클래스에서 Observer 인터페이스를 구현하고 옵저버로 등록하기만 하면 됩니다. 주제 객체는 전혀 신경도 쓰지 않습니다. subject는 Observer 인터페이스만 구현한다면 어떤 객체에든지 연락을 하기 때문입니다.


- 주제와 옵저버는 서로 독립적으로 재사용할 수 있음. 주제나 옵저버를 다른 용도로 활용할 일이 있다고 해도 손쉽게 재사용할 수 있음. 그 둘이 서로 단단하게 결합되어 있지않기 때문(느슨한 결합)


- 주제나 옵저버가 바뀌더라도 서로한테 영향을 미치지는 않습니다. 둘이 서로 느슨하게 결합되어 있기 때문에 주제 혹은 옵저버 인터페이스를 구현한다는 조건만 만족된다면 어떻게 바꿔도 문제가 없습니다.


서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

느슨하게 해야하는 이유는 느슨한 결합을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있기 때문입니다. 객체 사이의 상호의존성을 최소화해야 합니다.



옵저버 패턴 코드 다운로드

코드에는 푸시와 풀 방식이 들어있습니다.

푸시는 subject와 observer를 인터페이스로 구현했고, 풀은 java.util의 observer와 observable을 import했습니다.


푸시방식에서는 notify를 하면 직접 subject 쪽에서 observer들을 update하지만

풀방식에서는 setChanged() 함수를 이용하여 데이터가 변했음을 알려주고,

notifyObservers()를 호출하는데 이는 데이터를 푸시하는 것이 아닌

옵저버 측에서 get함수를 이용하여 데이터를 가져다 쓰는 것입니다.


java.util.observable의 단점

- 이는 인터페이스가 아닌 클래스입니다. 그러므로 구현과 재사용성에 있어서 제약조건이 많습니다.

클래스이기 때문에 서브클래스를 만들어야하는데 이미 다른 클래스를 상속하고 있다면 다중상속이 되므로

Observable기능을 사용할 수 없습니다.


- 또한 인터페이스가 아니기 때문에 직접 구현하는 것이 불가능합니다. java.util 구현을 다른 구현으로 바꾸는 것은

불가능합니다.


- Observable 클래스의 핵심 메소드를 외부에서 호출할 수 없습니다. setChanged() 메소드는 protected로 되어있기 때문에 서브클래스에서만 호출이 가능합니다.


제가 보는 책입니다.

블로그는 참조만 해주시고 진짜 공부하려면

책 사는 것을 권장합니다.

진짜 좋은 책입니다.

Head First - Design Patterns 입니다.


Strategy_Pattern.jar

아래에서 설명할 스트래티지 패턴의 예제입니다.

다운로드 받으시고 실행하시면 좀 더 이해하기 수월하실 겁니다.

자바파일입니다.


문제의 시작

어떤 팀은 성공적인 오리 연못 시뮬레이션 게임을 만들었다.

이 게임에서의 오리는 헤엄도 치고 꽥꽥거리는 소리도 내는 매우 다양한 오리 종류를 보여줄 수 있다.

처음으로 이 오리를 디자인한 사람들은 Duck이라는 수퍼클래스를 만들었고,

Duck 클래스를 확장하여 다른 종류의 오리들을 만들었다.


Duck클래스에는 quack(), swim(), display()등의 메소드가 존재한다.


이제 오리들을 날아다닐 수 있게 하고 싶다.

그렇다면!


어떻게 해결해볼까??

수퍼클래스인 Duck클래스에 fly() 메소드를 추가하면 되지 않을까??

정말 간단하다.


하지만 문제가 발생했다. 날 수 있는 오리도 있지만, 날지 못하는 오리 인형 같은 것들이 날라다니기 시작한 것이다.

수퍼클래스에 fly() 메소드가 추가되면서 일부 서브클래스에는 적합하지 않은 행동이 전부 추가된 것이다.


상속을 활용하여 코드를 재사용할 수 도 있지만 이런 부작용이 발생할 수 있다.


오리 종류마다 quack()메소드(울음 소리)를 오버라이드 한 것처럼 fly() 메소드 또한 오버라이드하면 되지않을까!?

오리 인형은 아무 동작도 하지않게 오버라이드하면 되겠다!


하지만 이 방법 또한 문제가 많다.

오리의 종류마다 오버라이드를 한다면 모든 오리의 행동을 알기 힘들다는 것이다.

또한, 런타임 도중에 오리의 특징을 바꾸기도 힘들다.


인터페이스는 어떨까???

fly() 메소드를 Duck 수퍼클래스에서 빼고 fly() 메소드가 들어가있는 Flyable 인터페이스를 만든다면!

하늘을 나는 오리에 대해서만 이 인터페이스를 구현하여 fly() 메소드를 집어넣으면 되겠다!!!!


모든 오리들이 꽥꽥 우는 것도 아니니 Quackable이라는 인터페이스를 같이 만들어보자!


으악.. 이 방법도 문제가 있다.

서브클래스에서 이 인터페이스들(Flyable, Quackable)을 구현한다면 날거나 울음 소리에 대해 전부 오버라이딩을 해야하므로

코드의 재사용을 전혀 할 수 없다.

100가지의 오리 종류가 있다면 100가지 모두 일일이 다 구현해야하는 것이다.


진짜 어떻게 해결해야하지??ㅠ_ㅠ

이 상황에 가장 어울리는 디자인 원칙이 있다.


애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리하라.


코드에 새로운 요구사항이 있을 때마다 바뀌는 부분이 있다면, 바뀌지 않는 다른 부분으로부터 골라내서 분리해야한다는 말이다.

바뀌는 부분은 따로 뽑아서 캡슐화한다면, 바뀌지 않는 부분에는 영향을 미치지 않은 채로 그 부분만 고치거나 확장할 수 있다.

모든 패턴은 시스템의 일부분을 다른 부분과 독립적으로 변화시킬 수 있는 방법을 제공하기 위한 것이다.


수퍼클래스 Duck에서 fly()와 quack()을 제외하면 다른 부분은 잘 작동한다.

그렇다면 달라지는 부분인 fly()와 quack()을 분리하여 새로운 집합을 만들어보자.


그리고 Duck()의 행동을 동적으로 혹은 유연하게 바꿀 수 있도록, fly와 quack부분에 대해 setter 메소드를 포함하자.


두번째 디자인 원칙!


구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.


여기서 말하는 인터페이스는 자바의 인터페이스라기 보다는 객체가 코드에 의해서 고정되지 않도록, 어떤 상위 형식에 맞춰서 프로그래밍함으로써

다형성을 활용해야한다는 것이다.

그리고 상위 형식에 맞춰서 프로그래밍하라는 원칙은 변수를 선언할 때는 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다는 말이다.

이렇게 하는 이유는 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 집어넣을 수 있기 때문이다.

또한, 이렇게 하면 변수를 선언하는 클래스에서 실제 객체의 형식을 몰라도 된다.


예를 들어..


Dog d = new Dog();

d.bark();


이렇게 한다면 구체적인 구현에 맞춰서 코딩을 해야한다.

하지만 상위 형식에 맞춰 코딩한다면??


Animal animal = new Dog();

animal.makeSound();


Dog라는 걸 알고 있긴 하지만 다형성을 활용하여 Animal에 대한 레퍼런스를 써도 된다는 말이다.


더 바람직한 방법은 인스턴스를 만드는 과정을 new Dog()처럼 직접만드는 대신 구체적으로 구현된 객체를 실행시에 대입하는 것이다.


a = getAnimal();

a.makeSound();


Animal의 하위 형식 가운데 어떤 형식인지 몰라도 makeSound()에 대해 올바른 반응을 할 수 있다.


어쨋든!!

위에서 분리한 fly와 quack을 인터페이스로 FlyBehavior 와 QuackBehavior로 구현을 하고 

그 인터페이스 안에 FlyWithWings, FlyNoway 그리고 Quack, Squeak, MuteQuack으로 구체적인 행동을 하는 클래스를 만든다.


이런 식으로 디자인하면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할 수 있다.

그런 행동이 더 이상 Duck클래스 안에 숨겨져 있지 않기 때문이다.


그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 클래스는

Duck클래스를 전혀 건드리지 않고도 새로운 행동을 추가할 수 있다.


이렇게 한다면 상속을 쓸 때 떠안는 부담을 전부 떨쳐 버리고 코드 재사용의 장점을 그대로 누릴 수 있다.


이 패턴이 바로 스트래티지 패턴이다!


스트래티지 패턴의 정의는! 바로바로!


알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다.

스트래티지 패턴을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다!

'CS기본지식 > 디자인 패턴' 카테고리의 다른 글

데코레이터 패턴(Decorator Pattern)  (0) 2017.08.28
옵저버 패턴(Observer Pattern)  (0) 2017.08.25

1의 보수란 어떤 수를 커다란 2의 제곱수-1에서 빼서 얻은 이진수이다. 또는 비트를 반전시켜 얻을수 있다. 1의 보수는 대부분의 산술연산에서 원래 숫자의 음수처럼 취급된다. 주어진 이진수와 자리수가 같고 모든 자리가 1인 수에서 주어진 수를 빼서 얻은 수가 1의 보수이다. 혹은 주어진 이진수의 모든 자리의 숫자를 반전(0을 1로, 1을 0으로)시키면 1의 보수를 얻을 수 있다.


https://ko.wikipedia.org/wiki/1%EC%9D%98_%EB%B3%B4%EC%88%98



그러면 1의 보수를 왜쓰냐!!!!!!!하면!!

음수(-)를 표현해주기 위해서 입니다!

컴퓨터는 숫자를 비트로 나타내자나요!?? 4bit라고 생각했을 때 0001이면 1, 0010이면 2

이런식으로 0000~1111 까지 4비트라면 0~15를 나타낼 수 있습니다.

이런 방식이 흔히 우리가 코딩할 때 쓰는 unsigned 방식입니다. 음수를 나타내지 않는 방식이죠.


int는 32비트이니깐 0 ~ (2의 32승 - 1)까지 나타낼 수 있겠죠????

unsigned에 2의 32승 - 1을 대입하시면 숫자가 망가지지 않는 걸 볼 수 있습니다.


그런데

signed(int앞에 키워드를 안붙여도됨 디폴트 값임)에 2의 32승 -1을 넣으면 깨질까욥 안꺠질까욥?

깨집니다!!!!!!!


이유는 signed를 사용하면 음수 값을 사용할 수 있게 되는데! 32비트개의 비트중 맨 앞에 비트를 음수인지 양수인지 구분하는 비트로 사용하기 때문입니다.

그렇기 때문에 0 ~ (2^32 - 1) 이었던 범위가 ! 반으로 싹 ! 쪼게집니다. 그러면 -(2^31 - 1) ~ (2^31 - 1)까지 표현이 가능합니다!


근데 여기서 1의 보수의 문제점을 발견할 수 있습니다!

무엇이냐!!!!!!

4비트로 예를 들어드릴게요!

4비트를 쭉! 써보겠습니다.

0000

0001

0010

0011

0100

0101

0110

0111

1000

1001

1010

1011

1100

1101

1110

1111


이렇게 16가지의 숫자가있죠! 0에서 15까지!!!!!

근데 우리는 맨앞의 부호를 -로 쓰기로했으니 -7에서 7까지의 숫자가 표현이가능합니다!!!!!

여기서 발생하는 문제는 무엇일까요!?

-7 ~ 7까지의 숫자는 16개가 아니고 15개죠??? 왜일까요????

0이 두번 체크되기 때문인데요


1의 보수는 모든 비트를 반전시키는 것이라고 말씀드렸습니다.


그렇다면 0000을 반전시키면 1111 -> -0 값을 갖는데 -0이라는 숫자는 0과 같죠. 두 번 세줄 필요가 없는 것입니다.

그렇다면 이 문제를 어떻게 해결할까요??


바로!!!!!!!!


2의 보수입니다.!

2의 보수 = 1의 보수 + 1 인데요!!!!!!!!

정의 한번 볼까욥


2의 보수란 어떤 수를 커다란 2의 제곱수에서 빼서 얻은 이진수이다. 2의 보수는 대부분의 산술연산에서 원래 숫자의 음수처럼 취급된다. 주어진 이진수보다 한 자리 높고 가장 높은 자리가 1이며 나머지가 0인 수에서 주어진 수를 빼서 얻은 수가 2의 보수이다. 혹은 주어진 이진수의 모든 자리의 숫자를 반전(0을 1로, 1을 0으로)시킨 뒤 여기에 1을 더하면 2의 보수를 얻을 수 있다.


https://ko.wikipedia.org/wiki/2%EC%9D%98_%EB%B3%B4%EC%88%98


이렇게하면 4비트로 -8부터 7까지 표현이 가능해집니다!


그 이유는 !!!!!

0 ~ 7 까지는 총 8개인데 0 ~ 7 까지를 반전시키면 ! -0 ~ -7에서 -1 ~ -8로 바껴버리지요용!!!!!!!! 그러므로 -8 ~ 7까지 총 16개의 수를 전부 사용할 수 있습니당!!!!!!!!!!!!!

'CS기본지식 > 컴퓨터 기본 지식' 카테고리의 다른 글

rest란??  (0) 2017.11.19

중위표기식의 식을 입력 받아서 후위 표기식으로 바꾼 후

계산하는 스택 계산기입니다.


후위로 바꾸는 방법을 모르신다면 아래의 글을 참고해주세요

http://donggod.tistory.com/45


그리고 정석적인 방법이 아닙니다!!!

그냥 제가 생각나는데로 코딩했기 때문입니다...ㅠ_ㅠ....(근데 이게 정석일수도? 냐하하..)


문자열로 받은 식을

후위로 바꿉니다.

후위로 바꿀 때 숫자 뒤에는 항상 '$'을 붙였습니다. 문자열이기 때문에 숫자라는 구분을 넣어준 것입니다.


예를들어 1000 + 300

이면

1000300+가 될텐데 1000300인건지 아니면 1000 300 인것인지 구분하기 위함입니다.

그래서 1000$300$+가 됩니다.


후위를 스택으로 계산하는 방법은

후위식에서 AB+C+라는 식이 있으면


A와 B를 스택에 삽입하고 기호를 만나면 스택에서 가장 위의 값 두개를 팝하고 +연산을 한 후에

다시 스택에 넣습니다.

과정을 보여드리자면


스택 : A,B

기호 : +


그럼 A와 B를 꺼내서 (A+B)라는 값이 만들어집니다


이것을 다시 스택에 넣습니다.


스택 : (A+B)

기호 : 


또 스택에 C가들어오겠죠

스택 : (A+B),C

기호 : +


또 두개를 꺼내서 계산합니다


(A+B+C)라는 값이 만들어지곘죠


스택 : A+B+C

기호 :


완성됐습니다!


아래는 코드입니다.

궁금한 점은 댓글 주세요


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <cstdio>
#include <iostream>
#include <utility>
#include <stack>
#include <string.h>
using namespace std;
 
 
//이전 자료구조 글을 참고하세요 [1918] 후위표기식
// http://donggod.tistory.com/45
//그 코드에서 바뀐 부분만 주석 달겠습니다.
void prefix(char *str, char *output) {
    stack<char> s;
    int oIdx = 0;
 
    for (int i = 0; i < strlen(str); i++) {
        //A~Z가 아닌 숫자로 바뀜
        //숫자일 때 까지 output에 넣고
        //마지막에 '$'를 삽입
        //ex) 1000+30 이면 1000$30$+
 
        if (str[i] >= '0' && str[i] <= '9') {
            int j;
            for (j = i; str[j] >= '0' && str[j] <= '9'; j++) {
                output[oIdx++= str[j];
            }
            output[oIdx++= '$';
            i = j - 1;
        }
        else {
            if (str[i] == '(') s.push(str[i]);
            else if (str[i] == ')') {
                while (s.top() != '(') {
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.pop();
            }
            else if (str[i] == '*' || str[i] == '/') {
                while (!s.empty() && (s.top() == '*' || s.top() == '/')) {
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.push(str[i]);
            }
            else {
                while (!s.empty() && s.top() != '(') {
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.push(str[i]);
 
            }
        }
    }
 
    while (!s.empty()) {
        output[oIdx++= s.top();
        s.pop();
    }
    output[oIdx] = '\0';
}
 
//prefix로 바뀐 output, 인덱스를 포인터로 받음
//'$'를 만날 때 까지 10씩 곱해주며 숫자로 바꾼 후 리턴
int toInt(char *output, int* idx) {
    int ret = 0;
    for (; output[*idx] != '$'; (*idx)++) {
        ret *= 10;
        ret += output[*idx] - '0';
    }
    return ret;
}
 
int calcuration(char *output) {
    stack<int> s;
 
    for (int i = 0; i < strlen(output); i++) {
        if (output[i] >= '0' && output[i] <= '9') {
            int a = toInt(output, &i);//문자열을 숫자로 바꿈
            s.push(a);//스택에 삽입
        }
        else {
            int b = s.top();
            s.pop();
            int a = s.top();
            s.pop();
 
            switch (output[i]) {
            case '+':
                a += b;
                break;
            case '-':
                a -= b;
                break;
            case '/':
                a /= b;
                break;
            case '*':
                a *= b;
                break;
            }
            
            s.push(a);
        }
    }
 
    return s.top();
}
 
int main() {
    char str[10000];
    char output[10000];
 
    scanf("%s", str);
    prefix(str, output);
 
    printf("%d\n", calcuration(output));
 
    return 0;
}
cs


'CS기본지식 > 자료구조' 카테고리의 다른 글

[C언어] 우선순위 큐(Priority Queue)  (3) 2017.09.07
[2263] 트리의 순회  (1) 2017.08.27
[1918] 후위표기식  (2) 2017.06.13
이중 연결 리스트  (0) 2017.04.22
단순 연결 리스트  (0) 2017.04.22

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


어떤 회사의 인턴 사전 시험에서 스택 계산기 문제가 나왔습니다.


식을 문자열로 받고 계산하는 문제였는데..


총 7문제?? 정도였던것 같은데.. 손코딩으로 해야해서..

시간이 모잘라서 못풀었습니다ㅠ_ㅠ

기억도 되살릴겸 다시 풀어봤습니다!

----------------------------------------


어떤식을 후위연산으로 바꿀 때 중요한 것은

기호들의 우선순위 입니다.

*, / 곱하기와 나누기는 우선순위가 같습니다.

+, - 플러스와 마이너스는 우선순위가 같습니다.


위 두개의 우선순위는 ('+', '-') < ('*', '/') 이렇게 됩니다.

곱하기와 나누기가 플러스 마이너스 보다 우선순위가 높은 것이지요.


예를 들어 설명해드리자면!


3 + 4 * 8 이라는 식이 있으면 답은 3 + 32 = 35 겠지요!?

여기서 * 곱하기를 먼저 계산하셨을겁니다! 이 우선순위를 말하는 것입니다!


ABC+*라는 식은 A * (B + C)입니다.

A * (B + C)라는 식을 후위연산으로 바꾸는 과정을 보여드릴게요!


스택은 왼쪽이 bottom 가장 오른쪽이 탑입니다.

출력은 그냥 왼쪽부터 오른쪽 순입니다.

먼저 A를 만났습니다.


1.

스택 :

출력 : A


가 되겠죠?

과정을 쭉 보여드릴게요오오~~


2.

스택 : *

출력 : A


3.

스택 : *(

출력 : A


4.

스택 : *(

출력 : AB


5.

스택 : *(+

출력 : AB



6.

스택 : *(+

출력 : ABC


7.
이제 여기서 중요합니다!
')' 기호를 만나면 ! 스택에서 '(' 까지 전부 pop 해줍니다!!!! 여기서는 + 밖에없으므로!

스택 : *(+

출력 : ABC


스택 : *
출력 : ABC+

가 됩니다!

이제 문자열이 끝났으니
스택에 남아있는것을 스택이 empty가 될때까지 비워주면서 차례차례 출력 문자열에 넣어줍니다!
결과 : ABC+*
가 됩니다!

ABC*+라는식은 A + B * C입니다.

A + B * C 를 한번 위의 절차처럼 따라해보세요!!!

주의할 점은 *가 +보다 우선순위가 높다는 점!


아래는 1918 문제의 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <cstdio>
#include <iostream>
#include <utility>
#include <stack>
#include <string.h>
using namespace std;
 
int main() {
    char str[1000]; //input
    char output[1000]; // output
 
    scanf("%s", str);
 
    stack<char> s; // +-*/(를 저장할 스택
    int oIdx = 0// 출력 문자열의 인덱스
 
    // str의 문자열을 끝까지
    for (int i = 0; i < strlen(str); i++) {
        if (str[i] >= 'A' && str[i] <= 'Z') output[oIdx++= str[i]; // A~Z는 출력문자열에 추가
        else {
            //그게아니면 전부 기호이므로
            //아래처럼 처리
 
            if (str[i] == '(') s.push(str[i]); // '('문자는 무조건 스택에 추가
            else if (str[i] == ')') { // ')'문자는 '('문자를 만날 때까지 pop
                while (s.top() != '(') {
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.pop();
            }
            else if (str[i] == '*' || str[i] == '/') {
                //우선순위가 나보다 높거나 같은것을 pop
                //( '/','*'보다 우선순위가 높은것은 없음)
                while (!s.empty() && (s.top() == '*' || s.top() == '/')) {
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.push(str[i]);
            }
            else { // '+', '-' 인 경우
                while (!s.empty() && s.top() != '(') {
                    //우선순위가 나보다 높거나 같은것을 pop
                    //('+', '-'보다는 전부 우선순위가 높으므로 '('나 스택이 빌 때까지 pop)
                    output[oIdx++= s.top();
                    s.pop();
                }
                s.push(str[i]);
 
            }
        }
    }
 
    //스택에 남아있는 것을 전부 pop
    while (!s.empty()) {
        output[oIdx++= s.top();
        s.pop();
    }
    output[oIdx] = '\0';
 
    printf("%s\n", output);
 
    return 0;
}
cs


'CS기본지식 > 자료구조' 카테고리의 다른 글

[C언어] 우선순위 큐(Priority Queue)  (3) 2017.09.07
[2263] 트리의 순회  (1) 2017.08.27
스택 계산기  (0) 2017.06.13
이중 연결 리스트  (0) 2017.04.22
단순 연결 리스트  (0) 2017.04.22

https://ko.wikipedia.org/wiki/Join_(SQL)


위키 잠고했습니다!


아래 쿼리문으로 테이블 생성하고 데이터 삽입해주세요!

CREATE TABLE department
(
 DepartmentID INT,
 DepartmentName VARCHAR(20)
);

CREATE TABLE employee
(
 LastName VARCHAR(20),
 DepartmentID INT
);

INSERT INTO department(DepartmentID, DepartmentName) VALUES(31, '영업부');
INSERT INTO department(DepartmentID, DepartmentName) VALUES(33, '기술부');
INSERT INTO department(DepartmentID, DepartmentName) VALUES(34, '사무부');
INSERT INTO department(DepartmentID, DepartmentName) VALUES(35, '마케팅');

INSERT INTO employee(LastName, DepartmentID) VALUES('Rafferty', 31);
INSERT INTO employee(LastName, DepartmentID) VALUES('Jones', 33);
INSERT INTO employee(LastName, DepartmentID) VALUES('Steinberg', 33);
INSERT INTO employee(LastName, DepartmentID) VALUES('Robinson', 34);
INSERT INTO employee(LastName, DepartmentID) VALUES('Smith', 34);
INSERT INTO employee(LastName, DepartmentID) VALUES('John', NULL);


그리고 크로스 조인을 해보겠습니다!!


크로스 조인은 아래의 쿼리와 결과가 같네요!


크로스 조인은


이 두 테이블의 곱을 나타내는 것 같습니다!

왼쪽 임플로이 테이블은 데이터가 6개 오른쪽 디퍼트먼트 테이블은 4개네요!

위에서 쿼리를 썼을 때 24행이라는 결과가 보이시죠!?

6 * 4 = 24 입니다.


Employee테이블에서 (Rafferty, 31)행에 대해서 Department의 (31, 영업), (32, 기술), (33, 사무), (34, 마케팅) 4개의 행이 대응됩니다.

이걸 Rafferty~John까지 반복하면 24개의 행이 생성되겠죠!



이제 내부 조인 차례입니다!!!!!!!!!!!!! 내부 조인은 영어로 Inner Join이라고 하네욧!

내부 조인의 특징!

내부 조인은 가장 흔한!!! 결합 방식이랍니다!!

그리고! 조인 구문(조인 쿼리문에서 ON 뒤의 부분을 말하는 것 같습니다!)에 충족하는 A테이블의 행과 B테이블의 행을 결합하여서 출력합니당!


아그리고 !! 조인에는 명시적 조인 표현과 암묵적 조인 표현이라는게 있습니다!!

위에서 크로스 조인이라고 언급하고 출력한것이 명시적 조인, 그 밑에 select * from 임플로이, 디파트먼트 이 것이 암묵적 조인입니다.!

cross join이라고 명시했기 떄문에! 명시적 조인이구요!, 암묵적 조인에는 join을 언급하진 않았지만 조인한 것과 같은 결과를 보여주기 때문에 암묵적 조인 같습니다!


내부 조인에서도 한번 해보겠습니다!


쿼리문을 해석하자면! department 테이블이 employee테이블에 내부 조인을 하네요!

On뒤에 조건이 나오죠! 임플로이 테이블의 departmentid와 디파트먼트 테이블의 departmentid가 같으면

데이터를 보여줘라 이런 내용입니다.

가운데 두 컬럼을 보시면 31 31, 33 33, ...으로 같은것을 볼 수 있습니다.


아래는 암묵적으로 내부조인을 하는 쿼리문입니다!


오옷!!!

위 두개의 결과가 같네요 신기하네요!@!

위에서 했던 크로스 조인의 결과에서 where을 이용하여 조건을 거네요!

크로스 조인의 결과 중에서! department아이디가 같은거만 출력하는 것입니다!

재밌네요 ㅎ..ㅎ


아아 참고로 매번 employee, department 이런식으로 쓰기보다는 별명을 붙여주면 편리하게 사용할 수 있습니다!

SELECT * 
FROM employee e INNER JOIN department d 
  ON e.DepartmentID = d.DepartmentID;


이렇게! 테이블명 뒤에다가 별명을 붙여주시면 됩니다! 실행보세욧



내부 조인을 세부적으로 분류하면

동일 조인(equi-join), 자연 조인(natural join), 교차 조인(cross-join)으로 나눌 수 있답니다!


동일 조인은 위에서 보여준 내부조인과 같은 쿼리네요!


자연조인은!!!!


자연 조인(natural join)은 동일 조인의 한 유형으로 조인 구문이 조인된 테이블에서 동일한 컬럼명을 가진 2개의 테이블에서 모든 컬럼들을 비교함으로써, 암시적으로 일어나는 구문이다. 결과적으로 나온 조인된 테이블은 동일한 이름을 가진 컬럼의 각 쌍에 대한 단 하나의 컬럼만 포함하고 있다.


위키에서 이렇게 설명하고있네요!!!


하지만 위험한 조인인가 봅니다!! 아래 이러한 설명도 붙어있습니다.


대부분의 전문가들은 NATURAL JOIN이 위험한 것이며, 그러므로 이것의 사용을 강력하게 비권장하고 있다.[3] 그러한 위험은 다른 테이블에 다른 컬럼으로 동일한 이름을 가진 새로운 컬럼을 무심코 추가하는데서 오는 것이다.

현존하는 자연 조인은 자연스럽게 (다른 컬럼에서 온) 이전보다 다른 기준을 이용해서 비교를 위해 비교를 하거나 일치하는 것을 찾아서 새로운 컬럼을 이용할 것이다. 그리하여 테이블 내에 있는 데이터가 변경되지 않고, 증가만 해도 현존하는 질의어는 다른 결과물을 생성할 것이다.



이제 외부 조인을 해보겠습니다!!!!!!!


외부조인은 왜!?!? 왜쓸까욧!!!

equi join은 조인을 생성할 때 동일한 값이 없다면 데이터를 반환하지 못하는데요!!!!

이 때 동일한 값이 없는 행들도 포함하여 조회하기 위해서

외부 조인을 사용합니다!


left 외부 조인을 해보겠쑵니당!!!



위의 내부조인과 다르게 john이 표시되어있습니다!!!!


그리고 오른쪽 조인 왼쪽조인은 기능적으로 동일하다고 하네요!!!

아래의 설명을 보시죠!


오른쪽과 왼쪽 외부 조인은 기능적으로 동일하다. 양자 모두 다른 것들이 하지 않는 어떠한 기능도 제공하지 않는다. 그래서 오른쪽과 왼쪽 외부 조인은 테이블 순서가 변경되기만 하면, 서로 대체할 수 있다.


그렇다네요!!!!

right join도 어떻게쓰는지 보겠습니다!.


컬럼 순서만 바뀌었을 뿐이지 결과는 같네욧!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

이만 마치겠숩니당 뿅뿅


'CS기본지식 > 데이터베이스' 카테고리의 다른 글

정규화가 무엇일까?  (0) 2017.11.19
트랜잭션  (0) 2017.11.19
[SQL] select문  (0) 2017.06.08
테이블 수정 및 삭제  (0) 2017.06.08
varchar와 char의 차이  (0) 2017.06.07

SELECT 구문 형식


select select_list(컬럼, cnt, avg 등등) [ into new_table ]

[ from table_source ] [ where search_condition ]

[ group by group_by_expression ]

[ having search_condition ]

[ order by order_expression [ asc | desc ] ]


GROUP BY : 특정 열이나 특정 열을 연산할 결과를 집계 키로 정의하여 그 집계 키의 Unique 값에 따라 그룹을 짓는 연산자.

DISTINCT : 단순히 unique값만을 추출하기 위해 사용.


위 둘의 차이 : group by는 집계 키(count(*), sum(), max(), min(), avg() 등) 기준으로 집합 연산, distinct는 unique만 뽑아냄.


ex)

1. select distinct player_id, team_id from player

2. select distinct player_id, team_id, count(*) from player

3. select team_id, count(*) from player group by team_id


1번 예시는 player_id, team_id 컬럼에서 중복제거가 되어 출력됨.

2번 예시는 오류가 난다. 이유는 distinct에 집계 함수를 썼기 때문이다.(count)

3번 예시는 team_id로 그룹화 되어 각 team_id당 몇개의 데이터가 있는지 출력된다.


select문을 계속 연습해보겠다.


이러한 데이터들이 있다.

위의 3번처럼 completed에 대해서 그룹화하고 count를 출력해보겠다.


INTO : INTO는 조건에 맞는 기존 테이블의 열 내용을 새 테이블을 만들어 가져온다.

(테스트해보니 지원안하는 DB도 있는 것 같군요.. H2로 하고 있는데 안되네용..또르르)


ex) select * into [새로운 테이블 명] from [데이터를 가져올 테이블] where 조건



having절 : where과 비슷한데 group에 대해서 적용되는 조건이라고 생각하면 된다.


WHERE절 조건

 등호

설명 

같다 

<> 

같지 않다

작다 

크다 

<=

작거나 같다 

=> 

크거나 같다 


(실험 결과 not의 위치는 컬럼명 앞이나 뒤 아무데나 상관없는듯)

컬럼명 between a and b :  a와 b사이의 값을 가진 결과를 보여줌(a, b 포함)

not 컬럼명 between a and b : a와 b사이의 값을 제외하고 보여줌

컬럼명 is null : 컬럼의 value가null인 데이터를 찾음

컬럼명 is not null : 컬럼의 value가 null이 아닌 데이터를 찾음

컬럼명 like ''

--------------

%가나다 : 멍충이가나다, 바보가나다, 하가나다 등 뒤에 가나다가 붙는 데이터를 찾음

가나다% : 가나다멍충이, 가나다바보, 가나다라마바사아자차카타하 등 앞에 가나다가 붙는 데이터를 찾음

%가나다% : 가운데 가나다가 들어가는 데이터를 찾음.

_가나다 : 킼가나다, ㅎ가나다 등 앞에 한글자 불특정문자가 오는 가나다를 찾음.


컬럼명 in('', '') : in안의 데이터들이 포함되어있는 데이터를 보여줌.(or과 같음)




'CS기본지식 > 데이터베이스' 카테고리의 다른 글

트랜잭션  (0) 2017.11.19
조인  (0) 2017.06.10
테이블 수정 및 삭제  (0) 2017.06.08
varchar와 char의 차이  (0) 2017.06.07
테이블 생성문  (0) 2017.06.07

참고 블로그

http://hyeonstorage.tistory.com/292



테이블 수정

1. 컬럼 추가(맨 뒤에 추가됨)

alter table 테이블명

add 컬럼명 datatype;


ex)

alter table player

add (age int));


2. 컬럼 삭제

alter table 테이블명

dop column 컬럼명;


ex)

alter table player

drop column age;


3. 컬럼 수정

alter table 테이블명

modify (컬럼명1 데이터 유형 [default 식][not null],

컬럼명2 데이터 유형 [default 식][not null]);


ex)

alter table player   

modify (player_name varchar(30) default 'hahahaha' not null);


특징

- 컬럼의 크기를 늘릴 수는 있지만 줄이지는 못함. (기존의 데이터가 훼손될 수 있기 때문)

- 해당 컬럼이 null값만을 가지고 있으면 데이터 type을 바꿀 수 있음.

- 해당 컬럼의 default값을 바꾸면 변경 이후의 삽입에만 영향을 줌.(기존의 것이 바뀌지 않음을 의미)

- 해당 컬럼에 null 값이 없을 경우에만 not null 제약조건을 추가할 수 있음.


4. 컬럼명 수정

alter table 테이블명

rename column 변경할 컬럼명 to 새로운 컬럼명;


alter table player

rename column player_id to team_id;


5. 제약조건 추가

alter table 테이블명

add constraint 제약조건명 제약조건(컬럼명);


ex)

alter table player

add constraint player_fk

foreign key(team_id) references team(team_id);


해석

player 테이블을 바꾸겠다.

player_fk라는 이름의 제약조건을 추가하겠다.

team_id를 외래키로 하고 team테이블의 team_id를 참조하겠다.


6. 제약조건 삭제

alter table 테이블명

drop constraint 제약조건명;


ex)

alter table player

drop constraint player_fk;


테이블 삭제

drop table 테이블명


'CS기본지식 > 데이터베이스' 카테고리의 다른 글

트랜잭션  (0) 2017.11.19
조인  (0) 2017.06.10
[SQL] select문  (0) 2017.06.08
varchar와 char의 차이  (0) 2017.06.07
테이블 생성문  (0) 2017.06.07

+ Recent posts