[2024.08.20 TIL] 알고리즘 및 도전 문제

박지영·2024년 8월 20일
0

Today I Learned

목록 보기
27/88

📕 도전 문제 6 ~ 10

📃 문제 6

주어진 문자열에서 중복된 문자를 제거하고, 남은 문자들을 원래 순서대로 반환하는 함수를 작성하세요.

🔒 제한사항

  • 문자열의 길이는 1 이상 1000 이하입니다.

💻 답

// 문자열에서 중복된 문자 제거, 남은 문자 원래순서대로 반환
// Set 객체는 고유한 값만 삽입될 수 있다. (= 중복 제거)

function distinct (s) {
    // new Set을 사용하면 Set 객체가 만들어지기 때문에
    // 배열과 비슷하지만 배열이 아니다.
    // ... 전개 연산자를 이용하여 배열로 만든 후 문자열로 조합한다..
    const str = [...new Set(s)];
     
    return str.join('');
}

const s = "adsfawezsafy";

console.log(distinct(s)); // adsfwezy
  • 문제

문자열의 중복된 문자를 제거 하는 방법하면 딱 바로 떠올랐던 방법이다.

JS 기본 문법 때 배운 Set을 사용하기로 하고 구현 해보았는데 문제가 하나 있었다.

new Set(s)로 Set 객체를 만든 후 다시 문자열로 만들려고 하니

Set 객체는 배열이 아니라서 join 등을 사용할 수 없었고

로그를 찍으니 Set(8) { 'a', ... 'y' } 이런식으로 나왔다.

  • 해결

공부했던 부분을 다시 찾아보았는데 ES6 문법의 전개 연산자가 배열이나 객체의 요소를 하나 하나

풀어서 리턴하는 걸 상기했다.

그래서 [...new Set(s)]로 바꾸니 해결됐다.


📃 문제 7

주어진 배열에서 최솟값과 최댓값을 찾고, [최솟값, 최댓값] 형태의 배열을 반환하는 함수를 작성하세요.

🔒 제한사항

  • 배열의 길이는 1 이상 1000 이하입니다.
  • 배열의 원소는 -1000 이상 1000 이하의 정수입니다.

💻 답

// 배열에서 최솟값, 최댓값 찾기 [최솟값, 최댓값] 형태로 반환
// 새 배열을 만든 후 최솟값과 최댓값을 차례로 삽입

function minMax (arr) {
    const newArr = [];
    //최솟값 /전개 구문으로 배열을 해체
    newArr.push(Math.min(...arr));
    //최댓값 /전개 구문으로 배열을 해체
    newArr.push(Math.max(...arr));
    return newArr;
}

const arr = [1,2,3,3,4,5,6,7,8,9,10,11];

console.log(minMax(arr)); // [1, 11]

생각한대로 바로 구현되어서 문제가 없었다.


📃 문제 8

주어진 문자열을 요약하는 함수를 작성해주세요!

📌 예시

  • str = ‘aaabbbc’ // 출력: ‘a3/b3/c1’
  • str = ‘ahhhhz’ // 출력: ‘a1/h4/z1’

💻 답

// 문자열 요약
// 각 문자의 갯수를 카운트해서 출력

function summary (s) {
    // 각 문자를 담을 객체 obj
    const obj = {};
    // 반환할 문자열 str
    let str = "";

    // 현재 요소가 객체 있으면 현재 요소++ 없으면 현재 요소를 1로 초기화
    for (let i = 0; i < s.length; i++) {
        const char = s[i];
        if (obj[char]) obj[char]++;
        else obj[char] = 1;
    }

    // 카운트한 객체의 키
    let temp1 = Object.keys(obj);
    // 카운트한 객체의 값
    let temp2 = Object.values(obj);
    for (let i = 0; i < temp1.length; i++ ) {
        // 키+값 형태의 문자열 추가
        str += temp1[i]+temp2[i];
        // 마지막 요소에 /를 추가하지 않기 위해 마지막 요소면 중단
        if (i == temp1.length-1) break;
        str += "/";
    }
    return str;
}

const str = "aaabbbc";

console.log(summary(str)); // a3/b3/c1
  • 문제

오늘 아침에 강창민 튜터님의 알고리즘 세션에서 쓰셨던 방법이 바로 떠올랐다.

문제는 객체의 키와 값을 둘 다 가져와야 된다는 부분이였는데

obj.a 같이 키를 알고 있을 때 값을 가져오는 방법은 알고 있었는데

키가 무엇인지 아는 방법이 떠오르지 않아 js 기본 문법에 정리해놓았던 다시 찾아보았다.

  • 해결

객체 메소드에 답을 찾을 수 있었는데

객체의 키들을 가져오는 메소드 Object.keys() 값을 가져오는 Object.values()를 사용하니

해결할 수 있었다.


📃 문제 9

주어진 배열에서 두 수를 선택하여 그 합이 주어진 target 값과 일치하는지 확인하는 함수를 작성하세요. 일치하는 경우 true, 그렇지 않은 경우 false를 반환하세요.

🔒 제한사항

  • 배열의 길이는 2 이상 1000 이하입니다.
  • 배열의 원소는 1 이상 1000 이하의 자연수입니다.

💻 답

// 배열에서 두 수를 선택하여 합이 target과 일치하는지 확인
// 일치하는 경우 true 아닐 경우 false 반환

