ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [알고리즘에서 유용]Python TeamNote 정리
    알고리즘/알고리즘 공부 정리 2021. 9. 24. 17:07
    728x90

    1. 정렬

    1-1. 정렬 라이브러리

    """
     1. sorted 함수 사용
    """
    array = [7, 5, 9, 0, 1, 6, 2, 4, 8]
    result = sorted(array)
    print(result)  # [0, 1, 2, 4, 5, 6, 7, 8, 9]
    
    """
     2. sort 메소드 사용 - 리스트 변수 
    """
    array = [7, 5, 9, 0, 1, 6, 2, 4, 8]
    
    array.sort()
    print(array)  # # [0, 1, 2, 4, 5, 6, 7, 8, 9]
    
    """
     3. sort함수 - 람다 
    """
    data = [(25, 'Na'), (20, 'Kim'), (23, 'Seo'), (28, 'Park'), (20, 'Ahn')]
    data.sort(key=lambda x: x[0])   # Stable Sort (When using a key)
    print(data)
    
    """
    4. 정렬 여러개하는 경우
    """
    data = [(25, 'Na'), (20, 'Kim'), (23, 'Seo'), (28, 'Park'), (20, 'Ahn')]
    s = sorted(data, key=lambda x: (x[1], x[2]))

     

    2. BFS / DFS

    2-1. bfs

    2-1-1. 인접 리스트

    from collections import deque
    
    def bfs(graph, visited, start):
        dq = deque([start])
        visited[start] = True
    
        while dq:
            now = dq.popleft()
            print(now, end=" ")
            for next in graph[now]:
                if not visited[next]:
                    dq.append(next)
                    visited[next] = True
    
    graph = [
        [],
        [2, 3, 8],
        [1, 7],
        [1, 4, 5],
        [3, 5],
        [3, 4],
        [7],
        [2, 6, 8],
        [1, 7]
    ]
    
    visited = [False] * len(graph)
    bfs(graph, visited, 1)

    2-1-2. 인접 행렬

    • 섬의 수 구하기
    from collections import deque
    
    # 상, 하, 좌, 우
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]
    
    n, m = 4, 3  # 세로, 가로
    graph = [
        [1, 1, 1],
        [0, 0, 0],
        [1, 1, 1],
        [1, 1, 0]
        ]
    
    visited = [[False] * m for _ in range(n)]   # 방문 유무
    count = 0   # 섬의 수
    dq = deque()
    
    for i in range(n):
        for j in range(m):
            if not visited[i][j] and graph[i][j] == 1:
                count += 1
                dq.append((i, j))
                visited[i][j] = True
    
            while dq:
                x, y = dq.popleft()
                for d in range(4):
                    nx = x + dx[d]
                    ny = y + dy[d]
                    if nx < 0 or ny < 0 or nx >= n or ny >= m:
                        continue
                    if visited[nx][ny] or graph[nx][ny] != 1:
                        continue
                    dq.append((nx, ny))
                    visited[nx][ny] = True
    print(count)
    • 최단 거리 구하기
    """
    시작 (0,0) 위치에서 (n, m) 출구까지 이동한 거리
    """
    
    from collections import deque
    n, m = 5, 6
    
    graph = [[1, 0, 1, 0, 1, 0],
             [1, 1, 1, 1, 1, 1],
             [0, 0, 0, 0, 0, 1],
             [1, 1, 1, 1, 1, 1],
             [1, 1, 1, 1, 1, 1]]
    
    dx = [0, 0, -1, 1]
    dy = [-1, 1, 0, 0]
    
    result = 0
    dq = deque()
    dq.append((0, 0))
    
    while dq:
        x, y = dq.popleft()
        for d in range(4):
            nx = x + dx[d]
            ny = y + dy[d]
            if nx < 0 or ny < 0 or nx >= n or ny >= m:
                continue
            if graph[nx][ny] == 1:
                graph[nx][ny] = graph[x][y] + 1
                dq.append((nx, ny))
    
    print(graph[n - 1][m - 1])

    2-2. dfs

    2-2-1. 인접 리스트

    def dfs(graph, visited, start):
        visited[start] = True
        print(start, end=" ")
        for next in graph[start]:
            if not visited[next]:
                dfs(graph, visited, next)
    
    graph = [
        [],
        [2, 3, 8],
        [1, 7],
        [1, 4, 5],
        [3, 5],
        [3, 4],
        [7],
        [2, 6, 8],
        [1, 7]
    ]
    
    visited = [False] * len(graph)
    dfs(graph, visited, 1)

    2-2-2. 인접 행렬

    n, m = 4, 3  # 세로, 가로
    graph = [
        [1, 1, 1],
        [0, 0, 0],
        [1, 1, 1],
        [1, 1, 0]
        ]
    
    visited = [[False] * m for _ in range(n)]   # 방문 유무
    
    def dfs(i, j):
        if i < 0 or j < 0 or i >= n or j >= m:
            return False
        if graph[i][j] == 0:
            return False
        graph[i][j] = 0
        dfs(i + 1, j)
        dfs(i - 1, j)
        dfs(i, j + 1)
        dfs(i, j - 1)
        return True
    
    count = 0   # 독립된 노드 수
    for i in range(n):
        for j in range(m):
            if dfs(i, j):
                count += 1
    print(count)

     

    3. 이진 탐색

    3-1. 이진 탐색 구현

    def binary_search(array, target):
        left = 0
        right = len(array) - 1
    
        while left <= right:
            mid = (left + right) // 2
            if array[mid] < target:
                left = mid + 1
            elif array[mid] > target:
                right = mid - 1
            elif array[mid] == target:
                return mid
        return None
    
    array=[-1, 0, 3, 5, 9, 12]     # 정렬된 리스트!
    
    result = binary_search(array=array, target=2)
    print(result)

    3-2. 이진탐색 모듈 사용(bisect)

    import bisect
    
    def binary_search(array=list(), target=int()):
        index = bisect.bisect_left(array, target)
        if index < len(array) and array[index] == target:
            return index
        return None
    
    array=[-1, 0 , 3, 5, 9, 12]     # 정렬된 리스트!
    
    result = binary_search(array=array, target=5)
    print(result)

    3-3. 이진탐색 모듈 사용하여 범위 구하기 (bisect)

    import bisect
    
    def count_by_range(a, left_val, right_val):
        right_val = bisect.bisect_right(a, right_val)
        left_val = bisect.bisect_left(a, left_val)
        return right_val - left_val
    
    a = [1, 2, 3, 3, 3, 3, 4, 4, 8, 9]
    
    print(count_by_range(a, 4, 4))
    print(count_by_range(a, -1, 3))

     

    4. Graph

    • 다익스트라, 플로이드 와샬: 가중치가 있는 노드 거리 구하기
    • 크루스칼: 가장 적은 비용으로 모든 노드를 연결
    • 위상 정렬: 순서가 정해진 작업을 차례대로 수행할 때 사용

    4-1. 다익스트라 - 한점 ~ 모든 노드 거리 구하기

    import sys
    import heapq
    INF = int(1e9)
    
    def dijkstra(start):
        """
        1. 시작 노드 최단 테이블 0 설정 & q에 삽입
        2. 방문하지 않은 노드 중 가장 짧은 노드를 선택 > heapq 사용(최소힙)
        3. 해당 노드를 거쳐가는 다른 노드를 계산하여 작은 경우는 갱신 & 큐에 넣기
        4. 2~3 반복
        :param start:
        :return:
        """
        q = []
        # 1. 최단 거리 테이블 초기화
        heapq.heappush(q, (0, start))
        distance[start] = 0
        while q:
            dist, now = heapq.heappop(q)
            if dist > distance[now]:   # 이미 방문함
                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]))
    
    n, m = map(int, sys.stdin.readline().split())  # 노드, 간선
    start = int(sys.stdin.readline().split()[0])
    graph = [[] for _ in range(n + 1)]
    distance = [INF] * (n + 1)
    
    for _ in range(m):
        a, b, c = map(int, sys.stdin.readline().split())   # a -> b, c 비용
        graph[a].append((b, c))
    
    dijkstra(start)
    for i in range(1, n + 1):
        if distance[i] == INF:
            print("INF")
        else:
            print(distance[i])
    
    '''
    [Input Example 1]
    5 6
    1
    5 1 1
    1 2 2
    1 3 3
    2 3 4
    2 4 5
    3 4 6
    [Output Example 1]
    0
    2
    3
    7
    INFINITY
    '''

    4-2. 플로이드 와샬 - 모든 노드

    import sys
    INF = int(1e9)
    
    n = int(sys.stdin.readline().split()[0])   # 노드 수
    m = int(sys.stdin.readline().split()[0])   # 간선 수
    
    # 1. 2차원 배열 - 모든 값을 초기화: 모든 거리 무한, 자신으로 가는 거리 0
    graph = [[INF] * (n + 1) for _ in range(n + 1)]    # 모든 최단 거리를 저장
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if i == j:
                graph[i][j] = 0
    
    for _ in range(m):
        a, b, c = map(int, sys.stdin.readline().split())
        graph[a][b] = c
    
    # 2. 다이나믹 프로그래밍
    for k in range(1, n + 1):
        for a in range(1, n + 1):
            for b in range(1, n + 1):
                graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
    
    for a in range(1, n + 1):
        for b in range(1, n + 1):
            if graph[a][b] == INF:
                print("inf")
            else:
                print(graph[a][b], end=" ")
        print()
    
    """
    [Input Example 1]
    4
    7
    1 2 4
    1 4 6
    2 1 3
    2 3 7
    3 1 5
    3 4 4
    4 3 2
    [Output Example 1]
    0 4 8 6 
    3 0 7 9 
    5 9 0 4 
    7 11 2 0 
    """

    4-3. 크루스칼 - 모든 노드 연결

    import sys
    
    def find_parent(parent, x):
        """
        자기가 속한 최종 부모 찾기
        :param parent:
        :param x:
        :return:
        """
        if parent[x] != x:
            parent[x] = find_parent(parent, parent[x])
        return parent[x]
    
    def union_parent(parent, a, b):
        """
        두 원소 합치기 - 부모 동일
        :param parent:
        :param a:
        :param b:
        :return:
        """
        a = find_parent(parent, a)
        b = find_parent(parent, b)
        if a < b:
            parent[b] = a
        else:
            parent[a] = b
    
    v, e = map(int, sys.stdin.readline().split())  # 노드 개수, 간선 개수
    parent = [0] * (v + 1)
    
    edges = []
    result = 0
    
    # 1. 부모 저장 테이블 - 자기 자신으로 세팅
    for i in range(1, v + 1):
        parent[i] = i
    
    for _ in range(e):
        a, b, cost = map(int, sys.stdin.readline().split())
        edges.append((cost, a, b))
    
    # 2. 간선 그래프를 비용에 따른 오름차순으로 설정. 그리디 - 정렬 필요
    edges.sort()
    
    for edge in edges:
        cost, a, b = edge
        if find_parent(parent, a) != find_parent(parent, b):   # 사이클 발생 안한 경우 합치기
            union_parent(parent, a, b)
            result += cost
    print(result)  # 연결된 노드 최종 비용
    
    """
     크루스칼 알고리즘
    
    [Input Example 1]
    7 9
    1 2 29
    1 5 75
    2 3 35
    2 6 34
    3 4 7
    4 6 23
    4 7 13
    5 6 53
    6 7 25
    [Output Example 1]
    159
    """

    4-4. 위상정렬 - 순서

    from collections import deque
    import sys
    
    v, e = map(int, sys.stdin.readline().split())  # 노드 수, 간선 수
    
    indegree = [0] * (v + 1)
    graph = [[] for i in range(v + 1)]
    
    for _ in range(e):
        a, b = map(int, sys.stdin.readline().split())
        graph[a].append(b)  # a > b
        indegree[b] += 1    # 진입 차수
    
    def topology_sort():
        result = []
        q = deque()
        # 1. 진입차수가 0인 모든 노드를 큐에 넣는다.
        for i in range(1, v + 1):
            if indegree[i] == 0:
                q.append(i)
        while q:
            now = q.popleft()
            result.append(now)
            for i in graph[now]:
                # 2. 큐에서 원소를 꺼내 해당 노드에서 나가는 간선을 그래프에서 제거
                indegree[i] -= 1
                # 3. 새롭게 진입차수가 0이된 노드를 큐에 넣기
                if indegree[i] == 0:
                    q.append(i)
    
        for i in result:
            print(i, end=' ')
    
    topology_sort()
    
    """
     위상 정렬 알고리즘
    [Input Example 1]
    7 8
    1 2
    1 5
    2 3
    2 6
    3 4
    4 7
    5 6
    6 4
    [Output Example 1]
    1 2 5 3 6 4 7
    """

    5. 완전 탐색

    5-1. 백트래킹

    6. 기타

    6-1. 투포인터

    6-2. 구간 합

    6-3. 2차원 돌리기

    댓글

Designed by Tistory.