문제 링크

https://www.acmicpc.net/problem/1920

 

구현

<첫 답안>

const fs = require('fs');
const input = fs.readFileSync('예제.txt', 'utf8').trim().split('\n'); // 백준 경로:'/dev/stdin'

const N = parseInt(input[0]);
const arrayA = input[1].split(' ').map(Number).sort((a,b)=> a-b);
const M = parseInt(input[2]);
const arrayB = input[3].split(' ').map(Number);

const result = [];

//B의 수가 A에 존재하면 1, 아니면 0

//binary search
let start = 0;
let end = N-1;

for(const target of arrayB){
  while(start<=end){ //루프 종료 조건 : 시작인덱스>종료인덱스
    let mid = Math.floor((start + end)/2);
    let midValue = arrayA[mid];

    if(midValue>target){
      end = mid - 1;
    }
    else if(midValue<target){
      start = mid + 1;
    }
    else if(midValue === target){ //반복문 종료
      result.push(1);
      break;
    }
    else { //못찾았을때
      result.push(0);
    }
  }
}

console.log(result);

 

<문제점>

- start, end, found 를 각 target마다 초기화해야한다.

 

- target값이 없을 때 로직을 잘못 구현했다.

 else { //못찾았을때
      result.push(0);
    }

 

while 문에서 이 코드는 절대 실행되지 않는다.

target 값이 없으면 인덱스가 조정되어 while 루프가 끝날 뿐, else에 도달하지 않는다.

따라서 while문이 끝난 뒤 찾지 못한 사실을 확인하고, 0을 추가해야한다.

 

<올바른 답안>

const fs = require('fs');
const input = fs.readFileSync('예제.txt', 'utf8').trim().split('\n'); // 백준 경로:'/dev/stdin'

const N = parseInt(input[0]);
const arrayA = input[1]
  .split(' ')
  .map(Number)
  .sort((a, b) => a - b);
const M = parseInt(input[2]);
const arrayB = input[3].split(' ').map(Number);

const result = [];

//B의 수가 A에 존재하면 1, 아니면 0

//binary search

for (const target of arrayB) {
  let start = 0;
  let end = N - 1;
  let found = false;

  while (start <= end) {
    //루프 종료 조건 : 시작인덱스>종료인덱스
    let mid = Math.floor((start + end) / 2);
    let midValue = arrayA[mid];

    if (midValue > target) {
      end = mid - 1;
    } else if (midValue < target) {
      start = mid + 1;
    } else {
      //midValue === target
      result.push(1);
      found = true;
      break;
    }
  }
  if (!found) {
    result.push(0);
  }
}

console.log(result.join('\n'));

 

1. 배열 사용

function createAdjacencyList(edges, nodeCount) {
  const adjacencyList = Array.from({ length: nodeCount + 1 }, () => []);

  for (const [u, v] of edges) {
    adjacencyList[u].push(v);
    adjacencyList[v].push(u);
  }

  return adjacencyList;
}

// 입력값
const nodeCount = 6;
const edgeList = [
  [1, 2],
  [2, 5],
  [5, 1],
  [3, 4],
  [4, 6]
];

// 인접 리스트 생성
const adjacencyList = createAdjacencyList(edgeList, nodeCount);

// 출력
adjacencyList.forEach((nodes, index) => {
  if (index !== 0) console.log(index, ":", nodes);
});

 

 

2. Map 객체 사용

function createAdjacencyList(edges, nodeCount) {
  const adjacencyList = new Map();

  // 노드 개수만큼 빈 배열을 미리 초기화
  for (let i = 1; i <= nodeCount; i++) {
    adjacencyList.set(i, []);
  }

  // 양방향 그래프이므로 양쪽에 추가
  for (const [u, v] of edges) {
    adjacencyList.get(u).push(v);
    adjacencyList.get(v).push(u);
  }

  return adjacencyList;
}

// 입력값
const nodeCount = 6;
const edgeList = [
  [1, 2],
  [2, 5],
  [5, 1],
  [3, 4],
  [4, 6]
];

// 인접 리스트 생성
const adjacencyList = createAdjacencyList(edgeList, nodeCount);

// 출력
for (let [key, value] of adjacencyList) {
  console.log(key, ":", value);
}

 

📌 출력 결과

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

 

배열을 사용하면 메모리 효율이 좋고, Map을 사용하면 키 값을 유연하게 설정할 수 있다.

문제 링크 

https://school.programmers.co.kr/learn/courses/30/lessons/81302

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

 

구현

function solution(places) {
    //각 대기실마다 거리두기를 지켰는지 확인하는 함수
    const checkRule = (place) => {
        //문자열을 배열로 변환 
        const placeArr = place.map(row => [...row]);
        
        //P 좌표들
        const poses = [];
        
        //P를 찾는다 => 좌표 추출 => 거리 계산
        for(let i=0;i<5;i++){
            for(let j=0;j<5;j++){
                if(placeArr[i][j] === 'P'){
                    poses.push([i,j]);
                }
            }
        }
        
        //모든 항목에 대해 거리 계산
        for(let i=0; i<poses.length;i++){
            for(let j=i+1;j<poses.length;j++){
                const [x1, y1] = poses[i];
                const [x2, y2] = poses[j];
                const distance = Math.abs(x1-x2) + Math.abs(y1-y2);
                
                //거리가 2이하면, 사이에 있는 항목 확인
                if(distance<=2){
                    let isSafe = false;
                    
                    // 1. 수직
                    if(x1===x2 && Math.abs(y1-y2)===2){
                        if(placeArr[x1][(y1+y2)/2]==='X'){
                            isSafe = true;
                        }
                    }
                    //2. 수평
                    if(y1===y2 && Math.abs(x1-x2)===2){
                        if(placeArr[(x1+x2)/2][y1] === 'X'){
                            isSafe = true;
                        }
                    }
                    
                    //3. 대각선
                    if(Math.abs(x1-x2) === 1 && Math.abs(y1-y2) === 1){
                        if(placeArr[x1][y2]==='X' && placeArr[x2][y1]==='X'){
                            isSafe = true;
                        }
                    }
                    
                    if(!isSafe){
                        return 0;
                    }
                }
            }
        }
        return 1;
    };
    
        return places.map(checkRule);
    
}

 

처음에는 구조분해 할당을 통해

const [place1, place2, place3, place4, place5] = places;

이렇게 두고 각 항목마다 checkRule 함수를 적용했었다.

 

map을 활용하면 배열 안의 주어진 모든 요소에 함수를 적용해서 새로운 배열을 만들 수 있다.

 

 

* 2차원 배열에서 특정 문자열의 위치 정보 찾기 => 2중 순회 활용

if(arr[i][j] === 'X') return [i,j];

 

💡 유형: 자료구조

문제 링크

https://www.acmicpc.net/problem/2346

 

풀이(시간초과)

<상황>

1. 1~N번까지 풍선 원형 배치 (시계방향)

2. 풍선 안에 번호 적힌 종이 있음 

 

<실행>

1. 1번 풍선을 터뜨린다.

2. 1번 풍선 안에 있는 종이를 꺼내어 그 종이에 적혀있는 값만큼 이동하여 다음 풍선을 터뜨린다.

3. 이동방향은 시계방향이고, 이미 터진 풍선은 빼고 이동한다.

 

풍선 인덱스 정보와 풍선 속 숫자 정보를 함께 저장하는 배열을 새로 생성했다.

const fs = require('fs');
const input = fs.readFileSync('예제.txt', 'utf8').trim().split('\n');

const N = parseInt(input[0]);
let balloons = input[1].split(' ').map(Number).map((val, idx) => [val, idx + 1]); // [풍선 속 숫자, 풍선 번호]
const result = [];

let index = 0; // 첫 번째 풍선부터 시작

while (balloons.length > 0) {
    let [paperNum, balloonNum] = balloons[index]; // 현재 풍선 정보 저장
    result.push(balloonNum); // 터뜨린 풍선의 원래 번호 저장

    balloons.splice(index, 1); // 풍선 제거

    if (balloons.length === 0) break; // 모든 풍선을 터뜨렸으면 종료

    // 다음 위치 계산
    if (paperNum > 0) {
        index = (index + paperNum - 1) % balloons.length; // 오른쪽 이동(현재 위치 포함하므로 -1)
    } else {
        index = (index + paperNum) % balloons.length; // 왼쪽 이동
        if (index < 0) index += balloons.length; // 음수 인덱스 보정
    }
}
console.log(result.join(' '));

 

 

 

node.js는 기본적으로 메모리를 많이 사용하므로 이런 많은 연산이 필요할 때 적은 메모리 제한을 두는 문제에 쓰는 것은 적합하지 않다.(백준에서 자바스크립트 문제를 풀기 어려운 이유ㅎㅎㅠ) 따라서 이 문제는 파이썬 등 다른 언어로 풀이하는 것이 맞지만, 나는 Javascript 로 알고리즘을 학습하는 데에 목적이 있으므로 이 풀이를 올려본다.

문제 링크

https://www.acmicpc.net/problem/1158

 

풀이

const fs = require('node:fs');
const input = fs.readFileSync('예제.txt','utf8').split(' '); // 백준 경로:'/dev/stdin'

