(C++新手向)(哈希表x图论)以HashTable为索引,实现图的增加,删除和打印(底层实现,有修改)

2023/12/14日修改:

①Graph类的addEdge函数中存在BUG,在Insert的时候会传入了vertices,这会导致在查找哈希表中的节点时,会返回和数组连接的链表的第一个元素的地址
②Graph类的deleteEdge函数中存在BUG,在Delete的时候传入了vertices,这会导致删除和数组连接的链表的第一个元素的地址

③在HashTable::Delete函数中存在BUG,当判定时第一个节点时,少加了一个return,这会导致当找到的是第一个节点时,还会循环遍历到结尾,并且输出删除失败

       

        在实现图的增加,删除和打印的过程中,寻找当前顶点的索引会花费大量的时间,所以本文中利用哈希表来简化操作,实现时间复杂度的降低,以下是代码实现:

各种节点的定义:

struct ArcNode
{
    VerTexType adjvex;
    struct ArcNode* next;
    int weight;
    ArcNode(VerTexType adjvex, int weight) :adjvex(adjvex), weight(weight), next(nullptr) {};
};
struct VNode
{
    VerTexType data;
    ArcNode* firstarc;
    VNode() :data(NULL), firstarc(nullptr) {};
};
typedef struct HashNode
{
    char key;
    VNode* vertexPtr;
    struct HashNode* next;
    HashNode(char k, VNode* ptr) :key(k), vertexPtr(ptr), next(nullptr) {};
}HashNode;

HashTable类的封装 

class HashTable
{
public:
    HashTable();
    ~HashTable();
    int gethashvalue(VerTexType key) const;
    void Insert(VerTexType key, VNode* vertexPtr); 
    void Delete(VerTexType key, VNode* vertexPtr);
    void Print();
private:
    HashNode** table = new HashNode* [TABLE_SIZE];
    int hashvalue(VerTexType key) const;
};

Graph类封装

class Graph {
public:
    Graph();
    ~Graph();
    void addEdge(VerTexType from,VerTexType to,int weight);
    void deleteEdge(VerTexType from, VerTexType to);
    void PrintALGraph();
    int getvertexnum();
    int getarcnum();
    VNode* getvertices();
private:    
    int vertexnum=0;
    int arcnum=0;
    VNode* vertices;
    HashTable VertexMap;
};

以下是HashTable类的具体实现

HashTable::HashTable()
{
    //错误处理,此处也可以改为table=New HashNode*[TABLE_SIZE];if(table==NULL) 若if成立,则错误,输出内存分配失败并且exit(EXIT_FAILURE)。
    try
    {
        table = new HashNode * [TABLE_SIZE];
        for (int i = 0; i < TABLE_SIZE; i++)
        {
            table[i] = nullptr;
        }
    }
    catch (bad_alloc& e)
    {
        cout << "内存分配失败(0xC0000005),请稍后重试。" << e.what() << endl;
        exit(EXIT_FAILURE);
    }
}

HashTable::~HashTable()
{
    for (int i = 0; i < TABLE_SIZE; i++)//迭代删除数组内的元素
    {
        HashNode* current = table[i];
        while (current != nullptr)
        {
            HashNode* toDelete = current;
            current = current->next;
            delete toDelete;
            return;
        }       
    }
    delete[] table;//直接删除整个指针数组
}

int HashTable::hashvalue(VerTexType key) const
{
    return key % TABLE_SIZE;
}

int HashTable::gethashvalue(VerTexType key) const
{
    return hashvalue(key);
}//公共接口访问私有函数

void HashTable::Insert(VerTexType key, VNode* vertexPtr)
{
    int hashIndex = hashvalue(key);
    if (!table[hashIndex])
    {
        table[hashIndex] = new HashNode(key, vertexPtr);
    }
    else
    {
        HashNode* current = table[hashIndex];
        while (current->next != nullptr)
        {
            current = current->next;
        }
        HashNode* newNode = new HashNode(key, vertexPtr);
        current->next = newNode;
    }
}

