2020-07-31【数据结构】图,树,深度优先、广度优先遍历,最小生成树,floyd最小环

大佬源码c#
大佬源码c

https://www.jianshu.com/p/bce71b2bdbc8

图的深度广度优先遍历

https://www.cnblogs.com/nr-zhang/p/11236369.html
树也算图
C# 通用树形数据结构
二叉树的前序、中序、后序遍历,本质上也可以认为是深度优先遍历。
二叉树的层序遍历,本质上也可以认为是广度优先遍历。

图的数据结构主要分两种,邻接矩阵(二维数组)和邻接链表。
对于插入来说,邻接链表更加方便。但是对于确定两顶点连接关系来说,邻接矩阵更方便。

邻接链表的结构说明:
c#图的邻接链表


添加顶点:直接添加
添加边:是从一个顶点,添加一条边到另一个顶点,因此需要设置起始顶点的firstEdge/secondEdge...,然后设置该边edge的endVex。

图的深度优先遍历,分为连通图的遍历,以及非连通图的遍历。
区别在于,连通图,只需要从第一个点开始遍历即可:第一个点,找到它的第一个边,然后获得边的终点,如果该终点未被访问过,递归遍历该点...递归结束后,找到它的第二个边...
非连通图:对每个点都要检索一遍;或者对每个非连通的子图其中一个点开始都搜索一遍。

连通图:所有点都能通过连线及中间连线访问到
连通分量:若无向图不是连通图,但图中存储某个子图复合连通图的性质,则该子图为连通分量。(说白了,就是大图里,分为多个小连通图,有几个小连通图,就有几个连通分量)

生成树

对连通图进行遍历,过程中所经过的边和顶点的组合可看作是一棵普通树,通常称为生成树。
生成树必须满足两个条件:1.包含连通图中所有顶点;2.任意两顶点之间有且仅有一条通路;即生成树中边数量=顶点数量-1

Kruskal最小生成树
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class AGraph
{
    List m_Vertexes;  //图的顶点集合
    //public List Vertexes { get => m_Vertexes; }

    public AGraph() : this(10) { }
    public AGraph(int capacity)
    {
        m_Vertexes = new List(capacity);
    }

    // 添加一组顶点
    public void AddVertex(params TValue[] items)
    {
        foreach (var x in items)
        {
            if (x != null)
            {
                AddVertex(x);
            }
        }
    }

    // 添加一个顶点
    public void AddVertex(TValue item)
    {
        if (Contains(item))
        {
            Debug.LogError("插入了重复节点!");
            return;
        }
        m_Vertexes.Add(new VertexNode(item));
    }

    // 添加无向边
    public void AddEdge(TValue from, TValue to, float weight = 0)
    {
        VertexNode fromVer = Find(from);//找到起始节点
        if (fromVer == null)
        {
            Debug.LogError("头节点并不存在!");
            return;
        }
        VertexNode toVer = Find(to);//找到结束节点
        if (toVer == null)
        {
            Debug.LogError("尾节点并不存在!");
            return;
        }
        AddEdge(fromVer, toVer, weight);
    }

    // 添加单向边
    public void AddDirectionEdge(TValue from, TValue to, float weight = 0)
    {
        VertexNode fromVer = Find(from);//找到起始节点
        if (fromVer == null)
        {
            Debug.LogError("头节点并不存在!");
            return;
        }
        VertexNode toVer = Find(to);//找到结束节点
        if (toVer == null)
        {
            Debug.LogError("尾节点并不存在!");
            return;
        }
        //无向边两个节点都需记录的边的信息
        AddDirectedEdge(fromVer, toVer, weight);
    }

    /// 
    /// 连线中所有的顶点
    /// randomWeight:是否给每条边一个随机值,否则都给1
    /// 
    public void LineAllVertexes(float randomWeight0 = 0, float randomWeight1 = 0)
    {
        for (int i = 0; i < m_Vertexes.Count; i++)
        {
            for (int j = 0; j < m_Vertexes.Count; j++)
            {
                if (i != j)
                {
                    // 判断是否已经连过线,如果连过那么不要连了。(不判断也没问题,因为内部有判定,不会重复添加)
                    if (GetEdge(m_Vertexes[i], m_Vertexes[j]) == null)
                    {
                        AddEdge(m_Vertexes[i], m_Vertexes[j], Random.Range(randomWeight0, randomWeight1));
                    }
                }
            }
        }
    }



    // 根据顶点添加边(内部调用)
    private void AddEdge(VertexNode fromVer, VertexNode toVer, float weight = 0)
    {
        //无向边两个节点都需记录的边的信息
        AddDirectedEdge(fromVer, toVer, weight);
        AddDirectedEdge(toVer, fromVer, weight);
    }
    
    private void AddDirectedEdge(VertexNode fromVer, VertexNode toVer, float weight = 0)
    {
        if (fromVer.firstEdge == null)//无邻接点的时候
        {
            fromVer.firstEdge = new EdgeNode(toVer, weight);
        }
        else
        {
            EdgeNode tmp, edgeNode = fromVer.firstEdge;
            do
            {
                //检查是否添加了重复边
                if (edgeNode.endvex.data.Equals(toVer.data))
                {
                    Debug.LogError("添加了重复的边!");
                    return;
                }
                tmp = edgeNode;
                edgeNode = edgeNode.next;
            } while (edgeNode != null);
            tmp.next = new EdgeNode(toVer, weight);//添加到链表末尾。
        }
    }

    // 获取一条边,如果获取失败则返回null
    private EdgeNode GetEdge(VertexNode fromVer, VertexNode toVer)
    {
        EdgeNode edgeNode = fromVer.firstEdge;
        while (edgeNode != null)
        {
            if (edgeNode.endvex.data.Equals(toVer.data))
            {
                //找到了这条边
                return edgeNode;
            }
            else
            {
                //没找到,继续往下找
                edgeNode = edgeNode.next;
            }
        }
        return null;
    }


    public bool Contains(TValue item)//查图中是否包含某项
    {
        foreach (VertexNode v in m_Vertexes)
        {
            if (v.data.Equals(item))
            {
                return true;
            }
        }
        return false;
    }
    private VertexNode Find(TValue item)//查找指定项并返回
    {
        foreach (VertexNode v in m_Vertexes)
        {
            if (v.data.Equals(item))
            {
                return v;
            }
        }
        return null;
    }

    private int GetIndex(TValue item)   //查找指定项,返回index
    {
        for(int i = 0;i < m_Vertexes.Count;i ++)
        {
            if(m_Vertexes[i].data.Equals(item))
            {
                return i;
            }
        }
        return -1;
    }

    public void Draw()
    {

    }

    //public override string ToString()//仅用于测试
    //{
    //    //打印每个顶点和它的邻接点
    //    string s = string.Empty;
    //    foreach (VertexNode v in items)
    //    {
    //        s += v.data.ToString() + ":";
    //        if (v.firstEdge != null)
    //        {
    //            EdgeNode tmp = v.firstEdge;
    //            while (tmp != null)
    //            {
    //                s += tmp.endvex.data.ToString() + "_" + tmp.weight + " ";
    //                tmp = tmp.next;
    //            }
    //        }
    //        s += "\r\n";
    //    }
    //    return s;
    //}


    //深度优先搜索


    public void DFSTraverse()
    {
        InitVisited();//将visited标志全部置为false
        DFS(m_Vertexes[0]);//从第一个顶点开始遍历
    }
    //广度优先搜索遍历
    public void BFSTraverse()
    {
        InitVisited();//
        BFS(m_Vertexes[0]);//从第一个顶点开始遍历
    }
    //非连通图深度优先遍历
    public void DFSTraverse2()
    {
        InitVisited();
        foreach (VertexNode v in m_Vertexes)
        {
            if (!v.visited)
            {
                DFS(v);
            }
        }
    }
    //非连通图广度优先遍历
    public void BFSTraverse2()
    {
        InitVisited();
        foreach (VertexNode v in m_Vertexes)
        {
            if (!v.visited)
            {
                BFS(v);
            }
        }
    }


    //初始化visited标志
    private void InitVisited()
    {
        foreach (VertexNode v in m_Vertexes)
        {
            v.visited = false;//全部设为false
        }
    }
    //深度优先遍历
    private void DFS(VertexNode v)
    {
        v.visited = true;//将访问标志设为true
        Debug.Log(v.data + " ");//访问
        EdgeNode node = v.firstEdge;
        while (node != null)//访问此顶点的所有邻节点
        {
            //如果邻节点未被访问,则递归访问它的边
            if (!node.endvex.visited)
            {
                DFS(node.endvex);//递归
            }
            node = node.next;//访问下一个节点
        }
    }
    //广度优先遍历
    private void BFS(VertexNode v)//使用队列进行广度优先搜索
    {
        //创建一个队列
        Queue queue = new Queue();
        Debug.Log(v.data + " ");
        v.visited = true;
        queue.Enqueue(v);
        while (queue.Count > 0)
        {
            VertexNode w = queue.Dequeue();
            EdgeNode node = w.firstEdge;
            while (node != null)
            {
                if (!node.endvex.visited)
                {
                    Debug.Log(node.endvex.data + " ");//访问
                    node.endvex.visited = true;//设置访问标志
                    queue.Enqueue(node.endvex);//进队
                }
                node = node.next;//访问下一个邻节点
            }
        }
    }

    /// 
    /// 边节点:连着终点,并且拥有“起点”连接的下一个边的指针
    /// 
    public class EdgeNode
    {
        public VertexNode endvex;    //边连着的“终点”
        public EdgeNode next;       //下一条边
        public float weight;        //边权重

        public EdgeNode(VertexNode endVex, float edgeWeight = 0)
        {
            endvex = endVex;
            weight = edgeWeight;
        }
    }

    /// 
    /// 顶点:作为起点,连着一个边
    /// 
    public class VertexNode
    {
        public TValue data; //数据
        public EdgeNode firstEdge;  //第一条相邻边
        public bool visited;        //访问标志,用于遍历
        public VertexNode(TValue value)
        {
            data = value;
        }
    }


    //-------------------------------------------------

    List m_SpanVertexes;  //最小生成树的顶点集合
    List m_SpanEdges;
    public List spanEdges { get => m_SpanEdges; }

    public void Kruskal()
    {
        // 创建生成树节点列表
        CopyNodes();
        // 获得根据权重排序好的边列表
        var edges = GetSortedEdges();

        int treeEdgeCount = 0;  //树连边个数

        int[] vends = new int[m_SpanVertexes.Count];    // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
        m_SpanEdges = new List();


        // 遍历所有的边
        for (int i = 0;i < edges.Length;i ++)
        {
            //m_SpanEdges.Add(edges[i]);

            //var vex1 = edges[i].begin.parentNode;  //获得起点_父节点
            //var vex2 = edges[i].end.parentNode;    //获得终点_父节点

            var p1 = GetIndex(edges[i].begin.data);
            var p2 = GetIndex(edges[i].end.data);

            var m = get_end(vends, p1);                 // 获取p1在"已有的最小生成树"中的终点
            var n = get_end(vends, p2);                 // 获取p2在"已有的最小生成树"中的终点
                                                        // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
            if (m != n)
            {
                vends[m] = n;                       // 设置m在"已有的最小生成树"中的终点为n

                m_SpanEdges.Add(edges[i]);
                treeEdgeCount++;
                if (treeEdgeCount == (m_SpanVertexes.Count - 1)) break; //边的数量达到最大值
            }
        }

        Debug.LogError(treeEdgeCount);
    }


    int get_end(int[] vends, int i)
    {
        while (vends[i] != 0)
            i = vends[i];
        return i;
    }


    private void CopyNodes()
    {
        m_SpanVertexes = new List(m_Vertexes.Count);
        for (int i = 0; i < m_Vertexes.Count; i++)
        {
            var vertex = new SpanTreeVertexNode();
            vertex.data = m_Vertexes[i].data;
            vertex.parentNode = null;
            m_SpanVertexes.Add(vertex);
        }
    }

    // 遍历所有顶点,获得无向边,然后排序无向边。
    private SpanTreeEdgeNode[] GetSortedEdges()
    {
        InitVisited();

        List result = new List();

        for (int i = 0; i < m_Vertexes.Count; i++)
        {
            m_Vertexes[i].visited = true;
            EdgeNode edge = m_Vertexes[i].firstEdge;    //获得每个顶点的第一条边
            while(edge != null)
            {
                // 遍历所有的边,如果终点还没访问过,那么创建生成树的边结构,加入列表
                if(edge.endvex.visited == false)
                {
                    var spanTreeEdge = new SpanTreeEdgeNode();

                    spanTreeEdge.begin = m_SpanVertexes[i];
                    spanTreeEdge.end = m_SpanVertexes[GetIndex(edge.endvex.data)];
                    spanTreeEdge.weight = edge.weight;

                    //{ begin = m_SpanVertexes[i], end = m_SpanVertexes[GetIndex(edge.endvex.data)], weight = edge.weight };
                    result.Add(spanTreeEdge);
                }
                edge = edge.next;
            }
        }

        return result.OrderBy(x => x.weight).ToArray();
    }




    public class SpanTreeEdgeNode
    {
        public SpanTreeVertexNode begin;
        public SpanTreeVertexNode end;
        public float weight;
    }

    public class SpanTreeVertexNode
    {
        public TValue data;
        public SpanTreeVertexNode parentNode;
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrimGenerater : MonoBehaviour
{
    AGraph roomGraph;
    private void Start()
    {
        roomGraph = new AGraph();
        Room normal1 = new Room(RoomType.Normal);
        Room normal2 = new Room(RoomType.Normal);
        Room normal3 = new Room(RoomType.Normal);
        Room normal4 = new Room(RoomType.Normal);
        Room normal5 = new Room(RoomType.Normal);
        Room normal6 = new Room(RoomType.Normal);

        roomGraph.AddVertex(normal1, normal2, normal3, normal4, normal5, normal6);
        roomGraph.LineAllVertexes(0, 10);
        roomGraph.Kruskal();
    }

    private void Update()
    {
        foreach (var x in roomGraph.spanEdges)
        {
            //Debug.Log(x.begin.data.ToString() + " " + x.end.data.ToString() + " " + x.weight);

            Debug.DrawLine(x.begin.data.roomObject.transform.position, x.end.data.roomObject.transform.position, Color.red);
        }
    }
}


public enum RoomType
{
    Entrance,   //入口
    Boss,       //boss房
    Normal,     //普通房间
    Treasure,   //宝箱房间
    Secret,     //密室
    Sacrifice,  //献祭房
    Gamble,     //赌博房
    Teleport,   //传送房
    Chanllenge, //挑战房
    Puzzle,     //解谜房
    Shop,       //商店房
    X,          //原初房
}

public class Room
{
    private RoomType m_RoomType;
    public RoomType RoomTp { get => m_RoomType; }

    private int m_RoomID;
    public static int roomIndex;

    public Vector3 roomSize;    //房间的大小

    public GameObject roomObject;   //房间物体 

    public Room(RoomType roomType, float roomX = 1, float roomY = 1, float roomZ = 1)
    {
        m_RoomType = roomType;
        roomIndex++;
        m_RoomID = roomIndex;

        roomSize = new Vector3(roomX, roomY, roomZ);

        roomObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
        roomObject.transform.parent = GameObject.Find("PrimGenerater").transform;
        roomObject.transform.localScale = roomSize;
        roomObject.name = ToString();
    }

    public override string ToString()
    {
        return m_RoomType.ToString() + m_RoomID;
    }
}
图的最小环:Floyd

https://blog.csdn.net/Harington/article/details/81982299

你可能感兴趣的:(2020-07-31【数据结构】图,树,深度优先、广度优先遍历,最小生成树,floyd最小环)