• 문제

한국중학교에 다니는 학생들은 각자 정수 번호를 갖고 있습니다. 이 학교 학생 3명의 정수 번호를 더했을 때 0이 되면 3명의 학생은 삼총사라고 합니다. 예를 들어, 5명의 학생이 있고, 각각의 정수 번호가 순서대로 -2, 3, 0, 2, -5일 때, 첫 번째, 세 번째, 네 번째 학생의 정수 번호를 더하면 0이므로 세 학생은 삼총사입니다. 또한, 두 번째, 네 번째, 다섯 번째 학생의 정수 번호를 더해도 0이므로 세 학생도 삼총사입니다. 따라서 이 경우 한국중학교에서는 두 가지 방법으로 삼총사를 만들 수 있습니다.

한국중학교 학생들의 번호를 나타내는 정수 배열 number가 매개변수로 주어질 때, 학생들 중 삼총사를 만들 수 있는 방법의 수를 return 하도록 solution 함수를 완성하세요.

 

  • 답안
def solution(numbers):
    len_num = len(numbers)

    cnt = 0
    for i in range(len_num):
        for j in range(i+1, len_num):
            for k in range(j+1, len_num):
                if numbers[i] + numbers[j] + numbers[k] == 0:
                    cnt += 1

    return cnt

 

 

  • 이 문제의 키워드
    • 리스트의 길이가 3부터 13까지로 정해져있으므로 완전탐색해도 시간복잡도 기준에 위배되지 않는 상황
    • 시간 복잡도가 크지 않다면 완전탐색(brutal force)으로 해결
    • 조합 combinations를 활용할 수도 있겠지만 이점 많지 않음
  • 문제

햄버거 가게에서 일을 하는 상수는 햄버거를 포장하는 일을 합니다. 함께 일을 하는 다른 직원들이 햄버거에 들어갈 재료를 조리해 주면 조리된 순서대로 상수의 앞에 아래서부터 위로 쌓이게 되고, 상수는 순서에 맞게 쌓여서 완성된 햄버거를 따로 옮겨 포장을 하게 됩니다. 상수가 일하는 가게는 정해진 순서(아래서부터, 빵 – 야채 – 고기 - 빵)로 쌓인 햄버거만 포장을 합니다. 상수는 손이 굉장히 빠르기 때문에 상수가 포장하는 동안 속 재료가 추가적으로 들어오는 일은 없으며, 재료의 높이는 무시하여 재료가 높이 쌓여서 일이 힘들어지는 경우는 없습니다.

예를 들어, 상수의 앞에 쌓이는 재료의 순서가 [야채, 빵, 빵, 야채, 고기, 빵, 야채, 고기, 빵]일 때, 상수는 여섯 번째 재료가 쌓였을 때, 세 번째 재료부터 여섯 번째 재료를 이용하여 햄버거를 포장하고, 아홉 번째 재료가 쌓였을 때, 두 번째 재료와 일곱 번째 재료부터 아홉 번째 재료를 이용하여 햄버거를 포장합니다. 즉, 2개의 햄버거를 포장하게 됩니다.

상수에게 전해지는 재료의 정보를 나타내는 정수 배열 ingredient가 주어졌을 때, 상수가 포장하는 햄버거의 개수를 return 하도록 solution 함수를 완성하시오.

 

  • 답안
def solution(ingredient):
    answer = 0
    stack = []
    
    for i in ingredient:
        stack.append(i)
        if stack[-4:] == [1, 2, 3, 1]:
            answer += 1
            for _ in range(4):
                stack.pop()
    
    return answer

 

 

  • 이 문제 키워드
    • 리스트 원소의 나열이 특정 조건을 만족하면 처리하도록 할 때, stack 구조 활용
    • 만족하는 경우를 제외하고 나머지를 고려하고 싶을 때, pop()으로 제거
    • stack에서 또 for문을 쓰는게 아니라, [-4:]와 같이 고정 인덱싱으로 조건문 확인 가능
  • 문제

네오와 프로도가 숫자놀이를 하고 있습니다. 네오가 프로도에게 숫자를 건넬 때 일부 자릿수를 영단어로 바꾼 카드를 건네주면 프로도는 원래 숫자를 찾는 게임입니다.

다음은 숫자의 일부 자릿수를 영단어로 바꾸는 예시입니다.

1478 → "one4seveneight"
234567 → "23four5six7"
10203 → "1zerotwozero3"
이렇게 숫자의 일부 자릿수가 영단어로 바뀌어졌거나, 혹은 바뀌지 않고 그대로인 문자열 s가 매개변수로 주어집니다. s가 의미하는 원래 숫자를 return 하도록 solution 함수를 완성해주세요.

 

  • 답안
def solution(s):
    answer = ''
    temp = ''
    num_dict = {'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4',
               'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9'}
    for i in s:
        if i.isdigit():
            answer += i
        else:
            temp += i
            if temp in num_dict:
                answer += num_dict[temp]
                temp = ''
    return int(answer)

 

  • 이 문제의 키워드
    • 문자열을 숫자로 변환하도록 미리 dictionary 선언
    • temp를 설정 해놓고, 완성되면 리셋하는 로직 잘 사용하자
  • 문제

문자열 s에는 공백으로 구분된 숫자들이 저장되어 있습니다. str에 나타나는 숫자 중 최소값과 최대값을 찾아 이를 "(최소값) (최대값)"형태의 문자열을 반환하는 함수, solution을 완성하세요.
예를들어 s가 "1 2 3 4"라면 "1 4"를 리턴하고, "-1 -2 -3 -4"라면 "-4 -1"을 리턴하면 됩니다.

return
"1 2 3 4" "1 4"
"-1 -2 -3 -4" "-4 -1"
"-1 -1"  "-1 -1"

 

  • 답안
def solution(s):
    answer = ''
    nums = s.split(' ')
    num_list = list(map(int, nums))
    
    min_num = min(num_list)
    max_num = max(num_list)
    
    answer = f'{min_num} {max_num}'
    return answer

 

  • 이 문제의 키워드
    • 문자열을 여러 요소로 나눈다던가, 공백을 제거해주는 과정이 필요할 때, s.split(' ') 이용하기
    • int(a), int(b) 여러번 쓸 필요없이 map(int, list) 로 한번에 처리해주자
    • map은 <map ~~> 객체로 저장되므로 list() 해주자
    • 쉽지만 문자열을 다루는 법과 map의 사용에 대해 다시 한번 생각해 볼 수 있는 문제
  • 문제

효진이는 멀리 뛰기를 연습하고 있습니다. 효진이는 한번에 1칸, 또는 2칸을 뛸 수 있습니다. 칸이 총 4개 있을 때, 효진이는
(1칸, 1칸, 1칸, 1칸)
(1칸, 2칸, 1칸)
(1칸, 1칸, 2칸)
(2칸, 1칸, 1칸)
(2칸, 2칸)
의 5가지 방법으로 맨 끝 칸에 도달할 수 있습니다. 멀리뛰기에 사용될 칸의 수 n이 주어질 때, 효진이가 끝에 도달하는 방법이 몇 가지인지 알아내, 여기에 1234567를 나눈 나머지를 리턴하는 함수, solution을 완성하세요. 예를 들어 4가 입력된다면, 5를 return하면 됩니다.

 

  • 답안
def solution(n):
    dp = [0] * (n+3)
    dp[1] = 1
    dp[2] = 2
    
    for i in range(3, n+3):
        dp[i] = dp[i-2] + dp[i-1]
    return dp[n] % 1234567

 

 

  • 이 문제 키워드
    • 피보나치로 해결. 재귀를 사용하면 시간효율성 낮고, depth 조절을 해야된다. DP로 하면 반복 범위가 정해지는 효과
    • 피보나치를 판단하는 방법은 각 경우를 계산해서 1, 2, 3, 5, 8, 13,,,, 해보니 피보나치! 이게 아님
    • 5칸의 경우: * -> 점프를 의미
      • Case 1: * * * * / *       4번째 점프까지하고 1칸 건너는 한가지 경우 (n-1)항 경우의수 x 1
      • Case 2: * * * / * *       3번째 점프까지하고 2칸 건너는 한가지 경우 (n-2)항 경우의수 x 1 
      • 그래서 5번째 칸까지 건너는 경우의 수가 (n-1)항 경우의수x1 + (n-2)항 경우의수x1 이 된 것! -> 피보나치
    • 다만, for i in range(3, n+3): 처럼 범위 지정할 때, 시작점이 끝점보다 작은지 꼭 확인하자.
  • 문제

얀에서는 매년 달리기 경주가 열립니다. 해설진들은 선수들이 자기 바로 앞의 선수를 추월할 때 추월한 선수의 이름을 부릅니다. 예를 들어 1등부터 3등까지 "mumu", "soe", "poe" 선수들이 순서대로 달리고 있을 때, 해설진이 "soe"선수를 불렀다면 2등인 "soe" 선수가 1등인 "mumu" 선수를 추월했다는 것입니다. 즉 "soe" 선수가 1등, "mumu" 선수가 2등으로 바뀝니다.

