'파이썬 알고리즘 인터뷰(박상길, 책만)' 참조

 

1. 문제 - 이진트리 직렬화 & 역직렬화(리트코드 297)

image

- 문제: 이진 트리를 배열로 직렬화하고, 반대로 역직렬화하는 기능을 구현하라.
    - Input: root = [1,2,3,null,null,4,5]
    - Output: [1,2,3,null,null,4,5]

직렬화(Serialize)는 논리적 구조인 이진트리를 파일이나 메모리에 저장하기 위해 배열구조로 바꾸는 것을 말하며 반대는 과정은 역직렬화(Deserialize)라고 한다.

본 문제는 이진트리를 배열로 바꾸는 직렬화 함수와 배열을 이진트리로 바꾸는 역직렬화 함수 모두를 구현하는 것이다.

 

2. 풀이

스크린샷 2022-07-11 오전 11 28 25

 

1) 직렬화

 

1-1

우선, 이진 트리를 배열화 한 모습을 도식화하면 위와 같다.

노드 레벨 구분의 편의를 위해 인덱스를 1부터 시작했다. 인덱스 1, 2, 4, 8, … 순서로 앞 수에 곱하기 2를 하면 노드 레벨의 시작 위치를 구분할 수 있기 때문이다.

이진트리를 배열로 바꾸기 위해 너비 우선 탐색 방식(BFS)을 사용한다.

우선, 리스트 보다 시간 효율적인 데크 자료형으로 ‘큐'를 생성한다.

그리고 배열을 담을 리스트(result)를 만들어 주는데, 앞 서 말했듯이 인덱스가 1부터 시작하기 때문에 ‘#’ 하나를 미리 채워놓았다.

파이썬의 경우 None값을 string 형태로 출력할 수 없기 때문에 ‘#’를 이용하여 None을 표현했다.


def serialize(self, root):
    queue = collections.deque([root])
    result = ['#']

 

1-2

while 반복문을 통해 큐(queue)에서 노드를 하나씩 추출한 다음, 해당 노드의 자식 노드를 다시 큐에 삽입하는 과정을 반복한다.

추출된 부모 노드의 값을 결과를 담을 리스트(result)에 추가하되, 빈 노드의 경우 ‘#’를 추가한다.

최종 값을 하나의 문자열로 출력해야 하기 때문에 리스트(result)의 요소들을 join() 함수를 이용하여 통합한다.


def serialize(self, root):
    queue = collections.deque([root])
    result = ['#']

    # 트리 BFS 직렬화
    while queue:
        node = queue.popleft()

        if node:
            queue.append(node.left)
            queue.append(node.right)

            result.append(str(node.val))

        else:
            result.append('#')

    return ' '.join(result)

 

2) 역직렬화

 

2 - 1

직렬화 과정에서 join() 함수로 통합했던 배열을 split() 함수로 분할한 후 ‘nodes’ 변수에 담아준다.

루트 노드(root)를 생성하고, 해당 노드를 큐에 삽입한다.

인덱스 변수에 정수 2를 할당하는데, 루트 노드의 왼쪽 자식 노드가 인덱스 2에 위치하기 때문이다.

# 역직렬화
    def deserialize(self, data):

        # 예외 처리 
        if data == '# #':
            return None

        nodes = data.split()
        root = TreeNode(nodes[1])
        queue = collections.deque([root])

        index = 2

 

2 - 2

while 반복문을 통해 부모 노드를 큐에서 출력한 후, 자식 노드를 큐에 다시 삽입하는 과정을 반복한다.

이때 인덱스를 ‘+1’씩 증가시키며 부모 노드와 자식 노드를 연결한다.

자식 노드가 존재하지 않을 경우, queue에 추가하지 않음으로써 빈 노드를 처리한다.

while queue:

        node = queue.popleft()

    # 자식 노드 존재 시, 큐에 삽입
    if nodes[index] != '#':
        node.left = TreeNode(int(nodes[index]))
        queue.append(node.left)
    index += 1

    if nodes[index] != '#':
        node.right = TreeNode(int(nodes[index]))
        queue.append(node.right)
    index += 1

 

3) 전체 코드

class Codec:
    # 직렬화
    def serialize(self, root):

        queue = collections.deque([root])
        result = ['#']

        # 트리 BFS 직렬화
        while queue:
            node = queue.popleft()

            if node:
                queue.append(node.left)
                queue.append(node.right)

                result.append(str(node.val))

            else:
                result.append('#')

        return ' '.join(result)



    # 역직렬화
    def deserialize(self, data):

        # 예외 처리 
        if data == '# #':
            return None

        nodes = data.split()
        root = TreeNode(nodes[1])
        queue = collections.deque([root])

        index = 2

        while queue:

            node = queue.popleft()

            # 자식 노드 존재 시, 큐에 삽입
            if nodes[index] != '#':
                node.left = TreeNode(int(nodes[index]))
                queue.append(node.left)
            index += 1

            if nodes[index] != '#':
                node.right = TreeNode(int(nodes[index]))
                queue.append(node.right)
            index += 1

        return root

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조

