728x90

 

문제는 틈틈히 푸는데 TIL를 쓰기엔 정성이 부족해서 잘안하게 되네요.. ㅎㅎ

 

오늘은 백준 문제 12605번 : 단어순서 뒤집기 에 대해서 이야기 해볼까 합니다.

 

 


문제

스페이스로 띄어쓰기 된 단어들의 리스트가 주어질때, 단어들을 반대 순서로 뒤집어라. 각 라인은 w개의 영단어로 이루어져 있으며, 총 L개의 알파벳을 가진다. 각 행은 알파벳과 스페이스로만 이루어져 있다. 단어 사이에는 하나의 스페이스만 들어간다.

 

입력

첫 행은 N이며, 전체 케이스의 개수이다.
N개의 케이스들이 이어지는데, 각 케이스는 스페이스로 띄어진 단어들이다. 스페이스는 라인의 처음과 끝에는 나타나지 않는다. N과 L은 다음 범위를 가진다.
N = 51 ≤ L ≤ 25

 

 

 

앞서 그냥 받은 문자열을 뒤집는것이 아닌 항해99에서 내준 문제는 스택/큐 를 사용해서 문제를 풀라고 하여서 Stack을 사용했습니다.

 

public class backjoon12605 {
    public static void main(String[] args) throws IOException {
    	//BufferedReader로 입력받기
    	//BufferedReader를 사용해 입력을 받아 n에 저장합니다. n은 테스트 케이스의 수입니다.
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        //for문이 각 테스트 케이스마다 실행됩니다.
        //Stack<String> q = new Stack<>();가 for문 안에 있어, 각 테스트 케이스마다 새로운 Stack이 생성됩니다.
        for(int i =1; i<=n; i++){
            Stack<String> q = new Stack<>();
            //입력된 문장을 split(" ")으로 단어별로 나누어 String 배열 que에 저장합니다.
            String[] que = br.readLine().split(" ");
            //for-each 루프를 사용해 각 단어를 Stack에 push()로 추가합니다.
            for(String a : que){
                q.push(a);
            }
            //System.out.print("Case #" + i + ":");를 출력해 현재 테스트 케이스 번호를 출력합니다.
            //while (!q.isEmpty()) 루프에서 Stack이 비어있지 않을 때까지 pop()을 사용해 단어를 출력합니다. Stack은 LIFO(Last In, First Out) 구조이므로 마지막에 push()된 단어부터 출력되어 단어 순서가 역순이 됩니다.
            //System.out.println();로 각 테스트 케이스 출력을 마치고 줄바꿈을 합니다.
            System.out.print("Case #"+i+": ");
            while(!q.isEmpty()){
                System.out.print(" "+ q.pop());
            }
            System.out.println();
        }

    }
}

 

TIP : 

 

각 테스트 케이스마다 Stack을 새로 생성하므로, 이전 테스트 케이스의 데이터가 현재 테스트 케이스에 영향을 주지 않습니다.
Stack의 내용을 pop()하면서 단어를 출력할 때, 단어가 역순으로 출력됩니다.

stack이 for문 밖에 선언이 되어있어도 잘 작동되고 답변 똑같이 나옵니다. 그러나 그럴 경우,
모든 테스트 케이스가 동일한 Stack 인스턴스를 공유하기 때문에 예상치 못한 결과를 초래할 수 있습니다.
다만, 현재 코드 구조에서는 매 반복문 안에서 Stack 이 비워질 때까지 pop() 을 호출하므로 동작할 수 있습니다.

 

728x90

'잡담' 카테고리의 다른 글

아파치 스톰 과 아파치 카프카  (0) 2024.07.16
JDBC -1  (0) 2022.01.13
API-IO (입출력 성능향상 보조 스트림)  (0) 2021.12.20
Input, Output  (0) 2021.12.20
728x90

스택(stack) 은 메모리영역으로 지역 변수가 저장되고 기능이 실행되는 영역입니다. 그리고 명령어 포인터는 그냥 포인터 일뿐입니다. 

스레드가 실행할 다음 명령어의 주소를 가리키느 역할을 한다. 각각의 스레드가 자체  스택과 명령어 포인트를 가지는지 쉽게 이해하려면 각각의 스레드는 특정 순간에 서로 다른 함수를 이용해 다른 명령을 수행한다는것만 기억하자. 

 

컨텍스 스위치


하나의 스레드 실행을 멈추고 다른 스레드를 스케줄링한 다음 다시 실행하는것이 컨텍스 스위치입니다. 

동시에 많은 스레드를 다룰 때는 효율성이 떨어지기 때문입니다. 이것이 병행성을 위한 대가입니다. 

우리가 생각을 가다듬고 집중력을 회복하는 시간과 똑같습니다. 집이나 직장에서 여러 일을 동시에 하면 생산성이 떨이지게 됩니다. 

주변의 뱅하는 받는다거나 다른 일로 전환하는 순간에 말이죠~! 

운영체제도 마찬가지 입니다.  

 

CPU 에서 실행되는 각 스레드는 CPU 내의 레지스터나 캐시 메모리 내의 커널 리소스 같은 것을 일정 부분 차지하게 됩니다. 

다른 스레드로 전화할 때는 기존의 모든 데이터를 저장하고 또 다른 스레드의 리소스를 CPU와 메모리에 복원해야 합니다. 

 

컨텍스트 스위치에서 알아야한느것은 너무 많은 스레드를 가동하게 되면 'Trashing' 이 발생한다는겁니다. 

이 말은 즉슨, 운영 체제가 우리가 시킨 일을 생산적으로 해내는 대신 스레드 관리 즉, 컨텍스트 스위치에 더 많은 시간을 할애한다는 거죠.

