有向图

一. 有向图的相关术语

在有向图中,边是单向的:每条边连接的两个顶点都是一个有序对,它们的邻接性是单向的。我们开发过程中碰到的很多场景都是有向图:比如任务調度的依赖关系,社交网络的任务关系等等都是天然的有向图。

以下概念都是针对有向图的:

(1)==有向图==:一幅有向图是由一组顶点和一组有方向的边组成的,每条有方向的边都连接着有序的一对顶点。

(2)==顶点的出度==:该顶点指出的边的总数。

(3)==顶点的入度==:指向该顶点的边的总数。

(4)比如对于有序对(w,v) 一般代表w->v 的一条有向边。

(5)==有向路径==:由一系列顶点组成,其中每个顶点都存在一条有向边从它指向序列中的下一个顶点。

(6)==有向环==:为一条至少含有一条边且起点和终点相同的有向路径。

(7)==简单有向环==:是一条除了起点和终点外,不含有重复的顶点和边的环。

二. 有向图的存储数据结构

首先定义有向图的API接口,DiGraph 本质上和Graph是一样的。

public class Digraph
Digraph(int v) 创建一幅含有v个顶点,但是没有边的有向图
Digraph(In in) 从输入流中读取一幅有向图
int E() 边的总数
int V() 边的总数
void addEdge(int v,int w) 添加一条有向边v ->w
Iterble<Integer>adj(int v) 由顶点v出发的有向边所连接的所有顶点
Digraph reverse() 该图的反向图
String toString() 对象的字符串表示形式

1. 有向图的表示

和无向图的表示类似,我们还是使用 ==邻接表== 来存储有向图,其中边 v—>w 表示为顶点v所对应的邻接链表中包含一个w顶点。

2. 有向图取反

上面的API中给出了reverse() 方法,将有向图中的所有有向边反转,并生成副本。

3. 有向图的实现

使用邻接表来存储有向图,用数组来表示图中的每个节点的集合,并且数组的下标就表示节点的标识符。

下面就是有向图的实现方式:

package com.example.algorithm4.graphs;

import edu.princeton.cs.algs4.Bag;

/** * 有向图的实现 * * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class DiGraph {
    /** * 节点的总个数 */
    private final int V;
    /** * 边的总个数 */
    private int E;
    /** * 有向图的邻接表表示法 */
    private Bag<Integer>[] adj;

    /** * 创建一个含有V个节点,但是0条边的有向图 * @param V */
    public DiGraph(int V) {
        this.V = V;
        this.E = 0;
        adj = (Bag<Integer>[])new Bag[V];
        for (int i=0; i<this.V; i++){
            adj[V] = new Bag<>();
        }
    }


    public int E(){
        return this.E;
    }


    public int V(){
        return this.V;
    }

    /** * 添加一条 v-->w 的边 * @param v 有向边的源在数组中的标识符 * @param w 有向边的终在数组中的标识符 */
    public void addEgge(int v,int w){
        adj[v].add(w);
        this.E++;
    }

    /** * 节点v 的所有出度的终顶点 * @param v 节点v 的标识符 * @return */
    public Iterable<Integer> adj(int v){
        return adj[v];
    }

    /** * 此有向图反转之后的副本 * @return */
    public DiGraph reverse(){
        DiGraph reverse = new DiGraph(this.V);
        for(int i=0 ;i<this.V; i++){
            for (int w : adj[i]){
                //边反转
                reverse.addEgge(w,i);
            }
        }
        return reverse;
    }
}

4. 符号有向图的实现

有向图的邻接表表示和无向图的邻接表表示区别不大,仅仅在于边的处理上有一点区别。如果对于有向图的结点的标识我们也想用字符串来表示呢?其实现方法几乎和无向图一样,增加一个Map用来保存顶点的符号到数组下表的映射关系,然后用字符数组保存所有的符号,数组下标天然表示每个顶点的index。 代码实现上只需要把SymbolGraph中的Graph替换成DiGraph就可,下面也给出实现代码:

package com.example.algorithm4.graphs;

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.ST;

