现在的位置: 首页 > 综合 > 正文

图算法-接口及实现

2013年09月22日 ⁄ 综合 ⁄ 共 5299字 ⁄ 字号 评论关闭

许多计算应用程序不仅包含项集合,而且包含这些项对之间的连通集合。这些连通关系直接产生大量问题:跟随这些连通 ,能够从一个项到达另一个项吗?从已知项,可以到达其他多少个项?从此项到彼项的最佳路径是哪一条?

我们使用一种称作图(graph)的抽象对象来给这种情形建模。下面给出本文使用的研究图算法的基本ADT接口:

typedef struct{
 int v;
 int w;
} Edge;
Edge EDGE(int, int);

struct graph{
 int V;
 int E;
 int **adj;
};

typedef struct graph* Graph;
Graph GRAPHinit(int);
void  GRAPHinsert(Graph, Edge);
void  GRAPHremove(Graph, Edge);
int   GRAPHedges(Edge[], Graph G);
Graph GRAPHcopy(Graph);
void  GRAPHdestroy(Graph);

void  GRAPHshow(Graph G);
Graph GRAPHrand(int, int);
int ** MATRIXint(int, int, int);

接下来是他们的实现:

Edge EDGE(int v, int w)
{
 Edge edge;
 edge.v = v;
 edge.w = w;
 return edge;
}

Graph GRAPHinit(int V)
{
 Graph G = (Graph)malloc(sizeof *G);
 G->V = V;
 G->E = 0;
 G->adj = MATRIXint(V, V, 0);
 return G;
}

void GRAPHinsert(Graph G, Edge e)
{
 int v = e.v, w = e.w;
 if(G->adj[v][w] == 0)
  G->E++;
 G->adj[v][w] = 1;
 G->adj[w][v] = 1;
}

void GRAPHremove(Graph G, Edge e)
{
 int v = e.v, w = e.w;
 if(G->adj[v][w] == 1)
  G->E--;
 G->adj[v][w] = 0;
 G->adj[w][v] = 0;
}

/**
 * get the edges of the Graph
 */
int GRAPHedges(Edge a[], Graph G)
{
 int v, w, E = 0;
 for(v = 0; v < G->V; v++)
  for(w = v; w < G->V; w++)
   if(G->adj[v][w] == 1)
    a[E++] = EDGE(v, w);
 return E;
}

/**
 * create a matrix of integer
 * r - row of the matrix
 * c - column of the matrix
 * val - the value of the matrix
 */
int ** MATRIXint(int r, int c, int val)
{
 int i,j;
 int **t = (int **)malloc(r * sizeof(int *));
 for(i = 0; i<r; i++)
  t[i] = (int *)malloc(c*sizeof(int));
 for(i = 0; i<r; i++)
  for(j = 0; j<c; j++)
   t[i][j] = val;
 return t;
}

Graph GRAPHcopy(Graph G)
{
 int i,j;
 Graph g = (Graph)malloc(sizeof *G);
 g->V = G->V;
 g->E = G->E;
 g->adj = MATRIXint(G->V, G->V, 0);
 for(i = 0; i<G->V; i++)
  for(j = 0; j<G->V; j++)
   g->adj[i][j] = G->adj[i][j];
 return g;
}

/**
 * destroy the graph and free the memory, first free
 * the adj, then free the graph
 */
void GRAPHdestroy(Graph G)
{
 int i;
 for(i = 0; i<G->V; i++)
  free(G->adj[i]);
 free(G);
}

void GRAPHshow(Graph G)
{
 int i,j;
 printf("%d vertices, %d edges/n", G->V, G->E);
 for(i = 0; i < G->V; i++)
 {
  printf("%2d: ", i);
  for(j = 0; j < G->V; j++)
   if(G->adj[i][j] == 1)
    printf(" %2d", j);
  printf("/n");
 }
}

/**
 * generate a random graph which has V vertices and E edges
 */
Graph GRAPHrand(int V, int E)
{
 // create a graph
 Graph g = GRAPHinit(V);
 while(g->E < E)
 {
  Edge e;
  e.v = rand() % V;
  e.w = rand() % V;
  GRAPHinsert(g, e);
 }
 return g;
}

当然这里面没有涉及到遍历图之类的算法,不过这是基本的东西嘛。然后我们可以编写图形化程序,将图这种抽象结构以图形的方式展现出来。下面先写一个窗口程序,其中g为Graph类型,pt为POINT结构指针,为了在WinMain主函数和WndProc之间传递,将它们声明为全局变量就可以了:
Graph g;
POINT *pt;

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

/**
 * entry of the program
 */
