数据结构2021温习篇——图(7b)_邻接表

前言

上节我们接触了邻接矩阵存储图的结点之间的关系,但是当图的边特别稀疏时,邻接矩阵就有很多 0 元素,造成空间极大的浪费。本节接触的邻接表可以解决这个问题。



解析



完整代码

//
// Created by Dell on 2019/12/15.
//

#include<iostream>
#include<queue>
#include <iomanip>

using namespace std;

const int maxWeight = 9999;
const int DefaultSize = 30;

template<class T, class E>
struct Edge {
    int dest;    //边的另一个顶点位置
    E cost;            //边上的权值
    Edge<T, E> *link;

    Edge() {}

    Edge(int num, E weight) : dest(num), cost(weight), link(0) {}

    bool operator!=(Edge<T, E> &R) const {
        return (dest != R.dest) ? true : false;
    }
};

template<class T, class E>
struct Vertex {
    T data;
    Edge<T, E> *adj;
};

template<class T, class E>
class Graphlnk {
public:
    Graphlnk(int sz = DefaultSize);

    ~Graphlnk();

    T getValue(int i) {
        return (i >= 0 && i < numVertices) ? NodeTable[i].data : 0;
    }

    E getWeight(int v1, int v2);

    bool insertVertex(const T &vertex);

    bool removeVertex(int v);

    bool insertEdge(int v1, int v2, E weight);

    bool removeEdge(int v1, int v2);

    int getFirstNeighbor(int v);

    int getNextNeighbor(int v, int w);

    void CreateNodeTable();

    void PrintDest(); //把邻接表的样子输出来
    void PrintMatrix(); //把邻接矩阵的样子输出来

    //遍历
    void DFS();

    void BFS();


private:
    int numVertices;
    int maxVertices;
    int numEdges;
    bool *visited; //用于dfs和bfs
    //结点表
    Vertex<T, E> *NodeTable;


    int getVertexPos(const T vertex) {
        for (int i = 0; i < numVertices; i++) {
            if (NodeTable[i].data == vertex)
                return i;
        }
        return -1;
    }

    void DFS(int v);
};


template<class T, class E>
Graphlnk<T, E>::Graphlnk(int sz) {
    maxVertices = sz;
    numVertices = 0;
    numEdges = 0;
    visited = nullptr;

    NodeTable = new Vertex<T, E>[maxVertices];
    if (NodeTable == nullptr) {
        cerr << "分存分配错误" << endl;
        exit(1);
    }
    for (int i = 0; i < maxVertices; i++) {
        NodeTable[i].adj = nullptr;
    }
}


template<class T, class E>
Graphlnk<T, E>::~Graphlnk() {
    for (int i = 0; i < numVertices; i++) {
        Edge<T, E> *p = NodeTable[i].adj;
        while (p != nullptr) {
            NodeTable[i].adj = p->link;
            delete p;
            p = NodeTable[i].adj;
        }
    }
    delete[]NodeTable;
}


template<class T, class E>
E Graphlnk<T, E>::getWeight(int v1, int v2) {
    if (v1 >= 0 && v1 < numVertices && v2 >= 0 && v2 < numVertices) {
        Edge<T, E> *p = NodeTable[v1].adj;
        while (p != 0 && p->dest != v2) {
            p = p->link;
        }
        if (p != 0)
            return p->cost;  //找到此边,返回权值
    }
    return 0;
}


template<class T, class E>
bool Graphlnk<T, E>::insertVertex(const T &vertex) {
    if (numVertices == maxVertices)
        return false;
    NodeTable[numVertices].data = vertex;
    numVertices++;
    return true;
}


template<class T, class E>
bool Graphlnk<T, E>::removeVertex(int v) {
    if (numVertices == 1 || v < 0 || v >= numVertices)
        return false;
    Edge<T, E> *p, *s, *t;
    int i, k;
    while (NodeTable[v].adj != 0) {
        p = NodeTable[v].adj;
        k = p->dest;
        s = NodeTable[k].adj;  //找对称存放的边结点
        t = nullptr;                 //t 是 s的前一个指针,跟着s走,方便后续删除结点
        while (s != 0 && s->dest != v) {
            t = s;
            s = s->link;
        }
        if (s != nullptr) {
            if (t == nullptr)
                NodeTable[k].adj = s->link;
            else
                t->link = s->link;
            delete s;
        }

        NodeTable[v].adj = p->link;
        delete p;
        numEdges--;
    }

    numVertices--;   //图的顶点数减1
    NodeTable[v].data = NodeTable[numVertices].data;  //用最后一个结点来填补
    p = NodeTable[v].adj = NodeTable[numVertices].adj;
    while (p != 0) {
        s = NodeTable[p->dest].adj;
        while (s != nullptr) {
            if (s->dest == numVertices) {
                s->dest = v;
                break;
            } else
                s = s->link;
        }
        ///
        //书中没有这句代码
        p = p->link;
        //
    }

    //问题:把最后一个结点拿来填补,那么其他顶点有连接到numVertices的怎么办
    return true;
}


template<class T, class E>
bool Graphlnk<T, E>::insertEdge(int v1, int v2, E weight) {
    if (v1 >= 0 && v1 < numVertices && v2 >= 0 && v2 < numVertices) {
        Edge<T, E> *q, *p = NodeTable[v1].adj;
        while (p != nullptr && p->dest != v2)
            p = p->link;
        if (p != nullptr)
            return false; //已有此边,否则p此时必为0
        p = new Edge<T, E>;
        q = new Edge<T, E>;

        p->dest = v2;
        p->cost = weight;
        q->dest = v1;
        q->cost = weight;

        //头插入v1的边链表
        p->link = NodeTable[v1].adj;
        NodeTable[v1].adj = p;

        //头插入v2的边链表
        q->link = NodeTable[v2].adj;
        NodeTable[v2].adj = q;


        numEdges++;
        return true;
    }
    return false;
}


template<class T, class E>
bool Graphlnk<T, E>::removeEdge(int v1, int v2) {
    if (v1 >= 0 && v1 < numVertices && v2 >= 0 && v2 < numVertices) {
        Edge<T, E> *p = NodeTable[v1].adj;
        Edge<T, E> *q = nullptr, *s = p; //q是p上一个指针,方便删除操作

        while (p != nullptr && p->dest != v2) {
            q = p;
            p = p->link;
        }
        if (p != nullptr) {
            if (p == s)    //要删的结点被头指针指着
                NodeTable[v1].adj = p->link;
            else  //此时不是头指针指着的结点q必然不空
                q->link = p->link;
            delete p;
        } else
            return false; // p == 0,没找到这条边

        p = NodeTable[v2].adj;  //v2对应边链表中删除
        q = nullptr;
        s = p;
        while (p->dest != v1) {
            q = p;
            p = p->link;
        }
        if (p == s)   //要删的结点被头指针指着
            NodeTable[v2].adj = p->link;
        else
            q->link = p->link;
        delete p;
        return true;
    }
    return false;
}


template<class T, class E>
int Graphlnk<T, E>::getFirstNeighbor(int v) {
    if (v >= 0 && v < numVertices) {
        Edge<T, E> *p = NodeTable[v].adj;
        if (p != 0)
            return p->dest;//存在,返回第一个临界点
    }
    return -1;
}


template<class T, class E>
int Graphlnk<T, E>::getNextNeighbor(int v, int w) {
    if (v >= 0 && v < numVertices) {
        Edge<T, E> *p = NodeTable[v].adj;
        while (p != 0 && p->dest != w)
            p = p->link;
        if (p != 0 && p->link != 0)
            return p->link->dest;
    }
    return -1;  //-1代表不存在
}


//建立邻接表结构
template<class T, class E>
void Graphlnk<T, E>::CreateNodeTable() {
    int n, i, j, m;
    Edge<T, E> *p;

    cout << "请输入要创建的结点个数" << endl;
    cin >> n; //结点个数
    if (n > maxVertices) {
        cout << "超过最大结点数" << endl;
        return;
    }
    numVertices = n;
    for (i = 0; i < n; i++) {
        NodeTable[i].adj = 0;  //预设为空链
        cout << "请输入编号为 " << i << "的结点的值: ";
        cin >> NodeTable[i].data;

        cout << "请输入" << NodeTable[i].data << "的邻接点个数:";
        cin >> m;
        cout << "请输入它的 " << m << "个邻接点(相邻顶点编号 权重)" << endl;
        for (j = 0; j < m; j++) {
            p = new Edge<T, E>;
            cin >> p->dest;
            cin >> p->cost;
            //头插入建链
            p->link = NodeTable[i].adj;
            NodeTable[i].adj = p;
        }
    }
}


