스택과 큐는 배열에서 발전된 형태의 자료구조
스택(stack) = 쌓아 올린다는 것을 의미
스택 자료구조 = 차곡차곡 쌓아 올린 형태의 자료구조
스택의 특징인 후입선출(LIFO)을 활용하여 여러 분야에서 활용 가능하다.
-> 깊이 우선 탐색(DFS), 백트래킹 종류의 코딩 테스트에 효과적이며, 개념 자체가 재귀함수 알고리즘 원리와 일맥상통하기 때문이다.
스택의 후입선출이라는 독특한 성질이 종종 시간 복잡도를 줄이거나 특정한 문제의 조건을 손쉽게 해결하는 실마리가 될 떄가 있다.
문제에 접근할 때 혹시 스택을 이용하면 손쉽게 풀리지 않는지 한 번쯤 고민해보자!
자바에서 Stack은 java.util.Stack을 import하면 바로 사용할 수 있습니다.
Stack 선언
import java.util.Stack; //import
Stack<Integer> stack = new Stack<>(); //int형 스택 선언
Stack<String> stack = new Stack<>(); //char형 스택 선언
Stack 값 추가
Stack<Integer> stack = new Stack<>(); //int형 스택 선언
stack.push(1); // stack에 값 1 추가
stack.push(2); // stack에 값 2 추가
stack.push(3); // stack에 값 3 추가
Stack 값 삭제
Stack<Integer> stack = new Stack<>(); //int형 스택 선언
stack.push(1); // stack에 값 1 추가
stack.push(2); // stack에 값 2 추가
stack.push(3); // stack에 값 3 추가
stack.pop(); // stack에 값 제거
stack.clear(); // stack의 전체 값 제거 (초기화)
스택에서 값을 제거하고싶다면 pop()이라는 메서드를 사용하면 됩니다.
pop을 하면 가장 위쪽에 있는 원소의 값이 아래 그림과 같이 제거됩니다.
스택의 값을 모두 제거하고싶다면 clear()라는 메서드를 사용하면 됩니다.
Stack의 가장 상단의 값 출력
Stack<Integer> stack = new Stack<>(); //int형 스택 선언
stack.push(1); // stack에 값 1 추가
stack.push(2); // stack에 값 2 추가
stack.push(3); // stack에 값 3 추가
stack.peek(); // stack의 가장 상단의 값 출력
Stack의 기타 메서드
Stack<Integer> stack = new Stack<>(); //int형 스택 선언
stack.push(1); // stack에 값 1 추가
stack.push(2); // stack에 값 2 추가
stack.size(); // stack의 크기 출력 : 2
stack.empty(); // stack이 비어있는제 check (비어있다면 true)
stack.contains(1) // stack에 1이 있는지 check (있다면 true)
참고
https://coding-factory.tistory.com/601
Queue 의 사전적 의미
1. (무엇을 기다리는 사람, 자동차 등의) 줄 , 혹은 줄을 서서 기다리는 것
-> 꽉 찼을 때 : add = 예외 발생 / offer = false 리턴
큐는 주로 데이터가 입력된 시간 순서대로 처리해야 할 필요가 있는 상황에 이용한다.
-> BFS에서 자주 사용!
자바에서는 스택을 클래스로 구현하여 제공하지만 큐는 Queue인터페이스만 있고 별도의 클래스가 없다.
그래서 Queue인터페이스를 구현한 클래스들을 사용해야 한다
이 구조가 잘 이해가지 않는다면 디자인 패턴중 생성패턴을 공부해보길 바란다.
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
queue.offer(1);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(5);
while(!queue.isEmpty()) {
System.out.println(queue.poll());
}
}
}
큐 인터페이스를 이용하면 간단하게 구현할수 있다. 하지만 큐를 만들줄도 알아야 한다.
queuelsEmpty() : 큐 안이 비었는지 판단하는 함수
queueIsFull() : 배열의 최대크기를 벗어나면 안되기에 큐에 더이상 들어갈 공간이 없는지 판단하는 함수
size() : queue에 현재 들어가 있는 데이터의 개수를 return하는 함수
push(int value) : 큐 안에 데이터를 집어넣는 함수
peek() : 큐 안의 데이터들중 가장 먼저 나오는 데이터를 return 하는 함수
pop() : 큐 안의 데이터들 중 가장 먼저 나올수 있는 데이터를 return 하고 삭제하는 함수
public class ArrayQueue {
int MAX = 1000;
int front; //머리 쪽에 위치할 index값, pop할때 참조하는 index
int rear; //꼬리 쪽에 위치할 index값, push할때 참조하는 index
int [] queue;
public ArrayQueue() {
front = rear = 0; //초기값 0
queue = new int[MAX]; //배열 생성
}
public boolean queueisEmpty() { //queue에 아무것도 들어있지 않은지 판단하는 함수
return front == rear;
}
public boolean queueisFull() { //queue가 가득 차 공간이 없는지 판단하는 함수
if(rear == MAX-1) {
return true;
}else
return false;
}
public int size() { //queue에 현재 들어가 있는 데이터의 개수를 return
return front-rear;
}
public void push(int value) {
if(queueisFull()) {
System.out.println("Queue is Full");
return;
}
queue[rear++] = value; //rear가 위치한 곳에 값을 넣어주고 rear를 증가시킨다.
}
public int pop() {
if(queueisEmpty()) {
System.out.println("Queue is Empty");
return -1;
}
int popValue = queue[front++];
return popValue;
}
public int peek() {
if(queueisEmpty()) {
System.out.println("Queue is Empty");
return -1;
}
int popValue = queue[front];
return popValue;
}
}
배열로 구현한 Queue는 push pop이 간단하지만 배열의 크기가 유한해 큐의 크기가 다 차버리면 사용할수 없다.
그러면 LinkedList로 Queue를 구현해야 한다.
Node를 이용해서 Queue를 구현한다. 값이 들어갈 Node와 Queue를 관리할 NodeManager 클래스를 만들면 된다.
public class QueueNode {
int value; //값을 넣음
QueueNode queueNode; //다음 노드를 가리킴
public QueueNode(int value) {
this.value = value;
queueNode = null;
}
public int getValue() {
return value;
}
public QueueNode getNextNode() {
return queueNode;
}
public void setNextNode(QueueNode queueNode) {
this.queueNode = queueNode;
}
}
public class QueueNodeManager{ //큐의 기능을 만들 클래스
QueueNode front,rear;
public QueueNodeManager() {
front = rear = null;
}
public boolean queueisEmpty() {
if(front == null && rear == null) {
return true;
}else {
return false;
}
}
public void push(int value) {
QueueNode queueNode = new QueueNode(value);
if(queueisEmpty()) { //큐안에 데이터가 없으면 첫번째 Node에 front와 rear를 연결
front = rear = queueNode;
}else {
rear.setNextNode(queueNode); //큐 안에 데이터가 있으면 rear를 다음 노드에 연결 후 rear의 값을 마지막 노드로 삽입
rear = queueNode;
}
}
public QueueNode pop() {
if(queueisEmpty()) {
System.out.println("Queue is Empty");
return null;
}else {
QueueNode popNode = front;
front = front.getNextNode();
return popNode;
}
}
public QueueNode peek() {
if(queueisEmpty()) {
System.out.println("Queue is Empty");
return null;
}else {
return front;
}
}
public int size() {
QueueNode front2 = front;
QueueNode rear2 = rear;
int count = 0;
while(front2 != rear2 && rear2 !=null) { //큐가 비어있는 경우가 있을수도 있을때도 생각해야함
count++;
rear2 = rear2.getNextNode();
}
return count;
}
}
LinkedList로 Queue를 만들게 되면 규현은 복잡하지만 큐의 크기도 무한하고 Queue안에 원하는 기능을 넣을수도 있다는 장점이 있다.
값이 들어간 순서와 상관 없이 우선순위가 높은 데이터가 먼저 나오는 자료구조
큐 설정에 따라 front에 항상 최댓값 또는 최솟값이 위치
힙을 이용해 구현 (힙 = 트리 종류중 하나)