Development record of developer who study hard everyday.

레이블이 코딩테스트인 게시물을 표시합니다. 모든 게시물 표시
레이블이 코딩테스트인 게시물을 표시합니다. 모든 게시물 표시
, , , ,

코딩테스트 불길한 수열 문제

 코딩테스트 불길한 수열 문제

코딩테스트

문제

4, 13, 413, 134와 같이 숫자가 13과 4를 이용해서 만들 수 있는 수를 불길한 수라고 한다. 그리고 불길한 수가 오름차순으로 나열된 수열을 불길한 수열이라고 한다. 예를 들어 불길한 수열은 다음과 같이 나열될 수 있다.

ex) S = {4, 13, 44, 134, 413, 444, 1313......}

n이 매개변수로 주어질 때, 불길한 수열에서 n번째 불길한 수를 구하시오.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
from heapq import heappush, heappop, heapify
 
def solution2(n):
    q = [4,13]
    heapify(q)
  
    while n:
        n-=1
        v=heappop(q)
        heappush(q,v*10+4)
        heappush(q,v*100+13)
      
    return v
 
print(solution2(100))
cs



핵심은 heap을 사용한다.

heapq 모듈은 이진트리 기반의 최소 힙 자료구조를 제공한다.

min heap을 사용하면 원소들이 항상 정렬된 상태로 추가되고 삭제되며, min heap에서 

가장 작은 원소는 언제나 인덱스 0이다.

내부적으로 min heap 내의 모든 원소(k)는 2k+1, 2k+2보다 작거나 같다.


최소 힙 생성


heapq 모듈은 파이썬의 보통 리스트를 마치 최소 힙처럼 다룰 수 있도록 도와준다.

그래서 빈 리스트를 생성한 다음에 heapq 모듈의 함수를 호출할 때마다 이 리스트를 인자로 넘긴다.

heap = []

from heapq import heappush

heappush(heap, 4)
heappush(heap, 1)
heappush(heap, 7)
heappush(heap, 3)
print(heap)

[1, 3, 7, 4]

결과값처럼 가장 작은 1이 인덱스 0에 위치하며, 인덱스 1(=k)에 위치한 3은 인덱스

3(=2k+1)에 위치한 4보다 크므로 힙의 공식을 만족합니다.

내부적으로 이진트리에 원소를 추가하는 heappush() 함수는 O(logN)의 시간복잡도를 가진다.

힙에서 원소 삭제

from heapq import heappop

print(heappop(heap))
print(heap)

1
[3, 4, 7]

heapq 모듈의 heappop() 함수를 이용하여 힙에서 원소를 삭제할 수 있다.

원소를 삭제할 대상 리스트를 인자로 넘기면, 가장 작은 원소를 삭제 후에 그 값을 리턴한다.

힙에서의 원소 삭제 역시 O(logN)의 시간복잡도를 가진다.

기존 리스트를 힙으로 변환

from heapq import heapify

heap = [4, 1, 7, 3, 8, 5]
heapify(heap)
print(heap)

[1, 3, 5, 4, 8, 7]

heapq 모듈의 heapify()라는 함수를 사용하면 리스트를 힙으로 만들 수 있다.

heapify() 함수의 성능은 O(N)이다.

nums = [4, 1, 7, 3, 8, 5]

heap = nums[:]
heapify(heap)

print(nums)
print(heap)

[4, 1, 7, 3, 8, 5]
[1, 3, 5, 4, 8, 7]

이때, 주의할 점은 새로운 리스트를 반환하는 것이 아니라 인자로 넘긴 리스트를 직접 변경한다.

최대 힙

heapq 모듈은 최소 힙을 기본으로 동작하기 때문에 최대 힙으로 활용하려면 약간의 요령이 필요하다.

바로 힙에 튜플을 원소로 추가하거나 삭제하면, 튜플 내에서 맨 앞에 있는 값을 기준으로 최소 힙이 구성되는 원리를 이용하면된다.