/** * 符号有向图的实现 * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class SymbolDiGraph {
    private ST<String,Integer> st; // map,顶点符号名 -> 数组索引
    private String[] keys; // 数组索引 -> 顶点符号名
    private DiGraph G; // 图

    /** * 构造一颗符号有向图 * @param stream 顶点边的数据源 * @param sp 顶点分隔符 */
    public SymbolDiGraph(String stream,String sp) {
        st = new ST<>();
        In in = new In(stream); // First pass
        while (in.hasNextLine()) {// builds the index
            String[] a = in.readLine().split(sp); // by reading strings
            for (int i = 0; i < a.length; i++) {// to associate each
                if (!st.contains(a[i])) {// distinct string
                    st.put(a[i],st.size()); // with an index.
                }
            }
        }
        keys = new String[st.size()]; //建立索引 --> 结点符号
        for (String name : st.keys()) {// to get string keys
            keys[st.get(name)] = name; // is an array.
        }

        // 创建符号图
        G = new DiGraph(st.size());
        in = new In(stream); // Second pass
        while (in.hasNextLine()) {// builds the graph
            String[] a = in.readLine().split(sp); // by connecting the
            int v = st.get(a[0]); // source vertex
            for (int i = 1; i < a.length; i++) {// on each line
                G.addEdge(v,st.get(a[i])); // to all the others.
            }
        }
    }

    public boolean contains(String s) {
        return st.contains(s);
    }

    public int index(String s) {
        return st.get(s);
    }

    public String name(int v) {
        return keys[v];
    }

    public DiGraph G() {
        return G;
    }
}

三. 有向图中的可达性

我们在无向图中介绍的第一个算法就是DFS,其实无向图是很多算法的基础,解决了单点连通性的问题,可以判断无向图中指定的两个顶点是否连通。其思想对于有向图同样适用~

定义:有向图的单点可达性:给定一个有向图和一个起点s,判断是否存在一条从起点s到指定节点v的有向路径。

针对以上问题,设计DirectedDFS 算法的API:

public class DirectedDFS
DirectedDFS(DiGraph G,int s) 在G中找到从s可达的所有顶点
DirectedDFS(DiGraph G,Iterable<Integer> sources) 在G中找到从sources的所有顶点中可达的所有顶点
boolean reachable(int v) d顶点v是可达的嘛

1. 使用DFS实现有向图的可达性分析

在有向图中,深度优先搜索标记由一个集合的顶点可达的所有顶点所需的时间与被标记的所有顶点的出度之和成正比。

下面是有向图的可达性算法的实现:

package com.example.algorithm4.graphs;

/** * 有向图的深度优先遍历 * * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class DirectedDFS {
    /** * 从起点s开始DFS访问过的路径标记 */
    private boolean[] marked;

    public DirectedDFS(Digraph digraph,int s) {
        this.marked = new boolean[digraph.V()];
        dfs(digraph,s);
    }

    public DirectedDFS(Digraph digraph,Iterable<Integer> sources) {
        this.marked = new boolean[digraph.V()];
        for(int s : sources){
            if (!marked[s]){
                dfs(digraph,s);
            }
        }
    }

    /** * 从节点v 开始有向图的DFS * * @param digraph 有向图 * @param v 结点 */
    private void dfs(Digraph digraph,int v){
        this.marked[v] = true;
        for (int w : digraph.adj(v)){
            if(!this.marked[w]){
                dfs(digraph,w);
            }
        }
    }

    public boolean marked(int v){
        return marked[v];
    }
}

这份深度优先搜索能够实现判断一个或则一组(多点可达性)顶点能到达哪些其他顶点。

2. 标记-清除的垃圾收集

多点可达性的一个重要的实际应用是在典型的内存管理上,比如JVM内存管理,在一幅有向图中,一个顶点代表一个对象,一条边代表一个对象对另外一个对象的引用。 有向图的模型很好的可以应用在JVM内存管理上面。当从根节点遍历时候,那么不可达的顶点就代表不会再被使用,就可以被回收以释放内存。

标记-清除的垃圾回收算法会为每个对象保存一个位作为垃圾回收之用,然后周期性的运行一个类似于DirectedDFS的有向图可达性算法来标记所有可以被访问到的对象,然后清理所有对象,回收没有被标记的对象,以达到释放内存的目的。

3. 有向图的寻路

DepthFirstPaths 和 BreadthFirstPaths 也都是有向图处理中的重要算法。可以用处理下面问题:

  • 单点有向路径:给定一个起点s,从s到给定目的顶点v是否存在一条有向路径。如果有就找出这条路径。
  • 单点最短有向路径:给定一个起点s,从s到给定目的顶点v是否存在一条有向路径。如果有就找出最短的路径。

下图给出了一个使用深度优先遍历在一幅有向图中寻找能够从顶点0到达的所有顶点的轨迹的示意图:

四. 环和有向无环图(DFS和拓扑排序)

在有向图的应用场景中对于有向环的检测十分重要,因为有向环就代表着死循环,然后实际的项目中由于依赖关系的复杂,几乎不可能肉眼检测有向环,所以检测有向环是否存在就至关重要。

对于有向环的检测主要有两种方法:DSF和拓扑排序。下面以实际例子来分析

1. 调度问题(拓扑排序)

一个广泛使用的模型就是给定一组任务并安排它们的执行顺序,限制条件是这些任务的执行方法和起始时间。限制条件可能还包括任务的耗时以及消耗的其他资源。最重要的一种限制条件叫做优先级限制,它指明了哪些任务必须在哪些任务之前完成。 不同类型的限制条件会产生不同类型不同难度的调度问题

下面以一个正在安排课程的大学生为例,有些课程是其余课程的先导课程:如图:

再假设该学生一次只能修一门课,问题就可以抽象成以下模型:

优先级限制下的調度问题:给定一组需要完成的任务,以及一组关于任务完成的先后次序的优先级顺序。在满足条件下以最优方案完成任务。为了简化模型,以数字标识顶点,顶点代表任务。可以等价成下图:

拓扑排序:给定一幅有向图,将所有顶点排序,使得所有的有向边均从排在前面元素指向排在后面的元素。

上面的实例的拓扑排序图如下:所有边都朝下,清晰描绘了这幅有向图模型代表的优先级限制下調度问题的解决方法:

拓扑排序有很多典型的应用,比如任务調度,继承等等。

2. 有向环的检测方法一:DFS

定义。有向无环图就是一幅不包含环的有向图。

有向环的检测,我们可以借助于DFS算法,也可以借助拓扑排序。算法DirectedCycle 实现了环的检测,API如下:

public class DirectedCycle
DirectedCycle(Digraph digraph) 寻找有向环的构造函数
boolean hasCycle() digraph是否有环
Iterable<Integer> cycle() 有向环中的所有顶点

下图是一个在有向环中寻找环的过程示意图:

算法实现如下:

package com.example.algorithm4.graphs;

import java.util.Stack;

/** * 有向图环的检测:DFS算法 * * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class DirectedCycle {
    /** * 从起点开始DFS访问过的路径标记 */
    private boolean[] marked;
    /** * 从起点到一个顶点v的已知路径上的最后一个顶点 */
    private int[] edgeto;
    /** * 如果存在有向环,就保存有向环中的所有顶点。 */
    private Stack<Integer> cycle;
    /** * 递归调用栈上的所有顶点 * 这里类似于Java里面的一个set,里面保存了所有已经访问过的顶点 */
    private boolean[] onStack;

    /** * 构造器 * * @param digraph 有向图 */
    public DirectedCycle(Digraph digraph) {
        this.marked = new boolean[digraph.V()];
        this.edgeto = new int[digraph.V()];
        this.onStack = new boolean[digraph.V()];
        for (int v=0; v<digraph.V(); v++) {
            if( !marked[v] ) {
                dfs(digraph,v);
            }
        }
    }

    /** * 从节点v 开始有向图的DFS * * @param digraph 有向图 * @param v 结点 */
    private void dfs(Digraph digraph,int v){
        // 标记当前结点在堆栈路径上
        this.onStack[v] = true;
        this.marked[v] = true;
        for (int w : digraph.adj(v)){
            // 如果当前图已经有环就直接返回
            if (this.hasCycle(w)){
                return;
            } else if(!this.marked[w]){
                // 如果当前结点没有被标记过,递归dfs
                edgeto[w] = v;
                dfs(digraph,w);
            } else if (onStack[w]){
                // (1)当前结点被标记过来了,(2)而且在已经访问过得堆栈上,则存在环
                cycle = new Stack<>();
                for(int x = v; x!=w; x = this.edgeto[x]) {
                    cycle.push(x);
                }
                cycle.push(w);
                cycle.push(v);
            }
        }
        // 递归回溯时,当前结点重置为不在环的堆栈上。
        this.onStack[v] = false;
    }

    public boolean hasCycle(int v){
        return cycle!=null;
    }

    public Iterable<Integer> cycle(){
        return cycle;
    }
}

上面检测有向图是否有环的算法采用标准的递归dfs算法,添加了一个布尔型数组onStack[] 来保存递归调用期间栈上面的所有顶点。当找到一条边 v->w 且w在栈中时,就代表找到了有向环。环上所有顶点都可以通过edgeto[]数组得到。

