class iterator;
iterator begin();
iterator end();
class iterator
{
private:
BST* m_Owner; // 1
BSTNode<T1, T2>* m_TargetNode; // 2
};
public:
iterator()
:m_Owner(nullptr)
, m_TargetNode(nullptr)
{}
iterator(BST* _Owner, BSTNode<T1, T2>* _Node)
:m_Owner(_Owner)
, m_TargetNode(_Node)
{}
public:
class iterator;
iterator begin()
{
assert(m_Root); // 1
BSTNode<T1, T2>* pNode = m_Root; // 2
while (pNode->pLeftChild) // 3
{
pNode = pNode->pLeftChild; // 4
}
return iterator(this, pNode); // 5
};
BST<int, float>::iterator myiter = bst.begin();
inline void BST<T1, T2>::Circit(BSTNode<T1, T2>* _Node)
{
if (nullptr == _Node)
{
return;
}
//delete _Node; // 1
Circit(_Node->pLeftChild);
//delete _Node; // 2
Circit(_Node->pRightChild);
//delete _Node; // 3
}
효율성 문제: 재귀 함수를 사용하면 편리할 수 있지만, 이는 많은 연산을 필요로 하여 비효율적일 수 있습니다. 특히, 데이터의 양이 많을 경우, 각 데이터에 대해 함수 호출이 발생하므로 처리 효율이 떨어질 수 있습니다.
반복문 사용의 이유: 소멸자(destructor)에서 모든 데이터에 접근하여 처리해야 하는 상황에서 반복문을 사용하는 것이 더 적절할 수 있습니다. 이는 재귀 함수를 사용할 때 발생하는 다수의 함수 호출을 줄임으로써 최적화를 가능하게 합니다.
데이터 양의 불확실성: 데이터의 양이 불확실한 상황에서는 반복문을 사용하는 것이 더 유연하고 효율적인 해결책이 될 수 있습니다. 재귀 함수는 데이터 양에 따라 성능이 크게 달라질 수 있기 때문입니다.
레벨 순회의 개념: 레벨 순회는 트리의 각 레벨을 순서대로 방문하는 순회 방식입니다. 이는 트리의 루트 노드부터 시작하여, 각 레벨의 모든 노드를 왼쪽에서 오른쪽으로 방문합니다.
구현 방법: 레벨 순회를 구현하기 위해 간단한 리스트 또는 큐(queue) 자료구조를 사용합니다. 루트 노드를 리스트의 첫 번째 요소로 넣고, 이 노드를 처리한 후 해당 노드의 자식 노드들을 리스트에 추가합니다. 이 과정을 모든 노드가 처리될 때까지 반복합니다.
예시:
큐 자료구조의 중요성: 레벨 순회를 효율적으로 수행하기 위해서는 큐 자료구조가 필요합니다. 큐는 선입선출(FIFO: First-In, First-Out)의 특성을 가지며, 이 특성은 각 레벨의 노드를 순차적으로 처리하는 데 적합합니다.
T pop_front();
T pop_back();
template<typename T>
T List<T>::pop_front()
{
assert(m_pHead);
T data = m_pHead->Data;
if (nullptr == m_pHead->pNext)
{
delete m_pHead;
m_pHead = nullptr;
m_pTail = nullptr;
}
else
{
m_pHead = m_pHead->pNext;
delete m_pHead->pPrev;
m_pHead->pPrev = nullptr;
}
//m_pHead = m_pHead->pNext;
//delete = m_pHead->pPrev;
//m_pHead->pPrev = nullptr;
--m_CurCount;
return data;
}
template<typename T>
T List<T>::pop_front()
{
}
assert(m_pHead);
T data = m_pHead->Data;
m_pHead = m_pHead->pNext
delete m_pHead->pPrev;
m_pHead->pPrev = nullptr;
if (nullptr == m_pHead->pNext) // 1
{
delete m_pHead; // 2
m_pHead = nullptr; // 3
m_pTail = nullptr; // 4
}
else
{
m_pHead = m_pHead->pNext; // 1
delete m_pHead->pPrev; // 2
m_pHead->pPrev = nullptr; // 3
}
--m_CurCount;
return data;
#include "List.h"
~BST()
{
// 모든 노드들을 삭제
List<BSTNode<T1, T2>*> queue;
queue.push_back(m_Root);
while (!queue.empty)
{
BSTNode < T1, T2 >* pNode = queue.pop_front();
if (nullptr != pNode->pLeftChild)
queue.pop_front(pNode->pLeftChild);
if (nullptr != pNode->pRightChild)
queue.pop_front(pNode->pRightChild);
delete pNode;
}
}
List<BSTNode<T1, T2>*> queue;
queue.push_back(m_Root);
// 6.2
bool empty()
{
// 1
if (0 == m_CurCount)
return true;
else
return false;
// 2
bool bReturn = false;
0 == m_CurCount ? return true : return false;
return bReturn;
// 3
return 0 == m_CurCount ? true : false;
}
while (!queue.empty) // 1
{
BSTNode < T1, T2 >* pNode = queue.pop_front(); // 2
if (nullptr != pNode->pLeftChild) // 3
queue.pop_front(pNode->pLeftChild); // 4
if (nullptr != pNode->pRightChild) // 5
queue.pop_front(pNode->pRightChild); // 6
delete pNode; // 7
}
1
2 3
4 5 6 7
public:
iterator& operator ++()
{
if (m_TargetNode->HasRightChild())
{
BSTNode<T1, T2>* pNextNode = m_TargetNode->pRightChild;
while (pNextNode->pLeftChild) { pNextNode = pNextNode->pLeftChild; }
m_TargetNode = pNextNode;
}
else
{
BSTNode<T1, T2>* pNextNode = m_TargetNode;
do {
if (pNextNode->IsRoot())
{
// end iterator
break;
}
else
{
pNextNode = pNextNode->pParent;
}
} while (!pNextNode->IsLeftChild())
m_TargetNode = pNextNode->pParent;
}
return (*this);
}
iterator& operator ++()
{
return (*this);
}
iterator& operator --()
{
return (*this);
}
m_TargetNode = m_TargetNode->pParent->pLeftChild;
// 1
bool HasRightChild() { return pRightChild; }
bool HasLeftChild() { return pLeftChild; }
// 2
bool IsLeftChild() { return pParent->pLeftChild == this; }
bool IsRightChild() { return pParent->pRightChild == this; }
// 3
bool IsRoot() { return !pParent; }
if (m_TargetNode->HasRightChild()) // 1
{
BSTNode<T1, T2>* pNextNode = m_TargetNode->pRightChild; // 2
while (pNextNode->pLeftChild) { pNextNode = pNextNode->pLeftChild; } // 3
m_TargetNode = pNextNode; // 4
}
else
{
BSTNode<T1, T2>* pNextNode = m_TargetNode;
// 1
do {
if (pNextNode->IsRoot())
{
// end iterator // 2
break;
}
else
{
pNextNode = pNextNode->pParent;
}
} while (!pNextNode->IsLeftChild())
m_TargetNode = pNextNode->pParent;
}
BST
클래스의 iterator& operator ++()
함수는 이진 탐색 트리에서 중위 순회(Inorder Traversal)를 수행하며 다음 노드로 이동하는 기능을 합니다. 중위 순회는 왼쪽 자식 - 현재 노드 - 오른쪽 자식 순서로 노드를 방문합니다. 이 함수의 로직은 다음과 같이 상세하게 설명될 수 있습니다:
m_TargetNode
)의 오른쪽 서브트리에서 중위 순회의 다음 노드를 찾습니다.m_TargetNode
를 그 오른쪽 자식 노드로 이동합니다.pNextNode
가 루트 노드이면, 이는 순회가 끝났음을 나타내고, 반복문을 중단합니다.m_TargetNode
를 포함하는 this
이터레이터의 참조를 반환합니다. 이렇게 함으로써 이터레이터는 중위 순회의 다음 노드를 가리키게 됩니다.#include <iostream>
#include "BST.h"
#include <set>
#include <map>
using std::set;
using std::map;
using std::make_pair;
int main()
{
set<int> intset;
intset.insert(100);
intset.insert(150);
intset.insert(170);
intset.insert(125);
intset.insert(80);
intset.insert(90);
intset.insert(50);
set<int>::iterator iter = intset.find(125);
iter = intset.find(124);
if (iter != intset.end())
{
}
else
{
}
// map 사용
map<int, int> intmap;
intmap.insert(make_pair(100, 1));
intmap.insert(make_pair(150, 2));
intmap.insert(make_pair(170, 3));
intmap.insert(make_pair(125, 4));
intmap.insert(make_pair(80, 5));
intmap.insert(make_pair(90, 6));
intmap.insert(make_pair(50, 7));
map<int, int>::iterator mapiter = intmap.find(50);
if (mapiter != intmap.end())
{
(*mapiter).first;
(*mapiter).second;
}
// BST insert 테스트
BST<int, float> bst;
bst.insert(make_bstpair(100, 1.1f));
bst.insert(make_bstpair(150, 2.2f));
bst.insert(make_bstpair(50, 2.2f));
bst.insert(make_bstpair(170, 1.1f));
bst.insert(make_bstpair(125, 2.2f));
bst.insert(make_bstpair(25, 2.2f));
bst.insert(make_bstpair(75, 2.2f));
bst.Circit();
// main에 추가된 코드 단 한줄.
BST<int, float>::iterator myiter = bst.begin();
return 0;
}
#pragma once
#include <iostream>
#include <assert.h>
#include "List.h"
// Binary Search Tree(BST)
template<typename T1, typename T2>
struct BSTPair
{
T1 first;
T2 second;
BSTPair()
{}
BSTPair(const T1& _first, const T2& _second)
: first(_first)
, second(_second)
{
}
};
template<typename T1, typename T2>
struct BSTNode
{
BSTPair<T1, T2> pair;
BSTNode* pParent;
BSTNode* pLeftChild;
BSTNode* pRightChild;
bool HasRightChild() { return pRightChild; }
bool HasLeftChild() { return pLeftChild; }
bool IsLeftChild() { pParent->pLeftChild == this; }
bool IsRightChild() { pParent->pRightChild == this; }
bool IsRoot() { return !pParent; }
BSTNode()
: pParent(nullptr)
, pLeftChild(nullptr)
, pRightChild(nullptr)
{}
BSTNode(const BSTPair<T1, T2>& _pair, BSTNode* _Parent = nullptr, BSTNode* _LeftChild = nullptr, BSTNode* _RightChild = nullptr)
: pair(_pair)
, pParent(_Parent)
, pLeftChild(_LeftChild)
, pRightChild(_RightChild)
{}
};
template<typename T1, typename T2>
class BST
{
private:
BSTNode<T1, T2>* m_Root; // 루트 노드 주소
int m_CurCount; // 현재 데이터 개수
public:
void insert(const BSTPair<T1, T2>& _pair);
public:
void Circit()
{
Circit(m_Root);
}
private:
void Circit(BSTNode<T1, T2>* _Node);
public:
class iterator;
iterator begin()
{
assert(m_Root);
BSTNode<T1, T2>* pNode = m_Root;
while (pNode->pLeftChild) { pNode = pNode->pLeftChild; }
return iterator(this, pNode);
}
//iterator end();
//iterator find(const T1& _key);
public:
BST()
: m_Root(nullptr)
, m_CurCount(0)
{}
~BST()
{
// 모든 노드들을 삭제
// 재귀함수
// 레벨순회
List<BSTNode<T1, T2>*> queue;
queue.push_back(m_Root);
while (!queue.empty())
{
BSTNode<T1, T2>* pNode = queue.pop_front();
if (nullptr != pNode->pLeftChild)
queue.push_back(pNode->pLeftChild);
if (nullptr != pNode->pRightChild)
queue.push_back(pNode->pRightChild);
delete pNode;
}
}
class iterator
{
private:
BST* m_Owner;
BSTNode<T1, T2>* m_TargetNode;
public:
iterator& operator ++()
{
// 오른쪽 자식이 있으면
if (m_TargetNode->HasRightChild())
{
// 오른쪽 자식으로 간다.
BSTNode<T1, T2>* pNextNode = m_TargetNode->pRightChild;
// 왼쪽자식이 없을 때 까지 내려간다.
while (pNextNode->pLeftChild) { pNextNode = pNextNode->pLeftChild; }
m_TargetNode = pNextNode;
}
// 오른쪽 자식이 없으며
else
{
BSTNode<T1, T2>* pNextNode = m_TargetNode;
// 부모의 왼쪽 자식일때 까지 올라가서 그때 부모가 나의 후속자
do {
if (pNextNode->IsRoot())
{
// end iterator
break;
}
else
{
pNextNode = pNextNode->pParent;
}
} while (!pNextNode->IsLeftChild())
m_TargetNode = pNextNode->pParent;
}
return (*this);
}
iterator& operator--()
{
return (*this);
}
public:
iterator()
: m_Owner(nullptr)
, m_TargetNode(nullptr)
{}
iterator(BST* _Owner, BSTNode<T1, T2>* _Node)
: m_Owner(_Owner)
, m_TargetNode(_Node)
{}
};
};
// 입력된 T1, T2 타입의 데이터를 묶어서 BSTPair<T1, T2> 타입 구조체로 반환
template<typename T1, typename T2>
BSTPair<T1, T2> make_bstpair(const T1& _first, const T2& _second)
{
return BSTPair<T1, T2>(_first, _second);
}
template<typename T1, typename T2>
void BST<T1, T2>::insert(const BSTPair<T1, T2>& _pair)
{
BSTNode<T1, T2>* pNewNode = new BSTNode<T1, T2>(_pair);
// 최초로 데이터가 입력된 상황
if (nullptr == m_Root)
{
m_Root = pNewNode;
}
// 데이터가 2개 이상인 경우
else
{
BSTNode<T1, T2>* pNode = m_Root;
while (true)
{
// 입력된 first 값이 현재 노드의 first 값보다 작은 경우
if (pNewNode->pair.first < pNode->pair.first)
{
// 왼쪽이 비어있으면
if (nullptr == pNode->pLeftChild)
{
// 현재 노드의 왼쪽으로 연결
pNode->pLeftChild = pNewNode;
pNewNode->pParent = pNode;
break;
}
// 왼쪽으로 내려간다.
pNode = pNode->pLeftChild;
}
// 입력된 first 값이 현재 노드의 first 값보다 큰 경우
else if (pNode->pair.first < pNewNode->pair.first)
{
// 현재 노드의 오른쪽이 비어있으며
if (nullptr == pNode->pRightChild)
{
// 입력된 노드를 현재 노드의 오른쪽으로 연결
pNode->pRightChild = pNewNode;
pNewNode->pParent = pNode;
break;
}
// 오른쪽으로 내려간다.
pNode = pNode->pRightChild;
}
}
}
++m_CurCount;
}
template<typename T1, typename T2>
inline void BST<T1, T2>::Circit(BSTNode<T1, T2>* _Node)
{
if (nullptr == _Node)
{
return;
}
Circit(_Node->pLeftChild);
//std::cout << _Node->pair.first << std::endl;
Circit(_Node->pRightChild);
}
// 클래스 내부
T pop_front();
T pop_back();
bool empty()
{
return 0 == m_CurCount ? true : false;
}
// 클래스 내부
template<typename T>
T List<T>::pop_front()
{
assert(m_pHead);
// 헤드노드가 들고있는 데이터를 복사해둔다.
T data = m_pHead->Data;
// 삭제할 헤드노드의 다음이 존재하지 않는다면 (데이터가 1개였다)
if (nullptr == m_pHead->pNext)
{
// 헤드노드를 삭제한다.
delete m_pHead;
// 헤드, 테일 포인터를 nullptr 로 만든다.
m_pHead = nullptr;
m_pTail = nullptr;
}
else
{
// 삭제할 헤드의 다음을 새로운 헤드로 지정한다.
m_pHead = m_pHead->pNext;
// 새로지정한 헤드의 이전이 삭제할 원래 해드노드였다.
delete m_pHead->pPrev;
// 새로 지정한 헤드의 이전을 nullptr 만든다.
m_pHead->pPrev = nullptr;
}
// 데이터 개수를 하나 줄인다.
--m_CurCount;
// 삭제한 헤드노드에 저장해두었던 데이터를 반환해준다.
return data;
}
template<typename T>
T List<T>::pop_back()
{
return T();
}
1차 24.01.05
2차 24.01.09
3차 24.01.10
4차 24.01.11
5차 24.01.12