클래스를 통해 구현
ADT의 내용
※ 주의! ADT는 기능만 명세하고, 구현방법은 명세하지 않음! ⇒ 성능분석 X
성능분석에 사용되는건 의사 코드(pseudo code)임
last-in-first-out(LIFO)
자료구조가 수행해야할 3가지 필수기능 : 탐색, 삽입, 삭제
=> 스택은 삭제(pop) 연산에 집중된 자료구조
같은 타입의 데이터만 저장가능. 단, 저장하는 타입은 상관x
자료구조 구현은 자유롭게 하되, 그 특성에 만족하는 적절한 구현을 할것
push(object) : 리턴값 x. 같은 타입의 데이터만 저장 가능
object pop() : 프로그래머의 취향(필요)에 따라 삭제만 하고 리턴을 (object) 지정 안해도 되고, 삭제와 동시에 리턴까지 구현할 수도있다.
object top() : 꼭대기에 누가 있는지 답해줄 수만 있으면 됨
integer size() : element 개수 정수형으로 리턴
boolean empty() : 비어있는지 판단후 boolean형 리턴
각 메소드는 필요에 따라 리턴 유무, 리턴 타입등의 상세 구현내용을 변경 가능함
스택이 비어있을 때 empty() 또는 pop() 을 호출하면
예외 핸들러 throw(StackEmpty) 를 동작시킴.
// 메소드의 선언만 적어놓음 (각 메소드 정의 내용은 안 적어놓았음)
template <typename E>
class Stack{
public:
int size() const;
bbol empty() const;
const E& top() const;
throw(StackEmpty);
void push(const E& e);
void pop() throw(StackEmpty);
}
어떤 코드의 특별한 케이스에 대해 문제가 발생하는 상황을 해결해줌
형태 : throw 핸들러 명칭
스택에서 핸들러를 사용하는 상황들
=> 함수 호출이 발생하면 그때 까지의 상황을 스택에 저장해 놓았다가, 함수 내용을 실행 후 다시 돌아와서 스택에 저장해 놓앗던 작업 내용을 다시 꺼내서 실행함.
웹 브라우저에서 뒤로가기 버튼 (가장 최신에 접속했던 사이트로 돌아가야 할것임)
Undo sequence in a text editor => UNDO 기능을 구현하기 위해 명령어들을 저장해야
할 상황
예제 분석
배열로 스택을 "구현" 한다는 것 : 배열을 이용해서 데이터를 저장하고, 배열을 이용해서 연산 (push, top, pop) 을 할수있도록 알고리즘을 만들고 함수를 디자인 하는 것
스택에서는 저장할 데이터 객체외에 꼭대기 위치(인덱스)를 저장하는 정수형 변수가 필요
(배열은 element들의 위치를 인덱스를 기반으로 access 하므로)
링크드 리스트는 위치를 실제 주소(포인터 활용) 로 저장
// t : 스택의 꼭대기 인덱스를 저장하는 정수형 변수
// int t = -1; 으로 초기값이 설정되었을 것임
// S : 배열
Algorithm size()
return t+1
Algorithm empty()
{
if S.size() = 0 then
return true
else
return false
}
Algorithm pop()
if empty() then
throw StackEmpty
else
t <- t -1 // 꼭대기 인덱스만 하나 이동시키면 마치 삭제된듯한 효과 발생(실제로 메모리에서 삭제되진 않음)
return S[t+1] // t를 1감소시켰으므로 꼭대기 원소를 삭제하려면 t+1 에 있는 것을 삭제
Algorithm top()
if empty() then
throw StackEmpty
else
return S[t]
Algorithm push(o)
if t = S.size() -1 then // 배열이 꽉찼는데 push 하는 경우
throw StackFull
else
t <- t + 1
S[t] <- 0
스택은 Abstract 데이터 타입이므로 기능만 명세하고, 성능은 명세하지 않는다.
하지만 스택을 배열 또는 링크드 리스트로 "구현" 한 것은 성능 분석이 가능하다.
즉, ADT 는 "구현" 이 되어야 "성능 분석" 이 가능하다.
5가지 기능(push,pop,top,empty,size) 모두 O(1) 로 수행
공간은 O(N) 만큼 사용 => n 이 아니라 N이다!!! (n:배열(스택)안의 element 개수, N:할당한 배열의 크기)
(처음 메모리를 할당할때 배열의 크기인 N 만큼 연산이 수행되므로)
한계 : 최대크기가 미리 정해져있으며, 변하지 않는다. → stackFull 에러 발생
Algorithm size()
return t+1
Algorithm pop()
if empty() then
throw StackEmpty
else
t <- t -1
return S[t+1]
Algorithm top()
if empty() then
throw StackEmpty
else
return S[t]
Algorithm push(o)
if t = S.size() -1 then // 배열이 꽉찼는데 push 하는 경우
throw StackFull
else
t <- t + 1
S[t] <- o
링크드리스트의 head 를 top 으로 설정
자료구조는 데이터가 들어오고 나가는 문이 존재
임의의 위치 정보만 주면 빠른 연산 수행가능
모든 연산이 O(1) 으로 수행
push,pop,top 연산 수도코드(대충 작성함)
// p : 노드 포인터 (node* p = new node;)
Algorithm push(data)
p <- new node pointer struct(?) 이렇게 적는게 맞나
p.data <- data
top.next <- p
top <- p
size <- size + 1
Algorithm pop()
if empty() then
throw StackEmpty
else
q <- top
top <- top.next
size <- size - 1
return p <- data
Algorithm top()
if empty() then
throw StackEmpty
else
return top.data
Algorithm empty()
if top = NULL then
return true
else
return false
싱글리 리스트 tail에 대한 pop연산이 O(n) 이 걸리기 때문
A. 마지막 원소를 삭제시키는 pop을 비교하자.
cf. delete tmp로 기존 head, tail의 주소가 해제됨