1) 이더넷 프레임

  • (이더넷 프레임) ‘이더넷 헤더'와 ‘이더넷 트레일러'를 데이터그램에 붙여서 ‘이더넷 프레임'으로 캡슐화하고, 이 이더넷 프레임이 신호가 돼서 케이블로 전달
    • (헤더) 데이터종류특정하는 값
    • (데이터그램) 전송되는 데이터로 3계층 PDU를 의미
    • (트레일러) 에러 체크용 비트열
  • (프레임 에러) 에러가 있었던 프레임은 파기image

2) 이더넷 동작

  • (플러딩) 허브를 사용하는 경우 모든 기기신호도달되므로 본인에게 해당 없는 신호가 전달되는 문제
    • (대응) 수신한 프레임수신처 MAC 주소를 보고 자신에게 온 것 외에 다른 프레임파기
  • (충돌) 여러 기기가 동시신호를 보낼 경우 신호의 충돌이 발생
    • (CSMA/CD) 충돌을 최소화하기 위한 액세스 제어
      • 케이블에 신호를 보내는 액세스를 제어하여 충돌을 방지하는 방법을 CSMA/CD(Carrier Sense Mulitple Access/Collision Detection)이라고 함
      • (CS) 신호감지(CS)는 누가 송신 중이면 송신하지 않는 방식
      • (MA) 다중엑세스(MA)는 아무도 송신하고 있지 않을 때 송신한다는 규칙
      • (CD) 충돌검사(CD)는 송신 후 충돌이 발생하면 다시 재수행한다는 절차

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조

1) 주소와 캐스트

  • (주소) 데이터를 보내는 상대(송신처)와 자신(수신처)을 특정하는 데이터
  • (어드레싱) 주소를 어떻게 사용하고, 배정할지, 등을 결정하는 과정
  • (주소의 종류) 데이터 전송 방법에 따라 3가지로 분류
    • (유니캐스트) ‘1대 1’ 데이터 통신
    • (브로드캐스트) ‘1대 전체' 데이터 통신
    • (멀티캐스트)1대 다수' 데이터 통신
  • (주소의 특징)
    • 각각의 기기는 유니캐스트 주소를 최소 한 개를 가지고 있음
    • 복수의 인터페이스를 가진 기기는 인터페이스마다 유니캐스트 주소를 가짐
    • 유니 캐스트 주소는 같은 것이 없어야 함

2) MAC 주소

  • (MAC 주소) 이더넷에서 사용하는 주소로 인터페이스에 지정된 고정 주소

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조

 

14. OSI 2계층의 역할과 개요

1) 개요

  • (신호 충돌 방지) 신호송신 전이나 수신 후에 데이터를 바르게 송수신하는 순서가 필요
  • (2계층 역할) 신호가 닿는 범위(세그먼트)에서의 데이터 전송에 관해 규정

2) 프레이밍과 신호의 동기

  • (프레이밍)
    • (분류) 1계층의 케이블 및 신호의 종류에 따라 2계층의 규칙은 달라지며, 대표적으로 ‘LAN’‘WAN’으로 구분함
    • (이더넷) 2계층에서 LAN용 규칙으로 ‘LAN’의 사실 표준이다.
    • (프레이밍) 세그먼트 내의 데이터 전송 규칙 중 하나로 1계층에서 주고받는 신호를 비트화해서 거기에 의미부여하는 방법
    • (프리엠블) 송수신자 간 비트를 읽는 타이밍을 맞추기 위해서 프레임시작된다는 사실을 사전에 알리는 신호를 프리엠블(preamble)이라고 함
  • (동기 통신) 송수신자 간 비트를 읽는 타이밍을 맞추기 위한 또 다른 방법으로 클락(Clock) 신호라 불리는 ‘타이밍을 맞추는 신호'를 보냄

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조

13. 허브

1) 허브의 기능

  • (의미) 여러 대의 컴퓨터 네트워크 장비를 연결하는 장치
  • (기능)
    • (1. 신호의 증폭과 재생) 감쇠에 의해 약해진 신호를 본래 형태로 증폭 및 재생
    • (2. 기기 연결) 복수의 기기연결하여 네트워크 구성
  • (연속 접속) 더 큰 네트워크를 만들기 위해 허브와 허브를 연결하는 작업을 연속 접속(cascade connection)이라고 함

2) 충돌 도메인

  • (플러딩) 수신한 포트 이외에 모든 포트에 수신한 신호송신하는 것
  • (충돌 도메인) 신호를 송신하면 충돌이 발생할지도 모르는 범위를 말하며, 허브로 연결된 모든 컴퓨터는 같은 충돌 도메인(collision Domain)에 포함됨

‘파이썬 알고리즘 인터뷰(박상길, 책만)’ 참조

1. 문제 - 상위 K 빈도 요소(리트코드 347)

- 문제: 상위 k번 이상 등장하는 요소를 출력하라

  - Input: nums = [1,1,1,2,2,3], k = 2
  - Output: [1,2]

