zoukankan      html  css  js  c++  java
  • 重学数据结构系列之——图的储存

    学习来源:计蒜客

    1.什么是图

    比如说我们的关系网络,就来微信来说,我们把用户看成是一个点,是好友的我们就把这两个点连起来。
    如下面的就是一个简单的图


    2.图的分类


    有向图和无向图,上图就是无向图,有向图是单向的,边用箭头来表示,如下:


    3.图的常用概念


    顶点:就是那些点咯
    边:就是连接两个顶点的线,分为有向边和无线边

    下面一般是指有向图
    入度:该顶点有多少个箭头指着
    出度:该顶点有多少条边箭头指着别人
    顶点的度为入度与出度之和

    3.图一般的储存方式


    邻接矩阵和邻接表

    4.邻接矩阵储存的实现


    其实就是用一个二维数组来储存,比如说是二维数组mat,mat[1][2]=1 ,就说明编号为1,2的两个顶点有边(这里编号从0开始)
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class Graph{
    private:
    	int **mat;	//邻接矩阵,其实就是二维数组
    	int n;	//顶点个数
    public:
    	Graph(int input_n){
    		n= input_n;
    		//先分配n个int*的指针,再对每个指针再循环分配
    		mat = new int*[n];
    		for (int i = 0; i < n; i++) {
    			mat[i] = new int[n];
    			//将内存中mat[i]的sizeof(int)*n个字节的内容全部设置为那个字符的ASCII值(这里的字符好似0)
    			//它是对较大的结构体或数组进行清零操作的一种最快方法
    			memset(mat[i], 0, sizeof(int)*n);//Sets buffers to a specified character.
    		}
    	}
    	~Graph(){
    		//析构时注意先析构里面的,再外层的
    		for (int i = 0; i < n; i++) {
    			delete[] mat[i];
    		}
    		delete[] mat;
    	}
    	//插入边,直接将对于的位置置为1就可以了
    	void insert(int x, int y){
    		mat[x][y] = 1;
    	}
    	//输出邻接矩阵
    	void output(){
    		for (int i = 0; i < n; i++) {
    			for (int j = 0; j < n; j++) {
    				cout<<mat[i][j]<<" ";
    			}
    			cout<<endl;
    		}
    	}
    };
    
    int main(){
    	//n:顶点个数	m:有向边个数
    	//x,y: 表示插入一条x连向y的有向边
    	int n, m, x, y;
    	cin >> n >> m;
    	Graph g(n);
    	for (int i = 0; i< m; i++) {
    		cin >> x >> y;
    		g.insert(x, y);
    	}
    	g.output();
    	return 0;
    }

    对memset的理解: 通过调试,可以看到内存的地址的值全都变成了00000000,因为int是四个字节嘛,那int指针也是。


    运行结果:



    5.邻接表的储存实现

    #include <iostream>
    using namespace std;
    
    //链表结点
    class LinkedListNode{
    public:
    	//vertex:链表结点的值
    	int vertex;
    	//指向下一个结点的指针
    	LinkedListNode *next;
    	LinkedListNode(int vertex_input){
    		vertex = vertex_input;
    		next = NULL;
    	}
    };
    
    //链表
    class LinkedList{
    public:
    	LinkedListNode *head;
    
    	LinkedList(){
    		head = NULL;
    	}
    
    	~LinkedList(){
    		//顺着链析构
    		while (head != NULL) {
    			LinkedListNode *delete_node = head;
    			head = head->next;
    			delete delete_node;
    		}
    	}
    	//插入函数,这里直接插到头的前面了
    	void insert(int vertex){
    		LinkedListNode *node = new LinkedListNode(vertex);
    		node->next = head;
    		head = node;
    	}
    };
    
    //图
    class Graph{
    private:
    	//储存链表的指针
    	LinkedList *edges;
    	//n:顶点数目
    	int n;
    public:
    	Graph(int input_n){
    		n = input_n;
    		edges = new LinkedList[n];
    	}
    	~Graph(){
    		delete[] edges;
    	}
    	
    	//插入就调用链表的插入
    	void insert(int x, int y) {
    		edges[x].insert(y);
        }
    	
    	//循环输出每个链表的每个结点的值即可
        void output() {
    		for (int i = 0; i < n; i++) {
    			cout<<i<<":";
    			//auto 是 C++11 的新特性,在定义变量时无需指定类型,编译器会通过类型推导得到实际的类型
    			//但我用vc++6.0,所以不支持
    			//for (auto* j = edges[i].head; j != NULL; j = j->next) {
    			for (LinkedListNode* j = edges[i].head; j != NULL; j = j->next) {
    				cout<<j->vertex<<" ";
    			}
    			cout<<endl;
    		}
        }
    };
    
    int main(){
    	int n, m, x, y;
        cin >> n >> m;
        Graph g(n);
        for (int i = 0; i < m; ++i) {
            cin >> x >> y;
            g.insert(x, y);
        }
        g.output();
    	return 0;
    }

    运行结果:(可以看到结果非常直观,那个顶点有没有指向谁的边都一眼看出来)








  • 相关阅读:
    金蝶 kis一些功能的内部逻辑和个人技巧
    mysql横向和纵向合并sql数据用于展示,快递导出导入海量数据
    laravel carbon时间处理组件开发文档-中文版
    金蝶kis数据库说明(转载)
    java微信支付,对账单下载
    RabbitMQ中文文档PHP版本(七)--发布者确认
    RabbitMQ中文文档PHP版本(六)--远程过程调用(RPC)
    RabbitMQ中文文档PHP版本(五)--主题
    RabbitMQ中文文档PHP版本(四)--路由
    RabbitMQ中文文档PHP版本(二)--发布/订阅
  • 原文地址:https://www.cnblogs.com/cnsec/p/13286548.html
Copyright © 2011-2022 走看看