int APIENTRY WinMain(HINSTANCE hInstance,
      HINSTANCE hPrevInstance,
      LPSTR    lpCmdLine,
      int       nCmdShow)
{
 // do some initialize work.
 g = GRAPHrand(25, 50);
 pt = (POINT *)malloc(sizeof(*pt)*(g->V));

 // create window struct.
 WNDCLASSEX wndclass;
 wndclass.cbClsExtra = 0;
 wndclass.cbSize = sizeof(wndclass);
 wndclass.cbWndExtra = 0;
 wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
 wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
 wndclass.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hIconSm = NULL;
 wndclass.hInstance = hInstance;
 wndclass.lpfnWndProc = WndProc;
 wndclass.lpszClassName = "MainFrame";
 wndclass.lpszMenuName = NULL;
 wndclass.style = CS_HREDRAW | CS_VREDRAW;

 // register class
 ::RegisterClassEx(&wndclass);

 // create window
 HWND hwnd = ::CreateWindowEx(0,
         "MainFrame",
         "My Program",
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, // x
         CW_USEDEFAULT, // y
         CW_USEDEFAULT, // nWidth
         CW_USEDEFAULT, // nHeight
         NULL,
         NULL,
         hInstance,
         NULL);
 
 if(hwnd == NULL)
 {
  ::MessageBox(NULL, "CreateWindowEx failed", "Error", MB_OK);
  return -1;
 }
 // generate a random graph
 RandVertices(g, pt, hwnd);

 // show application window
 ::ShowWindow(hwnd, nCmdShow);
 ::UpdateWindow(hwnd);

 // message loop
 MSG msg;
 while(::GetMessage(&msg, NULL, 0, 0))
 {
  ::TranslateMessage(&msg);
  ::DispatchMessage(&msg);
 }

 return msg.wParam;
}

/**
 * Window Precedure
 */
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 switch(message)
 {
 case WM_PAINT:  
  {
   HDC hdc;
   PAINTSTRUCT ps;
   hdc = BeginPaint(hwnd, &ps);
   // make the vertices filled
   SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH));
   DrawEdges(g, pt, hwnd, hdc);
   DrawVertices(g, pt, hwnd, ps.hdc);
   EndPaint(hwnd, &ps);
   break;
  }
 case WM_DESTROY:
  ::PostQuitMessage(0);
  break;
 }
 return ::DefWindowProc(hwnd, message, wParam, lParam);
}

RandVertices()为每个顶点随即安排一个坐标,这个程序中DrawEdges(),DrawVertices()是我们图抽象数据类型与图形化显示的桥梁。在此我们用它来连接抽象和具体的东西,它的实现如下:

void DrawEdges(Graph g, POINT pt[], HWND hwnd, HDC hdc)
{
 int i;
 Edge *edge = (Edge *)malloc(sizeof(*edge)*(g->E)); 

 // get the edges
 GRAPHedges(edge, g);

 // draw the edge
 for(i = 0; i < g->E; i++)
 {
  MoveToEx(hdc, pt[edge[i].v].x, pt[edge[i].v].y, NULL);
  LineTo(hdc, pt[edge[i].w].x, pt[edge[i].w].y);
 }
 free(edge);
}

void DrawVertices(Graph g, POINT pt[], HWND hwnd, HDC hdc)
{
 int i;
 // draw the vertices of the graph 
 for(i = 0; i < g->V; i++)
  Ellipse(hdc, pt[i].x-3, pt[i].y-3, pt[i].x+3, pt[i].y+3);
}

/**
 * set the position of the vertices in the client
 * remark - the size of p[] must equal to the number of the vertices in
 *          graph g
 */
void RandVertices(Graph g, POINT pt[], HWND hwnd)
{
 int i;
 int cxClient, cyClient;
 RECT rt;

 ::GetClientRect(hwnd, &rt);
 // get the width and height of the client
 cxClient = rt.right - rt.left;
 cyClient = rt.bottom - rt.top;

 // randomly set x and y for every vertices
 for(i = 0; i < g->V; i++)
 {
  pt[i].x = abs(rand())%cxClient;
  pt[i].y = abs(rand())%cyClient;
 }
}

程序很简单,但可以进行大量的扩展,你可以对其修改,让生成的顶点位置由你来安排,选择绘制出定点,搜索图中的回路,寻找一个定点到另一个定点的路径等等,然后在显示到你的界面上,相信会很有意思的,呵呵。

下面是我的一个25个顶点,50条边的随机生成图,相信经过你更好的设计会生成更加复杂的图,如果能图形化解决实际的问题,那么就更能为那些不理解算法的人讲述一个形象的方案了:

 

抱歉!评论已关闭.