다시 말해, (우선순위, 값) 구조의 튜플을 힙에 추가하거나 삭제하면 된다.

그리고 힙에서 값을 읽어올 때는 각 튜플에서 인덱승 1에 있는 값을 취하면된다.

from heapq import heappush, heappop

nums = [4, 1, 7, 3, 8, 5]
heap = []

for num in nums:
  heappush(heap, (-num, num))  # (우선 순위, 값)

while heap:
  print(heappop(heap)[1])  # index 1

8
7
5
4
3
1


n번째 최소값/최대값

최소 힙이나 최대 힙을 사용하면 n번째로 작은 값이나 큰 값을 효과적으로 구할 수 있다.

from heapq import heappush, heappop

def nth_smallest(nums, n):
    heap = []
    for num in nums:
        heappush(heap, num)

    nth_min = None
    for _ in range(n):
        nth_min = heappop(heap)

    return nth_min

print(nth_smallest([4, 1, 7, 3, 8, 5], 3))

4

혹은

from heapq import nsmallest

nsmallest(3, [4, 1, 7, 3, 8, 5])[-1]

nsmallest() 함수를 사용한다.

nsmallest() 함수는 주어진 리스트에서 가장 작은 n개의 값을 담은 리스트를 반환한다.

그 리스트의 마지막 값이 n번째 작은 값이 된다.

반대로 n번째 최대값을 구할 때는 nlargest()함수를 사용한다.

from heapq import nlargest

nlargest(3, [4, 1, 7, 3, 8, 5])[-1]




Share:
Read More
, , , , , ,

파이썬(Python) 선택정렬, 삽입정렬, 퀵정렬

 파이썬(Python) 선택정렬, 삽입정렬, 퀵정렬

알고리즘 파이썬

1. 선택정렬

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def select_sort(array):
  for i in range(len(array)):
    min_index = i
    for j in range(i + 1, len(array)):
      if array[min_index] > array[j]:
        min_index = j
    array[min_index], array[i] = array[i], array[min_index]

  return array
  
array = [3, 4, 7, 2, 1, 0, 8]

print(select_sort(array))

1 => 선택정렬 구현한 함수: 선택정렬 수행 후 정렬된 배열 반환

3 => 가장 작은 값의 인덱스 초기화

5 => 가장 작은 값의 인덱스 저장

7 => 가장 작은 값이 앞에 오도록 값 교체

9 => 선택정렬이 완료된 배열 반환


2. 삽입정렬

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def insert_sort(array):
  for i in range(1, len(array)):
    for j in range(i, 0, -1):
      if array[j] < array[j-1]:
        array[j], array[j-1] = array[j-1], array[j]
      else:
        break;
        
  return array
    
array = [3, 4, 7, 2, 1, 0, 8]

print(insert_sort(array))

1 => 삽입정렬 정의한 함수: 정렬된 배열을 반환

2 => 맨 처음 값은 정렬되었다고 가정

3 => 정렬하려는 요소보다 작은 값을 만날 때까지 값 교환

6 => 정렬하려는 요소보다 작은 값을 만나면 for문 돌지않는다.

6번줄 때문에 삽입정렬은 이미 정렬된 값이 많은 배열에서 효과적이다!


3. 퀵정렬

 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
def quick_sort1(array, start, end):
  if start >= end: return

  pivot = start
  left = start + 1
  right = end

  while left <= right:
    while left <= end and array[left] <= array[pivot]:
      left += 1
    while right > start and array[right] >= array[pivot]:
      right -= 1

    if left > right:
      array[pivot], array[right] = array[right], array[pivot]
    else: 
      array[left], array[right] = array[right], array[left]

  quick_sort1(array, start, right -1)
  quick_sort1(array, right + 1, end)
    
  
array = [3, 4, 7, 2, 1, 0, 8]
quick_sort1(array, 0, len(array) - 1)
print(array)

