본문 바로가기
자료구조와 알고리즘/[서적]C++로 구현하는 자료구조와 알고리즘

3장. 배열, 링크드 리스트, 그리고 재귀

by 헛둘이 2022. 9. 5.

3.1 배열 사용

  • 배열에 게임 엔트리 저장
    • 배열에 객체들을 저장하는 것이 배열의 흔한 용도
    • 그 외 병원에 환자들의 정보를 등록하는 것, 축구팀의 선수 이름을 등록하는 것..
    • 상위 점수 엔트리를 표현하는 객체에 무엇을 포함할 것인지를 생각한다
    • 가장 간단히 떠오르는 건 score과 이름일 것이다.
class GameEntry
{
public:
	GameEntry(const string& n = "", int s = 0); // 이름과 점수를 받는다.
	string getName() const;				// getter
	int getScore() const;				// getter
    
private:
	string name;					// 이름
	int score;					// 점수
};

 

 

  • 상위 점수 클래스 작성
    • Scores 클래스는 배열 entries의 상위 점수들을 저장한다.
    • 상위 점수의 최대 개수는 인스턴스마다 다를 수 있으므로, 원하는 최대값을 정의하는 maxEntries 멤버 변수를 만든다.
    • 엔트리의 실제 개수를 관리하기 위해 numEntries를 만든다.
class GameEntry;
class Scores
{
public:

	Scores(int maxEnt = 10);		// 생성자에서 최대 값 받아서 new로 메모리 할당
	~Scores();				// 소멸될 때 소멸자에서 소멸
	void add(const GameEntry& e);		// Score Board에 추가하기
	GameEntry remove(int i)			// i번째 위치에 있는 Entry 제거
		throw(out_of_range);


private:
	int maxEntries;					// 엔트리 최대 값
	int numEntries;					// 현재 엔트리의 개수
	GameEntry* entries;				// 엔트리 배열
};

 

  • 생성자 코드 작성
    • 생성자는 배열의 최대 크기를 저장하고 배열 공간을 할당한다.
    • 그리고 배열 내부 개수를 0으로 대입하여 초기화한다.
  • 소멸자 코드 작성
    • 소멸자는 생성자에서 할당한 배열을 해제한다.
Scores::Scores(int maxEnt)
{
	maxEntries = maxEnt; 
    // maxEntries는 엔트리의 최대 개수이다.
    
	entries = new GameEntry[maxEntries]; 
    // entries는 GameEntry의 배열이고, 최대 maxEntries의 수만큼 저장된다.
    
	numEntries = 0;
    // numEntries는 현재 들어온 엔트리의 개수이며, 생성자에서 0으로 초기화한다.
}

Scores::~Scores()
{
	delete[] entries;
    // 생성자에서 할당된 entries를 소멸자에서 해제한다.
}
  • 비디오 게임을 할 수록 add 함수를 통해 추가적인 GameEntry 객체들이 배열에 복사됨
  • 나중에 추가될 remove 함수를 통해 i번째에 있는 GameEntry 객체를 제거할 수 있음
  • 범위는 0 <= i <= numEntries - 1이어야 하고, 이를 초과하면 IndexOutOfBounds 예외를 발생시킨다.

 

  • add 멤버 함수 작성
void Scores::add(const GameEntry& e)
{
	// 배열이 꽉 차있는지 검토한다.
	int newScore = e.getScore();

	// Entries가 꽉 찼을 경우
	if (numEntries == maxEntries) {
		// 만약 새로 들어올 GameEntry의 점수가 현재 Entry의 가장 작은 요소보다 작을 경우
		if (newScore <= entries[maxEntries - 1].getScore())
			return;
	}

	// 꽉 찬게 아니라면 요소를 추가한다.
	else 
		numEntries++;

	// numEntries++를 했으므로 numEntries - 2가 현재 마지막 요소
	// entries[i+1] = entries[i]; 이렇게 이동하므로
	// numEntries - 2부터 이동해야 한다.
	int i = numEntries - 2;

	
	// newScore가 entries[i]보다 작을때까지 반복
	// ex) 3[440] 4[420]  , 넣어야 할 값 e: 430
	// 인덱스가 4일 때, 아래 반복문이 실행되고, 4에 있던 값이 5로 들어간다.
	// 인덱스가 3이 되면 430 > 440이라서 루프를 탈출한다.
	while (i >= 0 && newScore > entries[i].getScore())
	// i >= 0인 이유는 인덱스가 0 미만으로 내려갈 수 없기 때문
	{
		entries[i + 1] = entries[i]; // 위에서 설명했던 부분
		i--;
	}

	// i + 1에 삽입하는 이유는? 빈 공간인 4번 인덱스에 넣어줘야 하므로.
	entries[i + 1] = e;
}

while 루프 돌기 전 상태

 

i가 3이 되고 440이 430보다 크기 때문에 while문을 탈출했고 그래서 430은 i+1 인덱스에 들어가야 한다.

 

  • remove 함수 작성
GameEntry Scores::remove(int i) throw(out_of_range)
{
	// i가 정상 범위 안에 있는지 검사
	if ((i < 0) || (i >= numEntries)) // i가 0보다 작거나 numEntries보다 같거나 큰지
		throw out_of_range::exception("Invalid Index");

	GameEntry e = entries[i]; // 정상적인 범위 안의 i이므로 가져온다.

	// i번째에 있는 entry를 덮어쓰며 맨 마지막 엔트리까지 앞으로 1칸씩 이동

	// i가 3이라고 가정하면
	for (int j = i + 1; j < numEntries; j++) // 루프는 4로 시작
	{
		entries[j - 1] = entries[j]; // 4번째에 있는 값을 3번째로 덮어쓴다.
	}

	numEntries--; // 총 개수를 1개 줄임
	return e;
}
  • remove의 구현은 add와 비슷하고, 단지 반대의 과정이다.
  • add는 요소를 추가하기 위해 뒤로 미는 연산인데,
  • remove는 제거된 요소의 빈 자리를 채우기 위해 앞으로 1칸씩 당기는 연산이다.
  • e의 존재는 제거된 위치의 entries를 반환하기 위해 존재한다.

 

 


마무리하며..
  • 이 함수들은 더 정교한 자료구조를 만들기 위해 반복적으로 사용될 기술들의 기초.
  • 모든 자료구조가 실제적인 방법들을 통해 구현되어야 하므로 이 구조를 이해하는게 도움이 된다.

댓글