【Java高阶数据结构】图补充-拓扑排序

简介: 【Java高阶数据结构】图补充-拓扑排序

Java高阶数据结构 & 图补充-拓扑排序


1. 什么是拓扑排序

图片来源:简单、快速地带你了解图论以及拓扑排序!_哔哩哔哩_bilibili

  • 讲得很好哦!

这里我以羊了个羊小游戏这款砖块消除类小游戏为例:

有一个规则:

  • 当上层砖块覆盖下层砖块的时候,下层砖块不可以被选中移动(暗)
  • 上层砖块移走后,下层砖块才能被移动(亮)

则可以用如下图表示这种逻辑结构:

  • A->B,代表A覆盖B
  • 即一种无环不带权有向图
  1. 带回路的话,那么这个顶点就相当于自己覆盖了自己,不合理
  2. 无向的话,一条边则是双向的,同1不合理
  3. 箭头代表覆盖关系,权值不重要

那么我们给每个砖块一个layer值:

要求:

  • layer值大的砖块显示上,覆盖layer值小的砖块
  • 即A->B,layer(A) > layer(B)

在开发过程中需要这个layer,至于用处是什么,不做解释,这里这不是重点。

  • 至于这款游戏的原理,感兴趣的可以去网上找找资料研究一下

我们只需要找到一个序列,保证上方的覆盖者要在被覆盖者前方,再为其安排一个递减的序列

那么问题是:如何生成这个序列呢?

  • 这个排序,就叫做 “拓扑排序”

类似的,如果你需要学习一系列的课程,那么一些知识一定需要另一些知识作为基础~

  • 而显然的,符合实际的是:这种序列不应该唯一
  • 学习路径肯定不一定单一呀~
  • 事实上也如此~
  • 一个图的拓扑排序很有可能有多解

2. 拓扑排序算法思想-卡恩算法

  • 本文章用邻接表的存储结果!

基础知识传送门:Java高阶数据结构 & 图 & 图的表示与遍历_s:103的博客-CSDN博客

例子:

前面提到,如果成环则没有拓扑序列,那么我们也可以通过能否拓扑排序,来判断一个图是否带环

  • 这也算是拓扑排序的一个额外得到功能吧

这里只讲解一种简单直接的算法,卡恩算法

  • 重点在于每个顶点的入度

步骤:

  1. 遍历一遍邻接表,计算所有顶点的入度
  2. 挑选一个入度为0的顶点,并输出
  3. 刚才挑中的顶以及其指向的顶点的入度都减1
  • -1的入度代表此顶点被删除
  1. 挑选一个入度为0的顶点,并输出
  2. 刚才挑中的顶以及其指向的顶点的入度都减1
  3. 重复这个操作,直到所有顶点都被删除(入度都为-1)
  • 如果最终是因为没有入度为0的顶点而不是全部顶点都被删除而停止的循环,则说明存在环

疑问:

1. 你很快会意识到,如果出现多个入度为0的顶点,应该怎么办?

答:先挑选谁都无所谓,因为这两个入度为0的顶点,是一种并列的关系,不会相互影响。即使他们有可能分支下去会有公共顶点,这也不会导致“乱了规则”的现象,因为“汇聚的第一个公共顶点”入度至少为2,只有其入度为0的时候才能被选中

  • 也就是说,只有如果先选中的入度为0的顶点输出后,诞生新的入度为0的顶点,一定也与原来入度为0的顶点也是并列
  • 这也是序列不唯一的原因~

2. 为什么入度为0的顶点先被选中?

答:这很显然,入度为0,代表没人指向它

  • 也就是说,它没被覆盖,而只覆盖别人!
  • 万物之源

算法复杂度分析:

  • 每个顶点和每条边都刚好被访问一次

V因为顶点数,E为边数

  • 则时间复杂度为O(V + E)

动图演示:

3. 拓扑排序代码实现

以下代码就不做过多解释了,完全依照刚才的算法思想~

3.1 遍历链表计算入度

