2019-03-26

프로그래머스 강좌, 자료구조와 알고리즘 - 2

자료구조와 알고리즘 - 2 link

10강. 양뱡향 연결 리스트(Doubly Linked Lists)

노드간 참조공간을 뒤따라 오는 개체 참조 외에도 앞서오는 개체 참조공간도 생성하여
앞에서 뒤로, 뒤에서 앞으로 링크가 되어있는 형태
class Node:
    def __init__(self, item):
        self.data = item
        self.prev = None
        self.next = None
        
class DoublyLinkedList:
    def __init__(self):
        self.nodeCount = 0
        self.head = Node(None)
        self.tail = Node(None)
        self.head.prev = None
        self.head.next = self.tail
        self.tail.prev = self.head
        self.tail.next = None

    def __repr__(self):
        if self.nodeCount == 0:
            return 'LinkedList: empty'
        s = ''
        curr = self.head
        while curr.next.next:
            curr = curr.next
            s += repr(curr.data)
            if curr.next.next is not None:
                s += ' -> '
        return s

    def getLength(self):
        return self.nodeCount

    def traverse(self):
        result = []
        curr = self.head
        while curr.next.next:
            curr = curr.next
            result.append(curr.data)
        return result

    def getAt(self, pos):
        if pos < 0 or pos > self.nodeCount:
            return None
        if pos > self.nodeCount // 2:
            i = 0
            curr = self.tail
            while i < self.nodeCount - pos + 1:
                curr = curr.prev
                i += 1
        else:
            i = 0
            curr = self.head
            while i < pos:
                curr = curr.next
                i += 1
        return curr

    def insertAfter(self, prev, newNode):
        next = prev.next
        newNode.prev = prev
        newNode.next = next
        prev.next = newNode
        next.prev = newNode
        self.nodeCount += 1
        return True

    def insertAt(self, pos, newNode):
        if pos < 1 or pos > self.nodeCount + 1:
            return False
        prev = self.getAt(pos - 1)
        return self.insertAfter(prev, newNode)

실습1

목표: 리스트를 역순으로 출력하는 함수 구성
 def reverse(self):
     target = self.tail
     returnList = []

     while target.prev is not self.head:
         returnList.append(target.prev.data)
         target = target.prev
     
      return returnList

실습2

목표: 선택된 next 앞에 newNode 삽입
    def insertBefore(self, next, newNode):
        if next is self.head:
            return False
            
        next.prev.next = newNode
        newNode.prev = next.prev
        newNode.next = next
        next.prev = newNode

        self.nodeCount += 1
        return True

실습3

목표: 값을 뽑아내는 popAfter(), popBefore(), popAt() 구현 / popAt()은 popAfter()나 popBefore()를 사용해 구현
    def popAfter(self, prev):
        if prev is self.tail:
            return None
        targetNode = prev.next

        prev.next = targetNode.next
        targetNode.next.prev = prev
        self.nodeCount -= 1
        return targetNode.data

    def popBefore(self, next):
        if next is self.head:
            return None
        targetNode = next.prev

        next.prev = targetNode.prev
        targetNode.prev.next = next
        self.nodeCount -= 1
        return targetNode.data

    def popAt(self, pos):
        if pos < 1 or pos > self.nodeCount:
            raise IndexError
        
        if self.nodeCount is 1 or pos is 1:
            return self.popAfter(self.head)
        else:
            prevNode = self.getAt(pos-1)
            return self.popAfter(prevNode)

실습4

목표: 전달받은 리스트를 뒤에 병합하는 기능 구현
    def concat(self, L):
        self.tail.prev.next = L.head.next
        L.head.next.prev = self.tail.prev
        self.tail = L.tail
        
        self.nodeCount += L.nodeCount
다른사람의 풀이를 통해 python, 를 통해 한번에 여러 대입이 가능함을 알았다. 상당히 직관적인 프로그래밍이 가능한 것 같다. 하지만 가독성으로는 기존 방식이 더 유리한듯 하다.

11강. 스택(Stacks)

