TIL: 2022-05-17 알고리즘 day4

김하연·2022년 5월 17일
0

TIL: Today I Leaned

목록 보기
8/26

1. 제일 작은 수 제거하기

정수를 저장한 배열, arr 에서 가장 작은 수를 제거한 배열을 리턴하는 함수, solution을 완성해주세요. 단, 리턴하려는 배열이 빈 배열인 경우엔 배열에 -1을 채워 리턴하세요. 예를들어 arr이 [4,3,2,1]인 경우는 [4,3,2]를 리턴 하고, [10]면 [-1]을 리턴 합니다.

예시

입출력 예

arr			return
[4,3,2,1]	[4,3,2]
[10]		[-1]

내가 작성한 solution.js

function solution(arr) {
    var answer = [];
    if(arr.length === 1){
        answer = [-1];
    }else{
        arr.splice(arr.indexOf(Math.min(...arr)),1)
        answer = [...arr];
    }
    return answer;
}

arr의 length가 1일 경우를 체크해서 -1을 answer에 넣고, 그게 아닐 경우에는 Math.Min()을 사용해 arr에서 가장 작은 값의 인덱스를 찾고 그 요소를 arr에서 제거한 뒤 answer에 대입하는 방식으로 풀이했다.




2. 콜라츠 추측

1937년 Collatz란 사람에 의해 제기된 이 추측은, 주어진 수가 1이 될때까지 다음 작업을 반복하면, 모든 수를 1로 만들 수 있다는 추측입니다. 작업은 다음과 같습니다.

1-1. 입력된 수가 짝수라면 2로 나눕니다. 
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다.
2. 결과로 나온 수에 같은 작업을 1이 될 때까지 반복합니다.

예를 들어, 입력된 수가 6이라면 6→3→10→5→16→8→4→2→1 이 되어 총 8번 만에 1이 됩니다. 위 작업을 몇 번이나 반복해야하는지 반환하는 함수, solution을 완성해 주세요. 단, 작업을 500번을 반복해도 1이 되지 않는다면 –1을 반환해 주세요.

예시

입출력 예

n		result
6		8
16		4
626331	-1

내가 작성한 solution.js

function solution(num) {
    var answer = 0;
    for(let i=1; i<501; i++){
        num = num%2 ? (num*3)+1 : num/2;
      	if(num === 1) return i;
      	else if(i === 500) return -1;
    }
    return answer;
}

처음엔 이렇게 작성하고 제출하기 했더니 딱 한가지 케이스가 통과를 못하길래 뭐가문제일까 하고 고민을 해봤다. 다른 케이스들은 잘 넘어가는데 단 한개의 케이스가 걸린다니 그럼 딱 하나 예외가 될 수 있을만한 숫자가 있을 것 같았고, 생각해보니 num의 값이 1일 경우에는 내 코드로 올바른 정답을 낼 수가 없었다.
애초에 들어온 숫자가 1이라면 계산을 할 필요가 없으니 답은 0이 출력되어야 하는데, 처음 loop에 들어온 num이 이미 1인지 아닌지를 체크하는 코드가 없었다.

function solution(num) {
    var answer = 0;
    for(let i=1; i<501; i++){
        if(num === 1){
            return 0;
        }else{
            num = num%2 ? (num*3)+1 : num/2;
            if(num === 1) return i;
            else if(i === 500) return -1;
        }
    }
    return answer;
}

그래서 if 조건문을 하나 더 추가해서 num이 1일 경우에는 바로 0을 반환하도록 하고, 그게 아닐 경우 num을 계산하는 식이 작동하도록 수정하였다.
테스트는 통과했지만, if문 안에 if문이 또 있어서 약간 찝찝한 너낌...?

++ 추가로, 팀원분들고 리뷰 하면서 알게된건데 이 경우에는 사람들이 while문을 for문보다 많이 활용했고, 그 이유가 for문은 특정 length가 정해져있다면 쓰기에 유용하고 while문일 경우에는 length가 없으면서 특정 조건들을 만족시켜야 할 때 쓰면 더 좋다고 한다.