function sumEqual(arr, target) {
    // 중첩 반복문으로 완전 탐색
    // 마지막은 다음 요소와 더할 수 없으니 시행하지 않음
    for (let i = 0; i < arr.length -1; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            // 인덱스가 겹치지 않는 두 수의 합
            let sum = arr[i] + arr[j];
            // 두 수의 합과 target이 일치하면 true 반환
            if (sum == target) return true;
        }
    }
    // 반복문 종료까지 true가 반환되지 않으면 false 반환 
    return false;
}

const arr = [2, 7, 11, 15];
const target = 9;

console.log(sumEqual(arr, target)); // true
  • 문제

두 수를 선택한다는 개념이 잘 이해가 안됐다. 내가 해석하기론 n번째 수를 각기 다른 값과

한번 씩 더해서 그 값이 있는지 확인한다고 생각하고 로직을 만들었다.

중첩 for문이 바로 떠올랐는데 전부 다 돌면 같은 연산이 많아 진다는게 문제였다.

  • 해결

그래서 j의 초기값을 i와 겹치지 않게 해서 연산 횟수를 줄였다.


📃 문제 10

주어진 문자열이 유효한 괄호 조합인지 확인하는 함수를 작성하세요. 유효한 조합은 모든 여는 괄호가 올바르게 닫혀야 하며, 괄호의 순서도 일치해야 합니다.

🔒 제한사항

  • 문자열의 길이는 1 이상 1000 이하입니다.
  • 괄호는 (), {}, []의 세 종류입니다.

💻 답

// 문자열이 유효한 괄호 조합인지 확인
// 괄호가 전부 올바르게 여닫는지 확인, 순서도 확인

// 절반으로 나눈 후 앞 부분은 앞에서 부터 뒷 부분은 뒤에서 부터 서로 비교

function bracketCheck (str) {
    // 짝수가 아니면 짝이 맞을 수 없으르모 false 반환
    if (str.length % 2 !== 0) return false;

    // 괄호가 올바르게 열고 닫혔는지 체크하는 객체 check
    const check = {
        "[" : "]",
        "{" : "}",
        "(" : ")"
    };
    
    str = str.split('');
    // 절반으로 나눈 앞 부분
    let front = str.slice(0, str.length/2);
    // 절반으로 나눈 뒷 부분
    // 나눈 후 반전 시켜서 비교
    let back = str.slice(str.length/2).reverse();

    // check 객체의 키에 해당하는 front의 값이 back과 일치하는지 확인
    // 일치하지 않는 괄호가 있으면 짝이 안맞으므로 false 반환
    for (let i = 0; i < front.length; i++) {
        if (check[front[i]] !== back[i]) return false;
    }

    return true;
}

const str = "[{([)]}]";

console.log(bracketCheck(str)); // false

예전에 풀어보았던 푸드 파이트 문제의 방법이 떠올랐다.

양쪽이 0을 기준으로 대칭이였는데

그것을 이용해서 앞부분만 연산하고 뒷부분은 앞부분을 뒤집어서 붙이기만 하면 됐었다.

괄호가 열고 닫히는 것도 대칭이니 이 부분을 응용하기로 했다.

주어진 문자열을 반으로 나누고 뒷부분을 반전 시켜 앞부분과 짝을 이루는지 확인하는

로직이다.

  • 문제

좋은 방법이라고 생각했지만 치명적인 문제가 있었다. "([)]"이나 "(){}"같이 대칭이 되지 않으면

무조건 false가 나왔다. 계속 생각해봤지만 반으로 자르고 비교하는 시점에서 대칭이 아니면

전제부터 틀렸다는 결론을 내렸다.

  • 해결
// 문자열이 유효한 괄호 조합인지 확인
// 괄호가 전부 올바르게 여닫는지 확인, 순서도 확인

function bracketCheck (str) {
    // 짝수가 아니면 짝이 맞을 수 없으르모 false 반환
    if (str.length % 2 !== 0) return false;
    // 여는 괄호를 추가할 배열
    const arr = [];

    // 괄호가 올바르게 열고 닫혔는지 체크하는 객체 check
    const check = {
        "[" : "]",
        "{" : "}",
        "(" : ")"
    };
    
    for (let i = 0; i < str.length; i++) {
        const open = str[i];
        // str[i]가 check에 값이 있을 경우 = 열린 괄호인 경우
        if (check[open]) {
            // 열린 괄호(키)의 값(닫힌 괄호)를 판별하고
            // 열린 괄호의 짝이 맞는지 확인하기 위해 arr에 추가
            arr.push(open);
        // 닫힌 괄호인 경우
        } else {
            // 마지막 열린 괄호의 짝을 확인하기 위해 pop으로 제거하고
            // close에 삽입
            const close = arr.pop();

            // check의 키(close)(여는 괄호)에 대응하는 값(닫는 괄호)인지 판별
            if (check[close] !== open) return false;
        }
    }

    return arr.length == 0;
}

const str = "([)]";

console.log(bracketCheck(str)); // false

열린 괄호를 추가할 배열을 만들고 열린 괄호일 경우 그 열린 괄호를 배열에 추가한다.

닫힌 괄호가 나왔을 때 추가한 배열의 마지막 열린 괄호와 현재 닫힌 괄호가 짝을 이루는지

check 객체의 키와 밸류를 이용해서 판별했다.

stack 구조로 만든 로직이다. 후입선출, 마지막 값을 빼는 구조.

profile
신입 개발자

0개의 댓글