본문 바로가기

코딩 및 기타/어서와! 자료구조와 알고리즘

13 일차

728x90

환형 큐

∙ 배열을 이용한 큐의 구현

- 크기가 8인 배열에 3개의 데이터가 추가된 큐의 상태

 

 ∙ 배열을 이용하여 구현한 큐의 문제점

- 큐에 데이터의 삽입과 삭제가 지속적으로 발생할 경우, 배열의 앞부분에 무효화되는 공간이 늘어남

 

∙ 환형 큐(circular queue)

- 선입 선출(FIFO) 원칙에 따라 작업이 수행되고, 마지막 위치가 마치 원을 이루듯이 다시 첫 번째 위치에 연결되는 선형 데이터 구조

- 큐의 front와 rear 값이 시계 방향으로 한 칸씩 이동하는 형태

> 실제 구현에서는 배열 전체 크기를 이용하여 나머지(%) 연산을 수행

- 큐가 empty 또는 full 상태인지를 확인하기 위해 원소의 개수를 따로 저장해야 함

 

 

∙ 환형 큐에서 데이터 추가

rear = (rear + 1) % capacity; // 전체 배열 크기의 나머지 
arr[rear] = e;
count++;

 

 ∙환형 큐에서 데이터 삭제

front = (front + 1) % capacity;
count--;

 

∙ 환형 큐 클래스 소스코드

#include <iostream>

#define MAX_QUEUE 10

template <typename T>
class CircularQueue
{
public:
	CircularQueue(int sz = MAX_QUEUE)
	{
		arr = new T[sz];
		front_idx = 0;
		rear_idx = -1;
		count = 0;
		capacity = sz;
	}

	~CircularQueue()
	{
		delete [] arr;
	}

	void enqueue(const T& e)
	{
		if (full()) {
			std::cout << "Overflow error!" << std::endl;
			return;
		}

		rear_idx = (rear_idx + 1) % capacity;
		arr[rear_idx] = e;
		count++;
	}

	void dequeue()
	{
		if (empty()) {
			std::cout << "Underflow error!" << std::endl;
			return;
		}

		front_idx = (front_idx + 1) % capacity;
		count--;
	}

	const T& front() const { return arr[front_idx]; }

	bool empty() const { return count == 0; }
	int full() const { return count == capacity; }
	int size() const { return count; }

private:
	T* arr;
	int front_idx;
	int rear_idx;
	int count;
	int capacity;
};

using namespace std;

int main()
{
	CircularQueue<int> q(5);

	q.enqueue(10);
	q.enqueue(20);
	q.enqueue(30);
	q.enqueue(40);
	q.enqueue(50);  // full 상태
	q.dequeue();
	q.dequeue();    // 원소 2개 삭제
	q.enqueue(60);
	q.enqueue(70);  // full 상태
	q.enqueue(80);  // 오버플로우 에러!

	while (!q.empty()) {
		auto& e = q.front();
		cout << e << ", ";
		q.dequeue();
	}
	cout << endl;
}

 

양방향 큐

∙ 양방향 큐

- deque [dek] [덱] Double Ended QUEue

- 삽입과 삭제가 양쪽 끝에서 모두 가능한 자료 구조

 

- 양방향 큐의 주요 연산

> push_front(e) : 양방향 큐의 맨 앞에서 원소 e를 추가

> pop_front() : 양방향 큐의 맨 앞에 있는 원소를 삭제

 

> push_back(e) : 양방향 큐의 맨 뒤에 원소 e를 추가

> pop_back() : 양방향 큐의 맨 뒤에 있는 원소를 삭제

 

> front() : 양방향 큐의 맨 앞에 있는 원소를 참조

> back() : 양방향 큐의 맨 뒤에 있는 원소를 참조

 

> empty() : 양방향 큐가 비어 있으면 true를 반환

> size() : 양방향 큐의 원소 개수를

 

∙ 양방향 큐의 구현

- 배열을 이용한 양방향 큐의 구현

> 환형 큐와 비슷한 방식으로 양방향 큐의 맨 앞과 맨 마지막 위치를 갱신

 

- 연결 리스트를 이용한 양방향 큐의 구현

> 이중 연결 리스트를 이용하여 연결 리스트의 맨 앞과 맨 뒤에 자료를 각각 추가 & 삭제 가능

 

 

∙ std::deque

- 양방향 큐의 동작을 지원하는 순차 컨터이너

- <deque>에 정의되어 있음

- deque의 특징

> 원소에 대해 임의 접근 동작이 O(1) 시간 복잡도로 동작

> 맨 앞과 맨 뒤에 자료를 추가 또는 삭제하는 연산이 O(1) 시간 복잡도로 동작

> 중간 위치에서 자료를 삽입 또는 삭제는 O(n) 시간 복잡도로 동작(n은 원소 개수)

> stack, queue 등의 STL 컨테이너 어댑터의 기본 컨테이너로 사용

 

∙ std::deque의 구현 방식

- C++ 표준은 어떤 기능의 동작만을 정의할 뿐이며, 어떻게 구현해야 하는지 정의하지 않음

- 보통 std::deque은 단인 메모리 청크를 사용하지 않으며, 대신 크기가 같은 여러 개의 메모리 청크를 사용하여 데이터를 저장함

 

∙ 소스 코드

#include <iostream>
#include <deque>

using namespace std;

int main()
{
    // deque 생성
    deque<int> d = {10, 20, 30, 40};
    
    // 데이터 추가
    d.push_front(50); // 50, 10, 20, 30, 40
    d.push_back(60); // 50, 10, 20, 30, 40, 60
    
    // 데이터 출력 1
    for (int i = 0; i < d.size(); i++){
        cout << d[i] << ",";
    }
    cout << endl;
    
    // 데이터 출력 2
    for (auto i : d){
    	cout << i << ", ";
    }
    cout << endl;
}

 

'코딩 및 기타 > 어서와! 자료구조와 알고리즘' 카테고리의 다른 글

재귀 알고리즘 주요 예제  (0) 2023.08.22
14 일차  (0) 2023.08.22
12일차  (0) 2023.08.20
11일차  (0) 2023.08.17
10일차  (0) 2023.08.16