📍 문제

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

📍 풀이

고려해야 할 조건들부터 살펴보자!

 

1. 음식 종류별즐겨찾기 수가 가장 많은 식당의
    => 음식 종류마다 즐겨찾기 수가 가장 많은 식당 하나만 가져오기

2. 음식 종류를 기준으로 내림차순 정렬

 

 

내가 처음 쓴 코드는

1번 조건을 위해 MAX()를 이용하였다.

SELECT FOOD_TYPE, REST_ID, REST_NAME, MAX(FAVORITES) AS FAVORITES
FROM REST_INFO
GROUP BY FOOD_TYPE
ORDER BY FOOD_TYPE DESC

이 코드를 실행하면 다음과 같은 결과가 나온다.

얼핏 보면 정답과 일치해 보이지만, 일식의 경우 정답과 다른 결과가 나온다.

 

이런 결과가 나오는 이유는

GROUP BY로 묶으면 가장 상단에 있는 데이터들을 임의로 가져온다.

따라서, SELECT에 MAX를 해도 최대값을 가져오는 게 아니라 그룹화된 테이블 가장 상단에 있는 값을 가져온다.

 

 

그렇다면 어떻게 해야 할까?

이 문제의 핵심은,

 

서브 쿼리를 사용해 최대값을 따로 찾아주어야 한다

SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES
FROM REST_INFO
WHERE (FOOD_TYPE, FAVORITES)
IN
(SELECT FOOD_TYPE, MAX(FAVORITES)
FROM REST_INFO
GROUP BY FOOD_TYPE)
ORDER BY FOOD_TYPE DESC

 

 

복잡해 보이는 이 쿼리가 어떤 순서로 진행되는지 살펴보자.

그 전에 참고할 SQL의 쿼리 실행 순서 관련 :

 

sql 의 쿼리 실행 순서

서브쿼리에 대한 문제를 풀다가 왜 서브쿼리를 써야하지 이해가 안됬는데 실행 순서떄문에 그런것이였다 ㅇㅁㅇ 그런 기념으로 날잡고 한번 실행순서 정리해봅니다 1.FROM 절 (+ Join) 가장 먼저

monawa.tistory.com

 

 

1. FROM REST_INFO : REST_INFO 테이블에서 데이터 가져오기

2. GROUP BY FOOD_TYPE : 'FOOD_TYPE' 칼럼을 기준으로 데이터 그룹화
    => 각 'FOOD_TYPE'의 식당들이 하나의 그룹으로 묶임

3.  SELECT FOOD_TYPE, MAX(FAVORITES) : 각 그룹에서 'FAVORITES'의 최대갑승ㄹ 찾고 해당하는 'FOOD_TYPE' 선택

4. IN : 서브 쿼리가 완성되었고, 이 결과가 바깥쪽 쿼리의 WHERE 조건과 매치되는지 확인하기 위해 사용

5. WHERE (FOOD_TYPE, FAVORITES) IN : 바깥 쿼리에서 'FOOD_TYPE'과 'FAVORITES'가 내부 쿼리의 결과와 일치하는 행들을 선택합니다. 이는 각 'FOOD_TYPE' 마다 즐겨찾기 수가 가장 많은 식당만을 추출하는 작업

6. SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES : 선택된 행들에서 'FOOD_TYPE', 'REST_ID', 'REST_NAME', 'FAVORITES' 칼럼을 선택

7. ORDER BY FOOD_TYPE DESC : 결과를 'FOOD_TYPE' 을 기준으로 내림차순으로 정렬

📍 문제

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

📍 풀이

이 문제의 핵심은 UNION 을 사용하는 것!

 

오프라인/온라인의 쿼리 결과를 합치기 위해 UNION을 사용해 쿼리의 결과를 합치고, 중복된 행은 제거한다.

이때, UNION 연산을 사용하면 중복값은 제거되어 하나만 추가된 새로운 테이블을 만든다.

두 개의 결과를 결합하는 연산자에는

UNION UNION ALL 연산이 있는데,

두 연산의 차이점을 요약하자면

  • UNION : 중복된 행을 제거하여 보여줌 ( 결과는 정렬되어 반환됨)
  • UNION ALL : 중복된 행 제거하지 않고 모든 행을 포함하여 보여줌 ( 결과는 정렬되지 않고 반환됨)

 

 

그렇다면, 고려해야할 조건들을 살펴보자!

 

1. 2022년 3월의 오프라인/온라인 상품

2. SALES_DATE 출력 형태 "2022-03-01" 

3. ❗️OFFLINE_SALE 테이블의 판매 데이터의 USER_ID 값은 NULL 로 표시❗️

4. 판매일, 상품ID, 유저ID 순으로 오름차순 정렬

 

1. YEAR(), MONTH() 를 활용하여 2022년 3월의 상품만 조회

WHERE YEAR(SALES_DATE) = 2022 AND MONTH(SALES_DATE) = 3

 

2. DATE_FORMAT 활용하여 "%Y-%m-%d" 로 출력 형태 지정

SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT

 

3.  NULL AS 활용하여 OFFLINE_SALE 테이블의 판매 데이터의 USER_ID 값은 NULL로 표시

SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE, 
PRODUCT_ID, NULL AS USER_ID, SALES_AMOUNT

 

4. ORDER BY 를 활용하여 판매일, 상품ID, 유저ID 순으로 오름차순 정렬

ORDER BY SALES_DATE, PRODUCT_ID, USER_ID

 

 

📍 전체 코드

SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM ONLINE_SALE
WHERE YEAR(SALES_DATE) = 2022 AND MONTH(SALES_DATE) = 3
UNION
SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE, PRODUCT_ID, NULL AS USER_ID, SALES_AMOUNT
FROM OFFLINE_SALE
WHERE YEAR(SALES_DATE) = 2022 AND MONTH(SALES_DATE) = 3
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID

📍 문제

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

📍 풀이

고려해야할 조건들을 살펴보자!

 

1. 자동차 종류가 'SUV'

2. 평균 일일 대여 요금

3. 소수 첫 번째 자리에서 반올림

4. 컬럼명 : AVERAGE_FEE

 

1. WHERE절 활용하여 자동차 종류가 "SUV" 인 것만 조회

WHERE CAR_TYPE = "SUV"

2. 집계 함수 AVG() 활용하여 평균 일일 대여 요금 구하기

SELECT ROUND(AVG(DAILY_FEE), 0) AS AVERAGE_FEE

3. ROUND() 함수 적용하여 소수 첫 번째 자리에서 반올림

SELECT ROUND(AVG(DAILY_FEE), 0) AS AVERAGE_FEE

4. AS를 활용하여 컬럼명을 AVERAGE_FEE로 지정

 

SELECT ROUND(AVG(DAILY_FEE), 0) AS AVERAGE_FEE

 

📍 코드

SELECT ROUND(AVG(DAILY_FEE), 0) AS AVERAGE_FEE
FROM CAR_RENTAL_COMPANY_CAR
WHERE CAR_TYPE = "SUV"

 

📍 문제

 

📍 풀이

입출력 예의 첫 번째인 [6, 10, 2]를 보자.

예상했겠지만 이 문제를 integer로 정렬하면 답은 1062 가 된다. => 틀림!

즉, integer로 정렬하면 올바를 답을 구할 수 없다.

 

따라서, string으로 변환 후, 비교해야 한다.

 

먼저 입력 받은 numbers 들을 string으로 변환하여 vector에 삽입한다.그리고 나서 정렬을 할 건데, (여기서 sort사용자 정의 compare 함수를 사용하였다.)

 

compare 함수에서는for문을 돌며

 

a[i]와 b[i]가 같다면  a+b를 int로 바꾼 것과 b+a를 int로 바꾼 것 중 더 큰 수를 가지는 문자열을 결정한다.(예를 들어 "3""30"을 비교할 때, 0번째 인덱스에서 두 문자열이 동일한 숫자를 가지고 있다. 따라서 stoi("3"+"30")stoi("30"+"3") 를 비교하면 330이 303보다 크다. )

 

그렇지 않다면, (a[i]와 b[i]가 같지 않다면)

a[i]와 b[i]를 비교하여 더 큰 숫자를 가진 문자열을 결정한다.

 

 

그런데, 이렇게 코드를 쓰고 제출하면 테스트케이스 11에서만 실패가 뜬다.

 

 

🖐 여기서 잠깐!

이 문제에는 예외 처리 해야 하는 케이스가 하나 있다.

모든 numbers가 0일 때 ( [0, 0, 0, 0] 의 경우, "0000"으로 출력됨)

따라서, answer의 첫 원소가 "0"인 경우, "0"을 return하는 예외 처리를 해 주어야 한다.

 

📍 코드

#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

bool compare(string a, string b) {
    bool answer = true;

    int mx = max(a.size(), b.size());

    for (int i{ 0 }; i < mx; i++) {
        if (int(a[i]) == int(b[i])) {
            answer = stoi(a + b) > stoi(b + a);
            break;
        }
        else {
            answer = a[i] > b[i];
            break;
        }
    }
    
    return answer;
}

string solution(vector<int> numbers) {
    string answer = "";
    vector<string> v;

    for (int i{ 0 }; i < numbers.size(); i++) {
        v.push_back(to_string(numbers[i]));
    }

    sort(v.begin(), v.end(),compare);

    if (v[0] == "0") {
        return "0";
    }

    for (int i{ 0 }; i < v.size(); i++) {
        answer += v[i];
    }

    return answer;
}

📍 문제

📍 풀이

자료구조 deque 사용

deque에 입력 받은 원소들 삽입

 

첫 번째 for문에서

먼저,

deque의 첫번째 원소를 변수 a에 저장 (a에 저장된 값이 현재 계산할 주식 price)

deque의 첫번째 원소 pop

 

그리고 두 번째 for문에서

1) a (현재 비교를 할 price)보다 deque의 i번째 원소보다 크거나 같으면 cnt++ 를 해 준다 (가격이 떨어지지 않은 경우)

2) 그렇지 않으면, 가격이 떨어졌으므로, cnt++를 해 주고 break로 반복문을 빠져나간다