입력값으로 주어진 리스트에서 동일한 요소가 가장 많은 값 순서대로 k개를 출력하는 문제다.

예시로 주어진 리스트에서 엘리먼트 ‘1’, ‘2’, ‘3’은 각각 3개, 2개, 1개가 있다.

개수가 많은 순서대로 2개(k=2)를 출력하면 ‘1’과 ‘2’가 출력된다.

2. 풀이 - 파이썬 모듈 활용

‘우선순위 큐’를 이용하여 엘리먼트의 빈도수 순서대로 정렬하는 방식이 일반적인 풀이일 것이다.

하지만 파이썬 모듈을 활용한 간결한 풀이 방법을 사용하려 한다.

우선 전체 코드는 아래와 같다.

짧은 코드이지만 다양한 기능을 함축하고 있다.

def topKFrequent(self, nums, k):

    return list(zip(*collections.Counter(nums).most_common(k)))[0]

1) collections.Counter()

코드를 세분화 하여 살펴보자.

우선 collections.Counter()모듈은 엘리먼트(key)와 그 개수(value)를 튜플 형태로 반환한다.

스크린샷 2022-07-09 오전 11 11 22

2) most_common(k)

most_common(k) 매서드르 활용하면 value값이 큰 순서대로 k개를 반환한다.

스크린샷 2022-07-09 오전 11 11 47

3) zip()

zip() 함수는 서로 다른 튜플 내 요소들을 인덱스에 맞게 짝지어 튜플로 반환한다.

>>> a = (1, 2)
>>> b = (3, 4)
>>> list(zip(a,b))

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

하지만 ‘zip(collections.Counter(nums).most_common(k))’를 실행해보면 위의 예시와 다른 방식으로 출력된다. 엘리먼트끼리 짝지어지는 것이 아니라, 하나로 묶인 엘리먼트와 빈 값이 짝지어진다.

image

4) 아스테리스크(*)

아스테리스크(*)는 시퀀스를 언팩(Unpack)하는 연산자다. 위 예시에서 튜플로 묶인 엘리먼트를 언팩한 후 zip() 함수를 적용하면 우리가 원하는 형태의 값을 얻을 수 있다.

image

5) 인덱싱

우리는 개수가 가장 많은 엘리먼트 k개(1과 2)를 리스트에 담아서 출력하고자 한다. 따라서 인덱싱으로 리스트의 첫 번째 요소를 출력하면 최종 답을 구할 수 있다.

image

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조

 

12. 신호와 충돌

신호

  • (종류) 아날로그 신호디지털 신호가 있으며 아날로그 신호는 연속적인 파장이며 디지털 신호는 스위칭 작용(ON/OFF)으로 표현 가능한 불연속적 신호다
  • (비트와 디지털 신호) RZ(Return to Zero)부호라고 불리는 방법이 대표적이며 스위칭 작용에 따라 일정 전압 이상일 경우 ‘1’로, 이하일 경우 ‘0’으로 표현한다.
  • (통신속도) 신호의 형태와 전송방법에 따라서 통신 속도가 결정되며, 보통 통신속도는 1초 동안 전해지는 비트수bps(bir per second)로 표현함
    • 통신속도(bps) = 1초간 신호의 횟수 x 1 신호에서의 비트수

신호에서 발생하는 문제

  • (감쇠) 장거리 케이블을 지나는 동안 신호가 약해짐
  • (노이즈) 외적 요인에 의해 신호의 진폭이 변형됨
  • (충돌) 동일 케이블에 동시에 전송된 신호가 충돌해 진폭이 붕괴됨
  • image

‘하루 3분 네트워크 교실(아미노 에이지, 김현주, 영진닷컴)’ 참조


  • (물리 계층) OSI 참조 모델의 1 계층은 전기 및 기계적인 전송에 관한 내용으로, 케이블이 연결되어 있는 기기에 신호를 전달하는 역할을 수행함
  • (통신 매체) 신호가 지나가는 파이프 역할을 하는 매개체로 크게 ‘유선'‘무선’으로 나뉨
    • (동선) 전기신호를 사용하는 케이블로 UTP(Unshielded Twist Pair Cable)이 주로 사용됨
    • (광파이버) 광신호를 사용하는 케이블로 신호의 안정성속도가 ‘동선’보다 뛰어나지만 굽히기가 어려워 좁은 곳에서 배선하기 어렵다는 단점이 있다.

  • (인터페이스)
    • (의미) 컴퓨터에서 송신할 데이터(비트)를 케이블에 맞는 신호변환해서 케이블로 보내고, 케이블에서 수신된 신호를 컴퓨터에 사용되는 데이터로 변환하는 기기
    • (LAN용) LAN용 케이블에 접속하기 위한 인터페이스로 NIC(Network Interface Card)가 대표적임
    • (WAN용) WAN의 경우 통상 PC에 NIC를 부착하지 않고 DCE(Data Circuit terminating Equipment: 회선 종단 기기)라는 별도의 신호 변환기를 사용

+ Recent posts