[선형] 9. 스택




스택 구현은 쉬운데 활용해서 문제푸는게 너무 어렵다. 문제 여러번 복습하고 많이 풀기

1. 스택 (Stack)

스택

  • 나중에 넣은 데이터가 먼저 나오는 LIFO(Last In First Out) 기반의 선형 자료 구조
  • 데이터 전체 획득 / 비어 있는지 확인 : Stack.getBuffer(), Stack.isEmpty()
  • 추가 / 삭제 / 마지막 데이터 조회 / 크기 확인 : Stack.push(), Stack.pop(), Stack.peak(), Stack.size()
  • 데이터 위치 / 준재 여부 확인 : Stack.indexOf(), Stack.includes()



2. 스택 구현하기

getBuffer(), isEmpty()

// Stack() : 생성자 함수
function Stack(array){
    this.array = array ? array : []
}

// getBuffer() : 객체 내 데이터 셋 반환
Stack.prototype.getBuffer = function(){
    return this.array.slice()
}

// isEmpty() : 객체 내 데이터 o/x
Stack.prototype.isEmpty = function (){
    return this.array.length ===0
}

let stack = new Stack([1,2,3])

console.log(stack) //Stack { array: [ 1, 2, 3 ] }

let data= stack.getBuffer()
console.log(data) //[ 1, 2, 3 ]

console.log(data === stack.array) // false
console.log(Object.getOwnPropertyDescriptors(Stack.prototype))



push(), pop(), peak(), size()

// push() : 데이터 추가
Stack.prototype.push = function(element){
    return this.array.push(element)
}

// pop() : 데이터 삭제
Stack.prototype.pop = function() {
    return this.array.pop()
}

// peak() : 가장 끝 데이터 반환
Stack.prototype.peak = function(){
    return this.array[this.array.length-1]
}

// size() : 스택 내 데이터 개수 확인
Stack.prototype.size = function(){
    return this.array.length
}

let stack = new Stack([1,2,3])
console.log(stack) //Stack { array: [ 1, 2, 3 ] }

stack.push(4)
console.log(stack) //Stack { array: [ 1, 2, 3, 4 ] }

stack.pop()
console.log(stack) //Stack { array: [ 1, 2, 3 ] }
console.log(stack.peak()) //3
console.log(stack.size()) //3



indexOf(), includes()

// indexOf() : 매개변수로 넣어온 element 위치 확인
Stack.prototype.indexOf = function (element, position = 0) {
  for (let i = position; i < this.array.length; i++) {
    if (element === this.array[i]) return i;
  }
  return -1;
  // return this.array.indexOf(element, position)
};

// includes() : 데이터 존재 여부 확인
Stack.prototype.includes = function (element) {
  for (let i = 0; i < this.array.length; i++) {
    if (element === this.array[i]) return true;
  }
  return false;
};

let stack = new Stack([1, 2, 3]);
console.log(stack); //Stack { array: [ 1, 2, 3 ] }

console.log(stack.indexOf(2)); //1

console.log(stack.includes(4)); //false
console.log(stack.includes(3)); //true



3. 문제 - 기차 운행

오른쪽 그림처럼 열차가 들어갔다 나올 수 있는 플랫폼이 있다.
열차가 1번부터 3번까지 순서대로 들어온다고 했을 때, 입력 순서대로 나갈 수 있는지 없는지 판단하는 프로그램을 작성하시오.
입력은 차량 순서 번호가 적혀 있는 배열이며, 가능 여부에 따라 true/false 를 반환한다.

function answer(train) {
  // 코드 구현 시작 영역
  let input_order = [1, 2, 3];
  let temp = [];
  for (let i = 0; i < 3; i++) {
    if (input_order[i] === train[i]) {
      temp.unshift(null);
    } else {
      temp.unshift(input_order[i]);
    }
  }
  for (let i = 0; i < 3; i++) {
    if (temp[i] !== null) {
      if (temp[i] !== train[i]) {
        return false;
      }
    }
  }
  return true;
  // 코드 구현 종료 영역
}

let input = [
  [1, 2, 3],
  [3, 2, 1],
  [3, 1, 2],
];

for (let i = 0; i < input.length; i++) {
  process.stdout.write(`#${i + 1} `);
  console.log(answer(input[i]));
}

// #1 true
// #2 true
// #3 false
// 메소드 활용하여 가독성 증가시키기
function answer(train) {
  let stack = [];
  let num = 0;

  for (let i = 0; i < train.length; i++) {
    while (stack.length == 0 || stack[stack.length - 1] < train[i]) {
      stack.push(++num);
    }
    if (stack[stack.length - 1] == train[i]) {
      stack.pop();
    } else {
      return false;
    }
  }
  return true;

  // 코드 구현 종료 영역
}
if (!Array.prototype.peak) {
  Array.prototype.peak = function () {
    return this[this.length - 1];
  };
}
if (!Array.prototype.isEmpty) {
  Array.prototype.isEmpty = function () {
    return this.length == 0;
  };
}

