一.概念

无向图:G = (V ,  E)(Vertex顶点,Edge边)

无向完全图:任意2个节点相连      Cn2条 = n(n-1) / 2

连通图:任意两个节点之间有路径课通。

极大连通子图:G` = (V` , E`)   V` € V , E` € E,  G` € G 

极大连通分量:几个极大连通子图

度:最大为n-1

边:(v1,v5) (v5,v1) 依附于顶点v1,v5   是同一个意思

有向图:G = <V , E>   <顶点,弧>   弧包括弧头和弧尾 

有向完全图;任意两个顶点之间都要两条弧    An2   n(n-1)

度:入度(被指向) , 出度(指出去)  二者相加为度

n个顶点的无向图  ,边数多少可能为连通图,   n-1

n个顶点的无向图  ,边数多少一定为连通图,  (n-1)(n-2)/2+1

二.图的创建:

1.邻接矩阵   无向图关于对角线对称  

2.邻接接表

边多用矩阵,边少用邻接链表

矩阵唯一,链表不唯一(无序)

代码:对于二维数组的申请,二维数组本质上是以为数组,所以我们申请一个长度为v*v的以为数组,通过下标来定位行和列。

或者申请一个装着指针,长度为v的一维数组,再给每一个指针申请长度为v的int型数组。这样做可以实现二维数组的功能。但是,每一行的地址不是连续的,不是真正意义上的二维数组。

typedef struct node
{
    int nVertex;
    int nEdge;
    int *p;
}Graph;
Graph* create()
{
    /*int **arr =(int**) malloc(sizeof(int*)*n);
    for(int i = 0 ; i < n; i++)
        arr[i] = (int*)malloc(sizeof(int)*n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            arr[i][j] = 0;*/
    Graph* pGraph = (Graph*)malloc(sizeof(Graph));
    int v,e;
    printf("请输入图的顶点和边数\n");
    scanf("%d%d",&v,&e);
    pGraph->nVertex = v;
    pGraph->nEdge = e;
    pGraph->p =(int*) malloc(sizeof(int)*v*v);
    memset(pGraph->p,0,sizeof(int)*v*v);
    int v1,v2;
    for(int i = 0; i < e;i++)
    {
        printf("请输入两个顶点确定一条边\n");
        scanf("%d%d",&v1,&v2);
        if(v1>=1 && v2>=1 && v1<=v && v2<=v && v1!=v2 && pGraph->p[(v1-1)*v+v2-1] == 0)
        {
            pGraph->p[(v1-1)*v+(v2-1)] = 1;
            pGraph->p[(v2-1)*v+(v1-1)] = 1;
        }
        else
        {
            i--;
        }
    }

    return pGraph;
}

 

三.图的遍历:

深度优先遍历DFS:

测试数据:6 9   1 2 ; 1 3 ;1 4;2 3; 2 4;2 5;3 4;3 6;4 5;

则矩阵为: 

      0  1  1  1  0  0
      1  0  1  1  1  0
      1  1  0  1  0  1
      1  1  1  0  1  0
      0  1  0  1  0  0
      0  0  1  0  0  0

思路:假设从顶点1开始,

    第一行:v2为1且没有标记过,则跳转至第二行。

    第二行:v1标记过,v3为1且没被标记过,则跳至第三行。

    第三行:v1,v2为1但被标记过,v4为1且没被标记过,则跳转至第4行。

    第四行:v1,v2,v3为1但被标记过,v5为1且没被标记过,则跳转至第5行。

    第五行:v2,v4为1但被标记过。查看第五行是由第四行跳转来的,则返回原来位置继续查找。

    第四行:第四行没有能跳转的节点,则返回地三行的原来位置。

    第三行:v6为1且没被标记过,则跳转至第6行。

    第六行:v3为1但被标记过。则返回第三行的原来位置。

    第三行:没有节点跳转。则返回第二行原来位置。

    第二行:没有v4,v5为1且被标记过。则返回第一行原来的位置。

    第一行:v3,v4为1但被标记过。结束。

所以要申请额外的标记数组:

void DFS(Graph* pGraph,int n,int* arr)
{
    printf("%d ",n);
    arr[n] = 1;
    for(int i = 1; i <= pGraph->nVertex; i++)
    {
        if(pGraph->p[(n-1)*pGraph->nVertex+i-1] == 1 && arr[i] == 0)
        {
            DFS(pGraph,i,arr);
        }
    }
}
void MyDFS(Graph* pGraph,int n)
{
    int* arr = (int*)malloc(sizeof(int) * (pGraph->nVertex));
    memset(arr,0,sizeof(arr));
    DFS(pGraph,n,arr);
}

广度优先遍历BFS:

思路:与树的BFS十分相似,需要借助辅助队列,

  假设从第一个节点开始,则将第一行为1的节点标记后,压入队列。然后再将队列中的第一个元素所在行,为1的节点标记后压入队列。

void BFS(Graph* pGraph,int begin)
{
    Queue* myqueue = Init();
    int* arr = (int*) malloc(sizeof(int)*pGraph->nVertex);
    memset(arr,0,sizeof(arr)*pGraph->nVertex);
    arr[begin-1] =1;
    Push(myqueue,begin);
    while(!IsEmpty(myqueue))
    {
        begin = Pop(myqueue);
        printf("%d ",begin);
        for(int i = 0; i < pGraph->nVertex; i++)
        {
            if(pGraph->p[(begin-1)*pGraph->nVertex + i] == 1 && arr[i] == 0 )
            {
                arr[i] = 1;
                Push(myqueue,i+1);
            }
        }
    }
}

 

posted @ 2018-05-24 09:28  Lune-Qiu  阅读(219)  评论(0编辑  收藏  举报