C++笔试题模版汇总(三)

目录

 

1、邻接矩阵

2、DFS

3、BFS

4、拓扑排序

4、Dijkstra

5、队列优化的Bellman-Ford算法

6、spfa

7、Floyd算法

8、prim最小生成树

9、Kruskal-最小生成树

10、染色法判别二分图

12、匈牙利算法


1、邻接矩阵

对于无向图中的边ab,存储两条有向边a->b, b->a。
因此我们可以只考虑有向图的存储。

(1) 邻接矩阵:g[a][b] 存储边a->b

(2) 邻接表:

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

// 添加一条边a->b,链表操作:插入元素
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// 初始化
idx = 0;
memset(h, -1, sizeof h);

2、DFS

时间复杂度 O(n + m),n表示点数,m表示边数

void dfs(int u)
{
    if (u == n)//  当走到第n个位置,说明我们把所有位置填满了
    {
        //输出结果
        for (int i = 0; i < n; i ++) printf("%d ", path[i]);
        puts("");
        return;
    }
    
    for (int i = 1; i<= n; i ++)//枚举当前位置能填哪些数
        if (!st[i])//如果该数没被用过
        {
            path[u] = i;//将i放到当前位置
            st[i] = true;//记录i已被用过
            dfs(u + 1);//递归到下一层
            //当dfs结束之后,一条遍历完所以需要恢复现场
            st[i] = false;
        }
}

3、BFS

时间复杂度 O(n + m),n表示点数,m表示边数

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();//出队

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!s[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);//入队
        }
    }
}

4、拓扑排序

时间复杂度 O(n + m),n表示点数,m表示边数


bool topsort()
{
    int hh = 0, tt = -1;
    //d[i] 存储点i的入度
    for (int i = 1; i <= n; i ++)
        if (!d[i])
            q[++ tt] = i;//入队
    
    while (hh <= tt)
    {
        
        int t = q[hh ++];
        //枚举t的所有出边
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            d[j] --;//删除j
            if (d[j] == 0) q[++ tt] = j;//删除完将j插入队列
        }
    }
    
    return tt == n - 1;//如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
}

4、Dijkstra

朴素做法:

优化:

//朴素版
typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

//优化版
int n;      // 总点数
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储每个点到1号点的最短距离
bool st[N];     // 存储每个点是否在队列中

// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

5、队列优化的Bellman-Ford算法

时间复杂度 平均情况下O(m),最坏情况下 O(nm),n表示点数,m表示边数

 

int dist[N], backup[N];//遍历之前备份dist
 
struct Edge
{
    int a, b, w;
}edges[M];
 
int bellman_ford()
{
    //初始化
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;//第一个点初始化为0
    
    for (int i = 0; i < k; i ++)
    {
        memcpy(backup, dist, sizeof dist);//存上一次迭代结果
        for (int j = 0; j < m; j ++)
        {//遍历所有边
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            dist[b] = min(dist[b], backup[a] + w);//只用上一次结果来更新这次的距离,避免出现串联
        }
    }
    
    if (dist[n] > 0x3f3f3f3f / 2) return -1;//0x3f3f3f3f / 2 
    return dist[n];
}

6、spfa

int h[N], w[N], e[N], ne[N], idx;
int dist[N];//dist[N]狄杰斯特拉的距离,表示从1号点到其他点的最短距离是多少。
bool st[N];//st[]每个点最短路是否确定
 
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
 
int spfa()
{
    //1、初始化
    memset(dist, 0x3f, sizeof dist);//初始化
    dist[1] = 0;//第一个点标记0
    
    queue<int> q;//定义队列
    q.push(1);//第一个点入队
    st[1] = true;//标记1号点,防止重复存储
    
    while (q.size())//while不空
    {
        int t = q.front();//取队头
        q.pop();//删除队头
        
        st[t] = false;//t不在队列
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];//取出当前点
            if(dist[j] > dist[t] + w[i])//如果大于就更新
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])//如果j不在队列,才加进去
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
} 

判断负环