선수들의 이름이 1등부터 현재 등수 순서대로 담긴 문자열 배열 players와 해설진이 부른 이름을 담은 문자열 배열 callings가 매개변수로 주어질 때, 경주가 끝났을 때 선수들의 이름을 1등부터 등수 순서대로 배열에 담아 return 하는 solution 함수를 완성해주세요.

 

입출력 예
players callings result
["mumu", "soe", "poe", "kai", "mine"] ["kai", "kai", "mine", "mine"] ["mumu", "kai", "mine", "soe", "poe"]

 

  • 답안
def solution(players, callings):
    answer = []
    pl_dict = {value: idx for idx, value in enumerate(players)}
    
    for call in callings:
        ord_now = pl_dict[call]
        ord_fast = ord_now - 1 
        
        players[ord_now] = players[ord_fast] 
        players[ord_fast] = call
        
        pl_dict[call] -= 1
        pl_dict[players[ord_now]] += 1
        
    answer = players
    
    return answer

 

  • 이 문제 키워드
    • 해시로 해결하는 문제였음. 그러나, dict 는 key -> value 기능이 있지만 value -> key 조회 기능은 따로없음
    • key -> value 조회를 위해 dict를 설정하고, value -> key로 값 조회, 수정 할 때는 list에 작업
    • dict 에서 순위(value) 값에 변화를 주고, list에서 이 순위(index)를 조회해 변화를 주는 방향을 활용하자

 

  • 문제 

나만의 카카오 성격 유형 검사지를 만드려고 합니다.
성격 유형 검사는 4개의 지표에 따라 두 유형 중 하나로 결정됩니다.

 

1번 지표 라이언형(R) 튜브형(T)
2번 지표 콘형(C) 프로도형(F)
3번 지표 제이지형(J) 무지형(M)
4번 지표 어피치형(A) 네오형(N)

 

검사지에는 총 n개의 질문이 있고, 각 질문에는 아래와 같은 7개의 선택지가 있습니다.


매우 비동의, 비동의, 약간 비동의, 모르겠음, 약간 동의, 동의, 매우 동의


각 질문은 1가지 지표로 성격 유형 점수를 판단합니다.
예를 들어, 어떤 한 질문에서 4번 지표로 아래 표처럼 점수를 매길 수 있습니다.

이때 검사자가 질문에서 약간 동의 선택지를 선택할 경우 어피치형(A) 성격 유형 1점을 받게 됩니다. 만약 검사자가 매우 비동의 선택지를 선택할 경우 네오형(N) 성격 유형 3점을 받게 됩니다.

- 매우 동의나 매우 비동의 선택지를 선택하면 3점을 얻습니다.
- 동의나 비동의 선택지를 선택하면 2점을 얻습니다.
- 약간 동의나 약간 비동의 선택지를 선택하면 1점을 얻습니다.
- 모르겠음 선택지를 선택하면 점수를 얻지 않습니다.
- 검사 결과는 모든 질문의 성격 유형 점수를 더하여 각 지표에서 더 높은 점수를 받은 성격 유형이 검사자의 성격 유형이라고 판단

 

survey choices return
['AN', 'CF', 'MJ', 'RT', 'NA'] [5, 3, 2, 7, 5] 'TCMA'
['TR', 'RT', 'TR'] [7, 1, 3] 'RCJA'

 

  • 답안
score = {'R': 0, 'T': 0, 'C': 0, 'F': 0, 'J': 0, 'M': 0, 'A': 0, 'N': 0}

def solution(survey, choices):
    answer = ''
    for s, c in zip(survey, choices):
        if c > 4:
            score[s[1]] += c - 4
        if c < 4:
            score[s[0]] += 4 - c
    
    score_li = list(score.items())
    for i in range(0, len(score)-1, 2):
        if score_li[i][1] >= score_li[i+1][1]:
            answer += score_li[i][0]
        else:
            answer += score_li[i+1][0]
        
    return answer

 

  • 이 문제 키워드
    • dict 자체로 접근까지는 어렵지 않았으나, dict.items()로 key, value를 모두 호출하고, list로 바꾸어 주어 인덱싱하는 과정이 필요했음
    • 두 가지 객체를 같이 loop 할 때, zip 사용하기, 원소와 인덱스를 같이 loop 할 때, enumerate 사용하기, dict에서 key, value를 같이 loop 할 때, dict.items() 사용하기를 기억하자
    • sort할 대상의 수가 적으면 선언할 때, 순서대로 적자
    • 문자열은 .sort()가 되지 않는다 -> sorted()는 가능
    • 문제 잘보기 -> 인덱스와 그에 해당하는 번호를 혼동하지 말자

내 웹 서비스 페이지가 에러나는 경우(X)  웹 서비스에서 외부 URL을 호출할 때 에러나는 경우(O) 처리 방법

 

웹 서비스를 개발하다가 외부 URL(기사, 블로그 등)을 입력해 활용하는 기능을 추가하려는데,

페이지를 직접 접속하면 잘 열리지만, 서비스에 URL을 넣고 호출하면 error code가 나오면서 의도한 기능이 수행되지 않는 경우가 생긴다.

 

이 때는 직접 에러 페이지(403, 404, 500, 503 등)을 접속해서 확인할 수가 없으니, 해당 페이지의 예외처리를 따로 해줄 필요가 있다.

 

 

다만, 이 예시에서는 실제로 접속해서 에러를 확인할 수 있는 페이지를 다뤄보도록 한다.

이렇게 에러 페이지가 나오는 경우, html코드로 판단해 처리할 수 있다.

 

 

F12를 눌러 개발자 도구를 열면, <head> > <title>에 "404 Not Found"와 같이 에러코드가 적히게 된다.

그렇기 때문에, 페이지의 html 정보를 가져와서 title이 error code라면, 특정 행동을 하도록 코드를 짜주면 된다.

 

from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup

url = 'https://www.youtube.com/dsaqwd123'

try:
    open = urlopen(url)
    soup = BeautifulSoup(open, 'html.parser')
    print(soup)

except HTTPError as e:
    error_html = e.read()
    soup = BeautifulSoup(error_html, 'html.parser')

    title = soup.find('title').text
    print(title) # 404 Not Found

# if title == '404 Not Found':
#     에러 시 처리

 

크롤링 시 사용하는 BeautifulSoup으로 <title>에 있는 정보를 가져올 수 있고, 해당 부분의 text가 에러 코드일 경우의 처리를 해준다.

 

제로베이스 데이터 취업 스쿨 과정 학습 내용을 정리한 포스팅입니다.

 

📍 근삿값

특정 값과 가장 근사한 값을 배열에서 찾기

 

inputNum = 17
minNum = 30

num = [25, 15, 20, 10, 30]

for n in nums:
     absNum = abs(n-inputNum)
     
     if absNum < minNum:
         minNum = absNum
         nearNum = n
         
print(nearNum) # 15

- 특정값과 차이가 가장 작은 값이 근삿값이 된다(abs(n-inputNum))

- 배열을 돌면서 차이가 가장 작은 값이 나올 때, nearNum으로 저장한다.

 


📍 재귀

return 값으로 자신 함수를 다시 호출하는 것

 

def factorial(num):
    if num > 0:
        return num * factorial(num-1)
    else:
        return 1
        
        
print(factorial(10)) # 3628800

- factorial(팩토리얼)은 수를 1씩 빼면서 모두 곱하는 함수이다.

- 반복문을 사용하지 않고 바로 num-1한 값을 내 자신 함수에 넣어서 재귀적으로 호출한다.

 


📍 병합정렬

자료구조를 분할하고 각각의 분할된 자료구조를 정렬한 후 다시 병합하는 정렬 방법

 

 

def mSort(nums):
    if len(nums) < 2:
        return nums

    mid_idx = len(nums) // 2
    left_nums = mSort(nums[0:mid_idx])
    right_nums = mSort(nums[mid_idx:len(nums)])

    mergedNums = []
    left_idx = 0; right_idx = 0
    while left_idx < len(left_nums) and right_idx < len(right_nums):
        if left_nums[left_idx] < right_nums[right_idx]:
            mergedNums.append(left_nums[left_idx])
            left_idx += 1
        else:
            mergedNums.append(right_nums[right_idx])
            right_idx += 1

    mergedNums = mergedNums + left_nums[left_idx:]
    mergedNums = mergedNums + right_nums[right_idx:]
    print(f'mergedNums: {mergedNums}')
    return mergedNums


nums = [8, 1, 4, 3, 2, 5, 10, 6]
print(f'mergedNums: {mSort(nums)}') # 1, 2, 3, 4, 5, 6, 8, 10

- 좌우를 나눠서 서로 값을 비교하고, 더 작은 값을 병합해나간다.

