2025-01-03 TIL

알고리즘 문제

문제를 풀기 전에 질문 갯수를 봤는데 다른 문제들보다 훨씬 많아서 걱정했는데 문제가 어렵진 않았습니다.
프로그래머스 제일 작은 수 제거하기

문제가 간단해 쉽게 풀었습니다.
프로그래머스 가운데 글자 가져오기

홀짝을 구분하는 문제라 어렵지 않아 쉽게 풀었습니다.
프로그래머스 수박수박수박수박수박수?

스파르타 내일배움 부트캠프

벡터 구현 과제

이번주에 특정 기능을 하는 프로그램을 만드는 과제가 주어졌습니다.

첫 문제의 내용은 다음과 같습니다.

필수 기능 가이드

  • 클래스의 이름은 SimpleVector라고 합니다.
  • 타입에 의존하지 않고 데이터를 받을수 있는 배열을 멤버변수로 갖습니다.
  • 생성자는 아래와 같이 구현 합니다.
    • 기본 생성자는 크기가 10인 배열을 만듭니다.
    • 숫자를 하나 받는 생성자는 해당 숫자에 해당되는 크기의 배열을 만듭니다.
  • 아래와 같은 멤버함수를 구현 합니다.
    • push_back 인자로 받은 원소를 맨 뒤에 추가 합니다. 반환값은 없습니다. 배열의 크기가 꽉 찼는데 원소가 더 들어올경우 아무 동작도 하지 않습니다.
    • pop_back은 벡터의 마지막 원소를 제거 합니다. 만약 제거할 원소가 없다면 아무 동작도 하지 않으며, 인자 및 반환값은 없습니다.
    • size는 인자가 없고 현재 원소의 개수를 반환합니다.
    • capacity 현재 내부 배열의 크기를 반환합니다.

도전 기능 가이드

  • 복사 생성자를 구현 합니다.
  • 아래 멤버함수를 추가로 변경/구현 합니다.
    • push_back에서 배열의 크기가 꽉 찼는데 원소가 더 들어올경우, 기존 배열보다 크기를 5만큼 더 늘리고 새로운 원소까지 추가됩니다.(기존에 있던 값도 유지되야 합니다.)
    • resize는 정수 하나를 인자로 받습니다. 해당 정수가 현재 배열의 크기보다 작으면 아무 동작도 하지 않습니다. 만약 현재 배열보다 크기가 크면 해당 값만큼 크기를 재할당 합니다.(기존 원소는 그대로 있어야 합니다.)
    • sortData는 내부 데이터를 정렬하는 함수 입니다. 직접 정렬하지 않고 STL의 sort함수를 활용해서 정렬 합니다.

필수

  • 배열의 맨 끝에 원소를 삽입/삭제 하는 기능이 요구사항에 맞게 동작하는지 확인
  • 템플릿을 적용한 경우와 적용하지 않은 경우를 비교해서 설명할 수 있는지 확인
  • 요구사항에 있는 생성자 구현시, 다수의 생성자를 만들지 않고, 기본인자를 활용해서 하나의 생성자로 처리할 수 있는지(처리했는지) 확인

도전

  • 배열 크기 확장 및 정렬 로직이 요구사항에 맞게 동작하는지 확인
  • 원소 추가시 메모리를 재 할당하는 경우에 어떤 방식으로 코드가 동작하는지 그림을 그려서 설명할 수 있는지 확인
  • 메모리를 재 할당해야 하는 경우에 예외가 발생하지 않고 정상적으로 동작하는지 확인
  • 소멸자에서 동적할당한 메모리가 제대로 해지되고 있는지 확인

아래 코드는 제가 작성한 코드입니다.

#include <iostream>
#include <algorithm>

template <typename T>
class SimpleVector
{
private:
	T* startPoint;
	int currentSize;
	int currentCapacity;

	void reSize(int newCapacity)
	{
		T* newData = new T[newCapacity];
		std::copy(startPoint, startPoint + currentSize, newData);
		delete[] startPoint;
		startPoint = newData;
		currentCapacity = newCapacity;

		std::cout << "배열 크기 " << newCapacity << "로확장" << std::endl;
	}

public:
	// 기본 인자를 사용해서 하나의 생성자를 사용
	SimpleVector(int size = 10) : startPoint(new T[size]), currentSize(0), currentCapacity(size) {}

	// 복사 생성자
	SimpleVector(const SimpleVector& other) : startPoint(nullptr), currentSize(other.currentSize), currentCapacity(other.currentCapacity)
	{
		this->startPoint = new T[other.currentCapacity];
		std::copy(other.startPoint, other.startPoint + other.currentSize, this->startPoint);
	}

	// 소멸자
	~SimpleVector()
	{
		// 동적 배열 메모리 해제
		delete[] startPoint;
	}

	void push_back(const T& value)
	{
		// 배열이 가득 찼는지 확인
		if (currentSize == currentCapacity)
		{
			int newCapacity = currentCapacity + 5;
			reSize(newCapacity);
		}

		// 배열의 마지막에 값 대입 및 사이즈 변경
		startPoint[currentSize++] = value;

		// 대입된 값 출력
		std::cout << "push_back : " << startPoint[currentSize - 1] << std::endl;
	}

	void pop_back()
	{
		// 배열에 값이 있는지 확인
		if (currentSize <= 0)
		{
			return;
		}

		T deleteValue = startPoint[currentSize - 1];

		// 배열의 크기 감소
		--currentSize;

		// 삭제된 값 출력
		std::cout << "pop_back : " << deleteValue << std::endl;
	}

	int size() const { return currentSize; }

	int capacity() const { return currentCapacity; }

	// 정렬 및 출력
	void sortData()
	{
		std::sort(startPoint, startPoint + currentSize);

		for (int i = 0; i < currentSize; ++i)
		{
			std::cout << "정렬 " << i << "번째: " << *(startPoint + i) << std::endl;
		}
	}
};

int main()
{
	SimpleVector<int>test;

	for (int i = 0; i < 7; ++i)
	{
		test.push_back(i);
		test.push_back(i * 2.5);
	}

	SimpleVector<int>test2(test);

	test2.pop_back();

	std::cout << "배열 사이즈: " << test2.size() << std::endl;
	std::cout << "배열 용량: " << test2.capacity() << std::endl;

	test2.sortData();
}

도서관 구현 과제

이번주 중으로 제출하는 또 다른 문제입니다.

제공된 간단한 도서관 관리 프로그램 코드를 분석 제공된 코드를 활용해서 간단한 검색 기능을 추가한다 (간단한 반복문 정도 구현 수준) 대여 기능(도전 기능)을 추가한다.(아예 새로운 기능을 구현하는 수준)

필수 기능 가이드

  • 책 제목으로 검색이 가능해야 합니다.
  • 작가로 검색이 가능해야 합니다.
  • 책 제목이 동일한 경우는 없다고 가정 합니다.

도전 기능 가이드

기존 도서관 관리 프로그램에 필수 기능을 추가한 상태에서 아래 대여 기능을 추가해주세요

  • 특정 책의 대여 여부를 알 수 있어야 합니다.
  • 책의 이름으로 대여 여부를 검색하고 대여가 아닌 경우 대여할 수 있어야 합니다.
  • 책의 작가로 대여 여부를 검색하고 대여가 아닌 경우 대여할 수 있어야 합니다.
  • 책을 반납할 수 있어야 합니다.
  • 모든 책의 재고는 난이도를 낮추기 위해 3권으로 통일 합니다.

필수

  • 책 제목으로 검색 시 정확히 결과가 반환되는지 확인
  • 작가로 검색 시 결과가 올바르게 표시되는지 확인.
  • 객체지향적 설계 원칙(클래스와 메서드 활용)을 얼마나 잘 이해했는지
  • 검색 기능의 논리적 흐름과 데이터 접근 방식이 적절한지.
  • 코드의 가독성, 함수 분리 여부, 중복 코드 최소화 여부.
  • 검색 기능에서 입력 검증 처리와 에러 처리 여부.

도전

  • 책 대여 여부를 정확히 조회할 수 있는지 확인
  • 책 제목 및 작가로 대여 시 재고 감소가 올바르게 반영되는지 확인
  • 반납 기능이 재고를 정상적으로 복구하는지 확인
  • 객체지향적 설계와 책임 분리의 원칙을 준수했는지
  • 재고 관리와 대여/반납 로직이 논리적이고 일관성 있는지
  • 대여 및 반납 기능에서 에러 처리 및 사용자 친화적 메시지 출력 여부
  • 재고 관리를 효율적으로 구현했는지(중복 코드 최소화).
  • 프로그램 확장 가능성을 고려했는지(예: 다른 재고 정책 추가 가능성)

아래 코드는 제가 작성한 코드입니다.

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>

class Book
{
private:
	std::string title;
	std::string author;

public:
	Book(const std::string& bookTitle, const std::string& bookAuthor) : title(bookTitle), author(bookAuthor) {}

    std::string getTitle() const { return title; }

    std::string getAuthor() const { return author; }
};

class BookManager
{
private:
	std::vector<Book> books; // 책 목록 저장

public:
    // 책 추가 메서드
	void addBook(const std::string& bookTitle, const std::string& bookAuthor)
	{
		books.push_back(Book(bookTitle, bookAuthor)); // push_back 사용
		std::cout << "책이 추가되었습니다: " << bookTitle << " by " << bookAuthor << std::endl;
	}

    // 모든 책 출력 메서드
    void displayAllBooks() const
    {
        if (books.empty())
        {
            std::cout << "현재 등록된 책이 없습니다." << std::endl;
            return;
        }

        std::cout << "현재 도서 목록:" << std::endl;
        for (size_t i = 0; i < books.size(); ++i) // 일반적인 for문 사용
        {
            std::cout << "- " << books[i].getTitle() << " by " << books[i].getAuthor() << std::endl;
        }
    }

    Book* findBookByTitle(std::string& titleName)
    {
        for (int i = 0; i < books.size(); ++i)
        {
            std::string currentTitle = books[i].getTitle();

            if (currentTitle.compare(titleName) == 0)
            {
                return &books[i];
            }
        }

        return nullptr;
    }

    Book* findBookByAuthor(std::string& authorName)
    {
        for (int i = 0; i < books.size(); ++i)
        {
            std::string currentAuthor = books[i].getAuthor();

            if (currentAuthor.compare(authorName) == 0)
            {
                return &books[i];
            }
        }

        return nullptr;
    }

    // 검색을 책 제목으로하는 메서드
    // 책 제목이 동일한 경우는 없다고 가정
    void searchByTitle(std::string titleName)
    {
        Book* curBook = findBookByTitle(titleName);

        if (nullptr == curBook)
        {
            std::cout << "책을 찾지 못했습니다!" << std::endl;
        }
        else
        {
            std::cout << "책을 찾았습니다!" << std::endl;
            std::cout << "- " << curBook->getTitle() << " by " << curBook->getAuthor() << std::endl;
        }
    }

    // 검색을 책 작가로하는 메서드
    void searchByAuthor(std::string authorName)
    {
        Book* curBook = findBookByAuthor(authorName);

        if (nullptr == curBook)
        {
            std::cout << "책을 찾지 못했습니다!" << std::endl;
        }
        else
        {
            std::cout << "책을 찾았습니다!" << std::endl;
            std::cout << "- " << curBook->getTitle() << " by " << curBook->getAuthor() << std::endl;
        }
    }

    Book* getBookByTitle(std::string titleName)
    {
        return findBookByTitle(titleName);
    }

    Book* getBookByAuthor(std::string AuthorName)
    {
        return findBookByAuthor(AuthorName);
    }
};

class BorrowManager
{
private:
    std::unordered_map<std::string, int> stock;

public:
    void initializeStock(Book* book, int quantity = 3)
    {
        stock.insert({ book->getTitle(), quantity});
    }

    void displayStock()
    {
        for (auto e : stock)
        {
            std::cout << "- 제목: " << e.first << " 책의 남은 재고는 " << e.second << "개 입니다." << std::endl;
        }
    }

    void brrowBook(std::string title)
    {
        if (stock.find(title) == stock.end())
        {
            std::cout << "재고 목록에서 책을 찾지 못했습니다!" << std::endl;
            return;
        }

        if (0 >= stock[title])
        {
            std::cout << "재고가 남아있지 않아 대여가 불가능합니다!" << std::endl;
            return;
        }

        while (true)
        {
            std::cout << "최대 " << stock[title] << "개 대여 가능합니다." << std::endl;
            std::cout << "몇 개를 대여하시겠습니까?" << std::endl;

            int input;
            std::cin >> input;

            if (0 <= input && stock[title] >= input)
            {
                stock[title] -= input;

                std::cout << input << "개가 대여 됐습니다." << std::endl;
                break;
            }
            else
            {
                std::cout << "잘못된 입력입니다." << std::endl;
            }
        }
    }

    void returnBook(std::string title)
    {
        if (stock.find(title) == stock.end())
        {
            std::cout << "재고 목록에서 책을 찾지 못했습니다!" << std::endl;
            return;
        }

        if (3 <= stock[title])
        {
            std::cout << "대여된 수량이 없습니다." << std::endl;
            return;
        }

        while (true)
        {
            std::cout << "최대 " << 3 - stock[title] << "개 반납 가능합니다." << std::endl;
            std::cout << "몇 개를 반납하시겠습니까?" << std::endl;

            int input;
            std::cin >> input;

            if (0 <= input && 3 - stock[title] >= input)
            {
                stock[title] += input;

                std::cout << input << "개가 반납 됐습니다." << std::endl;
                break;
            }
            else
            {
                std::cout << "잘못된 입력입니다." << std::endl;
            }
        }
    }
};

enum class InputName : int
{
    NONE,
    AddBook,
    DisplayAllBooks,
    SearchByTitle,
    SearchByAuthor,
    DisplayAllStock,
    BorrowByTitle,
    BorrowByAuthor,
    ReturnByTitle,
    ReturnByAuthor,
    End,
};

// 도서관 관리 프로그램의 기본 메뉴를 반복적으로 출력하여 사용자 입력을 처리합니다.
// 프로그램 종료를 선택하기 전까지 계속 동작합니다.
void runLibrary(BookManager& bookManager, BorrowManager& borrowManager)
{
    bool bIsRunning = true;
    while (bIsRunning) {
        std::cout << "\n도서관 관리 프로그램" << std::endl;
        std::cout << static_cast<int>(InputName::AddBook) << ". 책 추가" << std::endl; // 책 정보를 입력받아 책 목록에 추가
        std::cout << static_cast<int>(InputName::DisplayAllBooks) << ". 모든 책 출력" << std::endl; // 현재 책 목록에 있는 모든 책 출력
        std::cout << static_cast<int>(InputName::SearchByTitle) << ". 책 제목으로 검색" << std::endl;
        std::cout << static_cast<int>(InputName::SearchByAuthor) << ". 책 작가로 검색" << std::endl;
        std::cout << static_cast<int>(InputName::DisplayAllStock) << ". 모든 책의 재고 출력" << std::endl;
        std::cout << static_cast<int>(InputName::BorrowByTitle) << ". 책 제목으로 대여" << std::endl;
        std::cout << static_cast<int>(InputName::BorrowByAuthor) << ". 책 작가로 대여" << std::endl;
        std::cout << static_cast<int>(InputName::ReturnByTitle) << ". 책 제목으로 반납" << std::endl;
        std::cout << static_cast<int>(InputName::ReturnByAuthor) << ". 책 작가로 반납" << std::endl;
        std::cout << static_cast<int>(InputName::End) << ". 종료" << std::endl; // 프로그램 종료
        std::cout << "선택: ";

        int choice; // 사용자의 메뉴 선택을 저장
        std::cin >> choice;

        switch (static_cast<InputName>(choice))
        {
        case InputName::AddBook:
            // 사용자로부터 책 제목과 저자명을 입력받아 BookManager에 추가합니다.
            {
            std::string title, author;
            std::cout << "책 제목: ";
            std::cin.ignore(); // 이전 입력의 잔여 버퍼를 제거
            getline(std::cin, title); // 제목 입력 (공백 포함)
            std::cout << "책 저자: ";
            getline(std::cin, author); // 저자명 입력 (공백 포함)
            bookManager.addBook(title, author); // 입력받은 책 정보를 추가
            borrowManager.initializeStock(bookManager.getBookByTitle(title));
            }
            break;
        case InputName::DisplayAllBooks:
            // 현재 BookManager에 저장된 책 목록을 출력합니다.
            bookManager.displayAllBooks();
            break;
        case InputName::SearchByTitle:
            // 현재 BookManager에 저장된 책 목록에서 책 이름으로 책을 찾아 출력합니다.
            {
            std::string title;
            std::cout << "검색하려는 책 제목: ";
            std::cin.ignore();
            getline(std::cin, title);
            bookManager.searchByTitle(title);
            }
            break;
        case InputName::SearchByAuthor:
            // 현재 BookManager에 저장된 책 목록에서 책 작가로 책을 찾아 출력합니다.
            {
            std::string author;
            std::cout << "검색하려는 책 작가: ";
            std::cin.ignore();
            getline(std::cin, author);
            bookManager.searchByAuthor(author);
            }
            break;
        case InputName::DisplayAllStock:
            // 현재 BorrowManager에 저장된 책의 재고를 출력합니다.
            borrowManager.displayStock();
            break;
        case InputName::BorrowByTitle:
            {
                std::string title;
                std::cout << "빌리려는 책 제목: ";
                std::cin.ignore();
                getline(std::cin, title);
                Book* curBook = bookManager.getBookByTitle(title);
                if (nullptr != curBook)
                {
                    borrowManager.brrowBook(curBook->getTitle());
                }
                else
                {
                    std::cout << "책을 찾을 수 없습니다." << std::endl;
                }
            }
            break;
        case InputName::BorrowByAuthor:
            {
                std::string author;
                std::cout << "빌리려는 책의 작가: ";
                std::cin.ignore();
                getline(std::cin, author);
                Book* curBook = bookManager.getBookByAuthor(author);
                if (nullptr != curBook)
                {
                    borrowManager.brrowBook(curBook->getTitle());
                }
                else
                {
                    std::cout << "책을 찾을 수 없습니다." << std::endl;
                }
            }
            break;
        case InputName::ReturnByTitle:
            {
                std::string title;
                std::cout << "반납하려는 책 제목: ";
                std::cin.ignore();
                getline(std::cin, title);
                Book* curBook = bookManager.getBookByTitle(title);
                if (nullptr != curBook)
                {
                    borrowManager.returnBook(curBook->getTitle());
                }
            }
            break;
        case InputName::ReturnByAuthor:
            {
                std::string author;
                std::cout << "반납하려는 책 작가: ";
                std::cin.ignore();
                getline(std::cin, author);
                Book* curBook = bookManager.getBookByAuthor(author);
                if (nullptr != curBook)
                {
                    borrowManager.returnBook(curBook->getTitle());
                }
            }
            break;
        case InputName::End:
            // 프로그램을 종료하고 사용자에게 메시지를 출력합니다.
            std::cout << "프로그램을 종료합니다." << std::endl;
            bIsRunning = false; // while 루프 종료
            break;
        default:
            // 잘못된 입력 처리
            // 메뉴에 없는 번호를 입력했을 경우 경고 메시지를 출력합니다.
            std::cin.clear(); // 오류 상태 초기화
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 버퍼의 입력 가능한 최대 문자의 수만큼 모든 문자를 무시하고 개행 문자를 만나면 중단
            std::cout << "잘못된 입력입니다. 다시 시도하세요." << std::endl;
            break;
        }
    }
}

int main() {
    BookManager bookManager;
    BorrowManager borrowManager;

    runLibrary(bookManager, borrowManager);
}

Date:     Updated:

카테고리:

태그:

TIL 카테고리 내 다른 글 보러가기

댓글남기기