++ 그리고 if안에 if가 찝찝해서 검색해봤는데, 링크 내용을 확인해보면 if문이 논리적으로 짜여져있으면서 간단히 알아볼 수 있게 되어있다면 상관이 없지만 if문이 네스팅되지 않아도 되는 구조라면 수정해주는게 좋다. 또한 if문이 네스팅 되어있는 구조 자체는 논리적으로 문제가 되지는 않지만 다른 사람들과 함께 작업할 때 알아보기 어렵다거나 무언가를 빼먹을 수 있는 이슈가 있으므로 결론은... 최대한 논리적이고 간단 명료하게 짜라는 것 같다...ㅎ ..어렵네 ㅎㅎ..




3. 하샤드 수

양의 정수 x가 하샤드 수이려면 x의 자릿수의 합으로 x가 나누어져야 합니다. 예를 들어 18의 자릿수 합은 1+8=9이고, 18은 9로 나누어 떨어지므로 18은 하샤드 수입니다. 자연수 x를 입력받아 x가 하샤드 수인지 아닌지 검사하는 함수, solution을 완성해주세요.

  • x는 1 이상, 10000 이하인 정수입니다.

예시

입출력 예

arr	return
10	true
12	true
11	false
13	false

내가 작성한 solution.js

function solution(x) {
    var answer = true;
    let sum = (x+'').split('').reduce((acc,cur)=> acc = acc + (+cur),0)
    return answer = (x%sum === 0);
}

이 문제도 숫자로 구성된 문자열을 더해야해서 우선 x에 ''문자열을 더해서 string형태로 바꿔주고, split으로 나눠서 배열로 만들었다. 그 다음에 reduce를 사용해서 배열 안의 모든 숫자를 더한 값을 sum에 담았고, x라는 숫자를 sum으로 나누었을 때 나머지가 0이면 하샤드 수이므로 true를, 아닐 경우 false를 반환하게 하였다.

    return answer = x%sum === 0 ? true : false;
    return answer = (x%sum === 0)
	// 원래 return 내용을 위의 첫번째 방식으로 작성했었는데,
	// (x%sum === 0) 이 조건 자체가 true혹은 fasle로 반환되기에 삼항연산자는 쓰지 않아도 돼서 삭제했다.



4. 3진법 뒤집기

자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.


예시

입출력 예

입출력 예 #1

n (10진법)		n (3진법)		앞뒤 반전(3진법)		10진법으로 표현
45				1200			0021				7
따라서 7을 return 해야 합니다.

입출력 예 #2

n (10진법)		n (3진법)		앞뒤 반전(3진법)		10진법으로 표현
125				11122			22111				229
따라서 229를 return 해야 합니다.

내가 작성한 solution.js

function solution(n) {
    var answer = 0;
    return answer = Number.parseInt((n.toString(3).split('').reverse().join('')), 3);
}

이 문제는 애초에 10진법, 3진법을 모르는 나는... 좀 당황스러웠는데 3진법 변환하는 방법을 검색해보니 자바스크립트 자체 진법 변환 기능이 있다는걸 찾았고 그 방법을 사용했다.

var value = 10;
// 10 진법 -> 2, 8, 16 진법으로 변환
value.toString(2);    // 1010
value.toString(8);    // 12
value.toString(16);   // a

var bin = 1010,
    oct = 12,
    hex = 'a';
// 2, 8, 16 진법 -> 10진법으로 변환
Number.parseInt(bin, 2);    // 10
Number.parseInt(oct, 8);    // 10
Number.parseInt(hex, 16);   // 10

n을 toString(3)으로 3진법으로 변환한 후, 문자를 split('')으로 배열에 담은 뒤 배열의 순서를 뒤집은 reverse()를 사용했다. 그 후 배열을 다시 join('')으로 풀어주고 그 값을 문자열을 분석하여 특정 진수의 정수로 만들어주는 Number.parseInt에 넣어 출력하였다.




5. 같은 숫자는 싫어

배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,

  • arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.
  • arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.
    배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.

제한사항

  • 배열 arr의 크기 : 1,000,000 이하의 자연수
  • 배열 arr의 원소의 크기 : 0보다 크거나 같고 9보다 작거나 같은 정수

예시

입출력 예

arr					answer
[1,1,3,3,0,1,1]		[1,3,0,1]
[4,4,4,3,3]			[4,3]

내가 작성한 solution.js