1 => 퀵정렬을 정의한 함수: 반환값은 없음

4 => pivot은 항상 맨 처음 요소로 시작

8 => left와 right가 교차할 때까지 while문 수행

9 => left가 pivot 값보다 클 때까지 인덱스 증가

11 => right가 pivot 값보다 작을 때까지 인덱스 감소

14 => left와 right가 교차한 상태면 pivot과 right 값 교환

16 => left와 right가 교차한 상태가 아니면 left와 right 값 교환


이번에는 파이썬의 장점을 극대화한 퀵정렬 소스를 살펴보자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def quick_sort(array):
  #condition for return
  if len(array) <= 1 : return array
  
  pivot = array[0]
  tail = array[1:]

  left_side = [x for x in tail if x < pivot]
  right_side = [x for x in tail if x > pivot]

  return quick_sort(left_side) + [pivot] + quick_sort(right_side)

print(quick_sort(array))

1 => 퀵정렬을 정의한 함수: 인덱스가 한개일 때, 배열 반환

2 => 재귀 탈출 조건

5 => 퀵정렬의 첫번째 pivot은 항상 첫번째 요소

6 => 첫번째 요소를 제외한 나머지 값의 배열

8 => pivot보다 작은 값들을 모은 left_side 배열

9 => pivot보다 큰 값들을 모은 right_side 배열

11 => left_side와 right_side에 다시 퀵정렬을 수행하여 pivot과 연결한다.


Share:
Read More
, , , ,

파이썬 너비우선탐색(BFS) 알고리즘 소개

 너비우선탐색(BFS) 알고리즘 소개

안드로이드 블로그


위와 같은 그래프가 있을 때, 너비 우선 탐색(BFS)을 수행하는 코드를 파이썬으로 작성해보자.

BFS는 너비 우선 탐색이라고 부르며, 그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘이다.

BFS는 큐 자료구조를 이용하며, 구체적인 동작 과정은 다음과 같습니다.

  1. 탐색 시작 노드를 큐에 삽입하고 방문처리를 합니다.
  2. 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리를 합니다.
  3. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복합니다.


 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
from collections import deque

def bfs(graph, start, visited):
  queue = deque([start])
  visited[start] = True

  while queue:
    v = queue.popleft()
    print(v, end = ' ')

    for i in graph[v] :
      if not visited[i]:
        queue.append(i)
        visited[i] = True

graph = [
  [],
  [2, 3, 8],
  [1, 7],
  [1, 4, 5],
  [3, 5],
  [3, 4],
  [7],
  [2, 6, 8],
  [1, 7]
]

visited = [False] * 9

bfs(graph, 1, visited)


Share:
Read More
, , , , ,

파이썬 깊이우선탐색(DFS) 알고리즘

 깊이우선탐색(DFS) 알고리즘 with 파이썬

안드로이드 블로그

DFS는 깊이 우선 탐색이라고 부른다.

그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘이다.

스택자료구조나 재귀함수를 이용하며 구체적인 동작은 다음과 같다.

  • 탐색 시작 노드를 스택에 삽입하고 방문처리를 한다
  • 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문처리를 한다. 방문하지 않은 노드가 없으면 스택에서 최상단 노드를 꺼낸다.
  • 2번의 과정을 수행할 수 없을 때까지 반복한다.

dfs 그래프 예시



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def dfs(graph, v, visited):
  # 현재 노드를 방문처리
  visited[v] = True
  print(v, end = ' ')
  # 현재 노드와 연결된 다른 노드를 재귀적으로 방문
  for i in graph[v]:
    if not visited[i]:
      dfs(graph, i, visited)

graph = [
  [],
  [2, 3, 8],
  [1, 7],
  [1, 4, 5],
  [3, 5],
  [3, 4],
  [7],
  [2, 6, 8],
  [1, 7]
]

visited = [False] * 9

dfs(graph, 1, visited)


그냥 기본기니까 암기해두자!!

Share:
Read More