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: