다익스트라 알고리즘이란?

 

그래프에서 여러 개의 노드가 존재할 때, 특정한 노드에서 출발하여 다른 노드로 가는 각각의 최단 경로를 구해주는 알고리즘.

주로 최단 경로를 찾는 문제에서 자주 사용하는 알고리즘이다.

 

 

 

* 다익스트라 알고리즘 동작 원리

 

  1. 출발 노드를 설정
  2. 각 노드의 최단 거리를 기록할 테이블 정의 후, 최단 거리 테이블을 초기화
  3. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택 
  4. 해당 노드를 거쳐 다른 노드로 가는 간선 비용을 계산하여 최단 거리 테이블을 업데이트
  5. 위 과정에서 3, 4번을 반복

 

(단, 음의 간선이 존재하지 않을 때 즉, 간선(edge)의 값이 0 이상의 양수일 때 다익스트라 알고리즘이 정상적으로 동작)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

* 동작 원리 상세 설명

 

1. 출발 노드 설정 및 최단 거리 테이블 초기화

 

 

 

 

2. 시작 노드인 1번 노드에 인접한 노드들의 거리를 갱신 (최단 거리 테이블 갱신)

 

 

 

 

3.

3-1) 방문하지 않은 노드들 중에서 최단 거리가 가장 짧은 노드를 선택 > 4

3-2) 1번 노드 방문 처리 및 새롭게 변경된 시작 노드(4)를 기준으로 인접한 노드들의 거리를 갱신

 

3번 노드의 경우, min(5,1+3) 이라고 되어 있다. 여기서 5는 시작 노드 1번에서 3번 노드로 한 번에 갈 수 있는 거리였다. 그리고 1+3 이라고 되어 있는 부분은 시작노드 1번에서 4번 노드로 가는 거리비용 1  4번 노드에서 3번 노드로 가는 거리비용 3을 의미한다. 이 중 거리비용을 최소화하는 경로는 시작노드인 1번에서 3번으로 다이렉트로 한 번에 가는 것보다 4번 노드를 거쳐 3번 노드로 가는 1 -> 4 -> 3 으로 가는 경로가 최단 경로이다.

 

이와 같이 간선 비용을 계산하여 최단 거리 테이블을 업데이트 해준다.

 

 

 

 

 

 

 

4. 가장 마지막으로 갱신된 최단 거리 테이블 기준으로, 방문하지 않은 노드 중 가장 거리가 작은 노드를 다음 탐색 노드로 선정 (거리 비용이 동일할 경우에는 노드 번호가 작은 것부터 탐색)

 

새로 변경된 시작 노드 2에 인접한 노드 (3, 4)의 거리를 갱신한다. 3번 노드의 경우는, 기존의 최단 경로 값인 4와 1번에서 2번 노드로 가는 거리비용 2 와 2번 노드에서 3번 노드로 가는 거리비용 3 을 의미한다. 이때는 기존 최단 경로값이 더 최소값이기 때문에 최단 거리 테이블을 갱신하지 않는다. 4번 노드도 이와 마찬가지로 갱신되지 않는다.

 

 

그리고 방문하지 않은 노드 (3, 5, 6) 중 가장 거리가 작은 5번 노드를 다음 탐색 노드로 선정한다.

이런식으로 모든 노드를 한 번씩 탐색하여 다익스트라 알고리즘 과정을 최종적으로 완료한다.

 

결국 다익스트라 알고리즘은 방문하지 않은 노드 중에서 가장 거리가 짧은 노드를 선택하는 과정을 반복하는 게 핵심

 

 

 

 

 

 

 

 

 

 

 

 

 

 

* 다익스트라 알고리즘 구현 방법

 

방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택하기 위한 방법으로는 순차 탐색 & 최소힙 2가지가 있다.

 

 

1) 순차 탐색

O(N^2)의 시간 복잡도를 가진다. 총 O(N)번에 걸쳐 가장 짧은 노드를 매번 선형 탐색 해야하고, 현재 노드와 연결된 노드를 매번 일일히 확인해야 하기 때문이다. 따라서 전체 노드의 수가 5,000개 이하라면 순차탐색 방법을 써도 괜찮지만, 그 이상이라면 힙을 사용하는 개선된 다익스트라 알고리즘을 이용하는 것이 좋다.

 

import sys

input = sys.stdin.readline
INF = int(1e9) # 무한을 의미하는 값으로 10억 설정

# 노드의 개수, 간선의 개수를 입력받기
n, m = map(int, input().split())
# 시작 노드 번호를 입력받기
start = int(input())
# 노드 방문 여부 확인을 위한 테이블
visited = [False]*(n+1)
# 최단 거리 테이블
distance = [INF]*(n+1)

# 노드 연결정보
graph = [[] for i in range(n+1)]

for _ in range(m):
  # a번 노드에서 b번 노드로 가는 비용 c
  a,b,c = map(int, input().split())
  graph[a].append((b,c))

# 방문하지 않은 노드 중 가장 최단거리가 짧은 노드의 번호를 반환
def get_smallest_node():
  min_value = INF
  index = 0
  for i in range(1, n+1):
    if distance[i] < min_value and not visited[i]:
      min_value = distance[i]
      index = i
  return index




# 다익스트라 알고리즘
def dijkstra(start):
  # 시작 노드
  distance[start] = 0
  visited[start] = True
  
  # 출발노드와 인접한 노드들에 대해 최단 거리 테이블 갱신
  for j in graph[start]:
    distance[j[0]] = j[1]

  # 모든 노드에 대해 반복
  for i in range(n-1):
    # 현재 최단거리가 가장 짧은 노드를 꺼내서 방문처리
    now = get_smallest_node()
    visited[now] = True
    # 선택된 노드와 연결된 다른 노드를 확인
    for j in graph[now]:
      # 선택된 노드를 통해 가는 비용을 다시 계산
      # 선택된 노드의 비용 + 연결된 노드로 가는 비용
      cost = distance[now] + j[1]
      # 선택된 노드를 거쳐서 가는 비용이 더 짧은 경우
      if cost<distance[j[0]]:
        distance[j[0]] = cost # 최단거리 테이블 갱신

 

 

 

2) 최소힙 (우선순위 큐 자료구조 활용)

파이썬의 heapq 라이브러리는 원소로 튜플을 받으면 튜플의 첫 번째 원소를 기준으로 우선순위 큐를 구성한다. 따라서 (거리, 노드 번호) 순서대로 튜플 데이터를 구성해 우선순위 큐에 넣으면 거리순으로 정렬된다. 이를 통해 거리가 가장 짧은 노드를 선택한다. 힙에서 노드를 꺼냈는데 만약 해당 노드를 이미 처리한적이 있다면 무시하고 아직 처리하지 않은 노드에 대해서만 처리한다.

 

import heapq
import sys

input = sys.stdin.readline # 빠른 입력
INF = int(1e9) # 무한을 의미하는 값으로 10억 설정

# 노드의 개수, 간선의 개수를 입력받기
n, m = map(int, input().split())
# 시작 노드 번호를 입력받기
start = int(input())
# 최단 거리 테이블
distance = [INF]*(n+1)

# 노드 연결정보
graph = [[] for i in range(n+1)]

for _ in range(m):
  # a번노드에서 b번 노드로 가는 비용c
  a,b,c = map(int, input().split())
  graph[a].append((b,c))






# 다익스트라 알고리즘(최소힙 이용)
def dijkstra(start):
  q=[]
  # 시작 노드
  heapq.heappush(q, (0,start))
  distance[start] = 0

  while q:
    # 가장 최단거리가 짧은 노드에 대한 정보 꺼내기
    dist, now = heapq.heappop(q)

    # 이미 처리된 노드였다면 무시
    # 별도의 visited 테이블이 필요없이, 최단거리 테이블을 이용해 방문 여부 확인
    if distance[now] < dist:
      continue
    # 선택된 노드와 인접한 노드를 확인
    for i in graph[now]:
      cost = dist + i[1]
      # 선택된 노드를 거쳐서 이동하는 것이 더 빠른 경우
      if cost < distance[i[0]]:
        distance[i[0]] = cost
        heapq.heappush(q, (cost,i[0]))

 

 

 

개선된 다익스트라 알고리즘의 시간 복잡도는 O(ElogV)이다. 이때 E는 간선의 개수, 는 노드의 개수를 의미한다.

 

간선의 개수 E가 시간 복잡도에 관여하는 이유로는,

위 소스코드를 보면 우선순위 큐에서 꺼낸 노드를 탐색할 때, 해당 노드와 연결된 노드들만 탐색하므로 최악의 경우라도 총 간선의 개수인 E만큼을 반복하게 된다. 따라서 E개의 간선을 힙에 넣었다 빼는 것으로 볼 수 있으므로 O(ElogE)가 되게 된다. (힙에서 삽입, 삭제 시간복잡도는 O(logN)이다.)

 

logE < log(V^2)이고, log(V^2)은 2logV이기 때문에 O(logV)로 표현할 수 있다. 따라서, 다익스트라 알고리즘 전체 시간 복잡도를 O(ElogV) 로 표현할 수 있다.

'CS > 알고리즘' 카테고리의 다른 글

BFS & DFS  (0) 2022.08.21
이분탐색(Binary Search)  (0) 2022.08.13
힙(Heap)정렬  (0) 2022.08.06
선택 정렬  (0) 2022.07.30

 

 

 

BFS (Breadth First Search)

 

너비 우선 탐색이라고 부르며, 가까운 노드부터 탐색하는 알고리즘이다.

BFS는 큐(Queue) 자료구조를 이용하며, 동작 방식은 다음과 같다.

  • 탐색시작 노드를 큐에 저장(q.push()) 후 *방문 처리
  • 큐에서 노드를 꺼내(q.pop()) 해당 노드의 인접 노드들을 큐에 저장 후 방문 처리
  • 큐가 빌 때까지 반복

여기서 *방문 처리란 탐색한 노드를 재방문하지 않도록 구분하는 것. 즉, 큐에 한 번이라도 삽입된 노드를 다시 삽입하지 않도록 체크하는 것을 의미

 

 

 

 

 

 

 

 

 

 

BFS 동작 과정

 

1) 시작 노드인 노드 1을 큐에 삽입하고 방문 처리

 

 

2) 큐에서 노드 1을 꺼내고 노드 1과 인접한 노드 2, 3을 큐에 삽입하고 방문 처리

 

 

3) 큐에서 노드 2를 꺼내고 노드 2와 인접한 노드 8을 큐에 삽입하고 방문 처리

(일반적으로 인접한 노드가 2개 이상인 경우에는 해당 노드들 중 번호가 낮은 노드부터 처리)

 

 

4) 큐에서 노드 3을 꺼내고 노드 3과 인접한 노드 4, 5를 큐에 삽입하고 방문 처리

 

 

5) 큐에서 노드 8을 꺼내고 노드 8과 인접한 노드 6, 7을 큐에 삽입하고 방문 처리

 

 

6) 그래프 내 노드에서 방문하지 않은 노드가 없으므로 큐에서 모든 노드를 차례대로 꺼냄

 

결과적으로 노드의 탐색 순서, 즉 큐에 삽입한 순서는 다음과 같다.
1 -> 2 -> 3 -> 8 -> 4 -> 5 -> 6 -> 7

 

 

 

 

 

 

 

 

 

 

 

 

BFS 구현 코드

 

# deque 라이브러리 불러오기
from collections import deque

# BFS 메서드 정의
def bfs (graph, node, visited):
    # 큐 구현을 위한 deque 라이브러리 활용
    queue = deque([node])
    # 현재 노드를 방문 처리
    visited[node] = True
    
    # 큐가 완전히 빌 때까지 반복
    while queue:
        # 큐에 삽입된 순서대로 노드 하나 꺼내기
        v = queue.popleft()
        # 탐색 순서 출력
        print(v, end = ' ')
        # 현재 처리 중인 노드에서 방문하지 않은 인접 노드를 모두 큐에 삽입
        for i in graph[v]:
            if not (visited[i]):
                queue.append(i)
                visited[i] = True

 

 

 

 

 

 

 

 

 


DFS (Depth First Search)

 

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

DFS는 스택 자료구조를 이용하며, 동작 방식은 다음과 같다.

  • 현재 노드 방문처리​
  • 현재 노드와 연결된 다른 노드들 방문
  • 해당 노드를 방문하지 않았다면 재귀적으로 dfs 함수 실행

 

 

 

 

 

 

 

 

 

DFS 동작 과정

 

1) 시작 노드인 노드 1을 스택에 삽입하고 방문 처리

 

 

2) 스택 내 최상단 노드인 노드 1에 인접한 노드는 2, 3이 있음. 번호가 낮은 노드 2를 스택에 삽입하고 방문 처리

 

 

3) 스택 내 최상단 노드인 노드 2에 인접한 노드 8을 스택에 삽입하고 방문 처리

 

 

4) 스택 내 최상단 노드인 노드 8에 인접한 노드는 6과 7이 있으며, 번호가 낮은 노드 6을 스택에 삽입하고 방문 처리

 

 

5) 스택 내 최상단 노드인 노드 6에 인접하며 방문하지 않은 노드 7을 스택에 삽입하고 방문 처리

 

 

6) 최상단 노드인 노드 7에 인접하며 방문하지 않은 노드가 없으므로 스택에서 노드 7을 꺼냄

 

 

7) 최상단 노드인 노드 6에 인접하며 방문하지 않은 노드가 없으므로 스택에서 노드 6을 꺼냄

 

 

8) 마찬가지로 노드 8, 2도 위와 같은 과정을 거친 후, 노드 1에 인접하면서 방문 이력이 없는 노드 3을 스택에 삽입하고 방문 처리

 

 

9) 노드 3에 인접하면서 방문하지 않은 노드는 노드 4와 5가 있지만, 번호가 낮은 노드 4를 스택에 삽입하고 방문 처리

 

 

10) 노드 4에 인접한 노드 5를 스택에 넣고 방문 처리

 

 

11) 방문하지 않은 노드가 없기 때문에 스택에서 모든 노드를 차례대로 꺼냄

결과적으로 노드의 탐색 순서, 즉 스택에 삽입한 순서는 다음과 같다.

1 -> 2 -> 8 -> 6 -> 7 -> 3 -> 4 -> 5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DFS 구현 코드

 

# DFS 메서드 정의
def dfs (graph, node, visited):
    # 해당 노드를 방문 처리
    visited[node] = True
    # 탐색 순서 출력
    print(node, end = ' ')
    # 한 노드로부터 인접한 다른 노드를 재귀적으로 방문 처리
    for i in graph[node]:
        if not visited[i]:
            dfs(graph, i, visited)

'CS > 알고리즘' 카테고리의 다른 글

다익스트라(Dijkstra) 알고리즘  (0) 2022.08.27
이분탐색(Binary Search)  (0) 2022.08.13
힙(Heap)정렬  (0) 2022.08.06
선택 정렬  (0) 2022.07.30

이분탐색(=이진탐색)이란?

 

정렬되어 있는 배열에서 탐색 범위를 절반씩 좁혀가며 데이터를 탐색하는 방법

 

  • 이분 탐색은 배열 내부의 데이터가 정렬되어 있어야만 사용할 수 있는 알고리즘
  • 정렬된 리스트에만 사용할 수 있다는 단점이 있지만, 검색이 반복될 때마다 검색 범위가 절반으로 줄기 때문에 속도가 빠르다는 장점 존재
  • 변수 3개(start, end, mid)를 사용하여 탐색. 찾으려는 데이터중간점 위치(mid)에 있는 데이터를 반복적으로 비교해서 원하는 데이터를 찾는 것이 이진 탐색의 과정

 

 

 

 

 

 

 

 

 

 

 

 

이분탐색 과정

1. 배열의 중간 값(mid)을 가져옴

 

2. 중간 값과 찾으려는 데이터 값을 비교

 

3. 중간 값이 찾고자 하는 데이터와 같다면 종료 (mid = key)

   ▷ 중간 값보다 찾고자 하는 데이터 값이 크다면, 중간값 기준 배열의 오른쪽 구간을 대상으로 탐색 (mid < key)

   ▷ 중간 값보다 찾고자 하는 데이터 값이 작다면, 중간값 기준 배열의 왼쪽 구간을 대상으로 탐색 (mid > key)

 

4. 값을 찾을 때까지 반복

 

 

 

 

 


예시) 찾고자 하는 key 데이터 = 32

 

1) low, mid, high 값 설정

 

 

 

2) Mid < key 이므로, Mid값을 기준으로 오른쪽 구간을 탐색 범위로 설정

 

 

 

3) 변경된 low, high값에 기반해 Mid 값 다시 설정

 

 

 

4) 이번에는 Mid > key 이므로, Mid 값을 기준으로 왼쪽 구간을 탐색 범위로 설정

 

 

 

5) Mid 값 다시 설정

 

 

6) 찾고자 하는 값 (32)와 Mid 값이 동일하므로, 데이터를 반환

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이분탐색 코드

 

1) 반복문 이용

def binary_search(array, target, start, end):
    while start <= end:
        mid = (start + end) // 2

        # 원하는 값 찾은 경우 인덱스 반환
        if array[mid] == target:
            return mid
        # 원하는 값이 중간점의 값보다 작은 경우 왼쪽 부분(절반의 왼쪽 부분) 확인
        elif array[mid] > target:
            end = mid - 1
        # 원하는 값이 중간점의 값보다 큰 경우 오른쪽 부분(절반의 오른쪽 부분) 확인
        else:
            start = mid + 1

 

 

 

 

 

2) 재귀함수 이용

def binary_search(array, target, start, end):
    if start > end:
        return None
    mid = (start + end) // 2

    # 원하는 값 찾은 경우 인덱스 반환
    if array[mid] == target:
        return mid
    # 원하는 값이 중간점의 값보다 작은 경우 왼쪽 부분(절반의 왼쪽 부분) 확인
    elif array[mid] > target:
        return binary_search(array, target, start, mid - 1)
    # 원하는 값이 중간점의 값보다 큰 경우 오른쪽 부분(절반의 오른쪽 부분) 확인
    else:
        return binary_search(array, target, mid + 1, end)

 

 

 

 

 

 

 

 

 

 

 

 

이분탐색 시간복잡도

O(logN)


단계마다 탐색 범위를 반으로 나누므로 위와 같은 시간 복잡도를 가짐

 

EX) 처음 데이터의 개수가 32개라면, 이론적으로 1단계를 거치면 약 16개의 데이터가 남고, 2단계에서 약 8개, 3단계에서 약 4개의 데이터만 남게 됨.
즉, 이분 탐색은 탐색 범위를 절반씩 줄이고, O(logN)의 시간 복잡도를 보장

'CS > 알고리즘' 카테고리의 다른 글

다익스트라(Dijkstra) 알고리즘  (0) 2022.08.27
BFS & DFS  (0) 2022.08.21
힙(Heap)정렬  (0) 2022.08.06
선택 정렬  (0) 2022.07.30

자료구조 '힙(Heap)'

- 완전 이진 트리의 일종으로 우선순위 큐를 위하여 만들어진 자료구조
- 최댓값, 최솟값을 쉽게 추출할 수 있는 자료구조

- 힙의 종류

  1) 최대 힙 : 부모 노드의 키 값이 자식 노드의 키 값보다 크거나 같은 완전 이진 트리

  2) 최소 힙 : 부모 노드의 키 값이 자식 노드의 키 값보다 작거나 같은 완전 이진 트리

 

 

 

* 완전 이진 트리란?

  - 완전 이진트리는 마지막 레벨을 제외하고 모든 레벨이 완전히 채워져 있어야 함

  - 마지막 레벨은 꽉 차 있지 않아도 되지만, 노드가 왼쪽에서 오른쪽으로 채워져야 함

 

 

 

 

* 우선순위 큐(Priority Queue)란?

  - 큐는 먼저 들어오는 데이터가 먼저 나가는 FIFO(First In First Out) 형식의 자료구조

  - 우선순위 큐는 먼저 들어오는 데이터가 아닌, 우선순위가 높은 데이터가 먼저 나가는 형태의 자료구조

  - 우선순위 큐는 배열, 연결리스트, 힙 등의 자료구조를 통해 구현 가능 → '힙'으로 구현하는 것이 가장 효율적

 

 

 

 

 

 

 

 

 

 

 

 

 

 

힙 정렬

- 힙 자료구조의 특성을 이용한 정렬

- 배열을 힙 자료구조에 넣은 다음에 원소를 하나씩 빼면,

  → 최소힙일 경우는 작은 값부터 큰 값 순서대로 정렬되어서

  → 최대힙일 경우는 큰 값부터 작은 값 순서대로 정렬되어서 나옴

- 내림차순 정렬을 위해서는 최대 힙을 구성하고, 오름차순 정렬을 위해서는 최소 힙을 구성

 

 

 

 

 

 

 

 

 

 

 

 

힙 정렬 과정

  1. 정렬해야 할 N개의 요소들로 최대힙을 구성 → 내림차순 정렬
  2. 그 다음으로 한 번에 하나씩 요소를(최댓값부터) 힙에서 꺼냄
  3. 삭제되는 요소들(최댓값부터 삭제)은 값이 감소되는 순서로 정렬 [최대 힙 삭제 과정]

 

 

* 내림차순 정렬을 위한 최대 힙의 구현

  - 힙(heap)은 1차원 배열로 쉽게 구현될 수 있음
  - 정렬해야 할 N개의 요소들을 1차원 배열에 기억한 후, 최대힙 삽입을 통해 차례대로 삽입

 

 

 

 

* 최대 힙 삽입 과정

  - 힙에 새로운 요소가 들어오면, 일단 새로운 노드를 힙의 마지막 노드에 이어서 삽입
  - 새로운 노드를 부모 노드들과 교환해서 힙의 성질을 만족시킴

 

 

 

 

* 최대 힙 삭제 과정

  - 최대 힙(max heap)에서 삭제 연산은 최댓값을 가진 요소(=루트 노드)를 삭제하는 것
  - 삭제된 루트 노드에는 힙의 마지막 노드를 가져옴
  - 힙을 재구성

 

 

 

 

결론적으로...

힙 정렬이란, 힙(Heap)이라는 자료구조의 성질을 활용하여 정렬을 하는 방법을 의미한다.

먼저 입력되는 배열의 숫자들을 최대힙 또는 최소힙으로 구성시킨 후 (최소힙/최대힙 삽입 과정), 가장 상위 노드부터 출력하여(최소힙/최대힙 삭제 과정) 숫자들을 정렬한다.

 

 

 

 

 

 

 

 

 

 

 

 

힙 정렬 코드

/* 현재 요소의 개수가 heap_size인 힙 h에 item을 삽입한다. */
// 최대 힙(max heap) 삽입 함수
void insert_max_heap(HeapType *h, element item){
  int i;
  i = ++(h->heap_size); // 힙 크기를 하나 증가

  /* 트리를 거슬러 올라가면서 부모 노드와 비교하는 과정 */
  // i가 루트 노트(index: 1)이 아니고, 삽입할 item의 값이 i의 부모 노드(index: i/2)보다 크면
  while((i != 1) && (item.key > h->heap[i/2].key)){
    // i번째 노드와 부모 노드를 교환환다.
    h->heap[i] = h->heap[i/2];
    // 한 레벨 위로 올라단다.
    i /= 2;
  }
  h->heap[i] = item; // 새로운 노드를 삽입
}
// 최대 힙(max heap) 삭제 함수
element delete_max_heap(HeapType *h){
  int parent, child;
  element item, temp;

  item = h->heap[1]; // 루트 노드 값을 반환하기 위해 item에 할당
  temp = h->heap[(h->heap_size)--]; // 마지막 노드를 temp에 할당하고 힙 크기를 하나 감소
  parent = 1;
  child = 2;

  while(child <= h->heap_size){
    // 현재 노드의 자식 노드 중 더 큰 자식 노드를 찾는다. (루트 노드의 왼쪽 자식 노드(index: 2)부터 비교 시작)
    if( (child < h->heap_size) && ((h->heap[child].key) < h->heap[child+1].key) ){
      child++;
    }
    // 더 큰 자식 노드보다 마지막 노드가 크면, while문 중지
    if( temp.key >= h->heap[child].key ){
      break;
    }

    // 더 큰 자식 노드보다 마지막 노드가 작으면, 부모 노드와 더 큰 자식 노드를 교환
    h->heap[parent] = h->heap[child];
    // 한 단계 아래로 이동
    parent = child;
    child *= 2;
  }

  // 마지막 노드를 재구성한 위치에 삽입
  h->heap[parent] = temp;
  // 최댓값(루트 노드 값)을 반환
  return item;
}
// 우선순위 큐인 힙을 이용한 정렬
void heap_sort(element a[], int n){
  int i;
  HeapType h;

  init(&h);

  for(i=0; i<n; i++){
    insert_max_heap(&h, a[i]);
  }

  for(i=(n-1); i>=0; i--){
    a[i] = delete_max_heap(&h);
  }
}

 

 

 

 

 

 

 

