해시(Hash)

임의의 크기를 가진 데이터를 고정된 데이터의 크기로 변환시키는 것

 

특정한 배열의 인덱스나 위치를 입력하고자하는 데이터의 값을 이요해 저장하거나 찾을 수 있다. 

해쉬를 이용하면 즉시 저장하거나 찾고자하는 위치를 참조할 수 있으므로 빠른 속도로 처리할 수 있다. 

 

저장된 데이터에 바로 접근해서 값을 가져올 수 있다. 찾고자하는 값과 테이블의 인덱스가 동일하므로 값이 저장된 공간에 바로 접근해서 값을 가져올 수 있으므로 시간복잡도는 O(1)이다. 

 

테이블에 있는 값을 삽입, 수정, 삭제 하는 행위도 값이 어디있는지만 알고있으면 모두 한 방에 해결할 수 있다. 

 

해시테이블

해시함수라는 것에 한 번 통과시켜서 사용한다. 

해시 함수는 임의의 길이를 가지는 임의의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다. 

 

어떤 값이 들어오든 간에 다 뭉개서 내가 원하는 길이의 값으로 만든다. 

 

key와 value로 이루어진 Map이라는 객체를 사용해서 해시테이블을 만들어서 사용한다. 

자바스크립트의 Map은 시간복잡도를 줄이는데 결정적인 역할을 한다.

 

Map 객체 사용하기

Map 객체 생성

const map = new Map();

 Map의 메소드인 set함수 사용해서 객체에 요소 추가

map.set('B', 1)

기존 값에다가 증가시키기

map.get('B', 1)

 

 

예제문제 | 학급회장

문제

학급회장을 뽑는데 후보로 기호 A,B,C,D,E 후보가 등록을 했다. 투표 용지에는 반 학생들이 쓴 후보의 기호가 쓰여있으며, 선생님은 그 기호를 발표하고 있다. 발표가 끝난 후 어떤 기호의 후보가 학급 회장이 되었는지 출력해라. 

 

입력예제 | BACBACCACCBDEDE

출력예제 | C

 

코드

const solution = (str) => {
  let answer;
  let student = new Map();

  for (let x of str) {
    if (student.has(x)) {
      student.set(x, student.get(x) + 1);
    } else {
      student.set(x, 1);
    }
  }

  // 가장 작은 정수를 비교군으로 설정
  let max = Number.MIN_SAFE_INTEGER;

  for (let [key, value] of student) {
    if (value > max) {
      max = value;
      answer = key;
    }
  }
  return answer;
}

const str = "BACBACCACCBDEDE";
console.log(solution(str));

 

 

예제문제 | 아나그램

문제

아나그램이란 두 문자열이 알파벳의 나열 순서는 다르지만 그 구성이 일치하면 두 단어는 아나그램이라고 한다. 주어지는 두 단어가 아나그램이면 YES를 출력하고, 아니면 NO를 출력해라.

 

입력예제 | AbaAeCe, baeeACA

출력예제 | YES

 

코드

const solution = (word1, word2) => {
  // 아나그램 : 알파벳 나열 순서는 다르지만 그 구성(알파벳과 그 개수)이 일치
  // 길이가 다른 두 개의 단어가 주어지면, 그 두 단어가 아나그램인지 판별
  // 대소문자 구별

  let answer = "Yes";

  // map1 을 기준 객체로 만들어 놓고 
  // map1 과 word2를 비교해서 key가 존재하는지 보고, 있으면 value 비교
  let map1 = new Map();

  for (let x of word1) {
    if (map1.has(x)) {
      map1.set(x, map1.get(x) + 1);
    } else {
      map1.set(x, 1);
    }
  }

  for (let x of word2) {
    if (!map1.has(x) || map1.get(x) === 0) {
      return "No";
    }
    map1.set(x, map1.get(x) - 1);
  }

  return answer;
}

const word1 = "abaCC";
const word2 = "Caaab";
console.log(solution(word1, word2));

 

 

문제

5명의 학생이 주어질 때 각 학생이 받은 점수의 평균을 구하여 기준에 따라 학점을 부여

