Skip to content

Conversation

@oh-chaeyeon
Copy link
Collaborator

[해시(Hash)]

해시(Hash)란?

  • 정의: 데이터를 키-값(Key-Value) 형태로 저장하는 자료구조.
  • 사용 이유: 데이터를 빠르게 찾거나 저장하려고 할 때 사용.
  • JavaScript에서 주로 객체(Object) 또는 Map을 사용해 구현함.

해시의 기본 개념

  • Key: 데이터를 검색하기 위한 고유한 값.
  • Value: 키에 연결된 데이터

JavaScript 안의 해시

1. 객체(Object) : 키-값 쌍을 저장할 수 있는 기본 자료구조

const hashTable = {};
hashTable["name"] = "John"; // 데이터 추가
hashTable["age"] = 30;      // 데이터 추가
console.log(hashTable["name"]); // John
console.log(hashTable["age"]);  // 30

2. Map : 객체보다 유연하고 효율적인 해시 테이블

const map = new Map();
map.set("name", "John");
map.set("age", 30);
console.log(map.get("name")); // John
console.log(map.get("age"));  // 30

해시 관련 Tip!

  1. 객체와 Map의 차이
    : 객체는 단순히 키-값 쌍을 저장하는 데 유용하지만, Map은 순서를 보장하고 더 다양한 데이터 타입의 키를 지원하는 차이가 있음.
  2. 초기화 습관
    : 해시를 사용할 때 기본값을 설정하는 습관이 중요하다고 함. -> (frequency[num] || 0)처럼 초기값을 설정해야함.
  3. Set을 활용
    : 중복 요소를 관리하거나 확인할 때는 Set이 가장 효율적이라고 함

📌 푼 문제


📝 간단한 풀이 과정

모스부호 (1)

  • 문제 : 모스부호 해석
  • 공백을 기준으로 분리하고, 각 모스부호를 해시맵를 통해 각각 대응하는 알파벳으로 반환하고, 반환된걸 문자열로 합쳐서 반환.
function solution(letter) {
  const morse = {
    ".-": "a",
    "-...": "b",
    "-.-.": "c",
    "-..": "d",
    ".": "e",
    "..-.": "f",
    "--.": "g",
    "....": "h",
    "..": "i",
    ".---": "j",
    "-.-": "k",
    ".-..": "l",
    "--": "m",
    "-.": "n",
    "---": "o",
    ".--.": "p",
    "--.-": "q",
    ".-.": "r",
    "...": "s",
    "-": "t",
    "..-": "u",
    "...-": "v",
    ".--": "w",
    "-..-": "x",
    "-.--": "y",
    "--..": "z",
  };
  return letter
    .split(" ")
    .map((code) => morse[code])
    .join("");
}

A로 B 만들기

  • 문제 : before의 문자 순서를 바꿔서 after를 만들 수 있는지 확인하는 문제, 바꿀수 있으면 1, 없으면 0반환
  • before랑 after 각각 분리해서 정렬한 다음 비교하고, 같으면 1, 다르면 0을 반환하도록 함.
function solution(before, after) {
  return before.split("").sort().join("") === after.split("").sort().join("")
    ? 1
    : 0;
}

진료 순서 정하기

  • 문제 : 각 환자의 응급도를 보고, 높은 환자부터 낮은 환자로 진료순서를 매겨야 함.
  • map()함수의 역할 : 배열의 각 요소를 순회하면서 주어진 함수의 결과로 새로운 배열을 반환함.
  • sort()로 배열을 내림차순 정렬한 후, 각 요소의 인덱스를 찾아 + 1을 통해 진료 순서를 매기는 순으로 처리
function solution(emergency) {
  return emergency.map(
    (e) => [...emergency].sort((a, b) => b - a).indexOf(e) + 1 //indexOf(e)는 0부터 시작하는 인덱스를 반환해서 1를 더해야 함.
  );
}

등수 매기기

  • 문제 : 각 학생의 편균점수 계산해서 등수 부여하기
function solution(score) {
  const averages = score.map((s) => (s[0] + s[1]) / 2); //평균 점수 계산
  const sortedAverages = [...averages].sort((a, b) => b - a); //평균 점수 내림차순 정렬

  return averages.map((avg) => sortedAverages.indexOf(avg) + 1);  //각 평균 점수의 순위 계산
}

완주하지 못한 선수

  • 문제 : 완주하지 못한 한 선수를 찾아라~
  • 참가자의 이름을 카운트하고, 완주자의 이름을 차감하여 카운트가 0이 아닌 이름을 찾아내는 방식으로 해결함.
  • reduce()의 역할 : 배열의 각 요소를 순회하며 누적값을 계산함. -> 참가자의 이름(name)을 키로 하고, 그 이름이 몇 번 등장했는지를 값으로 저장하는 객체(acc)를 만드는 역할.
function solution(participant, completion) {
  const count = participant.reduce((acc, name) => {
    acc[name] = (acc[name] || 0) + 1;
    return acc;
  }, {});

  //완주한 사람들의 이름을 순회하면서,count 객체에서 해당이름이 있으면 1씩 감소. (완주한 사람이면 1 감소해서 0이 됨.)
  completion.forEach((name) => { 
    count[name]--;
  });

  //count[name] > 0 조건을 만족하는 이름=완주하지 못한 사람의 이름을 반환함.
  return Object.keys(count).find((name) => count[name] > 0); 
}

폰켓몬

  • 문제 : 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2 마리를 선택할 때, 최대한 다양한 종류의 폰켓몬을 포함하도록 선택하는 방법을 찾고, 그때의 폰켓몬 종류 번호의 개수를 반환해야 하는 문제.
  • Set 객체의 역할 : 중복을 제거한 유일한 값을 저장
function solution(nums) {

  //nums 배열에서 중복된 폰켓몬 번호를 제거하여, 중복 없는 폰켓몬 종류만으로 이루어진 배열 newNum을 만듬.
  const newNum = [...new Set(nums)];
  const selection = nums.length / 2;  //선택할 수 있는 최대 마리 수

  //고유한 폰켓몬의 개수(newNum.length)랑 선택 가능한 최대 마리 수(selection) 둘 중에 작은 값을 반환.
  return Math.min(newNum.length, selection); 
}

추억 점수

  • name 배열은 사람의 이름, yearning 배열은 각 사람의 그리움 점수를 나타내는데, 각 사진 속 인물들의 점수를 합해서 반환.
  • map()의 역할 : 각 사진에 대해 점수의 합을 계산한 결과를 새로운 배열에 저장
  • reduce()의 역할 : 사진에 등장한 각 인물(person)의 점수를 계속 더해 최종적으로 사진 점수를 계산함. (초기값은 0으로 시작해서 각 사람의 점수를 조건에 따라 누적함.)
function solution(name, yearning, photo) {
  return photo.map((p) => 
    //각 사진에 대해 인물들의 점수를 합산하여 반환
    p.reduce((sum, person) => {
      const index = name.indexOf(person); //인물의 인덱스 찾기

      //인물이 name 배열에 있으면 해당 점수를 더하고, 없으면 0을 더함
      return sum + (index !== -1 ? yearning[index] : 0); 
    }, 0)
  );
}

할인 행사

  • 문제 : 할인 상품 목록 중에서 원하는 모든 물건을 할인받을 수 있는 날짜의 총 일수를 구하는 문제(회원 등록 후 10일간 할인 상품 구매 가능)

  • required의 역할 : 각 물건별로 필요한 수량을 저장하는 객체
    예시) want = ["apple", "banana", "orange"]; number = [3, 2, 1]; -> required = { apple: 3, banana: 2, orange: 1 };

  • current의 역할 : 현재 10일간 등장한 상품과 그 개수를 저장.

function solution(want, number, discount) {
  const required = {};
  want.forEach((item, index) => {
    required[item] = number[index];
  });

  let day = 0;

  for (let i = 0; i <= discount.length - 10; i++) {
    const current = {};

    for (let j = i; j < i + 10; j++) {
      current[discount[j]] = (current[discount[j]] || 0) + 1;
    }

    const isValid = want.every( //**want.every** : 배열의 모든 요소가 조건을 만족하면 true를 반환
      (item) => (current[item] || 0) >= required[item] //current에 해당 물건이 없으면 기본값 0으로 처리
    );

    if (isValid) day++;
  }

  return day;
}

오픈채팅방

  • 문제 : 카카오톡 채팅방을 개설한 사람이 볼 수 있는 메시지를 문자열 배열 형태로 반환.
  • nicknames : userId와 nickname을 매핑하기 위한 객체.
function solution(record) {
  const nicknames = {};
  const result = [];

  //닉네임 업데이트 : 모든 기록을 순회하면서 Enter 또는 Change 동작에 따라 사용자의 최신 닉네임을 저장.
  record.forEach((entry) => {
    const [action, userId, nickname] = entry.split(" ");
    if (action !== "Leave") {
      nicknames[userId] = nickname;
    }
  });

 //record 배열을 다시 순회하며 메시지를 생성
  record.forEach((entry) => {
    const [action, userId] = entry.split(" ");
    if (action === "Enter") {
      result.push(`${nicknames[userId]}님이 들어왔습니다.`);
    } else if (action === "Leave") {
      result.push(`${nicknames[userId]}님이 나갔습니다.`);
    }
  });

  return result;
}

늦게 올리게 된점...정말 죄송합니다..

@oh-chaeyeon oh-chaeyeon self-assigned this Jan 27, 2025
Copy link
Collaborator

@JooKangsan JooKangsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오픈채팅과 할인행사는 바로이해하면서 보기가 어려워서...나중에 볼게요...! 수고하셨어요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indexOf는 O(n) 시간복잡도를 가져요. 해시맵 사용 시 O(1)로 개선 가능 할 거 같아요!
아래는 예시입니다!

function solution(name, yearning, photo) {
    const scoreMap = {};

    name.forEach((n, i) => scoreMap[n] = yearning[i]);
    
    return photo.map(p => 
        p.reduce((sum, person) => sum + (scoreMap[person] || 0), 0)
    );
}

@JooKangsan JooKangsan merged commit 5d03e2d into main Feb 6, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants