Humility

아무리 노력해도 최고가 되지 못할 수 있다⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀그럼에도 노력하는자가 가장 겸손한 것 아닌가

공부하는 블로그

유니티/문법

[Unity] 유니티 C#) 자료구조 스택(Stack), 큐(Queue)

새벽_글쓴이 2024. 11. 2. 02:40
반응형

Stack

스택의 구조

스택은 가장 나중에 들어간 데이터가 가장 먼저 나오는 LIFO(Last In First Out) 선입선출 구조이다다.

실생활의 예로는 책을 쌓아둔 더미를 생각하면 된다

맨 위에 있는 책(마지막에 올려둔 책)을 가장 먼저 집을 수 있는 것 처럼


스택 선언 방법

접근지정자 Stack<타입> 변수명 = new Stack<타입>();

 private Stack<string> books = new Stack<string>();

스택의 주요 동작

  • Push: 새로운 데이터를 스택 맨 위에 추가
  • Pop: 스택 맨 위의 데이터를 제거하고 반환
  • Peek: 맨 위 데이터를 제거하지 않고 확인만 함
  • Count: 스택에 있는 데이터의 개수 확인
  • Contains: 스택에 특정 데이터가 있는지 확인
  • Clear: 스택의 모든 데이터를 제거

스택의 특징

  • 데이터는 한쪽 끝(top)에서만 삽입/삭제 가능
  • 중간에 있는 데이터에 직접 접근 불가
  • 순서가 중요한 작업에 적합

장점

  • 구현이 간단함
  • 데이터 저장/접근이 빠름
  • 메모리 관리가 효율적

단점

  • 크기가 제한적일 수 있음
  • 중간 데이터 접근이 어려움
  • 데이터 검색이 어려움

스택 사용 예시

// 문자열 뒤집기 예제
string text = "Hello";
Stack<char> charStack = new Stack<char>();

// 문자를 하나씩 스택에 넣기
foreach (char c in text)
{
    charStack.Push(c);
}

// 스택에서 꺼내면서 뒤집힌 문자열 만들기
string reversed = "";
while (charStack.Count > 0)
{
    reversed += charStack.Pop();
}

Console.WriteLine(reversed);  // "olleH" 출력

주의

Stack<int> emptyStack = new Stack<int>();
// emptyStack.Pop();  // 에러 발생!

// 안전한 방법
if (emptyStack.Count > 0)
{
    int value = emptyStack.Pop();
}

 

비어있는 스택에서 Pop이나 Peek을 하면 에러 발생


실제활용예시

무기 교체 시스템
Stack<Weapon> weapons = new Stack<Weapon>();

// 무기 줍기
weapons.Push(new Weapon("기본 검"));
weapons.Push(new Weapon("불의 검"));

// 'Q'키를 누르면 이전 무기로 교체
if (Input.GetKeyDown(KeyCode.Q))
{
    Weapon currentWeapon = weapons.Pop();  // 현재 무기는 스택 아래로
    weapons.Push(currentWeapon);  // 다시 스택 맨 위로
}

 

카드 게임의 덱 시스템
Stack<Card> cardDeck = new Stack<Card>();

// 덱 셋업
void SetupDeck()
{
    // 덱에 카드 추가
    cardDeck.Push(new Card("드래곤"));
    cardDeck.Push(new Card("마법사"));
    cardDeck.Push(new Card("전사"));
}

// 카드 뽑기
void DrawCard()
{
    if (cardDeck.Count > 0)
    {
        Card drawnCard = cardDeck.Pop();
        Debug.Log($"{drawnCard.Name} 카드를 뽑았습니다!");
    }
}

Queue

큐의 구조

첫 번째로 들어온 데이터가 첫 번째로 나가는 FIFO(First In First Out) 선입 선출 구조이다

실생활에서는 은행 창구 대기줄이나 티켓 예매 대기열 같은 것이 큐의 좋은 예시다.


큐 선언 방법

접근지정자 Queue<타입변수명 = new Queue<타입>();

private Queue<string> queue = new Queue<string>();

큐 주요 동작큐 특징

  • Enqueue: 새로운 데이터를 큐의 뒤쪽에 추가
  • Dequeue: 큐의 앞쪽에서 데이터를 제거하고 반환
  • Peek: 첫 번째 데이터를 제거하지 않고 확인만 함
  • Count: 큐에 있는 데이터의 개수 확인
  • Clear: 큐의 모든 데이터 제거
  • Contains: 큐에 특정 데이터가 있는지 확인

장점

  • 순서가 보장됨 - 먼저 들어온 데이터가 먼저 처리됨 (FIFO)
  • 데이터를 임시로 저장하고 순서대로 처리하기에 적합
  • 멀티 스레드 환경에서 데이터를 안전하게 전달하는데 유용

단점

  • 중간에 있는 데이터에 직접 접근 불가
  • 데이터 검색이 어려움 (순차적으로 찾아야 함)
  • 크기가 고정되어 있는 경우, 메모리 낭비 또는 큐가 가득 찰 수 있음
  • 이미 제거한 데이터는 다시 접근할 수 없음

큐 사용 예시

Queue<GameEvent> eventQueue = new Queue<GameEvent>();

// 이벤트 등록
eventQueue.Enqueue(new GameEvent("PlayerJump"));
eventQueue.Enqueue(new GameEvent("EnemySpawn"));
eventQueue.Enqueue(new GameEvent("CollectItem"));

// 이벤트 처리
while (eventQueue.Count > 0)
{
    GameEvent currentEvent = eventQueue.Dequeue();
    ProcessEvent(currentEvent);
}

주의

Queue<int> emptyQueue = new Queue<int>();
// emptyQueue.Dequeue();  // 에러 발생!

// 안전한 방법
if (emptyQueue.Count > 0)
{
    int value = emptyQueue.Dequeue();
}

 

비어있는 큐에서 Dequeue나 Peek 시도시 에러 발생


실제활용예시

턴제 RPG 전투 시스템
Queue<Character> turnQueue = new Queue<Character>();

// 턴 순서 설정
void SetupBattleTurns()
{
    // 캐릭터들의 속도에 따라 턴 순서 정하기
    turnQueue.Enqueue(new Character("플레이어", 10));
    turnQueue.Enqueue(new Character("몬스터1", 8));
    turnQueue.Enqueue(new Character("몬스터2", 5));
}

// 턴 진행
void ProcessTurn()
{
    if (turnQueue.Count > 0)
    {
        Character current = turnQueue.Dequeue();
        current.TakeTurn();
        // 턴을 마친 캐릭터는 다시 큐의 끝으로
        turnQueue.Enqueue(current);
    }
}

 

게임 사운드 시스템
Queue<AudioClip> soundQueue = new Queue<AudioClip>();

// 효과음 예약
void AddSound(AudioClip clip)
{
    soundQueue.Enqueue(clip);
}

// 사운드 재생 처리
void Update()
{
    if (soundQueue.Count > 0 && !audioSource.isPlaying)
    {
        AudioClip nextSound = soundQueue.Dequeue();
        audioSource.PlayOneShot(nextSound);
    }
}
반응형