기존에 Stack을 다룰 때, 값들을 Stack에 넣어서 사용하는 간단한 문제만 풀어보다가 index를 스택에 저장하여 사용하는 방식의 문제가 있어서 기록합니다.

Problem

✏️ 문제 설명

temperatures 배열의 각 원소에 대해 자신보다 큰 값이 나오는 Index 차이를 알아내어, 해당 값을 output 배열에 넣어 반환.

 

Approach 1. O(n^2) : Brute False

변수선언 `output[]`: 정답을 저장할 배열

  1. `stack[]` : 순회한 값들을 저장할 배열
  2. `temperatures[]` 의 뒤에서부터 순회한다.
    • 각 temperature에 대해 스택의 앞에서 부터 순회하며, temperature보다 큰 값을 구할 때 까지 count 한다.
    • 큰 값이 나오면, 해당 값을 `ouput[]`의 맨 앞에 추가한다.
var dailyTemperatures1 = function (temperatures) {
  const output = [];
  let stack = [];
  while (temperatures.length > 0) {
    const target = temperatures.pop();
    let cnt = 0;
    let flag = false;
    for (const num of stack) {
      if (num <= target) cnt++;
      else {
        cnt++;
        flag = true;
        break;
      }
    }
    stack.unshift(target);
    if (flag) {
      output.unshift(cnt);
    } else {
      output.unshift(0);
    }
  }
  return output;
};

Time Out Error!

n ≤ 10^5 이므로, 10^4 * 10^4 = 1초 원칙에 의거 시간이 부족함.

Approach 2. O(n) : Stack (Monotonic Decreasing)

  1. 변수선언
    `idx_stack[]` : output이 정해지지 않은 temperatures의 index를 저장한 배열
    `ouput[]` : 결과값을 저장할 배열
  2. `temperatures []`순회
    각 temperature에 대해, `idx_stack[]` 의 뒤에서 부터, 자신보다 값이 작은게 오면, pop() 하여 output에 index 차이를 저장
var dailyTemperatures = function (temperatures) {
  const len = temperatures.length;

  const idx_stack = [];
  const output = new Array(len).fill(0);

  for (let i = 0; i < temperatures.length; i++) {
    const target = temperatures[i];
    while (
      idx_stack.length > 0 &&
      target > temperatures[idx_stack[idx_stack.length - 1]]
    ) {
      const idx = idx_stack.pop();
      output[idx] = i - idx;
    }
    idx_stack.push(i);
  }
  return output;
};

공유하고 싶었던 내용

  1. Stack을 다룰 때, index만을 다룰 수 있다는 유연함
  2. Monotonic 배열에서 while 문을 통한 배열 checking

JavaScript PS 문제들을 풀며 알게된 꿀팁들을 공유합니다 😎

 

꿀팁

1. 2차원 배열 순회방법

for ... of 문을 사용하면 2차원 배열을 쌈뽕하게 순회가능하다.