3. 顶点的深度优先次序与拓扑结构

在优先级限制下的調度问题等价于计算有向无环图中的所有顶点的拓扑排序,因此给出如下API:

public class Topological
Topological(Digraph digraph) 拓扑排序的构造函数
boolean isDAG() 图digraph是有向无环图嘛?
Iterable<Integer> order() 拓扑排序的所有顶点

定理:当且仅当一幅图是有向无环图时才能进行拓扑排序。也就是有向无环图才有拓扑排序。

首先我们先来看看 ==有向图中基于深度优先搜索的顶点排序== 的DepthFirstOrder算法。

其基本思想是:深度优先搜索正好只会访问每个顶点一次,如果将dfs()的参数顶点保存在一个数据结构中,遍历这个数据结构实际上就能访问图中的所有顶点,遍历的顺序取决于这个数据结构的性质以及是在递归调用之前还是调用之后进行保存。在应用中我们关注的是以下3种排列顺序:

  • 前序:在递归调用之前将顶点加入队列
  • 后序:在递归调用之后将顶点加入队列
  • 逆后续:在递归调用之后将顶点压入栈。

下图是用DepthFirstOrder 算法处理有序无环图产生的轨迹。实现简单,支持处理图的高级算法中十分有用的pre()、post()和reversePost()方法。 例如Topological类中order()方法就调用了reversePost()方法。

算法实现如下:

