2.1 스택 ADT
2.1.1 스택의 개념
- 스택에서 데이터 입/출력은 오로지 스택의 꼭대기에서만 이루어짐
- 가장 마지막에 들어간 데이터가 제일 먼저 나오고(LIFO Last-In-First Out) 가장 먼저 들어간 데이터는 가장 나중에 나옴(FILO First In Last out)
2.1.2 스택의 핵심 기능 : 삽입과 제거 연산
삽입 연산
- 스택 위에 새로운 노드(요소)를 "쌓는" 일
제거 연산
- 스택에서 최상위 노드를 "걷어"냄
2.2 배열로 구현하는 스택
- 배열 기반의 스택은 각 노드를 동적으로 생성하고 제거하는 대신, 스택 생성 초기에 사용자가 부여한 용량만큼의 노드를 한꺼번에 생성
- 최상위 노드의 위치를 나타내는 변수를 두고 삽입과 제거 연산 수행
2.2.1 배열 기반 스택과 스택의 노드 표현
노드 구조체
- 데이터만 담는 구조체로 표현
- 노드가 존재하는 위치는 배열의 인덱스로 알 수 있기 때문에 이전이나 다음 노드에 대한 포인터 필요 없음
typedef int ElementType;
typedef struct tagNode
{
ElementType Data;
} Node;
스택 구조체
- 용량 : 스택이 얼마만큼의 노드를 가질 수 있는지 알기 위해 사용
- 최상위 노드의 위치 : 삽입/제거 연산을 할 때 최상위 노드에 접근할 수 있게 도와줌
- 노드 배열 : 스택에 쌓이는 노드를 보관하는 데 사용
- 노드 배열을 힙에 할당하고 Nodes 포인터는 힙에 할당한 배열을 가리키는 용도로 사용
- Nodes 포인터는 힙에 할당한 배열의 첫 번째 요소를 가리킴
typedef struct tagArrayStack
{
int Capacity;
int Top;
Node* Nodes;
}ArrayStack;
2.2.2 배열 기반 스택의 기본 연산
스택 및 노드 생성 함수 AS_CreateStack()
- 스택을 생성하고 노드를 받아들일 공간을 준비
- Top는 노드가 삽입될 때마다 1씩 증가하고 노드가 삭제될 때마다 1씩 감소
void AS_CreateStack(ArrayStack**, int Capacity)
{
//스택을 힙에 생성
(*Stack) = (ArrayStack*)malloc(sizeof(ArrayStack));
//입력된 Capacity만큼의 노드를 힙에 생성
(*Stack) = (Node*)malloc(sizeof(Node)*Capacity);
//Capacity 및 Top 초기화
(*Stack)->Capacity = Capacity;
(*Stack)->Top = -1;
}
스택 및 노드 삭제 함수 AS_DesrotyStack()
- 스택 내의 노드와 스택을 삭제
void AS_DestroyStack(ArrayStack* Stack)
{
//노드를 힙에서 해제
free(Stack->Nodes);
//스택을 힙에서 해제
free(Stack);
}
노드 삽입 함수 AS_Push
- 최상위 노드의 인덱스(Top)에서 1을 더한 곳에 새 노드를 입력하도록 구현
void AS_Push(ArrayStack* Stack, ElementType Data)
{
Stack->Top++;
Stack->Nodes[Stack->Top].Data = Data;
}
노드 제거 함수 AS_Pop
- 최상위 노드의 인덱스(Top)값을 1만큼 낮추도록 구현
- 제거 연산에서는 최상위 노드에 있던 데이터를 호출자에게 반환해야 함
ElementType AS_Pop(ArrayStack* Stack)
{
int Position = Stack->Top--;
return Stack->Nodes[Postion].Data;
}
2.2.3 배열 기반 스택 예제 프로그램
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct tagNode
{
ElementType Data;
}Node;
typedef struct tagArrayStack
{
int Capacity;
int Top;
Node* Nodes;
}ArrayStack;
//스택 및 노드 생성
void AS_CreateStack(ArrayStack** Stack, int Capacity)
{
//스택을 힙에 생성
(*Stack) = (ArrayStack*)malloc(sizeof(ArrayStack));
//입력된 Capacity만큼의 노드를 힙에 생성
(*Stack)->Nodes = (Node*)malloc(sizeof(Node) * Capacity);
//Capacity 및 Top 초기화
(*Stack)->Capacity = Capacity;
(*Stack)->Top = -1;
}
//스택 및 노드 해제
void AS_DestroyStack(ArrayStack* Stack)
{
//노드를 힙에서 해제
free(Stack->Nodes);
//스택을 힙에서 해제
free(Stack);
}
//노드 삽입
void AS_Push(ArrayStack* Stack, ElementType Data)
{
Stack->Top++;
Stack->Nodes[Stack->Top].Data = Data;
}
//노드 제거
ElementType AS_Pop(ArrayStack* Stack)
{
int Position = Stack->Top--;
return Stack->Nodes[Position].Data;
}
//최상위 노드 반환
ElementType AS_Top(ArrayStack* Stack)
{
return Stack->Nodes[Stack->Top].Data;
}
//스택 크기 반환
int AS_GetSize(ArrayStack* Stack)
{
return Stack->Top + 1;
}
//스택이 비어있는지 검사
int AS_IsEmpty(ArrayStack* Stack)
{
return (Stack->Top == -1);
}
int main(void)
{
int i = 0;
ArrayStack* Stack = NULL;
AS_CreateStack(&Stack, 10);
AS_Push(Stack, 3);
AS_Push(Stack, 37);
AS_Push(Stack, 11);
AS_Push(Stack, 12);
printf("Capacity:%d, Size:%d, Top:%d\n\n",
Stack->Capacity, AS_GetSize(Stack), AS_Top(Stack));
for (i = 0; i < 4; i++)
{
if (AS_IsEmpty(Stack))
break;
printf("Popped:%d", AS_Pop(Stack));
if (!AS_IsEmpty(Stack))
printf("Current Top:%d\n", AS_Top(Stack));
else
printf("Stack Is Empty\n");
}
AS_DestroyStack(Stack);
return 0;
}
2.3 링크드 리스트로 구현하는 스택
2.3.1 링크드 리스트 기반 스택과 스택의 노드 표현
- 링크드 리스트로 스택을 구현하려면 노드는 자신의 위에 위치하는 노드에 대한 포인터를 갖고 있어야 함
- 노드 구조체
typedef struct tagNode
{
char* Data;
struct tagNode* NextNode;
}Node;
1) char* Data;
- 힙에 할당된 문자열의 주소를 가리킴
- char* 형은 포인터이기 때문에 문자열이 저장된 주소만 담을 수 있음
2) struct tagNode* NextNode;
- 자기위에 쌓여있는 노드의 주소를 가리킴
- 링크드 리스트 스택의 구조체
typedef struct tagLinkedListStack
{
Node* List;
Node* Top;
}LinkedListStack;
1) Node* List;
- 데이터를 담는 링크드 리스트를 가리킴
- 힙에 존재하는 헤드 노드의 주소를 가리킴
2)Node* Top;
- 힙에 존재하는 테일 노드의 주소를 가리킴
- 스택의 입출력이 이루어지는 최상위 노드에 대한 포인터
2.3.2 링크드 리스트 기반 스택의 기본 연산
스택 생성 함수 LLS_CreateStack
- LinkedListStack 구조체를 힙에 할당
void LLS_CreateStack(LinkedListStack** Stack)
{
//스택을 힙에 생성
(*Stack) = (LinkedListStack*)malloc(LinkedListStack));
(*Stack)->List = NULL;
(*Stack)->Top = NULL;
}
스택 소멸 함수 LLS_DestroyStack
- 먼저 각 노드를 제거하고 힙에서 LinkedListStack 구조체를 할당 해제
void LLS_DestroyStack(LinkedListStack* Stack)
{
while(!LLS_IsEmpty(Stack))
{
Node*Propped = LLS_Pop(Stack);
LLS_DestroyNode(Popped);
}
//스택을 힙에서 해제
free(Stack);
}
노드 생성 함수 LLS_CreateNode
- 노드를 힙에 생성할 떄 문자열을 저장할 공간도 함께 생성해야 함
Node* LLS_CreateNode(char* NewData)
{
Node* NewNode = (Node*)malloc(sizeof(Node)); //Node 구조체 할당
NewNode->Data = (char*)malloc(strlen(NewData)+1); // Node 구조체의 Data 필드 할당
strcpy(NewNode->Data, NewData); //데이터를 저장
NewNode->NextNode = NULL; //다음 노드에 대한 포인터는 NULL로 초기화
return NewNode; // 노드의 주소를 반환
}
노드 소멸 함수 LLS_DestroyNode
- 노드를 메모리에서 제거
void LLS_DestroyNode(Node*_Node)
{
free(_Node->Data); //노드의 힙에서 Data 필드 할당 해제
free(_Node); //노드 할당 해제
}
노드 삽입 함수 LLS_Push
- 삽입 연산은 스택의 최상위 노드 Top에 새 노드를 얹도록 구현
- 새 노드가 최상위 노드가 됨
- 새로운 최상위 노드의 주소를 LinkedListStack 구조체의 Top 필드에 등록
void LLS_Push(LinkedListStack* Stack, Node* NewNode)
{
if(Stack->List == NULL)
{
Stack->List = NewNode;
}
else
{
//스택의 Top 위에 새 노드를 얹음
Stack->Top->NextNode = NewNode;
}
//스택의 Top 필드에 새 노드의 주소를 등록
Stack->Top = NewNode;
}
노드 제거 함수 LLS_Pop
- 현재 최상위 노드(Top)의 주소를 다른 포인터에 복사
- 새로운 최상위 노드 (현재 최상위 노드)의 바로 아래(이전)노드를 찾음
- LinkedListStack 구조체의 Top 필드에 새로운 최상위 노드의 주소를 등록
- 아까 저장했던 예전 최상위 노드의 주소를 반환
Node* LLS_Pop(LinkedListStack * Stack)
{
//LLS_Pop() 함수가 반환할 최상위 노드 저장
Node* TopNode = Stack->Top;
if(Stack->List == Stack->Top)
{
Stack->List = NULL;
Stack->Top = NULL:
}
else
{
//Top 아래에 있던 노드를 새로운 CurrentTop에 저장
Node* CurrentTop = Stack->List;
while( CurrentTop != NULL && CurrentTop->NextNode != Stack->Top)
{
CurrentTop = CurrentTop->NextNode;
}
//CurrentTop을 Top에 저장
Stack->Top = CurrentTop;
Stack->Top->NextNode = NULL;
}
return TopNode;
}
2.3.3 링크드 리스트 기반 스택 예제 프로그램
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//노드 구조체
typedef struct tagNode
{
char* Data; //힙에 할당된 문자열의 주소를 가리킴
struct tagNode* NextNode; //자기 위에 쌓여있는 노드의 주소를 가리킴
}Node;
//스택 구조체
typedef struct tagLinkedListStack
{
Node* List; //힙에 존재하는 헤드 노드의 주소를 가리킴
Node* Top; //힙에 존재하는 테일 노드의 주소를 가리킴
}LinkedListStack;
void LLS_CreateStack(LinkedListStack** Stack);
void LLS_DestroyStack(LinkedListStack* Stack);
Node* LLS_CreateNode(char* Data);
void LLS_DestroyNode(Node* _Node);
void LLS_Push(LinkedListStack* Stack, Node* NewNode);
Node* LLS_Pop(LinkedListStack* Stack);
Node* LLS_Top(LinkedListStack* Stack);
int LLS_GetSize(LinkedListStack* Stack);
int LLS_IsEmpty(LinkedListStack* Stack);
int main(void)
{
int i = 0;
int Count = 0;
Node* Popped;
LinkedListStack* Stack;
LLS_CreateStack(&Stack);
LLS_Push(Stack, LLS_CreateNode("abc"));
LLS_Push(Stack, LLS_CreateNode("def"));
LLS_Push(Stack, LLS_CreateNode("efg"));
LLS_Push(Stack, LLS_CreateNode("hij"));
Count = LLS_GetSize(Stack);
printf("Size:%d, Top:%s\n\n", Count, LLS_Top(Stack)->Data);
for (i = 0; i < Count; i++)
{
if (LLS_IsEmpty(Stack))
break;
Popped = LLS_Pop(Stack);
printf("Popped:%s", Popped->Data);
LLS_DestroyNode(Popped);
if (!LLS_IsEmpty(Stack))
{
printf("Current Top: %s\n", LLS_Top(Stack)->Data);
}
else
{
printf("Stack Is Empty\n");
}
}
LLS_DestroyStack(Stack);
return 0;
}
//스택 생성
void LLS_CreateStack(LinkedListStack * *Stack)
{
//스택을 힙에 생성
(*Stack) = (LinkedListStack*)malloc(sizeof(LinkedListStack));
(*Stack)->List = NULL;
(*Stack)->Top = NULL;
}
//스택 소멸
void LLS_DestroyStack(LinkedListStack * Stack)
{
//각 노드 제거
while (!LLS_IsEmtpy(Stack))
{
Node* Propped = LLS_Pop(Stack);
LLS_DestroyStack(Propped);
}
//스택을 힙에서 해제
free(Stack);
}
//노드 생성
Node* LLS_CreateNode(char* NewData)
{
//Node 구조체 메모리 할당
Node* NewNode = (Node*)malloc(sizeof(Node));
//Node 구조체의 Data 필드 메모리 할당
NewNode->Data = (char*)malloc(strlen(NewData) + 1);
strcpy_s(NewNode->Data,100, NewData); //데이터 저장
NewNode->NextNode = NULL; //다음 노드에 대한 포인터는 NULL로 초기화
return NewNode; //노드의 주소를 반환
}
//노드 소멸
void LLS_DestroyNode(Node * _Node)
{
free(_Node->Data);
free(_Node);
}
//노드 삽입
void LLS_Push(LinkedListStack * Stack, Node * NewNode)
{
if (Stack->List == NULL)
{
Stack->List = NewNode;
}
else
{
//스택의 Top에 신규 노드를 연결
Stack->Top->NextNode = NewNode;
}
//스택의 Top 필드에 새 노드의 주소를 등록
Stack->Top = NewNode;
}
//노드 제거
Node* LLS_Pop(LinkedListStack * Stack)
{
//LLS_Pop() 함수가 반환할 최상위 노드 저장
Node* TopNode = Stack->Top;
if (Stack->List == Stack->Top)
{
Stack->List = NULL;
Stack->Top = NULL;
}
else
{
//Top 아래에 있던 노드를 새로운 CurrentTop에 저장
Node* CurrentTop = Stack->List;
while (CurrentTop != NULL && CurrentTop->NextNode != Stack->Top)
{
CurrentTop = CurrentTop->NextNode;
}
//CurrentTop을 Top에 저장
Stack->Top = CurrentTop;
Stack->Top->NextNode = NULL;
}
return TopNode;
}
//최상위 노드 반환
Node* LLS_Top(LinkedListStack * Stack)
{
return Stack->Top;
}
//스택 사이즈
int LLS_GetSize(LinkedListStack * Stack)
{
int Count = 0;
Node* Current = Stack->List;
while (Current != NULL)
{
Current = Current->NextNode;
Count++;
}
return Count;
}
//스택이 비어있는지 확인
int LLS_IsEmpty(LinkedListStack * Stack)
{
return (Stack->List == NULL);
}
'C언어 > 알고리즘' 카테고리의 다른 글
[자료구조] ch 4 트리 (1) - 이진 트리 (2) | 2023.09.18 |
---|---|
[자료구조] ch 3. 큐 (0) | 2023.09.18 |
[자료구조] ch 2. 스택(2) - 사칙 연산 계산기 (0) | 2023.09.15 |
[자료구조] ch1 리스트 (2) (0) | 2023.09.11 |
[자료구조] ch 01 리스트 (1) (0) | 2023.09.11 |