const [n, k] = input.map(Number);
const circle = [];
const result = [];

for(let i=1;i<=n;i++) circle.push(i); //[1,2,3,4,5,6,7]

//k번째 사람을 제거한다.
//제거하고, 그 시점부터 다시 k번째

let remove = 0;
while(circle.length>0){
    remove = (remove + k-1) % circle.length;
    result.push(circle.splice(remove,1)[0]);
}

console.log(`<${result.join(', ')}>`);

 

 

백준에서 자바스크립트를 사용해서 문제를 푸는 방법은 아래 블로그를 참고했다.

https://nyang-in.tistory.com/156

 

[백준]백준에서 node.js 입출력 방법 정리(백준/자바스크립트/코딩테스트/알고리즘)

안녕하세요. 이번 시간에는 백준에서 node.js 입출력 방법에 대해 알아보겠습니다. 자바스크립트로 코딩테스트를 준비할 경우, 백준에서는 node.js를 선택하여야 합니다. 그런데 node.js가 좀 번거롭

nyang-in.tistory.com

 

여기서 내 VSCode는 fs를 불러올 때

A Node.js builtin module should be imported with the node: protocol 오류가 발생했다.

내 VSCode에서 코드 검사 도구(ESLint, Biome 등)를 사용하고 있기 때문이었다. 이 도구들은 빌트인 모듈을 명시적인 node: 프로토콜로 임포트하도록 강제한다. 따라서 아래와 같이 모듈을 불러와야했다.

const fs = require('node:fs');

 

 

문제 링크

https://www.acmicpc.net/problem/2164

 

구현 & 풀이

첫 답안은 시간 초과가 났다.

const fs = require('node:fs');
const n = Number.parseInt(fs.readFileSync('예제.txt','utf8')); // 백준 경로:'/dev/stdin'

const stack = [];
for(i=1;i<n+1;i++){
    stack.push(i); //stack = [1,2,3,4]
}

while(stack.length>1){
    stack.shift(); //제일 위에 있는 카드를 버림 234  
    if(stack.length>1){
        const first = stack.shift();
        stack.push(first); //이후 제일 위에 있는 카드를 맨 밑으로 옮김  342
    }
}

console.log(stack[0]);

 

생각해보니 굳이 while문 내에서 동일한 조건의 if문을 사용할 필요가 없어서 코드를 줄였다.

const fs = require('fs');
const n = Number(fs.readFileSync('/dev/stdin', 'utf8'));

const queue = Array.from({ length: n }, (_, i) => i + 1); // 1~N까지 초기화

while (queue.length > 1) {
    queue.shift(); // 첫 번째 카드 버림
    queue.push(queue.shift()); // 그다음 카드를 뒤로 보냄
}

console.log(queue[0]); // 마지막 남은 카드 출력

 

하지만 계속 시간초과가 났는데, 그 이유는 shift() 메서드의 비효율성 때문이다. shift() 연산은 배열의 첫 번째 요소를 제거하고 나머지 모든 요소를 한 칸씩 앞으로 이동시키는 O(n) 시간 복잡도를 가진다. 큰 n 값에 대해 이 연산을 반복적으로 수행하면 전체 시간 복잡도 성능이 크게 저하된다.

 

따라서 실제로 자료구조를 조작하는 것이 아니라 포인터를 조작하는 방법을 사용했다. 

const fs = require('node:fs');
const n = Number.parseInt(fs.readFileSync('예제.txt','utf8')); // 백준 경로:'/dev/stdin'

const deque = []
for(let i=1; i<=n; i++) deque.push(i);

let front = 0; //첫번째 원소를 가리키는 포인터

while(deque.length - front > 1){
    front++; //첫번째 카드 버리기
    deque.push(deque[front]); //두번째 카드를 맨 뒤로 이동
    front++; //세번째 카드가 맨 위에 있음
}

console.log(deque[front]);

 

 

포인터를 통해 인덱스만 조작하므로 O(1) 시간 복잡도를 가져 효율적이다.

 

🚀 Deque(덱) vs 일반 Queue(큐) 비교

자료구조 삽입(push) 삭제(shift/pop) 양 쪽 조작
Queue O(1) O(N) (shift) ❌ 한쪽에서만 삽입, 반대쪽에서 삭제
Deque(Double-ended Queue) O(1) O(1) ✅ 양쪽 조작 가능

 

=>자바스크립트는 기본적으로 Array를 사용해서 구현한다. 이때 shift()를 사용하지 않고, 포인터를 사용해 Deque처럼 구현하는 것이 효율적이다. 

 

+ Recent posts