해쉬란?

해쉬는 입력 데이터를 고정된 길이의 데이터로 변환된 값을 말한다. 다른 말로는 해시 값이라고한다.

 

 

사용하는경우 

배열의 인덱스위치, 위치, 데이터 값을 저장하거나 검색할때 활용된다.

 

특징 

키(KEY)에 데이터(VALUE)를 매핑할 수 있는 데이터 구조

해쉬 함수를 통해 키의 데이터를 배열에 저장할 수 있는 주소(인덱스 번호)를 계산

 

 

 

 

 

장단점

장점

데이터 저장/ 읽기 속도가 빠름 (검색속도가 빠름)

해시는 키에 대한 데이터가 있는지 확인이 쉬움

 

단점

일반적으로 저장곤간이 많이 필요

여러 키에 해당하는 주소(인덱스)가 동일한 경우 충돌을 해결하기 위한 별도 자료구조 필요

 

 

 

 

 

 

 

코드

class hashTable{
    constructor(size){
        this.storage = [];
        if(size){
            this.size = size;
        }
        else{
            this.size = 100;
        }
    }
    insert = (key,value) => {               
        let index = this.hash(key);
        
        if(this.storage[index] === undefined){
            this.storage[index] = [[key, value]];
        }
        else{
            let storageFlag = false;
            for(let i = 0; i < this.storage[index].length; i++){
                if(this.storage[index][i][0] === key){
                    this.storage[index][i][1] = value;
                    storageFlag = true;
                }
            }
            if(!storageFlag){
                this.storage[index].push([key,value]);
            }
        }
    }
    delete = (key) => {
        let index = this.hash(key);
        if(this.storage[index] === undefined){
            return false;
        }
        else if(this.storage[index].length === 1 && this.storage[index][0][0] === key){
            this.storage.splice(index,1);
            return true;
        }
        else{
            for(let i = 0; i < this.storage[index].length; i++){
                if(this.storage[index][i][0] === key){
                    this.storage[index].splice(i,1)
                    return true;
                }
            }
            return false;
        }
    }
    search = (key) => {
        let index = this.hash(key);
        if(this.storage[index] === undefined){
            return false;
        }
        else if(this.storage[index].length === 1 && this.storage[index][0][0] === key){
            return this.storage[index][0][1];
        }
        else{
            for(let i = 0; i < this.storage[index].length; i++){
                if(this.storage[index][i][0] === key){
                    return this.storage[index][i][1];
                }
            }
            return false;
        } 
    }

    hash = (key) => {
        let hash = 0;
        for(let i = 0; i < key.length; i++){
            hash += key.charCodeAt(i);
        }
        return hash % this.size;
    }

    getTable(){
        return this.storage;
    }

}

let data = new hashTable(100);
data.insert(1,5);
data.insert('asd', 12);
data.insert(213,14);
data.insert('a', 'b');
data.insert('213', '12');
console.log(data.search(1));
console.log(data.search(213));
data.delete(1);
console.log(data.search(1));
data.insert(1,10)
data.delete('a');
console.log(data.search(1));
console.log(data.getTable())

그리디 알고리즘이란? 

최적의 값을 구해야하는 상황에서 사용되는 방법으로

' 매선택에서 지금 이순간 당장 최적인 답을 선택하여 적합한 결과를 도출하자'  가 모토이다.

 

비유하자면 마시멜로 실험을 생각하면된다 -> 그리디 알고리즘을 사용한다는것은 당장 눈앞에있는 마시멜로를 먹는것이다.

하지만 이방법을 사용하는것은 기다렸다가 2개를 먹는다라는 최적해를 보장해주지 못한다

 

 

사용하는이유

항상 최적의 선택을하기때문에 앞서봤던 깊이우선탐색(dp) 보다 빠른 속도를 낸다

 

특징

최적부분 구조이다. -> 한번의 선택이 다음선택에는 전혀 무관한 값이며 매순간의 최적해가 문제에대한 최적해야여한다는의미이다.

 

 

 

 

그리디의 에니메이션 출처 : 위키피디아

 

 

원리

위의 그림을 보면서 아래 설명의 봐보자. 

1. 시작 정점을 7로 설정 (최대값을 구할때)

2. 3과 12중에 하나를 골라! 당장! 12가 크네 선택

3. 5과 6중에 하나를 골라! 당장! 6이 크네

 

이런식으로 탐색을 한다. 당장의 선택에만 선택하므로. 당장의이익만보는 그리디 알고리즘이라 하는것이다.

 

장단점

장점

간단하고 직관적인 방법이다.

또한 원리를 나열할때 3번에 끝난것을보아 계산 시간이 짧아서 대용량 데이터에도 적용이 가능하다

 

 

단점

최적화가 보장되지 않는다는 것이다. 위의 에니메이션을 보면 

맨아래노드의 최대값이 99인 큰값이 존재해서   7 ->3 -> 99 로 진행한다면 더 큰 최대값을 도출할수있는데

당장의 큰값만 선택하다보니 보다 작은 값이 도출되었음을 알 수 있다.

 

 

코드

function getChange(value) {
  let changes = [10000, 5000, 1000, 500, 100, 50, 10];
  let won = Math.floor(value / 10) * 10; // 1의 자리 원화 반내림
  let i = 0;
  let counts = [];

  while (true) {
    if (won >= changes[i]) {
      let count = Math.floor(won / changes[i]);
      won = won - changes[i] * count;
      counts[i] = count;
    } else {
      counts[i] = 0;
    }

    i++;
    if (won === 0) {
      for (let j = 0; j < changes.length - i; j++) {
        counts.push(0);
      }
      break;
    }
  }

  changes.map((change, index) => {
    console.log(`${change.toLocaleString()}원 ${counts[index]}개`);
  });
}

 

 

 

'프로그래밍 > algorithm' 카테고리의 다른 글

[알고리즘] Hash (해쉬)이란  (0) 2024.02.12
[알고리즘] DFS (깊이 우선탐색)이란  (1) 2024.02.11
Codewars 사용  (0) 2022.03.21

 

깊이 우선탐색 (Depth-Firtst Search)

 

깊이우선 탐색이란?

루트노드에서 시작해서 다음 분기로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법이라고한다.

즉, 깊게 탐색, 완벽탐색

 

사용하는경우 

모든 노드를 방문 하고자 하는 경우에 이방법을 선택한다.

 

특징 

1. 자기자신을 호출하는 순환 알고리즘의 형태를 가지고있다.

2. 어떤 노드를 방문했었는지 여부를 반드시 검사해야한다.

 

 

 

DFS의 애니메이션 출처 : 위키피디아

 

원리

위의 그림을 보면서 아래 설명을 봐보자.

1. 시작 정점을 1로 설정

2. 1번 탐색 시작 1번과 연결되었고 방문 안한 정점이 어디지? 2번이 있네. 2번으로 가자.

3. 2번과 연결되었고, 방문 안한것? 3번이네. 3번으로 가자.

4. 3번은? 4번이 있네. 4번으로 가자.

5. 4번은? 없는데..?? 그러면 3번으로 다시 올라가자.

6. 3번은? 방문 안한곳이 없네.. 다시 2번... 다시 1번

7. 1번은? 5번이 있네. 5번으로 가자.

-> 이런 식으로 방문 안한 곳이 없을때까지 반복

 

이런식으로 쭉 탐색을 한다. 이런식으로하면 한길로 깊이 있게 파고들어가게 된다. 이래서 깊이 우선탐색이라는 말이 나온것이다.

 

 

장단점

장점

코드가 직관적이고, 구현하기 쉽다.
-> 사실 일정행동을 계속 반복하는데, 이를 만들어놓고 끝까지 계속 돌려 쓰는 것이기 때문에 비교적 간단하게 코드를 구현할 수 있다.

 

단점

깊이가 엄청 깊어지면, 메모리 비용을 예측하기 어렵다.
-> 앞서 설명한 것처럼 깊이가 깊어질수록 함수가 하나씩 쌓이는 구조이다. 이 때문에 깊이가 엄청 깊어진다면 스택 메모리가 지나치게 커질 수 있다.

 

또한 최단 경로를 알 수 없다.

 

코드

const graph = {
  A: ["B", "C"],
  B: ["A", "D"],
  C: ["A", "G", "H", "I"],
  D: ["B", "E", "F"],
  E: ["D"],
  F: ["D"],
  G: ["C"],
  H: ["C"],
  I: ["C", "J"],
  J: ["I"]
};

const DFS = (graph, startNode) => {
  const visited = []; // 탐색을 마친 노드들
  let needVisit = []; // 탐색해야할 노드들

  needVisit.push(startNode); // 노드 탐색 시작

  while (needVisit.length !== 0) { // 탐색해야할 노드가 남아있다면
    const node = needVisit.shift(); // queue이기 때문에 선입선출, shift()를 사용한다.
    if (!visited.includes(node)) { // 해당 노드가 탐색된 적 없다면
      visited.push(node); 
      needVisit = [...graph[node], ...needVisit];
    }
  }
  return visited;
};

console.log(DFS(graph, "A"));
// ["A", "B", "D", "E", "F", "C", "G", "H", "I", "J"]

 

 

'프로그래밍 > algorithm' 카테고리의 다른 글

[알고리즘] Hash (해쉬)이란  (0) 2024.02.12
[알고리즘] Greedy Algorithm (탐욕알고리즘) 이란  (1) 2024.02.12
Codewars 사용  (0) 2022.03.21
function solution(land) {

    for(let i =0; i <=land.length; i++){    //행
        let landVal = 0;
        let landValSum = 0;
        let lengthAr = land[0].length;
        
        for(let j=0; j<=lengthAr-1; j++)   //열
        {
             // 왼쪽 행이랑 겹치는부분 존재한다면
            if(i !== 0 && land[i-1][j] !== 0){
                // 그행의 아래행 계속찾기
                for(let k=j; k<= 500; k++){
                   if(land[i-1][k] == 0){   
                        landValSum = land[i-1][k-1];
                        landValSum ++;
                        break;
                   } 
                }
                 land[i][j]  = landValSum;
            }else{
                if(land[i][j] === 1){
                    land[i][j] = landVal++;
                }
            }
        }
    }
    
    var answer = 0;
    return answer;
}

 

 

 

버그버그버그

'프로그래밍 > 코딩테스트' 카테고리의 다른 글

[정렬] K번째수  (0) 2023.10.28
[해시] 전화번호 목록  (0) 2023.10.21

 

1. 실행 컨텍스트란

실행가능한 자바스크립트 코드 블록이  실행되는 환경이다.

 

함수가 실행되면 함수 실행에 해당하는 실행 컨텍스트가 생성되고, 자바스크립트 엔진에 있는 콜스택에 차곡차곡 쌓인다.

그리고 가장위에있는 컨텍스트와 관련 있는 코드를 실행하면서, 전체 코드의 환경과 순서를 보장하게 된다.

 

 

 

아래의 예제로 이해보자

let santa = false; // 전역 컨텍스트
var rudolf = false;
function christmas() {	
  let santa = true;

  function givePresent() { 
    let gift = "cat";
    console.log(gift); // cat
    console.log(santa); // false
    console.log(rudolf); // false
  }

  givePresent(); // (2)
}

christmas(); // (1)

 

이런 코드를 실행하려고 하면 

처음으로 전역 컨텍스트가 호출 스택에 추가된다

실행컨텍스트가 호출스택에 쌓여가는 과정을 그림으로 그려 이해해보자

 

2. 실행 컨텍스트의 구성

 

2-1. 전역 컨텍스트의 생성

 

전역 컨텍스트란

코드가 실행되면 바로 생성되며, 컨텐스트중 제일 먼저 호출스택에 저장되는 컨텍스트이다.

생성단계에서 let으로 선언한 변수인 santa는 선언만되고 초기화는 이루어지지않고,

var로 선언된 rudolf는 선언과 동시에 undefined로 초기화가 이루어진다.

 

christmas() 도 함수선언문으로 선언되어서 호이스팅이 일어나 생성단계에서 전역컨텍스트에 기록된다

 

2-2. 전역 컨텍스트의 실행

 

전역컨텍스트가 실행단계로 들어간다. 이과정에서 함수 호출, 식별자 할당 등이 이루어진다.

초기화 되지 않아 아무 값이 없던 santa가 false로 할당 된다.

 

2-3. 함수 컨텍스트의 생성 christmas()

 

전역컨텍스트가 생성된이후 가장 먼저 호출되는 함수는 christmas() 이다.

따라서 호출스텍에 전역 컨텍스트 다음으로 christmas() 함수 컨텍스트가 쌓이게된다.

 

 

2-3. 함수 컨텍스트의 생성 givePresent()

 

christmas 함수 내부에서 givePresent 함수를 선언하고 호출한다.

따라서 givePresent 함수 컨텍스트가 호출 스택에 쌓이게 된다.

 

 

 

 

 

여기서 의문점?

let santa = false; // 전역 컨텍스트
var rudolf = false;
function christmas() {	
  let santa = true;

  function givePresent() { 
    let gift = "cat";
    console.log(gift); // cat
    console.log(santa); // false    -------->>>>>> 어떻게 호출??
    console.log(rudolf); // false  ------->>>>>> 어떻게 호출??
  }  

  givePresent(); // (2)
}

christmas(); // (1)

 

이해하던도중 그렇다면  givePresent 컨텍스트를 실행할때 어떻게 chrismas에있는 컨텍스트에 있는 santa를 호출할수있었을까??

라는 의문점이 든다.

이것을 이해하려면 실행컨테스트에 특수한객체인 outer를 이해해야한다.

 

 

3. outer 

console.log(gift)

1. gift의 값을 가져오기위해 givePresent 함수 컨텍스트의 환경 레코드를 참조한다.

2. givePresent 함수 컨텍스트의 환경 레코드에서 gift의 정보를 찾았기 때문에 console.log()를 문제없이 호출한다.

 

console.log(santa)

1. santa 의 값을 가져오기위해 givePresent 함수 컨텍스트의 환경 레코드를 참조한다.

2. 환경레코드에서 santa의 값을 찾지 못한다.

3. outer를 이용해 외부 렉시컬 환경인 chrismas 함수 컨텍스트로 이동후 환경레코드를 탐색한다.

4. santa 값을 찾으며 console.log()를 문제없이 호출한다.

 

console.log(rudolf)

1. rudolf의 값을 가져오기 위해 givePresent 함수 컨텍스트의 환경 레코드를 참조한다.

2. 환경레코드에서 rudolf의 값을 찾지 못한다.

3.  outer를 이용해 외부 렉시컬 환경인 chrismas 함수 컨텍스트로 이동후 환경레코드를 탐색한다.

4. 환경 레코드에서 rudolf를 찾지못해 outer를 이용하여 전역 컨텍스트로 이동한다.

5. 환경레코드를 탐색하여 rudolf를 찾고 console.log()를 문제없이 호출한다.

 

 

4. 스코프 체인(scope chain)

위에서 자바스크립트 엔진이 해당 영역에서 식별자를 찾는데 발견하지 못했을때 outer를 이용해여 외부 렉시컬 환경, 즉

외부 영역으로 이동하여 식별자를 찾는다는 것을 알 수 있었다.

 

이렇게 식별자를 찾기위해 외부스코프로 이동하여 탐색하는 현상을 스코프 체인이라고 한다.

 

 

 

문제 설명

배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.

예를 들어 array가 [1, 5, 2, 6, 3, 7, 4], i = 2, j = 5, k = 3이라면

  1. array의 2번째부터 5번째까지 자르면 [5, 2, 6, 3]입니다.
  2. 1에서 나온 배열을 정렬하면 [2, 3, 5, 6]입니다.
  3. 2에서 나온 배열의 3번째 숫자는 5입니다.

배열 array, [i, j, k]를 원소로 가진 2차원 배열 commands가 매개변수로 주어질 때, commands의 모든 원소에 대해 앞서 설명한 연산을 적용했을 때 나온 결과를 배열에 담아 return 하도록 solution 함수를 작성해주세요.

제한사항

  • array의 길이는 1 이상 100 이하입니다.
  • array의 각 원소는 1 이상 100 이하입니다.
  • commands의 길이는 1 이상 50 이하입니다.
  • commands의 각 원소는 길이가 3입니다.

입출력 예

array commands return

[1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]] [5, 6, 3]

입출력 예 설명

[1, 5, 2, 6, 3, 7, 4]를 2번째부터 5번째까지 자른 후 정렬합니다. [2, 3, 5, 6]의 세 번째 숫자는 5입니다.

[1, 5, 2, 6, 3, 7, 4]를 4번째부터 4번째까지 자른 후 정렬합니다. [6]의 첫 번째 숫자는 6입니다.

[1, 5, 2, 6, 3, 7, 4]를 1번째부터 7번째까지 자릅니다. [1, 2, 3, 4, 5, 6, 7]의 세 번째 숫자는 3입니다.

 

 


// 풀이과정
// 변수 : 새로운배열 k, 정렬후 자리수선택 배열 d
// 필요 : 1반복분(commands 개수),  정렬(버블)

function solution(array, commands) {
  var reArray = [];
  var answer = [];

  for (var i = 0; i < commands.length; i++) {
    var fromVar = commands[i][0];
    var toVar = commands[i][1];
    for (fromVar; fromVar <= toVar; fromVar++) {
      reArray.push(array[fromVar - 1]);
    }

    // 버블정렬
    for (var j = 0; j < reArray.length; j++) {
      for (var z = j + 1; z < reArray.length; z++) {
        if (reArray[j] > reArray[z]) {
          var tmp = reArray[z];
          reArray[z] = reArray[j];
          reArray[j] = tmp;
        }
      }
    }
    // console.log(reArray + "정렬후배열");

    answer.push(reArray[commands[i][2] - 1]);
    reArray = [];
  }
  // console.log(answer + "정답!");
  return answer;
}

solution(
  [1, 5, 2, 6, 3, 7, 4],
  [
    [2, 5, 3],
    [4, 4, 1],
    [1, 7, 3],
  ]
);

 

 

 

 

'프로그래밍 > 코딩테스트' 카테고리의 다른 글

[PCCP 기출문제] 2번 / 석유 시추  (0) 2024.02.05
[해시] 전화번호 목록  (0) 2023.10.21

+ Recent posts