본문 바로가기
알고리즘

프로그래머스 Lv0 알고리즘 풀이

by junvely 2023. 2. 21.

프로그래머스 Lv0 알고리즘 풀이

 

1. 몫 구하기 

정수 num1, num2가 매개변수로 주어질 때, num1을 num2로 나눈 몫을 return 하도록 solution 함수를 완성해주세요.

function solution(num1, num2) {
    return parseInt(num1/num2) //기존
    return Math.floor(num/num2) //더 나은 풀이1
    return Math.truns(num1/num2) //더 나은 풀이2
}

- parseInt() => 문자열에서 숫자 데이터만 추출하여 숫자열로 변환, 공백 무시, 소수점 무시 => 소수점 버림 기능으로 활용은 가능하나, 그 의도의 목적이 아닌, string을 number로 변환하기 위한 함수이기 때문에 불필요한 문자 변환 등의 기능이 포함되므로, 차라리 수학적으로 소수점 버림의 기능을 하는 Math.floor( )를 사용하는 것이 더 바람직한 것 같다. => 소수점을 버리는 Math.truc()가 존재 했음. 함수의 의미 상 가장 좋은 방법인 것 같다.

- Math.floor() : 내림 floor => 바닥까지 내린다고 생각 3.5 -> 3 -3.5 -> -4

- Math.trunc() : 소수점 이하는 버린다. => 소수점 버릴 때, 즉 정수만 추출할 때는 Math.floor보다는 Math.trunc가 더 좋은 것 같다.(함수의 의미상)

=> Math.floor()와 Math.trunc()의 차이 중요❗ : 만약 음수일 경우, Math.floor()는 -23.3인 경우에 내림을 하기 때문에 -24를 Return하게 된다. Math.trunc()는 그저 소주점 밑으로는 전부 버린다. 따라서 그저 소수점을 버릴 것이냐, 아니면 수학적인 계산이 필요하여 음수인 경우에도 적용할 것이냐에 따라 선택하면 될 것이다.

 

 

2. 짝수 홀수 갯수

정수가 담긴 리스트 num_list가 주어질 때, num_list의 원소 중 짝수와 홀수의 개수를 담은 배열을 return 하도록 solution 함수를 완성해보세요.

내 풀이)

function solution(num_list) {
    let even = 0;
    for(let num of num_list){
        if(num % 2 === 0){
            even++
        }
    }
    return [even,num_list.length - even];
    }

reduce를 이용한 풀이)

const num_list = [1, 2, 3, 4, 5, 6, 7];
const result = num_list.reduce(
  (acc, curr) => {
    curr % 2 === 0 ? acc[0]++ : acc[1]++;
    return acc;
  },
  [0, 0]
);
console.log(result);

 

3. 분수의 덧셈

첫 번째 분수의 분자와 분모를 뜻하는 numer1, denom1, 두 번째 분수의 분자와 분모를 뜻하는 numer2, denom2가 매개변수로 주어집니다. 두 분수를 더한 값을 기약 분수로 나타냈을 때 분자와 분모를 순서대로 담은 배열을 return 하도록 solution 함수를 완성해보세요.

function solution(numer1, denom1, numer2, denom2) {
  let answer = [0, 0];
  const topNum = numer1 * denom2 + numer2 * denom1;
  const botNum = denom1 * denom2;

  let maxNum;
  for (let i = 1; i <= topNum; i++) {
    if (topNum % i === 0 && botNum % i === 0) {
      maxNum = i;
    }
  }
  answer[0] = topNum / maxNum;
  answer[1] = botNum / maxNum;
  return answer;
}

두 분수를 더한 값의 분모, 분자를 최대 공약수를 구해 나누어 약분한 값을 배열[ , ]에 넣어 제출한다.

범위는 분모의 범위까지 설정

문제점 1) 분모의 숫자가 커질수록 for문을 돌리는 시간이 오래걸릴 것이다.

다른 분의 정답 풀이)

function fnGCD(a, b){
    return (a%b)? fnGCD(b, a%b) : b; // 이 부분이 이해가 안간다..
}

function solution(denum1, num1, denum2, num2) {
    let denum = denum1*num2 + denum2*num1;
    let num = num1 * num2;
    let gcd = fnGCD(denum, num); //최대공약수

    return [denum/gcd, num/gcd];
}

먼저, 분수는 1. 이미 기약분수일 경우, 2. 약분이 가능할 경우로 나뉜다.

1. 이미 기약분수일 경우 약분이 되는 숫자가 1밖에 없으므로 최대공약수는 1로 딱 떨어져 측정되어 denum/1 결과값은 그대로 출력될 것이다. ex) [12, 35] => 나머지가 1 => 최대 공약수 1

2. 약분이 가능할 경우는 다시

=>  1) a / b하여 나머지가 0일 경우 => 딱 떨어짐 => b가 최대 공약수가 된다.

=> 2) 나머지가 r일 경우, b분모를 a로 나머지를 b로 하여 다시 나눈다. 또 나머지가 있을 경우 다시 b 분모를 a로, 그 나머지를 b로 하여 떨어질 때까지 나눈다. 나머지가 0이되면 b가 최대 공약수 이다.

1. a를 분모(b)로 나눠서 떨어질 경우(===0) => 최대공약수는 분모(b)가 된다. => ok

 => ? 공식같은 건가.. 안떨어지면 분모를 나머지로 다시 나누기 ... 대입하여 계산했을 때 완벽하지만 왜 분모를 나머지로 나누다보면 떨어지는지에 대해 수학적으로 이해는 잘 가지 않는다..😢 이런 경우 수학문제를 풀 때 처럼 공식같이 외워야 하나..?

+같은 방식 while문을 이용한 풀이)

function solution(denum1, num1, denum2, num2) {
    function gcd(a, b) {
        while (b != 0) {
            let r = a%b;
            a = b;
            b = r;
            };
        return a;
        }

    let denum = denum1*num2 + denum2*num1;
    let num = num1*num2;
    let g = gcd(denum, num);

    return [denum/g, num/g];
}

 

4. 각도기 => if문의 다른 방법들

각에서 0도 초과 90도 미만은 예각, 90도는 직각, 90도 초과 180도 미만은 둔각 180도는 평각으로 분류합니다. 각 angle이 매개변수로 주어질 때 예각일 때 1, 직각일 때 2, 둔각일 때 3, 평각일 때 4를 return하도록 solution 함수를 완성해주세요.

평소 작성하던 if문)

function solution(angle) {
    if(0 < angle && angle < 90){
        return 1;
    }else if(angle===90){
        return 2;
    }else if(90 < angle && angle  < 180){
        return 3;
    }else if(angle === 180){
        return 4;
    }else{
        return;
    }
}

- 삼항 연산자 사용(복잡한 로직이 아닐 경우에만 중첩 사용하기)

function solution(angle) {
    return angle < 90 ? 1 : angle === 90 ? 2 : angle < 180 ? 3 : 4;
}

- 배열을 만들어 .filter 사용

function solution(angle) {
    return [0, 90, 91, 180].filter(x => angle>=x).length;
}

 

5. 최빈값 구하기

최빈값은 주어진 값 중에서 가장 자주 나오는 값을 의미합니다. 정수 배열 array가 매개변수로 주어질 때, 최빈값을 return 하도록 solution 함수를 완성해보세요. 최빈값이 여러 개면 -1을 return 합니다.

function solution(array) {
  // Map객체 생성(중복 제거 및 set, get, delete 등 가능)
  // 배열 요소들을 객체 내부에 key : length 형태로 추가
  let newArr = new Map();
  for (let val of array) {
    newArr.set(val, (newArr.get(val) || 0) + 1);
  }
  // 최빈값 구하기 => 새 배열 생성 후, array=>a[1]의 내림차순으로 변경
  newArr = [...newArr].sort((a, b) => b[1] - a[1]);
  // 최빈값이 중복일 경우 -1반환
  const maxCount = newArr.filter((num) => num[1] === newArr[0][1]).length;
  console.log(max);
  // return maxCount === 1 ? newArr[0][1] : -1;
  return maxCount === 1 ? newArr[0][0] : -1;
}

 

