2019-03-26

프로그래머스 알고리즘, 완주하지 못한 선수

목표 (문제)

completion 배열에서 participant 배열 중 제외된 대상 값을 return

조건

  • participant 배열 속 선수의 수는 1명 이상 100,000명 이하입니다.
  • completion의 길이는 participant의 길이보다 1 작습니다.
  • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
  • 선수명이 같은 경우가 있을 수 있습니다.

코드

public String solution(String[] participant, String[] completion) {
    HashMap<String, Integer> start = new HashMap<String, Integer>();
    for (String item : participant) {
        Integer hashItem = start.get(item);
        if (hashItem == null) {
            start.put(item, 1);
        } else {
            start.put(item, hashItem + 1);
        }
    }

    HashMap<String, Integer> retire = (HashMap) start.clone();
    for (String item : completion) {
        retire.put(item, retire.get(item) - 1);
    }
    
    for(String item : retire.keySet()) {
        if(retire.get(item)> 0) {
            return item;
        }
    }
    return null;
}

프로그래머스 알고리즘, 베스트 앨범

목표 (문제)

  1. 같은 장르가 많은 노래 그룹을 먼저 수록합니다.
  2. 같은 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
  3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.

조건

  • genres[i]는 고유번호가 i인 노래의 장르입니다.
  • plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
  • genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
  • 장르 종류는 100개 미만입니다.
  • 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
  • 모든 장르는 재생된 횟수가 다릅니다.

잡설

domain 구성하면 복잡하지 않게 해결될거같긴 한데 다른방법도 해보고 싶어서
나름 구성해보았으나… 저 sort 메서드를 합칠 방법이 뭐가 있을까?
분명히 저렇게 중복되는걸 자료형 선택해서 하나의 메서드로 합쳐 처리하는 방법이 있을텐데
그걸 모르겠네

코드

import java.util.*;
import java.util.Map.Entry;

class Solution {
    public int[] solution(String[] genres, int[] plays) {
        HashMap<String, HashMap<Integer, Integer>> music = new HashMap<>();
        HashMap<String, Integer> total = new HashMap<>();

        for (int index = 0, maxIndex = plays.length; index < maxIndex; index++) {
            HashMap<Integer, Integer> item;
            if (music.containsKey(genres[index])) {
                item = music.get(genres[index]);
                total.put(genres[index], total.get(genres[index]) + plays[index]);
            } else {
                item = new HashMap<>();
                total.put(genres[index], plays[index]);
            }
            item.put(index, plays[index]);
            music.put(genres[index], item);
        }

        List<Integer> answer = new ArrayList<>();
        for (String item : sortTotal(total).keySet()) {
            Set<Integer> sortedMusic = sortMusic(music.get(item)).keySet();
            int index = 0;
            for (Integer item2 : sortedMusic) {
                if (index++ > 1 ) {
                    break;
                }
                answer.add(item2);
            }
        }
        return answer.stream().mapToInt(i->i).toArray();
    }

    public LinkedHashMap<String, Integer> sortTotal(HashMap<String, Integer> total) {
        List<Entry<String, Integer>> list = new ArrayList<>(total.entrySet());
        list.sort(Entry.comparingByValue(Comparator.reverseOrder()));

        LinkedHashMap<String, Integer> sortedTotal = new LinkedHashMap<>();
        for (Entry<String, Integer> entry : list) {
            sortedTotal.put(entry.getKey(), entry.getValue());
        }

        return sortedTotal;
    }

    public LinkedHashMap<Integer, Integer> sortMusic(HashMap<Integer, Integer> music) {
        List<Entry<Integer, Integer>> list = new ArrayList<>(music.entrySet());
        list.sort(Entry.comparingByValue(Comparator.reverseOrder()));

        LinkedHashMap<Integer, Integer> sortedMusic = new LinkedHashMap<>();
        for (Entry<Integer, Integer> entry : list) {
            sortedMusic.put(entry.getKey(), entry.getValue());
        }

        return sortedMusic;
    }
}

프로그래머스 강좌, 자료구조와 알고리즘 - 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인 것.