조건 : 자기자신을 평가한 점수가 유일한 최고점 또는 유일한 최저점이라면, 그 점수는 제외하고 평균을 구한다. 

 

 

풀이

평균을 먼저 구하고, 학점을 구한다. 각 행에 평균을 구해야 할 점수가 들어있다. 

 

 

통과하지못한 코드

const solution = (scores) => {
  let sum = 0;

  let sumArr = [];
  let scoreArr = [];
  let studentScoreArr = [];
  let len;

  let answer = '';

  // 배열 가공
  for (let i = 0; i < scores.length; i++) {
    studentScoreArr = [];
    for (let j = 0; j < scores.length; j++) {
      studentScoreArr.push(scores[j][i]);
    }
    scoreArr.push(studentScoreArr);
  }

  // 점수 가공
  for (let i = 0; i < scores.length; i++) {
    sum = 0;
    len = scoreArr[i].length;
    for (let j = 0; j < scores.length; j++) {
      let findIdx = scoreArr.indexOf(scoreArr[i]);
      let findCurrentIdx = scoreArr[i].indexOf(scoreArr[i][j]);

      if (findIdx === findCurrentIdx) {

        const myScore = scoreArr[i][j];
        const dubCheck = isDuplicate(scoreArr[i]);

        if (dubCheck) {
          if (Math.max(...scoreArr[i]) === myScore || Math.min(...scoreArr[i]) === myScore) {
            let myIdx = scoreArr[i].indexOf(myScore);
            console.log(myIdx);
            scoreArr[i].splice(myIdx, 1, 0);
            len--;
          } else {
            sum += scoreArr[i][j];
          }
        } else if (Math.max(...scoreArr[i]) === myScore || Math.min(...scoreArr[i]) === myScore) {
          let myIdx = scoreArr[i].indexOf(myScore);
          scoreArr[i].splice(myIdx, 1, 0);
          len--;
        } else {
          sum += scoreArr[i][j]
        }
      } else {
        sum += scoreArr[i][j];
      }
    }
    console.log(len);
    sumArr.push(sum / len);
  }

  // 학점 계산
  for (let i = 0; i < sumArr.length; i++) {
    if (sumArr[i] >= 90) {
      answer += 'A';
    } else if (90 > sumArr[i] && sumArr[i] >= 80) {
      answer += 'B';
    } else if (80 > sumArr[i] && sumArr[i] >= 70) {
      answer += 'C';
    } else if (70 > sumArr[i] && sumArr[i] >= 50) {
      answer += 'D';
    } else {
      answer += 'F';
    }
  }
  return answer;
}

const isDuplicate = (arr) => {
  const isDup = arr.some(function (x) {
    return arr.indexOf(x) !== arr.lastIndexOf(x);
  });
  return isDup;
}

기본 테스트는 통과했는데 테스트 케이스 3개를 통과하지 못했다. 

 

 

 

 

통과한 코드

const solution = (scores) => {
  let answer = '';

  for (let i = 0; i < scores.length; i++) {
    let myScore = scores[i][i];
    let count = scores.length;

    let min = 101;
    let max = -1;
    let sum = 0;

    let flag = true;

    for (let j = 0; j < scores.length; j++) {
      let score = scores[j][i];

      if (i !== j && myScore === score) { // 내점수가 아니고 동일한 점수가 아닐 때 
        flag = false;
      }

      min = Math.min(min, score);
      max = Math.max(max, score);
      sum += score;
    }

    if (flag && (min === myScore || max === myScore)) { // 내 점수일 때
      count--;
      sum -= myScore;
    }

    answer += findGrade(sum / count);
  }
  return answer;
}

const findGrade = (score) => {
  if (score >= 90) {
    return 'A';
  } else if (90 > score && score >= 80) {
    return 'B';
  } else if (80 > score && score >= 70) {
    return 'C';
  } else if (70 > score && score >= 50) {
    return 'D';
  } else {
    return 'F';
  }
}

문제

숫자의 일부 자릿수가 영단어로 바뀌어졌거나, 혹은 바뀌지 않고 그대로인 문자열 s가 매개변수로 주어진다. s가 의미하는 원래 숫자를 return 하도록 solution 함수를 완성하라.

