// Graph.h
#pragma once
#include "Stack.h"
#include "Queue.h"
#include <assert.h>
template <typename T>
class CEdge
{
template <typename T>
friend class CGraph;
template <typename T>
friend class CGraphNode;
private:
CEdge()
{
m_Node = nullptr;
}
~CEdge()
{
}
private:
class CGraphNode<T>* m_Node;
};
template <typename T>
class CGraphNode
{
template <typename T>
friend class CGraph;
private:
CGraphNode()
{
m_Size = 0;
m_Capacity = 1;
m_EdgeArray = new CEdge<T>*[m_Capacity];
m_Visit = false;
}
~CGraphNode()
{
for (int i = 0; i < m_Size; ++i)
{
delete m_EdgeArray[i];
}
delete[] m_EdgeArray;
}
private:
CEdge<T>** m_EdgeArray;
int m_Size;
int m_Capacity;
T m_Data;
bool m_Visit;
private:
void AddEdge(CGraphNode<T>* Node)
{
if (m_Size == m_Capacity)
{
m_Capacity *= 2;
CEdge<T>** Array = new CEdge<T>*[m_Capacity];
memcpy(Array, m_EdgeArray, sizeof(CEdge<T>*) * m_Size);
delete[] m_EdgeArray;
m_EdgeArray = Array;
}
CEdge<T>* Edge = new CEdge<T>;
Edge->m_Node = Node;
m_EdgeArray[m_Size] = Edge;
++m_Size;
}
};
template <typename T>
class CGraph
{
public:
CGraph()
{
m_Size = 0;
m_Capacity = 4;
m_NodeArray = new CGraphNode<T>*[m_Capacity];
}
~CGraph()
{
for (int i = 0; i < m_Size; ++i)
{
delete m_NodeArray[i];
}
delete[] m_NodeArray;
}
private:
CGraphNode<T>** m_NodeArray;
int m_Size;
int m_Capacity;
CStack<CGraphNode<T>*> m_Stack;
CQueue<CGraphNode<T>*> m_Queue;
public:
void insert(const T& data)
{
if (m_Size == m_Capacity)
{
m_Capacity *= 2;
CGraphNode<T>** Array = new CGraphNode<T>*[m_Capacity];
memcpy(Array, m_NodeArray, sizeof(CGraphNode<T>*) * m_Size);
delete[] m_NodeArray;
m_NodeArray = Array;
}
CGraphNode<T>* Node = new CGraphNode<T>;
Node->m_Data = data;
m_NodeArray[m_Size] = Node;
++m_Size;
}
// 노드에 edge를 추가해준다.
void AddEdge(const T& Src, const T& Dest)
{
CGraphNode<T>* SrcNode = nullptr;
CGraphNode<T>* DestNode = nullptr;
for (int i = 0; i < m_Size; ++i)
{
if (m_NodeArray[i]->m_Data == Src)
SrcNode = m_NodeArray[i];
else if (m_NodeArray[i]->m_Data == Dest)
DestNode = m_NodeArray[i];
if (SrcNode && DestNode)
break;
}
if (!SrcNode || !DestNode)
return;
SrcNode->AddEdge(DestNode);
DestNode->AddEdge(SrcNode);
}
// data를 갖고 있는 노드를 찾아서 반환하는 함수
CGraphNode<T>* FindNode(const T& Data)
{
CGraphNode<T>* Node = nullptr;
for (int i = 0; i < m_Size; ++i)
{
if (m_NodeArray[i]->m_Data == Data)
{
Node = m_NodeArray[i];
return Node;
}
}
if (!Node)
{
assert(Node != nullptr);
}
}
void DFS()
{
CGraphNode<T>* Start = m_NodeArray[0];
if (Start->m_Visit == false)
{
m_Stack.push(Start);
Start->m_Visit = true;
}
CGraphNode<T>* Node = nullptr;
while (!m_Stack.empty())
{
Node = m_Stack.top();
m_Stack.pop();
VisitOutput(Node);
// 인접 노드중에 아직 스택에 안들어 간 것 중에 가장 작은것 부터 탐색하고 싶어서
// m_EdgeArray안의 노드들의 값 기준으로 내림차순 정렬 => 스택에서는 작은값 부터 top에 위치
for (int i = 0; i < Node->m_Size - 1; ++i)
{
for (int j = i + 1; j < Node->m_Size; ++j)
{
T DataTmp1 = Node->m_EdgeArray[i]->m_Node->m_Data;
T DataTmp2 = Node->m_EdgeArray[j]->m_Node->m_Data;
if (DataTmp1 < DataTmp2)
{
CEdge<T>* EdgeTmp = Node->m_EdgeArray[i];
Node->m_EdgeArray[i] = Node->m_EdgeArray[j];
Node->m_EdgeArray[j] = EdgeTmp;
}
}
}
for (int i = 0; i < Node->m_Size; ++i)
{
CGraphNode<T>* TmpNode = Node->m_EdgeArray[i]->m_Node;
if (TmpNode->m_Visit == false)
{
m_Stack.push(TmpNode);
TmpNode->m_Visit = true;
}
}
}
}
void BFS()
{
CGraphNode<T>* Start = m_NodeArray[0];
if (Start->m_Visit == false)
{
m_Queue.push(Start);
Start->m_Visit = true;
}
CGraphNode<T>* Node = nullptr;
while (!m_Queue.empty())
{
Node = m_Queue.front();
m_Queue.pop();
VisitOutput(Node);
// 인접 노드중에 아직 큐에 안들어 간 것 중에 가장 작은것 부터 탐색하고 싶어서
// m_EdgeArray안의 노드들의 값 기준으로 오름차순 정렬 => 큐에서는 작은값 부터 front에 위치
for (int i = 0; i < Node->m_Size - 1; ++i)
{
for (int j = i + 1; j < Node->m_Size; ++j)
{
T DataTmp1 = Node->m_EdgeArray[i]->m_Node->m_Data;
T DataTmp2 = Node->m_EdgeArray[j]->m_Node->m_Data;
if (DataTmp1 > DataTmp2)
{
CEdge<T>* EdgeTmp = Node->m_EdgeArray[i];
Node->m_EdgeArray[i] = Node->m_EdgeArray[j];
Node->m_EdgeArray[j] = EdgeTmp;
}
}
}
for (int i = 0; i < Node->m_Size; ++i)
{
CGraphNode<T>* TmpNode = Node->m_EdgeArray[i]->m_Node;
if (TmpNode->m_Visit == false)
{
m_Queue.push(TmpNode);
TmpNode->m_Visit = true;
}
}
}
}
// 그래프 내 모든 노드의 m_Visit = false로 다시 복귀
void ResetVisit()
{
for (int i = 0; i < m_Size; ++i)
{
m_NodeArray[i]->m_Visit = false;
}
}
void DFSRecursive()
{
DFSRecursive(m_NodeArray[0]);
}
private:
void VisitOutput(CGraphNode<T>* Node)
{
std::cout << "Visit Node : " << Node->m_Data << std::endl;
}
void DFSRecursive(CGraphNode<T>* Node)
{
if (Node->m_Visit)
{
return;
}
Node->m_Visit = true;
VisitOutput(Node);
// 인접한 노드를 오름차순으로 정렬해서 재귀호출 -> 인접한 노드중에서 낮은 값의 노드 먼저 탐색
for (int i = 0; i < Node->m_Size - 1; ++i)
{
for (int j = i + 1; j < Node->m_Size; ++j)
{
T DataTmp1 = Node->m_EdgeArray[i]->m_Node->m_Data;
T DataTmp2 = Node->m_EdgeArray[j]->m_Node->m_Data;
if (DataTmp1 > DataTmp2)
{
CEdge<T>* EdgeTmp = Node->m_EdgeArray[i];
Node->m_EdgeArray[i] = Node->m_EdgeArray[j];
Node->m_EdgeArray[j] = EdgeTmp;
}
}
}
for (int i = 0; i < Node->m_Size; ++i)
{
CGraphNode<T>* NextNode = Node->m_EdgeArray[i]->m_Node;
DFSRecursive(NextNode);
}
}
};
// Queue.h
#pragma once
#include <assert.h>
template <typename T>
class CQueueNode
{
template <typename T>
friend class CQueue;
private:
CQueueNode() :
m_Next(nullptr)
{
}
~CQueueNode()
{
}
private:
CQueueNode<T>* m_Next;
T m_Data;
};
template <typename T>
class CQueue
{
public:
CQueue()
{
m_FirstNode = nullptr;
m_LastNode = nullptr;
m_Size = 0;
}
~CQueue()
{
clear();
}
private:
CQueueNode<T>* m_FirstNode;
CQueueNode<T>* m_LastNode;
int m_Size;
public:
void push(const T& data)
{
CQueueNode<T>* Node = new CQueueNode<T>;
Node->m_Data = data;
// 기존에 추가된 가장 마지막노드의 다음노드로 새로 생성된 노드를 지정한다.
if (m_LastNode)
m_LastNode->m_Next = Node;
// 만약 처음 추가되는 노드라면 FirstNode를 새로 생성된 노드로 채워준다.
if (!m_FirstNode)
m_FirstNode = Node;
// 가장 마지막 노드를 새로 생성된 노드로 갱신한다.
m_LastNode = Node;
++m_Size;
}
T& front() const
{
if (empty())
assert(false);
return m_FirstNode->m_Data;
}
void pop()
{
if (empty())
assert(false);
CQueueNode<T>* Next = m_FirstNode->m_Next;
delete m_FirstNode;
m_FirstNode = Next;
if (!m_FirstNode)
m_LastNode = nullptr;
--m_Size;
}
int size() const
{
return m_Size;
}
bool empty() const
{
return m_Size == 0;
}
void clear()
{
while (m_FirstNode)
{
CQueueNode<T>* Next = m_FirstNode->m_Next;
delete m_FirstNode;
m_FirstNode = Next;
}
m_LastNode = nullptr;
m_Size = 0;
}
};
// Stack.h
#pragma once
#include <assert.h>
template <typename T>
class CStackNode
{
template <typename T>
friend class CStack;
private:
CStackNode() :
m_Next(nullptr)
{
}
~CStackNode()
{
}
private:
CStackNode<T>* m_Next;
T m_Data;
};
template <typename T>
class CStack
{
public:
CStack()
{
m_LastNode = nullptr;
m_Size = 0;
}
~CStack()
{
clear();
}
private:
// 가장 마지막에 추가된 노드의 주소를 담는다.
CStackNode<T>* m_LastNode;
int m_Size;
public:
void push(const T& data)
{
// 데이터를 저장하기 위한 노드를 생성한다.
CStackNode<T>* Node = new CStackNode<T>;
Node->m_Data = data;
// 새로 생성된 노드의 다음노드를 기존에 마지막에 추가된 노드로 지정해준다.
// 만약 처음 추가하는 노드라면 m_LastNode는 nullptr이 들어가 있으므로
// 새로생성된 노드의 다음으로는 nullptr이 지정될 것이다.
// 즉, 다음 노드가 nullptr이라면 더이상 노드가 없다는 의미이다.
Node->m_Next = m_LastNode;
// 마지막으로 추가된 노드를 갱신해주도록 한다.
m_LastNode = Node;
++m_Size;
}
T& top() const
{
if (empty())
assert(false);
return m_LastNode->m_Data;
}
void pop()
{
if (empty())
assert(false);
CStackNode<T>* Next = m_LastNode->m_Next;
delete m_LastNode;
m_LastNode = Next;
--m_Size;
}
int size() const
{
return m_Size;
}
bool empty() const
{
return m_Size == 0;
}
void clear()
{
while (m_LastNode)
{
CStackNode<T>* Next = m_LastNode->m_Next;
delete m_LastNode;
m_LastNode = Next;
}
m_Size = 0;
}
};
// main.cpp
#include <iostream>
#include "Graph.h"
int main()
{
CGraph<int> graph;
for (int i = 1; i < 10; ++i)
{
graph.insert(i);
}
// 각 노드들의 m_EdgeArray가 연결된 노드의 값 기준으로
// 정렬되어 있지 않도록 해도 작은값 순서대로 탐색하는지 보기
graph.AddEdge(7, 8);
graph.AddEdge(1, 7);
graph.AddEdge(2, 3);
graph.AddEdge(1, 2);
graph.AddEdge(1, 5);
graph.AddEdge(3, 4);
graph.AddEdge(5, 6);
graph.AddEdge(8, 9);
std::cout << "========= DFS =========" << std::endl;
graph.DFS();
graph.ResetVisit();
std::cout << "========= DFS Recursive=========" << std::endl;
graph.DFSRecursive();
graph.ResetVisit();
std::cout << "========= BFS =========" << std::endl;
graph.BFS();
return 0;
}
stack과 queue는 STL을 사용하지 않고 직접 구현했던 것을 사용해봤다. 한가지 눈에 띄는 점은 같은 DFS라도 스택을 이용해서 구현하느냐, 재귀 함수를 이용해서 구현하느냐에 따라 탐색 순서가 달라졌다(물론 두 순서 모두 유효한 탐색 순서로 탐색한다).
'공부 > Algorithm' 카테고리의 다른 글
JPS(Jump Point Search) Algorithm (0) | 2022.02.17 |
---|---|
Dijkstra algorithm(다익스트라 알고리즘) 구현 (0) | 2021.08.11 |
병합 정렬(Merge sort) (0) | 2021.08.10 |
마스터 정리(Master Method), Big-Oh, Theta, Omega Notation (0) | 2021.08.07 |
힙 정렬 (Heap Sort) 구현 (0) | 2021.08.07 |