Study/자료구조,알고리즘

[C++ 쉽게 풀어쓴 자료구조] 프로그래밍 프로젝트 8

게임 만드는 나무꾼 2022. 9. 20. 15:43

완전 이진트리 검사 함수 구현

  • 완전 이진트리란 높이n 의 트리에서 n-1레벨까지는 포화상태, n레벨의 노드들은 왼쪽부터 채워져 있는 상태인 이진트리를 의미한다.
  • 완전 이진트리 검사는 레벨 순회를 통해 구현했다.
  • Left와 Right중 하나라도 null node일 경우, 그 이후 나타나는 노드들은 모든 노드가 단말노드여야 한다.
bool CBinaryTree::IsFull()
{
	std::queue<CBinaryNode*> bQueue;
	bQueue.push(m_Root);

	while (!bQueue.empty())
	{
		bool meetNotFull = false;
		CBinaryNode* node = bQueue.front();

		// full node라면 계속 탐색한다.
		if (node->GetLeft() && node->GetRight())
		{
			bQueue.push(node->GetLeft());
			bQueue.push(node->GetRight());
			bQueue.pop();
		}
		else
		{
			// Full Node가 아닌 노드를 만났을 경우, 모든 노드가 단말 노드여야 한다.
			if (meetNotFull)
			{
				if (!node->IsLeaf())
					return false;
			}
			else
			{
				// 오른쪽만 있는 노드가 존재하면 완전 이진트리가 아니다
				if (!node->GetLeft() && node->GetRight())
					return false;

				// Full Node가 아닌 Node를 처음 만남
				meetNotFull = true;

				if (node->GetLeft())
					bQueue.push(node->GetLeft());

				bQueue.pop();
			}
		}
	}

	return true;
}

임의의 노드 레벨 구하기

  • level n 인 트리는 2^(n-1)개의 노드 이상을 가질 수 없다는 성질을 이용해 레벨을 구할 수 있다.
int CBinaryTree::GetLevel(CBinaryNode* node)
{
	if (!m_Root)
		return 0;

	int level = 1;
	int	nodeCount = 0;
	std::queue<CBinaryNode*> nodeQueue;
	nodeQueue.push(m_Root);

	// 레벨 순회
	while (!nodeQueue.empty())
	{
		++nodeCount;

		CBinaryNode* curNode = nodeQueue.front();

		// level의 높이를 가진 트리가 가질 수 있는 최대 노드 개수를 넘어간 경우
		if (pow(2, level) <= nodeCount)
		{
			++level;
		}

		if (curNode == node)
			return level;

		if (curNode->GetLeft())
			nodeQueue.push(curNode->GetLeft());

		if (curNode->GetRight())
			nodeQueue.push(curNode->GetRight());

		nodeQueue.pop();
	}

	return 0;
}

이진트리 균형 여부

  • 재귀적으로 구현했다.
bool CBinaryTree::IsBalanced()
{
	if (!m_Root)
		return false;

	return m_Root->IsBalanced();
}
bool CBinaryNode::IsBalanced()
{
	bool balanced = true;

	int rLevel = 0;
	int lLevel = 0;

	if (m_Left)
		lLevel = m_Left->GetLevel();

	if (m_Right)
		rLevel = m_Right->GetLevel();

	// 레벨차가 2 이상으로 나는 경우
	if (abs(lLevel - rLevel) > 1)
		return false;

	// 자식 노드도 판별한다.
	if (m_Left)
		balanced = m_Left->IsBalanced();

	if (!balanced)
		return false;

	if (m_Right)
		balanced = m_Right->IsBalanced();

	return true;
}

경로의 길이 합

  • 이또한 재귀적으로 구현했다.
  • 노드가 자식 노드들의 경로의 합을 구해서 리턴하도록