function solution(arr)
{
    var answer = [];
    let j = 1;
    for(let i=0; i<arr.length; i++){
        if(arr[i] === arr[j]){
            arr.splice(j,1);
            i-=1;
        }else{
            answer.push(arr[i]);
            j++;
        }
    }    
    return answer;
}

이 코드는 정확성 테스는 모두 통과했는데, 효율성 테스트에서 시관초과로 모두 실패..ㅎ.....
기존 배열인 arr를 splice하고 i값을 다시 줄여서 돌리고 j값도 올리고.. 이런게 시간을 잡아먹나 싶어서 기존 배열을 건드리지 않고 answer 변수에 값만 추가하는 방식으로 수정해보기로 했다.

function solution(arr)
{
    var answer = [];
    for(let i=0; i<arr.length; i++){
        if(arr[i] !== arr[i-1]){
            answer.push(arr[i]);
        }else if(arr[i] !== arr[i+1] && arr[i] !== arr[i-1]){
            answer.push(arr[i]);
        }
    }    
    return answer;
}

다시 수정한 코드인데 솔직히 예쁘거나 간결하고 정확하지도 않은 느낌이지만...^^ 나름 머리를 굴린 결과다...

[1(이전 숫자), 3(현재 숫자), 3(다음 숫자)]

loop를 돌고 있는 현재 숫자와 이전 숫자가 같지 않을 경우 현재 숫자를 우선 answer 배열에 추가하도록 했고, 현재 숫자의 index가 0이어서 이전 숫자가 없을 경우에는 undefined가 반환되기 때문에 arr[i] !== undefined 는 true의 값을 같게 되어 이 조건에 함께 작동한다.

[1(이전 숫자), 3(현재 숫자), 0(다음 숫자)]

또, 위에처럼 이전 숫자와 다음 숫자가 현재 돌고있는 숫자와 같지 않을 경우에도 현재 숫자를 answer에 추가해주도록 했다.

이전 숫자와 현재숫자, 다음숫자와 현재숫자가 같을 경우에는 해당 숫자를 추가할 필요가 없기 때문에 추가해야하는 상황들에 대해서만 고려를 해서 if문을 작성했다.

function solution(arr) // for loop 활용
{
    var answer = [];
    for(let i=0; i<arr.length; i++){
        if(arr[i] !== arr[i+1]){
            answer.push(arr[i]);
        }
    }    
    return answer;
}

function solution(arr) // filter 활용
{
    var answer = [];
    answer = arr.filter((item,idx)=>{
        return item !== arr[idx+1];
    })
    return answer;
}

그러고 나서 다른 사람들의 풀이를 보니.. 뭐야 ㅋㅋㅋㅋㅋ 그냥 현재 숫자랑 다음 숫자만 비교해서 같으면 안넣고 넘어가고 다를때만 현재 숫자를 answer에 넣어주면 되는거였잖아...
뒤늦게 깨닫고나서 if에 if else에 머리 엄청 굴렸던 나 자신이 떠올라서 너무 자괴감이 들었지만... 괜찮아 이렇게 배우는거지..............
나름 반성할 겸 내가 제대로 이해한건지 확인할 겸 for문 그대로 수정한 방법이랑, filter 활용한 방법까지 총 두가지로 수정해봤다..잘되네..?ㅎ...허탈..




6. 두 개 뽑아서 더하기

정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.

제한사항

  • numbers의 길이는 2 이상 100 이하입니다.
  • numbers의 모든 수는 0 이상 100 이하입니다.

예시

입출력 예

numbers			result
[2,1,3,4,1]		[2,3,4,5,6,7]
[5,0,2,7]		[2,5,7,9,12]

내가 작성한 solution.js

function solution(numbers) {
    var answer = [];
    for(let i=0; i<numbers.length; i++){
        for(let j=i+1; j<numbers.length; j++){
            let sum = numbers[i]+numbers[j];
            if(!answer.includes(sum)){
                answer.push(sum);
            }
        }
    }
    answer.sort((a,b)=>{return a - b})
    return answer;
}

for문을 이중으로 돌려서 numbers의 i번째 숫자와 i+1번째 숫자를 더하고 그 숫자가 answer이라는 배열에 없을 경우에만 push하도록 했다.
그 후에 answer 배열을 sort로 오름차순으로 다시 정렬했다.


