数据结构---图

1. 实现有向图、无向图的邻接矩阵和邻接表表示方法
1.1 邻接矩阵表示无向图
#define  isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))

//邻接矩阵表示无向图

typedef struct _graph{
    char vexs[100];      //顶点表
    int matrix[100][100];   //邻接矩阵
    int vexnum,edgnum;    //顶点的个数和边的个数
}Graph,*PGraph;

//创建图
Graph* create_graph()
{
    char c1,c2;
    int v,e;
    int i,p1,p2;
    Graph* pG;
    
    //输入顶点数和边数
    printf("input vertex number: ");
    scanf("%d",&v);
    printf("input edge number: ");
    scanf("%d",&e);
    if(v<1 || e<1 || (e>(v*(v-1))))
    {
        printf("input error \n");
        return NULL;
    }
    
    if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
    memset(pG,0,sizeof(Graph));
    
    //初始化顶点数和边数
    pG->vexnum=v;
    pG->edgnum=e;
    
    //初始化顶点
    for(i=0;i<pG->vexnum;i++)
    {
        printf("vertex(%d): ",i);
        pG->vexs[i]=read_char;
    }
    
    //初始化边
    for(i=0;i<pG->edgnum;i++)
    {
        //读取边的起始顶点和结束顶点
        printf("edge(%d): ",i);
        c1=read_char();
        c2=read_char();
        
        p1=get_position(*pG,c1);
        p2=get_position(*pG,c2);
        if(p1==-1 || p2==-1)
        {
            printf("input error \n");
            free(pG);
            return NULL;
        }
        
        pG->matrix[p1][p2]=1;
        pG->matrix[p2][p1]=1;
    }
    return pG;
}

//返回ch在matrix矩阵中的位置

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

//读取一个输入字符
static char read_char()
{
    char ch;
    do{
        ch=getchar();
    }while(!isLetter(ch);
    
    return ch;
}
1.2 邻接矩阵表示有向图
#define  isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))

//邻接矩阵表示有向图

typedef struct _graph{
    char vexs[100];      //顶点表
    int matrix[100][100];   //邻接矩阵
    int vexnum,edgnum;    //顶点的个数和边的个数
}Graph,*PGraph;

//创建图
Graph* create_graph()
{
    char c1,c2;
    int v,e;
    int i,p1,p2;
    Graph* pG;
    
    //输入顶点数和边数
    printf("input vertex number: ");
    scanf("%d",&v);
    printf("input edge number: ");
    scanf("%d",&e);
    if(v<1 || e<1 || (e>(v*(v-1))))
    {
        printf("input error \n");
        return NULL;
    }
    
    if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
    memset(pG,0,sizeof(Graph));
    
    //初始化顶点数和边数
    pG->vexnum=v;
    pG->edgnum=e;
    
    //初始化顶点
    for(i=0;i<pG->vexnum;i++)
    {
        printf("vertex(%d): ",i);
        pG->vexs[i]=read_char;
    }
    
    //初始化边
    for(i=0;i<pG->edgnum;i++)
    {
        //读取边的起始顶点和结束顶点
        printf("edge(%d): ",i);
        c1=read_char();
        c2=read_char();
        
        p1=get_position(*pG,c1);
        p2=get_position(*pG,c2);
        if(p1==-1 || p2==-1)
        {
            printf("input error \n");
            free(pG);
            return NULL;
        }
        
        pG->matrix[p1][p2]=1;
        // pG->matrix[p2][p1]=1;     //区别
    }
    return pG;
}

//返回ch在matrix矩阵中的位置

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

//读取一个输入字符
static char read_char()
{
    char ch;
    do{
        ch=getchar();
    }while(!isLetter(ch);
    
    return ch;
}
1.3 邻接表表示无向图
//无向图
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))

//邻接表中对应的链表的顶点
typedef struct _ENode
{
    int ivex;                       //该边所指向的顶点的位置
    struct _ENode *next_edge;       //指向下一条弧的指针
}ENode,*PENode;

//邻接表中的顶点
typedef struct _VNode
{
    char data;
    ENode *first_edge;   //指向第一条依附该顶点的弧
}VNode;

//邻接表
typedef struct _LGraph
{
    int vexnum;                  //图的顶点的数目
    int  edgnum;                //图的边的数目
    VNode vexs[MAX];
}LGraph;

//返回ch在matrix矩阵中的位置

static int get_position(LGraph g, char ch)
{
    int i;
    for(i=0; i<g.vexnum; i++)
        if(g.vexs[i].data==ch)
            return i;
    return -1;
}

//读取一个字符
static char read_char()
{
    char ch;

    do {
        ch = getchar();
    } while(!isLetter(ch));

    return ch;
}

//将node链接到list的末尾
static void link_last(ENode *list,ENode *node)
{
    ENode *p=list;
    
    while(p->next_edge)
        p=p->next_edge;
    p->next_edge=node;
}


//创建邻接表
LGraph* create_lgraph()
{
    char c1,c2;
    int v,e;
    int i,p1,p2;
    ENode *node1,*node2;
    LGraph* pG;
    
    //输入顶点数和边数
    printf("input vertex number: ");
    scanf("%d",&v);
    printf("input edge number: ");
    scanf("%d",&e);
    if ( v < 1 || e < 1 || (e > (v * (v-1))))
    {
        printf("input error\n");
        return NULL;
    }
    
    if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )
        return NULL;
    memset(pG, 0, sizeof(LGraph));

    //初始化顶点数和边数
    pG->vexnum=v;
    pG->edgnum=e;
    //初始化顶点
    for(i=0;i<pG->vexnum;i++){
        printf("vertex(%d): ",i);
        pG->vexs[i].data=read_char();
        pG->vexs[i].first_edge=NULL;
    }
    
    //初始化边
    for(i=0;i<pG->edgnum;i++)
    {
        //读取边的起始顶点和结束顶点
        printf("edge(%d): ",i);
        c1=read_char();
        c2=read_char();
        
        p1=get_position(*pG,c1);
        p2=get_position(*pG,c2);
        
        //初始化node1
        node1=(ENode*)malloc(sizeof(ENode));
        node1->ivex=p2;
        //将node1链接到p1所在链表的末尾
        if(pG->vexs[p1].first_edge==NULL)
            pG->vexs[p1].first_edge=node1;
        else
            link_last(pG->vexs[p1].first_edge,node1);
        
        //初始化node2
        node2=(ENode*)malloc(sizeof(ENOde));
        node2->ivex=p1;
        
        //将node2链接到p2所在链表的末尾
        if(pG->vexs[p2].first_edge == NULL)
            pG->vexs[p2].first_edge = node2;
        else
            link_last(pG->vexs[p2].first_edge, node2);
    }
    
    return pG;
}
1.4 邻接表表示有向图
//有向图
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))

//邻接表中对应的链表的顶点
typedef struct _ENode
{
    int ivex;                       //该边所指向的顶点的位置
    struct _ENode *next_edge;       //指向下一条弧的指针
}ENode,*PENode;

//邻接表中的顶点
typedef struct _VNode
{
    char data;
    ENode *first_edge;   //指向第一条依附该顶点的弧
}VNode;

//邻接表
typedef struct _LGraph
{
    int vexnum;                  //图的顶点的数目
    int  edgnum;                //图的边的数目
    VNode vexs[MAX];
}LGraph;

//返回ch在matrix矩阵中的位置

static int get_position(LGraph g, char ch)
{
    int i;
    for(i=0; i<g.vexnum; i++)
        if(g.vexs[i].data==ch)
            return i;
    return -1;
}

//读取一个字符
static char read_char()
{
    char ch;

    do {
        ch = getchar();
    } while(!isLetter(ch));

    return ch;
}

//将node链接到list的末尾
static void link_last(ENode *list,ENode *node)
{
    ENode *p=list;
    
    while(p->next_edge)
        p=p->next_edge;
    p->next_edge=node;
}


//创建邻接表
LGraph* create_lgraph()
{
    char c1,c2;
    int v,e;
    int i,p1,p2;
    ENode *node1,*node2;
    LGraph* pG;
    
    //输入顶点数和边数
    printf("input vertex number: ");
    scanf("%d",&v);
    printf("input edge number: ");
    scanf("%d",&e);
    if ( v < 1 || e < 1 || (e > (v * (v-1))))
    {
        printf("input error\n");
        return NULL;
    }
    
    if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )
        return NULL;
    memset(pG, 0, sizeof(LGraph));

    //初始化顶点数和边数
    pG->vexnum=v;
    pG->edgnum=e;
    //初始化顶点
    for(i=0;i<pG->vexnum;i++){
        printf("vertex(%d): ",i);
        pG->vexs[i].data=read_char();
        pG->vexs[i].first_edge=NULL;
    }
    
    //初始化边
    for(i=0;i<pG->edgnum;i++)
    {
        //读取边的起始顶点和结束顶点
        printf("edge(%d): ",i);
        c1=read_char();
        c2=read_char();
        
        p1=get_position(*pG,c1);
        p2=get_position(*pG,c2);
        
        //初始化node1
        node1=(ENode*)malloc(sizeof(ENode));
        node1->ivex=p2;
        //将node1链接到p1所在链表的末尾
        if(pG->vexs[p1].first_edge==NULL)
            pG->vexs[p1].first_edge=node1;
        else
            link_last(pG->vexs[p1].first_edge,node1);
        
       
    }
    
    return pG;
}

参考

2. 实现图的深度优先搜索、广度优先搜索
2.1 深度优先搜索

DFS:图的深度优先搜索思想是,假设初始状态是图中所有的顶点均未访问,则从某个顶点v出发,首先访问该顶点,然后依次从他的各个未访问的邻接点出发进行搜索,直到图中所有和v有路径相通的顶点都被访问到。若此时还有顶点没有被访问,则选择一个未被访问的顶点作为起点,重复上述过程,直到图中所有顶点都被访问到,是一个递归的过程。类似于树的先序遍历。

//深度优先 --无向图 邻接矩阵
static void DFS(Graph G,int i,int *visited)
{
    int w;
    
    visited[i]=1;
    printf("%c ",G.vexs[i]);
    
    for(w=first_vertex(G,i);w>=0;w=next_vertix(G,i,w))
    {
        if(!visited[w])
            DFS(G,w,visited);
    }
}

void DFSTraverse(Graph G)
{
    int i;
    int visited[MAX];
    
    for(i=0;i<G.vexnum;i++)
        visited[i]=0;
        
    printf("DFS: ");
    for(i=0;i<G.vexnum;i++)
    {
        if(!visited[i])
            DFS(G,i,visited);
    }
    printf("\n");
}

2.2 广度优先搜索

BFS:广度优先搜索的思想是,从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。类似于树的层次遍历。

//广度优先搜索  无向图 邻接表

void BFS(LGraph G){
    int head=0;
    int rear=0;
    int queue[MAX];
    int visited[MAX];
    int i,j,k;
    ENode *node;
    
    for(i=0;i<G.vexnum;i++)
        visited[i]=0;
        
    printf("BFS: ");
    for (i = 0; i < G.vexnum; i++)
    {
        if (!visited[i])
        {
            visited[i] = 1;
            printf("%c ", G.vexs[i].data);
            queue[rear++] = i;  // 入队列
        }
        while (head != rear) 
        {
            j = queue[head++];  // 出队列
            node = G.vexs[j].first_edge;
            while (node != NULL)
            {
                k = node->ivex;
                if (!visited[k])
                {
                    visited[k] = 1;
                    printf("%c ", G.vexs[k].data);
                    queue[rear++] = k;
                }
                node = node->next_edge;
            }
        }
    }
    printf("\n");
}
3. 实现 Dijkstra 算法、A* 算法
3.1 Dijkstra 算法

Dijkstra 算法:Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。

/*参数说明:
 *        G -- 图
 *       vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
 *     prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
 *     dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
 */
 
 void dijkstra(LGraph G,int vs,int prev[],int dist[])
 {
     int i,j,k;
     int min;
     int tmp;
     int flag[MAX];   //flag[i]=1表示顶点vs到顶点i的最短路径已经获取
     
     //初始化
     for(i=0;i<G.vexnum;i++)
     {
         flag[i]=0;
         prev[i]=0;
         dist[i]=get_weiht(G,vs,i);      
     }
     
     //对顶点vs进行初始化
     flag[vs]=1;
     dist[vs]=0;
     
     //遍历G.vexnum-1次,每次找出一个顶点的最短路径
     for(i=1;i<G.vexnum;i++)
     {
         //寻找距离vs最近的顶点k
         min=INF;
         for(j=0;j<G.vexnum;j++)
         {
             if(flag[j]==0 && dist[j]<min)
             {
                 min=dist[j];
                 k=j;
             }
         }
         
         //标记顶点k为已经获取到最短路径
         flag[k]=1;
         
         //修改当前最短路径和前驱顶点
         for(j=0;j<G.vexnum;j++)
         {
             tmp=get_weight(G,k,j);
             tmp=(tmp==INF?INF:(min+tmp));
             if(flag[j]==0 && (tmp<dist[j]))
             {
                 dist[j]=tmp;
                 prev[j]=k;
             }
         }
     }
     
     //打印结果
     printf("dijkstra(%c): \n",G.vexs[vs].data);
     for(i=0;i<G.vexnum;i++)
        printf("shortest(%c,%c)=%d\n",G.vexs[vs].data,G.vexs[i].data,dist[i]);
 }
 
3.2 A* 算法

在网上看了好多关于该算法的讲解,大部分原理讲的对初学者都很不友好,终于找到一篇通俗易懂的。

参考文章

4. 实现拓扑排序的 Kahn 算法、DFS 算法

存在前提:当且仅当一个有向图为有向无环图时,才能得到对应于该图的拓扑排序;每一个有向无环图都至少存在一种拓扑排序。

拓扑排序是遍历所有的结点,每两个结点之间有先后关系。并且在搜索下一个结点的时候,这个结点之前的结点已经全部被搜索过。


//Kahn算法,关键在于需要维护一个入度为0的顶点的集合
int n,m;
int inDeg[N];                        //i点的入度
vector<int>vec[N];                  //i点的邻接表,即i与哪些点相连
int ans[N];                         //排序结果数组
 
int topSort()             //返回值代表是否有环,排序结果在ans数组
{
    int cnt=0;
    queue<int>q;            //如果需要相同优先级的顶点,序号小的先输出,则可建立优先队列
    
    while(!q.empty())
        q.pop();
    for(int i=1;i<=n;i++)
        if(inDeg[i]==0)
            q.push(i);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        ans[++cnt]=now;              //个数+1并存数组
        int len = vec[now].size();
        for(int i=0;i<len;i++)
        {
            inDeg[vec[now][i]]--;
            if(inDeg[vec[now][i]]==0)
                q.push(vec[now][i]);
        }
    }
    return (cnt==n)?1:0;                //是否等于n,等于n代表无环
}
 
void init()                         //输入数据,n个点,m条边
{
    int x,y;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        vec[i].clear();
    memset(inDeg,0,sizeof(inDeg));
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        inDeg[y]++;
        vec[x].push_back(y);
    }
}




//dfs实现,从出度的角度来构造,递归到最后返回
int n,m;
vector<int>vec[N];                  //i点的邻接表,即i与哪些点相连
int ans[N],cnt;                      //排序结果数组,cnt记录个数
int parent[N];                      //记录其前置节点
 
int vis[N];                     //标记数组vis[i]=0表示还未访问过点i,c[i]=1表示已经访问过点i,并且还递归访问过它的所有子孙,c[i]=-1表示正在访问中,尚未返回
 
bool dfs(int s)                     //深度优先搜索(邻接表实现),记录时间戳,寻找最短路径
{
    vis[s]=-1;   //正在访问
    //Time++;d[s]=Time;
    int len = vec[s].size();
    for(int i=0;i<len;i++)
    {
        int tmp=vec[s][i];
        if(vis[tmp]<0)                  //如果子孙比父亲先访问,说明存在有向环,失败退出
            return false;
        else if(!vis[tmp]&&!dfs(tmp))    //如果子孙未被访问,访问子孙返回假,说明也是失败
            return false;
        parent[tmp]=s;
    }
    vis[s]=1;
    //Time++;f[s]=Time;
    ans[cnt++]=s;                       //结果是逆序的,递归的原因
    return true;
}
 
bool topSort()                       //返回值代表是否有环,排序结果在ans数组
{
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        parent[i]=-1;
        vis[i]=0;
    }
    //Time=0;
    for(int i=1;i<=n;i++)
        if(!vis[i])
            if(dfs(i)==false)
                return false;
    return true;
}
 
void init()                          //输入数据,n个点,m条边
{
    int x,y;
    for(int i=1;i<=n;i++)
        vec[i].clear();
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        vec[x].push_back(y);
    }
}
5. 对应的 LeetCode 练习题
5.1 Number of Islands(岛屿的个数)

LeetCode 200

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int count=0;
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid[0].size();j++){
                if(grid[i][j] >= '1'){
                    count++;
                    dfs(grid,i,j);
                }
            }
        }
        return count;
    }
    
    void dfs(vector<vector<char>>& grid,int i,int j){
        if(i>=grid.size() || i<0 || j<0 || j>=grid[0].size() || grid[i][j] !='1')
            return;
        
        grid[i][j]='0';
        dfs(grid,i+1,j);
        dfs(grid,i,j+1);
        dfs(grid,i-1,j);
        dfs(grid,i,j-1);
    }
};
5.2 Valid Sudoku(有效的数独)

LeetCode 36

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)
            {
                if(board[i][j]=='.')
                    continue;
                for(int k=j+1;k<9;k++)
                    if(board[i][j]==board[i][k])
                        return false;
                for(int k=i+1;k<9;k++)
                    if(board[i][j]==board[k][j])
                        return false;
                for(int a=i+1;a<(i/3+1)*3;a++)
                {
                    for(int b=j/3*3;b<j;b++)
                        if(board[i][j]==board[a][b])
                            return false;
                    for(int b=j+1;b<j/3*3+3;b++)
                        if(board[i][j]==board[a][b])
                            return false;
                }
            }
        return true;
    }
};
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值