int CBinaryTree::GetPathLength()
{
	if (!m_Root)
		return false;

	return m_Root->GetPathLength(1);
}
int CBinaryNode::GetPathLength(int Level)
{
	std::cout << " Data : " << m_Data << " ";

	if (!m_Left && !m_Right)
		return 0;

	if (m_Left && m_Right)
		return ((Level * 2) + m_Left->GetPathLength(Level + 1) + m_Right->GetPathLength(Level + 1));

	if (m_Left)
		return Level + m_Left->GetPathLength(Level + 1);

	if (m_Right)
		return Level + m_Right->GetPathLength(Level + 1);
}

이진트리 좌우반전

  • 가장 쉬웠다. 그냥 자식들 먼저 스왑해주고 자신의 left right노드를 스왑해주면 끝
bool CBinaryNode::Swap()
{
	bool swap = true;

	if (m_Left)
		swap = m_Left->Swap();

	if (m_Right)
		swap = m_Right->Swap();

	CBinaryNode* temp = m_Left;
	m_Left = m_Right;
	m_Right = temp;

	return true;
}

현재 트리와 다른 트리 분리되어있는지 확인 (Disjoint)

  • 순환적으로 구현하라고 해서 레벨순회하면서 노드별로 other tree가 해당 노드를 포함하고 있는지 확인하도록 했다
bool CBinaryTree::IsDisjointFrom(CBinaryTree* tree)
{
	if (!m_Root)
	{
		return true;
	}

	std::queue<CBinaryNode*> nodeQueue;
	nodeQueue.push(m_Root);

	while (!nodeQueue.empty())
	{
		CBinaryNode* curNode = nodeQueue.front();

		// other 트리가 현재 노드를 포함하고 있는지 확인
		if (tree->Include(curNode))
			return false;

		if (curNode->GetLeft())
			nodeQueue.push(curNode->GetLeft());

		if (curNode->GetRight())
			nodeQueue.push(curNode->GetRight());

		nodeQueue.pop();
	}

	return true;
}

bool CBinaryTree::Include(CBinaryNode* node)
{
	if (!m_Root)
		return false;

	std::queue<CBinaryNode*> nodeQueue;
	nodeQueue.push(m_Root);

	while (!nodeQueue.empty())
	{
		CBinaryNode* curNode = nodeQueue.front();

		if (curNode == node)
			return true;

		if (curNode->GetLeft())
			nodeQueue.push(curNode->GetLeft());

		if (curNode->GetRight())
			nodeQueue.push(curNode->GetRight());

		nodeQueue.pop();
	}

	return false;
}

모든 서브트리가 분리되어있는지(Valid) 확인

  • 서브트리가 분리되어 있는지 확인하고, 그 서브트리들의 서브트리들도 서로 분리되어있는지 확인해야 한다.
  • 따라서 재귀적으로 구현
bool CBinaryTree::IsValid()
{
	if (!m_Root)
		return true;

	bool valid = true;

	std::queue<CBinaryNode*> nodeQueue;
	nodeQueue.push(m_Root);

	while (!nodeQueue.empty())
	{
		CBinaryNode* curNode = nodeQueue.front();

		// left right 둘 다 있는 경우 Disjoint한지 확인한다.
		if (curNode->GetRight() && curNode->GetLeft())
		{
			CBinaryTree leftTree;
			leftTree.SetRoot(curNode->GetLeft());

			CBinaryTree rightTree;
			rightTree.SetRoot(curNode->GetRight());

			if (!leftTree.IsDisjointFrom(&rightTree))
				return false;
		}

		// left right가 Disjoint 하다면 left right가 각자 Valid한지 검사
		if (curNode->GetLeft())
		{
			CBinaryTree leftTree;
			leftTree.SetRoot(curNode->GetLeft());

			if (!leftTree.IsValid())
				return false;

			nodeQueue.push(curNode->GetLeft());
		}

		if (curNode->GetRight())
		{
			CBinaryTree rightTree;
			rightTree.SetRoot(curNode->GetRight());

			if (!rightTree.IsValid())
				return false;

			nodeQueue.push(curNode->GetRight());
		}

		nodeQueue.pop();
	}

	return true;
}

트리를 공부하면서 재귀적으로 구현해야 할 것들이 많아 문제를 잘게 쪼개서 보아야 잘 풀리는 것들이 많았다.