ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인접 리스트를 통한 그래프 구현
    프로그래밍 기초/자료구조 2021. 5. 3. 09:37

      인접 리스트 통한 그래프 구현 

    그래프 구현/인접 리스트

    인접 리스트는 각각의 노드를 하나의 포인터로 하여 특정 노드에서 이동 가능한 노드를 표현하는 방법으로 다음과 같이 표현된다.

     

    구현이 필요한 메서드 및 속성은 다음과 같다.

    • 생성자
      • ArrayListGraph() 비어있는는 인스턴스 생성
    • 속성 
      • NodeCount 현재 노드의 개수
    • 메서드 
      • InsertNode() 노드 추가
      • InsertEdge(int, int) 앞의 노드에서 뒷 노드로 이동하는 에지 추가
      • RemoveNode(int) 지정된 인덱스의 노드 제거
      • RemoveEdge(int, int) 앞의 노드에서 뒷 노드로 이동하는 제거
      • Clear() 저장되어 있는 모든 데이터 삭제
      • GetDegreeIn(int) 노드의 진입 차수 반환
      • GetDegreeOut(int) 노드의 진출 차수 반환

     

    구현 코드

    ArrayListGraph.h

    #pragma once
    #include <iostream>
    
    struct GraphNode
    {
    	GraphNode(int value)
    		: m_idx(value), m_next(nullptr)
    	{
    	}
    
    	int m_idx;
    	GraphNode* m_next;
    };
    
    struct GraphHeader
    {
    	GraphHeader(int value)
    		: m_idx(value), m_data(nullptr), m_next(nullptr)
    	{
    	}
    
    	int m_idx;
    	GraphNode* m_data;
    	GraphHeader* m_next;
    };
    
    class ArrayListGraph
    {
    public:
    #pragma region 생성자
    	ArrayListGraph();
    	~ArrayListGraph();
    #pragma endregion
    
    #pragma region 속성
    	size_t NodeCount() { return m_nodeCount; }
    #pragma endregion
    
    #pragma region 메서드
    	void InsertNode(int num);
    	void InsertEdge(int from, int to);
    
    	void RemoveNode(int num);
    	void RemoveEdge(int from, int to);
    	void Clear();
    
    	size_t GetDegreeIn(int num);
    	size_t GetDegreeOut(int num);
    
    	bool ContainsNode(int num);
    	bool ContainsEdge(int from, int to);
    
    	GraphHeader* GetTargetHeader(int num);
    
    	void PrintInfo();
    #pragma endregion
    
    private:
    #pragma region Class Util
    	GraphHeader* PopHeader(int value);
    	void PushHeader(GraphHeader* header);
    
    	GraphNode* PopNode(int value);
    	void PushNode(GraphNode* node);
    #pragma endregion
    
    #pragma region 변수
    	size_t m_nodeCount;
    
    	GraphHeader* m_graphHeaders;
    	GraphHeader* m_freeHeaders;
    
    	GraphNode* m_freeNodes;
    #pragma endregion
    };

     

    ArrayListGraph.cpp

    #include "ArrayListGraph.h"
    
    #pragma region 생성자
    /// <summary>
    /// 지정된 크기 혹은 기본 크기의 ArrayListGraph를 생성한다.
    /// </summary>
    ArrayListGraph::ArrayListGraph()
    	: m_nodeCount(0), m_graphHeaders(nullptr), m_freeHeaders(nullptr), m_freeNodes(nullptr)
    {
    }
    
    /// <summary>
    /// 메모리 누수를 막기 위해 동적 생성한 노드들을 제거한다.
    /// </summary>
    ArrayListGraph::~ArrayListGraph()
    {
    	while (m_freeNodes != nullptr)
    	{
    		GraphNode* curNode{ m_freeNodes };
    		m_freeNodes = m_freeNodes->m_next;
    		delete curNode;
    	}
    
    	while (m_graphHeaders != nullptr)
    	{
    		GraphHeader* curHead{ m_graphHeaders };
    		m_graphHeaders = m_graphHeaders->m_next;
    
    		while (curHead->m_data != nullptr)
    		{
    			GraphNode* curNode{ curHead->m_data };
    			curHead->m_data = curNode->m_next;
    			delete curNode;
    		}
    
    		delete curHead;
    	}
    
    	while (m_freeHeaders != nullptr)
    	{
    		GraphHeader* curHead{ m_freeHeaders };
    		m_freeHeaders = m_freeHeaders->m_next;
    		delete curHead;
    	}
    }
    #pragma endregion
    
    #pragma region 메서드
    /// <summary>
    /// ArrayListGraph에 노드를 추가한다.
    /// </summary>
    void ArrayListGraph::InsertNode(int num)
    {
    	if (ContainsNode(num))
    	{
    		return;
    	}
    
    	GraphHeader* newHeader{ PopHeader(num) };
    	newHeader->m_next = m_graphHeaders;
    	m_graphHeaders = newHeader;
    
    	m_nodeCount++;
    }
    
    /// <summary>
    /// ArrayListGraph에 from -> to 인 간선을 추가한다.
    /// </summary>
    /// <param name="from">시작 노드</param>
    /// <param name="to">끝 노드</param>
    void ArrayListGraph::InsertEdge(int from, int to)
    {
    	if (ContainsEdge(from, to))
    	{
    		return;
    	}
    
    	GraphHeader* fromHeader{ GetTargetHeader(from) };
    	GraphNode* newNode{ PopNode(to) };
    	newNode->m_next = fromHeader->m_data;
    	fromHeader->m_data = newNode;
    }
    
    /// <summary>
    /// ArrayListGraph에서 지정된 인덱스의 노드를 제거한다.
    /// </summary>
    /// <param name="value">제거할 값</param>
    void ArrayListGraph::RemoveNode(int num)
    {
    	if (!ContainsNode(num))
    	{
    		std::cout << "존재하지 않는 노드를 제거하려 하고 있습니다.\n";
    		return;
    	}
    
    	GraphHeader* curHeader{ m_graphHeaders };
    
    	while (curHeader != nullptr)
    	{
    		if (ContainsEdge(curHeader->m_idx, num))
    		{
    			RemoveEdge(curHeader->m_idx, num);
    		}
    
    		curHeader = curHeader->m_next;
    	}
    
    	GraphHeader* prevHeader{ nullptr };
    	curHeader = m_graphHeaders;
    
    	while (curHeader != nullptr)
    	{
    		if (curHeader->m_idx == num)
    		{
    			break;
    		}
    
    		prevHeader = curHeader;
    		curHeader = curHeader->m_next;
    	}
    
    	if (prevHeader == nullptr)
    	{
    		m_graphHeaders = curHeader->m_next;
    	}
    	else
    	{
    		prevHeader->m_next = curHeader->m_next;
    	}
    
    	PushHeader(curHeader);
    
    	m_nodeCount--;
    }
    
    /// <summary>
    /// ArrayListGraph에서 from -> to 인 간선을 제거한다.
    /// </summary>
    /// <param name="from">시작 노드</param>
    /// <param name="to">끝 노드</param>
    void ArrayListGraph::RemoveEdge(int from, int to)
    {
    	if (!ContainsNode(from) || !ContainsNode(to))
    	{
    		std::cout << "존재하지 않는 에지를 제거하려 하고 있습니다.\n";
    		return;
    	}
    
    	GraphHeader* targetHeader{ GetTargetHeader(from) };
    
    	GraphNode* prevNode{ nullptr };
    	GraphNode* curNode{ targetHeader->m_data };
    
    	while (curNode != nullptr)
    	{
    		if (curNode->m_idx == to)
    		{
    			break;
    		}
    
    		prevNode = curNode;
    		curNode = curNode->m_next;
    	}
    
    	if (prevNode == nullptr)
    	{
    		targetHeader->m_data = curNode->m_next;
    	}
    	else
    	{
    		prevNode->m_next = curNode->m_next;
    	}
    
    	PushNode(curNode);
    }
    
    /// <summary>
    /// ArrayListGraph의 모든 노드 및 간선을 제거한다.
    /// </summary>
    void ArrayListGraph::Clear()
    {
    	while (m_graphHeaders != nullptr)
    	{
    		GraphHeader* curHead{ m_graphHeaders };
    		m_graphHeaders = m_graphHeaders->m_next;
    		PushHeader(curHead);
    	}
    
    	m_nodeCount = 0;
    }
    
    /// <summary>
    /// 지정된 인덱스의 노드에 진입하는 차수를 반환한다.
    /// </summary>
    /// <param name="num">노드의 번호</param>
    size_t ArrayListGraph::GetDegreeIn(int num)
    {
    	if (!ContainsNode(num))
    	{
    		return -1;
    	}
    
    	int count{ 0 };
    	GraphHeader* curHead{ m_graphHeaders };
    
    	while (curHead != nullptr)
    	{
    		GraphNode* curNode{ curHead->m_data };
    		while (curNode != nullptr)
    		{
    			if (curNode->m_idx == num)
    			{
    				count++;
    			}
    			curNode = curNode->m_next;
    		}
    		curHead = curHead->m_next;
    	}
    
    	return count;
    }
    
    /// <summary>
    /// 지정된 인덱스의 노드에서 진출하는 차수를 반환한다.
    /// </summary>
    /// <param name="num">노드의 번호</param>
    size_t ArrayListGraph::GetDegreeOut(int num)
    {
    	GraphHeader* targetHeader{ GetTargetHeader(num) };
    
    	if (targetHeader == nullptr)
    	{
    		return -1;
    	}
    	
    	int count{ 0 };
    	GraphNode* targetNode = targetHeader->m_data;
    
    	while (targetNode != nullptr)
    	{
    		targetNode = targetNode->m_next;
    		count++;
    	}
    
    	return count;
    }
    
    /// <summary>
    /// 그래프에 지정된 숫자의 노드가 존재하는지 검사
    /// </summary>
    /// <param name="num">확인할 번호</param>
    /// <returns>노드 존재 여부</returns>
    bool ArrayListGraph::ContainsNode(int num)
    {
    	GraphHeader* curHead{ m_graphHeaders };
    	while (curHead != nullptr)
    	{
    		if (curHead->m_idx == num)
    		{
    			return true;
    		}
    		curHead = curHead->m_next;
    	}
    
    	return false;
    }
    
    /// <summary>
    /// 그래프에 시작 노드에서 끝 노드로의 에지가 존재하는지 검사
    /// </summary>
    /// <param name="from">시작 노드 번호</param>
    /// <param name="to">끝 노드 번호</param>
    /// <returns>에지 존재 여부</returns>
    bool ArrayListGraph::ContainsEdge(int from, int to)
    {
    	if (!ContainsNode(from) || !ContainsNode(to))
    	{
    		std::cout << "존재하지 않는 노드를 추가하려 하고 있습니다.\n";
    		return true;
    	}
    
    	GraphHeader* targetHeader{ GetTargetHeader(from) };
    	GraphNode* targetNode{ targetHeader->m_data };
    	while (targetNode != nullptr)
    	{
    		if (targetNode->m_idx == to)
    		{
    			return true;
    		}
    		targetNode = targetNode->m_next;
    	}
    
    	return false;
    }
    
    /// <summary>
    /// 지정된 번호의 헤더가 존재하는 경우 헤더 반환
    /// </summary>
    /// <param name="num">노드 번호</param>
    /// <returns>해당 번호의 노드(없으면 nullptr)</returns>
    GraphHeader* ArrayListGraph::GetTargetHeader(int num)
    {
    	GraphHeader* target{ m_graphHeaders };
    	while (target != nullptr)
    	{
    		if (target->m_idx == num)
    		{
    			break;
    		}
    		target = target->m_next;
    	}
    
    	return target;
    }
    
    /// <summary>
    /// 테스트용 리스트 정보 출력 함수
    /// </summary>
    void ArrayListGraph::PrintInfo()
    {
    	std::cout << "----------------------\n";
    	std::cout << "Count: " << m_nodeCount << '\n';
    	GraphHeader* curHeader{ m_graphHeaders };
    	while (curHeader != nullptr)
    	{
    		std::cout << curHeader->m_idx << " : ";
    		if (curHeader->m_data == nullptr)
    		{
    			std::cout << "NULL\n";
    		}
    		else
    		{
    			GraphNode* curNode{ curHeader->m_data };
    			while (curNode != nullptr)
    			{
    				std::cout << curNode->m_idx << ' ';
    				curNode = curNode->m_next;
    			}
    			std::cout << '\n';
    		}
    		curHeader = curHeader->m_next;
    	}
    	std::cout << "----------------------\n\n";
    }
    #pragma endregion
    
    #pragma region Class Util
    /// <summary>
    /// 자유 공간 리스트에서 헤더를 가져오거나 새로 생성한다.
    /// </summary>
    /// <param name="value">헤더 생성 시 인덱스 값</param>
    /// <returns>새 헤더</returns>
    GraphHeader* ArrayListGraph::PopHeader(int value)
    {
    	GraphHeader* newNode{ nullptr };
    	if (m_freeHeaders == nullptr)
    	{
    		newNode = new GraphHeader(value);
    	}
    	else
    	{
    		newNode = m_freeHeaders;
    		m_freeHeaders = m_freeHeaders->m_next;
    
    		newNode->m_idx = value;
    		newNode->m_data = nullptr;
    		newNode->m_next = nullptr;
    	}
    	return newNode;
    }
    
    /// <summary>
    /// 자유 공간 리스트에 제거된 헤더를 저장한다.
    /// </summary>
    /// <param name="node">제거된 헤더</param>
    void ArrayListGraph::PushHeader(GraphHeader* header)
    {
    	while (header->m_data != nullptr)
    	{
    		GraphNode* curNode{ header->m_data };
    		header->m_data = curNode->m_next;
    		PushNode(curNode);
    	}
    
    	header->m_next = m_freeHeaders;
    	m_freeHeaders = header;
    }
    
    /// <summary>
    /// 자유 공간 리스트에서 노드를 가져오거나 새로 생성한다.
    /// </summary>
    /// <param name="value">노드 생성시 초기값</param>
    /// <returns>새 노드</returns>
    GraphNode* ArrayListGraph::PopNode(int value)
    {
    	GraphNode* newNode{ nullptr };
    	if (m_freeNodes == nullptr)
    	{
    		newNode = new GraphNode(value);
    	}
    	else
    	{
    		newNode = m_freeNodes;
    		m_freeNodes = m_freeNodes->m_next;
    
    		newNode->m_idx = value;
    		newNode->m_next = nullptr;
    	}
    	return newNode;
    }
    
    /// <summary>
    /// 자유 공간 리스트에 제거된 노드를 저장한다.
    /// </summary>
    /// <param name="node">제거된 노드</param>
    void ArrayListGraph::PushNode(GraphNode* node)
    {
    	node->m_next = m_freeNodes;
    	m_freeNodes = node;
    }
    #pragma endregion

     

    테스트 코드

    main.cpp

    #include <iostream>
    #include "Graph/ArrayListGraph.h"
    
    // 생성한 자료구조 테스트용 메인
    int main()
    {
    	ArrayListGraph graph;
    	graph.InsertNode(1);
    	graph.InsertNode(2);
    	graph.InsertNode(3);
    	graph.InsertNode(4);
    
    	graph.InsertEdge(1, 2);
    	graph.InsertEdge(1, 4);
    	graph.InsertEdge(2, 3);
    	graph.InsertEdge(2, 4);
    	graph.InsertEdge(3, 4);
    
    	graph.PrintInfo();
    
    	std::cout << graph.GetDegreeIn(1) << ' ' << graph.GetDegreeOut(1) << '\n';
    
    	graph.RemoveEdge(1, 4);
    
    	graph.PrintInfo();
    
    	std::cout << graph.GetDegreeIn(1) << ' ' << graph.GetDegreeOut(1) << '\n';
    
    	graph.RemoveNode(2);
    
    	graph.PrintInfo();
    }

     

    실행 결과

    ----------------------
    Count: 4
    4 : NULL
    3 : 4
    2 : 4 3
    1 : 4 2
    ----------------------
    
    0 2
    ----------------------
    Count: 4
    4 : NULL
    3 : 4
    2 : 4 3
    1 : 2
    ----------------------
    
    0 1
    ----------------------
    Count: 3
    4 : NULL
    3 : 4
    1 : NULL
    ----------------------

     

    댓글

Designed by Tistory.