structure of stack
후입선출(LIFO; Last In - First Out)로써 데이터의 입출이 동작하는 형태의 자료구조
  • size(): 현재 스택에 들어 있는 데이터 원소의 수를 구함
  • isEmpty(): 현재 스택이 비어 있는지를 판단 (size() == 0?)
  • push(x): 데이터 원소 x 를 스택에 추가
  • pop(): 스택에 가장 나중에 저장된 데이터 원소를 제거 (또한, 반환)
  • peek(): 스택에 가장 나중에 저장된 데이터 원소를 참조 (반환), 그러나 제거하지는 않음
from doublylinkedlist import Node
from doublylinkedlist import DoublyLinkedList

class ArrayStack:
 def __init__(self):
  self.data = []
 def size(self):
  return len(self.data)
 def isEmpty(self):
  return self.size() == 0
 def push(self, item):
  self.data.append(item)
 def pop(self):
  return self.data.pop()
 def peek(self):
  return self.data[-1]

class LinkedListStack:
 def __init__(self):
  self.data = DoublyLinkedList()
 def size(self):
  return self.data.getLength()
 def isEmpty(self):
  return self.size() == 0
 def push(self, item):
  node = Node(item)
  self.data.insertAt(self.size() + 1, node)
 def pop(self):
  return self.data.popAt(self.size())
 def peek(self):
  return self.data.getAt(self.size()).data

실습

목표: stack 을 이용한 완성된 괄호( ) 와 같은 형태가 존재 여부를 확인하는 기능
class ArrayStack:
    def __init__(self):
        self.data = []
    def size(self):
        return len(self.data)
    def isEmpty(self):
        return self.size() == 0
    def push(self, item):
        self.data.append(item)
    def pop(self):
        return self.data.pop()
    def peek(self):
        return self.data[-1]

def solution(expr):
    match = {
        ')': '(',
        '}': '{',
        ']': '['
    }
    S = ArrayStack()
    for c in expr:
        if c in '({[':
         S.push(c)
        elif c in match:
            if S.isEmpty():
                return False
            else:
             t = match[c]
                if t != S.pop():
                    return False
    return S.isEmpty()

12강. 스택의 응용 - 수식의 후위 표기법 (Postfix Notation)

postfix notation
중위표기법 (A+(B*C))-(D/E) - 연산자를 피연산자 사이에 표시, 일반적인 표기법
전위표기법 -+A*BC/DE - 연산자를 먼저, 피연산자를 나중에 표시
후위표기법 ABC*+DE/- - 피연산자를 먼저, 연산자를 나중에 표시

실습

목표: 괄호 유무에 상관없이 중위 표기를 후위 표기로 변경하는 기능 구현
에러 수정필요
def solution(S):
    opStack = ArrayStack()
    answer = ''
    
    for c in S:
        if c in prec:
            if c is not '(' and not opStack.isEmpty() and prec[opStack.peek()] >= prec[c]:
                answer += opStack.pop()
            opStack.push(c)
        elif c is ')':
            while True:
                op = opStack.pop()
                if op is not '(':
                 break
                answer += op
        else:
            answer += c
            
    while not opStack.isEmpty():
        op = opStack.pop()
        if op is not '(':
            answer += op
        
    return answer
