본문 바로가기
카테고리 없음

2주차 자료구조&알고리즘 스택&큐

by 소고기 굽는 개발자 2024. 3. 26.

스택(Stack)

개념

 - 스택은 데이터를 차곡차곡 쌓아올린 자료구조의 형태를 말함

 - 마지막에 들어온 데이터가 먼저 출력되는 구조를 가지고 있음

 

자료구조의 특징

 - LIFO(Last In Frist Out) 구조를 가짐

   * LIFO(Last In Frist Out)란?

     : 마지막에 입력한 데이터를 마지막에 출력하는 구조를 말함

 

큐(Queue)

개념

 - 큐는 먼저 들어온 것이 먼저 나가는 자료구조의 형태를 말함 

 - 처음에 들어온 데이터가 먼저 출력되는 구조를 가지고 있음

 

자료구조의 특징

 - FIFO(First In Frist Out) 구조를 가짐

   * FIFO(First In Frist Out)란?

     : 처음 입력한 데이터를 마지막에 출력하는 구조를 말함

 

출처(좀 더 자세한 사항은 아래 블로그를 확인)

https://pridiot.tistory.com/68

 

[Java] 스택(Stack)과 큐(Queue)

공부했던 자료 정리하는 용도입니다. 재배포, 수정하지 마세요. 스택(Stack)과 큐(Queue) Queue 는 먼저 들어간 데이터를 먼저 꺼내는 FIFO구조로 되어있고, Stack 은 LIFO구조로 되어있어서 마지막에 저

pridiot.tistory.com

 

내부 작동 원리

- Stack

  : Stack은 기본적으로 Vector를 상속받아 구현되며, Vector의 영향을 받아 Thread-Safe하다는 특징을 가지고 있음

   * Thread-Safe: 여러 Thread가 동시에 하나의 객체의 메서드를 호출하더라도 객체의 상태가 일관되게 유지되는 것

  : Stack은 Thread-Safe를 보장하기 위해 일부 메서드에 synchronized를 사용

   * synchronized: Thread-Safe를 달성하기위해 메서드를 동기적으로 실행하는 것 

class Stack<E> extends Vector<E>{
	...
    
}

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	
    ...
}

 

- Stack.push

  : 알고리즘에 적용되진 않았으나 기본적으로 Stack 클래스에 내장된 메서드

  : 배열에 push한 데이터를 저장한 뒤 입력된 값을 return

  : 쓰기 작업시 배열이 가득찼을 경우 확장된 새로운 배열을 만듦

class Stack<E> extends Vector<E>{
	...
    
	public E push(E item) {
        // Vector.addElement를 통해 요소를 추가
        addElement(item); 

        return item;
    }
    ...
}

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	
    // Vector의 요소를 저장하는 배열
    protected Object[] elementData;
    
    // Vector에 실제로 저장된 요소의 수
    protected int elementCount;
    
    // 요소를 Vector에 추가
    public synchronized void addElement(E obj) {
    	// 구조변경에 따른 증가(빠른 실패를 보장하기 위해 사용)
        modCount++;
        add(obj, elementData, elementCount);
    }
    
    // 요소를 Vector에 추가
    // return 값이 존재한다는 것을 제외하면 addElement과 동일
    public synchronized boolean add(E e) {
        modCount++;
        add(e, elementData, elementCount);
        return true;
    }
    
    private void add(E e, Object[] elementData, int s) {
        // 배열이 가득찼을 경우, 확장
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        elementCount = s + 1;
    }
    
    private Object[] grow() {
        return grow(elementCount + 1);
    }
    
    // 특정 용량으로 Vector의 크기를 증가
    private Object[] grow(int minCapacity) {
    	// 현재 용량을 가져옴
        int oldCapacity = elementData.length;
        
        // 새 용량을 계산
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                capacityIncrement > 0 ? capacityIncrement : oldCapacity
                                           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
}

 

- Queue

  : Queue는 Collection을 상속받는 Interface로서 LinkedList를 구현체로 하여 사용됨

    * LinkedList: Node(객체)들이 주소 포인터를 서로 가르키며 이어지는 구조임

public interface Queue<E> extends Collection<E> {
	...
}

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
...
    public LinkedList() {
    }
...
}

 

- Queue.add

  : 알고리즘에 사용된 알고리즘

  : Node 배열의 마지막에 입력한 데이터를 저장한 뒤 true를 return

public interface Queue<E> extends Collection<E> {
	...
    boolean add(E e);
    ...
}

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    // LinkedList의 크기를 저장하는 변수
    transient int size = 0;

    // 첫 번째 노드를 가리키는 포인터, 연결 리스트의 시작점
    transient Node<E> first;

    // 마지막 노드를 가리키는 포인터, 연결 리스트의 끝점
    transient Node<E> last;

    public LinkedList() {
    }

    
    public boolean add(E e) {
        // 주어진 요소를 리스트의 마지막에 추가
        linkLast(e);
        return true;
    }

    void linkLast(E e) {
        // 현재 마지막 노드를 임시로 저장합니다.
        final Node<E> l = last;
        // 현재의 마지막 노드(l)이고, 다음 노드는 null인 새 노드를 생성
        final Node<E> newNode = new Node<>(l, e, null);
        // 새 노드를 새로운 마지막 노드로 설정
        last = newNode;
        
        // 만약 리스트가 비어있었다면(즉, l이 null이었다면) 
        if (l == null)
            // 새 노드를 첫 번째 노드로 설정
            first = newNode;
        // 리스트가 비어있지 않았다면,
        else
        	//  이전 마지막 노드의 다음 노드로 새 노드를 설정
            l.next = newNode;
        // 리스트의 크기를 1 증가시킵니다.
        size++;
        
        // 구조변경에 따른 증가(빠른 실패를 보장하기 위해 사용)
        modCount++;
    }
}