자료구조 및 알고리즘/코딩테스트 준비

코딩테스트 준비 3편

viamemine 2024. 6. 10. 17:59
728x90
반응형
더보기
더보기

해당 게시글은 https://covenant.tistory.com/143 을 참고하여 작성했습니다.

 

이번 챕터는 문제 풀이 중간 중간에 들어가는 꼭 ! 기억해야 풀이 시간이 줄어드는 순열, 조합, 빈도계산, 덱, 우선순위 큐에 대해 알아보겠습니다. 

 

1. 순열, 조합

1-1. 순수한 방법

for문 2개를 사용해서 nC2를 구하는 방법은 다음과 같습니다.

for i in range(0, N-1): 
    for j in range(i+1, N): 
        print(i, j)

 

백준 9613번 GCD 합 문제를 풀 수 있습니다. GCD는 다음 챕터에서 살펴볼 것입니다.
그렇다면 nC3은? nC4는...? for문을 사용해서는 한계가 있습니다.

 

1-2. itertools을 사용한 조합

from itertools import combinations
print(list(combinations([1, 2, 3, 4], 3)))

 

combinations의 첫 번째 인자에 배열을 넣고, 두 번째 인자에는 nCm 이라면 m에 해당하는 값을 넣습니다. 출력 결과는 다음과 같습니다.

