8.3.3 프림 알고리즘 구현

이제 프림 알고리즘을 구현해 보아요.

프림 알고리즘에서는 최소 비용의 정점을 선택하는 내부 알고리즘이 필요해요.

프림 알고리즘에서 정점을 선택해 나갈 때 현재까지 선택한 정점에서 갈 수 있는 정점 목록에서 최소 비용의 정점을 선택해야겠죠.

이를 위해 다음과 같은 논리가 필요해요.

정점선택알고리즘(mstree:최소신장트리,graph:원본 그래프)

selectedge:= NULL으로 초기화

seek:= graph의 시작 정점 위치

end:= graph의 마지막 정점 위치

반복(seek가 end가 아닐 때)

    edge:=seek에 있는 간선

조건(edge가 mstree에 선택한 정점을 하나만 포함하고 있을 때)

조건(selectedge가 있다면)

조건(selectedge의 비중이 edge의 비중보다 크면)

                selectedge:= edge

아니면

            selectedge:= edge

mstree에 selectedge의 끝 정점을 추가

mstree에 selectedge를 추가

이제 프림알고리즘을 구현해 보아요.

void SelectVertex(Graph *mstree,Graph *origin);
Graph *Prim(Graph *origin)
{
    Graph *mstree = 0;

최소신장트리를 만들 빈 그래프를 생성합니다.

    mstree = New_Graph();

원본 그래프의 맨 앞의 정점을 최소신장트리에 추가합니다.

    Graph_AddVertex(mstree,Graph_GetFront(origin));

최소신장트리에 정점 개수가 원본 그래프의 정점 개수보다 작다면 정점을 선택하는 것을 반복합니다.

    while(Graph_GetVtCnt(mstree)<Graph_GetVtCnt(origin))
    {

최적의 정점을 선택하여 최소신장트리에 추가하는 함수는 별도로 구현합시다.

        SelectVertex(mstree,origin);
    }
    return mstree;
}

다음은 최적의 정점을 선택하는 함수입니다.

int IsOnlyOneInclude(Edge *edge, Array *selected);
void SelectVertex(Graph *mstree,Graph *origin)
{

최소신장트리에 선택한 정점 배열과 원본 그래프의 간선 집합을 참조합시다.

    Array *selected = mstree->vertexs;
    Array *ori_edges = origin->edges;

선택한 간선을 참조할 변수를 초기화합니다.

    Edge *selectedge=0;

원본 그래프의 간선 집합의 시작 간선 위치와 마지막 간선 위치를 구합니다.

    Iterator seek = 0, end = 0;
    Edge *edge = 0;
    seek = Array_Begin(ori_edges);
    end = Array_End(ori_edges);

간선의 위치를 이동하면서 다음을 반복합니다.

    for(seek = seek; seek != end; ++seek)
    {

현재 위치의 간선을 참조합니다.

        edge = (Edge *)(*seek);

만약 선택한 정점이 간선에 하나만 포함하고 있다면 선택 후보 간선입니다.

        if(IsOnlyOneInclude(edge,selected))
        {

만약 이미 선택한 후보 간선이 있는지 확인합니다.

            if(selectedge)
            {

있다면 선택한 후보 간선의 비중이 현재 간선의 비중보다 크면 선택한 후보 간선을 바꿉니다.

                if(selectedge->weight>edge->weight)
                {
                    selectedge = edge;
                }
            }
            else
            {

만약 선택한 후보 간선이 없을 때는 현재 간선을 후보 간선으로 설정합니다.

                selectedge = edge;
            }
        }
    }

최소신장트리에 간선의 끝점을 추가합니다. 여기에서 두 끝점을 추가하지만 함수 내부에서는 같은 정점은 추가하지 않도록 구현하였으므로 선택하지 않은 새 정점만 추가하므로 문제가 없습니다.

    Graph_AddVertex(mstree,selectedge->ep1);
    Graph_AddVertex(mstree,selectedge->ep2);

최소신장트리에 간선을 추가합니다.

    Graph_AddEdge(mstree,selectedge->ep1,selectedge->ep2,selectedge->weight);
}

다음은 간선의 끝점이 선택한 정점을 하나만 포함하고 있는지 확인하는 함수입니다.

int IsOnlyOneInclude(Edge *edge, Array *selected)
{
    Iterator seek = 0, end = 0;
    int cnt = 0;
    Vertex vt;

정점의 시작 위치와 끝 위치를 구합니다.

    seek = Array_Begin(selected);
    end = Array_End(selected);

정점의 위치를 끝까지 이동하면서 다음을 반복합니다.

    for(seek = seek; seek != end; ++seek)
    {

현재 위치의 정점을 구합니다.

        vt = (Vertex)(*seek);

만약 간선에 현재 위치의 정점이 있다면 cnt 변수를 1 증가합니다.

        if(Edge_Include(edge,vt))
        {
            cnt++;
        }
    }

반복문을 탈출하였을 때 cnt값이 1이면 선택한 정점 중에 하나만 포함하고 있는 간선입니다.

    return cnt==1;
}