A-(B-C)-D*(E/F)+G 처리 요청시, ABC--DEF/*-G+ 가 아닌 ABC--DEF/*G+-로 처리된다. 이유는?
python에선 값을 while 문안에서 할당하여 바로 비교하는 연산은 불가하다고 한다. 참조

13강. 후위 표기 수식 계산

실습

목표: 후위 표기법으로 전달받은 수식을 연산하여 반환
# infixToPostfix 메소드는 12강의 solution 사용

def postfixEval(tokenList):
    evalStack = ArrayStack()
    
    for t in tokenList:
        if isinstance(t,int):
            evalStack.push(t)
        else: #isOperator
            y = evalStack.pop()
            x = evalStack.pop()
            if t is '+':
                evalStack.push(x + y)
            elif t is '-':
                evalStack.push(x - y)
            elif t is '*':
                evalStack.push(x * y)
            else:
                evalStack.push(x / y)
    
    return evalStack.peek()

프로그래머스 강좌, 자료구조와 알고리즘 - 1

자료구조와 알고리즘 - 1 link

1강은 소개로 생략

2강. 선형 배열(Linear Array)

python 의 list는 자료형의 제한이 없음
O(1) 작업
  • 원소 덧붙이기 .append()
  • 원소 하나를 꺼내기 .pop()
O(n) 작업
  • 원소 삽입하기 .insert()
  • 원소 삭제하기 .del()
기타
  • 원소 탐색하기: .index()

실습1

목표: 리스트 L 에서 오름차순에 맞게 x의 값을 삽입
def solution(L, x):
    for i, v in enumerate(L):
        if v > x:
            L.insert(i, x)
            break
    else:
        L.append(x)
    return L

실습2

목표: 리스트 L 에서 x 값의 index 반환, 부재시 -1 반환
def solution(L, x):
    answer = []
    for i, v in enumerate(L):
        if v == x:
            answer.append(i)
    if len(answer) == 0:
        answer.append(-1)
    
    return answer

3강. 정렬과 탐색(Sorting & Searching)

내장 정렬기능
  1. 파이썬 내장 함수 sorted() - 정렬된 리스트 반환(deep copy)
  2. 리스트에 쓸 수 있는 메서드 .sort() - 해당 리스트 정렬
역순 정렬(reverse)
reverse=true 를 매개변수로 전달
ex) sorted(L, reverse=true), L.sort(reverse=true)
정렬방식 지정(key)
java의 Comparator 와 유사. sorted(L, key=lambda x: len(x)) 로 사용시 L 리스트의 개체값의 문자열 길이가 짧은 순으로 정렬 진행. dictionary 자료형도 L.sort(key=lambda x:x['score'], reverse=true) 의 형태로 x[‘score’] 값이 큰순으로 정렬가능.
탐색 알고리즘
  1. 선형 탐색(linear search) - 순차적으로 탐색과정 진행, O(n)
  2. 이진 탐색(binary search) - 정렬되어있는 경우 가능, 절반씩 쪼개며 탐색, O(logn)

실습1

목표 : 효율성을 위해 이진탐색으로 오름차순 정렬된 배열에서 x의 index값을 반환, 부재시 -1 반환
def solution(L, x):
    low=0
    high=len(L)-1
    answer = 0
    while(low<=high):
        mid=(low+high)//2
        if L[mid]==x:
            answer = mid
            break
        elif L[mid]>x:
            high=mid-1
        else:
            low=mid+1
    else:
     return -1
    return answer

4강. 재귀 알고리즘(Recursive Algorithms) 기초

실습1

목표 : 피보나치 수열 구현
  • 재귀
    def solution(x):
        answer = 0;
        if x <= 1:
            answer = x
        else:
            answer = solution(x-1) + solution(x-2)
        return answer
    
  • 반복문
    def solution(x):
        answer = 0;
        if x <= 1:
            answer = x
        else:
            now = 1;
            before = 0;
            for i in range(1, x):
                answer = before + now
                before = now
                now = answer
        return answer
    

5강. 재귀 알고리즘(Recursive Algorithms) 응용

자원 효율: 재귀적 < 반복적

실습1

목표: 재귀적 이진탐색 구현
def solution(L, x, l, u):
    if x < L[l] or x > L[u]:
        return -1
    mid = (l + u) // 2
    if x == L[mid]:
        return mid
    elif x < L[mid]:
        return solution(L, x, l , mid - 1)

    else:
        return solution(L, x, mid + 1 , u)

6강. 알고리즘의 복잡도 (Comlexity of Algorithms)

점근적 표기법
  • Big-θ : 평균적인 복잡도, θ(n) 로 표기
  • Big-O : 최악의 경우 복잡도 (주로 사용), O(n) 로 표기
  • Big-Ω : 최선의 경우 복잡도, Ω(n) 로 표기
image
정렬 알고리즘 예시 - 병합 정렬( merge sort)
정렬할 배열들을 각각 최소 비교단위로 분리하여 비교 -> O(logn)
정렬된 최소 비교단위 배열을 병합 - > O(n) => O(nlogn)
6강 실습. X

7강. 연결 리스트(Linked Lists) (1)

단방향 연결 리스트
image
다음 대상에 대한 주소를 참조하여 리스트를 구조화
  • 특정 원소 참조 (k 번째)
  • 리스트 순회 (list traversal)
  • 길이 얻어내기

실습1

목표: linked list를 순회하며 각 노드값을 리스트로 만들어 반환하는 traverse 함수구현
class Node:
    def __init__(self, item):
        self.data = item
        self.next = None

class LinkedList:
    def __init__(self):
        self.nodeCount = 0
        self.head = None
        self.tail = None

    def getAt(self, pos):
        if pos < 1 or pos > self.nodeCount:
            return None
        i = 1
        curr = self.head
        while i < pos:
            curr = curr.next
            i += 1
        return curr

    def traverse(self):
        returnList = []
        target = self.head
        while target is not None:
            returnList.append(target.data)
            target = target.next
        
        return returnList

8강. 연결 리스트(Linked List) (2)

  • 원소의 삽입 (insertion)
  • 원소의 삭제 (deletion)
  • 두 리스트 합치기 (concatenation)

9강.연결 리스트(Linked List) (3)

dummy node 를 linked list 의 head로 추가
class Node:
 def __init__(self, item):
  self.data = item
  self.next = None
  
class LinkedList:
 def __init__(self):
  self.nodeCount = 0
  self.head = Node(None)
  self.tail = None
  self.head.next = self.tail

 def __repr__(self):
  if self.nodeCount == 0:
   return 'LinkedList: empty'
   
  s = ''
  curr = self.head
  while curr.next:
   curr = curr.next
   s += repr(curr.data)
   if curr.next is not None:
    s += ' -> '
  return s

 def getLength(self):
  return self.nodeCount

 def traverse(self):
  result = []
  curr = self.head
  while curr.next:
   curr = curr.next
   result.append(curr.data)
  return result

 def getAt(self, pos):
  if pos < 0 or pos > self.nodeCount:
   return None
  i = 0
  curr = self.head
  while i < pos:
   curr = curr.next
   i += 1

  return curr

 def insertAfter(self, prev, newNode):
  newNode.next = prev.next
  if prev.next is None:
   self.tail = newNode
  prev.next = newNode
  self.nodeCount += 1
  return True

 def insertAt(self, pos, newNode):
  if pos < 1 or pos > self.nodeCount + 1:
   return False
  if pos != 1 and pos == self.nodeCount + 1:
   prev = self.tail
  else:
   prev = self.getAt(pos - 1)
  return self.insertAfter(prev, newNode)

 def concat(self, L):
  self.tail.next = L.head.next
  if L.tail:
   self.tail = L.tail
  self.nodeCount += L.nodeCount

실습1

목표: 노드를 뽑아내는 함수 구현
def popAfter(self, prev):
    curr = prev.next
    if prev.data is None:
        prev.next = curr.next
        self.tail = curr.next
    elif prev.next == self.tail:
        prev.next = None
        self.tail = prev
    else:
        prev.next = curr.next

    self.nodeCount -= 1
    return curr.data


def popAt(self, pos):
    if pos < 1 or pos > self.nodeCount:
        raise IndexError

    prev = self.getAt(pos - 1)
    return self.popAfter(prev)

프로세스 실행기간 제한 명령어, timeout

timeout

프로세스를 설정된 시간동안 동작하도록 제한

사용법

timeout [OPTION] NUMBER[SUFFIX] COMMAND [ARG]…
timeout [OPTION]

NUMBER[SUFFIX]

실행하고자 하는 시간 입력. 기본 단위는 초(sec).
분은 m, 시간은 h로 사용한다
timeout 3m #3 분
timeout 1h #1시간

[OPTION]

  • -s, --signal=SIGNAL
    specify the signal to be sent on timeout.
    SIGNAL may be a name like ‘HUP’ or a number. See ‘kill -l’ for a list of signals
  • –help
    display this help and exit
  • –version
    output version information and exit

COMMAND [ARG]

실행할 대상인 프로세스의 옵션을 포함한 구동명령

패킷 관제 명령어, tcpdump

tcpdump

네트워크 인터페이스를 통해 오가는 패킷 정보들을 로그정보로 얻어내기위해 사용

사용법

tcpdump [ options] [ ‘expression’ ] [host]

OPTION

  • -w filename
    dump기록을 콘솔이 아닌 파일형태로 저장. 기록이 끝난뒤 파일로 저장
  • -U
    버퍼가 가득찰 때 저장는 것이 아닌, 통신 발생시 파일로 저장
  • -v
    구문 분석 및 인쇄 할 때 자세한 출력을 생성. 예를 들어 IP 패킷의 수명, 식별, 총 길이 및 옵션이 인쇄됩니다. 또한 IP 및 ICMP 헤더 체크섬 확인과 같은 추가 패킷 무결성 검사를 가능하게합니다.
  • -vv
    더 자세한 출력. 예를 들어 추가 필드는 NFS 응답 패킷에서 인쇄되고 SMB 패킷은 완전히 디코딩됩니다.
    -vvv
  • 더 자세한 출력. 예를 들어 telnet SBSE 옵션이 모두 인쇄됩니다. 함께 -X 텔넷 옵션뿐만 아니라 진수로 인쇄됩니다.
  • -G rotate_seconds
    일정 주기로 반복하여 dump 기록 작성하며, -w와 함께 사용하면 해당 파일로 반복해서 덮어 씌워집니다. -c count 지정시 파일명이 filename._count_로 명명됩니다.
  • -c count
    지정한 수만큼 반복됩니다.

expression #

expression and expression 의 형태로 조합가능
  • tcpdump host 192.168.0.1
    host 를 지정하면, 이 ip 로 들어오거가 나가는 양방향 패킷 모두 보여줌
  • tcpdump src 192.168.0.1
    host 중에서 src 가 192.168.0.1인것 만 지정
  • tcpdump dst 192.168.0.1
    host 중에서 dst 가 192.168.0.1인것 만 지정
  • tcpdump net 192.168.0.1/24
    CIDR 포맷으로 지정
  • tcpdump tcp
    TCP 인것만
  • tcpdump udp
    UDP 인것만
  • tcpdump port 3389
    포트 양뱡항으로 3389인 것.
  • tcpdump src port 3389
    src 포트가 3389인 것.
  • tcpdump dst port 3389
    dst 포트가 3389인 것.

TLS

TLS

TCP/IP 네트워크를 사용하는 통신에 적용되며, 통신 과정에서 전송계층 종단간 보안과 데이터 무결성을 확보해준다.

TLS 암호화

TLS는 대칭 및 비대칭 암호화 조합을 사용하며,이는 데이터를 안전하게 전송할 때 성능과 보안간에 좋은 절충안을 제공하기 때문입니다.

대칭 암호화

대칭 암호화를 사용하면 데이터는 보낸 사람과받는 사람 모두에게 알려진 비밀 키로 암호화되고 해독됩니다. 대칭 암호화는 연산 속도면에서 효율적이지만 공통의 비밀 키를 안전한 방식으로 공유해야 할 필요가 있음을 의미합니다.

1.대칭키 교환
대칭키
3.암호문 전송
대칭키
송신자
수신자
2.암호화
4.복호화

비대칭 암호화

비대칭 암호화 혹은 공유키 방식은 출제자만이 알고 있는 특정한 종류의 정보(비밀 키) 없이는 매우 풀기 어려운 수학적 문제(공개 키)를 바탕으로 만들어진다. 키를 만드는 사람은 이 문제(공개 키)를 일반에 공개하고 특정한 정보(비밀 키)는 자신만이 알수 있도록 숨긴다. 그러면 어떤 사람이건 이 문제를 이용해 메시지를 암호화하면 키를 만든 사람만이 이 문제를 풀어 원래 메시지를 해독할 수 있다.

1.공개키, 개인키 생성
2.생성된 공개키 저장
3.저장된 공개키 획득
공개키
5.암호문 전송
개인키
송신자
수신자
공개키_저장소
4.암호화
6.복호화

CA

CA(Certificate Authority)는 X.509 표준을 준수하는 디지털 인증서를 발급하는 곳 입니다. 디지털 인증서는 인증서 소유자 (주체)의 공개 키를 인증하고 소유자는 인증서로 보호되는 도메인을 제어합니다. 따라서 신뢰할 수있는 제 3 자로서의 역할을 수행하여 클라이언트가 검증 된 서버에 연결한다는 보증을 제공합니다.

TLS Handshake

ClientServer1) "client hello"2) "server hello"3) Server certificatie4) Client Key Exchage5) Send client certificate7) Client "finished"9) Server "finished"Exchange Messageloop[ encrypted ]ClientServer
  1. 통신상태 확인을 위한 hello 메시지로 정보 교환, 클라이언트에서 사용 가능한 TLS 버전, 암호 모음 목록, 난수 및 압축 방법 목록, SNI 등이 포함합니다.
  2. 통신상태 확인을 위한 hello 메시지로 정보 교환, 서버에서 사용할 TLS 버전, 암호 모음 목록, 난수 및 압축 방법 목록 등을 포함합니다.
  3. 서버의 디지털 인증서를 유효기간과 발급된 서버가 올바른지 등을 확인합니다.
  4. 클라이언트와 서버가 후속 메시지 데이터를 암호화하는 데 사용될 비밀 키를 계산할 수 있도록 하는 임의의 바이트 문자열을 보냅니다. 임의의 바이트 문자열 자체는 서버의 공개키로 암호화됩니다.
  5. 만약 "클라이언트 인증서 요청"을 보낸 경우 클라이언트는 클라이언트의 디지털 인증서 또는 "디지털 인증서 경고 없음"과 함께 클라이언트의 개인 키로 암호화된 임의의 바이트 문자열을 보냅니다. 이 경고는 경고일 뿐이지만 일부 구현에서는 클라이언트 인증이 필수 일 경우 Handshake가 실패합니다.
  6. 클라이언트의 인증서를 확인합니다.
  7. Handshake의 클라이언트 부분이 완료되었음을 나타내는 “완료” 메시지를 비밀키로 암호화합니다.
  8. Handshake의 서버 부분이 완료되었음을 나타내는 “완료” 메시지를 비밀키로 암호화합니다.
  9. 서버와 클라이언트는 이제 공유 비밀키로 대칭적으로 암호화된 메시지를 교환할 수 있습니다.

SNI

SNI(Server Name Indication)는 TLS 프로토콜의 확장입니다. 클라이언트는 TLS Handshake의 SNI 항목을 사용하여 연결할 호스트 이름을 지정합니다. 이렇게 하면 서버(Apache, Nginx 또는 HAProxy와 같은 로드 밸런서)가 단일 IP 주소의 모든 인증서를 호스팅하는 동안 연결을 설정하는데 필요한 해당 개인 키를 선택할 수 있습니다.
SNI가 사용되면 서버의 hostname이 TLS Handshake 과정에 포함되며, HTTPS로 공유되는 IP 주소에 있더라도 고유한 TLS 인증서를 가질 수 있습니다.
sni

ESNI

TLS Handshake 과정에서 SNI 정보를 교환할때 평문으로 전달되는 것을 암호화하여 중간자가 탈취할 수 없도록 하는 구조
HTTP
HTTP 를 통한 통신
HTTPS + SNI
HTTPS 를 통한 암호화
HTTPS + ESNI
ESNI 추가 암호화

참조

Google Analytics

Analytics

애널리틱스는 데이터의 의미있는 패턴을 발견, 해석 및 전달합니다. 효과적인 의사 결정을 위해 이러한 패턴을 적용하는 과정이며 성능을 수치화하기 위해 통계, 컴퓨터 프로그래밍 및 운영 연구의 동시 적용에 의존합니다.

Blog Setting

Jekyll 에 Google analytics 를 적용하려면 이곳으로 이동하여 아래 이미지처럼 가입을 눌러 가입진행.
packet loss percentage
각 설정들을 올바르게 입력 후
packet loss percentage
packet loss percentage
추적 ID 가져오기를 하면 약관 동의 진행 절차를 진행 하고
packet loss percentage위 같이 추적하기 위한 코드를 제공하는데 그 코드를 페이지의 Head 부분에 등록하면 대시보드를 통해 접근을 확인할 수 있다.
현재 실시간만 확인이 되는데 다른것도 알아봐야 되겠다.