- Map() 객체 생성 : Map객체는 중복을 허용하지 않기 때문에 알아서 중복을 제거해 준다(객체의 중복 제거에서도 유용하게 사용가능). 또 set(), get(), delete() 등의 유용한 메소드를 사용할 수 있다. => Set객체도 있음, 둘의 차이점은 뭘까?

1) .get(' ') => 해당하는 key값을 찾아서 반환

2) .set(key, value) => 객체 내부에 설정한 key와 value 값으로 key : value를 추가, 수정해 준다.

3) .delete(' ') => 해당 하는 key값을 삭제한다.

 

- 풀이과정

1. 최빈값을 구하기 위해 key-length값의 짝으로 [ [ , ], [ , ], ... [ , ] ]배열 형식으로 생성해야 한다. 배열로 [key,value]쌍을 추가하는 것은 쉽지 않으므로(중복 제거, 배열은 키값으로 접근이 어려움, length++ 등), {key:value}를 쌍으로 추가 가능한 Map() 객체를 생성하여 .set() 메소드로 먼저 객체를 만들어 형태로 잡아놓은 후, 배열로 다시 변환한다.

- .get과 .set메소드를 사용하기 위해 new Map() 생성

-  for of 문으로 array의 value값을 가져와서 Map()객체에 set(key, value) key값으로 추가, value의 값 +1 하기

 

2. Map 객체를 내림차순으로 변경하고, filter로 최빈값의 중복 여부를 확인해야 하기 때문에 => newArr객체를 복사하여 배열로 변경하고 [ [1,2], [2,3] ... [4,1] ] 형태로 배열을 생성

- 내림차순으로 변경 : sort() 를 이용해 콜백으로 a[1] - b[1] 내림차순으로 => *a - b뿐만 아니라 a[1]] - b[1] 형태로 세부 조정이 가능한줄 처음 알았다..신세계

- 최빈값의 중복여부 확인 위해 filter => newArr[0][1] 최빈값과 중복되는 갯수 length를 확인

 

3. maxLength가 1 ? 일 경우 그대로 최빈값(newArr[0][0])출력 : 아닐경우 -1 return

마지막에 최빈값의 갯수를 return하라는줄 알고 계속 newArr[0][1]을 return했는데 정답률이 37.5로 나와서 멘붕이었다....알고보니 최빈값의 값을 return하라는 것이었다....후...내 시간...다음부턴 문제를 꼼꼼히!! 확인하자..😢

 

 

6. 옹알이(1)

❗regex => 정규 표현식을 이용하여 문자열을 검사하는 간단한 방법!! 반드시 숙지 하자.

그동안 정규 표현식은 아이디나 비밀번호 유효성 검사 등, 숫자나 문자열 타입을 지정하는 데에만 이용했었는데, 이렇게 특정 문자열들에 한정하여 검사할 수 있는지 처음 알았다.... 너무 어려워서 계속 고민하다 포기했는데 이렇게 간단한 방법이 있을 줄 처음 알았다... 반드시 숙지 해 놓도록 하자!!

  function solution() {
    const babbling = ["ayaye", "uuuma", "ye", "yemawoo", "ayaa"];
    //["aya", "ye", "woo", "ma"] 검사하기

    const regex = /^(aya|ye|woo|ma)+$/; //해당 문자열들로만 이루어져 있는지 검사(true/false)
    let count = 0;

    babbling.forEach((word) => regex.test(word) && count++);

    return count;
  }

  solution();

내 풀이)

function solution() {
    const babbling = ["ayaye", "uuuma", "ye", "yemawoo", "ayaa"];
    const array = ["aya", "ye", "woo", "ma"];
    let a = 0;
    for (let word of babbling) {
      let newStr = word; // 2. 원래 for문의 if문 안에 있었는데 밖으로 빼주니까 정상처리됨

      for (let j = 0; j < array.length; j++) {
        const wordIncludes = word.includes(array[j]); // 단어가 있는지 검사

        // 단어가 있을 경우만
        if (wordIncludes) {
          // 단어를 제거하고 length가 0이면 +1
          newStr = newStr.replace(array[j], ""); // 1. let newStr을 여기서 선언했는데 비정상처리..for문 밖으로 뺴줌
          newStr.length === 0 && a++;
        }
      }
    }
  }
  solution();