void HashTable::Delete(VerTexType key, VNode* vertexPtr)//传入VNode*类型的变量,会自动计算顶点的哈希值,并且执行删除操作
{
    int index = hashvalue(key);
    if (!table[index])
    {
        cout << "Empty Map(in function Delete)!" << endl;
        return;
    }
    if (table[index]->vertexPtr->data == key)
    {

        HashNode* nodeToDelete = table[index];//变量名称要清晰易懂!
        table[index] = table[index]->next;
        delete nodeToDelete;
        return;//此处有修改
    }
    HashNode* current = table[index];
    while (current->next != nullptr)
    {
        if (current->next->vertexPtr->data == key)
        {
            HashNode* toDelete = current->next;
            current->next = current->next->next;
            delete toDelete;
        }
        current = current->next;
    }
}

以下是Graph类的具体实现

Graph::Graph()
{
    cout << "请输入顶点数,边数" << endl;
    cin >> vertexnum >> arcnum;
    vertices = new VNode[vertexnum];
    cout << "请输入顶点名称" << endl;
    for (int i = 0; i < vertexnum; i++)
    {
        char vertexname;
        cin >> vertexname;
        vertices[i].data = vertexname;
    }
}

Graph::~Graph()
{
    for (int i = 0; i < vertexnum; i++)
    {
        ArcNode* currentArc = vertices[i].firstarc;//第一条边
        while (currentArc != nullptr)
        {
            ArcNode* tempArc = currentArc;
            currentArc = currentArc->next;
            delete tempArc;
        }        
    }
    delete[] vertices;
}

void Graph::addEdge(VerTexType from,VerTexType to,int weight)
{
    int fromIndex = -1;//找from的顶点信息,若找不到,则return,默认为-1
    int toIndex = -1;//找to的顶点信息,若找不到,则return,默认为-1
    for (int i = 0; i < arcnum; i++)//找参数from是否存在
    {
        if (vertices[i].data == from)
        {
            fromIndex = i;
            break;
        }
    }
    if (fromIndex==-1)
    {
        cout << "起始顶点未找到,正在退出……" << endl<<"警告:此次addEdge已被忽略。"<<endl;
        return;
    }
    for (int i = 0; i < vertexnum; i++)//找参数to是否存在
    {
        if (vertices[i].data == to)
        {
            toIndex = i;
            break;
        }
    }
    if (toIndex==-1)
    {
        cout << "终止顶点未找到,正在退出……" << endl << "警告:此次addEdge已被忽略。" << endl;
        return;
    }
    //重复边的处理,若发现则直接忽略
    for (ArcNode* arc = vertices[fromIndex].firstarc; arc != nullptr; arc = arc->next)
    {
        if (arc->adjvex == to)
        {
            cout<< "警告:边 " << from << " -> " << to << " 已经存在。此次addEdge已被忽略。" << endl;
            return;
        }
    }
    //操作哈希表
    VertexMap.Insert(from,&vertices[fromIndex]);//此处有修改

    //哈希表操作完毕,接下来是邻接表
    if (!vertices[fromIndex].firstarc)
    {
        ArcNode* newArc = new ArcNode(to, weight);
        vertices[fromIndex].firstarc = newArc;
    }
    else
    {
        ArcNode* newArc = new ArcNode(to, weight);//初始化,储存数据的节点
        ArcNode* current = vertices[fromIndex].firstarc;//用来遍历,因为没有赋初值(解引用,所以只需要指针大小的内存),正好可以用来遍历
        while (current->next != nullptr)
        {            
            current = current->next;
        }
        current->next = newArc;
    }    
}

void Graph::deleteEdge(VerTexType from, VerTexType to)
{
    int fromIndex = -1;//找from的顶点信息,若找不到,则return,默认为-1
    int toIndex = -1;//找to的顶点信息,若找不到,则return,默认为-1
    for (int i = 0; i < arcnum; i++)//找参数from是否存在
    {
        if (vertices[i].data == from)
        {
            fromIndex = i;
            break;
        }
    }
    if (fromIndex == -1)
    {
        cout << "起始顶点未找到,正在退出……" << endl << "警告:此次deleteEdge已被忽略。" << endl;
        return;
    }
    for (int i = 0; i < vertexnum; i++)//找参数to是否存在
    {
        if (vertices[i].data == to)
        {
            toIndex = i;
            break;
        }
    }
    if (toIndex == -1)
    {
        cout << "终止顶点未找到,正在退出……" << endl << "警告:此次deleteEdge已被忽略。" << endl;
        return;
    }
    //下面先处理哈希表
    VertexMap.Delete(from,&vertices[toIndex]);//此处有修改

    //哈希表操作完毕,接下来是邻接表
    if (!vertices[fromIndex].firstarc)//检查是不是空表
    {
        cout << vertices[fromIndex].data << "的邻接边为空!";
        return;
    }
    if (vertices[fromIndex].firstarc->adjvex == to)//检查是不是第一个节点
    {
        ArcNode* toDelete = vertices[fromIndex].firstarc;
        vertices[fromIndex].firstarc = vertices[fromIndex].firstarc->next;
        delete toDelete;
        return;
    }
    //排除完毕,开始遍历链表直到最后一个元素
    ArcNode* current = vertices[fromIndex].firstarc;
    while (current->next != nullptr)
    {
        if (current->next->adjvex == to)//若找到该边
        {
            ArcNode* toDelete = current->next;
            current->next = current->next->next;//则删除
            delete toDelete;
            return;
        }
        current = current->next;
    }
    cout << "找不到该边,删除失败!正在退出……" << endl;
}

void Graph::PrintALGraph()
{    
    cout << "图的邻接表表示如下:" << endl;
    for (int i = 0; i < vertexnum; i++)
    {
        cout << vertices[i].data << ": ";
        for (ArcNode* tempArc = vertices[i].firstarc; tempArc != NULL; tempArc = tempArc->next)
        {
            cout << tempArc->adjvex<<"("<<tempArc->weight<<") ";
        }
        cout << endl;
    }
}

int Graph::getarcnum()
{
    return arcnum;
}

int Graph::getvertexnum()
{
    return vertexnum;
}

VNode* Graph::getvertices()
{
    return vertices;
}

经过以上定义,我们实现了图的增加,删除和打印,以下是全部代码

#include <iostream>
#define TABLE_SIZE 20
using namespace std;
//Vertex Type
typedef char VerTexType;


//下面是各种结点
struct ArcNode
{
    VerTexType adjvex;
    struct ArcNode* next;
    int weight;
    ArcNode(VerTexType adjvex, int weight) :adjvex(adjvex), weight(weight), next(nullptr) {};
};
struct VNode
{
    VerTexType data;
    ArcNode* firstarc;
    VNode() :data(NULL), firstarc(nullptr) {};
};
typedef struct HashNode
{
    char key;
    VNode* vertexPtr;
    struct HashNode* next;
    HashNode(char k, VNode* ptr) :key(k), vertexPtr(ptr), next(nullptr) {};
}HashNode;



//HashTable类封装
class HashTable
{
public:
    HashTable();
    ~HashTable();
    int gethashvalue(VerTexType key) const;
    void Insert(VerTexType key, VNode* vertexPtr); 
    void Delete(VerTexType key, VNode* vertexPtr);
    void Print();
private:
    HashNode** table = new HashNode* [TABLE_SIZE];
    int hashvalue(VerTexType key) const;
};



//Graph类封装
class Graph {
public:
    Graph();
    ~Graph();
    void addEdge(VerTexType from,VerTexType to,int weight);
    void deleteEdge(VerTexType from, VerTexType to);
    void PrintALGraph();
    int getvertexnum();
    int getarcnum();
    VNode* getvertices();
private:    
    int vertexnum=0;
    int arcnum=0;
    VNode* vertices;
    HashTable VertexMap;
};


//下面是HashTable类的具体实现
HashTable::HashTable()
{
    //错误处理,此处也可以改为table=New HashNode*[TABLE_SIZE];if(table==NULL) 若if成立,则错误,输出内存分配失败并且exit(EXIT_FAILURE)。
    try
    {
        table = new HashNode * [TABLE_SIZE];
        for (int i = 0; i < TABLE_SIZE; i++)
        {
            table[i] = nullptr;
        }
    }
    catch (bad_alloc& e)
    {
        cout << "内存分配失败(0xC0000005),请稍后重试。" << e.what() << endl;
        exit(EXIT_FAILURE);
    }
}