힙 정렬 시간복잡도

최선, 평균, 최악 모두 O(nlogn)

 

 

 

 

 

 

 

 

힙 정렬 장단점

1. 장점

- 시간 복잡도가 좋은편 (최악의 경우에도 시간 복잡도인 O(nlogn)을 보장)
- 힙 정렬이 가장 유용한 경우는 전체 자료를 정렬하는 것이 아니라 가장 큰 값 몇 개만 필요할 때

 

2. 단점

- 데이터들의 상태에 따라 같은 O(nlogn) 시간 복잡도 라도 조금 느림
- 데이터의 순서가 바뀌는 불안정 정렬 알고리즘

 

 

 

 

 

 

※ 참고 - Python은 heapq라는 모듈을 통해 최소힙, 최대힙 쉽게 구현 가능

 

'CS > 알고리즘' 카테고리의 다른 글

다익스트라(Dijkstra) 알고리즘  (0) 2022.08.27
BFS & DFS  (0) 2022.08.21
이분탐색(Binary Search)  (0) 2022.08.13
선택 정렬  (0) 2022.07.30

선택 정렬이란?

 

* 데이터 배열에서 가장 작은(또는 가장 큰) 데이터를 선택하여 오름차순(또는 내림차순)으로 정렬할 때 사용

 

* 최소선택정렬 / 최대선택정렬 로 나뉨

 

  - 최소선택정렬 (Min-selection sort) : 매번 가장 작은 데이터를 선택하여 배열을 오름차순으로 변경
  - 최대선택정렬 (Max-selection sort) : 매번 가장 큰 데이터를 선택하여 배열을 내림차순으로 변경

 

 

 

 

 

 

 

 

 

선택 정렬 동작과정

(최소선택정렬 예시)


Step1. 전체 데이터에서 최솟값을 찾아냄
Step2. 찾은 최솟값을 배열의 가장 앞 원소와 교환
Step3. 그 다음으로 작은 데이터를 선택하여, 맨 앞에서 두번째 데이터와 자리 교환
Step4. 위와 같은 방식으로 오름차순 정렬 완료될 때까지 데이터를 선택하고 자리를 교환하는 과정을 반복

 

 

 

 

 

 

 

선택 정렬 알고리즘 구현

1) C

#include <stdio.h>
#include <stdlib.h>

int main() {
	int i, j, min, index, temp;
	int arr[10] = { 1, 9, 4, 6, 11, 10, 3, 15, 2, 13 };
	for (i = 0; i < 10; i++) {
		min = INT_MAX;
		for (j = i; j < 10; j++) {
			if (min > arr[j]) {
				min = arr[j];
				index = j;
			}
		}
		temp = arr[i];
		arr[i] = arr[index];
		arr[index] = temp;
	}
	for (i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	return 0;
}



2) Python

arr = [8, 3, 0, 9, 1, 6, 2, 4, 7, 5]

n = len(arr)
for i in range(n):
    # 가장 작은 데이터의 인덱스
    min_idx = i
    for j in range(i+1, n):
        # 최솟값 찾기
        if (arr[min_idx] > arr[j]):
            min_idx = j
    # 데이터 간의 자리 변경
    arr[i], arr[min_idx] = arr[min_idx], arr[i]

print(arr)

 

 

 

 

 

 

 


선택정렬 시간 복잡도

N개의 데이터가 있을 때 N-1번만큼 가장 작은 데이터를 찾고 맨 앞으로 보내는 과정이 필요

따라서 N + (N-1) + (N-2) + (N-3) + (N-4) + ... + 2 만큼의 연산횟수가 필요

 

이를 근사치로 계산하면 N * (N+1) / 2

빅오 표기법으로는 O(N*N) 으로 표시

'CS > 알고리즘' 카테고리의 다른 글

다익스트라(Dijkstra) 알고리즘  (0) 2022.08.27
BFS & DFS  (0) 2022.08.21
이분탐색(Binary Search)  (0) 2022.08.13
힙(Heap)정렬  (0) 2022.08.06

+ Recent posts