입력예제 | "one4seveneight"

출력예제 | 1478

 

처음 코드(실패)

const solution = (s) => {
  const numStr = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
  const num = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];

  let answer = s;

  for (let i = 0; i < numStr.length; i++) {
    let arr = answer.split(numStr[i]);

    if (arr.length > 1) {
      let idx = numStr.indexOf(numStr[i]);
      arr.splice(1, 0, num[idx]);
      answer = arr.join(i);
    }
  }
  let answer2 = answer.split('');

  let result = answer2.reduce((acc, cur, idx, arr) => {
    if (arr.indexOf(cur) === idx) acc.push(cur);
    return acc;
  }, []);

  return Number(result.join(''));
}

처음에는 이렇게 풀었는데, 테스트케이스만 보고 중복제거를 하면된다고 생각했는데, 하고보니 주어지는 문자열에 중복이 있을 수도 있는데 그것마저 없애는 코드가 되었다. 다시 새로 짜야했다. 

 

 

다른풀이

const solution = (s) => {
  let answer = 0;

  s = s.replace(/one/g, '1');
  s = s.replace(/two/g, '2');
  s = s.replace(/three/g, '3');
  s = s.replace(/four/g, '4');
  s = s.replace(/five/g, '5');
  s = s.replace(/six/g, '6');
  s = s.replace(/seven/g, '7');
  s = s.replace(/eight/g, '8');
  s = s.replace(/nine/g, '9');
  s = s.replace(/zero/g, '0');

  console.log(s);
  return Number(s);
}

 

 

다른풀이

const solution = (s) => {
  let numbers = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"];

  for (let i = 0; i < numbers.length; i++) {
    let arr = s.split(numbers[i]);
    s = arr.join(i);
  }

  return Number(s);
}

문제

같은 종류의 폰켓몬은 같은 번호를 가지고있다. 

최대한 다양한 종류의 폰켓몬을 가지길 원한다. 

최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려고 한다. 

 

N마리의 폰켓몬 종류 번호가 담긴 배열 nums가 주어질 때,

N/2마리의 폰켓몬을 선택하는 방법 중

가장 많은 종류의 포켓몬을 선택하는 방법을 찾아

폰켓몬 종류 번호의 개수를 return

 

자연수

양의 정수, 사물의 개수를 셀 때 쓰이는 수여서 가장 '자연스러운 수' 

 

 

코드

const solution = (nums) => {
  const selectNum = nums.length / 2;

  const possibleSelect = nums.reduce((acc, cur, idx, origin) => {
    if (origin.indexOf(cur) === idx) acc.push(cur);
    return acc;
  }, []);

  return possibleSelect.length >= selectNum ? selectNum : possibleSelect.length;
}

중복 제거를 먼저해주고, 개수를 찾아주면 되는 문제였다. 

reduce를 이용해서 중복제거를 해주었는데, 중복된 값을 허용하지 않는 Set 객체를 이용하면 더 간단하게 코드를 작성할 수 있었다. 

 

 

다른사람의 풀이

function solution(nums) {
  const max = nums.length / 2;
  const arr = [...new Set(nums)];

  return arr.length > max ? max : arr.length
}

문제

어떤 정수들이 있다. 이 정수들의 절댓값을 차례대로 담은 정수 배열에서, 실제 정수들의 합을 구하여 return해라

 

수학개념

절댓값(absolute value)

: 어떤 값을 양수로 만들어주기 위한 장치

 

정수

: 양의정수, 0, 음의 정수의 총칭

 

코드

const solution = (absolutes, signs) => {
  let answer = absolutes.map((num, idx) => signs[idx] ? num : parseInt(-num)).reduce((a, b) => a + b);
  return answer;
}

문제

정수를 담고 있는 배열 arr의 평균값을 return하는 함수, solution을 완성해라

 

에러난 코드

const average = (arr) => {
  let answer = 0;

  for (let i of arr) {
    answer += i;
  }

  return answer/arr.length;
}

문제에서 함수 solution을 작성하라고 했는데 나혼자 함수명을 따로 지어버렸다....안될리가 없는데 왜 안되지 싶었네.. 사소한 실수 하지않도록 하자.

 

 

코드

const solution = (arr) => {
  let answer = 0;

  for (let i of arr) {
    answer += i;
  }

  return answer/arr.length;
}

완전탐색으로 해결해야하는 문제 -> 조합을 이용

 

4Combination3 = 4C3

4개 중에 3개씩 선택할 때 나올 수 있는 모든 조합을 구한다. 이 때, 조합의 순서는 상관이 없다. 순서가 바뀌어도 같은 구성이기 때문에 하나의 조합으로 취급한다. 

 

소수 

: 1과 자기 자신만을 약수로 갖는 수, 자신보다 작은 두 개의 자연수를 곱하여 만들 수 없는 1보다 큰 자연수

 

처음 시도한 방법

조합으로 모든 경우의 수 배열로 찾아서 다 더한다음에 소수판별할 생각이었다. -> 실패

 

조합을 이용하기

서로 다른 n개의 물건에서 순서를 생각하지않고 r개를 택할 때, n개에서 r를 택하는 조합 -> nCr

5개 중에서 3개를 더하는 조합 5C3 = 5*4*3 /3*2*1 = 10가지

 

배열의 처음부터 하나씩 고정하면서 뒤의 나머지 배열에 대해서 조합을 구한다. 

 

 

 

코드

const solution = (nums) => {
  let answer = 0;
  const len = nums.length;

  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      for (let k = j + 1; k < len; k++) {
        const sum = nums[i] + nums[j] + nums[k];
        if (isPrime(sum)) answer++;
      }
    }
  }
  return answer;
}

const isPrime = (sum) => {
  for (let i = 2; i < sum; i++)
    if (sum % i === 0) return false;
  return true;
}

 

문제

인형이 없는 칸은 빈칸이다. 모든 인형은 1 x 1 크기의 격자 한 칸을 차지, 격자의 가장 아래 칸부터 차곡차곡 쌓임

사용자는 크레인을 좌우로 움직여서 멈춘 위치에서 가장 위에 있는 인형을 집어올릴 수 있다. 

 

집어올린 인형은 바구니에 쌓인다. 바구니의 가장 아래 칸부터 인형이 순서대로 쌓이게된다.

같은 모양의 인형 두 개가 바구니에 연속해서 쌓이게 되면 두 인형은 터뜨려지면서 바구니에서 사라진다. 

인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않는다.

 

화면의 격자의 상태가 담긴 2차원 배열 board

인형을 집기 위해 크레인을 작동시킨 위치가 담긴 배열 moves

크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수를 return

 

[제한사항]

board 의 각 칸의 0은 빈칸을 나타낸다. 

1 ~ 100 의 숫자는 각기 다른 인형의 모양을 의미하며, 같은 숫자는 같은 모양의 인형을 나타낸다. 

 

 

 

 

코드

stack이용해서 풀 수 있는 문제였다. 

const solution = (board, moves) => {
  // 2차원 배열에서 moves의 순서대로 꺼내서 배열에 담는다.
  // 중복되는 횟수 구해서 리턴
  let answer = 0;
  let basket = [];

  for (let i = 0; i < moves.length; i++) {
    let n = moves[i] - 1; //크레인 위치 n을 기준으로 세로길이만큼 탐색

    for (let j = 0; j < board.length; j++) {
      // 0이 아닌 숫자를 꺼내서 basket 배열에 담는다. 
      if (board[j][n] !== 0) {
        if (basket[basket.length - 1] === board[j][n]) {
          answer += 2;
          basket.pop();
        } else {
          basket.push(board[j][n]);
        }
        board[j][n] = 0; // 꺼낸 자리 0으로 초기화
        break; // 해당 한개만 꺼내기 위해서 (for문 멈추기)
      }
    }
  }
  return answer;
}

const board = [
  [0, 0, 0, 0, 0],
  [0, 0, 1, 0, 3],
  [0, 2, 5, 0, 1],
  [4, 2, 4, 4, 2],
  [3, 5, 1, 3, 1]
];
const moves = [1, 5, 3, 5, 1, 2, 1, 4];
console.log(solution(board, moves));

+ Recent posts