//获取所有顶点的入度
public int[] getDevs() {
    int n = arrayV.length;
    int[] arr = new int[n];
    for (int i = 0; i < n; i++) {
        Node cur = edgeList.get(i);
        while(cur != null) {
            arr[cur.dest]++;
            cur = cur.next;
        }
    }
    return arr;
}

3.2 挑选一个入度为0的顶点

  • 你也可以结合isVisted数组(标记数组)和堆去存储,节约时间
  • 这里用最简单的遍历法
public int getFirstZero(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        if(arr[i] == 0) {
            return i;
        }
    }
    return -1;
}

3.3 输出顶点

  • 这里我将顶点输出到队列里了
public void outputV(int index, int[] arr, Queue<Character> queue) {
    queue.offer(arrayV[index]);
    arr[index]--;
    Node cur = edgeList.get(index);
    while(cur != null) {
        arr[cur.dest]--;
        cur = cur.next;
    }
}

3.4 判断循环结束是否为全-1

public boolean isContainCir(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        if(arr[i] != -1) {
            return true;
        }
    }
    return false;
}

3.4 kahn方法

public boolean kahn(Queue<Character> queue) {
    int[] arr = getDevs();
    int index = getFirstZero(arr);
    while(index != -1) {
        outputV(index, arr, queue);
        index = getFirstZero(arr);
    }
    return isContainCir(arr);
}

3.5 测试

public static void main(String[] args) {
    //定义与构建图
    char[] chars = "012345678".toCharArray();
    GraphByList graph = new GraphByList(chars.length, true);
    graph.initArrayV(chars);
    graph.addEdge('0', '1', 1);
    graph.addEdge('0', '2', 1);
    graph.addEdge('1', '3', 1);
    graph.addEdge('2', '3', 1);
    graph.addEdge('2', '4', 1);
    graph.addEdge('4', '3', 1);
    graph.addEdge('6', '0', 1);
    graph.addEdge('7', '0', 1);
    graph.addEdge('7', '6', 1);
    graph.addEdge('8', '5', 1);
    //定义队列
    Queue<Character> queue = new LinkedList<>();
    boolean flag = graph.kahn(queue);
    System.out.println(flag ? "带环" : "不带环");
    System.out.println(queue);
}

目录
相关文章
|
1天前
|
算法 Java
<八大排序>万字详解(Java实现).插入排序、希尔排序、堆排序、快速排序、归并排序、计数排序...
<八大排序>万字详解(Java实现).插入排序、希尔排序、堆排序、快速排序、归并排序、计数排序
3 0
|
1天前
|
Java 索引
Java数据结构——栈
Java数据结构——栈
13 1
|
1天前
|
存储 Java
Java数据结构:链表
Java数据结构:链表
14 2
|
1天前
|
算法 Java
Java数据结构——队列
Java数据结构——队列
15 4
|
1天前
|
Java
DAY-1 | Java数据结构之链表:删除无头单链表中等于给定值 val 的所有节点
力扣203题解:使用时间复杂度为O(n)的思路删除链表中所有值为key的元素。引入辅助指针pre,记录cur的前一个节点,遍历链表时,若cur.val!=key,pre和cur同时前进;若cur.val==key,则pre.next=cur.next,cur继续前进,确保pre不急于跟随以处理连续相同值的情况。遍历结束后,处理头节点可能需要删除的特殊情况。
13 0
|
4天前
|
算法 搜索推荐 Java
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
7 0
TU^
|
5天前
|
存储 调度 索引
数据结构~~栈和队列
在计算机科学中,数据结构是构建高效程序的基础。栈和队列是两种重要且常用的线性数据结构,它们在解决各种问题中发挥着关键作用。
TU^
18 1
|
1天前
<数据结构>栈和队列. 顺序表实现栈,单链表实现队列.
<数据结构>栈和队列. 顺序表实现栈,单链表实现队列
12 3
|
1天前
|
C++
数据结构深入理解--栈
数据结构深入理解--栈
5 0
|
4天前
|
缓存 Java 编译器
JavaSE精选-栈和队列
JavaSE精选-栈和队列
10 1