240515_TIL

J Lee·2024년 5월 15일
0

아무리 사소하더라도 배움이 없는 날은 없다.

SQL코드카타 117번
친구 수가 가장 많은 '사람'과 그의 '친구 수'를 구하는 문제.

requester, accepter를 통틀어 가장 많은 id가 찍힌 사람이 친구 수가 가장 많다는 뜻이므로, 먼저 requester 사이드와 accepter 사이드에서 id별 건수를 각각 구한 후에 하나로 합쳐준다. 이 때 중복되는 id를 뭉개면 안 되므로 그냥 UNION이 아니라 UNION ALL을 쓴다는 점에 주의.

SELECT requester_id AS id,
       Count(*)     AS num
FROM   requestaccepted
GROUP  BY 1
UNION ALL
SELECT accepter_id AS id,
       Count(*)    AS num
FROM   requestaccepted
GROUP  BY 1 

그리고 이 테이블을 인라인뷰 서브쿼리로 처리해서 id와 num의 sum을 구해주고 id별로 그루핑하면 고유 id별로 친구 수를 구할 수 있다.

SELECT id,
       Sum(num) AS num
FROM   (SELECT requester_id AS id,
               Count(*)     AS num
        FROM   requestaccepted
        GROUP  BY 1
        UNION ALL
        SELECT accepter_id AS id,
               Count(*)    AS num
        FROM   requestaccepted
        GROUP  BY 1) a
GROUP  BY 1

이제 가장 많은 친구 수를 가진 id만 찾으면 되므로 sum(num)을 기준으로 내림차순 정렬하고 limit 1을 적용하면 정답.

SELECT id,
       Sum(num) AS num
FROM   (SELECT requester_id AS id,
               Count(*)     AS num
        FROM   requestaccepted
        GROUP  BY 1
        UNION ALL
        SELECT accepter_id AS id,
               Count(*)    AS num
        FROM   requestaccepted
        GROUP  BY 1) a
GROUP  BY 1
ORDER  BY 2 DESC
LIMIT  1

정답은 비교적 쉽게 구할 수 있었는데, 나중에 보니 문제에서 추가적인 조건을 하나 더 주었다.

만약 최대 친구 수가 동률일 경우에는 복수의 id를 출력해야 한다는 것. 이럴 경우에는 limit 1으로 처리하기 어렵다. 약간 복잡해지긴 하지만,

SELECT id,
       Count(*) AS num
FROM   (SELECT requester_id AS id
        FROM   requestaccepted
        UNION ALL
        SELECT accepter_id AS id
        FROM   requestaccepted) a
GROUP  BY 1
HAVING num = (SELECT Count(id)
              FROM   (SELECT requester_id AS id
                      FROM   requestaccepted
                      UNION ALL
                      SELECT accepter_id AS id
                      FROM   requestaccepted) b
              GROUP  BY id
              ORDER  BY 1 DESC
              LIMIT  1) 

이 방법으로 구할 수 있을 것 같다. 위의 코드와 원리는 거의 동일하지만, 본 쿼리 제일 마지막 부분을

  • group by 1 + order by 2 desc + limit 1

대신에

  • group by 1 + having num = (최대로 큰 숫자)

로 바꿔준 버전이다.


알고리즘 코드카타 41번

문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.

제한 사항

  • 문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.
  • 첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.

갈수록 이상해지는(...) 파이썬 문제. 오늘은 정말로 '이상한 문자 만들기'다.

단어별로 index에 따라 홀수/짝수를 구분한 다음 홀수면 소문자로, 짝수면 대문자로 바꿔서 리턴하는 알고리즘을 만드는 문제. 문자열 s는 한 개 이상의 단어로 이루어져 있고, 각 단어는 하나 이상의 공백으로 구분되어 있다고 한다.

먼저 입력받은 문자열 s를 공백에 따라 분리한 리스트를 만든다.

def solution(s):
    s1 = s.split(' ')

이제 리스트 s1에서 각 요소를 돌며 인덱스별 홀짝 여부를 판단한다. 이 때, 각 요소(단어)별로 길이가 다르므로 단어별로 반복문을 또 돌려야 한다.

def solution(s):
    s1 = s.split(' ')
    for i in s1:
        for j in range(len(i)):
            if j%2==0:
                i[j] = i[j].upper()
            else:
                i[j] = i[j].lower()

여기까지만 적어서 실행시켜 봤는데 오류가 났다.
TypeError: 'str' object does not support item assignment

파이썬에서 문자열은 불변(immutable)하기 때문에, 위처럼 인덱스를 통해 접근(i[j])해서 직접 할당해 주려고 하면 오류가 난다. 한 마디로 새로운 문자열을 정의하는 건 가능하지만, 있는 문자열을 곧장 바꾸는 건 안된다는 얘기. 문자열을 변경하려면 항상 새로운 문자열 객체를 생성하여 반환하는 방식을 사용해야 한다.

따라서 이 오류를 피하려면

  1. 대소문자를 변환해서 저장해 줄 임시 문자열과
  2. 1을 담아줄 임시 리스트를 따로 선언해 주어야 한다.

아래는 수정사항을 반영한 정답 코드.

def solution(s):
    s1 = s.split(' ')
    answer = []
    for i in s1:
        new = ''
        for j in range(len(i)):
            if j%2==0:
                new += i[j].upper()
            else:
                new += i[j].lower()
        answer.append(new)    
    return ' '.join(answer)

대소문자로 변환한 결과를 임시 문자열 new에 담고 그걸 다시 임시 리스트 answer에 담은 다음 join하는 방식이다.

파이썬 코드카타를 풀어가면서 조금씩 감을 잡고 있는 부분인데, 알고리즘을 만들 때는

  1. 연산 자체의 결과
  2. 1을 어디에 담아서 (↔ 어디에 담을 수 없는지)
  3. 2를 어떻게 출력할지 (↔ 어떻게 출력할 수 없는지)

이렇게 세 개의 축을 모두 고려해야 풀 수 있다. 당연히 1번부터 맞게 하는 게 출발점이지만 2,3번의 문법과 예외를 모르면 완성이 안 되더라. 다음번에도 쉽게 찾아볼 수 있게 오늘 풀이를 잘 정리해 두기.

profile
기본기를 소홀히 하지 말자

0개의 댓글