- 좌우를 나눌 때마다 한쪽이 또 같은 함수를 적용하게 되기 때문에 재귀로 해결한다.

 


📍 퀵 정렬

기준 값보다 작은 값과 큰 값으로 분리한 후 다시 합치는 정렬 방법

 

 

 

def qSort(nums):

    if len(nums) < 2:
        return nums

    mid_idx = len(nums) // 2
    mid_val = nums[mid_idx]

    small_nums = []
    same_nums = []
    big_nums = []


    for n in nums:
        if n < mid_val:
            small_nums.append(n)
        elif n == mid_val:
            same_nums.append(n)
        else:
            big_nums.append(n)

    return qSort(small_nums) + same_nums + qSort(big_nums)


nums = [8, 1, 4, 3, 2, 5, 4, 10, 6, 8]
print(qSort(nums)) # 1, 2, 3, 4, 4, 6, 8, 8, 10

- 기준을 정하면 좌우가 아닌 크고 작은 값으로 같은 함수를 적용해야 할 대상이 정해진다.

- 기준보다 작은 쪽에 있는 수들에 같은 함수를 재귀적으로 적용하고, 기준보다 큰 쪽에 있는 수들에 같은 함수를 재귀적으로 적용해 마지막에 합쳐 준다.

 

제로베이스 데이터 취업 스쿨 과정 학습 내용을 정리한 포스팅입니다.

 

📍 이진탐색

정렬되어 있는 자료구조에서 중앙값과의 크고 작음을 이용해서 데이터를 검색한다.

 

nums = [4, 10, 22, 5, 0, 17, 7, 11, 9, 61, 88]

nums.sort()

searchData = 17

result_idx = -1

start_idx = 0
end_idx = len(nums) - 1
mid_idx = (start_idx + end_idx) // 2
mid_value = nums[mid_idx]

while True:
	if searchData == nums[len(nums) -1]:
        result_idx = len(nums) - 1
        break
    
    if searchData > mid_value:
        start_idx = mid_idx
        mid_idx = (start_idx + end_idx) // 2
        mid_value = nums[mid_idx]
        
    elif searchData < mid_value:
        end_idx = mid_idx
        mid_idx = (start_idx + end_idx) // 2
        mid_value = nums[mid_idx]
    
    if searchData == mid_value:
        result_idx = mid_idx
        break

- 검색(찾으려는 값과 일치하는 지 확인)할 때 중간값(mid_value)와 비교한 후, 반절을 고려 대상에서 제외해나가는 과정을 반복하기 때문에 빠르게 탐색할 수 있다.

 


📍 버블정렬

처음부터 끝까지 인접하는 인덱스의 값을 순차적으로 비교하면서 큰 숫자를 가장 끝으로 옮기는 알고리즘

 

nums = [10, 2, 7, 21, 0]

length = len(nums) - 1
for i in range(length):
    for j in range(length - i):
        if nums[j] > nums[j+1]:
            nums[j], nums[j+1] = nums[j+1], nums[j]
            
print(nums) # [0, 2, 7, 10, 21]

- 첫번째 for문의 첫 회에서 제일 큰 값이 맨 끝으로 가게 된다.

- 두번째는 그 다음으로 큰 값을 3번째에, 세번째는 그 다음으로 큰 값을 2번째에 두는 과정이 반복되는 것

 


📍 삽입정렬

왼쪽에 정렬되어 있는 자료배열과 비교하여 오른쪽 값의 정렬 위치를 찾아 삽입한다.

 

nums = [5, 10, 2, 1, 0]

for i1 in range(1, len(nums)):
    i2 = i1 - 1
    cNum = nums[i1]
    
    while nums[i2] > cNum and i2 >= 0:
        nums[i2 + 1] = nums[i2]
        i2 -= 1
        
    nums[i2 + 1] = cNum

- for 문에서 현재 기준이 되는 오른쪽의 값을 설정.

- while 문에서 왼쪽에 있는 값들이 기준 값보다 크면 값을 바꿔주는 과정 진행.

 

 


📍 선택정렬

주어진 리스트 중에 최소값을 찾아 그 값을 맨 앞에 위치한 값과 교체하는 방식

 

nums = [4, 2, 5, 1, 3]

for i in range(len(nums) -1):
    min_idx = i
    
    for j in range(i+1, len(nums)):
        if nums[min_idx] > nums[j]:
            min_idx = j
            
    nums[i], nums[min_idx] = nums[min_idx], nums[i]

+ Recent posts