또, 스레드는 프로세스보다 리소스를 더 적게 사용합니다. 프로세스 내의 스레듣르은 공유하는 리소스가 많이 때문입니다. 

 

즉, 같은 프로세스에 속한 두 스레드 간의 컨텍스트 스위치가 각각 다른 프로세스의 두 스레드 간의 컨텍스트 스위치보다 효율적인 겁니다. 

 

컨텍스트 스위칭을 알았으니  운영체제가 어떤 스레드를 실행할지 언제 컨텍스트 스위치를 결정하는지 알아봅시다. 

 

예시) 텍스트 편집기로 과제를 한다고 가정을 합니다. 

좋아하는 음악을 들으면서 텍스트 편집기를 사용한다고 하면, 프로세스는 2개 입니다. 

 

간단한 설명을 위해 음악 플레이어에 스레드 2개가 있다고 합니다. 

하나는 파일에서 음악을 로딩해 스피커로 전송하는거고 

하나는 UI 스레드로 트랙의 상황을 보여주고 재생, 멈춤 버튼의 마우스 클릭에 반응합니다. 

텍스트 편집기도 스레드는 2개 입니다. 

하나는 역시, UI 스레드로 우리가 입력한 걸 보여주며 키보드와 마우스 입력에 반응합니다. 

다른 스레드는 현재 작업을 2초마다 파일에 저장하죠. 

 

 

Arriaval Order Length
1 file Saver
2 Music player
3 text editor UI
4 Music Player UI

 

이렇게 하나의 코어에 스레드 4개가 되었고 이제 코어 하나로 스케줄을 짜야합니다. 

 

작업의 도착순서와 실행시간이 주어진다고 가정한다면 운영체제는 누구를 가정 먼저 실행할까요?

먼저 도착하는 작업이 먼저 실행되는것이라고 가정하면 

위에 1->2->3->4 순서입니다. 단 이 접근법의 문제점은 실행 시간이 긴 스레드가 먼저 도착하면 다른 스레드에 기아 현싱이 발생한다는 겁니다.

특히 UI 스레드에는 큰 문제입니다. 애플리케이션의 응답성을 방해해 최악의 사용자 경험을 야기할 테니까뇽

위에 표를 보듯이 UI 스레드는 보통 짧습니다. UI 스레드는 사용자의 입역을 단순히 화면에 띄우기만 하거든요. 

그럼 짧은 순서대로 스케쥴을 짜면 어떻게 될까요??

아까와 전반대의 문제가 발생합니다. 

 

사용자 관련 이벤트가 항상 시스템에 있는 겁니다. 

가장 짧은 작업을 항상 맨 앞에 배치하게 되면 계산이 들어간 긴 작업들은 영원히 실행이 될수 없는겁니다. 

이 짧은 모의 실험을 통해 우리가 알게 된것이 있습니다. 

운영체제가 스레드에 CPU 시간을 공평하게 할당할 때 어떤 문제점이 생기는지 말이죠. 

그럼 일반적인 운영체제에서는 어떻게 작동하는지 알아봅시다.

 

운영체제는 '에포크'에 맞춰 시간을 적당한 크기로 나눕니다. 

 

그리고 스레드의 타임 슬라이스를 종류별로 에포크에 할당하죠. 

하지만 모든 스레드가 각 에포크에서 실행되거나 완료되지는 않아요. 

스레드에 실간을 할당하는 방법은 운영 체제가 각각의 스레드에 적용하는 동적 우선순위에 달렸습니다. 

정적 우선순위는 개발자가 미리 설정하고 보너스는 운영체제가 각각의 메포크마다 조절합니다. 

이렇게 하면 운영체제는 즉각적인 반은이 필요한 실시간 스레드나 인터랙티브 스레드에게 우선권을 주게 됩니다. 

하지만 동시에 기아 현상을 막기 위해 이전 에포크에서 실행 시간이 부족했거나 완료되 않은 스레드도 놓지지 않습니다. 

첫 번째 멀티 스레드 애플리케이션 작업을 시작하기 전에 마지막으로 확인할 것은 하나의 프로그램에 멀티스레드를 사용하는 시점과 새로 생성환 프로그램을 다른 프로세스에 실행하는 시점입니다. 

 

어떤 방식을 쓸 것인지 마음속으로 결정해야 합니다. 멀티 스레드 접근법과 멀티 프로레스 접근법 중에서 말이죠 !!  

스레드들은 많은 리소스를 공유합니다 그러니 많은 데이터를 공유하는 다양한 작업을 실행하려면 멀티 스레드 애플리케이션 아키텍처가 좋을겁니다 .뿐만 아니라 스레드는 생성과 파기가 훨씬 빠르고 같은 프로세스 안에서 멀티 스레드끼리 전환하는것이 여러 프로세스 사이에서 전환하는것보다 훨씬 빠릅니다. 

하지만 독립된 프로그램을 독립된 프로세스에 실행하는 게 좋을 수도 있습니다. 

가장 중요한 점이 보안과 안정성이라고 하면말이죠

여기서는 각각의 프로세스가 완전히 독립되어 있지만  멀티 스레드 애플리케이션에서는 앱 전체가 스레드 하나에 다운될 수도 있습니다. 

또, 서로 관련이 없는 작업을 실행할 경우 같은 프로세스에 통합하는것도 아무 의미가 없다. 

 

컨텍스트 스위치와 이것이 성능에 미치는 영향을 배웠습니다. 다음 시간에는 운영체제의 일반적인 스레드 스케중링 방법과 각 운영체제가 자체적인 스케줄링 알고리즘으로 움직인다는것도 배웠습니다. 

멀티 스레드 애플리케이션 아키텍처와 멀티 프로레스 애플리케이션 아키텍처가 각각 어느 경우에 유용하게 사용될수 있는지도 알아봤습니다. 

728x90

+ Recent posts