template<class T, class E>
void Graphlnk<T, E>::DFS(int v) {
    visited[v] = true;
    cout << NodeTable[v].data << " ";
    Edge<T, E> *p = NodeTable[v].adj;
    while (p != 0) {
        if (!visited[p->dest])
            DFS(p->dest);
        p = p->link;
    }
}


template<class T, class E>
void Graphlnk<T, E>::DFS() {
    visited = new bool[numVertices];
    for (int i = 0; i < numVertices; i++)
        visited[i] = false;
    int v0;
    cout << "请输入深度优先遍历的出发点编号:0至 " << numVertices - 1 << endl;
    cin >> v0;
    DFS(v0);
    cout << endl << endl;
}


template<class T, class E>
void Graphlnk<T, E>::BFS() {
    visited = new bool[numVertices];
    for (int i = 0; i < numVertices; i++)
        visited[i] = false;
    int v;
    Edge<T, E> *p;
    queue<int> Q;
    cout << "请输入广度优先遍历的出发点编号:0至 " << numVertices - 1 << endl;
    cin >> v;
    Q.push(v);

    while (!Q.empty()) {
        v = Q.front();
        Q.pop();
        if (!visited[v]) {
            visited[v] = true;
            cout << NodeTable[v].data << " ";
            p = NodeTable[v].adj;
            while (p != 0) {
                if (!visited[p->dest])
                    Q.push(p->dest);
                p = p->link;
            }
        }
    }
    cout << endl << endl;
}


template<class T, class E>
void Graphlnk<T, E>::PrintDest() {
    Edge<T, E> *p;
    for (int i = 0; i < numVertices; i++) {
        cout << "-----与" << "编号为 " << i << " ,值为 ";
        cout << NodeTable[i].data << " 相连的点 : 顶点值(编号,权值)" << endl;
        p = NodeTable[i].adj;
        while (p != 0) {
            cout << NodeTable[p->dest].data;
            cout << "( " << p->dest << ", " << p->cost << ")" << " ";
            p = p->link;
        }
        cout << endl << endl;
    }
    cout << endl << endl;
}

template<class T, class E>
void Graphlnk<T, E>::PrintMatrix() {
    int i, j;
    E matrix[numVertices][numVertices];

    for (i = 0; i < numVertices; i++)
        for (j = 0; j < numVertices; j++)
            matrix[i][j] = (i == j) ? 0 : maxWeight;


    Edge<T, E> *p;
    for (i = 0; i < numVertices; i++) {
        p = NodeTable[i].adj;
        while (p != nullptr) {
            matrix[i][p->dest] = p->cost;
            p = p->link;
        }
    }

    cout << "图的邻接表用“邻接矩阵”形式表示: " << endl;
    cout << setw(8) << " ";
    for (i = 0; i < numVertices; i++)
        cout << setw(8) << NodeTable[i].data;
    cout << endl;
    for (i = 0; i < numVertices; i++) {
        cout << setw(8) << NodeTable[i].data;
        for (j = 0; j < numVertices; j++) {
            cout << setw(8) << matrix[i][j];
        }
        cout << endl;
    }
    cout << endl << endl;
}


int main() {
    Graphlnk<char, double> a;
    //a.CreateNodeTable();

    //把图先建立起来

    //先插入顶点
    a.insertVertex('a');
    a.insertVertex('b');
    a.insertVertex('c');
    a.insertVertex('d');
    a.insertVertex('e');
    a.insertVertex('f');
    a.insertVertex('g');


    //插入他们之间的边
    a.insertEdge(0, 1, 3.1);
    a.insertEdge(0, 2, 2.6);
    a.insertEdge(0, 4, 2.7);

    a.insertEdge(1, 3, 5.4);
    a.insertEdge(1, 5, 4.5);

    a.insertEdge(4, 5, 3.8);
    a.insertEdge(5, 6, 4.2);

    a.PrintDest();

//    a.PrintMatrix();



    cout << "删除带点e之后" << endl;

    a.removeVertex(4);
    a.PrintDest();

    cout << endl << "删除边bf后..." << endl;
    a.removeEdge(1, 5);
    a.PrintDest();


    a.BFS();
    a.DFS();
/*
	cout << endl << endl;
	cout << "a广度优先遍历" << endl;
	a.BFS();
	cout << endl << endl;
	cout << "a深度优先遍历" << endl;
	a.DFS();
*/
    cout << endl;
}
上一篇:Linux基线合规检查中各文件的作用及配置脚本


下一篇:图论_图的基础(更新中)