자료 구조의 종류
순차 리스트
: 각 자료가 메모리 중에서 서로 이웃해 있는 구조
: 선형 구조라고도 함
: 메모리에 연속되어 저장
: 배열 / 행렬 / 레코드 / 스택 / 큐 / 데크
배열
: 원소들의 크기와 자료형 같음
: 배열의 원소는 메모리 내에서 연속적으로 순서대로 저장
: 각 배열의 원소는 인덱스로 구별
레코드
: 서로 다른 데이터 형태와 크기로 구성되어 있는 데이터 구조 (이질적 구조)
: ex) 어떤 사람에 관한 자료들인 이름, 주민등록번호, 주소 등과 같이 서로 관계있는 여러 필드를 하나로 묶어서 구성
: 배열은 데이터 원소의 크기와 데이터 형태 동일하지만, 레코드는 데이터 원소의 크기와 데이터 형태 서로 다름
스택 (stack)
: 쌓아 올린 더미를 의미
: 선형 리스트 구조의 특별한 형태
: 리스트 내의 데이터 삽입과 삭제가 Top이라고 하는 한 쪽 끝에서만 일어나는 데이터 구조
: 데이터 구조에서의 스택이란, 기억 장치에 데이터를 일시적으로 쌓아 두었다가 필요할 때 꺼내서 사용할 수 있는 주기억장치(메모리)나 레지스터의 일부를 할당하여 사용하는 임시적인 기억을 의미
: 가장 마지막에 삽입한 데이터를 제일 먼저 삭제
: 후입선출 (LIFO : Last-In First Out) 리스트라고도 불림
Top
: 스택에 대한 삽입과 삭제가 이루어지는 위치
: 가장 최근에 입력된 항목의 위치를 가리키는 포인터
: 출력 우선 순위가 가장 높은 원소를 가리키는 포인터
: 초기에 TOP은 0부터 시작
: 배열로 스택 구현 시, 배열 첨자가 0부터 시작하므로 Top을 -1로 초기화
Bottom
: Top의 반대쪽
: 노드의 입출력 불가
PUSH
: 삽입 연산
: 스택에 새로운 노드 입력
: Top = Top + 1
Pop
: 삭제 연산
: 스택에서 노드 삭제
: Top = Top - 1
오버플로우 (Overflow)
: 스택이 가득차 있을 때 (TOP == n) 새로운 노드 삽입 시, 스택에 더 이상 삽입을 할 수 없는 경우
: Top > n이 되는 경우
: 스택에 노드 삽입 전에 오버플로우가 발생하는지 확인해야 함 (isFull())
언더플로우(Underflow)
: 스택이 비어있을 때 (TOP == 0) 노드의 삭제 시, 더 이상 삭제를 할 수 없는 경우
: TOP < 0이 되는 경우
: 스택에서 노드 삭제 전에 언더플로우가 발생하는지 확인해야 함 (isEmpty())
스택의 이용
: 서브 프로그램의 복귀 주소( return address) 보관
: 산술식 표현 : a + b
: Java 프로그램에서 메서드 호출 및 실행 시 스택 사용
스택 구현 방법
(1) 배열 사용
package dataStructure;
public class MyStack {
private int stackSize;
private int top;
private char[] stackArr;
// 생성자 : 스택 초기화
// 배열 인덱스는 0부터 시작하므로 top -1로 초기화
public MyStack (int stackSize) {
top = -1;
this.stackSize = stackSize;
stackArr = new char[this.stackSize];
}
// 스택이 비었는지 확인하는 메서드
public boolean isEmpty() {
return top==-1; //top이 -1이면 true 아니면 false
}
// 스택이 가득 차있는지 확인한느 메서드
public boolean isFull() {
return top==stackSize-1; // 스택이 가득 차있으면 true 아니면 false
}
// 스택에 데이터 삽입하는 메서드
// 삽입 전 오버플로우 발생하는지 확인 -> full이 아니면 top을 하나 증가한 위치에 값 삽입
public void push(char item) {
if (isFull()) System.out.println("Stack is Full. Overflow");
else stackArr[++top] = item;
}
// 삭제 전 언더플로우 발생하는지 확인
public char pop() {
if (isEmpty()) {
System.out.println("Stack is emptry. Underflow");
return 'E'; //형식적으로 반환
}
else return stackArr[top--];
}
public char peek() {
if(isEmpty()) {
System.out.println("Stack Empty");
return 'E';
}
else return stackArr[top];
}
// 스택 내용을 비우는 clear()
public void clear() {
top = -1;
}
// 스택의 전체 데이터를 출력하는 showStack()
// 출력만 하고 반환값 없음
// 출력 전 스택 비었는지 왁인
// 스택에 있는 모든 요소의 값 출력
public void showStack() {
if(isEmpty()) System.out.println("Stack Empty");
else {
System.out.print("Stack items : ");
for (int i=0; i<=top; i++) {
System.out.print(i + ":" + stackArr[i] + " ");
}
System.out.println("\ntop : " + top);
}
}
}
(2) java.util.Stack 클래스 이용
import java.util.Stack;
Stack<String> stk = new Stack<String>();
큐 (queue)
: 선형 리스트 구조의 특별한 형태
: 기억장소의 한쪽 끝에서는 데이터의 입력이, 다른 한쪽에서는 데이터의 삭제가 이루어지는 구조
: 먼저 삽입한 데이터가 먼저 삭제 -> 선입선출 (FIFO : First-In First-Out) 구조
: 전위 포인터 (front) - 데이터가 삭제되는 끝을 가리키는 포인터
: 후위 포인터 (rear) - 데이터가 삽입되는 끝을 가리키는 포인터
큐에서의 오버플로우 해결 방법
: 후위 포인터 rear의 값이 n과 같을 때 새로운 데이터를 삽입하면 오버플로우 발생
(1) 이동 큐 (moving queue)
: 앞 부분이 비어 있는 경우 큐의 데이터를 앞 부분으로 이동
: 데이터 이동에 따른 시간적 부담 문제
(2) 원형 큐 (circular queue)
: 선형 큐의 앞(front)과 뒤(rear)를 이어 놓은 형태의 큐
: 새로운 항목을 삽입하기 위하여 rear를 시계방향으로 증가
큐의 응용
- 작업 도착 순서에 의한 스케줄링
- 대표적인 예 : 프린터
- 작업 순서
- front=0 작업 A > 작업 B -> 작업 C -> 작업 D rear=4
큐 예제
(1) 앞이 비었는데도 오버플로우 발생 : 이동 없음
public class MyQueue {
// 멤버 변수
private int queueSize; // 큐의 용량
private int front; // 전위 포인터. 첫 번째 요소 앞
private int rear; // 후위 포인터. 마지막 요소 값과 동일
private int num; //현재 데이터 수
private char[] queue; // 큐 본체 (변수 선언만. 아직 할당 못 받았음)
// 생성자에서 초기화
public MyQueue(int queueSize) {
// 배열 사용하므로 초기값 -1로 설정
front = rear = -1; // 큐가 비어 있는 상태
num = 0; // 데이터 수
this.queueSize = queueSize; // 큐 크기 설정
queue = new char[queueSize]; // 큐 생성
}
// 큐가 비어있는 상태 확인하는 isEmpty()
// true/false
public boolean isEmpty() {
// front와 rear 포인터가 같으면 큐가 비어있는 상태
// 데이터가 없으므로 포인터를 모두 -1로 초기화
// 배열 사용하므로 첫 번째가 0, 첫 번째 이전이 -1
if(front == rear) {
front = rear = -1;
}
// front와 rear 포인터가 같은 경우 true, 아니면 false return
return front == rear;
}
// 큐가 가득 차있는 상태 확인하는 isFull()
public boolean isFull() {
// rear 포인터가 큐의 마지막 인덱스와 동일하면 full 상태
return rear == queueSize - 1;
}
// 큐에 데이터 삽입하는 enqueue()
// (1) Full인지 확인
// (2) 데이터 삽입
public void enqueue(char item) {
if(isFull()) {
System.out.println("Queue Full!");
} else {
queue[++rear] = item; // rear 다음 위치에 데이터 삽입
num++; // 데이터 수 증가
}
}
// 큐에서 데이터 삭제하는 dequeue()
// (1) Empty인지 확인
// (2) 데이터 삭제 (삭제한 데이터 반환)
public char dequeue() {
if(isEmpty()) {
return 'E';
} else {
num--; // 데이터 수 감소
front++; // front 증가 (front 다음 위치 값 삭제)
return queue[front];
}
}
// 큐의 첫 번째 데이터 추출하는 peek()
// 추출해서 반환
public char peek() {
if(isEmpty()) {
System.out.println("peek 실패. Empty");
return 'E';
} else {
// front 다음 데이터 추출해서 반환
return queue[front + 1];
}
}
// 큐 초기화하는 clear()
public void clear() {
front = rear = -1;
System.out.println("clear!");
}
// 큐에 들어있는 모든 데이터를 출력하는 showQueue()
// (1) 큐가 비었는지 확인
// (2) 큐에 있는 모든 데이터 출력
public void showQueue() {
if(isEmpty()) {
System.out.println("Queue Empty");
} else {
System.out.print("Queue items : ");
//front + 1 위치부터 rear까지 출력
for(int i=front+1; i<=rear; i++) {
System.out.print(i + ":" + queue[i] + " ");
}
}
}
// 데이터 개수를 반환하는 numOfData()
public int numOfData() {
return num;
}
}
(2) 큐 이동해서 오버플로우 해결
public class MyQueueMove {
// 멤버 변수
private int queueSize; // 큐의 용량
private int front; // 전위 포인터. 첫 번째 요소 앞
private int rear; // 후위 포인터. 마지막 요소 값과 동일
private int num; //현재 데이터 수
private char[] queue; // 큐 본체 (변수 선언만. 아직 할당 못 받았음)
// 생성자에서 초기화
public MyQueueMove(int queueSize) {
// 배열 사용하므로 초기값 -1로 설정
front = rear = -1; // 큐가 비어 있는 상태
num = 0; // 데이터 수
this.queueSize = queueSize; // 큐 크기 설정
queue = new char[queueSize]; // 큐 생성
}
// 큐가 비어있는 상태 확인하는 isEmpty()
// true/false
public boolean isEmpty() {
// front와 rear 포인터가 같으면 큐가 비어있는 상태
// 데이터가 없으므로 포인터를 모두 -1로 초기화
// 배열 사용하므로 첫 번째가 0, 첫 번째 이전이 -1
if(front == rear) {
front = rear = -1;
}
// front와 rear 포인터가 같은 경우 true, 아니면 false return
return front == rear;
}
// 큐가 가득 차있는 상태 확인하는 isFull()
public boolean isFull() {
// rear 포인터가 큐의 마지막 인덱스와 동일하고
// 데이터 수가 queueSize와 동일하면 full 상태
return (rear == queueSize -1 && num == queueSize);
}
// 큐에 데이터 삽입하는 enqueue()
// (1) Full인지 확인
// (2) 데이터 삽입
public void enqueue(char item) {
if(isFull()) {
System.out.println("Queue Full!");
} else if(num != 0 && rear == queueSize -1){
// rear가 마지막 인덱스와 동일하지만
// 데이터가 1개라도 들어 있는 경우
// queue 이동 : 현재 queue를 복사해서
// 시작 인덱스 0으로 덮어 씀
//System.arraycopy(소스, 시작인덱스, 대상, 시작인덱스, 길이);
System.arraycopy(queue, front+1, queue, 0, queue.length - 1);
System.out.println("큐 이동 발생");
rear--;
front = -1;
queue[++rear] = item; // rear 다음 위치에 데이터 삽입
num++;
}else {
queue[++rear] = item; // rear 다음 위치에 데이터 삽입
num++; // 데이터 수 증가
}
}
// 큐에서 데이터 삭제하는 dequeue()
// (1) Empty인지 확인
// (2) 데이터 삭제 (삭제한 데이터 반환)
public char dequeue() {
if(isEmpty()) {
return 'E';
} else {
num--; // 데이터 수 감소
front++; // front 증가 (front 다음 위치 값 삭제)
return queue[front];
}
}
// 큐의 첫 번째 데이터 추출하는 peek()
// 추출해서 반환
public char peek() {
if(isEmpty()) {
System.out.println("peek 실패. Empty");
return 'E';
} else {
// front 다음 데이터 추출해서 반환
return queue[front + 1];
}
}
// 큐 초기화하는 clear()
public void clear() {
front = rear = -1;
System.out.println("clear!");
}
// 큐에 들어있는 모든 데이터를 출력하는 showQueue()
// (1) 큐가 비었는지 확인
// (2) 큐에 있는 모든 데이터 출력
public void showQueue() {
if(isEmpty()) {
System.out.println("Queue Empty");
} else {
System.out.print("Queue items : ");
//front + 1 위치부터 rear까지 출력
for(int i=front+1; i<=rear; i++) {
System.out.print(i + ":" + queue[i] + " ");
}
}
}
// 데이터 개수를 반환하는 numOfData()
public int numOfData() {
return num;
}
}
(3) java.util.Queue 인터페이스를 LinkedList로 구현 (ArrayList로 구현할 경우 추가/삭제 시 데이터 이동이 필요)
import java.util.LinkedList;
import java.util.Queue;
Queue<String> q = new LinkedList<String>();
데크 (deque : double ended queue)
: 삽입과 삭제가 양끝에서 이루어지는 구조
: 스택과 큐를 하나의 선형 리스크 구조에 복합시킨 형태
: end1과 end2 포인터 사용
: 양쪽 끝에서의 오버플로우의 가능성을 줄이기 위해 데크 내의 중심 근처에부터 저장 가능
데크 구현
java.util.Deque 인터페이스를 ArrayDeque로 구현
import java.util.ArrayDeque;
import java.util.Deque;
Deque<String> dq = new ArrayDeque<String>();
'Algorithm > 자료구조' 카테고리의 다른 글
Vector (0) | 2021.11.30 |
---|---|
List 인터페이스 - ArrayList (0) | 2021.11.30 |
리스트 (선형 리스트 / 단일 연결 리스트) (0) | 2021.11.29 |
이진 탐색 트리 (Binary Search Tree) (0) | 2021.11.26 |
Tree (0) | 2021.11.25 |