다른 사람의 풀이

function solution(numbers) {
    const temp = []

    for (let i = 0; i < numbers.length; i++) {
        for (let j = i + 1; j < numbers.length; j++) {
            temp.push(numbers[i] + numbers[j])
        }
    }

    const answer = [...new Set(temp)]

    return answer.sort((a, b) => a - b)
}

다른 사람의 풀이인데 temp라는 배열안에 숫자끼리 더해서 나온 모든 값을 집어넣고, 그 배열을 다시 스프레드 연산자로 풀어서 배열에 넣는데, 그걸 new Set이라는 구조에 집어넣어 처리했다. Set이 뭐길래 이게 가능한가 싶어서 댓글에 있는 링크를 타고 넘어가 확인해봤다.

Set

Set 객체는 자료형에 관계 없이 원시 값과 객체 참조 모두 유일한 값을 저장할 수 있습니다.

설명

Set 객체는 값 콜렉션으로, 삽입 순서대로 요소를 순회할 수 있습니다. 하나의 Set 내 값은 한 번만 나타날 수 있습니다. 즉, 어떤 값은 그 Set 콜렉션 내에서 유일합니다.
값 같음
Set 내의 값은 유일해야 하기 때문에 값이 같은지 검사를 수행합니다. 이전 ECMAScript 명세에서는 검사 알고리즘이 === 연산자와는 다른 것이었습니다. 특히, +0 === -0이었지만 Set에서는 +0과 -0이 다른 값이었습니다. 그러나 이는 ECMAScript 2015 명세에서 변경되었습니다. 브라우저 호환성의 "Key equality for -0 and 0"을 참고하세요.
NaN과 undefined도 Set에 저장할 수 있습니다. 원래 NaN !== NaN이지만, Set에서 NaN은 NaN과 같은 것으로 간주됩니다.

위의 이유로 let something = {1,2,3,3,3,4,5,7,6,4,2} 와 같은 객체를 new Set(something)안에 넣으면 중복된 값이 모두 제거되어 나온다고 한다.
새로운거 배웠다!!




7. 최소 직사각형

명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호		가로 길이		세로 길이
1			60			50
2			30			70
3			60			30
4			80			40

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.


예시

입출력 예

sizes												result
[[60, 50], [30, 70], [60, 30], [80, 40]]			4000
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]]		120
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]]		133

내가 작성한 solution.js

function solution(sizes) {
    var answer = 0;
    let big_arr = [];
    let small_arr = [];
    for(let i=0; i<sizes.length; i++){
        if(sizes[i][0] > sizes[i][1]){
            big_arr.push(sizes[i][0])
            small_arr.push(sizes[i][1])
        }else{
            small_arr.push(sizes[i][0])
            big_arr.push(sizes[i][1])
        }
    }
    return answer = Math.max(...big_arr) * Math.max(...small_arr);
}

이 문제는 내가 어떻게 풀어야하는지 감을 못잡아서..ㅋㅋ 진짜 온갖 짓을 다 하다가 도저히 답이 안나오겠다 싶어서 그냥 다른사람의 풀이를 참고했다.
카드 사이즈의 가로와 세로중에 값이 큰 요소들끼리 모으고, 값이 작은 요소들끼리 모아서 그 중에서 각 최대값을 찾아 곱하면 되는거였다.
이게 바로 알고리즘 공부인가... 어떻게 풀어야 하는지조차 감도 못잡았던 나 자신......


다른 사람의 풀이

function solution(sizes) {
    const [hor, ver] = sizes.reduce(([h, v], [a, b]) => {
        console.log(h,v,a,b)
        return [Math.max(h, Math.max(a, b)), Math.max(v, Math.min(a, b))]
    }, [0, 0])
    return hor * ver;
}

다른 사람 풀이중에 참고할만한게 있어서 가져왔다.
나도 처음에는 이걸 reduce로 풀면 좋지 않을까 생각했는데, 배열 안에 또 배열 구조로 되어있는걸 파라미터에 어떻게 담아서 적용시켜야 하는지 모르겠어서 포기했는데.. 이렇게 배열 구조 자체로 집어넣어도 처리가 가능하다는걸 알게됐다.

0개의 댓글