function answer(train) {
  let stack = [];
  let num = 0;

  for (let i = 0; i < train.length; i++) {
    while (stack.isEmpty() || stack.peak() < train[i]) {
      stack.push(++num);
    }
    if (stack.peak() == train[i]) {
      stack.pop();
    } else {
      return false;
    }
  }
  return true;

  // 코드 구현 종료 영역
}



4. 문제 - 괄호 짝 찾기 🤭

계산 수식이 주어졌을 때, 같은 짝의 괄호 위치를 찾는 프로그램을 제작하시오.
입력은 계산 수식으로 주어지며, 괄호의 짝 별 위치를 [시작, 끝]으로 찾아 2차원 배열 형태로 반환한다.
위치 시작 값은 0으로 시작하며, 하나라도 짝이 맞지 않을 경우 빈 배열을 반환한다.

if (!Array.prototype.peak) {
  Array.prototype.peak = function () {
    return this[this.length - 1];
  };
}
if (!Array.prototype.isEmpty) {
  Array.prototype.isEmpty = function () {
    return this.length == 0;
  };
}

function answer(str) {
  let result = [];
  let stack = [];
  for (let i = 0; i < str.length; i++) {
    if (str[i] == "(") {
      stack.push(i);
    } else if (str[i] == ")") {
      if (stack.isEmpty()) {
        return [];
      }
      result.push([stack.pop(), i]);
    }
  }

  if (!stack.isEmpty()){
      return []
  }

  // 코드 구현 종료 영역
  return result;
}

let input = [
  "(a+b)",
  "(a*(b+c)+d)",
  "(a*(b+c)+d+(e)",
  "(a*(b+c)+d)+e)",
  "(a*(b+c)+d)+(e*(f+g))",
];

for (let i = 0; i < input.length; i++) {
  process.stdout.write(`#${i + 1} `);
  console.log(answer(input[i]));
}

// #1 [ [ 0, 4 ] ]
// #2 [ [ 3, 7 ], [ 0, 10 ] ]
// #3 []
// #4 []
// #5 [ [ 3, 7 ], [ 0, 10 ], [ 15, 19 ], [ 12, 20 ] ]

오 push 는 ( 일 때만 하고 ) 들어올 때 pop 한다는 개념이 너무 신선하다. 기억해두기!



5. 문제 - 접시 꺼내기 🤭

접시가 a,b,c,d 순으로 한쪽이 막혀 있는 세척기에 들어간다고 할 때, b,a,c,d 순으로 꺼내기 위해서는
push,push,pop,pop,push,pop,push,pop 순으로 꺼내면 된다.
세척기에 꺼내야 하는 접시의 순서가 주어질 때, push/pop으로 접시가 꺼내 져야 하는 동작ㅇ르 계산하는 프로그램을 작성하시오.
입력은 접시의 수가 10개를 넘기지 않는 소문자 알파벳으로 수어지며,
접시 꺼내는 push/pop 연산 동작을 push = 0, pop = 1로 변환하여 배열을 반환한다.
(단, 주어진 순서로 못 꺼낼 경우, 빈 배열로 반환)

if (!Array.prototype.peak) {
  Array.prototype.peak = function () {
    return this[this.length - 1];
  };
}
if (!Array.prototype.isEmpty) {
  Array.prototype.isEmpty = function () {
    return this.length == 0;
  };
}

function answer(str) {
  let result = [];

  // 1. 접시의 순서 abcd ... 문자열
  // 2. 꺼내야 하는 접시, 세척기 안에 있는 알파벳보다 작을때 까지 push
  // 3. 최 상단 접시와 비교

  let stack = [];
  let dish = str.split("").sort().join("");
  let dish_index = 0;

  for (let i = 0; i < str.length; i++) {
    while (stack.isEmpty() || stack.peak() < str[i]) {
      stack.push(dish[dish_index++]);
      result.push(0);
    }

    if (stack.isEmpty() || stack.peak() > str[i]) {
      return [];
    } else {
      stack.pop();
      result.push(1);
    }
  }
  // 코드 구현 종료 영역
  return result;
}

let input = ["bacd", "dabc", "edcfgbijha"];

for (let i = 0; i < input.length; i++) {
  process.stdout.write(`#${i + 1} `);
  console.log(answer(input[i]));
}

// #1 [ [ 0, 4 ] ]
// #2 [ [ 3, 7 ], [ 0, 10 ] ]
// #3 []
// #4 []
// #5 [ [ 3, 7 ], [ 0, 10 ], [ 15, 19 ], [ 12, 20 ] ]