package com.example.algorithm4.graphs;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/** * 有向图中基于深度优先搜索的顶点排序 * * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class DepthFirstOrder {
    /** * 从起点开始DFS访问过的路径标记 */
    private boolean[] marked;
    /** * 所有顶点的前序排列 */
    private Queue<Integer> pre;
    /** * 所有顶点的后序排列 */
    private Queue<Integer> post;
    /** * 所有顶点的逆后序排列 */
    private Stack<Integer> reversePost;

    public DepthFirstOrder(Digraph digraph) {
        marked = new boolean[digraph.V()];
        pre = new LinkedList<>();
        post = new LinkedList<>();
        reversePost = new Stack<>();

        for (int v=0; v<digraph.V(); v++){
            if ( !marked[v] ) {
                dfs(digraph,int v){
        pre.add(v);
        this.marked[v] = true;
        for (int w : digraph.adj(v)){
            if(!this.marked[w]){
                dfs(digraph,w);
            }
        }
        post.add(v);
        reversePost.push(v);
    }

    public Iterable<Integer> pre(){
        return pre;
    }

    public Iterable<Integer> post(){
        return post;
    }

    public Iterable<Integer> reversePost(){
        return reversePost;
    }
}

该类允许使用各种顺序遍历DFS经过的顶点。这在高级的有向图处理算法中非常有用,因为搜索的递归性是的我们能够证明这段计算的许多性质。

下面给出拓扑排序==Topological==的实现算法:

package com.example.algorithm4.graphs;

/** * 有向无环图的 拓扑排序 * * @author 惜暮 * @email chris.lyt@alibaba-inc.com * @date 2017/12/7 */
public class Topological {
    /** * 顶点的拓扑排序 */
    private Iterable<Integer> order;

    public Topological(Digraph digraph){
        DirectedCycle cycleFinder = new DirectedCycle(digraph);
        if( !cycleFinder.hasCycle() ) {
            DepthFirstOrder dfs = new DepthFirstOrder(digraph);
            order = dfs.reversePost();
        }
    }

    public Iterable<Integer> order() {
        return order;
    }

    public boolean isDAG(){
        return order!=null;
    }
}

Topological的实现借助了DirectedCycle 检测环是否存在,借助DepthFirstOrder的逆后续来获取拓扑排序。

==定理:一幅有向无环图的拓扑排序就是所有顶点的逆后续排列。==

证明:

在任意一条边v->w, 在调用dfs(v) 时,下面三种情况,必有一种成立:

(1)dfs(w) 已经被调用过了,且已经返回了。 (w节点已经被标记true了)

(2)dfs(w)还没有被调用(w还没被标记), 因此v->w 会直接或则间接调用并返回dfs(w),且dfs(w)会在dfs(v)之前返回。

(3)dfs(w)已经被调用但还没返回。证明的关键在于,在DAG中,这种情况是不可能出现的。由于递归调用链的特性意味着存在从w到v的路径,但又存在v->w的边,所以存在一个环。

在上面(1)(2)两种情况中dfs(w) 会在dfs(v)之前完成,也就是后续排列中w排在v之前。 所以在逆后序中w排在v之后,因此任意一条边v->w 都如我们所愿地从排名比较靠前的顶点指向排名靠后的顶点。

==定理:使用深度优先算法对有向无环图进行拓扑排序所需要时间和 (V+E) 成正比。==

证明:从DirectedCycle和DepthFirstOrder的实现可知,两次搜索都访问了所有顶点和边,所以时间和(V+E)成正比。

下面给出了一个示例 DAG 的逆后续是拓扑排序的轨迹图:

五. 有向图中的强连通性

定义:有向图中,如果两个顶点v和w是互相可达的,则称它们为强连通的。也就是说存在一条从v到w的有向路径,也存在一条从w到v的有向路径。 如果一幅有向图中任意两个顶点都是强连通的,则称这幅有向图也是强连通的。

1. 强连通分量

有向图的强连通性也是一种顶点之间的平等关系,因为其有着如下性质:

  • 自反性:任意顶点v和自己都是强连通的。
  • 对称性:如果v和w是强连通的, 那么w和v也是强连通的。
  • 传递性:如果v和w是强连通的且w和x也是强连通的 ,那么v和x也是强连通的。

强连通分量就是:有向图的极大强连通子图

2.强连通分量应用

最典型应用就是网络,以顶点代表网页,超链接代表边。 下面给强连通分量的API:

public class SCC
SCC(Digraph digraph) 预处理构造函数
boolean stronglyConnected(int v,int w) v和w是强连通的吗?
int count() 图中强连通分量的总数
int id(int v) v所在强连通分量的标识符(0至count()-1 之间)

3. 计算强连通分量的KoSaraju算法(无)

4. 再谈可达性(无)

六. 总结(无)

【数据结构】有向图的更多相关文章

  1. swift篇第一期:简单的数据结构

    首先我们可以去使用Playground来编码,并且会实时的显示对应的编码信息,这样我们就不用每次都去运行程序来显示输出的东西了哦,也方便了我们对某些语句的验证,这个是比较赞的var与let前者为可变修饰符,后者为不可变从字面意思我们就可以很好的区分了常用的类型呢,跟其他语言基本相同啦,主要有几种:1.int类型2.Float,Double类型3.String类型4.Boolean类型当我们去声明一

  2. Swift 集合数据结构性能分析

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  3. Swift中的集合类数据结构

    在那种情况下,你将会需要一种基本的集合类数据结构。继续学习,你将会比较成熟的Cocoa数据结构与对应的纯Swift结构的性能。常见iOS数据结构iOS中三种最常用的数据结构是arrays,dictionaries和sets。除了在Swift和Objective-C中旧的Foundation框架中的数据结构,现在又有了新的仅支持Swift版本的数据结构与语言紧密结合在一起。Swift数组是同质的,意味着每一个Swift数组都只包含一种类型的对象。

  4. 11.Swift 中的类和结构体

    举例来说,以下情境中适合使用结构体:1.几何形状的大小,封装一个width属性和height属性,两者均为Double类型。这次就讲到这里,下次我们继续

  5. a place you can learn algorithms and data structures(算法和数据结构) in swift

    https://github.com/raywenderlich/swift-algorithm-club

  6. Swift3.0 类和结构体的选择

    结构体实例总是通过值传递,类实例总是通过引用传递先说说值类型和引用类型的区别值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝在Swift中,所有的结构体和枚举类型都是值类型。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体”Swift中,许多基本类型,诸如String,Array和Dictionary类型均以结构体的形式实现。Objective-C中Nsstring,NSArray和NSDictionary类型均以类的形式实现,而并非结构体。

  7. 【Swift】结构体和类

    Swift中结构体和类有很多共同点与结构体相比,类还有如下的附加功能:结构体和枚举是值类型值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。为了达到这个目的,Swift内建了两个恒等运算符:类和结构体的选择在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。Swift中,许多基本类型,诸如String,Array和Dictionary类型均以结构体的形式实现。

  8. 如何在Swift中创建打包数据结构?

    我正在将一个项目从Objective-C转换为Swift,我正在使用一个打包的结构来输入通过套接字发送的转换二进制消息:我不确定Swift中最好的方法是什么,我能得到的最接近的近似值是:翻译中丢失了两个重要的细节:没有保证整数类型的比特,并且没有结构打包.我不认为这可以在Swift中表达,但如果是这样,怎么样?

  9. android – 如何正确删除保留的实例片段

    解决方法正如@Luksprog所建议的,以下方法有效.但是,它仍然无法解释为什么通过onDetach完成的先前清理不起作用.如果有人能解释为什么这个解决方案有效,而以前没有,我会非常感激.版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  10. android – 数组适配器notifyDataSetChanged()将无法正常工作

    .有任何想法吗?

随机推荐

  1. 【数据结构】单调栈

    显然,每个发射站发来的能量有可能被0或1或2个其他发射站所接受,特别是为了安全,每个发射站接收到的能量总和是我们很关心的问题。由于数据很多,现只需要你帮忙计算出接收最多能量的发射站接收的能量是多少。输入输出格式输入格式:第1行:一个整数N;第2到N+1行:第i+1行有两个整数Hi和Vi,表示第i个人发射站的高度和发射的能量值。输入输出样例输入样例:34235610输出样例:7题解中有讲解代码实现

  2. BZOJ 1798 [Ahoi2009] Seq 维护序列seq [线段树+多重标记下传]【数据结构】

    有长为N的数列,不妨设为a1,a2,…Input第一行两个整数N和P。第二行含有N个非负整数,从左到右依次为a1,aN,。表示把所有满足t≤i≤g的ai改为ai×c。操作2:“2tgc”。同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。Output对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。SampleInput7431234567512553242379313347SampleOutput2358HINT初始时数列为。对第5次操作,和为29+34+15+16=

  3. 陈越《数据结构》第一讲 基本概念

    陈越《数据结构》第一讲基本概念1什么是数据结构1.1引子例子:如何在书架上摆放图书?数据结构是:1.数据对象在计算机中的组织方式;2.数据对象必定与一系列加在其上的操作相关联;3.完成这些操作所用的方法就是算法。抽象数据类型数据类型-数据对象集;-数据集合相关联的操作集。抽象-与存放数据的机器无关;-与数据存储的物理结构无关;-与实现操作的算法和编程语言均无关。

  4. 陈越《数据结构》第二章 线性结构

    表中元素个数称为线性表的长度;线性表没有元素时,称为空表;表起始位置称表头,表结束位置称表尾。插入和删除操作只能在链栈的栈顶进行。

  5. 【数据结构】

    非线性结构:线性结构的元素之间具有线性关系,非线性结构中的元素之间不再是序列的关系,他们呈现的是更复杂的层次关系,即一个数据元素有且仅有一个直接前驱,但可有另个或者多个直接后继,显然比序列关系复杂常见非线性结构:树,图散列表PHP中的hashtable就是哈希表就是由数组和链表组成,一个长度为16的数组中,每个元素存储的是一个链表的头结点。

  6. 【数据结构】【C++STL】FIFO队列&amp;优先队列

    首先都需要打头文件queueFIFO队列是先进先出的就好像排队一样STL定义FIFO队列优先队列的话是有优先级存在的STL定义优先队列定义方式都是大根堆FIFO队列和优先队列都有一些操作COYG

  7. 【数据结构】 堆

    自底向上://增加/减少已有节点值Heap_Increase_Key//向堆插入新的节点HeapInsert自顶向下://替换堆顶后,维持堆函数KeepHeap//弹出堆顶函数Pop

  8. 【数据结构】链表

    线性表的顺序存储结构有存储密度高及能够随机存取等优点,但存在以下不足:线性表的链式存储(单链表)的实现单向循环链表的实现

  9. 伸展树(SPLAY)个人总结+模板 [平衡树]【数据结构】【模板】

    前言最近3个月内,无论是现场赛还线上赛中SPLAY出现的概率大的惊人啊啊啊!!!然而不会的我就GG了,同时发现大家都会SPLAY,,,,然后就学习了一波。——————————————————————————-附上整体代码-md贴上来太卡了,去题解里看吧维护序列的维护一堆数的

  10. BZOJ 1895 &amp; POJ 3580 supermemo [SPLAY]【数据结构】

    Ay}Ttimes.Forexample,performing“REVOLVE242”on{1,5}INSERTxP:insertPafterAx.Forexample,performing“INSERT24”on{1,5}DELETEx:deleteAx.Forexample,performing“DELETE2”on{1,5}MINxy:querytheparticipantwhatistheminimumnumberinsub-sequence{Ax…Ay}.Forexample,thecorrec

返回
顶部