const arr = [[1,2],[3,4],[5,6]];
//❌ No! 
const arr = [[1,2],[3,4],[5,6]];
for (let i = 0; i < arr.length; i++){
  const [left,right] = arr[i];

//⭕ Yes! 
for (let [left,right] of arr){
  // logic...
}

 

2. 값 바꾸기

구조분해할당을 이용하면 깔끔하게 바꿀 수 있음.

let str1 = "One";
let str2 = "Two";
let str3 = "Three";
console.log(str1, str2, str3); // One Two Three

// ⭐ 구조분해할당!
[str1, str2, str3] = [str2, str3, str1];

console.log(str1, str2, str3); // Two Three One


약팁

1. 변수 선언 한줄에 하기

여러 변수를 선언하다 보면 가독성이 좋지 못하게 변수들을 선언하는 경우가 발생한다.

이럴때, 구조분해할당으로 이쁘게 선언가능하다.

// 여러 변수 선언
// ❌ 여러줄에 선언해서 못생긴 코드 탄생
let left = 0;
let right = arr.length -1;
let i = 0;
let j = 0;
// ⭕ 구조분해할다응로 한줄로 선언하기
let [left, right, i, j] = [0, arr.length-1, 0, 0];

주의

1. Array 객체는 -1 Index 접근이 불가능하다.

const arr = [1, 2, 3];
console.log(arr[-1]); // ❌ Error!

console.log(arr[arr.length - 1]); // ⭕ Correct~

 

2. sort 함수 문자열 취급 주의

문자열에 대한 2가지 Sorting 방법이 존재한다.

  1. 기본 함수 
    ASCII Code 기준으로 문자열을 sorting한다. (ASCII : A < a)
  2.  localeCompare 함수
    사전 순으로 sorting한다. (사전 순 : a < A)

그래서, 사전 순으로 정렬된 것을 ASCII Code 정렬 순으로 바꾸려면 복잡한 로직 필요 :(

 

3. forEach 구문의 `return`은 forEach 내부 콜백함수의 종료를 의미한다.

먼저, forEach와 관련된 간단한 문제를 보겠습니다.[문제 설명] : 배열 arr중에 첫번째 2의 index를 출력하는 간단한 문제[예상 결과] : 2 [실제 결과] : 3

const forEachTest = () => {
  const arr = [1, 2, 3];

  let answer;
  arr.forEach((element, index) => {
    if (element === 2) {
      answer = index;
      return;
    }
    answer = 3;
  });
  console.log(answer);
};

console.log(forEachTest());

3이 콘솔에 찍힐까요❓ forEach 함수를 클릭해보면 다음과 같은 함수임을 알수 있습니다.

/**
* Performs the specified action for each element in an array.
* @param callbackfn  A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
* @param thisArg  An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;

 

forEach 구문은 callbackfn을 받고, 이를 실행해주는 함수입니다.

화살표 함수임을 인지한 상태로 바라보면, `return` 구문은 해당 시점의  callbackFn을 종료하는 구문에 불과합니다.

 

따라서, 올바른 return 시점에서의 `answer = 3`은 실행되지 않지만, forEach 구문 역시 종료되지 않아서 `index == 2` 일때 answer = 3이 실행되는 것입니다.

 

따라서, forEach구문을 사용하려면 1️⃣`boolean` 값으로 진행여부를 판단하거나, 2️⃣`map` `for(let i = 0...`을 사용하시는게 좋겠습니다.

 

 

풀면서 생기는대로 추가하겠습니다 😁

 

'PS' 카테고리의 다른 글

프로그래머스 전화번호 목록 JS 풀이  (0) 2024.11.04
[파이썬] 코테 꿀팁  (2) 2024.10.10

🔎DFS (Depth First Search)

깊이 우선 탐색으로, 스택을 사용해 구현한다.

구현 방법

  1. graph를 배열의 형태로 완성한다.
    → 각 index에 해당 index번째 노드와 연결된 간선의 번호를 적는다.
    cf) 양방향인 경우, 양쪽에 적어야한다.
  2. 탐색 시작 노드를 스택에 삽입하고 방문처리한다.
  3. 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면 해당 노드를 스택에 넣고 방문 처리한다.
    → 방문하지 않은 인접 노드가 없으면 다시 스택에서 최상단 노드를 꺼낸다.
  4. 2번이 불가능 할때까지 반복한다.
# 방문 정보를 리스트 자료형으로 표현
# 노드는 9개지만, index 0번 노드는 사용하지 않는다.
visited =[False] * 10

# 각 노드가 연결된  정보를 리스트 자료형으로 표현 (2차원 리스트)
graph = [
	[],                 # 빈 자리
  [3, 4, 5],          # 1번 노드와 연결된 노드들
  [2, 3, 8],          # 2번 노드와 연결된 노드들
  [1, 7],             # 3번 노드와 연결된 노드들
  [1, 4, 5],          # 4번 노드와 연결된 노드들
  [3, 5],             # 5번 노드와 연결된 노드들
  [3, 4],             # 6번 노드와 연결된 노드들
  [7],                # 7번 노드와 연결된 노드들
  [2, 6, 8],          # 8번 노드와 연결된 노드들
  [1, 7]              # 9번 노드와 연결된 노드들
]

def DFS(graph, visited, start):
  # 현재 노드를 방문 처리
  visited[start] = True
  print(start, end = ' ')

  for i in graph[start]:
    if not visited[i]:
      DFS(graph, visited, i) // 재귀함수!!

# 현재 노드와 연결된 다른 노드를 재귀적으로 방문
DFS(graph, visited, 1)

🔎BFS (Breadth First Search)

너비 우선 탐색으로, deque를 사용하여 구현

 

구현 방법

  1. graph를 배열의 형태로 완성한다.cf) 양방향인 경우, 양쪽에 적어야한다.
    → 각 index에 해당 index번째 노드와 연결된 간선의 번호를 적는다.
  2. 탐색 시작 노드를 큐에 삽입하고 방문 처리한다.
  3. 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리한다.
  4. 2번이 불가능 할때까지 반복한다.
from collections import deque

# BFS
def BFS(graph, visited, start):
  queue = deque([start])

  # 첫 노드 방문 처리
  visited[start] = True

  # 큐가 빌 때까지 반복
  while queue:
    # 큐에서 하나의 원소를 뽑아 출력
    v = queue.popleft()
    print(v, end = ' ')

    # 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
    for i in graph[v]:
      if not visited[i]:
        queue.append(i)
        visited[i] = True

# 노드는 9개지만, index 0번 노드는 사용하지 않는다.
visited =[False] * 10

# 각 노드가 연결된  정보를 리스트 자료형으로 표현 (2차원 리스트)
graph = [
  [],                 # 빈 자리
  [],                 # 1번 노드와 연결된 노드들
  [2, 3, 8],          # 2번 노드와 연결된 노드들
  [1, 7],             # 3번 노드와 연결된 노드들
  [1, 4, 5],          # 4번 노드와 연결된 노드들
  [3, 5],             # 5번 노드와 연결된 노드들
  [3, 4],             # 6번 노드와 연결된 노드들
  [7],                # 7번 노드와 연결된 노드들
  [2, 6, 8],          # 8번 노드와 연결된 노드들
  [1, 7]              # 9번 노드와 연결된 노드들
]

BFS(graph, visited, 1)

✏️팁

  • 둘다 O(NlogN)의 시간 복잡도를 지닌다.
  • True / False를 적극 활용하기
visited =[False] * (N+1)
  • ⭐노드가 1부터 시작하는 경우, graph와 visited도 1부터 시작하기 → 헷갈림 방지
  • DFS의 재귀가 이미 visited를 모두 방문했음에도 실행하고 있을 때를 대비하여, 해 호출마다 check_end 함수를 넣을 수 있음.
  • 일반적으로, BFS가 DFS보다 빠름.

+ Recent posts