사용 책 : 코딩 테스트 합격자 되기 C++편

 

강의 주소 : https://www.inflearn.com/course/cpp-EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%A9%EA%B2%A9/dashboard

코딩 테스트 합격자 되기 - C++ 강의

 

 

강의 유튜브 주소 : https://www.youtube.com/watch?v=-TGCT74wFeg


코딩 테스트 합격자 되기 C++ - 06/07장 스택과 큐

1. 강의 개요

오늘 강의에서는 스택과 큐의 기본 개념을 배웠다. 스택과 큐는 코딩 테스트에서 자주 등장하는 자료구조이다.

2. 스택 (Stack)

스택의 개념

스택은 후입선출(LIFO, Last In First Out) 방식의 자료구조다. 즉, 가장 나중에 들어간 데이터가 가장 먼저 나온다.

(하노이탑이 생각났다)

스택의 주요 연산

  • push: 스택에 데이터를 넣는 연산이다.
  • pop: 스택에서 데이터를 꺼내는 연산이다.
  • top: 스택의 가장 위에 있는 데이터를 반환하는 연산이다.

스택의 사용 예시

  • 함수 호출 스택: 함수 호출 시 현재 함수의 실행 정보를 스택에 저장하고, 함수가 종료되면 스택에서 꺼내어 복원한다.
  • 웹페이지 탐색 히스토리: 우리가 웹페이지에서 뒤로가기 할 때, 가장 최근에 방문한 페이지를 먼저 되돌아간다.
  • 괄호 짝 맞추기: 여는 괄호와 닫는 괄호의 짝을 맞출 때 사용한다.
    (아마도 괄호뿐 아니라 짝이 있는 무언가나 절댓값이 같고 부호가 다른 무언가의 짝을 맞출 때도 응용이 될 것 같다.)

3. 큐 (Queue)

큐의 개념

큐는 선입선출(FIFO, First In First Out) 방식의 자료구조다. 즉, 가장 먼저 들어간 데이터가 가장 먼저 나온다.

일반적인 줄 서기를 생각하면 된다.

큐의 주요 연산

  • enqueue: 큐에 데이터를 넣는 연산이다.
  • dequeue: 큐에서 데이터를 꺼내는 연산이다.
  • front: 큐의 가장 앞에 있는 데이터를 반환하는 연산이다.

큐의 사용 예시

  • 줄 서기 시뮬레이션: 먼저 줄을 선 사람이 먼저 처리된다.
  • 요세푸스 문제: 원형으로 앉아 있는 사람들 중 특정 규칙에 따라 사람을 제거하는 문제다.

4. 추상 데이터 타입 (ADT)

추상 데이터 타입(ADT)은 자료구조의 세부 구현을 숨기고, 필요한 기능만을 명시한 것이다. ADT의 장점은 복잡한 내부 구현을 감추고, 사용자에게 필요한 기능만을 제공하여 효율성을 높인다. 일상적으로 쓰이는 리모콘, 세탁기, 자판기 등등 우리가 쓰는 제품, 시스템들이 여기에 속한다고 해도 될 것 같다.

5. 요세푸스 문제 해결: 배열 vs 큐

이제는 오늘 배운 개념을 사용해서 요세푸스 문제를 해결할 것이다.

문제 설명

요세푸스 문제는 다음과 같은 규칙을 따른다:

  • ( n )명의 사람들이 원형으로 앉아 있다.
  • 시작 위치부터 ( k )번째 사람을 제거한다.
  • 제거된 사람 다음부터 다시 ( k )번째 사람을 제거하고, 한 명이 남을 때까지 반복한다.

문제 분석

아마 이 문제가 까다로운 이유는 K번째 사람을 제거할 때마다 전체 배열이 변동되면서
동시에 기준점도 바뀐다는 점일 것이다. 반대로 이 까다로운 이유를 해결하면 문제는 쉽게 풀릴 것이다.

배열을 이용한 요세푸스 문제 해결

구현 방법

  1. 배열 초기화: 1부터 ( n )까지의 사람 번호를 배열에 저장한다.
  2. 사람 제거 과정: 현재 위치를 추적하며 ( k )번째 사람을 제거하고, 제거된 후에는 남은 사람들을 앞으로 당긴다.

시간 복잡도

  • 각 사람을 제거할 때마다 배열을 앞으로 당기는 작업 때문에 최악의 경우 시간 복잡도는 ( O(n^2))이다.

코드 예시

#include <iostream>
#include <vector>

void josephus(int n, int k) {
    std::vector<int> people(n);
    for (int i = 0; i < n; ++i) {
        people[i] = i + 1;
    }

    int index = 0;
    while (people.size() > 1) {
        index = (index + k - 1) % people.size();
        people.erase(people.begin() + index);
    }

    std::cout << "The last remaining person is " << people[0] << std::endl;
}

큐를 이용한 요세푸스 문제 해결

구현 방법

  1. 큐 초기화: 1부터 ( n )까지의 사람 번호를 큐에 저장한다.
  2. 사람 제거 과정: ( k-1 )번째 사람까지는 큐에서 꺼내어 다시 큐의 뒤로 보낸다. ( k )번째 사람은 큐에서 제거한다.

시간 복잡도

  • 각 사람을 제거할 때 ( k-1 )번의 큐 연산이 필요하므로, 시간 복잡도는 ( O(n \times k) )이다.

코드 예시

#include <iostream>
#include <queue>

void josephus(int n, int k) {
    std::queue<int> people;
    for (int i = 1; i <= n; ++i) {
        people.push(i);
    }

    while (people.size() > 1) {
        for (int i = 0; i < k - 1; ++i) {
            people.push(people.front());
            people.pop();
        }
        people.pop();
    }

    std::cout << "The last remaining person is " << people.front() << std::endl;
}

큐를 이용한 요세푸스 문제 해결과정 실제로 해보기

초기 상태:
큐: [1, 2, 3, 4, 5, 6, 7]

1회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 1 -> 뒤로 이동
    큐: [2, 3, 4, 5, 6, 7, 1]
  • 2 -> 뒤로 이동
    큐: [3, 4, 5, 6, 7, 1, 2]
    세 번째 요소 3을 제거:
    큐: [4, 5, 6, 7, 1, 2]

2회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 4 -> 뒤로 이동
    큐: [5, 6, 7, 1, 2, 4]
  • 5 -> 뒤로 이동
    큐: [6, 7, 1, 2, 4, 5]
    세 번째 요소 6을 제거:
    큐: [7, 1, 2, 4, 5]

3회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 7 -> 뒤로 이동
    큐: [1, 2, 4, 5, 7]
  • 1 -> 뒤로 이동
    큐: [2, 4, 5, 7, 1]
    세 번째 요소 2를 제거:
    큐: [4, 5, 7, 1]

4회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 4 -> 뒤로 이동
    큐: [5, 7, 1, 4]
  • 5 -> 뒤로 이동
    큐: [7, 1, 4, 5]
    세 번째 요소 7을 제거:
    큐: [1, 4, 5]

5회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 1 -> 뒤로 이동
    큐: [4, 5, 1]
  • 4 -> 뒤로 이동
    큐: [5, 1, 4]
    세 번째 요소 5를 제거:
    큐: [1, 4]

6회차:

첫 번째 ( k-1 )개의 요소를 뒤로 이동:

  • 1 -> 뒤로 이동
    큐: [4, 1]
  • 4 -> 뒤로 이동
    큐: [1, 4]
    세 번째 요소 4를 제거:
    큐: [1]

결국, 최후의 생존자는 1이다.

배열 vs 큐: 요세푸스 문제 해결 비교

항목 배열
구현 복잡도 상대적으로 복잡함 상대적으로 단순함
시간 복잡도 $O(n^2)$ $O(n × k)$
공간 복잡도 $O(n)$ $O(n)$
효율성 대규모 입력에 비효율적 대규모 입력에 효율적
알고리즘 이해도 배열의 원형 처리와 인덱스 조정 필요 큐의 기본 연산만으로 구현 가능
유연성 고정된 크기의 배열 필요 동적 크기 조정이 가능한 큐 활용 가능

'프로그래밍, 코딩 > 스터디' 카테고리의 다른 글

시간복잡도  (0) 2024.07.13

본격적으로 알고리즘을 공부하려고 한다.

사용 책 : 코딩 테스트 합격자 되기 C++편

강의 주소 : https://www.inflearn.com/course/cpp-EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%A9%EA%B2%A9/dashboard

 

[지금 무료] 코딩 테스트 합격자 되기 - C++ 강의 | dremdeveloper - 인프런

dremdeveloper | 코딩 테스트 합격을 위한 C++ 강의, 책 없이도 가능! 저자와 직접 소통 가능한 커뮤니티 제공!, [사진]여기에 문의 하세요https://open.kakao.com/o/gX0WnTCf📘 코딩 테스트 합격자 되기 - C++편

www.inflearn.com

 

 

강의 유튜브 주소 : https://www.youtube.com/watch?v=6oDZgi_7ao0&t=534s


알고리즘이란 무엇인가?

  • 유한한 수의 규칙에 따라 구별 가능한 기호들을 조작하여 입력 정수에서 출력 정수를 생성하기 위한
    일반화된 작업이다.
  • 함수라고도 볼 수 있을 것 같다.($\because$ 입력에 따른 예측가능한 출력)

그렇다면 좋은 알고리즘이란 무엇인가?

  • 작동하는 데 걸리는 시간을 측정하여 그 시간이 짧으면 된다.
  • 작동하는 실행된 연산횟수를 측정하여 그 시간이 짧으면 된다.

코딩테스트에서는 알고리즘의 성능을 연산횟수로 측정한다.

  • 그렇다면 연산횟수를 매번 정확하게 측정할까?
    • 그렇지 않다. '대충 어느정도이다'라고 까지만 알면 된다.
    • 구체적으로 어떻게 대충 말하는 것일까?(대충의 기준은?)
      • 마치, 수학에서 무한대로 극한을 취할 때와 같이 나보다 차수가 낮은 애들은 무시한다.
      • $$
        x! > 2^x(지수함수) > x^n > \cdots > x^3 > x^2 > x > x \log x > \log x(로그함수) > \text{상수}
        $$
      • 따라서, 다항식의 경우 최고차항의 차수가 시간복잡도이다.

자주 보이는 복잡도는 다음과 같다.

이를 어떻게 활용하는가?

  • 허용된 시간복잡도를 보고 어떤 알고리즘을 선택할지 결정하는 데 이용된다.
    • 즉, 문제에서 요구하는 성능을 만족하는 알고리즘을 택해야 한다.

메서드의 시간 복잡도를 파악하는 것이 중요하다.

  • 동일한 동작처럼 보이지만, 시간복잡도가 다를 수도 있다.
    • 기본 컨테이너 vs Unordered 컨테이너
    • vector와 dictionary/set에서 특정 key 존재 유무 확인하기
    • vect와 list에서 특정 위치 원소 가져오기

실전예시

  • 추후 계속 추가

'프로그래밍, 코딩 > 스터디' 카테고리의 다른 글

스택/큐  (1) 2024.07.27

+ Recent posts