6. 문제 - 기린의 시야 🤭

기린이 앞쪽만 볼 수 있는 경우, 다른 기린을 몇 마리 볼 수 있는지 총합을 구하는 프로그램을 작성하시오.
기린은 자신보다 작거나 같은 기린만 볼 수 있으며, 자신보다 큰 기린이 나올 경우 앞 기린들이 가려서 볼 수가 없다.
입력은 기린 별 키 값이 들어오며, 달느 기린을 볼 수 있는 총합을 구해 반환한다.
예를 들어 5,2,4,2,6,1 순의 기린 키가 입력으로 들어오면 1번 기린으 2,3,4 기린을 볼 수 있어 3마리, 2번은
볼 수 있는 기린이 없고, 3번은 1마리, 4번은 0마리, 5번은 1마리, 마지막 기린은 앞의 기린이 없으므로 0마리로, 답은 총 5마리 기린이다.

if (!Array.prototype.peak) {
  Array.prototype.peak = function () {
    return this[this.length - 1];
  };
}
if (!Array.prototype.isEmpty) {
  Array.prototype.isEmpty = function () {
    return this.length == 0;
  };
}

function answer(giraffe) {
  let result = 0;

  let stack = [];
  giraffe.push(Number.MAX_SAFE_INTEGER);
  for (let i = 0; i < giraffe.length; i++) {
    while (!stack.isEmpty() && stack.peak()["h"] < giraffe[i]) {
        result += i - stack.pop()["i"] - 1
    }
    stack.push({ h: giraffe[i], i: i });
  }
  // 코드 구현 종료 영역
  return result;
}

let input = [
  [10, 3, 7, 4, 12, 2],
  [7, 4, 12, 1, 13, 11, 12, 6],
  [20, 1, 19, 18, 15, 4, 6, 8, 3, 3],
];

for (let i = 0; i < input.length; i++) {
  process.stdout.write(`#${i + 1} `);
  console.log(answer(input[i]));
}

// #1 5
// #2 6
// #3 30



7. 문제 - 괄호 계산 🤭

4개의 기호 (,),[,] 를 이용해서 만들어지는 괄호열로, 아래 규칙으로 계산하는 프로그램을 작성하시오

  1. '()' 인 괄호 열 값은 2
  2. '[]' 인 괄호 열 값은 3
  3. '(x)' 인 괄호 열 값은 2 * X
  4. '[x]' 인 괄호 열 값은 3 * x
  5. ()[]() + []

예를 들어 ()[[] 는 2+3*3 = 11 이다.
만약 쌍이 맞지 않거나 기호 순서가 비정상적이라 올바른 괄호 셋이 만ㄷ르어지지 않는 경우에는 0을 반환한다.
입력은 4개의 기호로만 이루어진 괄호가 문자열 형태로 주어지며, 계산을 통해 나온 정수를 반환한다.

if (!Array.prototype.peak) {
  Array.prototype.peak = function () {
    return this[this.length - 1];
  };
}
if (!Array.prototype.isEmpty) {
  Array.prototype.isEmpty = function () {
    return this.length == 0;
  };
}

function answer(str) {
  let result = 0;

  // ( > x2
  // [ > x3
  // ) > /2
  // ] > /3
  // () or [], 현재 temp값을 result에 더해준다.

  let stack = [];
  let temp = 1;

  for (let i = 0; i < str.length; i++) {
    let mark = str[i];

    switch (mark) {
      case "(":
        temp *= 2;
        stack.push(mark);
        break;
      case "[":
        temp *= 3;
        stack.push(mark);
        break;
      case ")":
        if (stack.isEmpty() || stack.peak() != "(") {
          return 0;
        }
        if (str[i - 1] == "(") {
          result += temp;
        }
        stack.pop();
        temp /= 2;
        break;
      case "]":
        if (stack.isEmpty() || stack.peak() != "[") {
          return 0;
        }
        if (str[i - 1] == "[") {
          result += temp;
        }
        stack.pop();
        temp /= 3;
        break;
    }
  }

  if (!stack.isEmpty()) {
    return 0;
  }
  // 코드 구현 종료 영역
  return result;
}

let input = ["(()[[]])", "[][]((])", "(()[[]])([])"];

for (let i = 0; i < input.length; i++) {
  process.stdout.write(`#${i + 1} `);
  console.log(answer(input[i]));
}

// #1 22
// #2 0
// #3 28

이 문제는 정말 왜 이런 풀이로 변형되는지 이해가 안간다..ㅠㅠ 다시 시도해보기!

자료구조 문제를 풀 때는 해당 자료구조 구현법을 그대로 쓰는 것이 아닌, 그 특성을 활용하여 새로운 해결법으로 만들 것..!