👉🏻 스택 (stack, '쌓다, 포개다') : 직역 그대로 데이터를 순서대로 쌓는 구조입니다. 새로운 요소의 삽입과 기존 요소의 제거가 하나의 방향, 즉 스택의 최상단(top)에서만 발생하는 선형 데이터 구조입니다. 쉽게 말하자면 프링글스 감자칩을 먹을 때 뚜껑쪽에서 하나하나 꺼내먹는 상황입니다. 이런 정책을 LIFO(Last In First Out) 혹은 FILO(First In Last Out)이라고 부릅니다.
고정 크기 스택은 고정 크기를 가지며 동적으로 늘어가거나 줄어들 수 없습니다. 스택이 가득 차고 여기에 요소를 추가하려고 하면 오버플로 오류가 발생합니다. 스택이 비어 있고 스택에서 요소를 제거하려고 하면 언더플로 오류가 발생합니다.
동적 크기 스택은 동적으로 늘어나거나 줄어들 수 있습니다. 스택이 가득 차면 새 요소를 수용할 수 있도록 자동으로 크기가 늘어나고, 스택이 비어 있으면 크기가 줄어듭니다. 스택 크기를 쉽게 조정할 수 있으므로 연결 목록을 사용하여 구현됩니다.
컴퓨트 프로그램에서 함수 호출을 추적하고 함수가 반환될 때 오바른 함수로 제어를 반환하는 데 사용됩니다.
프로그램 카운터의 값과 레지스터의 값을 컴퓨터 프로그램에 저장하여 함수가 반환될 때 프로그램이 이전 상태로 돌아갈 수 있도록 하는 스택 유형입니다.
스택은 컴퓨터 프로그램에서 사용자가 작업을 실행 취소하고 다시 실행할 수 있도록 하는 데 사용됩니다.
Method | 시간복잡도 | 작 업 |
---|---|---|
push() | O(1) | 스택에 요소를 삽입합니다. 스택이 가득 차면 overflow 상태라고 합니다. |
pop() | O(1) | 스택에서 요소를 제거합니다. 푸시된 순서의 역순으로 pop 되며, 스택이 비어 있으면 underflow의 조건이라고 합니다. |
isEmpty() | O(1) | 스택이 비어 있으면 true, 그렇지 않으면 false를 반환합니다. |
isFull() | O(1) | 스택이 가득 차 있으면 treu, 그렇지 않으면 false를 반환합니다. |
size() | O(1) | 스택 내의 모든 요소들의 개수(크기)를 반환합니다. |
top() | O(1) | 스택의 최상위 요소를 반환합니다. |
peek() | O(1) | top 포인터가 가리키는 데이터를 조회(확인)합니다. |
pop()
하면 stack underflow가 발생할 수 있습니다.컴퓨터 과학에서 표현식 평가, 함수 호출, 메모리 관리 등 다양한 응용 분야에 일반적으로 사용됩니다.
배열이나 연결리스트를 사용하여 구현할 수 있습니다. 스택에는 최상위 포인터가 포함됩니다.
스택을 위한 배열을 하나 만듭니다. index가 0이면 스택이 비어 있는 것이고, 스택에 요소를 삽입할 때 index 자리에 넣고 index를 올립니다. index가 초기 배열 크기 이상이면 stack이 꽉 찬 것이고, 요소를 삭제할 때는 index에 있던 값을 반환하고 index를 하나 줄입니다.
let index = -1; // 👉🏻 index가 pointer이므로 메모리를 덜 사용합니다.
let MAX = 1000; // 👉🏻 스택의 크기를 미리 정해야 해서 메모리 크기가 동적이지 않습니다.
const stack = Array(MAX).fill(0);
function isEmpty() {
return index < 0
// || return stack.length === 0 ? true : false;
}
function push(element) {
if(index >= (MAX - 1)) {
console.log("Stack Overflow!😱");
return false
} else {
index++;
stack[index] = element;
return true
}
}
function pop() {
if(index < 0) {
console.log("Stack Underflow!😰")
return 0;
} else {
let x = stack[index];
index--;
return x
}
}
function peek() {
if(index < 0) {
console.log("Stack Underflow!😰")
return 0
} else {
let x = stack[index];
return x
}
}
물리 메모리 상에서는 순서와 관계없이 무작위로 공간을 할당하고, 새 요소를 추가할 때 새 노드를 만들고 현재 최상위 노드의 다음 포인터로 새 노드 요소를 연결하면 됩니다. 런타임 시 필요에 따라 메모리가 확장﹒축소될 수 있어서 Overflow가 불가능합니다. 하지만 포인터 관련으로 추가 메모리가 필요하고 연결 리스트의 특징으로 무작위 접근이 불가능합니다.
let pointer;
class Node {
constructor(value) {
this.data = value;
this.next = null;
}
}
function isEmpty() {
if(pointer === null) return true;
else return false;
}
function push(value) {
let newNode = new Node(value);
if(pointer === null) pointer = newNode;
else {
let temp = pointer;
pointer = newNode;
newNode.next = temp;
}
}
function pop() {
let poped = Number.MIN_VALUE;
if(pointer === null) console.log("Stack if Empty🤷🏻♀️");
else {
poped = pointer.data;
pointer = pointer.next;
}
return poped;
}
function peek() {
if(pointer === null) {
console.log("Stack is empty🤷🏻♀️");
return Number.MIN_VALUE;
} else return pointer.data;
}