图论 - 一些经典小算法思想(无题目例子)

经典小算法

前言

主要介绍一些有意思的小算法

拓扑结构

简单来说,把一幅图拉平,而且这个拉平的图里面,所有的箭头方向都是一致的.

比如下图所有的箭头都是朝右的.

注意: 如果是一副有向图存在环,无法进行拓扑排序,因为肯定做不到所有箭头方向一致;

那图的拓扑结构如何实现呢?

这个特别简单,首先你要先确认好建图时对边的定义!

如果有向边定义为[依赖]关系:

比如节点2指向节点1.含义是节点2被节点1依赖.

那么我们让图结构后序遍历结果即可.

那如果是节点1指向节点2,含义是节点1被节点2依赖

那么我们只需把图结构后序遍历结果进行反转即可.

名流问题

给你 n 个人的社交关系(你知道任意两个人之间是否认识),然后请你找出这些人中的「名人」。

所谓「名人」有两个条件:

1、所有其他人都认识「名人」。

2、「名人」不认识任何其他人。

如果把每个人看做图中的节点,「认识」这种关系看做是节点之间的有向边,那么名人就是这幅图中一个特殊的节点:这个节点没有一条指向其他节点的有向边;且其他所有节点都有一条指向这个节点的有向边。

那么我们该如何表示这n个人的社交关系呢?

这个首先要看图的数据结构有两种存储形式:

  • 邻接表: 优势是节约存储空间
  • 临接矩阵: 优势可以迅速判断两个节点是否相邻.
    那对于名人问题而言: 需要经常判定两个人之间是否认识,所以这里选用临接矩阵来表示人与人之间的社交关系.

暴力解法

这里我们之谈思想,不聊代码.

我们可以暴力穷举,把每个人都看作候选人,然后判定是否符合名人的条件.

也就是说先选一个人,然后暴力拿其他人作比较.

很明显是一个双层for循环,最坏时间复杂度O(n^2).

优化解法

我们紧抓定义:

其他人都认识名人,但名人不认识其他人.

换句话说,这群人里面只有一个名人.

再换句话说,我比较两个人,一定能确认其中一个人不是名人.

那么我们比较规则是什么?我们先看一下两个比较有几种情况:

对于情况一,cand 认识 other,所以 cand 肯定不是名人,排除。因为名人不可能认识别人。

对于情况二,other 认识 cand,所以 other 肯定不是名人,排除。

对于情况三,他俩互相认识,肯定都不是名人,可以随便排除一个.

对于情况四,他俩互不认识,肯定都不是名人,可以随便排除一个。因为名人应该被所有其他人认识。

综上,只要观察任意两个之间的关系,就至少能确定一个人不是名人.

我们可以不断从候选人中选两个出来,然后排除掉一个,直到最后只剩下一个候选人,这时候再使用一个 for 循环判断这个候选人是否是货真价实的「名人」。

java 复制代码
class Solution extends Relation {
    public int findCelebrity(int n) {
        int cand = 0;
        for (int other = 1; other < n; other++) {
            if (!knows(other, cand) || knows(cand, other)) {
                // cand 不可能是名人,排除
                // 假设 other 是名人
                cand = other;
            } else {
                // other 不可能是名人,排除
                // 什么都不用做,继续假设 cand 是名人
            }
        }

        // 现在的 cand 是排除的最后结果,但不能保证一定是名人
        for (int other = 0; other < n; other++) {
            if (cand == other) continue;
            // 需要保证其他人都认识 cand,且 cand 不认识任何其他人
            if (!knows(other, cand) || knows(cand, other)) {
                return -1;
            }
        }

        return cand;
    }
}

二分图

二分图定义:

二分图的顶点集可分割为两个互不相交的子集,图中每条边依附的两个顶点都分属于这两个子集,且两个子集内的顶点不相邻。

给你一幅「图」,请你用两种颜色将图中的所有顶点着色,且使得任意一条边的两个端点的颜色都不相同,你能做到吗?

这就是图的「双色问题」,其实这个问题就等同于二分图的判定问题,如果你能够成功地将图染色,那么这幅图就是一幅二分图,反之则不是:

二分图的应用:二分图结构在某些场景可以更高效地存储数据。

二分图判定思路

说白了遍历一遍图,一遍遍历一边染色,看看能不能用两种颜色给所有节点染色,且相邻节点的颜色都不相同.

java 复制代码
// 图遍历框架
void traverse(Graph graph, boolean[] visited, int v) {
   visited[v] = true;
   // 遍历节点 v 的所有相邻节点 neighbor
   for (int neighbor : graph.neighbors(v)) {
       if (!visited[neighbor]) {
           // 相邻节点 neighbor 没有被访问过
           // 那么应该给节点 neighbor 涂上和节点 v 不同的颜色
           traverse(graph, visited, neighbor);
       } else {
           // 相邻节点 neighbor 已经被访问过
           // 那么应该比较节点 neighbor 和节点 v 的颜色
           // 若相同,则此图不是二分图
       }
   }
}
相关推荐
苏言の狗13 分钟前
小R的并集大小期望计算 | 蛮力
数据结构·算法
BineHello19 分钟前
MPC用优化求解器 - 解决无人机轨迹跟踪
算法·矩阵·自动驾驶·动态规划·无人机
誓约酱22 分钟前
(每日一题) 力扣 14 最长公共前缀
算法·leetcode·职场和发展
用户611881615196227 分钟前
Java基础面试题
java
DavidSoCool1 小时前
Elasticsearch Java API Client [8.17] 使用
java·大数据·elasticsearch
无世世1 小时前
【Java从入门到起飞】面向对象编程(高级)
java·开发语言
Vic101011 小时前
Mac如何查看 IDEA 的日志文件
java·macos·intellij-idea
陈逸轩*^_^*1 小时前
idea打不开,idea打不开,Error occurred during initialization of VM
java·ide·intellij-idea
冠位观测者1 小时前
【Leetcode 每日一题 - 补卡】2070. 每一个查询的最大美丽值
数据结构·算法·leetcode
=PNZ=BeijingL1 小时前
使用Mockito实现单元测试
java