HashTable::~HashTable()
{
    for (int i = 0; i < TABLE_SIZE; i++)//迭代删除数组内的元素
    {
        HashNode* current = table[i];
        while (current != nullptr)
        {
            HashNode* toDelete = current;
            current = current->next;
            delete toDelete;
        }       
    }
    delete[] table;//直接删除整个指针数组
}

int HashTable::hashvalue(VerTexType key) const
{
    return key % TABLE_SIZE;
}

int HashTable::gethashvalue(VerTexType key) const
{
    return hashvalue(key);
}//公共接口访问私有函数

void HashTable::Insert(VerTexType key, VNode* vertexPtr)
{
    int hashIndex = hashvalue(key);
    if (!table[hashIndex])
    {
        table[hashIndex] = new HashNode(key, vertexPtr);
    }
    else
    {
        HashNode* current = table[hashIndex];
        while (current->next != nullptr)
        {
            current = current->next;
        }
        HashNode* newNode = new HashNode(key, vertexPtr);
        current->next = newNode;
    }
}

void HashTable::Delete(VerTexType key, VNode* vertexPtr)//传入VNode*类型的变量,会自动计算顶点的哈希值,并且执行删除操作
{
    int index = hashvalue(key);
    if (!table[index])
    {
        cout << "Empty Map(in function Delete)!" << endl;
        return;
    }
    if (table[index]->vertexPtr->data == key)
    {

        HashNode* nodeToDelete = table[index];//变量名称要清晰易懂!
        table[index] = table[index]->next;
        delete nodeToDelete;
        return;
    }
    HashNode* current = table[index];
    while (current->next != nullptr)
    {
        if (current->next->vertexPtr->data == key)
        {
            HashNode* toDelete = current->next;
            current->next = current->next->next;
            delete toDelete;
            return;//此处有修改
        }
        current = current->next;
    }
}


//下面是Graph类的具体实现
Graph::Graph()
{
    cout << "请输入顶点数,边数" << endl;
    cin >> vertexnum >> arcnum;
    vertices = new VNode[vertexnum];
    cout << "请输入顶点名称" << endl;
    for (int i = 0; i < vertexnum; i++)
    {
        char vertexname;
        cin >> vertexname;
        vertices[i].data = vertexname;
    }
}

Graph::~Graph()
{
    for (int i = 0; i < vertexnum; i++)
    {
        ArcNode* currentArc = vertices[i].firstarc;//第一条边
        while (currentArc != nullptr)
        {
            ArcNode* tempArc = currentArc;
            currentArc = currentArc->next;
            delete tempArc;
        }        
    }
    delete[] vertices;
}

void Graph::addEdge(VerTexType from,VerTexType to,int weight)
{
    int fromIndex = -1;//找from的顶点信息,若找不到,则return,默认为-1
    int toIndex = -1;//找to的顶点信息,若找不到,则return,默认为-1
    for (int i = 0; i < arcnum; i++)//找参数from是否存在
    {
        if (vertices[i].data == from)
        {
            fromIndex = i;
            break;
        }
    }
    if (fromIndex==-1)
    {
        cout << "起始顶点未找到,正在退出……" << endl<<"警告:此次addEdge已被忽略。"<<endl;
        return;
    }
    for (int i = 0; i < vertexnum; i++)//找参数to是否存在
    {
        if (vertices[i].data == to)
        {
            toIndex = i;
            break;
        }
    }
    if (toIndex==-1)
    {
        cout << "终止顶点未找到,正在退出……" << endl << "警告:此次addEdge已被忽略。" << endl;
        return;
    }
    //重复边的处理,若发现则直接忽略
    for (ArcNode* arc = vertices[fromIndex].firstarc; arc != nullptr; arc = arc->next)
    {
        if (arc->adjvex == to)
        {
            cout<< "警告:边 " << from << " -> " << to << " 已经存在。此次addEdge已被忽略。" << endl;
            return;
        }
    }
    //操作哈希表
    VertexMap.Insert(from,&vertices[fromIndex]);//此处有修改

    //哈希表操作完毕,接下来是邻接表
    if (!vertices[fromIndex].firstarc)
    {
        ArcNode* newArc = new ArcNode(to, weight);
        vertices[fromIndex].firstarc = newArc;
    }
    else
    {
        ArcNode* newArc = new ArcNode(to, weight);//初始化,储存数据的节点
        ArcNode* current = vertices[fromIndex].firstarc;//用来遍历,因为没有赋初值(解引用,所以只需要指针大小的内存),正好可以用来遍历
        while (current->next != nullptr)
        {            
            current = current->next;
        }
        current->next = newArc;
    }    
}