int h[N], w[N], e[N], ne[N], idx;
//cnt[N]存边数
int dist[N], cnt[N];//dist[N]狄杰斯特拉的距离,表示从1号点到其他点的最短距离是多少。
bool st[N];//st[]每个点最短路是否确定
 
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
 
int spfa()
{
 
    queue<int> q;
    //判断是否存在负环,需要把所有点放在队列里面
    
    for (int i = 1; i <= n; i ++)
    {
        st[i] = true;
        q.push(i);//把所有点放进来
    }
    
    while (q.size())//while不空
    {
        int t = q.front();//取队头
        q.pop();//删除队头
        
        st[t] = false;//t不在队列
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];//取出当前点
            if(dist[j] > dist[t] + w[i])//如果大于就更新
            {
                dist[j] = dist[t] + w[i];
                //更新边
                cnt[j] = cnt[t] + 1; 
                if (cnt[j] >= n) return true;
                if (!st[j])//如果j不在队列,才加进去
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return false; 
} 

7、Floyd算法

时间复杂度是 O(n^3), n表示点数

初始化:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// 算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

8、prim最小生成树

时间复杂度是 O(n^2+m), n 表示点数,m 表示边数


//稠密图用邻接矩阵来存
int n, m;
int g[N][N];
int dist[N];
bool st[N];
 
int prim()
{
    //把所有距离初始化正无穷
    memset(dist, 0x3f, sizeof dist);//初始化成一个很大的数
    
    int res = 0;//存最小生成树所有边长度之和
    //n次迭代
    for(int i = 0; i < n; i ++)
    {
        int t = -1;
        //找所有集合外边中最小的
        for (int j = 1; j <= n; j ++)
        //在集合外,并且如果t == -1表示当前没有找到任何点,dist[t]距离 > dist[j]
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;//将t更新j
        if (i && dist[t] == INF) return INF;//当前图不连通,不存在最小生成树
        
        if (i) res += dist[t];//只要不是第一个点,就加上这条边
        //将t更新到集合的距离
        for (int j = 1; j <= n; j ++) dist[j] = min(dist[j], g[t][j]);
        
        st[t] = true;//标记t已访问过
    }
    
    return res;
}

9、Kruskal-最小生成树

int n, m;       // n是点数,m是边数
int p[N];       // 并查集的父节点数组

struct Edge     // 存储边
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

int find(int x)     // 并查集核心操作
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     // 如果两个连通块不连通,则将这两个连通块合并
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

10、染色法判别二分图

时间复杂度是 O(n+m), n 表示点数,m 表示边数

int h[N], e[M], ne[M], idx;//深度优先遍历用链表来存
int color[N];
 
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
 
//dfs一遍
bool dfs(int u, int c)
{
    //记录当前点的颜色
    color[u] = c;
    
    //遍历当前点零点
    for (int i = h[u]; i != -1; i = ne[i])
    {
        //记录当前点编号
        int j = e[i];
        if (!color[j])//如果没有染色,则染成另外一种颜色 3 - c
        {//3 - c: 把1变成2或者把2变成1
            if(!dfs(j, 3 - c)) return false;
        }
        //当前j已经染了颜色,如果和当前颜色不同则矛盾
        else if (color[j] == c) return false;//两条边颜色不一样
        
    }
    
    return true;
}

12、匈牙利算法

int h[N], e[M], ne[M], idx;
int match[N];//右边对应的点
bool st[N];//判断点是否重复
 
void add(int a, int b)
{
     e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
 
int find(int x)
{
    //1、枚举第x个男生所有看上女生的集合
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];//j表示当前集合点的编号
        if (!st[j]) //如果当前女生没有考虑过
        {
            st[j] = true;
            //如果当前女生没有被匹配任何男生,或者虽然匹配了男生,但是可以为男生找到下家
            if (match[j] == 0 || find(match[j]))
            {
                match[j] = x;//当前妹子可以匹配这个男生
                return true;
            }
        }
    }
    
    return false;
}

 

 

 

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值