여기서 cnt++를 해 주는 이유는 [ 1, 2, 3, 2, 3 ] 의 예에서 3->2 에서 가격이 떨어지지 않은 시간은 1이다.

 

🤔 여기서 잠깐!

이 문제는 prices의 길이는 10,000 이하이다.

따라서 보통의 문제에서는 이중 for문을 돌리게 되면, O(N^2)으로 시간초과가 나게 된다.

하지만 특이하게도 이 문제에서는 시간초과가 나지 않는다.

O(N)의 시간복잡도로 푸는 방법에 대해서도 생각해 보아야 한다. => 이후 글 참고~!

 

📍 코드

#include <vector>
#include <deque>
#include <iostream>
using namespace std;

vector<int> solution(vector<int> prices) {
    vector<int> answer;
    
    deque<int> d;

    for (int i{ 0 }; i < prices.size(); i++) {
        d.push_back(prices[i]);
    }

    for (int i{ 0 }; i < prices.size(); i++) {
        int cnt = 0;
        int a = d.front();
        d.erase(d.begin());

        for (int i{ 0 }; i < d.size(); i++) {
            if (a <= d[i]) {
                cnt++;
            }
            else {
                cnt++;
                break;
            }
        }
        answer.push_back(cnt);
    }
    return answer;
}

📍 문제

📍 풀이

  • 자료구조 스택 활용
  • '(' 를 담을 stack<char> open 선언

 올바른 괄호가 아닌 조건 2가지 

 1)  ')' 가 들어왔는데 스택이 비어있는 경우

 2)  마지막에 스택에 데이터가 남아있는 경우

 

입력 받은 string s의 길이만큼 반복문을 돌며

▶️ '(' 일 때 => 스택에 push

▶️ ')' 일 때 => 스택에서 pop

    이 때,  1)  스택이 비어있어서 pop을 하지 못하는 경우 => 올바르지 않은 괄호

 

반복문을 다 돌고도,  2)  마지막에 스택에 데이터가 남아있는 경우 => 올바르지 않은 괄호

 

📍 코드

#include <string>
#include <stack>
#include <iostream>
using namespace std;

bool solution(string s)
{
    bool answer = true;

    stack<char> open;

    for (int i{ 0 }; i < s.size(); i++) {
        // '(' 일 때
        if (s[i] == '(') {
            open.push(s[i]);
        }
        // ')' 일 때
        else {
            // ')'일 때, pop을 하려고 하는데 스택이 비어있다 -> 올바르지 않은 괄호
            if (open.size() == 0) {
                return false;
            }
            else {
                open.pop();
            }
        }
    }

    // 마지막에 스택에 데이터가 남아있다 -> 올바르지 않은 괄호
    if (open.size() != 0) {
        answer = false;
    }

    return answer;
}

📍 문제

📍 풀이

'('를 저장할 stack s1과

')'를 저장할 stack s2를 만든다.

 

입력 받은 string s를 첫번째 index부터 돌면서 '('')'를 각각의 stack에 삽입한다.

 

그런데, ')'일 때,

만약 s1의 size가 0이면 false이다.

예를 들어 "())"의 경우 마지막 ')'와 대응되는 '('가 없다.

 

즉, ')'일 때는 이와 대응되는 '('가 있어야 하기 때문에 s1의 사이즈가 0이면 false가 된다.

 

s1의 size가 0보다 크면,

s1과 s2 둘 다 pop 한다.

 

이렇게 for문을 돌고 난 후,

s1과 s2의 size는 둘 다 0이어야 true가 된다. (남아있는 원소가 없어야 한다)

 

 

테스트케이스 추가

"())(()" -> false

 

📍 코드

#include<string>
#include <iostream>
#include <stack>
using namespace std;

bool solution(string s)
{
    bool answer = true;

    stack<char> s1; // (
    stack<char> s2; // )

    for (int i{ 0 }; i < s.size(); i++) {
        if (s[i] == '(') {
            s1.push('(');
        }
        else {
            s2.push(')');
            if (s1.size() <= 0) {
                return false;
            }
            else {
                s1.pop();
                s2.pop();
            }
        }
    }

    if (s2.size() == 0 && s1.size() == 0) {
        answer = true;
    }
    else {
        answer = false;
    }

    return answer;
}

📍 문제

 

📍 풀이

길이가 같은 2개의 배열에서 각 원소들을 하나씩 매칭시켜 곱해 더했을 때 누적값이 최솟값을 구하려면,쉽게 말해, 두 수를 곱해 최소가 되려면  최대값 * 최소값  을 하면 된다.

 

따라서A 배열오름차순 정렬을 하고B 배열내림차순 정렬을 해서

 

for문을 돌며 각 배열의 같은 index 원소를 곱해 answer에 누적해 주면 된다.

 

 

📍 코드

#include <vector>
#include <algorithm>
using namespace std;

int solution(vector<int> A, vector<int> B)
{
    int answer = 0;

    sort(A.begin(), A.end());
    sort(B.begin(), B.end());
    reverse(B.begin(), B.end());

    for (int i{ 0 }; i < A.size(); i++) {
        answer += A[i] * B[i];
    }

    return answer;
}

+ Recent posts