[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

 

위에서 본 순수한 방법을 combinations를 사용하면 다음과 같이 표현할 수 있습니다.

list(combinations([0, 1, 2, 3], 2))

 

 

1-3. 순열

백준 15649번 N과 M (1) 문제를 살펴보겠습니다. 순열을 활용하면 문제를 풀 수 있습니다.

from itertools import permutations

N, M = map(int, input().split())
arr = [str(i) for i in range(1, N+1)]

for e in list(permutations(arr, M)):  
    print(" ".join(e))

 

1-4. 덤

중복 순열은 다음과 같이 사용합니다.

from itertools import product

 

중복 조합은 다음과 같이 사용합니다.

from itertools import combinations_with_replacement

 

하지만 입사를 위한 코딩테스트에서 중복 순열, 중복 조합을 사용해야만 문제가 풀리는 경우는 없습니다 ! itertools에는 순열과 조합 말고 다양한게 있구나 하고 넘어가시면 됩니다. 

 

 

2. 빈도 계산

모르면 기업 코딩테스트에서 조금 고생하게 되는 부분입니다. 바로 Counter 입니다. 이번에는 바로 예제로 시작해볼까요 ? 백준 2592번 대표값 입니다. 본 문제에서는 입력으로 들어오는 10개의 정수의 평균과 최빈값을 구해야 합니다. 평균은 sum(arr) // 10을 구하면 되니 넘어가고, 최빈값은 Counter().most_common()과 같이 구합니다.

from collections import Counter 

arr = [int(input()) for _ in range(10)]
val = Counter(arr).most_common()
print(sum(arr) // 10)
print(val[0][0])
[(30, 3), (40, 2), (60, 2), (10, 1), (20, 1), (50, 1)]

 

Counter(arr).most_common()을 수행했을 때 결과는 위와 같습니다. 리스트 안에 튜플이 있습니다. 튜플의 0번째 인덱스에는 arr의 숫자, 1번째 인덱스에는 arr 등장 빈도수가 들어 있습니다.

 

 

3. 힙

3-1. 최소힙, 최대힙

파이썬에서는 최소힙으로 구현되어 있습니다. 최소힙을 사용해서 저장하면 저장된 값 중 최솟값이 0번 인덱스, 즉 이진트리의 루트에 위치합니다.

import heapq
 
heap = []  
heapq.heappush(heap, 3)  
heapq.heappush(heap, 1)  
heapq.heappush(heap, 10)  
heapq.heappush(heap, 5)  
heapq.heappush(heap, 8)

 

파이썬에서 힙을 사용해서 3, 1, 10, 5, 8을 각각 힙에 넣었습니다. 출력을 해보면 다음과 같습니다.

[1, 3, 10, 5, 8]

 

     1 <--- Root 
   /   \ 
  3     10 
 / \   
5  8

 

힙에 위와 같이 저장되어 있습니다. 이진 트리의 루트에는 가장 작은 값이 저장되는 것을 확인할 수 있습니다. 

print(len(heap))
>> 5

 

힙의 길이는 리스트, 덱과 동일하게 len()을 이용합니다.

 

heapq.heappop(heap)

 

힙의 루트 원소를 제거하려면 heappop()을 이용합니다. 이 때 pop되는 값이 리턴됩니다.

import sys, heapq
heap = []

# 입력이 최대 10만이 들어올 수 있으므로 입력 가속을 하는게 좋습니다.
for i in range(int(sys.stdin.readline())):
    x = int(sys.stdin.readline()) * -1
    # 입력되는 값에 -1을 곱하여 최대힙을 구현할 수 있습니다

    if x == 0:
        # 배열에서 가장 큰 값을 출력하고, 그 값을 배열에서 제거
        if len(heap) == 0:
            print(0) # 비어있는 경우인데 가장 큰 값을 출력하라고 한 경우
        else:
            print(heapq.heappop(heap) * -1)

    else: # 배열에 x 값을 삽입하는 연산
        heapq.heappush(heap, x)

 

heapq를 사용하게 되면 최소힙을 구현하게 됩니다. 따라서 최대힙과는 다른 방식입니다. 그래서 음수 값을 사용해서 우선순위를 이용하여 최대힙처럼 응용한 것입니다. 최소힙의 원리를 이용하여 최대힙을 구현할 수 있습니다. 힙 공부를 마치며 다음 문제를 추천합니다.


4. 덱

덱은 데크라도고 하며 double-ended queue의 약자입니다. 선입선출의 개념인 FIFO와 더불어 나중에 온 값을 먼저 처리하는 LIFO 연산도 가능합니다. 즉, 양방향에서 데이터를 처리할 수 있습니다. collections.deque 즉 파이썬으로 stack 문제를 풀려면 deque를 사용해야 합니다. 

from collections import deque 
deque = deque() # 덱 초기화

 

덱을 사용하는 방법은 위와 같습니다.

deq = deque([i for i in range(1, 5)])

 

deq에는 [1, 2, 3, 4]가 저장됩니다.

deq.appendleft(10)

 

덱의 왼쪽에 값을 추가하려면 .appendleft(value)를 하면 됩니다. 실행을 마치면 덱에는 [10, 1, 2, 3, 4]가 저장됩니다.

deq.append(-10)

 

덱의 오른쪽에 값을 추가하려면 .append(value)를 씁니다. 덱에는 [10, 1, 2, 3, 4, -10]이 저장됩니다.

 

print(deq.pop())

 

덱의 오른쪽에서 값을 제거하려면 .pop()을 씁니다. pop되는 값이 리턴되며 비어있는 덱인 경우 indexError: pop from an empty deque 문구를 만날 수 있습니다. 따라서 빈 덱에 pop을 수행하는지 유의해야 합니다.

print(deq.popleft())

 

덱의 왼쪽에서 값을 제거하려면 .popleft()를 씁니다. 역시 pop되는 값이 리턴되며 비어있는 덱이면 오류를 만납니다.

len(dep)

 

덱의 길이를 구하는 방법은 리스트와 동일하게 len()을 사용합니다. 

deq.rotate(-1)

 

덱을 회전하는 방법이 있습니다. .rotate를 이용하면 됩니다. 인자로 들어간 값만큼 회전하며, 음수이면 좌측으로 회전합니다. .rotate를 활용해서 백준 2164번 카드2 문제를 풀어보세요. 

기본 덱 사용법을 살펴보았습니다. 다음 문제를 추천합니다. 

 

5. 우선순위 큐

우선순위 큐는 기업 코딩테스트에 자주 나오지 않습니다. 하지만 한 번 언급하고 넘어가겠습니다.

from queue import PriorityQueue
que = PriorityQueue()

 

위와 같이 우선순위 큐를 만들 수 있습니다.

que.put(4)
que.put(10)
que.put(2)

for i in range(len(que.queue)):  
    print(que.queue[i], end=" ")

 

.put()을 이용하여 우선순위 큐에 값을 넣습니다. .append()가 아니니 유의하세요 !

2 10 4

 

출력 결과는 위와 같습니다.

que.get()

 

우선순위 큐에 저장된 값은 .get()을 이용해서 제거합니다. 제거한 값이 리턴됩니다. 본 예제에서는 2가 제거됩니다. 

 

 

* 덤

문제를 풀다보면 실행시간을 알고 싶을 때가 있습니다. 이럴 때 다음과 같이 구현해주세요.

import time
start_time = int(round(time.time() * 1000))
some_func()
end_time = int(round(time.time() * 1000))
print('some_func()의 실행시간 : %d(ms)' % (end_time - start_time))

 

some_func()가 실행 시간을 ms 단위로 출력해줍니다.

728x90

'자료구조 및 알고리즘 > 코딩테스트 준비' 카테고리의 다른 글

다이나믹 프로그래밍  (0) 2024.06.12
정렬 ・ 이진 탐색  (0) 2024.06.12
주요 라이브러리 ・DFS ・BFS  (0) 2024.06.12
코딩테스트 준비 2편  (0) 2024.06.10
코딩테스트 준비 1편  (2) 2024.06.10