아몬드바 2023. 8. 15. 18:48
728x90

정의

 연결 데이터를 저장할 수 있는 자료 구조

 

용어

 노드 : 그래프에서 하나의 데이터 단위를 나타내는 객체

 엣지 : 그래프에서 두 노드의 직접적인 연결 관계 데이터

 인접 :  두 노드가 엣지로 연결되어 있을 때  

 경로 :  한 노드에서 다른 노드까지 가는 길

 차수 :  한 노드가 갖고 있는 엣지의 수, 하나의 노드에 연결된 엣지들의 수

 

종류

 무방향 그래프 : 노드의 엣지가 쌍방향일때

 방향 그래프 : 엣지가 떠나는 노드를 앞에 쓰고 들어가는 노드를 뒤에 적는 방식, ex) 팔로우 기능, A -> B (A,B)

  - 출력 차수 : 노드에서 나가는 엣지의 수

  - 입력 차수 : 노드에서 들어오는 엣지의 수

 가중치 그래프 : 한 경로에 있는 엣지의 가중치의 합

 비가중치 그래프 : 한 경로에 있는 엣지의 수

 

구현(노드, 엣지)

# 지하철역
class StationNonde:
	"""지하철 역 노드를 나타내는 클래스"""
    def __init__(self, name, num_exits):
    	self.name = name
        self.num_exits = num_exits
    
# 지하철 역 노드 인스턴스 생성
station_0 = StaionNode("교대역", 14)
station_1 = StaionNode("사당역", 14)
station_2 = StaionNode("종로3가역", 16)
station_3 = StaionNode("서울역", 16)

# 노드들을 파이썬 리스트에 저장
stations = [station_0, station_1, station_2, station_3]

# 지하철 역 노드들을 파이썬 딕셔너리에 저장한다
stations = {
	"교대역" : station_0,
    "사당역" : station_1,
    "종로3가역" : station_2,
    "서울역" : station_3
}

node_1 = stations["교대역"]
node_2 = stations["서울역"]
# 노드 구현
class StationNode:
    """간단한 지하철 역 노드 클래스"""
    def __init__(self, station_name):
        self.station_name = station_name
        


def create_station_nodes(input_file):
    """input_file에서 데이터를 읽어 와서 지하철 그래프 노드들을 리턴하는 함수"""
    stations = {}  # 지하철 역 노드들을 담을 딕셔너리

    # 파라미터로 받은 input_file 파일을 연다
    with open(input_file) as stations_raw_file:
        for line in stations_raw_file:  # 파일을 한 줄씩 받아온다
            subway_line = line.strip().split("-")  # 앞 뒤 띄어쓰기를 없애고 "-"를 기준점으로 데이터를 나눈다

            for name in subway_line:
                station_name = name.strip()  # 앞 뒤 띄어쓰기 없애기

                # 지하철 역 이름이 이미 저장한 key 인지 확인
                if station_name not in stations:
                    current_station = StationNode(station_name)  # 새로운 인스턴스를 생성하고
                    stations[station_name] = current_station  # dictionary에 역 이름은 key로, 역 노드 인스턴스를 value로 저장한다

    return stations



stations = create_station_nodes("./stations.txt")  # stations.txt 파일로 그래프 노드들을 만든다

# 테스트 코드
# stations에 저장한 역들 이름 출력 (채점을 위해 역 이름 순서대로 출력)
for station in sorted(stations.keys()):
    print(stations[station].station_name)

 

구현(엣지, 인접행렬)

 인접행렬

  1. 노드들의 연결 관계(엣지)를 나타내는 2차원 리스트

  2. 각 노드를 리스트에 저장해 고유 정수 인덱스를 준다.(2차원 행렬)

  3. 노드 수 x 노드 수 크기의 행렬을 만든다.

  4. 노드들의 엣지 유무 및 가중치에 따라 행렬의 요소를 채운다.

인접 행렬
가중치 그래프
방향 그래프

구현(인접 행렬)

adjacency_matrix = [[0 for i in range(6)] for i in range(6)]

adjacency_matrix[0][1] = 1
adjacency_matrix[1][0] = 1

adjacency_matrix[0][2] = 1
adjacency_matrix[2][0] = 1

adjacency_matrix[1][5] = 1
adjacency_matrix[5][1] = 1

adjacency_matrix[1][3] = 1
adjacency_matrix[3][1] = 1

adjacency_matrix[2][5] = 1
adjacency_matrix[5][2] = 1

adjacency_matrix[3][5] = 1
adjacency_matrix[5][3] = 1

adjacency_matrix[3][4] = 1
adjacency_matrix[4][3] = 1

adjacency_matrix[4][5] = 1
adjacency_matrix[5][4] = 1

print(adjacency_matrix)

 

구현(인접 리스트)

 인접 리스트 : 노드들의 인접 데이터를 리스트에 저장하는 방법

class StationNode:
    """간단한 지하철 역 노드 클래스"""
    def __init__(self, station_name):
        self.station_name = station_name
        self.adjacent_stations = []  # 인접 리스트


    def add_connection(self, other_station):
        """지하철 역 노드 사이 엣지 저장하기"""
        self.adjacent_stations.append(other_station)
        other_station.adjacent_stations.append(self)


    def __str__(self):
        """지하철 노드 문자열 메소드. 지하철 역 이름과 연결된 역들을 모두 출력해준다"""
        res_str = f"{self.station_name}: "  # 리턴할 문자열

        # 리턴할 문자열에 인접한 역 이름들 저장
        for station in self.adjacent_stations:
            res_str += f"{station.station_name} "

        return res_str
       

def create_subway_graph(input_file):
    """input_file에서 데이터를 읽어 와서 지하철 그래프를 리턴하는 함수"""
    stations = {}  # 지하철 역 노드들을 담을 딕셔너리

    # 파라미터로 받은 input_file 파일을 연다
    with open(input_file) as stations_raw_file:
        for line in stations_raw_file:  # 파일을 한 줄씩 받아온다
            previous_station = None  # 엣지를 저장하기 위한 도우미 변수. 현재 보고 있는 역 전 역을 저장한다
            subway_line = line.strip().split("-")  # 앞 뒤 띄어쓰기를 없애고 "-"를 기준점으로 데이터를 나눈다

            for name in subway_line:
                station_name = name.strip()  # 앞 뒤 띄어쓰기 없애기

                # 지하철 역 이름이 이미 저장한 key 인지 확인
                if station_name not in stations:
                    current_station = StationNode(station_name)  # 새로운 인스턴스를 생성하고
                    stations[station_name] = current_station  # dictionary에 역 이름은 key로, 역 인스턴스를 value로 저장한다

                else:
                    current_station = stations[station_name]  # 이미 저장한 역이면 stations에서 역 인스턴스를 갖고 온다

                if previous_station is not None:
                    current_station.add_connection(previous_station)  # 현재 역과 전 역의 엣지를 연결한다

                previous_station = current_station  # 현재 역을 전 역으로 저장

    return stations


stations = create_subway_graph("./stations.txt")  # stations.txt 파일로 그래프를 만든다

# stations에 저장한 역 인접 역들 출력 (채점을 위해 역 이름 순서대로 출력)
for station in sorted(stations.keys()):
        print(stations[station])
728x90