void Graph::deleteEdge(VerTexType from, VerTexType to)
{
    int fromIndex = -1;//找from的顶点信息,若找不到,则return,默认为-1
    int toIndex = -1;//找to的顶点信息,若找不到,则return,默认为-1
    for (int i = 0; i < arcnum; i++)//找参数from是否存在
    {
        if (vertices[i].data == from)
        {
            fromIndex = i;
            break;
        }
    }
    if (fromIndex == -1)
    {
        cout << "起始顶点未找到,正在退出……" << endl << "警告:此次deleteEdge已被忽略。" << endl;
        return;
    }
    for (int i = 0; i < vertexnum; i++)//找参数to是否存在
    {
        if (vertices[i].data == to)
        {
            toIndex = i;
            break;
        }
    }
    if (toIndex == -1)
    {
        cout << "终止顶点未找到,正在退出……" << endl << "警告:此次deleteEdge已被忽略。" << endl;
        return;
    }
    //下面先处理哈希表
    VertexMap.Delete(from,&vertices[toIndex]);//此处有修改

    //哈希表操作完毕,接下来是邻接表
    if (!vertices[fromIndex].firstarc)//检查是不是空表
    {
        cout << vertices[fromIndex].data << "的邻接边为空!";
        return;
    }
    if (vertices[fromIndex].firstarc->adjvex == to)//检查是不是第一个节点
    {
        ArcNode* toDelete = vertices[fromIndex].firstarc;
        vertices[fromIndex].firstarc = vertices[fromIndex].firstarc->next;
        delete toDelete;
        return;
    }
    //排除完毕,开始遍历链表直到最后一个元素
    ArcNode* current = vertices[fromIndex].firstarc;
    while (current->next != nullptr)
    {
        if (current->next->adjvex == to)//若找到该边
        {
            ArcNode* toDelete = current->next;
            current->next = current->next->next;//则删除
            delete toDelete;
            cout << "边" << from << "->" << to << "已被删除。" << endl;
            return;
        }
        current = current->next;
    }
    cout << "找不到该边,删除失败!正在退出……" << endl;
}

void Graph::PrintALGraph()
{    
    cout << "图的邻接表表示如下:" << endl;
    for (int i = 0; i < vertexnum; i++)
    {
        cout << vertices[i].data << ": ";
        for (ArcNode* tempArc = vertices[i].firstarc; tempArc != NULL; tempArc = tempArc->next)
        {
            cout << tempArc->adjvex<<"("<<tempArc->weight<<") ";
        }
        cout << endl;
    }
}

int Graph::getarcnum()
{
    return arcnum;
}

int Graph::getvertexnum()
{
    return vertexnum;
}

VNode* Graph::getvertices()
{
    return vertices;
}

int main()
{
    Graph G;
    for (int i = 0; i < G.getarcnum(); i++)
    {
        VerTexType from, to;
        int weight;
        cout << "请输入起始顶点,到达顶点,该有向边的权重,现在是" << i << "号元素,(i<=" << G.getarcnum()-1 << ")。" << endl;
        cin >> from >> to >> weight;
        G.addEdge(from,to,weight);
    }
    VerTexType A = 'A';VerTexType B = 'B';
    G.deleteEdge(A,B);
    G.PrintALGraph();
}

当输入

3 3

A B C

A B 4

A C 5

B C 6

时,程序可以正确输出         

图的邻接表表示如下:
A: C(5)
B: C(6)
C:   

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunrise丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值